从Rails2到Rails3

文章出处:http://shuaigelingfei.iteye.com/blog/1850238

 

博文转发,方便学习。如有不当之处,请告知作者

 

我们来看下rails3相比rails2, 进步在哪里, 优势又在什么地方.

 

 

 

1. 脚本命令

 

旧的命令                                      新的用法

 

script/generate                          rails g

 

script/console                            rails c

 

script/server                              rails s

 

script/dbconsole                        rails db

 

 

 

2. 配置文件

 

rails2: config/environment.rb

 

 

Rails::Initializer.run do |config|  
    config.load_paths += %W( #{RAILS_ROOT}/extras )  
    config.gem "bj" 
    config.gem "sqlite3-ruby", :lib => "sqlite3" 
    config.gem "aws-s3", :lib => "aws/s3" 
   config.plugins = [ :exception_notification ]  
   config.time_zone = 'UTC' 
end

 rails3:config/application.rb

 

 

module APP_NAME  
    class Application < Rails::Application  
        config.load_paths += %W( #{RAILS_ROOT}/extras )  
        config.plugins = [ :exception_notification ]  
        config.time_zone = 'UTC' 
    end 
end  

 这样就变成了一种架构式的应用, 我们可以根据方便的对config进行操作

 

 

3. 路由

 

在rails3中, 已经的路由可以继续工作, 而新的路由方式更加简洁.

 

在 rails2 中:

 

map.resources :posts do |post|  
    post.resources :comments 
end 

 而在rails3中, 表达更为形象:

 

 

resources :posts do 
    resources :comments 
end

 对于一些复杂的路由, rails2:

 

 

post.resources :comments,  
                 :member => { :preview => :post },  
                :collection => { :archived => :get }

 在rails3中可以这样表达:

 

 

resources :comments do 
    member do 
        post :preview 
    end 
    collection do 
        get :archived 
    end 
end 

 不够简洁? 我们还可以这样做:

 

 

resources :comments do 
    post :preview, :on => :member 
    get :archived, :on => :collection 
end 

 对于基本路由, rails2:

 

 

map.connect 'login', :controller => 'session', :action => 'new'

 那么在rails3中:

 

 

match 'login' => 'session#new'

 对于具名路由, rails2:

 

 

map.login 'login', :controller => 'session', :action => 'new' 

 在rails3中:

 

 

match 'login' => 'session#new', :as => :login 

 对于程序根路由, rails2:

 

 

map.root :controller => 'users', :action => 'index' 

 rails3:

 

 

root :to => 'users#index' 

 对于遗留路由, rails2:

 

 

map.connect ':controller/:action/:id' 
map.connect ':controller/:action/:id.:format' 

 那么在rails3中写法更优雅:

 

 

match ':controller(/:action(/:id(.:format)))'

 对于路由参数, rals2:

 

 

map.connect '/articles/:year/:month/:day', :controller => 'posts', :action => 'index' 

 rails3:

 

 

match '/articles/:year/:month/:day' => "posts#index"

 那么对于存档请求, 比如rails2:

 

 

map.connect '/articles/:year/:month/:day', :controller => 'posts', :action => 'index' 
map.connect '/articles/:year/:month', :controller => 'posts', :action => 'index' 
map.connect '/articles/:year', :controller => 'posts', :action => 'index' 

 在rails3中:

 

 

match '/articles(/:year(/:month(/:day)))' => "posts#index"

 指定请求方式, rails2:

 

 

map.connect '/articles/:year', :controller => 'posts', :action => 'index',  
:conditions => {:method => :get}

 在rails3中:

 

 

match '/articles/:year' => "posts#index", :via => :get 
#或者更简单的:  
get '/articles/:year' => "posts#index"

 对于跳转, rails3:

 

 

match 'signin', :to => redirect("/login")  
match 'users/:name', :to => redirect {|params| "/#{params[:name]}" }  
match 'google' => redirect('http://www.google.com/')

 路由约束: rails2中实际上使用了 :requirements 符号

 

 

map.connect '/:year', :controller => 'posts', :action => 'index',  
                                    :requirements => { :year => /\d{4}/ }

 在rails3中:

 

 

match '/:year' => "posts#index", :constraints => {:year => /\d{4}/}
:constraints => { :user_agent => /iphone/ }  
:constraints => { :ip => /192\.168\.1\.\d{1,3}/ }  
constraints(:host => /localhost/) do 
    resources :posts 
end 
constraints IpRestrictor do 
   get 'admin/accounts' => "queenbee#accounts" 
end 

 对于Rack应用, rails3:

 

 

get 'hello' => proc { |env| [200, {}, "Hello Rack"] }  
get 'rack_endpoint' => PostsController.action(:index)  
get 'rack_app' => CustomRackApp

 

 

4. Bundler与ActionController

 

一个典型的rails应用, 我们一般需要在 environment.rb 指定你的 gems:

 

config.gem "haml" 
config.gem "chronic", :version => '0.2.3' 

 

 

然后我们运行 $ rake gems:install, 该命令会取得并下载然后安装编译这些gems到你的系统RubyGems目录中.

 

之后我们运行 $ rake gems:unpack:dependencise, 把这些gem打包到你应用程序的vendor/gems目录中去.

 

 

 

这样做产生的问题:

 

1. 它直接绑定到Rails中

 

2. 没有从本质上解决依赖问题

 

3. 运行时容易发生冲突

 

 

 

在rails3中, 使用了 bundle 命令:

 

直接在你的 gemfile 中指定你的 gem

 

gem "haml" 
gem "chronic", '0.2.3'

 

 

然后运行 $ bundle, 该命令会会取得并下载然后安装编译这些gems

 

然后运行 $ bundle package 把gem源移到/vendor/cache中去.

 

这样rails应用中的gem与系统中的gem就不会相冲突.

 

一般的控制器语法:

 

class UsersController < ApplicationController  
    def index  
       @users = User.all  
        respond_to do |format|  
            format.html  
            format.xml { render :xml => @users.to_xml }   
        end 
    end 
  
    def show  
       @user = User.find(params[:id])  
        respond_to do |format|  
            format.html # show.html.erb  
            format.xml { render :xml => @user }  
        end 
    end 

 改进的语法:

 

 

class UsersController < ApplicationController  
    respond_to :html, :xml, :json 
    def index  
        @users = User.all  
       respond_with(@users)  
    end 
   def show  
       @user = User.find(params[:id])  
        respond_with(@user)  
    end 
....

 

 

5. ActionMailer

 

rails2:  $ script/generate mailer UserMailer welcome forgot_password

 

这将创建 app/models/user_mailer.rb

 

那么在rails3中: $ rails g mailer UserMailer welcome forgot_password

 

这将创建 app/mailers/user_mailer.rb

 

 

 

在实现部分, rails2:

 

def welcome(user, subdomain)  
    subject 'Welcome to TestApp' 
   recipients user.email  
    from '[email protected]' 
    body :user => user, :subdomain => subdomain  
end 
UserMailer.deliver_welcome(user, subdomain)

 在rails3中:

def welcome(user, subdomain)  
    @user = user  
    @subdomain = subdomain  
    mail(:from => "[email protected]",  
            :to => user.email,  
           :subject => "Welcome to TestApp")  
end 
UserMailer.welcome(user, subdomain).deliver 

 相比rails2, 我们在rails3下实现一个mail要简单的多:

class UserMailer < ActionMailer::Base  
    default :from => "[email protected]",  
                :reply_to => "[email protected]",  
               "X-Time-Code" => Time.now.to_i.to_s  
    def welcome(user, subdomain)  
        @user = user  
        @subdomain = subdomain  
        attachments['test.pdf'] = File.read("#{Rails.root}/public/test.pdf")  
        mail(:to => @user.email, :subject => "Welcome to TestApp") do |format|  
            format.html { render 'other_html_welcome' }  
            format.text { render 'other_text_welcome' }  
        end 
    end 
end  

 

6. ActiveRelation 以及 ActiveModel

在rails2中, 我们经常使用下面的方法来进行查询:

@posts = Post.find(:all, :conditions => {:published => true}) 

 

该方式将立即查询数据库然后返回Posts数组

 

而在rails3中:

@posts = Post.where(:published => true)

 该方法不会查询数据库, 仅仅返回一个 ActiveRecord::Relation 对象, 然后:

@posts = Post.where(:published => true)  
if params[:order]  
   @posts = @posts.order(params[:order])  
end 
@posts.each do |p|  
    ...                 #在这里进行查询, 实现延迟加载  
end 

 对于命名范围, 在rails2中:

class Post < ActiveRecord::Base  
    default_scope :order => 'title' 
    named_scope :published, :conditions => {:published => true}  
    named_scope :unpublished, :conditions => {:published => false}  
end 

 而在rails3中:

class Post < ActiveRecord::Base  
    default_scope order('title')  
    scope :published, where(:published => true)  
    scope :unpublished, where(:published => false)  
end 

 对于查找方法, rails2:

Post.find(:all, :conditions => {:author => "Joe"}, :includes => :comments, :order => "title", :limit => 10) 

 在rails3:

Post.find(:all, :conditions => {:author => "Joe"}, :includes => :comments,
:order => "title", :limit => 10) 

在rails 2中关联2个表时如下用: 

User.find(   
  :all,   
  :joins => :profile,   
  :conditions => ['profile.age = ?', 33])  

 在rails 3中变成如下

 
User.joins(:profile).where('profile.age = ?', 33) 

 区别在于,后者可以跟each,all,count, first

query = User.joins(:profile).where('profile.age = ?', 33)   
query.where('users.name = ?', name) unless name.nil?   
query.where('profile.email = ?', email) unless email.nil?   
query.all  

因为有了AREL的支持
https://github.com/rails/arel

最大的优势是组合和lazy loading 
 

class User
  scope :by_age, lambda do |age|
    joins(:profile).where('profile.age = ?', age) unless age.nil?
  end
  scope :by_name, lambda{ |name| where(name: name) unless name.nil? }
  scope :by_email, lambda do |email|
    joins(:profile).where('profile.email = ?', email) unless email.nil?
  end
end

User.by_age(33).by_name(params[:name]).by_email(params[:email]).all

 

当然还有重用

 

class Tag
  belongs_to :post
end

class Post
  has_many :tags
  belongs_to :user

  scope :tagged, lambda do |tag|
    joins(:tags).where('tags.name = ?', tag).group('posts.id')
  end
end

class User
  has_many :posts
end

# 标记为 'ruby-on-rails',并且是特定user的帖子
User.where('users.id = ?', 232423).posts.tagged('ruby-on-rails').count

  

7. 跨站点脚本(XSS)

在rails2中, 一般我们输入一段文本的时候, 我们往往会这样写: <%= h @post.body %>

那么在rails3中, <%= @post.body %> 默认输出的是一段safe html, 如果想输出XSS, 可以在前面加上 raw

你可能感兴趣的:(从Rails2到Rails3)