Friday, 20 February 2009

Moving to Rails 2.2.2

Having just made the move from 2.1.2 to 2.2.2, I hought I wold share some of the issues I came across. There was a bug in 2.1.2 which meant that the image_tag method appended a dot to the end of the image filename, which has been resolved, so that is a good start.

The way you use partials has been updated in Rails 2.2. While the changes are certainly an improvement, modifying code is not fun (I had about 50 changes to make; thank goodness functional testing will catch your mistakes).

The basic change is that render_partial and render_partial_collection now accept a single hash, rather than a string indicating the file and the important variable, and can be accessed through the render method (thanks to mcclelland for pointing that out). The hash should have the file mapped to the :partial key, and any variables you want in your partial go in a hash mapped to the :locals key (so immediately we can see this is an improvement; you can pass as many of these as you want). These are accessed like local variables in your partial (but really as method calls, I assume). Class variables can be accessed in your partial just as they can in normal views.

To render a collection, just map it to :collection. You can also map any HTML code you want between the elements of your collection to the :spacer key. Rails works out that if :partial is present, it has to render a partial, and if :collection is present, it has to render that partial as a collection, so I would use render for everything (and personally, I would have removed or made private render_partial and render_partial_collection given that we all have to change our code anyway).

Let us see how it has changed. In this first example, a single variable is passed, which I have called :list (so I do not have to change the partial itself):
# Old version
<%= render_partial 'list', @events %>

# New version
<%= render :partial => 'list', :locals => { :list => @events } %>

Here is a table that uses two partials. The first, for the headings, requires no variables. The second is passed an array mapped to :collection:
# Old version
<table width="100%">
<%= render_partial 'table_headings', nil %>
<%= render_partial_collection 'table_row', @samples %>

# New version
<table width="100%">
<%= render :partial => 'table_headings' %>
<%= render :partial => 'table_row', :collection => @samples %>

Error pages
There are three error pages in the public directory, which Rails uses when something goes wrong in the production environment (rather than giving error details). I think these were passed through ERb previously. This is no longer the case.

When running tests, if the system encounters an error, it just gives up, instead of noting the error, and moving on to the next test. I strongly suspect this is a bug.

If a user tries to do something when logged in, but without the required role, he gets redirected to the root page. In the controller, I specify a controller and an action. Previously, I could specify that in the redirect too, but not now. It took some seaching to find that I needed this:
assert_redirected_to ""

Another change in that Rails now generates tests in a different form:
#Old format
def test_my_method

#New format
test "my method" do

The old format still works - no need to change all your old tests. Rather than creating a whole new set of methods, you are now invoking the test method... which then defines a new method by prepending test_ to the name, and then doing exactly the same as it did previously.

Tomcat problem
Having got everything working fine in the development environment, and all tests passing, I was confident to move to the production environmemt...
19-Feb-2009 14:40:58 org.apache.catalina.core.ApplicationContext log
SEVERE: unable to create shared application instance
org.jruby.rack.RackInitializationException: undefined method `cache_template_loading=' for ActionView::Base:Class
19-Feb-2009 11:23:00 org.apache.catalina.core.ApplicationContext log
SEVERE: Exception caught
at org.jruby.rack.DefaultRackDispatcher.process(
at org.jruby.rack.RackFilter.doFilter(

Oh dear. After much searching (and updating JRuby from 1.1.4 to 1.1.6), I found this forum thread. The solution was to remove the following line from config/environment/production.rb (may also be in config/environment.rb):
config.action_view.cache_template_loading = true

A has_one problem
An obscure bug I came across for the has_one/belongs_to relationship. When I attempted to access one model from the other, I got a NoMethodError exception, and a complaint that I was tryong to do nil.include?. My has_many/belongs_to relationships worked fine. The problem was related to setting the time zone. I have no idea how that can be, but I was not the only one:

Anyway, the solution is to delete or comment out the old time zone configuration in config/environment.rb, and put in a new one.
  #config.time_zone = 'UTC'
config.active_record.default_timezone = :utc

Additional: Rails 2.3.2 has been out for a while, and I have tried using that. The only noticeable difference was application.rb was renamed to application_controller.rb. Right up until I tried to deploy on Tomcat and it all stopped working. There is a word around here, but I have a suspicion this assumes you have created your project with a more recent version of Rails than I did. Anyway, it did not work for me, so I am back on 2.2.2.

Further addition: Going back to 2.2.2 caused my unit tests to fail mysteriously, with rake just giving up (though functionals and integration tests were fine, and each unit test on its own was fine). It turned out that new models I had added with 2.3.2 had an additional test in test/units/helpers, and this was upsetting the rake task (possibly because I had deleted the associated helper files).

Struggling with Ruby: Contents Page


Unknown said...

You should probably just use "render :partial => 'blah'"

"render_partial :partial" just looks dumb.

F2Andy said...

I read the API that said use "render", but when I tried it, it got into a recursive loop. Sounds like your experience is dufferent. I will give it another go when I get the chance.

F2Andy said...

Okay, I have checked it now, and you are, of course, right. I had a play around and could not recreate the recursive loop I got first time around, so not sure what was happening there. I have updated my post; thanks for alerting me.

Julian said...

Awesome thanks for this post, it's what makes Rails POSSIBLE.

I'm sure there are reasons for the changes, but it's kinda sad that a point release of the framework breaks what must be tens if not hundreds of thousands of lines of code around the world.

143 references to assert_redirected_to with the abstract route format we had. Just seems wrong to have 'bare' URLs anywhere outside of routes.rb.