Rails 3

Now that rails 3 has entered beta phase, probably this is a good time to start learning about rails 3.

The official announcement:
Rails 3.0: Beta release

Installing rails 3
sudo gem install tzinfo builder memcache-client rack rack-test rack-mount erubis mail text-format thor bundler i18n
sudo gem install rails --pre

This is the canonical changes to rails 3:

Rails 3.0: Release Notes

This is a great introduction to rails 3:
The Path to Rails 3: Introduction
The Path to Rails 3: Approaching the upgrade
UPGRADING AN APPLICATION TO RAILS 3 — PART 1
Notes from the field upgrading to Rails 3

Rails 3 reading material:
Rails 3 Reading Material
Getting Up To Speed With Rails 3

Rails 3 compatible gem and plugin:
Rails Wiki


Apparently mongrel doesn't work with rails 3 yet, and also unicorn, the promising new kid on rails webserver battle doesn't work too:
Subject: [Rails] Edge Rails 3: Mongrel stuck in infinite loop, Unicorn missing uninitialized constant Rack::Runtime - msg#00437

So, you might want to use passenger 2.2.9 as rails webserver since it supports rails 3 already:
Phusion Passenger 2.2.9 released

Changes to rails 3 router & controller:
Revamped Routes in Rails 3
The Rails 3 Router: Rack it Up
What's New in Edge Rails: Default RESTful Rendering
What's New in Edge Rails: Set Flash in redirect_to
Generic Actions in Rails 3
Render Options in Rails 3

HTML escaping is now the default behavior:
SafeBuffers and Rails 3.0

New activerecord finder, you should really read this since a lot of finder will be deprecated:
Active Record Query Interface 3.0

New activemodel, basically this makes a model without a database table:
ActiveModel: Make Any Ruby Object Feel Like ActiveRecord

New actionmailer API:
New ActionMailer API in Rails 3.0

Edit:

Rails 3.0 Beta: 36 Links and Resources To Get You Going


Have another good link about rails 3? Feel free to comment.

ActiveRecord Multiple Parameter Finder

Building ActiveRecord finder is simple.

User.first
User.last
User.all(:conditions => ["age > ?", params[:age]])

We can even combine it with name scoping

class User < ActiveRecord::Base
  named_scope :older_than, lambda {|age|{:conditions => ["age >= ?", age]}}
end

After we make named scope, we can chain it to regular finder.

User.older_than(params[:age]).last

The problem is when we need a query based on unknown number of parameters. For example we may have built a form to search certain user with several fields, but we cannot force user to fill all the fields.

Usually the query built will be like this:

User.all(:conditions => [
  "age = :age AND address LIKE :address AND username LIKE :username",
  {:age => params[:age], :address => "%#{params[:address]}%",
   :username => "%#{params[:username]}%"}]

But what will happen if one of the parameters is blank?

The solution is build dynamic condition.

conditions = ""
value = {}
unless params[:age].blank?
  conditions += "age = :age"
  value[:age] = params[:age]
end
unless params[:address].blank?
  conditions += " AND " unless conditions.blank?
  conditions += "address LIKE :address"
  value[:address] = "%#{params[:address]}%"
end
unless params[:username].blank?
  conditions += " AND " unless conditions.blank?
  conditions += "username LIKE :username"
  value[:username] = "%#{params[:username]}%"
end
User.all(:conditions => [conditions, value])

We can even refactor that method.

conditions = []
value = {}
unless params[:age].blank?
  conditions << "age = :age"
  value[:age] = params[:age]
end
unless params[:address].blank?
  conditions << "address LIKE :address"
  value[:address] = "%#{params[:address]}%"
end
unless params[:username].blank?
  conditions << "username LIKE :username"
  value[:username] = "%#{params[:username]}%"
end
conditions = conditions.join(" AND ")
User.all(:conditions => [conditions, value])