从Rails2到Rails3

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

 

1. 脚本命令

旧的命令                                      新的用法

script/generate                          rails g

script/console                            rails c

script/server                              rails s

script/dbconsole                        rails db

 

2. 配置文件

rails2: config/environment.rb

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
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 中:

1
2
3
map.resources :posts do |post|  
     post.resources :comments 
end 

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

1
2
3
resources :posts do 
     resources :comments 
end

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

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

 在rails3中可以这样表达:

1
2
3
4
5
6
7
8
resources :comments do 
     member do 
         post :preview 
     end 
     collection do 
         get :archived 
     end 
end 

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

1
2
3
4
resources :comments do 
     post :preview , :on => :member 
     get :archived , :on => :collection 
end 

对于基本路由, rails2:

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

 那么在rails3中:

1
match 'login' => 'session#new'

对于具名路由, rails2:

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

在rails3中:

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

对于程序根路由, rails2:

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

rails3:

1
root :to => 'users#index' 

对于遗留路由, rails2:

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

 那么在rails3中写法更优雅:

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

对于路由参数, rals2:

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

rails3:
 

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

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

1
2
3
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中:
 

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

指定请求方式, rails2:

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

在rails3中:
 

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

对于跳转, rails3:

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

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

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

在rails3中:
 

1
match '/:year' => "posts#index" , :constraints => { :year => /\d{ 4 }/}
1
2
3
4
5
6
7
8
: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:

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

4. Bundler与ActionController

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

1
2
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

1
2
gem "haml" 
gem "chronic" , '0.2.3'

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

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

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

一般的控制器语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 
   
.... 

改进的语法:

1
2
3
4
5
6
7
8
9
10
11
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:

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

在rails3中:

1
2
3
4
5
6
7
def welcome(user, subdomain)  
     @user = user  
     @subdomain = subdomain  
     mail( :from => "[email protected]" ,  
             :to => user.email,  
            :subject => "Welcome to TestApp" )  
end 
1
UserMailer.welcome(user, subdomain).deliver 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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中, 我们经常使用下面的方法来进行查询:

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

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

 

而在rails3中:

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

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

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

对于命名范围, 在rails2中:

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


而在rails3中:

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

对于查找方法, rails2:
 

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


在rails3:

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

7. 跨站点脚本(XSS)

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

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

你可能感兴趣的:(rails3,Rails2)