aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--activemodel/README85
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb2
-rw-r--r--activemodel/lib/active_model/naming.rb2
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb12
-rw-r--r--railties/guides/rails_guides/generator.rb14
-rw-r--r--railties/guides/source/3_0_release_notes.textile4
-rw-r--r--railties/guides/source/action_controller_overview.textile4
-rw-r--r--railties/guides/source/active_record_querying.textile167
-rw-r--r--railties/guides/source/active_support_core_extensions.textile140
-rw-r--r--railties/guides/source/contributing_to_rails.textile21
-rw-r--r--railties/guides/source/credits.textile.erb8
-rw-r--r--railties/guides/source/getting_started.textile495
-rw-r--r--railties/guides/source/layout.html.erb2
-rw-r--r--railties/guides/source/layouts_and_rendering.textile370
-rw-r--r--railties/guides/source/routing.textile210
-rw-r--r--railties/lib/rails/application.rb37
-rw-r--r--railties/lib/rails/configuration.rb4
-rw-r--r--railties/lib/rails/engine.rb84
-rw-r--r--railties/lib/rails/plugin.rb15
-rw-r--r--railties/lib/rails/railtie.rb171
-rw-r--r--railties/lib/rails/subscriber.rb17
24 files changed, 1168 insertions, 710 deletions
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index c2ad7e9f77..4d29a03b92 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -424,7 +424,7 @@ module ActionView
# accepts_nested_attributes_for :address, :allow_destroy => true
# end
#
- # Now, when you use a form element with the <tt>_delete</tt> parameter,
+ # Now, when you use a form element with the <tt>_destroy</tt> parameter,
# with a value that evaluates to +true+, you will destroy the associated
# model (eg. 1, '1', true, or 'true'):
#
@@ -432,7 +432,7 @@ module ActionView
# ...
# <% person_form.fields_for :address do |address_fields| %>
# ...
- # Delete: <%= address_fields.check_box :_delete %>
+ # Delete: <%= address_fields.check_box :_destroy %>
# <% end %>
# <% end %>
#
@@ -505,14 +505,14 @@ module ActionView
# end
#
# This will allow you to specify which models to destroy in the
- # attributes hash by adding a form element for the <tt>_delete</tt>
+ # attributes hash by adding a form element for the <tt>_destroy</tt>
# parameter with a value that evaluates to +true+
# (eg. 1, '1', true, or 'true'):
#
# <% form_for @person, :url => { :action => "update" } do |person_form| %>
# ...
# <% person_form.fields_for :projects do |project_fields| %>
- # Delete: <%= project_fields.check_box :_delete %>
+ # Delete: <%= project_fields.check_box :_destroy %>
# <% end %>
# <% end %>
def fields_for(record_or_name_or_array, *args, &block)
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 0ffe770f5f..21acfbbee8 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -149,7 +149,7 @@ module ActionView
# end
#
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
- # collection_select(:post, :author_id, Author.all, :id, :name_with_initial, {:prompt => true})
+ # collection_select(:post, :author_id, Author.all, :id, :name_with_initial, :prompt => true)
#
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
# <select name="post[author_id]">
diff --git a/activemodel/README b/activemodel/README
index 0d6fd1f21c..3945a6da06 100644
--- a/activemodel/README
+++ b/activemodel/README
@@ -11,9 +11,9 @@ Active Model is a solution for this problem.
Active Model provides a known set of interfaces that your objects can implement
to then present a common interface to the Action Pack helpers. You can include
functionality from the following modules:
-
+
* Adding attribute magic to your objects
-
+
Add prefixes and suffixes to defined attribute methods...
class Person
@@ -34,7 +34,7 @@ functionality from the following modules:
{Learn more}[link:classes/ActiveModel/AttributeMethods.html]
* Adding callbacks to your objects
-
+
class Person
extend ActiveModel::Callbacks
define_model_callbacks :create
@@ -50,19 +50,19 @@ functionality from the following modules:
wrap your create method.
{Learn more}[link:classes/ActiveModel/CallBacks.html]
-
+
* For classes that already look like an Active Record object
-
+
class Person
include ActiveModel::Conversion
end
...returns the class itself when sent :to_model
-
+
{Learn more}[link:classes/ActiveModel/Conversion.html]
-
+
* Tracking changes in your object
-
+
Provides all the value tracking features implemented by ActiveRecord...
person = Person.new
@@ -77,29 +77,29 @@ functionality from the following modules:
person.previous_changes # => {'name' => ['bob, 'robert']}
{Learn more}[link:classes/ActiveModel/Dirty.html]
-
+
* Adding +errors+ support to your object
-
+
Provides the error messages to allow your object to interact with Action Pack
helpers seamlessly...
class Person
-
+
def initialize
@errors = ActiveModel::Errors.new(self)
end
-
+
attr_accessor :name
attr_reader :errors
-
+
def validate!
errors.add(:name, "can not be nil") if name == nil
end
-
+
def ErrorsPerson.human_attribute_name(attr, options = {})
"Name"
end
-
+
end
... gives you...
@@ -108,18 +108,18 @@ functionality from the following modules:
# => ["Name Can not be nil"]
person.errors.full_messages
# => ["Name Can not be nil"]
-
+
{Learn more}[link:classes/ActiveModel/Errors.html]
-
+
* Testing the compliance of your object
-
+
Use ActiveModel::Lint to test the compliance of your object to the
basic ActiveModel API...
{Learn more}[link:classes/ActiveModel/Lint/Tests.html]
-
+
* Providing a human face to your object
-
+
ActiveModel::Naming provides your model with the model_name convention
and a human_name attribute...
@@ -131,19 +131,19 @@ functionality from the following modules:
NamedPerson.model_name #=> "NamedPerson"
NamedPerson.model_name.human #=> "Named person"
-
+
{Learn more}[link:classes/ActiveModel/Naming.html]
-
+
* Adding observer support to your objects
-
+
ActiveModel::Observers allows your object to implement the Observer
pattern in a Rails App and take advantage of all the standard observer
functions.
{Learn more}[link:classes/ActiveModel/Observer.html]
-
+
* Making your object serializable
-
+
ActiveModel::Serialization provides a standard interface for your object
to provide to_json or to_xml serialization...
@@ -153,48 +153,37 @@ functionality from the following modules:
s.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
{Learn more}[link:classes/ActiveModel/Serialization.html]
-
-
-* Turning your object into a finite State Machine
-
- ActiveModel::StateMachine provides a clean way to include all the methods
- you need to transform your object into a finite State Machine...
-
- light = TrafficLight.new
- light.current_state #=> :red
- light.change_color! #=> true
- light.current_state #=> :green
-
- {Learn more}[link:classes/ActiveModel/StateMachine.html]
-
+
* Integrating with Rail's internationalization (i18n) handling through
ActiveModel::Translations...
-
+
class Person
extend ActiveModel::Translation
end
{Learn more}[link:classes/ActiveModel/Translation.html]
-
+
* Providing a full Validation stack for your objects...
-
+
class Person
include ActiveModel::Validations
-
+
attr_accessor :first_name, :last_name
-
+
+
validates_each :first_name, :last_name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
end
end
-
+
+
person = Person.new(:first_name => 'zoolander')
person.valid? #=> false
-
+
{Learn more}[link:classes/ActiveModel/Validations.html]
* Make custom validators
-
+
class Person
include ActiveModel::Validations
validates_with HasNameValidator
@@ -212,5 +201,5 @@ functionality from the following modules:
p.errors.full_messages #=> ["Name must exist"]
p.name = "Bob"
p.valid? #=> true
-
+
{Learn more}[link:classes/ActiveModel/Validator.html]
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 200a6afbf0..f7fb66bdfc 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -50,7 +50,6 @@ module ActiveModel
module AttributeMethods
extend ActiveSupport::Concern
- # Declare and check for suffixed attribute methods.
module ClassMethods
# Defines an "attribute" method (like +inheritance_column+ or
# +table_name+). A new (class) method will be created with the
@@ -283,6 +282,7 @@ module ActiveModel
end
end
+ # Returns true if the attribute methods defined have been generated.
def attribute_methods_generated?
@attribute_methods_generated ||= nil
end
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 89e8f8b1ea..b9fb5fe0c8 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -1,6 +1,7 @@
require 'active_support/inflector'
module ActiveModel
+
class Name < String
attr_reader :singular, :plural, :element, :collection, :partial_path
alias_method :cache_key, :collection
@@ -57,4 +58,5 @@ module ActiveModel
@_model_name ||= ActiveModel::Name.new(self)
end
end
+
end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 86149f1e5f..a185204680 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -85,8 +85,8 @@ module ActiveModel
@options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
end
- # To replicate the behavior in ActiveRecord#attributes,
- # <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
+ # To replicate the behavior in ActiveRecord#attributes, <tt>:except</tt>
+ # takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
# for a N level model but is set for the N+1 level models,
# then because <tt>:except</tt> is set to a default value, the second
# level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index df8aefea5a..c751e29908 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,8 +1,8 @@
class Module
# Provides a delegate class method to easily expose contained objects' methods
# as your own. Pass one or more methods (specified as symbols or strings)
- # and the name of the target object as the final <tt>:to</tt> option (also a symbol
- # or string). At least one method and the <tt>:to</tt> option are required.
+ # and the name of the target object via the <tt>:to</tt> option (also a symbol
+ # or string). At least one method and the <tt>:to</tt> option are required.
#
# Delegation is particularly useful with Active Record associations:
#
@@ -72,9 +72,9 @@ class Module
# invoice.customer_name # => "John Doe"
# invoice.customer_address # => "Vimmersvej 13"
#
- # If the object to which you delegate can be nil, you may want to use the
- # :allow_nil option. In that case, it returns nil instead of raising a
- # NoMethodError exception:
+ # If the delegate object is +nil+ an exception is raised, and that happens
+ # no matter whether +nil+ responds to the delegated method. You can get a
+ # +nil+ instead with the +:allow_nil+ option.
#
# class Foo
# attr_accessor :bar
@@ -124,7 +124,7 @@ class Module
#{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
rescue NoMethodError # rescue NoMethodError
if #{to}.nil? # if client.nil?
- #{on_nil}
+ #{on_nil} # return # depends on :allow_nil
else # else
raise # raise
end # end
diff --git a/railties/guides/rails_guides/generator.rb b/railties/guides/rails_guides/generator.rb
index bd25111405..6a41bde9f6 100644
--- a/railties/guides/rails_guides/generator.rb
+++ b/railties/guides/rails_guides/generator.rb
@@ -1,11 +1,5 @@
require 'set'
-class String
- def html_safe!
- self
- end unless "post 9415935902f120a9bac0bfce7129725a0db38ed3".respond_to?(:html_safe!)
-end
-
module RailsGuides
class Generator
attr_reader :output, :view_path, :view, :guides_dir
@@ -61,7 +55,7 @@ module RailsGuides
body = set_header_section(body, @view)
body = set_index(body, @view)
- result = view.render(:layout => 'layout', :text => textile(body).html_safe!)
+ result = view.render(:layout => 'layout', :text => textile(body).html_safe)
f.write result
warn_about_broken_links(result) if ENV.key?("WARN_BROKEN_LINKS")
end
@@ -77,8 +71,8 @@ module RailsGuides
header = textile(header)
- view.content_for(:page_title) { page_title.html_safe! }
- view.content_for(:header_section) { header.html_safe! }
+ view.content_for(:page_title) { page_title.html_safe }
+ view.content_for(:header_section) { header.html_safe }
new_body
end
@@ -109,7 +103,7 @@ module RailsGuides
index << '</ol>'
index << '</div>'
- view.content_for(:index_section) { index.html_safe! }
+ view.content_for(:index_section) { index.html_safe }
i.result
end
diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile
index 5e96f8cf06..8f072df1e8 100644
--- a/railties/guides/source/3_0_release_notes.textile
+++ b/railties/guides/source/3_0_release_notes.textile
@@ -80,7 +80,7 @@ h4. Vendoring Gems
Rails now uses a +Gemfile+ in the application root to determine the gems you require for your application to start. This +Gemfile+ is processed by the "Bundler":http://github.com/carlhuda/bundler, which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
-More information: - "Using bundler":http://yehudakatz.com/2009/11/03/using-the-new-gem-bundler-today/
+More information: - "bundler README on Github":http://github.com/carlhuda/bundler
h4. Living on the Edge
@@ -478,7 +478,7 @@ A large effort was made in Active Support to make it cherry pickable, that is, y
These are the main changes in Active Support:
* Large clean up of the library removing unused methods throughout.
-* Active Support no longer provides vendored versions of "TZInfo":http://tzinfo.rubyforge.org/, "Memcache Client":http://deveiate.org/projects/RMemCache/ and "Builder":http://builder.rubyforge.org/, these are all included as dependencies and installed via the <tt>gem bundle</tt> command.
+* Active Support no longer provides vendored versions of "TZInfo":http://tzinfo.rubyforge.org/, "Memcache Client":http://deveiate.org/projects/RMemCache/ and "Builder":http://builder.rubyforge.org/, these are all included as dependencies and installed via the <tt>bundle install</tt> command.
* Safe buffers are implemented in <tt>ActiveSupport::SafeBuffer</tt>.
* Added <tt>Array.uniq_by</tt> and <tt>Array.uniq_by!</tt>.
* Fixed bug on +TimeZone.seconds_to_utc_offset+ returning wrong value.
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index 46a28da8c4..bedca59c12 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -625,9 +625,9 @@ class ClientsController < ApplicationController
# returns it. The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
- send_data(generate_pdf,
+ send_data generate_pdf(client),
:filename => "#{client.name}.pdf",
- :type => "application/pdf")
+ :type => "application/pdf"
end
private
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 302dad4f1a..4bfcea587c 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -11,6 +11,8 @@ This guide covers different ways to retrieve data from the database using Active
endprologue.
+WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails.
+
If you're used to using raw SQL to find database records then, generally, you will find that there are better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.
Code examples throughout this guide will refer to one or more of the following models:
@@ -49,7 +51,21 @@ Active Record will perform queries on the database for you and is compatible wit
h3. Retrieving Objects from the Database
-To retrieve objects from the database, Active Record provides a class method called +Model.find+. This method allows you to pass arguments into it to perform certain queries on your database without the need of writing raw SQL.
+To retrieve objects from the database, Active Record provides several finder methods. These methods allows you to pass arguments into it to perform certain queries on your database without the need of writing raw SQL.
+
+The methods are:
+* +where+
+* +select+
+* +group+
+* +order+
+* +limit+
+* +joins+
+* +includes+
+* +lock+
+* +readonly+
+* +from+
+
+All of these methods return a Relation
Primary operation of <tt>Model.find(options)</tt> can be summarized as:
@@ -64,7 +80,7 @@ Active Record lets you retrieve a single object using three different ways.
h5. Using a Primary Key
-Using <tt>Model.find(primary_key, options = nil)</tt>, you can retrieve the object corresponding to the supplied _primary key_ and matching the supplied options (if any). For example:
+Using <tt>Model.find(primary_key)</tt>, you can retrieve the object corresponding to the supplied _primary key_ and matching the supplied options (if any). For example:
<ruby>
# Find the client with primary key (id) 10.
@@ -82,7 +98,7 @@ SELECT * FROM clients WHERE (clients.id = 10)
h5. +first+
-<tt>Model.first(options = nil)</tt> finds the first record matched by the supplied options. If no +options+ are supplied, the first matching record is returned. For example:
+<tt>Model.first</tt> finds the first record matched by the supplied options. For example:
<ruby>
client = Client.first
@@ -97,11 +113,9 @@ SELECT * FROM clients LIMIT 1
<tt>Model.first</tt> returns +nil+ if no matching record is found. No exception will be raised.
-NOTE: +Model.find(:first, options)+ is equivalent to +Model.first(options)+
-
h5. +last+
-<tt>Model.last(options = nil)</tt> finds the last record matched by the supplied options. If no +options+ are supplied, the last matching record is returned. For example:
+<tt>Model.last</tt> finds the last record matched by the supplied options. For example:
<ruby>
client = Client.last
@@ -116,13 +130,11 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
<tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised.
-NOTE: +Model.find(:last, options)+ is equivalent to +Model.last(options)+
-
h4. Retrieving Multiple Objects
h5. Using Multiple Primary Keys
-<tt>Model.find(array_of_primary_key, options = nil)</tt> also accepts an array of _primary keys_. An array of all the matching records for the supplied _primary keys_ is returned. For example:
+<tt>Model.find(array_of_primary_key)</tt> also accepts an array of _primary keys_. An array of all the matching records for the supplied _primary keys_ is returned. For example:
<ruby>
# Find the clients with primary keys 1 and 10.
@@ -138,26 +150,6 @@ SELECT * FROM clients WHERE (clients.id IN (1,10))
<tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for <strong>all</strong> of the supplied primary keys.
-h5. Find all
-
-<tt>Model.all(options = nil)</tt> finds all the records matching the supplied +options+. If no +options+ are supplied, all rows from the database are returned.
-
-<ruby>
-# Find all the clients.
-clients = Client.all
-=> [#<Client id: 1, name: => "Lifo">, #<Client id: 10, name: => "Ryan">, #<Client id: 221, name: => "Russel">]
-</ruby>
-
-And the equivalent SQL is:
-
-<sql>
-SELECT * FROM clients
-</sql>
-
-<tt>Model.all</tt> returns an empty array +[]+ if no matching record is found. No exception will be raised.
-
-NOTE: +Model.find(:all, options)+ is equivalent to +Model.all(options)+
-
h4. Retrieving Multiple Objects in Batches
Sometimes you need to iterate over a large set of records. For example to send a newsletter to all users, to export some data, etc.
@@ -166,14 +158,14 @@ The following may seem very straight forward at first:
<ruby>
# Very inefficient when users table has thousands of rows.
-User.all.each do |user|
+User.each do |user|
NewsLetter.weekly_deliver(user)
end
</ruby>
But if the total number of rows in the table is very large, the above approach may vary from being under performant to just plain impossible.
-This is because +User.all+ makes Active Record fetch _the entire table_, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory.
+This is because +User.each+ makes Active Record fetch _the entire table_, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory.
h5. +find_each+
@@ -232,16 +224,16 @@ The +find+ method allows you to specify conditions to limit the records returned
h4. Pure String Conditions
-If you'd like to add conditions to your find, you could just specify them in there, just like +Client.first(:conditions => "orders_count = '2'")+. This will find all clients where the +orders_count+ field's value is 2.
+If you'd like to add conditions to your find, you could just specify them in there, just like +Client.where("orders_count = '2'")+. This will find all clients where the +orders_count+ field's value is 2.
-WARNING: Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, +Client.first(:conditions => "name LIKE '%#{params[:name]}%'")+ is not safe. See the next section for the preferred way to handle conditions using an array.
+WARNING: Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, +Client.where("name LIKE '%#{params[:name]}%'")+ is not safe. See the next section for the preferred way to handle conditions using an array.
h4. Array Conditions
Now what if that number could vary, say as an argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like:
<ruby>
-Client.first(:conditions => ["orders_count = ?", params[:orders]])
+Client.where(["orders_count = ?", params[:orders]])
</ruby>
Active Record will go through the first element in the conditions value and any additional elements will replace the question marks +(?)+ in the first element.
@@ -249,7 +241,7 @@ Active Record will go through the first element in the conditions value and any
Or if you want to specify two conditions, you can do it like:
<ruby>
-Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])
+Client.where(["orders_count = ? AND locked = ?", params[:orders], false])
</ruby>
In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with the SQL representation of +false+, which depends on the adapter.
@@ -257,13 +249,13 @@ In this example, the first question mark will be replaced with the value in +par
The reason for doing code like:
<ruby>
-Client.first(:conditions => ["orders_count = ?", params[:orders]])
+Client.where(["orders_count = ?", params[:orders]])
</ruby>
instead of:
<ruby>
-Client.first(:conditions => "orders_count = #{params[:orders]}")
+Client.where("orders_count = #{params[:orders]}")
</ruby>
is because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
@@ -275,7 +267,7 @@ h5. Placeholder Conditions
Similar to the +(?)+ replacement style of params, you can also specify keys/values hash in your array conditions:
<ruby>
-Client.all(:conditions =>
+Client.where(
["created_at >= :start_date AND created_at <= :end_date", { :start_date => params[:start_date], :end_date => params[:end_date] }])
</ruby>
@@ -286,7 +278,7 @@ h5. Range Conditions
If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the +IN+ SQL statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:
<ruby>
-Client.all(:conditions => ["created_at IN (?)",
+Client.where(["created_at IN (?)",
(params[:start_date].to_date)..(params[:end_date].to_date)])
</ruby>
@@ -308,7 +300,7 @@ h5. Time and Date Conditions
Things can get *really* messy if you pass in Time objects as it will attempt to compare your field to *every second* in that range:
<ruby>
-Client.all(:conditions => ["created_at IN (?)",
+Client.where(["created_at IN (?)",
(params[:start_date].to_date.to_time)..(params[:end_date].to_date.to_time)])
</ruby>
@@ -329,14 +321,14 @@ Where _query_ is the actual query used to get that error.
In this example it would be better to use greater-than and less-than operators in SQL, like so:
<ruby>
-Client.all(:conditions =>
+Client.where(
["created_at > ? AND created_at < ?", params[:start_date], params[:end_date]])
</ruby>
You can also use the greater-than-or-equal-to and less-than-or-equal-to like this:
<ruby>
-Client.all(:conditions =>
+Client.where(
["created_at >= ? AND created_at <= ?", params[:start_date], params[:end_date]])
</ruby>
@@ -351,13 +343,13 @@ NOTE: Only equality, range and subset checking are possible with Hash conditions
h5. Equality Conditions
<ruby>
-Client.all(:conditions => { :locked => true })
+Client.where({ :locked => true })
</ruby>
The field name does not have to be a symbol it can also be a string:
<ruby>
-Client.all(:conditions => { 'locked' => true })
+Client.where({ 'locked' => true })
</ruby>
h5. Range Conditions
@@ -365,7 +357,7 @@ h5. Range Conditions
The good thing about this is that we can pass in a range for our fields without it generating a large query as shown in the preamble of this section.
<ruby>
-Client.all(:conditions => { :created_at => (Time.now.midnight - 1.day)..Time.now.midnight})
+Client.where({ :created_at => (Time.now.midnight - 1.day)..Time.now.midnight})
</ruby>
This will find all clients created yesterday by using a +BETWEEN+ SQL statement:
@@ -381,7 +373,7 @@ h5. Subset Conditions
If you want to find records using the +IN+ expression you can pass an array to the conditions hash:
<ruby>
-Client.all(:conditions => { :orders_count => [1,3,5] })
+Client.where({ :orders_count => [1,3,5] })
</ruby>
This code will generate SQL like this:
@@ -390,22 +382,6 @@ This code will generate SQL like this:
SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
</sql>
-h3. Find Options
-
-Apart from +:conditions+, +Model.find+ takes a variety of other options via the options hash for customizing the resulting record set.
-
-<ruby>
-Model.find(id_or_array_of_ids, options_hash)
-Model.find(:last, options_hash)
-Model.find(:first, options_hash)
-
-Model.first(options_hash)
-Model.last(options_hash)
-Model.all(options_hash)
-</ruby>
-
-The following sections give a top level overview of all the possible keys for the +options_hash+.
-
h4. Ordering
To retrieve records from the database in a specific order, you can specify the +:order+ option to the +find+ call.
@@ -413,37 +389,37 @@ To retrieve records from the database in a specific order, you can specify the +
For example, if you're getting a set of records and want to order them in ascending order by the +created_at+ field in your table:
<ruby>
-Client.all(:order => "created_at")
+Client.order("created_at")
</ruby>
You could specify +ASC+ or +DESC+ as well:
<ruby>
-Client.all(:order => "created_at DESC")
+Client.order("created_at DESC")
# OR
-Client.all(:order => "created_at ASC")
+Client.order("created_at ASC")
</ruby>
Or ordering by multiple fields:
<ruby>
-Client.all(:order => "orders_count ASC, created_at DESC")
+Client.order("orders_count ASC, created_at DESC")
</ruby>
h4. Selecting Specific Fields
By default, <tt>Model.find</tt> selects all the fields from the result set using +select *+.
-To select only a subset of fields from the result set, you can specify the subset via +:select+ option on the +find+.
+To select only a subset of fields from the result set, you can specify the subset via the +select+ method.
-NOTE: If the +:select+ option is used, all the returning objects will be "read only":#readonly-objects.
+NOTE: If the +select+ method is used, all the returning objects will be "read only":#readonly-objects.
<br />
For example, to select only +viewable_by+ and +locked+ columns:
<ruby>
-Client.all(:select => "viewable_by, locked")
+Client.select("viewable_by, locked")
</ruby>
The SQL query used by this find call will be somewhat like:
@@ -463,17 +439,17 @@ Where +&lt;attribute&gt;+ is the attribute you asked for. The +id+ method will n
You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the +DISTINCT+ function you can do it like this:
<ruby>
-Client.all(:select => "DISTINCT(name)")
+Client.select("DISTINCT(name)")
</ruby>
h4. Limit and Offset
-To apply +LIMIT+ to the SQL fired by the +Model.find+, you can specify the +LIMIT+ using +:limit+ and +:offset+ options on the find.
+To apply +LIMIT+ to the SQL fired by the +Model.find+, you can specify the +LIMIT+ using +limit+ and +offset+ methods on the relation.
-If you want to limit the amount of records to a certain subset of all the records retrieved you usually use +:limit+ for this, sometimes coupled with +:offset+. Limit is the maximum number of records that will be retrieved from a query, and offset is the number of records it will start reading from from the first record of the set. For example:
+If you want to limit the amount of records to a certain subset of all the records retrieved you usually use +limit+ for this, sometimes coupled with +offset+. Limit is the maximum number of records that will be retrieved from a query, and offset is the number of records it will start reading from from the first record of the set. For example:
<ruby>
-Client.all(:limit => 5)
+Client.limit(5)
</ruby>
This code will return a maximum of 5 clients and because it specifies no offset it will return the first 5 clients in the table. The SQL it executes will look like this:
@@ -482,10 +458,10 @@ This code will return a maximum of 5 clients and because it specifies no offset
SELECT * FROM clients LIMIT 5
</sql>
-Or specifying both +:limit+ and +:offset+:
+Or chaining both +limit+ and +offset+:
<ruby>
-Client.all(:limit => 5, :offset => 5)
+Client.limit(5).offset(5)
</ruby>
This code will return a maximum of 5 clients and because it specifies an offset this time, it will return these records starting from the 5th client in the clients table. The SQL looks like:
@@ -496,12 +472,12 @@ SELECT * FROM clients LIMIT 5, 5
h4. Group
-To apply +GROUP BY+ clause to the SQL fired by the +Model.find+, you can specify the +:group+ option on the find.
+To apply +GROUP BY+ clause to the SQL fired by the finder, you can specify the +group+ method on the find.
For example, if you want to find a collection of the dates orders were created on:
<ruby>
-Order.all(:group => "date(created_at)", :order => "created_at")
+Order.group("date(created_at)").order("created_at")
</ruby>
And this will give you a single +Order+ object for each date where there are orders in the database.
@@ -519,7 +495,7 @@ SQL uses +HAVING+ clause to specify conditions on the +GROUP BY+ fields. You can
For example:
<ruby>
-Order.all(:group => "date(created_at)", :having => ["created_at > ?", 1.month.ago])
+Order.group("date(created_at)".having(["created_at > ?", 1.month.ago])
</ruby>
The SQL that would be executed would be something like this:
@@ -532,18 +508,18 @@ This will return single order objects for each day, but only for the last month.
h4. Readonly Objects
-To explicitly disallow modification/destruction of the matching records returned by +Model.find+, you could specify the +:readonly+ option as +true+ to the find call.
+To explicitly disallow modification/destruction of the matching records returned in a Relation object, you could chain the +readonly+ method as +true+ to the find call.
Any attempt to alter or destroy the readonly records will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception. To set this option, specify it like this:
<ruby>
-Client.first(:readonly => true)
+Client.first.readonly(true)
</ruby>
If you assign this record to a variable client, calling the following code will raise an +ActiveRecord::ReadOnlyRecord+ exception:
<ruby>
-client = Client.first(:readonly => true)
+client = Client.first.readonly(true)
client.locked = false
client.save
</ruby>
@@ -676,7 +652,7 @@ Now all of the following will produce the expected join queries using +INNER JOI
h5. Joining a Single Association
<ruby>
-Category.all :joins => :posts
+Category.joins(:posts)
</ruby>
This produces:
@@ -689,7 +665,7 @@ SELECT categories.* FROM categories
h5. Joining Multiple Associations
<ruby>
-Post.all :joins => [:category, :comments]
+Post.joins(:category, :comments)
</ruby>
This produces:
@@ -703,13 +679,13 @@ SELECT posts.* FROM posts
h5. Joining Nested Associations (Single Level)
<ruby>
-Post.all :joins => {:comments => :guest}
+Post.joins(:comments => :guest)
</ruby>
h5. Joining Nested Associations (Multiple Level)
<ruby>
-Category.all :joins => {:posts => [{:comments => :guest}, :tags]}
+Category.joins(:posts => [{:comments => :guest}, :tags])
</ruby>
h4. Specifying Conditions on the Joined Tables
@@ -718,14 +694,14 @@ You can specify conditions on the joined tables using the regular "Array":#array
<ruby>
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
-Client.all :joins => :orders, :conditions => {'orders.created_at' => time_range}
+Client.joins(:orders).where('orders.created_at' => time_range)
</ruby>
An alternative and cleaner syntax to this is to nest the hash conditions:
<ruby>
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
-Client.all :joins => :orders, :conditions => {:orders => {:created_at => time_range}}
+Client.joins(:orders).where(:orders => {:created_at => time_range})
</ruby>
This will find all clients who have orders that were created yesterday, again using a +BETWEEN+ SQL expression.
@@ -750,12 +726,12 @@ This code looks fine at the first sight. But the problem lies within the total n
<strong>Solution to N <plus> 1 queries problem</strong>
-Active Record lets you specify all the associations in advanced that are going to be loaded. This is possible by specifying the +:include+ option of the +Model.find+ call. By +:include+, Active Record ensures that all the specified associations are loaded using minimum possible number of queries.
+Active Record lets you specify all the associations in advanced that are going to be loaded. This is possible by specifying the +include+ method of the +Model.find+ call. By +include+, Active Record ensures that all the specified associations are loaded using minimum possible number of queries.
Revisiting the above case, we could rewrite +Client.all+ to use eager load addresses:
<ruby>
-clients = Client.all(:include => :address, :limit => 10)
+clients = Client.include(:address).limit(10)
clients.each do |client|
puts client.address.postcode
@@ -772,12 +748,12 @@ SELECT addresses.* FROM addresses
h4. Eager Loading Multiple Associations
-Active Record lets you eager load any possible number of associations with a single +Model.find+ call by using an array, hash, or a nested hash of array/hash with the +:include+ option.
+Active Record lets you eager load any possible number of associations with a single +Model.find+ call by using an array, hash, or a nested hash of array/hash with the +include+ method.
h5. Array of Multiple Associations
<ruby>
-Post.all :include => [:category, :comments]
+Post.include(:category, :comments)
</ruby>
This loads all the posts and the associated category and comments for each post.
@@ -785,14 +761,14 @@ This loads all the posts and the associated category and comments for each post.
h5. Nested Associations Hash
<ruby>
-Category.find 1, :include => {:posts => [{:comments => :guest}, :tags]}
+Category.find(1).include(:posts => [{:comments => :guest}, :tags])
</ruby>
The above code finds the category with id 1 and eager loads all the posts associated with the found category. Additionally, it will also eager load every posts' tags and comments. Every comment's guest association will get eager loaded as well.
h4. Specifying Conditions on Eager Loaded Associations
-Even though Active Record lets you specify conditions on the eager loaded associations just like +:joins+, the recommended way is to use ":joins":#joining-tables instead.
+Even though Active Record lets you specify conditions on the eager loaded associations just like +joins+, the recommended way is to use "joins":#joining-tables instead.
h3. Dynamic Finders
@@ -889,10 +865,10 @@ Which will execute:
SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan')
</sql>
-You can also use +:include+ or +:joins+ for this to do something a little more complex:
+You can also use the +include+ or +joins+ methods for this to do something a little more complex:
<ruby>
-Client.count(:conditions => "clients.first_name = 'Ryan' AND orders.status = 'received'", :include => "orders")
+Client.count.where("clients.first_name = 'Ryan' AND orders.status = 'received'").include("orders")
</ruby>
Which will execute:
@@ -957,5 +933,6 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16
+* February 3, 2010: Update to Rails 3 by "James Miller":credits.html#bensie
* February 7, 2009: Second version by "Pratik":credits.html#lifo
* December 29 2008: Initial version by "Ryan Bigg":credits.html#radar
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index 3073c3a7a5..fb4c42f118 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -496,7 +496,42 @@ The class method +delegate+
h3. Extensions to +Class+
-h4. Class Attribute Accessors
+h4. Class Attributes
+
+The method +Class#class_attribute+ declares one or more inheritable class attributes that can be overriden at any level down the hierarchy:
+
+<ruby>
+class A
+ class_attribute :x
+end
+
+class B < A; end
+
+class C < B; end
+
+A.x = :a
+B.x # => :a
+C.x # => :a
+
+B.x = :b
+A.x # => :a
+C.x # => :b
+
+C.x = :c
+A.x # => :a
+B.x # => :b
+</ruby>
+
+For example that's the way the +allow_forgery_protection+ flag is implemented for controllers:
+
+<ruby>
+class_attribute :allow_forgery_protection
+self.allow_forgery_protection = true
+</ruby>
+
+For convenience +class_attribute+ defines also a predicate, so that declaration also generates +allow_forgery_protection?+. Such predicate returns the double boolean negation of the value.
+
+NOTE: Defined in +active_support/core_ext/class/attribute.rb+
The macros +cattr_reader+, +cattr_writer+, and +cattr_accessor+ are analogous to their +attr_*+ counterparts but for classes. They initialize a class variable to +nil+ unless it already exists, and generate the corresponding class methods to access it:
@@ -587,82 +622,6 @@ If for whatever reason an application loads the definition of a mailer class and
NOTE: Defined in +active_support/core_ext/class/delegating_attributes.rb+.
-h4. Subclasses
-
-The +subclasses+ method returns the names of all subclasses of a given class as an array of strings. That comprises not only direct subclasses, but all descendants down the hierarchy:
-
-<ruby>
-class C; end
-C.subclasses # => []
-
-Integer.subclasses # => ["Bignum", "Fixnum"]
-
-module M
- class A; end
- class B1 < A; end
- class B2 < A; end
-end
-
-module N
- class C < M::B1; end
-end
-
-M::A.subclasses # => ["N::C", "M::B2", "M::B1"]
-</ruby>
-
-The order in which these class names are returned is unspecified.
-
-See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
-
-NOTE: Defined in +active_support/core_ext/class/removal.rb+.
-
-h4. Class Removal
-
-Roughly speaking, the +remove_class+ method removes the class objects passed as arguments:
-
-<ruby>
-Class.remove_class(Hash, Dir) # => [Hash, Dir]
-Hash # => NameError: uninitialized constant Hash
-Dir # => NameError: uninitialized constant Dir
-</ruby>
-
-More specifically, +remove_class+ attempts to remove constants with the same name as the passed class objects from their parent modules. So technically this method does not guarantee the class objects themselves are not still valid and alive somewhere after the method call:
-
-<ruby>
-module M
- class A; end
- class B < A; end
-end
-
-A2 = M::A
-
-M::A.object_id # => 13053950
-Class.remove_class(M::A)
-
-M::B.superclass.object_id # => 13053950 (same object as before)
-A2.name # => "M::A" (name is hard-coded in object)
-</ruby>
-
-WARNING: Removing fundamental classes like +String+ can result in really funky behaviour.
-
-The method +remove_subclasses+ provides a shortcut for removing all descendants of a given class, where "removing" has the meaning explained above:
-
-<ruby>
-class A; end
-class B1 < A; end
-class B2 < A; end
-class C < A; end
-
-A.subclasses # => ["C", "B2", "B1"]
-A.remove_subclasses
-A.subclasses # => []
-C # => NameError: uninitialized constant C
-</ruby>
-
-See also +Object#remove_subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
-
-NOTE: Defined in +active_support/core_ext/class/removal.rb+.
-
h3. Extensions to +String+
h4. +squish+
@@ -816,7 +775,15 @@ NOTE: Defined in +active_support/core_ext/integer/inflections.rb+.
h3. Extensions to +Float+
-...
+h4. +round+
+
+The builtin method +Float#round+ rounds a float to the nearest integer. Active Support adds an optional parameter to let you specify a precision:
+
+<ruby>
+Math::E.round(4) # => 2.7183
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/float/rounding.rb+.
h3. Extensions to +BigDecimal+
@@ -1799,20 +1766,11 @@ NOTE: Defined in +active_support/core_ext/name_error.rb+.
h3. Extensions to +LoadError+
-Rails hijacks +LoadError.new+ to return a +MissingSourceFile+ exception:
+Active Support adds +is_missing?+ to +LoadError+, and also assigns that class to the constant +MissingSourceFile+ for backwards compatibility.
-<shell>
-$ ruby -e 'require "nonexistent"'
-...: no such file to load -- nonexistent (LoadError)
-...
-$ script/runner 'require "nonexistent"'
-...: no such file to load -- nonexistent (MissingSourceFile)
-...
-</shell>
-
-The class +MissingSourceFile+ is a subclass of +LoadError+, so any code that rescues +LoadError+ as usual still works as expected. Point is these exception objects respond to +is_missing?+, which given a path name tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension).
+Given a path name +is_missing?+ tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension).
-For example, when an action of +PostsController+ is called Rails tries to load +posts_helper.rb+, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist, but it in turn requires another library that is missing. In that case Rails must reraise the exception. The method +is_missing?+ provides a way to distinguish both cases:
+For example, when an action of +PostsController+ is called Rails tries to load +posts_helper.rb+, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist and in turn requires another library that is missing. In that case Rails must reraise the exception. The method +is_missing?+ provides a way to distinguish both cases:
<ruby>
def default_helper_module!
@@ -1820,7 +1778,7 @@ def default_helper_module!
module_path = module_name.underscore
helper module_path
rescue MissingSourceFile => e
- raise e unless e.is_missing? "#{module_path}_helper"
+ raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
index 805fcf1a32..1b6823a39a 100644
--- a/railties/guides/source/contributing_to_rails.textile
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -76,21 +76,28 @@ TIP: You may want to "put your git branch name in your shell prompt":http://gith
h4. Set up and Run the Tests
All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. Rails needs the +mocha+ gem for running some tests, so install it with:
+
<shell>
gem install mocha
</shell>
-For the tests that touch the database, this means creating the databases. If you're using MySQL:
+For the tests that touch the database, this means creating test databases. If you're using MySQL, create a user named +rails+ with privileges on the test databases.
+
+<shell>
+mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.*
+ to 'rails'@'localhost';
+mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.*
+ to 'rails'@'localhost';
+</shell>
+
+Enter this from the +activerecord+ directory to create the test databases:
<shell>
-mysql> create database activerecord_unittest;
-mysql> create database activerecord_unittest2;
-mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.*
- to 'rails'@'localhost';
-mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.*
- to 'rails'@'localhost';
+rake mysql:build_databases
</shell>
+NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation.
+
If you’re using another database, check the files under +activerecord/test/connections+ in the Rails source code for default connection information. You can edit these files if you _must_ on your machine to provide different credentials, but obviously you should not push any such changes back to Rails.
Now if you go back to the root of the Rails source on your machine and run +rake+ with no parameters, you should see every test in all of the Rails components pass. If you want to run the all ActiveRecord tests (or just a single one) with another database adapter, enter this from the +activerecord+ directory:
diff --git a/railties/guides/source/credits.textile.erb b/railties/guides/source/credits.textile.erb
index 49e390908f..28c70f2b39 100644
--- a/railties/guides/source/credits.textile.erb
+++ b/railties/guides/source/credits.textile.erb
@@ -43,6 +43,10 @@ p. We'd like to thank the following people for their tireless contributions to t
Cássio Marques is a Brazilian software developer working with different programming languages such as Ruby, JavaScript, CPP and Java, as an independent consultant. He blogs at "/* CODIFICANDO */":http://cassiomarques.wordpress.com, which is mainly written in Portuguese, but will soon get a new section for posts with English translation.
<% end %>
+<% author('James Miller', 'bensie') do %>
+ James Miller is a software developer for "JK Tech":http://www.jk-tech.com in San Diego, CA. Find me on GitHub, Gmail, Twitter, and Freenode as bensie.
+<% end %>
+
<% author('Emilio Tagua', 'miloops') do %>
Emilio Tagua -- a.k.a. miloops -- is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist. Cofounder of "Eventioz":http://eventioz.com. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as miloops.
<% end %>
@@ -50,3 +54,7 @@ p. We'd like to thank the following people for their tireless contributions to t
<% author('Heiko Webers', 'hawe') do %>
Heiko Webers is the founder of "bauland42":http://www.bauland42.de, a German web application security consulting and development company focused on Ruby on Rails. He blogs at the "Ruby on Rails Security Project":http://www.rorsecurity.info. After 10 years of desktop application development, Heiko has rarely looked back.
<% end %>
+
+<% author('Mikel Lindsaar', 'raasdnil') do %>
+ Mikel Lindsaar has been working with Rails since 2006 and is the author of the Ruby Mail gem and core contributor (he helped re-write ActionMailer's API). Mikel has a "blog":http://lindsaar.net/ and "tweets":http://twitter.com/raasdnil.
+<% end %>
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index bd6dbda199..c173748944 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -9,13 +9,13 @@ This guide covers getting up and running with Ruby on Rails. After reading it, y
endprologue.
-WARNING. This Guide is based on Rails 2.3.3. Some of the code shown here will not work in other versions of Rails.
+WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails.
h3. This Guide Assumes
This guide is designed for beginners who want to get started with a Rails application from scratch. It does not assume that you have any prior experience with Rails. However, to get the most out of it, you need to have some prerequisites installed:
-* The "Ruby":http://www.ruby-lang.org/en/downloads language
+* The "Ruby":http://www.ruby-lang.org/en/downloads language version 1.8.7 or higher
* The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system
* A working installation of "SQLite":http://www.sqlite.org (preferred), "MySQL":http://www.mysql.com, or "PostgreSQL":http://www.postgresql.org
@@ -91,7 +91,7 @@ Active Resource provides a framework for managing the connection between busines
h5. Railties
-Railties is the core Rails code that builds new Rails applications and glues the various frameworks together in any Rails application.
+Railties is the core Rails code that builds new Rails applications and glues the various frameworks and plugins together in any Rails application.
h5. Active Support
@@ -134,8 +134,6 @@ NOTE. There are some special circumstances in which you might want to use an alt
* If you're working on Windows, you may find it easier to install Instant Rails. Be aware, though, that "Instant Rails":http://instantrails.rubyforge.org/wiki/wiki.pl releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows.
* If you want to keep up with cutting-edge changes to Rails, you'll want to clone the "Rails source code":http://github.com/rails/rails/tree/master from github. This is not recommended as an option for beginners, though.
-WARNING. As of mid-2009, cloning the master branch will get you preliminary Rails 3.0 code. To follow along with this guide, you should clone the 2-3-stable branch instead.
-
h4. Creating the Blog Application
Open a terminal, navigate to a folder where you have rights to create files, and type:
@@ -167,10 +165,13 @@ $ cd blog
In any case, Rails will create a folder in your working directory called <tt>blog</tt>. Open up that folder and explore its contents. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here's a basic rundown on the function of each folder that Rails creates in a new application by default:
|_.File/Folder|_.Purpose|
-|README|This is a brief instruction manual for your application. Use it to tell others what your application does, how to set it up, and so on.|
+|Gemfile|This file allows you to specify what gem dependencies are needed for your Rails application.|
+|README.rdoc|This is a brief instruction manual for your application. Use it to tell others what your application does, how to set it up, and so on.|
|Rakefile|This file contains batch jobs that can be run from the terminal.|
|app/|Contains the controllers, models, and views for your application. You'll focus on this folder for the remainder of this guide.|
+|bin/|Holds various executables needed for your Rails application.|
|config/|Configure your application's runtime rules, routes, database, and more.|
+|config.ru|Rack configuration for Rack based servers used to start the application.|
|db/|Shows your current database schema, as well as the database migrations. You'll learn about migrations shortly.|
|doc/|In-depth documentation for your application.|
|lib/|Extended modules for your application (not covered in this guide).|
@@ -179,7 +180,18 @@ In any case, Rails will create a folder in your working directory called <tt>blo
|script/|Scripts provided by Rails to do recurring tasks, such as benchmarking, plugin installation, and starting the console or the web server.|
|test/|Unit tests, fixtures, and other test apparatus. These are covered in "Testing Rails Applications":testing.html|
|tmp/|Temporary files|
-|vendor/|A place for third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.|
+|vendor/|A place for all third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.|
+
+h4. Installing the Required Gems
+
+Rails uses the _Bundler_ gem to populate the +vendor+ directory with all the gems your application depends on. As we don't need any special gems beyond the default, we just need to do the following:
+
+<shell>
+$ gem install bundle
+$ gem bundle
+</shell>
+
+This will copy down the latest versions of all the gems you need to start a rails application.
h4. Configuring a Database
@@ -255,19 +267,11 @@ NOTE. Rake is a general-purpose command-runner that Rails uses for many things.
h3. Hello, Rails!
-One of the traditional places to start with a new language is by getting some text up on screen quickly. To do that in Rails, you need to create at minimum a controller and a view. Fortunately, you can do that in a single command. Enter this command in your terminal:
-
-<shell>
-$ script/generate controller home index
-</shell>
-
-TIP: If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+.
+One of the traditional places to start with a new language is by getting some text up on screen quickly, to do this, you need to get your Rails application server running.
-Rails will create several files for you, including +app/views/home/index.html.erb+. This is the template that will be used to display the results of the +index+ action (method) in the +home+ controller. Open this file in your text editor and edit it to contain a single line of code:
+h4. Before we begin
-<code class="html">
-<h1>Hello, Rails!</h1>
-</code>
+As an added help, you can find all the code of this application in a ready to run Git repository at "http://github.com/mikel/getting-started-code":http://github.com/mikel/getting-started-code.
h4. Starting up the Web Server
@@ -283,34 +287,51 @@ This will fire up an instance of the Mongrel web server by default (Rails can al
TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.
-The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to +http://localhost:3000/home/index+.
+The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. You can also click on the +About your application’s environment+ link to see a summary of your Application's environment.
+
+h4. Say "Hello", Rails
+
+To get Rails saying "Hello", you need to create at minimum a controller and a view. Fortunately, you can do that in a single command. Enter this command in your terminal:
+
+<shell>
+$ script/generate controller home index
+</shell>
+
+TIP: If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+.
+
+Rails will create several files for you, including +app/views/home/index.html.erb+. This is the template that will be used to display the results of the +index+ action (method) in the +home+ controller. Open this file in your text editor and edit it to contain a single line of code:
+
+<code class="html">
+<h1>Hello, Rails!</h1>
+</code>
h4. Setting the Application Home Page
-You'd probably like to replace the "Welcome Aboard" page with your own application's home page. The first step to doing this is to delete the default page from your application:
+Now that we have made the controller and view, we need to tell Rails when we want "Hello Rails" to show up. In our case, we want it to show up when we navigate to the root URL of our site, +http://127.0.0.1:3000/+, instead of the "Welcome Aboard" smoke test.
+
+The first step to doing this is to delete the default page from your application:
<shell>
$ rm public/index.html
</shell>
-Now, you have to tell Rails where your actual home page is located. Open the file +config/routes.rb+ in your editor. This is your application's, _routing file_, which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. At the bottom of the file you'll see the _default routes_:
+We need to do this as Rails will deliver any static file in the +public+ directory in preference to any dynamic contact we generate from the controllers.
-<ruby>
-map.connect ':controller/:action/:id'
-map.connect ':controller/:action/:id.:format'
-</ruby>
+Now, you have to tell Rails where your actual home page is located. Open the file +config/routes.rb+ in your editor. This is your application's, _routing file_, which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. There are only comments in this file, so we need to add at the top the following:
-The default routes handle simple requests such as +/home/index+: Rails translates that into a call to the +index+ action in the +home+ controller. As another example, +/posts/edit/1+ would run the +edit+ action in the +posts+ controller with an +id+ of 1.
+<ruby>
+Blog::Application.routes.draw do |map|
-To hook up your home page, you need to add another line to the routing file, above the default routes:
+ root :to => "home#index"
-<ruby>
-map.root :controller => "home"
+ # The priority is based upon order of creation:
+ # first created -> highest priority.
+ #...
</ruby>
-This line illustrates one tiny bit of the "convention over configuration" approach: if you don't specify an action, Rails assumes the +index+ action.
+The +root :to => "home#index"+ tells Rails to map the root action to the home controller's index action.
-Now if you navigate to +http://localhost:3000+ in your browser, you'll see the +home/index+ view.
+Now if you navigate to +http://localhost:3000+ in your browser, you'll see +Hello, Rails!+.
NOTE. For more information about routing, refer to "Rails Routing from the Outside In":routing.html.
@@ -330,28 +351,29 @@ NOTE. While scaffolding will get you up and running quickly, the "one size fits
The scaffold generator will build 14 files in your application, along with some folders, and edit one more. Here's a quick overview of what it creates:
-|_.File |_.Purpose|
-|app/models/post.rb |The Post model|
-|db/migrate/20090113124235_create_posts.rb |Migration to create the posts table in your database (your name will include a different timestamp)|
-|app/views/posts/index.html.erb |A view to display an index of all posts |
-|app/views/posts/show.html.erb |A view to display a single post|
-|app/views/posts/new.html.erb |A view to create a new post|
-|app/views/posts/edit.html.erb |A view to edit an existing post|
-|app/views/layouts/posts.html.erb |A view to control the overall look and feel of the other posts views|
-|public/stylesheets/scaffold.css |Cascading style sheet to make the scaffolded views look better|
-|app/controllers/posts_controller.rb |The Posts controller|
-|test/functional/posts_controller_test.rb |Functional testing harness for the posts controller|
-|app/helpers/posts_helper.rb |Helper functions to be used from the posts views|
-|config/routes.rb |Edited to include routing information for posts|
-|test/fixtures/posts.yml |Dummy posts for use in testing|
-|test/unit/post_test.rb |Unit testing harness for the posts model|
-|test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper|
+|_.File |_.Purpose|
+|app/models/post.rb |The Post model|
+|db/migrate/20100123083454_create_posts.rb |Migration to create the posts table in your database (your name will include a different timestamp)|
+|app/views/posts/index.html.erb| |A view to display an index of all posts |
+|app/views/posts/edit.html.erb |A view to edit an existing post|
+|app/views/posts/show.html.erb |A view to display a single post|
+|app/views/posts/new.html.erb |A view to create a new post|
+|app/views/posts/_form.html.erb |A view to control the overall look and feel of the other posts views|
+|app/views/layouts/posts.html.erb |A view to control the overall look and feel of the other posts views|
+|public/stylesheets/scaffold.css |Cascading style sheet to make the scaffolded views look better|
+|app/controllers/posts_controller.rb |The Posts controller|
+|test/functional/posts_controller_test.rb |Functional testing harness for the posts controller|
+|app/helpers/posts_helper.rb |Helper functions to be used from the posts views|
+|config/routes.rb |Edited to include routing information for posts|
+|test/fixtures/posts.yml |Dummy posts for use in testing|
+|test/unit/post_test.rb |Unit testing harness for the posts model|
+|test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper|
h4. Running a Migration
One of the products of the +script/generate scaffold+ command is a _database migration_. Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it's possible to undo a migration after it's been applied to your database. Migration filenames include a timestamp to ensure that they're processed in the order that they were created.
-If you look in the +db/migrate/20090113124235_create_posts.rb+ file (remember, yours will have a slightly different name), here's what you'll find:
+If you look in the +db/migrate/20100123083454_create_posts.rb+ file (remember, yours will have a slightly different name), here's what you'll find:
<ruby>
class CreatePosts < ActiveRecord::Migration
@@ -381,6 +403,15 @@ $ rake db:migrate
Remember, you can't run migrations before running +rake db:create+ to create your database, as we covered earlier.
+Rails will execute this migration command and tell you is created the Posts table.
+
+<shell>
+== CreatePosts: migrating =================================================
+-- create_table(:posts)
+ -> 0.0019s
+== CreatePosts: migrated (0.0020s) ========================================
+</shell>
+
NOTE. Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your +config/database.yml+ file.
h4. Adding a Link
@@ -423,8 +454,9 @@ Rails includes methods to help you validate the data that you send to models. Op
<ruby>
class Post < ActiveRecord::Base
- validates_presence_of :name, :title
- validates_length_of :title, :minimum => 5
+ validates :name, :presence => true
+ validates :title, :presence => true,
+ :length => { :minimum => 5 }
end
</ruby>
@@ -447,10 +479,9 @@ created_at: nil, updated_at: nil>
>> p.save
=> false
>> p.errors
-=> #<ActiveRecord::Errors:0x23bcf0c @base=#<Post id: nil, name: nil,
-title: nil, content: "A new post", created_at: nil, updated_at: nil>,
-@errors={"name"=>["can't be blank"], "title"=>["can't be blank",
-"is too short (minimum is 5 characters)"]}>
+=> #<OrderedHash {:title=>["can't be blank",
+ "is too short (minimum is 5 characters)"],
+ :name=>["can't be blank"]}>
</shell>
This code shows creating a new +Post+ instance, attempting to save it and getting +false+ for a return value (indicating that the save failed), and inspecting the +errors+ of the post.
@@ -472,7 +503,7 @@ def index
end
</ruby>
-This code sets the +@posts+ instance variable to an array of all posts in the database. +Post.find(:all)+ or +Post.all+ calls the +Post+ model to return all of the posts that are currently in the database, with no limiting conditions.
+This code sets the +@posts+ instance variable to an array of all posts in the database. +Post.all+ calls the +Post+ model to return all of the posts that are currently in the database, with no limiting conditions.
TIP: For more information on finding records with Active Record, see "Active Record Query Interface":active_record_querying.html.
@@ -486,17 +517,19 @@ The +respond_to+ block handles both HTML and XML calls to this action. If you br
<th>Name</th>
<th>Title</th>
<th>Content</th>
+ <th></th>
+ <th></th>
+ <th></th>
</tr>
<% @posts.each do |post| %>
<tr>
- <td><%=h post.name %></td>
- <td><%=h post.title %></td>
- <td><%=h post.content %></td>
+ <td><%= post.name %></td>
+ <td><%= post.title %></td>
+ <td><%= post.content %></td>
<td><%= link_to 'Show', post %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
- <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?',
- :method => :delete %></td>
+ <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
@@ -508,10 +541,11 @@ The +respond_to+ block handles both HTML and XML calls to this action. If you br
This view iterates over the contents of the +@posts+ array to display content and links. A few things to note in the view:
-* +h+ is a Rails helper method to sanitize displayed data, preventing cross-site scripting attacks
* +link_to+ builds a hyperlink to a particular destination
* +edit_post_path+ is a helper that Rails provides as part of RESTful routing. You'll see a variety of these helpers for the different actions that the controller includes.
+NOTE. In previous versions of Rails, you had to use +<%=h post.name %>+ so that any HTML would be escaped before being inserted into the page. In Rails 3.0, this is now the default. To get unescaped HTML, you now use +<%= raw post.name %>+.
+
TIP: For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html.
h4. Customizing the Layout
@@ -519,21 +553,17 @@ h4. Customizing the Layout
The view is only part of the story of how HTML is displayed in your web browser. Rails also has the concept of +layouts+, which are containers for views. When Rails renders a view to the browser, it does so by putting the view's HTML into a layout's HTML. The +script/generate scaffold+ command automatically created a default layout, +app/views/layouts/posts.html.erb+, for the posts. Open this layout in your editor and modify the +body+ tag:
<erb>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<!DOCTYPE html>
+<html>
<head>
- <meta http-equiv="content-type"
- content="text/html;charset=UTF-8" />
<title>Posts: <%= controller.action_name %></title>
<%= stylesheet_link_tag 'scaffold' %>
</head>
<body style="background: #EEEEEE;">
-<p style="color: green"><%= flash[:notice] %></p>
+<p class="notice"><%= notice %></p>
-<%= yield %>
+<%= yield %>
</body>
</html>
@@ -561,34 +591,48 @@ The +new.html.erb+ view displays this empty Post to the user:
<erb>
<h1>New post</h1>
+<%= render 'form' %>
+
+<%= link_to 'Back', posts_path %>
+</erb>
+
+The +<%= render 'form' %>+ line is our first introduction to _partials_ in Rails. A partial is a snippet of HTML and Ruby code that can be reused in multiple locations. In this case, the form used to make a new post, is very similar to a form used to edit a post, both have text fields for the name and title and a text area for the content with a button to make a new post or update the existing post.
+
+If you take a look at +views/posts/_form.html.erb+ file, you will see the following:
+
+<erb>
<% form_for(@post) do |f| %>
<%= f.error_messages %>
- <p>
+ <div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
- </p>
- <p>
+ </div>
+ <div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
- </p>
- <p>
+ </div>
+ <div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
- </p>
- <p>
- <%= f.submit "Create" %>
- </p>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
<% end %>
-
-<%= link_to 'Back', posts_path %>
</erb>
+This partial receives all the instance variables defined in the calling view file, so in this case, the controller assigned the new Post object to +@post+ and so, this is available in both the view and partial as +@post+.
+
+For more information on partials, refer to the "Layouts and Rendering in Rails":layouts_and_rendering.html#using-partials guide.
+
The +form_for+ block is used to create an HTML form. Within this block, you have access to methods to build various controls on the form. For example, +f.text_field :name+ tells Rails to create a text input on the form, and to hook it up to the +name+ attribute of the instance being displayed. You can only use these methods with attributes of the model that the form is based on (in this case +name+, +title+, and +content+). Rails uses +form_for+ in preference to having you write raw HTML because the code is more succinct, and because it explicitly ties the form to a particular model instance.
+The +form_for+ block is also smart enough to work out if you are doing a _New Post_ or an _Edit Post_ action, and will set the form +action+ tags and submit button names appropriately in the HTML output.
+
TIP: If you need to create an HTML form that displays arbitrary fields, not tied to a model, you should use the +form_tag+ method, which provides shortcuts for building forms that are not necessarily tied to a model instance.
-When the user clicks the +Create+ button on this form, the browser will send information back to the +create+ method of the controller (Rails knows to call the +create+ method because the form is sent with an HTTP POST request; that's one of the conventions that I mentioned earlier):
+When the user clicks the +Create Post+ button on this form, the browser will send information back to the +create+ method of the controller (Rails knows to call the +create+ method because the form is sent with an HTTP POST request; that's one of the conventions that I mentioned earlier):
<ruby>
def create
@@ -596,22 +640,24 @@ def create
respond_to do |format|
if @post.save
- flash[:notice] = 'Post was successfully created.'
- format.html { redirect_to(@post) }
- format.xml { render :xml => @post, :status => :created,
- :location => @post }
+ format.html { redirect_to(@post,
+ :notice => 'Post was successfully created.') }
+ format.xml { render :xml => @post,
+ :status => :created, :location => @post }
else
format.html { render :action => "new" }
format.xml { render :xml => @post.errors,
- :status => :unprocessable_entity }
+ :status => :unprocessable_entity }
end
end
end
</ruby>
-The +create+ action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the +params+ hash. After saving the new post, it uses +flash[:notice]+ to create an informational message for the user, and redirects to the show action for the post. If there's any problem, the +create+ action just shows the +new+ view a second time, with any error messages.
+The +create+ action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the +params+ hash. After successfully saving the new post, returns the appropriate format that the user has requested (HTML in our case). It then redirects the user to the resulting post +show+ action and sets a notice to the user that the Post was successfully created.
-Rails provides the +flash+ hash (usually just called the Flash) so that messages can be carried over to another action, providing the user with useful information on the status of their request. In the case of +create+, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record. The Flash carries over a message to the next action, so that when the user is redirected back to the +show+ action, they are presented with a message saying "Post was successfully created."
+If the post was not successfully saved, due to a validation error, then the controller returns the user back to the +new+ action with any error messages so that the user has the chance to fix the error and try again.
+
+The "Post was successfully created" message is stored inside of the Rails +flash+ hash, (usually just called the Flash) so that messages can be carried over to another action, providing the user with useful information on the status of their request. In the case of +create+, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record. The Flash carries over a message to the next action, so that when the user is redirected back to the +show+ action, they are presented with a message saying "Post was successfully created."
h4. Showing an Individual Post
@@ -633,17 +679,17 @@ The +show+ action uses +Post.find+ to search for a single record in the database
<erb>
<p>
<b>Name:</b>
- <%=h @post.name %>
+ <%= @post.name %>
</p>
<p>
<b>Title:</b>
- <%=h @post.title %>
+ <%= @post.title %>
</p>
<p>
<b>Content:</b>
- <%=h @post.content %>
+ <%= @post.content %>
</p>
@@ -666,30 +712,15 @@ After finding the requested post, Rails uses the +edit.html.erb+ view to display
<erb>
<h1>Editing post</h1>
-<% form_for(@post) do |f| %>
- <%= f.error_messages %>
-
- <p>
- <%= f.label :name %><br />
- <%= f.text_field :name %>
- </p>
- <p>
- <%= f.label :title %><br />
- <%= f.text_field :title %>
- </p>
- <p>
- <%= f.label :content %><br />
- <%= f.text_area :content %>
- </p>
- <p>
- <%= f.submit "Update" %>
- </p>
-<% end %>
+<%= render 'form' %>
<%= link_to 'Show', @post %> |
<%= link_to 'Back', posts_path %>
+<% end %>
</erb>
+Again, as with the +new+ action, the +edit+ action is using the +form+ partial, this time however, the form will do a PUT action to the PostsController and the submit button will display "Update Post"
+
Submitting the form created by this view will invoke the +update+ action within the controller:
<ruby>
@@ -698,13 +729,13 @@ def update
respond_to do |format|
if @post.update_attributes(params[:post])
- flash[:notice] = 'Post was successfully updated.'
- format.html { redirect_to(@post) }
+ format.html { redirect_to(@post,
+ :notice => 'Post was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @post.errors,
- :status => :unprocessable_entity }
+ :status => :unprocessable_entity }
end
end
end
@@ -712,8 +743,6 @@ end
In the +update+ action, Rails first uses the +:id+ parameter passed back from the edit view to locate the database record that's being edited. The +update_attributes+ call then takes the rest of the parameters from the request and applies them to this record. If all goes well, the user is redirected to the post's +show+ view. If there are any problems, it's back to +edit+ to correct them.
-NOTE. Sharp-eyed readers will have noticed that the +form_for+ declaration is identical for the +new+ and +edit+ views. Rails generates different code for the two forms because it's smart enough to notice that in the one case it's being passed a new record that has never been saved, and in the other case an existing record that has already been saved to the database. In a production Rails application, you would ordinarily eliminate this duplication by moving identical code to a _partial template_, which you could then include in both parent templates. But the scaffold generator tries not to make too many assumptions, and generates code that's easy to modify if you want different forms for +create+ and +edit+.
-
h4. Destroying a Post
Finally, clicking one of the +destroy+ links sends the associated id to the +destroy+ action:
@@ -734,60 +763,7 @@ The +destroy+ method of an Active Record model instance removes the correspondin
h3. DRYing up the Code
-At this point, it's worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views and _filters_ to help with duplication in controllers.
-
-h4. Using Partials to Eliminate View Duplication
-
-As you saw earlier, the scaffold-generated views for the +new+ and +edit+ actions are largely identical. You can pull the shared code out into a partial template. This requires editing the new and edit views, and adding a new template. The new +_form.html.erb+ template should be saved in the same +app/views/posts+ folder as the files from which it is being extracted. Note that the name of this file begins with an underscore; that's the Rails naming convention for partial templates.
-
-<tt>new.html.erb</tt>:
-
-<erb>
-<h1>New post</h1>
-
-<%= render :partial => "form" %>
-
-<%= link_to 'Back', posts_path %>
-</erb>
-
-<tt>edit.html.erb</tt>:
-
-<erb>
-<h1>Editing post</h1>
-
-<%= render :partial => "form" %>
-
-<%= link_to 'Show', @post %> |
-<%= link_to 'Back', posts_path %>
-</erb>
-
-<tt>_form.html.erb</tt>:
-
-<erb>
-<% form_for(@post) do |f| %>
- <%= f.error_messages %>
-
- <p>
- <%= f.label :name %><br />
- <%= f.text_field :name %>
- </p>
- <p>
- <%= f.label :title, "title" %><br />
- <%= f.text_field :title %>
- </p>
- <p>
- <%= f.label :content %><br />
- <%= f.text_area :content %>
- </p>
- <p>
- <%= f.submit "Save" %>
- </p>
-<% end %>
-</erb>
-
-Now, when Rails renders the +new+ or +edit+ view, it will insert the +_form+ partial at the indicated point. Note the naming convention for partials: if you refer to a partial named +form+ inside of a view, the corresponding file is +_form.html.erb+, with a leading underscore.
-
-For more information on partials, refer to the "Layouts and Rendering in Rails":layouts_and_rendering.html#using-partials guide.
+At this point, it's worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views (as you saw above with the +new+ and +edit+ views both sharing the +form+ partial) and _filters_ to help with duplication in controllers.
h4. Using Filters to Eliminate Controller Duplication
@@ -859,14 +835,13 @@ h4. Generating a Model
Models in Rails use a singular name, and their corresponding database tables use a plural name. For the model to hold comments, the convention is to use the name Comment. Even if you don't want to use the entire apparatus set up by scaffolding, most Rails developers still use generators to make things like models and controllers. To create the new model, run this command in your terminal:
<shell>
-$ script/generate model Comment commenter:string body:text
- post:references
+$ script/generate model Comment commenter:string body:text post:references
</shell>
This command will generate four files:
* +app/models/comment.rb+ - The model
-* +db/migrate/20091013214407_create_comments.rb+ - The migration
+* +db/migrate/20100124023310_create_comments.rb+ - The migration
* +test/unit/comment_test.rb+ and +test/fixtures/comments.yml+ - The test harness.
First, take a look at +comment.rb+:
@@ -905,7 +880,14 @@ The +t.references+ line sets up a foreign key column for the association between
$ rake db:migrate
</shell>
-Rails is smart enough to only execute the migrations that have not already been run against the current database.
+Rails is smart enough to only execute the migrations that have not already been run against the current database, so in this case you will just see:
+
+<shell>
+== CreateComments: migrating =================================================
+-- create_table(:comments)
+ -> 0.0019s
+== CreateComments: migrated (0.0020s) ========================================
+</shell>
h4. Associating Models
@@ -926,8 +908,9 @@ You'll need to edit the +post.rb+ file to add the other side of the association:
<ruby>
class Post < ActiveRecord::Base
- validates_presence_of :name, :title
- validates_length_of :title, :minimum => 5
+ validates :name, :presence => true
+ validates :title, :presence => true,
+ :length => { :minimum => 5 }
has_many :comments
end
</ruby>
@@ -936,12 +919,14 @@ These two declarations enable a good bit of automatic behavior. For example, if
TIP: For more information on Active Record associations, see the "Active Record Associations":association_basics.html guide.
-h4. Adding a Route
+h4. Adding a Route for Comments
-_Routes_ are entries in the +config/routes.rb+ file that tell Rails how to match incoming HTTP requests to controller actions. Open up that file and find the existing line referring to +posts+ (it will be right at the top of the file). Then edit it as follows:
+As with the +home+ controller, we will need to add a route so that Rails knows where we would like to navigate to see +comments+. Open up the +config/routes.rb+ file again, you will see an entry that was added automatically for +posts+ near the top by the scaffold generator, +resources :posts+, edit it as follows:
<ruby>
-map.resources :posts, :has_many => :comments
+resources :posts do
+ resources :comments
+end
</ruby>
This creates +comments+ as a _nested resource_ within +posts+. This is another part of capturing the hierarchical relationship that exists between posts and comments.
@@ -1063,20 +1048,19 @@ The +views/comments/index.html.erb+ view:
<tr>
<th>Commenter</th>
<th>Body</th>
+ <th></th>
+ <th></th>
+ <th></th>
</tr>
-<% for comment in @comments %>
+<% @comments.each do |comment| %>
<tr>
- <td><%=h comment.commenter %></td>
- <td><%=h comment.body %></td>
+ <td><%= comment.commenter %></td>
+ <td><%= comment.body %></td>
<td><%= link_to 'Show', post_comment_path(@post, comment) %></td>
- <td>
- <%= link_to 'Edit', edit_post_comment_path(@post, comment) %>
- </td>
- <td>
- <%= link_to 'Destroy', post_comment_path(@post, comment),
- :confirm => 'Are you sure?', :method => :delete %>
- </td>
+ <td><%= link_to 'Edit', edit_post_comment_path(@post, comment) %></td>
+ <td><%= link_to 'Destroy', post_comment_path(@post, comment),
+ :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
@@ -1087,28 +1071,45 @@ The +views/comments/index.html.erb+ view:
<%= link_to 'Back to Post', @post %>
</erb>
-The +views/comments/new.html.erb+ view:
+The +views/comments/new.html.erb+ view (again using a partial to render a form that is shared with the +edit+ view):
<erb>
<h1>New comment</h1>
+<%= render 'form' %>
+
+<%= link_to 'Back', post_comments_path(@post) %>
+</erb>
+
+The +views/comments/edit.html.erb+ view:
+
+<erb>
+<h1>Editing comment</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Show', post_comment_path(@post, @comment) %> |
+<%= link_to 'Back', post_comments_path(@post) %>
+</erb>
+
+The +views/comments/_form.html.erb+ partial:
+
+<erb>
<% form_for([@post, @comment]) do |f| %>
<%= f.error_messages %>
- <p>
+ <div class="field">
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
- </p>
- <p>
+ </div>
+ <div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
- </p>
- <p>
- <%= f.submit "Create" %>
- </p>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
<% end %>
-
-<%= link_to 'Back', post_comments_path(@post) %>
</erb>
The +views/comments/show.html.erb+ view:
@@ -1118,43 +1119,18 @@ The +views/comments/show.html.erb+ view:
<p>
<b>Commenter:</b>
- <%=h @comment.commenter %>
+ <%= @comment.commenter %>
</p>
<p>
<b>Comment:</b>
- <%=h @comment.body %>
+ <%= @comment.body %>
</p>
<%= link_to 'Edit', edit_post_comment_path(@post, @comment) %> |
<%= link_to 'Back', post_comments_path(@post) %>
</erb>
-The +views/comments/edit.html.erb+ view:
-
-<erb>
-<h1>Editing comment</h1>
-
-<% form_for([@post, @comment]) do |f| %>
- <%= f.error_messages %>
-
- <p>
- <%= f.label :commenter %><br />
- <%= f.text_field :commenter %>
- </p>
- <p>
- <%= f.label :body %><br />
- <%= f.text_area :body %>
- </p>
- <p>
- <%= f.submit "Update" %>
- </p>
-<% end %>
-
-<%= link_to 'Show', post_comment_path(@post, @comment) %> |
-<%= link_to 'Back', post_comments_path(@post) %>
-</erb>
-
Again, the added complexity here (compared to the views you saw for managing posts) comes from the necessity of juggling a post and its comments at the same time.
h4. Hooking Comments to Posts
@@ -1164,29 +1140,29 @@ As a next step, I'll modify the +views/posts/show.html.erb+ view to show the com
<erb>
<p>
<b>Name:</b>
- <%=h @post.name %>
+ <%= @post.name %>
</p>
<p>
<b>Title:</b>
- <%=h @post.title %>
+ <%= @post.title %>
</p>
<p>
<b>Content:</b>
- <%=h @post.content %>
+ <%= @post.content %>
</p>
<h2>Comments</h2>
-<% @post.comments.each do |c| %>
+<% @post.comments.each do |comment| %>
<p>
<b>Commenter:</b>
- <%=h c.commenter %>
+ <%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
- <%=h c.body %>
+ <%= comment.body %>
</p>
<% end %>
@@ -1203,21 +1179,22 @@ If you decide at some point to delete a post, you likely want to delete the comm
<ruby>
class Post < ActiveRecord::Base
- validates_presence_of :name, :title
- validates_length_of :title, :minimum => 5
+ validates :name, :presence => true
+ validates :title, :presence => true,
+ :length => { :minimum => 5 }
has_many :comments, :dependent => :destroy
end
</ruby>
h3. Building a Multi-Model Form
-Comments and posts are edited on two separate forms - which makes sense, given the flow of this mini-application. But what if you want to edit more than one thing on a single form? Rails 2.3 offers new support for nested forms. Let's add support for giving each post multiple tags, right in the form where you create the post. First, create a new model to hold the tags:
+Comments and posts are edited on two separate forms - which makes sense, given the flow of this mini-application. But what if you want to edit more than one thing on a single form? Rails offers support for nested forms. Let's add support for giving each post multiple tags, right in the form where you create the post. First, create a new model to hold the tags:
<shell>
$ script/generate model tag name:string post:references
</shell>
-Run the migration to create the database table:
+Again, run the migration to create the database table:
<shell>
$ rake db:migrate
@@ -1227,8 +1204,9 @@ Next, edit the +post.rb+ file to create the other side of the association, and t
<ruby>
class Post < ActiveRecord::Base
- validates_presence_of :name, :title
- validates_length_of :title, :minimum => 5
+ validates :name, :presence => true
+ validates :title, :presence => true,
+ :length => { :minimum => 5 }
has_many :comments
has_many :tags
@@ -1242,51 +1220,53 @@ The +:allow_destroy+ option on the nested attribute declaration tells Rails to d
You'll also need to modify +views/posts/_form.html.erb+ to include the tags:
<erb>
-<% @post.tags.build if @post.tags.empty? %>
+<% @post.tags.build %>
<% form_for(@post) do |post_form| %>
<%= post_form.error_messages %>
- <p>
+ <div class="field">
<%= post_form.label :name %><br />
<%= post_form.text_field :name %>
- </p>
- <p>
- <%= post_form.label :title, "Title" %><br />
+ </div>
+ <div class="field">
+ <%= post_form.label :title %><br />
<%= post_form.text_field :title %>
- </p>
- <p>
+ </div>
+ <div class="field">
<%= post_form.label :content %><br />
<%= post_form.text_area :content %>
- </p>
+ </div>
<h2>Tags</h2>
<% post_form.fields_for :tags do |tag_form| %>
- <p>
+ <div class="field">
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
- </p>
+ </div>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
- <p>
- <%= tag_form.label :_delete, 'Remove:' %>
- <%= tag_form.check_box :_delete %>
- </p>
+ <div class="field">
+ <%= tag_form.label :_destroy, 'Remove:' %>
+ <%= tag_form.check_box :_destroy %>
+ </div>
<% end %>
<% end %>
- <p>
- <%= post_form.submit "Save" %>
- </p>
+ <div class="actions">
+ <%= post_form.submit %>
+ </div>
<% end %>
</erb>
+You will note that we also have changed the +form_for(@post) do |f|+ to +form_for(@post) do |post_form|+ and changed all the +f+ method calls as well to show more clearly what is going on.
+
With these changes in place, you'll find that you can edit a post and its tags directly on the same view.
-NOTE. You may want to use JavaScript to dynamically add additional tags on a single form. For an example of this and other advanced techniques, see the "complex form examples application":http://github.com/alloy/complex-form-examples/tree/master.
+NOTE. You may want to use JavaScript to dynamically add additional tags on a single form. For an example of this and other advanced techniques, see the "complex form examples application":http://github.com/mikel/complex-form-examples/.
h3. What's Next?
Now that you've seen your first Rails application, you should feel free to update it and experiment on your own. But you don't have to do everything without help. As you need assistance getting up and running with Rails, feel free to consult these support resources:
-* The "Ruby On Rails guides":http://guides.rubyonrails.org
+* The "Ruby On Rails guides":http://guides.rails.info
* The "Ruby on Rails mailing list":http://groups.google.com/group/rubyonrails-talk
* The "#rubyonrails":irc://irc.freenode.net/#rubyonrails channel on irc.freenode.net
* The "Rails Wiki":http://wiki.rubyonrails.org/
@@ -1300,6 +1280,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2
+* January 24, 2010: Re-write for Rails 3.0 by "Mikel Lindsaar":credits:html#raasdnil
* July 18, 2009: Minor cleanup in anticipation of Rails 2.3.3 by "Mike Gunderloy":credits.html#mgunderloy
* February 1, 2009: Updated for Rails 2.3 by "Mike Gunderloy":credits.html#mgunderloy
* November 3, 2008: Formatting patch from Dave Rothlisberger
@@ -1307,4 +1288,4 @@ h3. Changelog
* October 16, 2008: Revised based on feedback from Pratik Naik by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
* October 13, 2008: First complete draft by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
* October 12, 2008: More detail, rearrangement, editing by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
-* September 8, 2008: initial version by James Miller (not yet approved for publication)
+* September 8, 2008: initial version by "James Miller":credits.html#bensie (not yet approved for publication)
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index 7dfcf4a507..5f324ece60 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -87,7 +87,7 @@
<div id="container">
<div class="wrapper">
<div id="mainCol">
- <%= yield.html_safe! %>
+ <%= yield.html_safe %>
</div>
</div>
</div>
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 0cee413ac3..2cb98e9ee6 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -27,21 +27,74 @@ I'll cover each of these methods in turn. But first, a few words about the very
h4. Rendering by Default: Convention Over Configuration in Action
-You've heard that Rails promotes "convention over configuration." Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to actions. For example, if you have this code in your +BooksController+ class:
+You've heard that Rails promotes "convention over configuration." Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to valid routes. For example, if you have this code in your +BooksController+ class:
<ruby>
-def show
- @book = Book.find(params[:id])
+class BooksController < ApplicationController
+end
+</ruby>
+
+And the following in your routes file:
+
+<ruby>
+resources :books
+</ruby>
+
+And you have a view file +app/views/books/index.html.erb+:
+
+<ruby>
+<h1>Books are coming soon!</h1>
+</ruby>
+
+Rails will automatically render +app/views/books/index.html.erb+ when you navigate to +/books+ and you will see on your screen that "Books are coming soon!"
+
+However a coming soon screen is only minimally useful, so you will soon create your +Book+ model and add the index action to +BooksController+:
+
+<ruby>
+class BooksController < ApplicationController
+ def index
+ @books = Book.all
+ end
end
</ruby>
-Rails will automatically render +app/views/books/show.html.erb+ after running the method. In fact, if you have the default catch-all route in place (+map.connect ':controller/:action/:id'+), Rails will even render views that don't have any code at all in the controller. For example, if you have the default route in place and a request comes in for +/books/sale_list+, Rails will render +app/views/books/sale_list.html.erb+ in response.
+Note that again, we have convention over configuration, in that there is no explicit render at the end of this index action. The rule is that if you do not explicitly render something by the end of the controller action, rails will look for the +action_name.html.erb+ template in the controllers view path and then render that, so in this case, Rails will render the +app/views/books/index.html.erb+ file.
-NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), +.rjs+ for RJS (javascript with embedded ruby) and +.builder+ for Builder (XML generator). You'll also find +.rhtml+ used for ERB templates and +.rxml+ for Builder templates, but those extensions are now formally deprecated and will be removed from a future version of Rails.
+So in our view, we want to display the properties of all the books, we could do this with an ERB template like this:
+
+<ruby>
+<h1>Listing Books</h1>
+
+<table>
+ <tr>
+ <th>Title</th>
+ <th>Summary</th>
+ <th></th>
+ <th></th>
+ <th></th>
+ </tr>
+
+<% @books.each do |book| %>
+ <tr>
+ <td><%= book.title %></td>
+ <td><%= book.content %></td>
+ <td><%= link_to 'Show', book %></td>
+ <td><%= link_to 'Edit', edit_book_path(book) %></td>
+ <td><%= link_to 'Remove', book, :confirm => 'Are you sure?', :method => :delete %></td>
+ </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New book', new_book_path %>
+</ruby>
+
+NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), +.rjs+ for RJS (javascript with embedded ruby) and +.builder+ for Builder (XML generator).
h4. Using +render+
-In most cases, the +ActionController::Base#render+ method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of +render+. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.
+In most cases, the +ActionController::Base#render+ method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customise the behaviour of +render+. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.
TIP: If you want to see the exact results of a call to +render+ without needing to inspect it in a browser, you can call +render_to_string+. This method takes exactly the same options as +render+, but it returns a string instead of sending a response back to the browser.
@@ -53,7 +106,24 @@ Perhaps the simplest thing you can do with +render+ is to render nothing at all:
render :nothing => true
</ruby>
-This will send an empty response to the browser (though it will include any status headers you set with the :status option, discussed below).
+If you look at the response for this using Curl you will see the following:
+
+<shell>
+ $ curl -i 127.0.0.1:3000/books
+HTTP/1.1 200 OK
+Connection: close
+Date: Sun, 24 Jan 2010 09:25:18 GMT
+Transfer-Encoding: chunked
+Content-Type: */*; charset=utf-8
+X-Runtime: 0.014297
+Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
+Cache-Control: no-cache
+
+
+ $
+</shell>
+
+We see there is an empty response (no data after the +Cache-Control+ line), but that Rails has set the response to 200 OK, so the request was successful. You can set the +:status+ options on render to change this response. Rendering nothing can be useful for AJAX requests where all you want to send back to the browser is an acknowledgement that the request was completed.
TIP: You should probably be using the +head+ method, discussed later in this guide, instead of +render :nothing+. This provides additional flexibility and makes it explicit that you're only generating HTTP headers.
@@ -73,7 +143,7 @@ def update
end
</ruby>
-If the call to +update_attributes+ fails, calling the +update+ action in this controller will render the +edit.html.erb+ template belonging to the same controller.
+If the call to +update_attributes+ fails, calling the +update+ action in this controller will render the +edit.html.erb+ template belonging to the same controller.
If you prefer, you can use a symbol instead of a string to specify the action to render:
@@ -89,7 +159,7 @@ def update
end
</ruby>
-To be explicit, you can use +render+ with the +:action+ option (though this is no longer necessary as of Rails 2.3):
+To be explicit, you can use +render+ with the +:action+ option (though this is no longer necessary in Rails 3.0):
<ruby>
def update
@@ -140,6 +210,31 @@ NOTE: By default, the file is rendered without using the current layout. If you
TIP: If you're running on Microsoft Windows, you should use the +:file+ option to render a file, because Windows filenames do not have the same format as Unix filenames.
+h5. Wrapping it up
+
+The above three methods of render (rendering another template within the controller, rendering a template within another controller and rendering an arbitrary file on the file system) are actually all variants of the same action.
+
+In fact, in the BooksController method, inside of the edit action where we want to render the edit template if the book does not update successfully, all of the following render calls would all render the +edit.html.erb+ template in the +views/books+ directory:
+
+<ruby>
+render :edit
+render :action => :edit
+render 'edit'
+render 'edit.html.erb'
+render :action => 'edit'
+render :action => 'edit.html.erb'
+render 'books/edit'
+render 'books/edit.html.erb'
+render :template => 'books/edit'
+render :template => 'books/edit.html.erb'
+render '/path/to/rails/app/views/books/edit'
+render '/path/to/rails/app/views/books/edit.html.erb'
+render :file => '/path/to/rails/app/views/books/edit'
+render :file => '/path/to/rails/app/views/books/edit.html.erb'
+</ruby>
+
+Which one you use is really a matter of style and convention, but the rule of thumb is to use the simplest one that makes sense for the code you are writing.
+
h5. Using +render+ with +:inline+
The +render+ method can do without a view completely, if you're willing to use the +:inline+ option to supply ERB as part of the method call. This is perfectly valid:
@@ -180,7 +275,7 @@ render :text => "OK"
TIP: Rendering pure text is most useful when you're responding to AJAX or web service requests that are expecting something other than proper HTML.
-NOTE: By default, if you use the +:text+ option, the file is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the +:layout => true+ option
+NOTE: By default, if you use the +:text+ option the text is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the +:layout => true+ option.
h5. Rendering JSON
@@ -314,21 +409,21 @@ end
Now, if the current user is a special user, they'll get a special layout when viewing a product. You can even use an inline method to determine the layout:
+You can also decide the layout by passing a Proc object, the block you give the Proc will be given the +controller+ instance, so you can make decisions based on the current request. For example:
+
<ruby>
class ProductsController < ApplicationController
- layout proc { |controller| controller.request.xhr? ? 'popup' : 'application' }
- # ...
+ layout Proc.new { |controller| controller.request.xhr? ? 'popup' : 'application' }
end
</ruby>
h6. Conditional Layouts
-Layouts specified at the controller level support +:only+ and +:except+ options that take either a method name or an array of method names:
+Layouts specified at the controller level support +:only+ and +:except+ options that take either a method name or an array of method names which correspond to method names within the controller:
<ruby>
class ProductsController < ApplicationController
layout "product", :except => [:index, :rss]
- #...
end
</ruby>
@@ -343,7 +438,6 @@ Layouts are shared downwards in the hierarchy, and more specific layouts always
<ruby>
class ApplicationController < ActionController::Base
layout "main"
- #...
end
</ruby>
@@ -351,7 +445,6 @@ end
<ruby>
class PostsController < ApplicationController
- # ...
end
</ruby>
@@ -360,7 +453,6 @@ end
<ruby>
class SpecialPostsController < PostsController
layout "special"
- # ...
end
</ruby>
@@ -418,6 +510,8 @@ def show
end
</ruby>
+Make sure you use +and return+ and not +&& return+ because while the former will work, the latter will not due to operator precedence in the Ruby Language.
+
Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. Therefore, the following will work without errors:
<ruby>
@@ -463,11 +557,11 @@ Consider these actions to see the difference:
<ruby>
def index
- @books = Book.find(:all)
+ @books = Book.all
end
def show
- @book = Book.find(params[:id])
+ @book = Book.find_by_id(params[:id])
if @book.nil?
render :action => "index"
end
@@ -478,19 +572,39 @@ With the code in this form, there will be likely be a problem if the +@book+ var
<ruby>
def index
- @books = Book.find(:all)
+ @books = Book.all
end
def show
- @book = Book.find(params[:id])
+ @book = Book.find_by_id(params[:id])
if @book.nil?
- redirect_to :action => "index"
+ redirect_to :action => :index
end
end
</ruby>
With this code, the browser will make a fresh request for the index page, the code in the +index+ method will run, and all will be well.
+The only downside to this code, is that it requires a round trip to the browser, the browser requested the show action with +/books/1+ and the controller finds that there are no books, so the controller sends out a 301 redirect response to the browser telling it to go to +/books/+, the browser complies and sends a new request back to the controller asking now for the +index+ action, the controller then gets all the books in the database and renders the index template, sending it back down to the browser which then shows it on your screen.
+
+While in a small app, this added latency might not be a problem, it is something to think about when speed of response is of the essence. One way to handle this double request (though a contrived example) could be:
+
+<ruby>
+def index
+ @books = Book.all
+end
+
+def show
+ @book = Book.find_by_id(params[:id])
+ if @book.nil?
+ @books = Book.all
+ render "index", :alert => 'Your book was not found!'
+ end
+end
+</ruby>
+
+Which would detect that there are no books populate the +@books+ instance variable with all the books in the database and then directly render the +index.html.erb+ template returning it to the browser with a flash alert message telling the user what happened.
+
h4. Using +head+ To Build Header-Only Responses
The +head+ method exists to let you send back responses to the browser that have only headers. It provides a more obvious alternative to calling +render :nothing+. The +head+ method takes one response, which is interpreted as a hash of header names and values. For example, you can return only an error header:
@@ -499,12 +613,39 @@ The +head+ method exists to let you send back responses to the browser that have
head :bad_request
</ruby>
+Which would produce the following header:
+
+<shell>
+HTTP/1.1 400 Bad Request
+Connection: close
+Date: Sun, 24 Jan 2010 12:15:53 GMT
+Transfer-Encoding: chunked
+Content-Type: text/html; charset=utf-8
+X-Runtime: 0.013483
+Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
+Cache-Control: no-cache
+</shell>
+
Or you can use other HTTP headers to convey additional information:
<ruby>
head :created, :location => photo_path(@photo)
</ruby>
+Which would produce:
+
+<shell>
+HTTP/1.1 201 Created
+Connection: close
+Date: Sun, 24 Jan 2010 12:16:44 GMT
+Transfer-Encoding: chunked
+Location: /photos/1
+Content-Type: text/html; charset=utf-8
+X-Runtime: 0.083496
+Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
+Cache-Control: no-cache
+</shell>
+
h3. Structuring Layouts
When Rails renders a view as a response, it does so by combining the view with the current layout (using the rules for finding the current layout that were covered earlier in this guide). Within a layout, you have access to three tools for combining different bits of output to form the overall response:
@@ -517,12 +658,14 @@ I'll discuss each of these in turn.
h4. Asset Tags
-Asset tags provide methods for generating HTML that links views to assets like images, javascript, stylesheets, and feeds. There are four types of include tag:
+Asset tags provide methods for generating HTML that links views to assets like images, videos, audio, javascript, stylesheets, and feeds. There are six types of include tag:
* +auto_discovery_link_tag+
* +javascript_include_tag+
* +stylesheet_link_tag+
* +image_tag+
+* +video_tag+
+* +audio_tag+
You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +&lt;head&gt;+ section of a layout.
@@ -663,10 +806,10 @@ You can even use dynamic paths such as +cache/#{current_site}/main/display+.
h5. Linking to Images with +image_tag+
-The +image_tag+ helper builds an HTML +&lt;image&gt;+ tag to the specified file. By default, files are loaded from +public/images+. If you don't specify an extension, +.png+ is assumed by default:
+The +image_tag+ helper builds an HTML +&lt;image /&gt;+ tag to the specified file. By default, files are loaded from +public/images+, note, you must specify the extension, previous versions of Rails would allow you to just call the image name and would append +.png+ if no extension was given, Rails 3.0 does not.
<erb>
-<%= image_tag "header" %>
+<%= image_tag "header.png" %>
</erb>
You can supply a path to the image if you like:
@@ -678,14 +821,93 @@ You can supply a path to the image if you like:
You can supply a hash of additional HTML options:
<erb>
-<%= image_tag "icons/delete.gif", :height => 45 %>
+<%= image_tag "icons/delete.gif", {:height => 45} %>
+</erb>
+
+You can also supply an alternate image to show on mouseover:
+
+<erb>
+<%= image_tag "home.gif", :onmouseover => "menu/home_highlight.gif" %>
+</erb>
+
+Or alternate text if the user has rendering images turned off in their browser, if you do not specify an explicit alt tag, it defaults to the file name of the file, capitalized and with no extension, for example, these two image tags would return the same code:
+
+<erb>
+<%= image_tag "home.gif" %>
+<%= image_tag "home.gif", :alt => "Home" %>
+</erb>
+
+You can also specify a special size tag, in the format "{width}x{height}":
+
+<erb>
+<%= image_tag "home.gif", :size => "50x20" %>
+</erb>
+
+In addition to the above special tags, you can supply a final hash of standard HTML options, such as +:class+ or +:id+ or +:name+:
+
+<erb>
+<%= image_tag "home.gif", :alt => "Go Home",
+ :id => "HomeImage",
+ :class => 'nav_bar' %>
+</erb>
+
+h5. Linking to Videos with +video_tag+
+
+The +video_tag+ helper builds an HTML 5 +&lt;video&gt;+ tag to the specified file. By default, files are loaded from +public/videos+.
+
+<erb>
+<%= video_tag "movie.ogg" %>
+</erb>
+
+Produces
+
+<erb>
+<video src="/videos/movie.ogg" />
</erb>
-There are also three special options you can use with +image_tag+:
+Like an +image_tag+ you can supply a path, either absolute, or relative to the +public/videos+ directory. Additionally you can specify the +:size => "#{width}x#{height}"+ option just like an +image_tag+. Video tags can also have any of the HTML options specified at the end (+id+, +class+ et al).
-* +:alt+ specifies the alt text for the image (which defaults to the file name of the file, capitalized and with no extension)
-* +:size+ specifies both width and height, in the format "{width}x{height}" (for example, "150x125")
-* +:mouseover+ sets an alternate image to be used when the onmouseover event is fired.
+The video tag also supports all of the +&lt;video&gt;+ HTML options through the HTML options hash, including:
+
+* +:poster => 'image_name.png'+, provides an image to put in place of the video before it starts playing.
+* +:autoplay => true+, starts playing the video on page load.
+* +:loop => true+, loops the video once it gets to the end.
+* +:controls => true+, provides browser supplied controls for the user to interact with the video.
+* +:autobuffer => true+, the video will pre load the file for the user on page load.
+
+You can also specify multiple videos to play by passing an array of videos to the +video_tag+:
+
+<erb>
+<%= video_tag ["trailer.ogg", "movie.ogg"] %>
+</erb>
+
+This will produce:
+
+<erb>
+<video><source src="trailer.ogg" /><source src="movie.ogg" /></video>
+</erb>
+
+h5. Linking to Audio files with +audio_tag+
+
+The +audio_tag+ helper builds an HTML 5 +&lt;audio&gt;+ tag to the specified file. By default, files are loaded from +public/audios+.
+
+<erb>
+<%= audio_tag "music.mp3" %>
+</erb>
+
+You can supply a path to the image if you like:
+
+<erb>
+<%= image_tag "music/first_song.mp3" %>
+</erb>
+
+You can also supply a hash of additional options, such as +:id+, +:class+ etc.
+
+Like the +video_tag+, the +audio_tag+ has special options:
+
+* +:autoplay => true+, starts playing the audio on page load
+* +:controls => true+, provides browser supplied controls for the user to interact with the audio.
+* +:autobuffer => true+, the audio will pre load the file for the user on page load.
h4. Understanding +yield+
@@ -752,13 +974,13 @@ h5. Naming Partials
To render a partial as part of a view, you use the +render+ method within the view, and include the +:partial+ option:
<ruby>
-<%= render :partial => "menu" %>
+<%= render "menu" %>
</ruby>
This will render a file named +_menu.html.erb+ at that point within the view being rendered. Note the leading underscore character: partials are named with a leading underscore to distinguish them from regular views, even though they are referred to without the underscore. This holds true even when you're pulling in a partial from another folder:
<ruby>
-<%= render :partial => "shared/menu" %>
+<%= render "shared/menu" %>
</ruby>
That code will pull in the partial from +app/views/shared/_menu.html.erb+.
@@ -768,14 +990,14 @@ h5. Using Partials to Simplify Views
One way to use partials is to treat them as the equivalent of subroutines: as a way to move details out of a view so that you can grasp what's going on more easily. For example, you might have a view that looked like this:
<erb>
-<%= render :partial => "shared/ad_banner" %>
+<%= render "shared/ad_banner" %>
<h1>Products</h1>
<p>Here are a few of our fine products:</p>
...
-<%= render :partial => "shared/footer" %>
+<%= render "shared/footer" %>
</erb>
Here, the +_ad_banner.html.erb+ and +_footer.html.erb+ partials could contain content that is shared among many pages in your application. You don't need to see the details of these sections when you're concentrating on a particular page.
@@ -787,7 +1009,7 @@ h5. Partial Layouts
A partial can use its own layout file, just as a view can use a layout. For example, you might call a partial like this:
<erb>
-<%= render :partial => "link_area", :layout => "graybar" %>
+<%= render "link_area", :layout => "graybar" %>
</erb>
This would look for a partial named +_link_area.html.erb+ and render it using the layout +_graybar.html.erb+. Note that layouts for partials follow the same leading-underscore naming as regular partials, and are placed in the same folder with the partial that they belong to (not in the master +layouts+ folder).
@@ -801,8 +1023,7 @@ You can also pass local variables into partials, making them even more powerful
<erb>
<h1>New zone</h1>
<%= error_messages_for :zone %>
-<%= render :partial => "form", :locals =>
- { :button_label => "Create zone", :zone => @zone } %>
+<%= render :partial => "form", :locals => { :zone => @zone } %>
</erb>
* +edit.html.erb+
@@ -810,8 +1031,7 @@ You can also pass local variables into partials, making them even more powerful
<erb>
<h1>Editing zone</h1>
<%= error_messages_for :zone %>
-<%= render :partial => "form", :locals =>
- { :button_label => "Update zone", :zone => @zone } %>
+<%= render :partial => "form", :locals => { :zone => @zone } %>
</erb>
* +_form.html.erb+
@@ -823,12 +1043,12 @@ You can also pass local variables into partials, making them even more powerful
<%= f.text_field :name %>
</p>
<p>
- <%= f.submit button_label %>
+ <%= f.submit %>
</p>
<% end %>
</erb>
-Although the same partial will be rendered into both views, the label on the submit button is controlled by a local variable passed into the partial.
+Although the same partial will be rendered into both views, Action View's submit helper will return "Create Zone" for the new action and "Update Zone" for the edit action.
Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the +:object+ option:
@@ -838,15 +1058,15 @@ Every partial also has a local variable with the same name as the partial (minus
Within the +customer+ partial, the +customer+ variable will refer to +@new_customer+ from the parent view.
-WARNING: In previous versions of Rails, the default local variable would look for an instance variable with the same name as the partial in the parent. This behavior is deprecated in Rails 2.2 and will be removed in a future version.
+WARNING: In previous versions of Rails, the default local variable would look for an instance variable with the same name as the partial in the parent. This behavior was deprecated in 2.3 and has been removed in Rails 3.0.
If you have an instance of a model to render into a partial, you can use a shorthand syntax:
<erb>
-<%= render :partial => @customer %>
+<%= render @customer %>
</erb>
-Assuming that the +@customer+ instance variable contains an instance of the +Customer+ model, this will use +_customer.html.erb+ to render it.
+Assuming that the +@customer+ instance variable contains an instance of the +Customer+ model, this will use +_customer.html.erb+ to render it and will pass the local variable +customer+ into the partial which will refer to the +@customer+ instance variable in the parent view.
h5. Rendering Collections
@@ -865,63 +1085,70 @@ Partials are very useful in rendering collections. When you pass a collection to
<p>Product Name: <%= product.name %></p>
</erb>
-When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is +_product+, and within the +_product+ partial, you can refer to +product+ to get the instance that is being rendered. To use a custom local variable name within the partial, specify the +:as+ option in the call to the partial:
+When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is +_product+, and within the +_product+ partial, you can refer to +product+ to get the instance that is being rendered.
+
+In Rails 3.0 there is also a shorthand for this, assuming +@posts+ is a collection of +post+ instances, you can simply do in the +index.html.erb+:
<erb>
-<%= render :partial => "product", :collection => @products, :as => :item %>
+<h1>Products</h1>
+<%= render @products %>
</erb>
-With this change, you can access an instance of the +@products+ collection as the +item+ local variable within the partial.
+To produce the same result.
-TIP: Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by +_counter+. For example, if you're rendering +@products+, within the partial you can refer to +product_counter+ to tell you how many times the partial has been rendered.
+Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection:
-You can also specify a second partial to be rendered between instances of the main partial by using the +:spacer_template+ option:
+* +index.html.erb+
<erb>
-<%= render :partial => "product", :collection => @products,
- :spacer_template => "product_ruler" %>
+<h1>Contacts</h1>
+<%= render [customer1, employee1, customer2, employee2] %>
</erb>
-Rails will render the +_product_ruler+ partial (with no data passed in to it) between each pair of +_product+ partials.
-
-There's also a shorthand syntax available for rendering collections. For example, if +@products+ is a collection of products, you can render the collection this way:
-
-* +index.html.erb+
+* +customers/_customer.html.erb+
<erb>
-<h1>Products</h1>
-<%= render :partial => @products %>
+<p>Customer: <%= customer.name %></p>
</erb>
-* +_product.html.erb+
+* +employees/_employee.html.erb+
<erb>
-<p>Product Name: <%= product.name %></p>
+<p>Employee: <%= employee.name %></p>
</erb>
-Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection:
+In this case, Rails will use the customer or employee partials as appropriate for each member of the collection.
-* +index.html.erb+
+h5. Local Variables
+
+To use a custom local variable name within the partial, specify the +:as+ option in the call to the partial:
<erb>
-<h1>Contacts</h1>
-<%= render :partial =>
- [customer1, employee1, customer2, employee2] %>
+<%= render :partial => "product", :collection => @products, :as => :item %>
</erb>
-* +_customer.html.erb+
+With this change, you can access an instance of the +@products+ collection as the +item+ local variable within the partial.
+
+You can also pass in arbitrary local variables to any partial you are rendering with the +:locals => {}+ option:
<erb>
-<p>Name: <%= customer.name %></p>
+<%= render :partial => 'products', :collection => @products,
+ :as => :item, :locals => {:title => "Products Page"} %>
</erb>
-* +_employee.html.erb+
+Would render a partial +_products.html.erb+ once for each instance of +product+ in the +@products+ instance variable passing the instance to the partial as a local variable called +item+ and to each partial, make the local variable +title+ available with the value +Products Page+.
+
+TIP: Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by +_counter+. For example, if you're rendering +@products+, within the partial you can refer to +product_counter+ to tell you how many times the partial has been rendered. This does not work in conjunction with the +:as => :value+ option.
+
+You can also specify a second partial to be rendered between instances of the main partial by using the +:spacer_template+ option:
+
+h5. Spacer Templates
<erb>
-<p>Name: <%= employee.name %></p>
+<%= render @products, :spacer_template => "product_ruler" %>
</erb>
-In this case, Rails will use the customer or employee partials as appropriate for each member of the collection.
+Rails will render the +_product_ruler+ partial (with no data passed in to it) between each pair of +_product+ partials.
h4. Using Nested Layouts
@@ -964,12 +1191,13 @@ On pages generated by +NewsController+, you want to hide the top menu and add a
That's it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the "content" div.
-There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If one is sure she will not subtemplate the +News+ layout, she can omit the +yield(:news_content) or + part.
+There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If you are sure you will not subtemplate the +News+ layout, you can replace the +yield(:news_content) or yield+ with simply +yield+.
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/15
+* January 25, 2010: Rails 3.0 Update by "Mikel Lindsaar":credits.html#raasdnil
* December 27, 2008: Merge patch from Rodrigo Rosenfeld Rosas covering subtemplates
* December 27, 2008: Information on new rendering defaults by "Mike Gunderloy":credits.html#mgunderloy
* November 9, 2008: Added partial collection counter by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 24f0578545..96dbd0dcd0 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -45,7 +45,7 @@ There are two components to routing in Rails: the routing engine itself, which i
h4. Processing the File
-In format, +routes.rb+ is nothing more than one big block sent to +ActionController::Routing::Routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file:
+In format, +routes.rb+ is nothing more than one big block sent to +ApplicationName::Application.routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file:
* RESTful Routes
* Named Routes
@@ -62,7 +62,7 @@ h4. RESTful Routes
RESTful routes take advantage of the built-in REST orientation of Rails to wrap up a lot of routing information in a single declaration. A RESTful route looks like this:
<ruby>
-map.resources :books
+resources :books
</ruby>
h4. Named Routes
@@ -70,16 +70,24 @@ h4. Named Routes
Named routes give you very readable links in your code, as well as handling incoming requests. Here's a typical named route:
<ruby>
+match 'login' => 'sessions#new', :as => 'login'
+</ruby>
+
+If you're coming from Rails 2, this route will be equivalent to:
+
+<ruby>
map.login '/login', :controller => 'sessions', :action => 'new'
</ruby>
+You will also notice that +sessions#new+ is a shorthand for +:controller => 'sessions', :action => 'new'+
+
h4. Nested Routes
Nested routes let you declare that one resource is contained within another resource. You'll see later on how this translates to URLs and paths in your code. For example, if your application includes parts, each of which belongs to an assembly, you might have this nested route declaration:
<ruby>
-map.resources :assemblies do |assemblies|
- assemblies.resources :parts
+resources :assemblies do
+ resources :parts
end
</ruby>
@@ -88,7 +96,7 @@ h4. Regular Routes
In many applications, you'll also see non-RESTful routing, which explicitly connects the parts of a URL to a particular action. For example,
<ruby>
-map.connect 'parts/:number', :controller => 'inventory', :action => 'show'
+match 'parts/:number' => 'inventory#show'
</ruby>
h4. Default Routes
@@ -96,8 +104,7 @@ h4. Default Routes
The default routes are a safety net that catch otherwise-unrouted requests. Many Rails applications will contain this pair of default routes:
<ruby>
-map.connect ':controller/:action/:id'
-map.connect ':controller/:action/:id.:format'
+match ':controller(/:action(/:id(.:format)))'
</ruby>
These default routes are automatically generated when you create a new Rails application. If you're using RESTful routing for everything in your application, you will probably want to remove them. But be sure you're not using the default routes before you remove them!
@@ -126,7 +133,7 @@ h4. CRUD, Verbs, and Actions
In Rails, a RESTful route provides a mapping between HTTP verbs, controller actions, and (implicitly) CRUD operations in a database. A single entry in the routing file, such as
<ruby>
-map.resources :photos
+resources :photos
</ruby>
creates seven different routes in your application:
@@ -164,26 +171,26 @@ photos_path # => "/photos"
h4. Defining Multiple Resources at the Same Time
-If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to +map.resources+:
+If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to +resources+:
<ruby>
-map.resources :photos, :books, :videos
+resources :photos, :books, :videos
</ruby>
This has exactly the same effect as
<ruby>
-map.resources :photos
-map.resources :books
-map.resources :videos
+resources :photos
+resources :books
+resources :videos
</ruby>
h4. Singular Resources
-You can also apply RESTful routing to singleton resources within your application. In this case, you use +map.resource+ instead of +map.resources+ and the route generation is slightly different. For example, a routing entry of
+You can also apply RESTful routing to singleton resources within your application. In this case, you use +resource+ instead of +resources+ and the route generation is slightly different. For example, a routing entry of
<ruby>
-map.resource :geocoder
+resource :geocoder
</ruby>
creates six different routes in your application:
@@ -226,7 +233,7 @@ h5. Using +:controller+
The +:controller+ option lets you use a controller name that is different from the public-facing resource name. For example, this routing entry:
<ruby>
-map.resources :photos, :controller => "images"
+resources :photos, :controller => "images"
</ruby>
will recognize incoming URLs containing +photo+ but route the requests to the Images controller:
@@ -247,7 +254,7 @@ h4. Controller Namespaces and Routing
Rails allows you to group your controllers into namespaces by saving them in folders underneath +app/controllers+. The +:controller+ option provides a convenient way to use these routes. For example, you might have a resource whose controller is purely for admin users in the +admin+ folder:
<ruby>
-map.resources :adminphotos, :controller => "admin/photos"
+resources :adminphotos, :controller => "admin/photos"
</ruby>
If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the +adminphoto_path+ helper, and you follow a link generated with +&lt;%= link_to "show", adminphoto(1) %&gt;+ you will end up on the view generated by +admin/photos/show+, but you will also end up in the same place if you have +&lt;%= link_to "show", {:controller => "photos", :action => "show"} %&gt;+ because Rails will generate the show URL relative to the current URL.
@@ -257,14 +264,14 @@ TIP: If you want to guarantee that a link goes to a top-level controller, use a
You can also specify a controller namespace with the +:namespace+ option instead of a path:
<ruby>
-map.resources :adminphotos, :namespace => "admin", :controller => "photos"
+resources :adminphotos, :namespace => "admin", :controller => "photos"
</ruby>
-This can be especially useful when combined with +with_options+ to map multiple namespaced routes together:
+This can be especially useful when map multiple namespaced routes together using +namespace+ block by:
<ruby>
-map.with_options(:namespace => "admin") do |admin|
- admin.resources :photos, :videos
+namespace :admin do
+ resources :photos, :videos
end
</ruby>
@@ -275,7 +282,7 @@ h5. Using +:singular+
If for some reason Rails isn't doing what you want in converting the plural resource name to a singular name in member routes, you can override its judgment with the +:singular+ option:
<ruby>
-map.resources :teeth, :singular => "tooth"
+resources :teeth, :singular => "tooth"
</ruby>
TIP: Depending on the other code in your application, you may prefer to add additional rules to the +Inflector+ class instead.
@@ -285,7 +292,7 @@ h5. Using +:requirements+
You can use the +:requirements+ option in a RESTful route to impose a format on the implied +:id+ parameter in the singular routes. For example:
<ruby>
-map.resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/}
+resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/}
</ruby>
This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, +/photos/1+ would no longer be recognized by this route, but +/photos/RR27+ would.
@@ -299,7 +306,7 @@ h5. Using +:as+
The +:as+ option lets you override the normal naming for the actual generated paths. For example:
<ruby>
-map.resources :photos, :as => "images"
+resources :photos, :as => "images"
</ruby>
will recognize incoming URLs containing +image+ but route the requests to the Photos controller:
@@ -320,7 +327,7 @@ h5. Using +:path_names+
The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs:
<ruby>
-map.resources :photos, :path_names => { :new => 'make', :edit => 'change' }
+resources :photos, :path_names => { :new => 'make', :edit => 'change' }
</ruby>
This would cause the routing to recognize URLs such as
@@ -343,7 +350,7 @@ h5. Using +:path_prefix+
The +:path_prefix+ option lets you add additional parameters that will be prefixed to the recognized paths. For example, suppose each photo in your application belongs to a particular photographer. In that case, you might declare this route:
<ruby>
-map.resources :photos, :path_prefix => '/photographers/:photographer_id'
+resources :photos, :path_prefix => '/photographers/:photographer_id'
</ruby>
Routes recognized by this entry would include:
@@ -362,9 +369,9 @@ h5. Using +:name_prefix+
You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use +:path_prefix+ to map differently. For example:
<ruby>
-map.resources :photos, :path_prefix => '/photographers/:photographer_id',
+resources :photos, :path_prefix => '/photographers/:photographer_id',
:name_prefix => 'photographer_'
-map.resources :photos, :path_prefix => '/agencies/:agency_id',
+resources :photos, :path_prefix => '/agencies/:agency_id',
:name_prefix => 'agency_'
</ruby>
@@ -377,7 +384,7 @@ h5. Using +:only+ and +:except+
By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option specifies that only certain routes should be generated:
<ruby>
-map.resources :photos, :only => [:index, :show]
+resources :photos, :only => [:index, :show]
</ruby>
With this declaration, a +GET+ request to +/photos+ would succeed, but a +POST+ request to +/photos+ (which would ordinarily be routed to the create action) will fail.
@@ -385,7 +392,7 @@ With this declaration, a +GET+ request to +/photos+ would succeed, but a +POST+
The +:except+ option specifies a route or list of routes that should _not_ be generated:
<ruby>
-map.resources :photos, :except => :destroy
+resources :photos, :except => :destroy
</ruby>
In this case, all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/<em>id</em>+) will be generated.
@@ -411,12 +418,12 @@ end
Each ad is logically subservient to one magazine. Nested routes allow you to capture this relationship in your routing. In this case, you might include this route declaration:
<ruby>
-map.resources :magazines do |magazine|
- magazine.resources :ads
+resources :magazines do
+ resources :ads
end
</ruby>
-TIP: Further below you'll learn about a convenient shortcut for this construct:<br/>+map.resources :magazines, :has_many => :ads+
+TIP: Further below you'll learn about a convenient shortcut for this construct:<br/>+resources :magazines, :has_many => :ads+
In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL:
@@ -437,16 +444,16 @@ h5. Using +:name_prefix+
The +:name_prefix+ option overrides the automatically-generated prefix in nested route helpers. For example,
<ruby>
-map.resources :magazines do |magazine|
- magazine.resources :ads, :name_prefix => 'periodical'
+resources :magazines do
+ resources :ads, :name_prefix => 'periodical'
end
</ruby>
This will create routing helpers such as +periodical_ads_url+ and +periodical_edit_ad_path+. You can even use +:name_prefix+ to suppress the prefix entirely:
<ruby>
-map.resources :magazines do |magazine|
- magazine.resources :ads, :name_prefix => nil
+resources :magazines do
+ resources :ads, :name_prefix => nil
end
</ruby>
@@ -462,16 +469,16 @@ h5. Using +:has_one+ and +:has_many+
The +:has_one+ and +:has_many+ options provide a succinct notation for simple nested routes. Use +:has_one+ to nest a singleton resource, or +:has_many+ to nest a plural resource:
<ruby>
-map.resources :photos, :has_one => :photographer, :has_many => [:publications, :versions]
+resources :photos, :has_one => :photographer, :has_many => [:publications, :versions]
</ruby>
This has the same effect as this set of declarations:
<ruby>
-map.resources :photos do |photo|
- photo.resource :photographer
- photo.resources :publications
- photo.resources :versions
+resources :photos do
+ resource :photographer
+ resources :publications
+ resources :versions
end
</ruby>
@@ -480,9 +487,9 @@ h5. Limits to Nesting
You can nest resources within other nested resources if you like. For example:
<ruby>
-map.resources :publishers do |publisher|
- publisher.resources :magazines do |magazine|
- magazine.resources :photos
+resources :publishers do
+ resources :magazines do
+ resources :photos
end
end
</ruby>
@@ -502,9 +509,9 @@ h5. Shallow Nesting
The +:shallow+ option provides an elegant solution to the difficulties of deeply-nested routes. If you specify this option at any level of routing, then paths for nested resources which reference a specific member (that is, those with an +:id+ parameter) will not use the parent path prefix or name prefix. To see what this means, consider this set of routes:
<ruby>
-map.resources :publishers, :shallow => true do |publisher|
- publisher.resources :magazines do |magazine|
- magazine.resources :photos
+resources :publishers, :shallow => true do
+ resources :magazines do
+ resources :photos
end
end
</ruby>
@@ -522,7 +529,7 @@ This will enable recognition of (among others) these routes:
With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with. If you like, you can combine shallow nesting with the +:has_one+ and +:has_many+ options:
<ruby>
-map.resources :publishers, :has_many => { :magazines => :photos }, :shallow => true
+resources :publishers, :has_many => { :magazines => :photos }, :shallow => true
</ruby>
h4. Route Generation from Arrays
@@ -530,8 +537,8 @@ h4. Route Generation from Arrays
In addition to using the generated routing helpers, Rails can also generate RESTful routes from an array of parameters. For example, suppose you have a set of routes generated with these entries in routes.rb:
<ruby>
-map.resources :magazines do |magazine|
- magazine.resources :ads
+resources :magazines do
+ resources :ads
end
</ruby>
@@ -554,17 +561,16 @@ h4. Namespaced Resources
It's possible to do some quite complex things by combining +:path_prefix+ and +:name_prefix+. For example, you can use the combination of these two options to move administrative resources to their own folder in your application:
<ruby>
-map.resources :photos, :path_prefix => 'admin', :controller => 'admin/photos'
-map.resources :tags, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_tags'
-map.resources :ratings, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_ratings'
+resources :photos, :path_prefix => 'admin', :controller => 'admin/photos'
+resources :tags, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_tags'
+resources :ratings, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_ratings'
</ruby>
The good news is that if you find yourself using this level of complexity, you can stop. Rails supports _namespaced resources_ to make placing resources in their own folder a snap. Here's the namespaced version of those same three routes:
<ruby>
-map.namespace(:admin) do |admin|
- admin.resources :photos,
- :has_many => { :tags, :ratings}
+namespace :admin do
+ resources :photos, :has_many => { :tags, :ratings }
end
</ruby>
@@ -576,18 +582,24 @@ You are not limited to the seven routes that RESTful routing creates by default.
h5. Adding Member Routes
-To add a member route, use the +:member+ option:
+To add a member route, just add +member+ block into resource block:
<ruby>
-map.resources :photos, :member => { :preview => :get }
+resources :photos do
+ member do
+ get :preview
+ end
+end
</ruby>
This will enable Rails to recognize URLs such as +/photos/1/preview+ using the GET HTTP verb, and route them to the preview action of the Photos controller. It will also create the +preview_photo_url+ and +preview_photo_path+ route helpers.
-Within the hash of member routes, each route name specifies the HTTP verb that it will recognize. You can use +:get+, +:put+, +:post+, +:delete+, or +:any+ here. You can also specify an array of methods, if you need more than one but you don't want to allow just anything:
+Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use +get+, +put+, +post+, +delete+, or +any+ here. If you don't have multiple +member+ route, you can also passing +:on+ to the routing.
<ruby>
-map.resources :photos, :member => { :prepare => [:get, :post] }
+resources :photos do
+ get :preview, :on => :member
+end
</ruby>
h5. Adding Collection Routes
@@ -595,32 +607,35 @@ h5. Adding Collection Routes
To add a collection route, use the +:collection+ option:
<ruby>
-map.resources :photos, :collection => { :search => :get }
+resources :photos do
+ collection do
+ get :search
+ end
+end
</ruby>
This will enable Rails to recognize URLs such as +/photos/search+ using the GET HTTP verb, and route them to the search action of the Photos controller. It will also create the +search_photos_url+ and +search_photos_path+ route helpers.
-Just as with member routes, you can specify an array of methods for a collection route:
+Just as with member routes, you can passing +:on+ to the routing.
<ruby>
-map.resources :photos, :collection => { :search => [:get, :post] }
+resources :photos do
+ get :search, :on => :collection
+end
</ruby>
h5. Adding New Routes
-To add a new route (one that creates a new resource), use the +:new+ option:
+As of writing, Rails 3 has deprecated +:new+ option from routing. You will need to explicit define the route using +match+ method
<ruby>
-map.resources :photos, :new => { :upload => :post }
+resources :photos
+match 'photos/new/upload' => 'photos#upload', :as => 'upload_new_photos'
</ruby>
-This will enable Rails to recognize URLs such as +/photos/new/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create the +upload_new_photos_path+ and +upload_new_photos_url+ route helpers.
-
-TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:<br/>+map.resources :photos, :new => { :new => :any }+<br/>This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use.
-
h5. A Note of Caution
-If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +:member+ and +:collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points.
+If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +member+ and +collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points.
h3. Regular Routes
@@ -633,7 +648,7 @@ h4. Bound Parameters
When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: +:controller+ maps to the name of a controller in your application, and +:action+ maps to the name of an action within that controller. For example, consider one of the default Rails routes:
<ruby>
-map.connect ':controller/:action/:id'
+match ':controller(/:action(/:id))'
</ruby>
If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +Photos+ controller, and to make the final parameter (1) available as +params[:id]+.
@@ -643,7 +658,7 @@ h4. Wildcard Components
You can set up as many wildcard symbols within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the matching action as part of the params hash. So, if you set up this route:
<ruby>
-map.connect ':controller/:action/:id/:user_id'
+match ':controller/:action/:id/:user_id'
</ruby>
An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be set to 2.
@@ -653,7 +668,7 @@ h4. Static Text
You can specify static text when creating a route. In this case, the static text is used only for matching the incoming requests:
<ruby>
-map.connect ':controller/:action/:id/with_user/:user_id'
+match ':controller/:action/:id/with_user/:user_id'
</ruby>
This route would respond to URLs such as +/photos/show/1/with_user/2+.
@@ -663,17 +678,17 @@ h4. Querystring Parameters
Rails routing automatically picks up querystring parameters and makes them available in the +params+ hash. For example, with this route:
<ruby>
-map.connect ':controller/:action/:id'
+match ':controller/:action/:id
</ruby>
An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be equal to 2.
h4. Defining Defaults
-You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters in a hash:
+You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters by putting it after +=>+:
<ruby>
-map.connect 'photos/:id', :controller => 'photos', :action => 'show'
+match 'photos/:id' => 'photos#show'
</ruby>
With this route, an incoming URL of +/photos/12+ would be dispatched to the +show+ action within the +Photos+ controller.
@@ -681,8 +696,7 @@ With this route, an incoming URL of +/photos/12+ would be dispatched to the +sho
You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example:
<ruby>
-map.connect 'photos/:id', :controller => 'photos', :action => 'show',
- :defaults => { :format => 'jpg' }
+match 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }
</ruby>
With this route, an incoming URL of +photos/12+ would be dispatched to the +show+ action within the +Photos+ controller, and +params[:format]+ will be set to +jpg+.
@@ -692,7 +706,7 @@ h4. Named Routes
Regular routes need not use the +connect+ method. You can use any other name here to create a _named route_. For example,
<ruby>
-map.logout '/logout', :controller => 'sessions', :action => 'destroy'
+match 'logout' => 'sessions#destroy', :as => :logout
</ruby>
This will do two things. First, requests to +/logout+ will be sent to the +destroy+ action of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code.
@@ -702,15 +716,13 @@ h4. Route Requirements
You can use the +:requirements+ option to enforce a format for any parameter in a route:
<ruby>
-map.connect 'photo/:id', :controller => 'photos', :action => 'show',
- :requirements => { :id => /[A-Z]\d{5}/ }
+match 'photo/:id' => 'photos#show', :requirements => { :id => /[A-Z]\d{5}/ }
</ruby>
This route would respond to URLs such as +/photo/A12345+. You can more succinctly express the same route this way:
<ruby>
-map.connect 'photo/:id', :controller => 'photos', :action => 'show',
- :id => /[A-Z]\d{5}/
+match 'photo/:id' => 'photos#show', :id => /[A-Z]\d{5}/
</ruby>
h4. Route Conditions
@@ -718,8 +730,7 @@ h4. Route Conditions
Route conditions (introduced with the +:conditions+ option) are designed to implement restrictions on routes. Currently, the only supported restriction is +:method+:
<ruby>
-map.connect 'photo/:id', :controller => 'photos', :action => 'show',
- :conditions => { :method => :get }
+match 'photo/:id' => 'photos#show', :conditions => { :method => :get }
</ruby>
As with conditions in RESTful routes, you can specify +:get+, +:post+, +:put+, +:delete+, or +:any+ for the acceptable method.
@@ -729,7 +740,7 @@ h4. Route Globbing
Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example
<ruby>
-map.connect 'photo/*other', :controller => 'photos', :action => 'unknown',
+match 'photo/*other' => 'photos#unknown'
</ruby>
This route would match +photo/12+ or +/photo/long/path/to/12+ equally well, creating an array of path segments as the value of +params[:other]+.
@@ -755,7 +766,7 @@ There's one more way in which routing can do different things depending on diffe
For instance, consider the second of the default routes in the boilerplate +routes.rb+ file:
<ruby>
-map.connect ':controller/:action/:id.:format'
+match ':controller(/:action(/:id(.:format)))'
</ruby>
This route matches requests such as +/photo/edit/1.xml+ or +/photo/show/2.rss+. Within the appropriate action code, you can issue different responses depending on the requested format:
@@ -781,11 +792,10 @@ Mime::Type.register "image/jpg", :jpg
h3. The Default Routes
-When you create a new Rails application, +routes.rb+ is initialized with two default routes:
+When you create a new Rails application, +routes.rb+ is initialized with a default route:
<ruby>
-map.connect ':controller/:action/:id'
-map.connect ':controller/:action/:id.:format'
+match ':controller(/:action(/:id(.:format)))'
</ruby>
These routes provide reasonable defaults for many URLs, if you're not using RESTful routing.
@@ -796,31 +806,24 @@ h3. The Empty Route
Don't confuse the default routes with the empty route. The empty route has one specific purpose: to route requests that come in to the root of the web site. For example, if your site is example.com, then requests to +http://example.com+ or +http://example.com/+ will be handled by the empty route.
-h4. Using +map.root+
+h4. Using +root+
-The preferred way to set up the empty route is with the +map.root+ command:
+The preferred way to set up the empty route is with the +root+ command:
<ruby>
-map.root :controller => "pages", :action => "main"
+root :to => 'pages#main'
</ruby>
The use of the +root+ method tells Rails that this route applies to requests for the root of the site.
-For better readability, you can specify an already-created route in your call to +map.root+:
-
-<ruby>
-map.index 'index', :controller => "pages", :action => "main"
-map.root :index
-</ruby>
-
-Because of the top-down processing of the file, the named route must be specified _before_ the call to +map.root+.
+Because of the top-down processing of the file, the named route must be specified _before_ the call to +root+.
h4. Connecting the Empty String
You can also specify an empty route by explicitly connecting the empty string:
<ruby>
-map.connect '', :controller => "pages", :action => "main"
+match '' => 'pages#main'
</ruby>
TIP: If the empty route does not seem to be working in your application, make sure that you have deleted the file +public/index.html+ from your Rails tree.
@@ -898,6 +901,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3
+* Febuary 1, 2010: Modifies the routing documentation to match new routing DSL in Rails 3, by Prem Sichanugrist
* October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes, by "Mike Gunderloy":credits.html#mgunderloy
* September 23, 2008: Added section on namespaced controllers and routing, by "Mike Gunderloy":credits.html#mgunderloy
* September 10, 2008: initial version by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 9384492486..f445ef4831 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -4,6 +4,43 @@ require 'rails/plugin'
require 'rails/engine'
module Rails
+ # In Rails 3.0, a Rails::Application object was introduced which is nothing more than
+ # an Engine but with the responsibility of coordinating the whole boot process.
+ #
+ # Opposite to Rails::Engine, you can only have one Rails::Application instance
+ # in your process and both Rails::Application and YourApplication::Application
+ # points to it.
+ #
+ # In other words, Rails::Application is Singleton and whenever you are accessing
+ # Rails::Application.config or YourApplication::Application.config, you are actually
+ # accessing YourApplication::Application.instance.config.
+ #
+ # == Initialization
+ #
+ # Rails::Application is responsible for executing all railties, engines and plugin
+ # initializers. Besides, it also executed some bootstrap initializers (check
+ # Rails::Application::Bootstrap) and finishing initializers, after all the others
+ # are executed (check Rails::Application::Finisher).
+ #
+ # == Configuration
+ #
+ # Besides providing the same configuration as Rails::Engine and Rails::Railtie,
+ # the application object has several specific configurations, for example
+ # "allow_concurrency", "cache_classes", "consider_all_requests_local", "filter_parameters",
+ # "logger", "metals", "reload_engines", "reload_plugins" and so forth.
+ #
+ # Check Rails::Application::Configuration to see them all.
+ #
+ # == Routes
+ #
+ # The application object is also responsible for holding the routes and reloading routes
+ # whenever the files change in development.
+ #
+ # == Middlewares and metals
+ #
+ # The Application is also responsible for building the middleware stack and setting up
+ # both application and engines metals.
+ #
class Application < Engine
autoload :Bootstrap, 'rails/application/bootstrap'
autoload :Configurable, 'rails/application/configurable'
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 7887a5d25f..80b72252f1 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -4,6 +4,7 @@ require 'rails/rack'
module Rails
module Configuration
+ # Holds coonfiguration shared between Railtie, Engine and Application.
module Shared
def middleware
@@default_middleware_stack ||= ActionDispatch::MiddlewareStack.new.tap do |middleware|
@@ -87,6 +88,8 @@ module Rails
end
end
+ # Generators configuration which uses method missing to wrap it in a nifty DSL.
+ # It also allows you to set generators fallbacks and aliases.
class Generators #:nodoc:
attr_accessor :aliases, :options, :fallbacks, :colorize_logging
@@ -119,6 +122,7 @@ module Rails
end
end
+ # Holds configs deprecated in 3.0. Will be removed on 3.1.
module Deprecated
def frameworks(*args)
raise "config.frameworks in no longer supported. See the generated " \
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 33d62c8155..096bb9b934 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -2,6 +2,90 @@ require 'active_support/core_ext/module/delegation'
require 'rails/railtie'
module Rails
+ # Rails::Engine allows you to wrap a specific Rails application and share it accross
+ # different applications. Since Rails 3.0, your Rails::Application is nothing
+ # more than an Engine, thus your engines will behave much more closer to an application
+ # since then.
+ #
+ # Any Rails::Engine is also a Rails::Railtie, so the same methods (like rake_tasks and
+ # generators) and configuration available in the latter can also be used in the former.
+ #
+ # == Creating an Engine
+ #
+ # In Rails versions before to 3.0, your gems automatically behaved as Engine, however
+ # this coupled Rails to Rubygems. Since Rails 3.0, if you want a gem to automatically
+ # behave as Engine, you have to specify an Engine for it somewhere inside your plugin
+ # lib folder (similar with how we spceify a Railtie):
+ #
+ # # lib/my_engine.rb
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # engine_name :my_engine
+ # end
+ # end
+ #
+ # Then ensure that this file is loaded at the top of your config/application.rb (or in
+ # your Gemfile) and it will automatically load models, controllers, helpers and metals
+ # inside app, load routes at "config/routes.rb", load locales at "config/locales/*",
+ # load tasks at "lib/tasks/*".
+ #
+ # == Configuration
+ #
+ # Besides the Railtie configuration which is shared across the application, in a
+ # Rails::Engine you can access load_paths, eager_load_paths and load_once_paths,
+ # which differently from a Railtie, are scoped to the current Engine.
+ #
+ # Example:
+ #
+ # class MyEngine < Rails::Engine
+ # # config.middleware is shared configururation
+ # config.middleware.use MyEngine::Middleware
+ #
+ # # Add a load path for this specific Engine
+ # config.load_paths << File.expand_path("../lib/some/path", __FILE__)
+ # end
+ #
+ # == Paths
+ #
+ # Since Rails 3.0, both your Application and Engines does not have hardcoded paths.
+ # This means that you are not required to place your controllers at "app/controllers",
+ # but in any place which you find convenient.
+ #
+ # For example, let's suppose you want to lay your controllers at lib/controllers, all
+ # you need to do is:
+ #
+ # class MyEngine < Rails::Engine
+ # paths.app.controllers = "lib/controllers"
+ # end
+ #
+ # You can also have your controllers being loaded from both "app/controllers" and
+ # "lib/controllers":
+ #
+ # class MyEngine < Rails::Engine
+ # paths.app.controllers << "lib/controllers"
+ # end
+ #
+ # The available paths in an Engine are:
+ #
+ # class MyEngine < Rails::Engine
+ # paths.app = "app"
+ # paths.app.controllers = "app/controllers"
+ # paths.app.helpers = "app/helpers"
+ # paths.app.models = "app/models"
+ # paths.app.metals = "app/metal"
+ # paths.app.views = "app/views"
+ # paths.lib = "lib"
+ # paths.lib.tasks = "lib/tasks"
+ # paths.config = "config"
+ # paths.config.initializers = "config/initializers"
+ # paths.config.locales = "config/locales"
+ # paths.config.routes = "config/routes.rb"
+ # end
+ #
+ # Your Application class adds a couple more paths to this set. And as in your Application,
+ # all folders under "app" are automatically added to the load path. So if you have
+ # "app/observers", it's added by default.
+ #
class Engine < Railtie
autoload :Configurable, "rails/engine/configurable"
autoload :Configuration, "rails/engine/configuration"
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index 64d03e7599..2a746e0fa6 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -1,6 +1,21 @@
require 'rails/engine'
module Rails
+ # Rails::Plugin is nothing more than a Rails::Engine, but since it's loaded too late
+ # in the boot process, it does not have the same configuration powers as a bare
+ # Rails::Engine.
+ #
+ # Opposite to Rails::Railtie and Rails::Engine, you are not supposed to inherit from
+ # Rails::Plugin. Rails::Plugin is automatically configured to be an engine by simply
+ # placing inside vendor/plugins. Since this is done automatically, you actually cannot
+ # declare a Rails::Engine inside your Plugin, otherwise it would cause the same files
+ # to be loaded twice. This means that if you want to ship an Engine as gem it cannot
+ # be used as plugin and vice-versa.
+ #
+ # Besides this conceptual difference, the only difference between Rails::Engine and
+ # Rails::Plugin is that plugins automatically load the file "init.rb" at the plugin
+ # root during the boot process.
+ #
class Plugin < Engine
def self.inherited(base)
raise "You cannot inherit from Rails::Plugin"
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index c038d0ac70..7bb932031c 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -2,6 +2,177 @@ require 'rails/initializable'
require 'rails/configuration'
module Rails
+ # Railtie is the core of the Rails Framework and provides several hooks to extend
+ # Rails and/or modify the initialization process.
+ #
+ # Every major component of Rails (Action Mailer, Action Controller,
+ # Action View, Active Record and Active Resource) are all Railties, so each of
+ # them is responsible to set their own initialization. This makes, for example,
+ # Rails absent of any ActiveRecord hook, allowing any other ORM to hook in.
+ #
+ # Developing a Rails extension does not _require_ any implementation of
+ # Railtie, but if you need to interact with the Rails framework
+ # during boot, or after boot, then Railtie is what you need to do that
+ # interaction.
+ #
+ # For example, the following would need you to implement Railtie in your
+ # plugin:
+ #
+ # * creating initializers
+ # * configuring a Rails framework or the Application, like setting a generator
+ # * adding Rails config.* keys to the environment
+ # * setting up a subscriber to the Rails +ActiveSupport::Notifications+
+ # * adding rake tasks into rails
+ #
+ # == Creating your Railtie
+ #
+ # Implementing Railtie in your Rails extension is done by creating a class
+ # Railtie that has your extension name and making sure that this gets loaded
+ # during boot time of the Rails stack.
+ #
+ # You can do this however you wish, but here is an example if you want to provide
+ # it for a gem that can be used with or without Rails:
+ #
+ # * Create a file (say, lib/my_gem/railtie.rb) which contains class Railtie inheriting from
+ # Rails::Railtie and is namespaced to your gem:
+ #
+ # # lib/my_gem/railtie.rb
+ # module MyGem
+ # class Railtie < Rails::Railtie
+ # railtie_name :mygem
+ # end
+ # end
+ #
+ # * Require your own gem as well as rails in this file:
+ #
+ # # lib/my_gem/railtie.rb
+ # require 'my_gem'
+ # require 'rails'
+ #
+ # module MyGem
+ # class Railtie < Rails::Railtie
+ # railtie_name :mygem
+ # end
+ # end
+ #
+ # * Make sure your Gem loads the railtie.rb file if Rails is loaded first, an easy
+ # way to check is by checking for the Rails constant which will exist if Rails
+ # has started:
+ #
+ # # lib/my_gem.rb
+ # module MyGem
+ # require 'lib/my_gem/railtie' if defined?(Rails)
+ # end
+ #
+ # * Or instead of doing the require automatically, you can ask your users to require
+ # it for you in their Gemfile:
+ #
+ # # #{USER_RAILS_ROOT}/Gemfile
+ # gem "my_gem", :require_as => ["my_gem", "my_gem/railtie"]
+ #
+ # == Initializers
+ #
+ # To add an initialization step from your Railtie to Rails boot process, you just need
+ # to create an initializer block:
+ #
+ # class MyRailtie < Rails::Railtie
+ # initializer "my_railtie.configure_rails_initialization" do
+ # # some initialization behavior
+ # end
+ # end
+ #
+ # If specified, the block can also receive the application object, in case you
+ # need to access some application specific configuration:
+ #
+ # class MyRailtie < Rails::Railtie
+ # initializer "my_railtie.configure_rails_initialization" do |app|
+ # if app.config.cache_classes
+ # # some initialization behavior
+ # end
+ # end
+ # end
+ #
+ # Finally, you can also pass :before and :after as option to initializer, in case
+ # you want to couple it with a specific step in the initialization process.
+ #
+ # == Configuration
+ #
+ # Inside the Railtie class, you can access a config object which contains configuration
+ # shared by all railties and the application:
+ #
+ # class MyRailtie < Rails::Railtie
+ # # Customize the ORM
+ # config.generators.orm :my_railtie_orm
+ #
+ # # Add a middleware
+ # config.middlewares.use MyRailtie::Middleware
+ #
+ # # Add a to_prepare block which is executed once in production
+ # # and before which request in development
+ # config.to_prepare do
+ # MyRailtie.setup!
+ # end
+ # end
+ #
+ # == Loading rake tasks and generators
+ #
+ # If your railtie has rake tasks, you can tell Rails to load them through the method
+ # rake tasks:
+ #
+ # class MyRailtie < Railtie
+ # rake_tasks do
+ # load "path/to/my_railtie.tasks"
+ # end
+ # end
+ #
+ # By default, Rails load generators from your load path. However, if you want to place
+ # your generators at a different location, you can specify in your Railtie a block which
+ # will load them during normal generators lookup:
+ #
+ # class MyRailtie < Railtie
+ # generators do
+ # require "path/to/my_railtie_generator"
+ # end
+ # end
+ #
+ # == Adding your subscriber
+ #
+ # Since version 3.0, Rails ships with a notification system which is used for several
+ # purposes, including logging. If you are sending notifications in your Railtie, you may
+ # want to add a subscriber to consume such notifications for logging purposes.
+ #
+ # The subscriber is added under the railtie_name namespace and only consumes notifications
+ # under the given namespace. For example, let's suppose your railtie is publishing the
+ # following "something_expensive" instrumentation:
+ #
+ # ActiveSupport::Notifications.instrument "my_railtie.something_expensive" do
+ # # something expensive
+ # end
+ #
+ # You can log this instrumentation with your own Rails::Subscriber:
+ #
+ # class MyRailtie::Subscriber < Rails::Subscriber
+ # def something_expensive(event)
+ # info("Something expensive took %.1fms" % event.duration)
+ # end
+ # end
+ #
+ # By registering it:
+ #
+ # class MyRailtie < Railtie
+ # subscriber MyRailtie::Subscriber.new
+ # end
+ #
+ # Take a look in Rails::Subscriber docs for more information.
+ #
+ # == Application, Plugin and Engine
+ #
+ # A Rails::Engine is nothing more than a Railtie with some initializers already set.
+ # And since Rails::Application and Rails::Plugin are engines, the same configuration
+ # described here can be used in all three.
+ #
+ # Be sure to look at the documentation of those specific classes for more information.
+ #
class Railtie
autoload :Configurable, "rails/railtie/configurable"
autoload :Configuration, "rails/railtie/configuration"
diff --git a/railties/lib/rails/subscriber.rb b/railties/lib/rails/subscriber.rb
index 8c62f562d9..9bf22f27a4 100644
--- a/railties/lib/rails/subscriber.rb
+++ b/railties/lib/rails/subscriber.rb
@@ -3,10 +3,10 @@ require 'active_support/notifications'
module Rails
# Rails::Subscriber is an object set to consume ActiveSupport::Notifications
- # on initialization with solely purpose of logging. The subscriber dispatches
- # notifications to a regirested object based on its given namespace.
+ # on initialization with the sole purpose of logging. The subscriber dispatches
+ # notifications to a registered object based on it's given namespace.
#
- # An example would be ActiveRecord subscriber responsible for logging queries:
+ # An example would be an Active Record subscriber responsible for logging queries:
#
# module ActiveRecord
# class Railtie
@@ -18,20 +18,20 @@ module Rails
# end
# end
#
- # It's finally registed as:
+ # Which would be registed as:
#
# Rails::Subscriber.add :active_record, ActiveRecord::Railtie::Subscriber.new
#
- # So whenever a "active_record.sql" notification arrive to Rails::Subscriber,
+ # So whenever an +active_record.sql+ notification arrives to Rails::Subscriber,
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
# the sql method.
#
- # This is useful because it avoids spanning several subscribers just for logging
- # purposes(which slows down the main thread). Besides of providing a centralized
+ # This avoids spanning several subscribers just for logging purposes
+ # (which slows down the main thread). It also provides a centralized
# facility on top of Rails.logger.
#
# Subscriber also has some helpers to deal with logging and automatically flushes
- # all logs when the request finishes (via action_dispatch.callback notification).
+ # all logs when the request finishes.
class Subscriber
mattr_accessor :colorize_logging
self.colorize_logging = true
@@ -97,7 +97,6 @@ module Rails
# option is set to true, it also adds bold to the string. This is based
# on Highline implementation and it automatically appends CLEAR to the end
# of the returned String.
- #
def color(text, color, bold=false)
return text unless colorize_logging
color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)