(This article by Gautam Rege is based on a talk he gave at Techweekend #8. It was first published on the Josh Software blog, and is reproduced here with permission.)
This is NOT a post about differences between Rails 2.x and Rails 3 but these are some musings about what I love in Rails. A lot of goodies come ‘in the box’ (I hate saying out-of-the-box) with Rails3 and some of them have been there since early version of Rails but somehow less frequently used or talked about. I spoke about this at Techweekend #8 and the presentation is here.
Bundler
Ever had a production system crash one day – without any code deployment or even anyone logging in. After the initial ‘its not me’ excuses, one system administrator says ‘Hey, I had updated the system libraries’. Having been burnt already before (hopefully), you check on the system and find that some system library has been upgraded and some gems have been upgraded and that is causing incompatibility between other gems etc. We had the case where rack (1.0.1) was upgraded to rack (1.1) causing incompatibility with the Rails gem we were running! The fix is simple — upgrade or downgrade your gems or libraries and you’re on your way. A few days later, another developer needs to deploy a simple sinatra application. He takes the latest version which requires rack > 1.1 and it automatically upgrades the gem. Boom! Your Rails app crashed again.
Did I hear you freeze the gems? Nah – not a good approach, as it causes your application deployment bundle to be huge and ‘frozen’. Every application you use would require to freeze gems and this does not really solve your problem.
Bundler (by Yahuda and Carl) built this awesome gem which is now the de-facto standard for any Rails application. In fact, it was so cool, its not Rails 2.x compatible and very highly recommended. You can now specify your dependencies in a Gemfile and prevent any clashes with any other gem versions and their dependencies. Since the gems are installed in the system default location (not frozen in your app), it means it us re-usable and version friendly!
source "http://rubygems.org"
gem "haml" # the latest version
gem "rack", "~>1.1" # v1.1 or greater
gem "nokogiri", :git => "git://github.com/tenderlove/nokogiri.git"
gem "splat", :path => "~/Code/splat" # local code base
group :test do # only in test environment
gem "rspec", :require => "spec"
end
UJS
Unobtrusive Java Script has been around for ages now but Rails lingered with prototype.js. Now, with the awesome features of JQuery, we can easily use UJS to solve some common and nagging problems:
- Avoid submit if button clicked twice!
- Make non-get requests from Hyperlinks!
- Submit form data via Ajax
Add :remote => true to hyperlink, forms and other elements. This adds the data-remote=true in your html properties. The ‘live’ JQuery function binding kicks in and sets up the events for various elements. Simple and awesome – this is available here.
XSS
Cross site scripting has been a pain to handle for a long time. Rails does this under covers – you dont event need to know too many details:
protect_from_forgery is automatically added during basic rails project creation. This ensures that every form created by the application has an authenticity_token as a hidden data field. During a post request, this is verified and thus ensures that the source of the form creation is the same server – this avoid session stealing where a malicious form is posted to your server using an authenticated user’s session!
While using UJS, you need to add csrf_meta_tag in your layout to avoid silent Ajax errors.
SQL injection is cleanly avoided with new where syntax:
# Wrong
where("user_name = '#{user_name}' AND "password = '#{password}'").first
# Correct
where("user_name = ? AND password = ?", user_name, password).first
# Correct and clean
where(:user_name => user_name, :password => password).first
In Rails3, all html spewed out is HTML SAFE! So, you cannot leave gaps for non-HTML safe code, even by mistake! If indeed you do trust the source, you can use the ‘raw’ method to spew out the HTML as is.
Rails Eager Loading
The N+1 query problem is fairly common and largely ignored until you hit serious performance issues. Straight out of the Rails guide, consider the case
clients = Client.all.limit(10)
clients.each do |client|
puts client.address.postcode
end
There are 11 queries fired here. Using the :includes construct, Rails does eager loading like this:
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
Here only 2 queries are fired as Rails includes the address relationship too while fetching the client objects.
Transliteration / Babosa
What happens to your permalinks if a user enters the information in Arabic? We faced exactly this issue and were asked by our client to prevent input which is not English. Woh! ActiveSuppprt in Rails3 addresses a lot of these transliteration issues:
"Jürgen Müller".to_slug.transliterate.to_s #=> "Jurgen Muller"
Performance using Identity Map
The awesomeness of Rails progression – As of this inclusion the Identity Map pattern is now part of Rails 3 caching mechanism. An identity map is a design pattern used to improve performance by providing a in-memory cache to prevent duplicate retrieval of the same object data from the database, in context of the same request or thread.
Optimistic Locking
A really old concept which has been there since REALLY early versions of Rails. This is commonly overlooked but is critically important when it comes to concurrent request processing. By adding a ‘lock_version’ field in the table, Rails automatically kicks into optimistic locking mode and prevents concurrent writes when the data is stale. The StaleObjectError is raised incase the lock_version is not the same as when it was read.
Named Scopes
This is almost cliched now 🙂 Mames scopes were added since Rails 2.1. Its one of the things I love about Rails. The scopes are chained together and the query is fired only when the data is really needed. This is excellent for report filters! Adding new filters is a breeze as its only one of the scopes to be chained. Remember that scopes do not return an Array but an association object like has_many. That is how they can be chained to other scopes.
I’m pretty sure I have missed some things here. Do comment on what features you like best about Rails3! 😉