Rails3 changesets
    ihower@gmail.com
        2010/8/17
About Me
•              a.k.a. ihower
    •   http://ihower.tw

    •   http://twitter.com/ihower

• Rails Developer since 2006
• The Organizer of Ruby Taiwan Community
 • http://ruby.tw
 • http://rubyconf.tw
Agenda
•   Bundler
•   ActiveRecord: New Query API
•   ActiveRecord: New Validation API
•   Views: XSS, Block Helper and JavaScript
•   I18n
•   New Routing API
•   New ActionMailer
•   Metal
1. Bundler
http://ihower.tw/blog/archives/4464
Gemfile
#
gem "rails", "3.0.0.rc"

#     require                   :require
gem "sqlite3-ruby", :require => "sqlite3"

#       Git                    branch, tag    ref
gem 'authlogic', :git => 'git://github.com/odorcicd/authlogic.git',
                          :branch => 'rails3'

#
gem "rails", :path => '/Users/ihower/github/rails'

# Group
group :test do
  gem "rspec-rails", ">= 2.0.0.beta.8"
  gem "webrat"
end
2. AR Query API
AR queries (1)
                     method chaining


# Rails 2
users = User.find(:all, :conditions => { :name =>
'ihower' }, :limit => 10, :order => 'age')

# Rails 3
users = User.where(:name => 'ihower').limit(20).order('age')
AR queries (2)
      Unify finders, named_scope, with_scope to Relation
# Rails 2
users = User
users = users.some_named_scope if params[:some]
sort = params[:sort] || "id"
conditions = {}

if params[:name]
  conditions = User.merge_conditions( conditions, { :name => params[:name] } )
end

if params[:age]
  conditions = User.merge_conditions( conditions, { :age => params[:age] } )
end

find_conditions = { :conditions => conditions, :order => "#{sort} #{dir}" }
sort = params[:sort] || "id"

users = users.find(:all, :conditions => conditions, :order => sort )
AR queries (2)
Unify finders, named_scope, with_scope to Relation


# Rails   3
users =   User
users =   users.some_scope if params[:some]
users =   users.where( :name => params[:name] ) if params[:name]
users =   users.where( :age => params[:age] ) if params[:age]
users =   users.order( params[:sort] || "id" )
AR queries (3)
Using class methods instead of scopes when you need lambda
    # Rails 3
    class Product < ActiveRecord::Base

      scope :discontinued, where(:discontinued => true)
      scope :cheaper_than, lambda { |price| where("price < ?", price) }

    end

    # Rails 3, prefer this way more
    class Product < ActiveRecord::Base

      scope :discontinued, where(:discontinued => true)

      def self.cheaper_than(price)
        where("price < ?", price)
      end

    end
3. AR Validation API
AR validation (1)
# Rails 2
class User < ActiveRecord::Base
  validates_presence_of :email
  validates_uniqueness_of :email
  validates_format_of :email, :with => /^[wd]+$/ :on => :create, :message =>
"is invalid"
end

# Rails 3
class User < ActiveRecord::Base
  validates :email,
            :presence => true,
            :uniqueness => true,
            :format => { :with => /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i }
end




                                    http://asciicasts.com/episodes/211-validations-in-rails-3
AR validation (2)
                           custom validator

# Rails 3
class User < ActiveRecord::Base
  validates :email, :presence => true,
                    :uniqueness => true,
                    :email_format => true
end

class EmailFormatValidator < ActiveModel::EachValidator
  def validate_each(object, attribute, value)
    unless value =~ /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i
      object.errors[attribute] << (options[:message] || "is not formatted
properly")
    end
  end
end
4.Views
Secure XSS
Rails3 will automatically escape any string that does not
            originate from inside of Rails itself

            # Rails2
            <%=h @person.title %>

            # Rails3
            <%=@person.title %>

            # unescape string
            <%= @person.title.html_safe %>
            <%= raw @person.title %>
Unobtrusive JavaScript
  # Rails 2
  link_to_remote "Name", url_path
  # Rails 3
  link_to "Name", url_path, :remote => true

  # Rails 2
  remote_form_for @article do
  end
  # Rails 3
  form_for @article, :remote => true do
  end
You can change rails.js
  to jQuery version
   http://ihower.tw/blog/archives/3917
consistent <%= %>
  # Rails 2
  <% form_for @article do %>
  end

  <% content_for :sidebar do %>
  <% end %>

  # Rails 3
  <%= form_for @article do %>
  end

  <%= content_for :sidebar do %>
  <% end %>
consistent <%= %> (2)
   # Rails2
   <% my_helper do %>
     blah
   <% end %>

   # Rails2 Helper
   def my_helper
     concat("header")
     yield
     concat("footer")
   end

   # or
   def my_helper(&block)
       tmp = with_output_buffer(&block)
       concat("header #{tmp} footer")
   end
consistent <%= %> (3)
   # Rails3
   <%= my_helper do %>
     blah
   <% end %>

   # Rails3 Helper
   def my_helper(&block)
     tmp = with_output_buffer(&block)
     "header #{tmp} footer"
   end
5. I18n
{{ }} becomes to %{}
6. Routing API
Routes
                            nice DSL
# Rails 2
map.resources :people, :member => { :dashboard => :get,
                                    :resend => :post,
                                    :upload => :put } do |people|
    people.resource :avatra
end

# Rails 3
resources :people do
    resource :avatar
    member do
        get :dashboard
        post :resend
        put :upload
    end
end
7. ActionMailer
ActionMailer
# Rails 2
class UserMailer < ActionMailer::Base

  def signup(user)
    recipients user.email
    from 'ihower@gmail.com'
    body :name => user.name
    subject "Signup"
  end

end

UserMailer.deliver_registration_confirmation(@user)
ActionMailer
# Rails 3
class UserMailer < ActionMailer::Base

  default :from => "ihower@gmail.com"

  def signup(user)
    @name = user.name
    mail(:to => user.email, :subject => "Signup" )
  end

end

UserMailer.registration_confirmation(@user).deliver
8. Metal
Removing Metal

• Use Rack middleware
• Use Rack endpoint in the router
 • you can inherit ActionController::Metal
    to gain controller’s feature
Yehuda’s benchmark
rps
2900    Rack
2200    config.middleware.use YourMiddleware
2000    Rails Route
1900    ActionController::Metal
1070    ActionController::Base
825     ActionController::Base     render :text
765     ActionController::Base     render :template
375     ActionController::Base     Template    Layout
Thanks.
More on http://ihower.tw/blog/archives/4590
                   and
         http://ihower.tw/rails3/

Rails3 changesets

  • 1.
    Rails3 changesets ihower@gmail.com 2010/8/17
  • 2.
    About Me • a.k.a. ihower • http://ihower.tw • http://twitter.com/ihower • Rails Developer since 2006 • The Organizer of Ruby Taiwan Community • http://ruby.tw • http://rubyconf.tw
  • 3.
    Agenda • Bundler • ActiveRecord: New Query API • ActiveRecord: New Validation API • Views: XSS, Block Helper and JavaScript • I18n • New Routing API • New ActionMailer • Metal
  • 4.
  • 5.
    Gemfile # gem "rails", "3.0.0.rc" # require :require gem "sqlite3-ruby", :require => "sqlite3" # Git branch, tag ref gem 'authlogic', :git => 'git://github.com/odorcicd/authlogic.git', :branch => 'rails3' # gem "rails", :path => '/Users/ihower/github/rails' # Group group :test do gem "rspec-rails", ">= 2.0.0.beta.8" gem "webrat" end
  • 6.
  • 7.
    AR queries (1) method chaining # Rails 2 users = User.find(:all, :conditions => { :name => 'ihower' }, :limit => 10, :order => 'age') # Rails 3 users = User.where(:name => 'ihower').limit(20).order('age')
  • 8.
    AR queries (2) Unify finders, named_scope, with_scope to Relation # Rails 2 users = User users = users.some_named_scope if params[:some] sort = params[:sort] || "id" conditions = {} if params[:name] conditions = User.merge_conditions( conditions, { :name => params[:name] } ) end if params[:age] conditions = User.merge_conditions( conditions, { :age => params[:age] } ) end find_conditions = { :conditions => conditions, :order => "#{sort} #{dir}" } sort = params[:sort] || "id" users = users.find(:all, :conditions => conditions, :order => sort )
  • 9.
    AR queries (2) Unifyfinders, named_scope, with_scope to Relation # Rails 3 users = User users = users.some_scope if params[:some] users = users.where( :name => params[:name] ) if params[:name] users = users.where( :age => params[:age] ) if params[:age] users = users.order( params[:sort] || "id" )
  • 10.
    AR queries (3) Usingclass methods instead of scopes when you need lambda # Rails 3 class Product < ActiveRecord::Base scope :discontinued, where(:discontinued => true) scope :cheaper_than, lambda { |price| where("price < ?", price) } end # Rails 3, prefer this way more class Product < ActiveRecord::Base scope :discontinued, where(:discontinued => true) def self.cheaper_than(price) where("price < ?", price) end end
  • 11.
  • 12.
    AR validation (1) #Rails 2 class User < ActiveRecord::Base validates_presence_of :email validates_uniqueness_of :email validates_format_of :email, :with => /^[wd]+$/ :on => :create, :message => "is invalid" end # Rails 3 class User < ActiveRecord::Base validates :email, :presence => true, :uniqueness => true, :format => { :with => /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i } end http://asciicasts.com/episodes/211-validations-in-rails-3
  • 13.
    AR validation (2) custom validator # Rails 3 class User < ActiveRecord::Base validates :email, :presence => true, :uniqueness => true, :email_format => true end class EmailFormatValidator < ActiveModel::EachValidator def validate_each(object, attribute, value) unless value =~ /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i object.errors[attribute] << (options[:message] || "is not formatted properly") end end end
  • 14.
  • 15.
    Secure XSS Rails3 willautomatically escape any string that does not originate from inside of Rails itself # Rails2 <%=h @person.title %> # Rails3 <%=@person.title %> # unescape string <%= @person.title.html_safe %> <%= raw @person.title %>
  • 16.
    Unobtrusive JavaScript # Rails 2 link_to_remote "Name", url_path # Rails 3 link_to "Name", url_path, :remote => true # Rails 2 remote_form_for @article do end # Rails 3 form_for @article, :remote => true do end
  • 17.
    You can changerails.js to jQuery version http://ihower.tw/blog/archives/3917
  • 18.
    consistent <%= %> # Rails 2 <% form_for @article do %> end <% content_for :sidebar do %> <% end %> # Rails 3 <%= form_for @article do %> end <%= content_for :sidebar do %> <% end %>
  • 19.
    consistent <%= %>(2) # Rails2 <% my_helper do %> blah <% end %> # Rails2 Helper def my_helper concat("header") yield concat("footer") end # or def my_helper(&block) tmp = with_output_buffer(&block) concat("header #{tmp} footer") end
  • 20.
    consistent <%= %>(3) # Rails3 <%= my_helper do %> blah <% end %> # Rails3 Helper def my_helper(&block) tmp = with_output_buffer(&block) "header #{tmp} footer" end
  • 21.
  • 22.
  • 23.
  • 24.
    Routes nice DSL # Rails 2 map.resources :people, :member => { :dashboard => :get, :resend => :post, :upload => :put } do |people| people.resource :avatra end # Rails 3 resources :people do resource :avatar member do get :dashboard post :resend put :upload end end
  • 25.
  • 26.
    ActionMailer # Rails 2 classUserMailer < ActionMailer::Base def signup(user) recipients user.email from 'ihower@gmail.com' body :name => user.name subject "Signup" end end UserMailer.deliver_registration_confirmation(@user)
  • 27.
    ActionMailer # Rails 3 classUserMailer < ActionMailer::Base default :from => "ihower@gmail.com" def signup(user) @name = user.name mail(:to => user.email, :subject => "Signup" ) end end UserMailer.registration_confirmation(@user).deliver
  • 28.
  • 29.
    Removing Metal • UseRack middleware • Use Rack endpoint in the router • you can inherit ActionController::Metal to gain controller’s feature
  • 30.
    Yehuda’s benchmark rps 2900 Rack 2200 config.middleware.use YourMiddleware 2000 Rails Route 1900 ActionController::Metal 1070 ActionController::Base 825 ActionController::Base render :text 765 ActionController::Base render :template 375 ActionController::Base Template Layout
  • 31.