Thursday, August 14, 2008

ActiveRecord: Can haz namespaces?

There are plenty of blog postings from has_many :through, err the blog and Pratik's blog describing how to completely avoid namespaced models. I unfortunately discovered these sites only after struggling with namespaces myself.

If namespaces were completely unsupported by ActiveRecord, I would leave it at that. Contrarily, namespaces are moderately supported, but lead first timers to the spooky edges of the Rails framework.

After giving it some thought, I determined that the best way to describe the state of namespacing in ActiveRecord is to act as a first time novice, pretending to use them for the first time!

The Requirements

I am an intern working for a company that has built their entire website with Ruby on Rails. My boss wants me to add statistics tracking similar to Google Analytics, and he wants it all to go into the Statistics:: namespace. The initial requirements include tracking the milliseconds that each HTTP request took to complete, how long users remained logged into their session, and which session each request belonged to.

This sounds like a really easy project, and I want to impress my boss by using all the best practices and conventions of Rails.

Table Name Surprise

I will start with a model that stores the length of each request. It makes sense to use a Rails script to generate Statistics::Request:

The generator conveniently created a statistics folder inside both app/models and test/unit. I am a little bit confused as to why the folder test/fixtures/statistics was created, when the actual fixture was placed in test/fixtures/statistics_requests.yml, but I will figure that out later.

The table name in the migration is statistics_requests, so I immediately assume that table names include the namespace. After migrating the database, I fire up the console to try out the model:

Ok, I'm done playing stupid. My final word is that namespaced models can be made to work, but there are some rough edges. Some final tips for you:

Don't have conflicting namespaces and class names. For example, I should not introduce a class named Statistics in the above example. Otherwise, I will end up seeing the following message: "warning: toplevel constant Statistics referenced by Statistics::Statistics".

Even when referencing other classes in the same namespace, it is best to always include the namespace prefix. Otherwise, you will run into issues with conflicting class names across different namespaces and Rails' automagical class loading.

Some things that Rails should do:

All APIs in Rails that support defining classes by Symbols should also support Strings. I was fortunate that I could pass in 'statistics/request_observer' to active_record.observers. However, other methods such as ActiveRecord::Observer.observe do not support strings.

I believe that there is enough reason to support prefixing database table names with the namespace. This is as simple as introducing a new option to ActiveRecord called 'include_namespace_in_table_name'.

I personally understand the workarounds for namespacing, but I do not believe that Rails should force us to use workarounds for this common requirement. My original goal of this blog post was to capture the difficulties that any newcomer will have with namespaces.

The patches to fix these 'bugs' are not difficult. The more difficult problem is convincing Rails core team that namespacing is a valid and common way to organize models.

Pratik - I was generally referring to the DHH quote "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." I don't think anyone is entirely against the idea, but unfavorable opinions do exist.

A while ago I was also quite frustrated with the namespaced models. I'm somewhat new to the Rails, but I decided a make a little patch that improves the support. It changes the way the tables are named and fixes the fixtures. It also make generators write fully working code (e.g. when generating scaffolds with namespaced models).