Namespaced models 19

Posted by pratik
on Sunday, December 09

I don’t really understand why people use namespaced models. I see ActiveRecord models as DSL for database. There is no concept of namespacing in Database, then why should you have them with models ? Apart from that, they are very buggy too !

“I am generally not a huge fan of namespaces for models. As I don’t think that’s a good fit for splitting up your domain.” - DHH

From what I’ve seen, the most common explanations given are :

  • To organize models
  • To reuse the code

Now let’s look at elegant solutions for both these problems.

For the purpose of this article, let’s assume you have models for different kind of pets. e.g. Dog, Cat & Rabbit.

How to organize models ?

Rails by default, wants you to put all your models in RAILS_ROOT/app/models directory. But that’s a convention. There is absolutely nothing that stops you from putting your model files anywhere you wish and organize them according to your liking and based on application specific logical groups.

1
2
3
4
5
Rails::Initializer.run do |config|
  # Your existing stuff

  config.load_paths << "#{RAILS_ROOT}/app/models/pets"
end

That’s it ! Now you can have dog.rb, cat.rb & rabbit.rb inside RAILS_ROOT/app/models/pets directory.

But what about reuse !?

Two ways to skin this cat :

  • Good ol’ mixins
  • Abstract models

Abstract models are the models which cannot have objects ( cannot be instantiated ) and hence they don’t have associated table as well. Every rails developer uses abstract model in their code without knowing it. ActiveRecord::Base. In our case, we can have an abstract model called Pet for keeping the common behavior of all the pets. And our models would look something like :

# RAILS_ROOT/app/models/pets/pet.rb
1
2
3
4
5
6
class Pet < ActiveRecord::Base
  self.abstract_class = true
  
  belongs_to :person
  validates_presence_of :name
end
# RAILS_ROOT/app/models/pets/dog.rb
1
2
3
4
5
class Dog < Pet
  def bark
    "baaw"
  end
end

That’s it. Dog will inherit all the methods/validations/associations from parent Pet model and so will all the other models who would inherit from Pet abstract model. Please note that this is not STI as we have set self.abstract_class = true in Pet.

Comments

Leave a response

  1. August Lilleaas (leethal)December 09, 2007 @ 10:08 PM

    Oh hey, that’s nice. Can’t think of any real-world uses of this, though, care to give any examples – if any? All my ideas involve STI, such as a hardcoded permission systems (Admin < User), task states (Finished < Task) etc.

  2. PratikDecember 09, 2007 @ 10:27 PM

    leethal : In many cases STI might not be the right fit, because you might end up having totally useless columns in your base table, and probably you may never be accessing your base model objects directly in some other cases.

    Let’s say, you have an Article model. And children model are BlogArticle and NewsPaperArticle. Here, STI may not be a right fit when,

    • You’re never using Article class directly in your application
    • All you have common between BlogArticle and NewsPaperArticle is text. BlogArticle might have url, permalink, pageviews, etc. While NewsPaperArticle may have page, column etc.

    I suggested to use abstract models for cases where people use namespaced models. And of course, abstract model can act as an interface as well.

  3. DavidDecember 10, 2007 @ 05:08 AM

    Databases like Oracle and M$oft Sql Server allow an user to access one or more schema, which can be used like namespaces.

    eg: Two schema Account and Product Account.Customers Account.Invoices Account.Invoice_Items Product.Products Product.Stock_Prices

    Though I don’t believe that ActiveRecord would automagic support this.

  4. StijnDecember 10, 2007 @ 10:53 AM

    Thanks, helpfull article ! I was just struggling with the “models in subdirectory” issue myself. I don’t need the base-model, it’s only for organization. One question though: I managed to do the same with the controllers so I get: pets/cats_controller – pets/dogs_controller – ... But I’m having difficulties with the views, I would like to have my views like this: views\pets\dogs\index.rhtml – views\pets\cats\index.rhtml – ... Probably something silly, but how do I point my actions in ‘dogs_controller’ to the dog-subdirectory in views ? (I’m getting the ‘template missing views/dogs/index.rhtml’ error with the default render option)

  5. PratikDecember 10, 2007 @ 01:21 PM

    Stijn : Thanks for the comment. For doing the same with views, you’ll need to append the path to ActionController::Base.view_paths. You should find more information here or you can check the docs/source as well.

  6. JustinDecember 10, 2007 @ 05:40 PM

    I’ve wondered about a way to inherit AR classes without triggering STI. I somehow missed abstract classes. Thanks for enlightening me!

  7. subbuDecember 12, 2007 @ 06:47 AM

    I tried out this example. But I get an error saying “Table ‘eventplanner_development.events’ doesn’t exist” when I try to add a record to either Ceremony or Party table. I have an abstract class like: class Event < ActiveRecord::Base self.abstract_class = true belongs_to :user validates_presence_of :name end

    and then my usual models like: class Ceremony < Event end

    class Party < Event end

    Am I missing something here?

  8. subbuDecember 12, 2007 @ 06:49 AM

    oops..something went wrong with my earlier comment. Hope its readable.

  9. PratikDecember 12, 2007 @ 12:24 PM

    Subbu : Well, you’re certainly missing something somewhere ! Restart the server ;-)

  10. TomDecember 12, 2007 @ 02:02 PM

    Without model namespaces, you have to think up (potentially counterintuitive) distinct names for every model in your application. For larger applications this really becomes a problem, especially when you also have to avoid all of the Ruby core classes that also live in the default namespace.

  11. PratikDecember 12, 2007 @ 03:09 PM

    Tom : So my argument against that is, you’ll surely have an associated table name with the model. E.g. If your model is called Item::Dvd, you’ll have a table called item_dvds, then why can’t you call your model ItemDvd ?

    I’ll just copy paste my comment from Mike’s blog

    By that post about namespaced models, I just meant to point out invalid reasons why most of the people use them. There still might be some cases ( which I haven’t really come across yet, so don’t know about them ) where namespacing model might be a good choice.

  12. TomDecember 12, 2007 @ 05:38 PM

    You’re right, you could just fake the effect of namespaces by prefixing all your model names instead, but… Ruby has namespaces! It’s extra effort and overhead to do all that prefixing, and you shouldn’t have to fake it if the language supports it. It’s Rails that’s causing the problem, not the people who are trying to use a good language feature for its intended purpose.

    I agree that namespaced models are a world of pain and probably not worth the hassle, and I pretty much agree that your suggested workarounds are the best way to go at the moment, but I also think that namespaces do have a genuine use (which is why Ruby provides them) and that fundamentally it’s a shame that Rails doesn’t pay proper attention to them.

    Roll on 3.0! :-)

  13. RiteshDecember 13, 2007 @ 11:46 AM

    Definitely, rails sucks when it comes to namespaces … especially in handling Item::Dvd and ItemDvd, where both map to item_dvd … its ambiguous. Whereas, logical separation is required as one of my projects has more than 40 models and I have to divide them into logical groups for convenience.

    @Pratik, using abstract class is a good technique for Pet {Dog, Cat, Rabbit} example, and it works because Dog, Cat and Rabbit are of type Pet. Its a solid alternative to STI.

    But this will not work when, for example, a company would like to divide its tables into groups of HR, Finance, Sales, etc. and there is no inheritance involved. Such cases do require a namespace.

    Also … I think @Tom has genuine arguments.

  14. AdityaDecember 28, 2007 @ 03:16 PM

    Pratik,

    I’m trying to get my head around STI. I think I have a valid use for it. I want to have cusomers of type Corporate or Individual. Each customer type stores certain attributes pertinent to its type. Each Customer type would end up having its own slightly different views. However there are a number of operations i just want to do on customers. For example i want to show a list of customers with just their basic information. The rest of the system doesnt really care much more about the type. For example a customer can have many “cases”. The case doesnt really care much for the type of the customer. It just belongs to a customer irrespective of the type.

    Sorry for being verbose. But i hope you get the gist. How to go about the best possible way in Rails?

    Thanks mate!

  15. Anthony GreenJanuary 04, 2008 @ 11:19 AM

    We have a Rails project that requires communicating with two databases. The main data repository is a remote db over which we have no control and a local one with extended informtaion. The application we are building serves as glue between the two.

    We’ve namespaced the models and put those relating to the remote database in the vendor plugins folder. Part of the reason was to allow portablilty of the code that deals with the remote database and make it avialable to other teams building different applications.

  16. Norman ClarkeJanuary 11, 2008 @ 08:57 PM

    I’m developing a plugin right now that needs a model. In this case I think it would be handy to have good support for namespaced models so that I don’t have to worry about conflicts with models in the application my plugin gets installed to. Then I could still pick simple and intuitive model names in my plugin.

    Since the namespaced model support seems to be a little flaky, right now my only viable options seems to be picking unusual names for the model, or making the developer generate the model and let him or her pick a name. Both options are OK, but I personally find them a little less elegant than a namespace.

  17. Yi WenMarch 26, 2008 @ 03:56 AM

    The main reason I like namespaced models is because the reason Eric Evans put in his Domain-Driven book. And I am going to quote it below:

    Choose MODULES that tell the story of the system and contain a cohesive set of concepts. This often yields low coupling between MODULES, but if it doesn’t, look for a way to change the model to disentangle the concepts, or search for an overlooked concept that might be the basis of a MODULE that would bring the elements together in a meaningful way. Seek low coupling in the sense of concepts that can be understood and reasoned about independently of each other. Refine the model until it partitions according to highlevel domain concepts and the corresponding code is decoupled as well. Give the MODULES names that become part of the UBIQUITOUS LANGUAGE. MODULES and their names should reflect insight into the domain.
  18. AndresMarch 27, 2008 @ 08:31 PM

    you sure your scope is complete in you opinion?

    It’s more like black and white to me here, You either namespace controller and model or neither.

    Why would you namespace just the models and not the controllers?

    Plus, As soon you put a controller inside a namespace everything else at the model expects to be namespaced. so adding directories to the load_path doesn’t help.

    I believe fully support for one nesting level might be a blessing for many,

    I would love to know your opinion on namespacing both controllers and models, or neither.

  19. DanimalMarch 30, 2008 @ 05:19 AM

    Pratik,

    I used your idea of grouping my similar models into subdirectories, simply for “cleanliness” as opposed to namespacing (which I agree is usually a bad idea for models as models would rarely overlap in name). Turns out that I had about 6 subdirectories of models (went a little hog-wild) so rather than write

    config.load_paths << "#{RAILS_ROOT}/app/models/XYZ"

    six times, I DRY’d it up. Woo!

    Dir.glob(”#{RAILS_ROOT}/app/models/*[^.rb]”).each{|dir| config.load_paths << dir }

    Enjoy!

    -Dan

    P.S. I didn’t know about the abstract class, but I had a situation that would have been perfect for it… I ended up making it work using “set_table_name” for each child class, but I now see that “abstract_class” would be cleaner as its only set on the parent.

Comment