# 個性化Rails生成器與模板
Rails 生成器是提高你工作效率的有力工具。通過本章節的學習,你可以了解如何創建和個性化生成器。
通過學習本章節,你將學到:
* 如何在你的Rails應用中辨別哪些生成器是可用的;
* 如何使用模板創建一個生成器;
* Rails應用在調用生成器之前如何找到他們;
* 如何通過創建一個生成器來定制你的 scaffold ;
* 如何通過改變生成器模板定制你的scaffold ;
* 如何使用回調復用生成器;
* 如何創建一個應用模板;
### Chapters
1. [簡單介紹](#%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D-)
2. [創建你的第一個生成器](#-%E5%88%9B%E5%BB%BA%E4%BD%A0%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E7%94%9F%E6%88%90%E5%99%A8)
3. [用生成器創建生成器](#%E7%94%A8%E7%94%9F%E6%88%90%E5%99%A8%E5%88%9B%E5%BB%BA%E7%94%9F%E6%88%90%E5%99%A8)
4. [生成器查找](#%E7%94%9F%E6%88%90%E5%99%A8%E6%9F%A5%E6%89%BE)
5. [個性化你的工作流 ](#%E4%B8%AA%E6%80%A7%E5%8C%96%E4%BD%A0%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81%E3%80%80)
6. [通過修改生成器模板個性化工作流](#%E9%80%9A%E8%BF%87%E4%BF%AE%E6%94%B9%E7%94%9F%E6%88%90%E5%99%A8%E6%A8%A1%E6%9D%BF%E4%B8%AA%E6%80%A7%E5%8C%96%E5%B7%A5%E4%BD%9C%E6%B5%81)
7. [讓生成器支持備選功能](#%E8%AE%A9%E7%94%9F%E6%88%90%E5%99%A8%E6%94%AF%E6%8C%81%E5%A4%87%E9%80%89%E5%8A%9F%E8%83%BD-)
8. [應用模版](#%E5%BA%94%E7%94%A8%E6%A8%A1%E7%89%88)
9. [生成器方法](#%E7%94%9F%E6%88%90%E5%99%A8%E6%96%B9%E6%B3%95)
* [`gem`](#gem)
* [`gem_group`](#gem_group)
* [`add_source`](#add_source)
* [`inject_into_file`](#inject_into_file)
* [`gsub_file`](#gsub_file)
* [`application`](#application)
* [`git`](#git)
* [`vendor`](#vendor)
* [`lib`](#lib)
* [`rakefile`](#rakefile)
* [`initializer`](#initializer)
* [`generate`](#generate)
* [`rake`](#rake)
* [`capify!`](#capify-bang)
* [`route`](#route)
* [`readme`](#readme)
### 1 簡單介紹
當使用`rails` 命令創建一個應用的時候,實際上使用的是一個Rails生成器,創建應用之后,你可以使用`rails generate`命令獲取當前可用的生成器列表:
```
$ rails new myapp
$ cd myapp
$ bin/rails generate
```
你將會看到和Rails相關的生成器列表,如果想了解這些生成器的詳情,可以做如下操作:
```
$ bin/rails generate helper --help
```
### 2 創建你的第一個生成器
從Rails 3.0開始,生成器都是基于[Thor](https://github.com/erikhuda/thor)構建的。Thor提供了強力的解析和操作文件的功能。比如,我們想讓生成器在`config/initializers`目錄下創建一個名為`initializer.rb`的文件:
第一步可以通過`lib/generators/initializer_generator.rb`中的代碼創建一個文件:
```
class InitializerGenerator < Rails::Generators::Base
def create_initializer_file
create_file "config/initializers/initializer.rb", "# Add initialization content here"
end
end
```
提示: `Thor::Actions`提供了`create_file`方法。關于`create_file`方法的詳情可以參考[Thor's documentation](http://rdoc.info/github/erikhuda/thor/master/Thor/Actions.html)
我們創建的生成器非常簡單: 它繼承自`Rails::Generators::Base`,只包含一個方法。當一個生成器被調用時,每個在生成器內部定義的方法都會順序執行一次。最終,我們會根據程序執行環境調用`create_file`方法,在目標文件目錄下創建一個文件。 如果你很熟悉Rails應用模板API,那么你在看生成器API時,也會輕車熟路,沒什么障礙。
為了調用我們剛才創建的生成器,我們只需要做如下操作:
```
$ bin/rails generate initializer
```
我們可以通過如下代碼,了解我們剛才創建的生成器的相關信息: `bash $ bin/rails generate initializer --help`
Rails可以對一個命名空間化的生成器自動生成一個很好的描述信息。比如 `ActiveRecord::Generators::ModelGenerator`。一般而言,我們可以通過2中方式生成相關的描述。第一種是在生成器內部調用`desc`方法:
```
class InitializerGenerator < Rails::Generators::Base
desc "This generator creates an initializer file at config/initializers"
def create_initializer_file
create_file "config/initializers/initializer.rb", "# Add initialization content here"
end
end
```
現在我們可以通過`--help`選項看到剛創建的生成器的描述信息。第二種是在生成器同名的目錄下創建一個名為`USAGE`的文件存放和生成器相關的描述信息。
### 3 用生成器創建生成器
生成器本身擁有一個生成器:
```
$ bin/rails generate generator initializer
create lib/generators/initializer
create lib/generators/initializer/initializer_generator.rb
create lib/generators/initializer/USAGE
create lib/generators/initializer/templates
```
這個生成器實際上只創建了這些:
```
class InitializerGenerator < Rails::Generators::NamedBase
source_root File.expand_path("../templates", __FILE__)
end
```
首先,我們注意到生成器是繼承自`Rails::Generators::NamedBase`而非`Rails::Generators::Base`, 這意味著,我們的生成器在被調用時,至少要接收一個參數,即初始化器的名字。這樣我們才能通過代碼中的變量`name`來訪問它。
我們可以通過查看生成器的描述信息來證實(別忘了刪除舊的生成器文件):
```
$ bin/rails generate initializer --help
Usage:
rails generate initializer NAME [options]
```
我們可以看到剛才創建的生成器有一個名為`source_root`的類方法。這個方法會指定生成器模板文件的存放路徑,一般情況下,會放在 `lib/generators/initializer/templates`目錄下。
為了了解生成器模板的作用,我們在`lib/generators/initializer/templates/initializer.rb`創建該文件,并添加如下內容:
```
# Add initialization content here
```
現在,我們來為生成器添加一個拷貝方法,將模板文件拷貝到指定目錄:
```
class InitializerGenerator < Rails::Generators::NamedBase
source_root File.expand_path("../templates", __FILE__)
def copy_initializer_file
copy_file "initializer.rb", "config/initializers/#{file_name}.rb"
end
end
```
接下來,使用剛才創建的生成器:
```
$ bin/rails generate initializer core_extensions
```
我們可以看到通過生成器的模板在`config/initializers/core_extensions.rb`創建了一個名為core_extensions的初始化器。這說明 `copy_file` 方法從指定文件下拷貝了一個文件到目標文件夾。因為我們是繼承自`Rails::Generators::NamedBase`的,所以會自動生成`file_name`方法 。
這個方法將在本章節的[final section](#generator-methods)實現完整功能。
### 4 生成器查找
當你運行 `rails generate initializer core_extensions` 命令時,Rails會做如下搜索:
```
rails/generators/initializer/initializer_generator.rb
generators/initializer/initializer_generator.rb
rails/generators/initializer_generator.rb
generators/initializer_generator.rb
```
如果沒有找到,你將會看到一個錯誤信息。
提示: 上面的例子把文件放在Rails應用的`lib`文件夾下,是因為該文件夾路徑屬于`$LOAD_PATH`。
### 5 個性化你的工作流
Rails自帶的生成器為工作流的個性化提供了支持。它們可以在`config/application.rb`中進行配置:
```
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, fixture: true
end
```
在個性化我們的工作流之前,我們先看看scaffold工具會做些什么:
```
$ bin/rails generate scaffold User name:string
invoke active_record
create db/migrate/20130924151154_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
invoke resource_route
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
invoke test_unit
create test/controllers/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
create test/helpers/users_helper_test.rb
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/users.js.coffee
invoke scss
create app/assets/stylesheets/users.css.scss
invoke scss
create app/assets/stylesheets/scaffolds.css.scss
```
通過上面的內容,我們可以很容易理解Rails3.0以上版本的生成器是如何工作的。scaffold生成器幾乎不生成文件。它只是調用其他生成器去做。這樣的話,我們可以很方便的添加/替換/刪除這些被調用的生成器。比如說,scaffold生成器調用了scaffold_controller生成器(調用了erb,test_unit和helper生成器),它們每個生成器都有一個單獨的響應方法,這樣就很容易實現代碼復用。
如果我們希望scaffold 在生成工作流時不必生成樣式表,腳本文件和測試固件等文件,那么我們可以進行如下配置:
```
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, fixture: false
g.stylesheets false
g.javascripts false
end
```
如果我們使用scaffold生成器創建另外一個資源時,就會發現樣式表,腳本文件和測試固件的文件都不再創建了。如果你想更深入的進行定制,比如使用DataMapper和RSpec 替換Active Record和TestUnit ,那么只需要把相關的gem文件引入,并配置你的生成器。
為了證明這一點,我們將創建一個新的helper生成器,簡單的添加一些實例變量訪問器。首先,我們創建一個帶Rails命名空間的的生成器,因為這樣為Rails方便搜索提供了支持:
```
$ bin/rails generate generator rails/my_helper
create lib/generators/rails/my_helper
create lib/generators/rails/my_helper/my_helper_generator.rb
create lib/generators/rails/my_helper/USAGE
create lib/generators/rails/my_helper/templates
```
現在,我們可以刪除`templates`和`source_root`文件了,因為我們將不會用到它們,接下來我們在生成器中添加如下代碼:
```
# lib/generators/rails/my_helper/my_helper_generator.rb
class Rails::MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
module #{class_name}Helper
attr_reader :#{plural_name}, :#{plural_name.singularize}
end
FILE
end
end
```
我們可以使用修改過的生成器為products提供一個helper文件:
```
$ bin/rails generate my_helper products
create app/helpers/products_helper.rb
```
這將會在 `app/helpers`目錄下生成一個對應的文件:
```
module ProductsHelper
attr_reader :products, :product
end
```
這就是我們希望看到的。現在,我們可以修改`config/application.rb`,告訴scaffold使用我們的helper 生成器:
```
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, fixture: false
g.stylesheets false
g.javascripts false
g.helper :my_helper
end
```
你將在生成動作列表中看到上述方法的調用:
```
$ bin/rails generate scaffold Article body:text
[...]
invoke my_helper
create app/helpers/articles_helper.rb
```
我們注意到新的helper生成器替換了Rails默認的調用。但有一件事情卻忽略了,如何為新的生成器提供測試呢?我們可以復用原有的helpers測試生成器。
從Rails 3.0開始,簡單的實現上述功能依賴于鉤子的概念。我們新的helper方法不需要拘泥于特定的測試框架,它可以簡單的提供一個鉤子,測試框架只需要實現這個鉤子并與之一致即可。
為此,我們需要對生成器做如下修改:
```
# lib/generators/rails/my_helper/my_helper_generator.rb
class Rails::MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
module #{class_name}Helper
attr_reader :#{plural_name}, :#{plural_name.singularize}
end
FILE
end
hook_for :test_framework
end
```
現在,當helper生成器被調用時,與之匹配的測試框架是TestUnit,那么這將會調用`Rails::TestUnitGenerator`和 `TestUnit::MyHelperGenerator`。如果他們都沒有定義,我們可以告訴生成器調用`TestUnit::Generators::HelperGenerator`來替代。對于一個Rails生成器來說,我們只需要添加如下代碼:
```
# Search for :helper instead of :my_helper
hook_for :test_framework, as: :helper
```
現在,你再次運行scaffold生成器生成Rails應用時,它就會生成相關的測試了。
### 6 通過修改生成器模板個性化工作流
上一章節中,我們只是簡單的在helper生成器中添加了一行代碼,沒有添加額外的功能。有一種簡便的方法可以實現它,那就是替換模版中已經存在的生成器。比如`Rails::Generators::HelperGenerator`。
從Rails 3.0開始,生成器不只是在源目錄中查找模版,它們也會搜索其他路徑。其中一個就是`lib/templates`,如果我們想定制`Rails::Generators::HelperGenerator`,那么我們可以在`lib/templates/rails/helper`中添加一個名為`helper.rb`的文件,文件內容包含如下代碼:
```
module <%= class_name %>Helper
attr_reader :<%= plural_name %>, :<%= plural_name.singularize %>
end
```
將 `config/application.rb`中重復的內容刪除:
```
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, fixture: false
g.stylesheets false
g.javascripts false
end
```
現在生成另外一個Rails應用時,你會發現得到的結果幾乎一致。這是一個很有用的功能,如果你只想修改`edit.html.erb`, `index.html.erb`等文件的布局,那么可以在`lib/templates/erb/scaffold`中進行配置。
### 7 讓生成器支持備選功能
最后將要介紹的生成器特性對插件生成器特別有用。舉個例子,如果你想給TestUnit添加一個名為 [shoulda](https://github.com/thoughtbot/shoulda)的特性,TestUnit已經實現了所有Rails要求的生成器功能,shoulda想重用其中的部分功能,shoulda不需要重新實現這些生成器,可以告訴Rails使用`TestUnit`的生成器,如果在`Shoulda`的命名空間中沒找到的話。
我們可以通過修改`config/application.rb`的內容,很方便的實現這個功能:
```
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :shoulda, fixture: false
g.stylesheets false
g.javascripts false
# Add a fallback!
g.fallbacks[:shoulda] = :test_unit
end
```
現在,如果你使用scaffold 創建一個Comment 資源,那么你將看到shoulda生成器被調用了,但最后調用的是TestUnit的生成器方法:
```
$ bin/rails generate scaffold Comment body:text
invoke active_record
create db/migrate/20130924143118_create_comments.rb
create app/models/comment.rb
invoke shoulda
create test/models/comment_test.rb
create test/fixtures/comments.yml
invoke resource_route
route resources :comments
invoke scaffold_controller
create app/controllers/comments_controller.rb
invoke erb
create app/views/comments
create app/views/comments/index.html.erb
create app/views/comments/edit.html.erb
create app/views/comments/show.html.erb
create app/views/comments/new.html.erb
create app/views/comments/_form.html.erb
invoke shoulda
create test/controllers/comments_controller_test.rb
invoke my_helper
create app/helpers/comments_helper.rb
invoke shoulda
create test/helpers/comments_helper_test.rb
invoke jbuilder
create app/views/comments/index.json.jbuilder
create app/views/comments/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/comments.js.coffee
invoke scss
```
備選功能支持你的生成器擁有單獨的響應,可以實現代碼復用,減少重復代碼。
### 8 應用模版
現在你已經了解如何在一個應用中使用生成器,那么你知道生成器還可以生成應用嗎? 這種生成器一般是由"template"來實現的。接下來我們會簡要介紹模版API,進一步了解可以參考[Rails Application Templates guide](rails_application_templates.html)。
```
gem "rspec-rails", group: "test"
gem "cucumber-rails", group: "test"
if yes?("Would you like to install Devise?")
gem "devise"
generate "devise:install"
model_name = ask("What would you like the user model to be called? [user]")
model_name = "user" if model_name.blank?
generate "devise", model_name
end
```
上述模版在`Gemfile`聲明了 `rspec-rails` 和 `cucumber-rails`兩個gem包屬于`test`組,之后會發送一個問題給使用者,是否希望安裝Devise?如果用戶同意安裝,那么模版會將Devise添加到`Gemfile`文件中,并運行 `devise:install`命令,之后根據用戶輸入的模塊名,指定`devise`所屬模塊。
假如你想使用一個名為`template.rb`的模版文件,我們可以通過在執行 `rails new`命令時,加上 `-m` 選項來改變輸出信息:
```
$ rails new thud -m template.rb
```
上述命令將會生成`Thud` 應用,并使用模版生成輸出信息。
模版文件不一定要存儲在本地文件中, `-m`選項也支持在線模版:
```
$ rails new thud -m https://gist.github.com/radar/722911/raw/
```
本文最后的章節沒有介紹如何生成大家都熟知的模版,而是介紹在開發模版過程中會用到的方法。同樣這些方法也可以通過生成器來調用。
### 9 生成器方法
下面要介紹的方法對生成器和模版來說都是可用的。
提示: Thor中未介紹的方法可以通過訪問[Thor's documentation](http://rdoc.info/github/erikhuda/thor/master/Thor/Actions.html)做進一步了解。
#### 9.1 `gem`
聲明一個gem在Rails應用中的依賴項。
```
gem "rspec", group: "test", version: "2.1.0"
gem "devise", "1.1.5"
```
可用的選項如下:
* `:group` - 在`Gemfile`中聲明所安裝的gem包所在的分組。
* `:version` - 聲明gem的版本信息,你也可以在該方法的第二個參數中聲明。
* `:git` - gem包相關的git地址
可以在該方法參數列表的最后添加額外的信息:
```
gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"
```
上述代碼將在`Gemfile`中添加如下內容:
```
gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"
```
#### 9.2 `gem_group`
將gem包安裝到指定組中:
```
gem_group :development, :test do
gem "rspec-rails"
end
```
#### 9.3 `add_source`
為`Gemfile`文件添加指定數據源:
```
add_source "http://gems.github.com"
```
#### 9.4 `inject_into_file`
在文件中插入一段代碼:
```
inject_into_file 'name_of_file.rb', after: "#The code goes below this line. Don't forget the Line break at the end\n" do <<-'RUBY'
puts "Hello World"
RUBY
end
```
#### 9.5 `gsub_file`
替換文件中的文本:
```
gsub_file 'name_of_file.rb', 'method.to_be_replaced', 'method.the_replacing_code'
```
使用正則表達式可以更準確的匹配信息。同時可以分別使用`append_file`和 `prepend_file`方法從文件的開始處或末尾處匹配信息。
#### 9.6 `application`
在`config/application.rb`文件中的application類定義之后添加一行信息。
```
application "config.asset_host = 'http://example.com'"
```
這個方法也可以寫成一個代碼塊的方式:
```
application do
"config.asset_host = 'http://example.com'"
end
```
可用的選項如下:
* `:env` -為配置文件指定運行環境,如果你希望寫成代碼塊的方式,可以這么做:
```
application(nil, env: "development") do
"config.asset_host = 'http://localhost:3000'"
end
```
#### 9.7 `git`
運行指定的git命令:
```
git :init
git add: "."
git commit: "-m First commit!"
git add: "onefile.rb", rm: "badfile.cxx"
```
哈希值可以作為git命令的參數來使用,上述代碼中指定了多個git命令,但并不能保證這些命令按順序執行。
#### 9.8 `vendor`
查找`vendor`文件加下指定文件是否包含指定內容:
```
vendor "sekrit.rb", '#top secret stuff'
```
這個方法也可以寫成一個代碼塊 :
```
vendor "seeds.rb" do
"puts 'in your app, seeding your database'"
end
```
#### 9.9 `lib`
查找`lib`文件加下指定文件是否包含指定內容:
```
lib "special.rb", "p Rails.root"
```
這個方法也可以寫成一個代碼塊 :
```
lib "super_special.rb" do
puts "Super special!"
end
```
#### 9.10 `rakefile`
在Rails應用的 `lib/tasks`文件夾下創建一個Rake文件。
```
rakefile "test.rake", "hello there"
```
這個方法也可以寫成一個代碼塊 :
```
rakefile "test.rake" do
%Q{
task rock: :environment do
puts "Rockin'"
end
}
end
```
#### 9.11 `initializer`
在Rails應用的`config/initializers` 目錄下創建一個初始化器:
```
initializer "begin.rb", "puts 'this is the beginning'"
```
這個方法也可以寫成一個代碼塊,并返回一個字符串:
```
initializer "begin.rb" do
"puts 'this is the beginning'"
end
```
#### 9.12 `generate`
運行指定的生成器,第一個參數是生成器名字,其余的直接傳給生成器:
```
generate "scaffold", "forums title:string description:text"
```
#### 9.13 `rake`
運行指定的Rake任務:
```
rake "db:migrate"
```
可用是選項如下:
* `:env` - 聲明rake任務的執行環境。
* `:sudo` - 是否使用`sudo`命令運行rake任務,默認不使用。
#### 9.14 `capify!`
在Rails應用的根目錄下使用Capistrano運行`capify`命令,生成和Rails應用相關的Capistrano配置文件。
```
capify!
```
#### 9.15 `route`
在`config/routes.rb` 文件中添加文本:
```
route "resources :people"
```
#### 9.16 `readme`
輸出模版的`source_path`相關的內容,通常是一個README文件。
```
readme "README"
```
### 反饋
歡迎幫忙改善指南質量。
如發現任何錯誤,歡迎修正。開始貢獻前,可先行閱讀[貢獻指南:文檔](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation)。
翻譯如有錯誤,深感抱歉,歡迎 [Fork](https://github.com/ruby-china/guides/fork) 修正,或至此處[回報](https://github.com/ruby-china/guides/issues/new)。
文章可能有未完成或過時的內容。請先檢查 [Edge Guides](http://edgeguides.rubyonrails.org) 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 [Ruby on Rails 指南準則](ruby_on_rails_guides_guidelines.html)來了解行文風格。
最后,任何關于 Ruby on Rails 文檔的討論,歡迎到 [rubyonrails-docs 郵件群組](http://groups.google.com/group/rubyonrails-docs)。
- Ruby on Rails 指南 (651bba1)
- 入門
- Rails 入門
- 模型
- Active Record 基礎
- Active Record 數據庫遷移
- Active Record 數據驗證
- Active Record 回調
- Active Record 關聯
- Active Record 查詢
- 視圖
- Action View 基礎
- Rails 布局和視圖渲染
- 表單幫助方法
- 控制器
- Action Controller 簡介
- Rails 路由全解
- 深入
- Active Support 核心擴展
- Rails 國際化 API
- Action Mailer 基礎
- Active Job 基礎
- Rails 程序測試指南
- Rails 安全指南
- 調試 Rails 程序
- 設置 Rails 程序
- Rails 命令行
- Rails 緩存簡介
- Asset Pipeline
- 在 Rails 中使用 JavaScript
- 引擎入門
- Rails 應用的初始化過程
- Autoloading and Reloading Constants
- 擴展 Rails
- Rails 插件入門
- Rails on Rack
- 個性化Rails生成器與模板
- Rails應用模版
- 貢獻 Ruby on Rails
- Contributing to Ruby on Rails
- API Documentation Guidelines
- Ruby on Rails Guides Guidelines
- Ruby on Rails 維護方針
- 發布記
- A Guide for Upgrading Ruby on Rails
- Ruby on Rails 4.2 發布記
- Ruby on Rails 4.1 發布記
- Ruby on Rails 4.0 Release Notes
- Ruby on Rails 3.2 Release Notes
- Ruby on Rails 3.1 Release Notes
- Ruby on Rails 3.0 Release Notes
- Ruby on Rails 2.3 Release Notes
- Ruby on Rails 2.2 Release Notes