Now, we found one of the users in our database and associated the new role with it.
>> admin.reload
=> …
>> admin.user
=> …
>> admin.company
=> #<Company id: 1, name: “Acme LLC”, created_at: “2008-05-26 00:45:43”, updated_at: “2008-05-26 00:45:43”>
>> user.company
=> #<Company id: 1, name: “Acme LLC”, created_at: “2008-05-26 00:45:43”, updated_at: “2008-05-26 00:45:43”>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
Now comes the interesting bit. First we reloaded the role just to be safe. We can see that it is properly associated with the user and the new company.
But the interesting part is that the user is directly associated with only one Company through its Role. The part to be careful about is that this user can actually "has many" other roles. That's the the has_one class method supports most of the finder's options including :conditions and, in the above example, :order. It will always limit the result set to 1 and get the first row if there are multiple, so beware possible business errors if you don't add enough tests to this.
h2. ActiveResource Enhancements
We've been exercising in the 'blog' project that we started in the "Rolling with Rails 2":/2007/12/12/rolling-with-rails-2-0-the-first-full-tutorial tutorial. But there is another very small one we create on "part 2":/2007/12/12/rolling-with-rails-2-0-the-first-full-tutorial-part-2 called 'blog_remote'. Now we are going to use this one to show some new features of ActiveResource.
First of all, if you got until here, you are in the 'blog' project directory. Start mongrel there with the usual './script/server'. This should load up in the default port 3000 (if you didn't change your environment).
From another terminal, get to the 'blog_remote' project where we will exercise some more Rails 2.1:
<macro:code>
cd ../blog_remote
cp -R ../blog/vendor/rails vendor/rails
|
Because the ‘blog_remote’ uses Rails 2, we have to freeze Edge Rails as well. This will not be necessary after you update your system-wide Rails gems when they are released, but for now, this would do.
You will remember that we have an app/models/post.rb like this one:
1
2
3
|
class Post < ActiveResource::Base
self.site = 'http://akita:akita@localhost:3000/admin'
end
|
It served to highlight the HTTP authentication that comes bundled with Rails 2.0. But if you’re like me, you probably didn’t like that much the fact that you had to concatenate the username and password along with the URL. There has been alternatives to that, but on Rails 2.1 we can now finally do:
1
2
3
4
5
6
|
class Post < ActiveResource::Base
self.site = 'http://localhost:3000/admin'
self.user = 'akita'
self.password = 'akita'
self.timeout = 5 # this is IMPORTANT!
end
|
This should make things easier to maintain. But the most interesting bit is the #timeout configuration. This is very important because the default HTTP timeout is 60 seconds. So, if for some reason the remote application is unavailable, your app would wait for as much as 1 full minute before raising an exception.
But you can imagine that this really doesn’t scale, because it would quickly lock every Rails instance you have for a minute. Benchmark your remote queries, make them small and try to make the smallest timeout possible for your app. More information here and, of course, we recommend that you do remote calls as little as possible in a publicly facing website. Cache what you can instead of going out all the time for every request.
To test if everything is running (assuming you already have the ‘blog’ project running), from ‘blog_remote’, open ‘script/console’:
>> Post.find(:all)
=> […]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
You should see the objects coming down the pipe, from the 'remote' blog project. Refer to my Rails 2.0 tutorial to understand how it was assembled.
h2. Even More
There are hundreds of changes in Rails 2.1, several enhancements. One particular thing I like is the optimization on routing. Oleg Andreev has the "scoop":http://www.novemberain.com/2008/1/17/routes-recognition. This should be pretty transparent for us.
Lot's of ActiveRecord tests were cleaned up by John Barnette, Documentation received a serious revamp thanks to Pratik Naik. ActiveRecord's attributes are now less expensive, and so on and so forth.
"Redemtion in a Blog":http://blog.codefront.net/2008/02/06/living-on-the-edge-of-rails-6-better-performance-git-support-and-more/ has also been following every single change in Edge and I'd like to borrow some of the most interesting tidbits that they reported.
One interesting tidbit is resource aliasing. Now you can do this in your config/routes.rb:
--- ruby
map.resources :comments, :as => 'comentarios'
|
That way http://your_site/comentarios should behave the same as http://your_site/comments. This is probably a good thing for SEO oriented web developers. You can find even more resource aliasing goodies in Carlos Brando’s plugin Custom Resource Name which should work in older versions of Rails. And speaking of mappings, now you can also do this:
1
2
|
map.new_session :controller => 'sessions', :action => 'new'
map.root :new_session
|
So ‘map.root’ now accepts a symbol as an argument to define the root path.
Ryan Bates complained (and made a fix) for this particular fields_for usage:
1
2
3
4
|
<% fields_for "project[task_attributes][]", task do |f| %>
<%= f.text_field :name, :index => nil %>
<%= f.hidden_field :id, :index => nil %>
<% end %>
|
Notice the :index => nil that makes it look ‘dirty’. Now you can simply do this:
1
2
3
4
|
<% fields_for "project[task_attributes][]", task, :index => nil do |f| %>
<%= f.text_field :name %>
<%= f.hidden_field :id %>
<% end %>
|
And we have another handy shortcut in views. Instead of doing this:
1
2
3
|
<% form_for(:person) do |f| %>
<%= render :partial => 'form', :locals => { :form => f } %>
<% end %>
|
We can now do a shorter version:
1
2
3
|
<% form_for(:person) do |f| %>
<%= render :partial => f %>
<% end %>
|
Which is significantly more readable and maintainable than the older version. Both should keep working in Rails 2.1, though.
Then, for those projects that makes intensive use of file downloading, we now have a better way to handle big file transfer without locking down a whole Mongrel instance:
1
|
send_file '/path/to.png', :x_sendfile => true, :type => 'image/png'
|
The old ‘send_file’ method now accepts the :x_sendfile header configuration. For what I understood, it instructs web servers such as Apache and LightTPD to handle the file transfer while leaving the Rails instance alone to handle other requests. This technique has been explained by John Guenin before, and it required you to install a plugin. But in Rails 2.1 this is now built-in as well.
Conclusion
Rails 2.1 brings lots of enhancements and optimizations that are very welcome. If you’re already using Rails 2, this upgrade should work almost out of the box. Again, if you’re at Rails 1.2 or below, there is not reason to not try this upgrade.
Instead of asking “Should/Can I upgrade my projects to Rails 2.1?” you should actually ask youself “Do I have full and throughly complete tests?” If you do, fine, you should be able to easily upgrade and see what your tests tell you. If there are only a small number of bugs, you should be able to fix them in a day or less. But if you don’t have full test coverage, first you should invest in making tests that covers everything, and only then think about upgrading.
The performance optimizations alone should be enough to justify upgrading to Rails 2.1.