A Journey On Rails

Namespaced models and controllers

Posted in code organization, Rails by Vikram Venkatesan on January 27, 2010

Namespacing ruby classes using modules is one of the powerful features of ruby that is underused in rails. The problem is with using them in models and controllers since rails’ default conventions and configurations do not provide much support for namespaces. For instance, if you want a model for storing user settings separate from the basic user information (say, User model), you may create a model called UserSetting with an underlying table called user_settings. Here, the prefix User which we have used is nothing but the context in which the model works. A better approach in my opinion is to put the Profile and Setting models inside a namespace, say User, like User::Profile and User::Setting. That way, the redundancy in naming is solved. Now, coming to how rails supports this usage, the major areas where you may face problem are

Table naming

Rails defaults to the table name inferred from the class name of the model, excluding the namespace. So, it will assume settings to be the table name. You will have to call set_table_name to make it use user_settings, if required.

set_table_name 'user_settings'

Associations

The association class names and foreign keys will have to be specified explicitly.

belongs_to :user_setting,
           :class_name => 'User::Setting'
           :foreign_key => 'user_settings_id'

Fixtures

Name based fixture association references won’t work for namespaced models. Say, there is a belongs_to :user_profile in User::Setting model. In user_settings.yml you cannot directly use the fixture name like follows.

setting_one:
 user_profile: profile_one

You may have to use hard-coded ids and use the foreign key column name, like user_profile_id : 5. I struggled the most to get around this limitation since I could not change all my old fixtures to use hard coded id’s. Honestly, I do not know any better way to make this work and am still searching for it.

Update: Call set_fixture_class :user_settings => User::Setting to enable using named fixtures as given above.

There are many articles and blogs talking about the merits and demerits of using namespaces in rails so as to keep the code structured and clean. A few even recommend not using namespaces due the lack of good support for it.

But, from my experience with it, the advantages you get by using them overweigh it’s demerits. It’s about time rails starts supporting namespaces. You may find it hard to get it working for the first time. Once it’s into practice, you will not regret the choice.

Testing Transactions

Posted in Uncategorized by Vikram Venkatesan on January 8, 2010

If you ever ran into a situation where you were not able to test the transaction logic in your code, the reason could have been that, rails runs each test case as a transaction itself and hence you don’t see the actual effect of rollback happening for your transaction. This happens when you are using transactional_fixtures that create fixture records only once while running the test suite life time by running the test cases as transactions, thereby undoing any DML modifications, leaving the fixtures intact. The default test_helper.rb generated by rails will have the following lines of code

# The only drawback to using transactional fixtures is when you actually
# need to test transactions.  Since your test is bracketed by a transaction,
# any transactions started in your code will be automatically rolled back.
self.use_transactional_fixtures = true

You can switch off trasactional fixtures for all tests by calling self.use_transactional_fixtures = false in your test_helper or in your test class. But, that will result in way too slow tests. How about disabling it only for the specific test case alone? From what i know, there isn’t a way to do so.

What you can rather do is, write a separate test class for such transactional tests setting self.use_transactional_fixtures = false. Things will work fine as you expected. One word of caution though! Now that you have disabled transactions, it’s your duty to clean up any data created by the test case(s), so that the new test class can co-exist with other tests using transactional fixtures. You can either do this manually at the end of the test method(s) or override the teardown method and add the cleanup code to it.

def teardown
 # Your code for undoing the data changes goes here....
end

Happy testing!!!