aboutsummaryrefslogtreecommitdiffstats
path: root/railties/guides/source
diff options
context:
space:
mode:
Diffstat (limited to 'railties/guides/source')
-rw-r--r--railties/guides/source/3_0_release_notes.textile66
-rw-r--r--railties/guides/source/action_controller_overview.textile4
-rw-r--r--railties/guides/source/active_record_querying.textile168
-rw-r--r--railties/guides/source/active_support_core_extensions.textile598
-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.textile1124
-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
10 files changed, 1714 insertions, 857 deletions
diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile
index 639e775bf5..4bee0f537f 100644
--- a/railties/guides/source/3_0_release_notes.textile
+++ b/railties/guides/source/3_0_release_notes.textile
@@ -1,13 +1,13 @@
h2. Rails 3.0: Release Notes
-Rails 3.0 is ponies and rainbows! It's going to cook you dinner and fold your laundry. You're going to wonder how life was ever possible before you it arrived. It's the Best Version of Rails We've Ever Done!
+Rails 3.0 is ponies and rainbows! It's going to cook you dinner and fold your laundry. You're going to wonder how life was ever possible before it arrived. It's the Best Version of Rails We've Ever Done!
But seriously now, it's really good stuff. There are all the good ideas brought over from when the Merb team joined the party and brought a focus on framework agnosticism, slimmer and faster internals, and a handful of tasty APIs. If you're coming to Rails 3.0 from Merb 1.x, you should recognize lots. If you're coming from Rails 2.x, you're going to love it too.
Even if you don't give a hoot about any of our internal cleanups, Rails 3.0 is going to delight. We have a bunch of new features and improved APIs. It's never been a better time to be a Rails developer. Some of the highlights are:
* Brand new router with an emphasis on RESTful declarations
-* New Action Mailer API modelled after Action Controller (now without the agonizing pain of sending multipart messages!)
+* New Action Mailer API modeled after Action Controller (now without the agonizing pain of sending multipart messages!)
* New Active Record chainable query language built on top of relational algebra
* Unobtrusive JavaScript helpers with drivers for Prototype, jQuery, and more coming (end of inline JS)
* Explicit dependency management with Bundler
@@ -20,6 +20,17 @@ endprologue.
WARNING: Rails 3.0 is currently in beta. This means that there are probably bugs and that you should "report them":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/overview if you see them. You also may not want to run the NORAD nuclear launch application off a beta version. But if you're starting development on a new application and you don't mind getting wind in your hair, please do jump on board!
+TIP: To install the Rails 3 prerelease beta using rubygems you have to install all the Rails dependencies first as these will not be installed for you by rubygems:
+
+<shell>
+# Use sudo if your setup requires it
+gem install tzinfo builder i18n memcache-client rack \
+ rake rack-test rack-mount erubis mail text-format \
+ thor bundler
+gem install rails --pre
+</shell>
+
+
h3. Upgrading to Rails 3
If you're upgrading an existing application, it's a great idea to have good test coverage before going in. You should also first upgrade to Rails 2.3.5 and make sure your application still runs as expected before attempting to update to Rails 3. Then take heed of the following changes:
@@ -43,7 +54,7 @@ rails console # instead of script/console
rails g scaffold post title:string # instead of script/generate scaffold post title:string
</shell>
-Run rails --help for a list of all the options.
+Run <tt>rails --help</tt> for a list of all the options.
h4. Dependencies and config.gem
@@ -65,7 +76,6 @@ Aside from Rails Upgrade tool, if you need more help, there are people on IRC an
More information - "The Path to Rails 3: Approaching the upgrade":http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade
-
h3. Creating a Rails 3.0 application
The new installing rails sequence (for the beta) is:
@@ -80,34 +90,32 @@ 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
+Bundler+ and +Gemfile+ makes freezing your Rails application easy as pie with the new dedicated <tt>bundle</tt> command, so <tt>rake freeze</tt> is no longer relevant and has been dropped.
-If you want to bundle straight from the Git repository, you can pass the edge flag:
+If you want to bundle straight from the Git repository, you can pass the +--edge+ flag:
<shell>
$ rails myapp --edge
</shell>
-More information:
-* "Spinning up a new Rails app":http://yehudakatz.com/2009/12/31/spinning-up-a-new-rails-app/
-* "Rails 3 and Passenger":http://cakebaker.42dh.com/2010/01/17/rails-3-and-passenger/
+If you have a local checkout of the Rails repository and want to generate an application using that, you can pass the +--dev+ flag:
+<shell>
+$ ruby /path/to/rails/railties/bin/rails myapp --dev
+</shell>
h3. Rails Architectural Changes
There are six major changes in the architecture of Rails.
-
h4. Railties Restrung
Railties was updated to provide a consistent plugin API for the entire Rails framework as well as a total rewrite of generators and the Rails bindings, the result is that developers can now hook into any significant stage of the generators and application framework in a consistent, defined manner.
-
h4. All Rails core components are decoupled
With the merge of Merb and Rails, one of the big jobs was to remove the tight coupling between Rails core components. This has now been achieved, and all Rails core components are now using the same API that you can use for developing plugins. This means any plugin you make, or any core component replacement (like DataMapper or Sequel) can access all the functionality that the Rails core components have access to and extend and enhance at will.
@@ -131,7 +139,7 @@ More Information: - "Rails Edge Architecture":http://yehudakatz.com/2009/06/11/r
h4. Arel Integration
-"Arel":http://github.com/rails/arel (or Active Relation) has been taken on as the underpinnings of Active Record and is now required for Rails. Arel provides an SQL abstraction that simplifies out Active Record and provides the underpinnings for the relation functionality in Active Record.
+"Arel":http://github.com/brynary/arel (or Active Relation) has been taken on as the underpinnings of Active Record and is now required for Rails. Arel provides an SQL abstraction that simplifies out Active Record and provides the underpinnings for the relation functionality in Active Record.
More information: - "Why I wrote Arel":http://magicscalingsprinkles.wordpress.com/2010/01/28/why-i-wrote-arel/.
@@ -166,7 +174,7 @@ h3. Railties
With the decoupling of the main Rails frameworks, Railties got a huge overhaul so as to make linking up frameworks, engines or plugins as painless and extensible as possible:
-* Each application now has it's own name space, application is started with <tt>YourAppName.boot</tt> for example, makes interacting with other applications a lot easier.
+* Each application now has its own name space, application is started with <tt>YourAppName.boot</tt> for example, makes interacting with other applications a lot easier.
* Anything under <tt>Rails.root/app</tt> is now added to the load path, so you can make <tt>app/observers/user_observer.rb</tt> and Rails will load it without any modifications.
* Rails 3.0 now provides a <tt>Rails.config</tt> object, which provides a central repository of all sorts of Rails wide configuration options.
@@ -203,7 +211,7 @@ Railties now deprecates:
More information:
* "Discovering Rails 3 generators":http://blog.plataformatec.com.br/2010/01/discovering-rails-3-generators
* "Making Generators for Rails 3 with Thor":http://caffeinedd.com/guides/331-making-generators-for-rails-3-with-thor
-
+* "The Rails Module (in Rails 3)":http://litanyagainstfear.com/blog/2010/02/03/the-rails-module/
h3. Action Pack
@@ -251,7 +259,7 @@ Action Dispatch is new in Rails 3.0 and provides a new, cleaner implementation f
<ruby>
# Instead of:
-ActionController::Routing::Routes.draw do
+ActionController::Routing::Routes.draw do |map|
map.resources :posts
end
@@ -386,7 +394,7 @@ Active Record received a lot of attention in Rails 3.0, including abstraction in
h4. Query Interface
-Active Record, through the use of Arel, now returns relations on it's core methods. The existing API in Rails 2.3.x is still supported and will not be deprecated until Rails 3.1 and not removed until Rails 3.2, however, the new API provides the following new methods that all return relations allowing them to be chained together:
+Active Record, through the use of Arel, now returns relations on its core methods. The existing API in Rails 2.3.x is still supported and will not be deprecated until Rails 3.1 and not removed until Rails 3.2, however, the new API provides the following new methods that all return relations allowing them to be chained together:
* <tt>where</tt> - provides conditions on the relation, what gets returned.
* <tt>select</tt> - choose what attributes of the models you wish to have returned from the database.
@@ -421,7 +429,7 @@ Additionally, many fixes in the Active Record branch:
* SQLite 2 support has been dropped in favour of SQLite 3.
* MySQL support for column order.
-* PostgreSQL adapter has had it's +TIME ZONE+ support fixed so it no longer inserts incorrect values.
+* PostgreSQL adapter has had its +TIME ZONE+ support fixed so it no longer inserts incorrect values.
* Support multiple schemas in table names for PostgreSQL.
* PostgreSQL support for the XML data type column.
* +table_name+ is now cached.
@@ -461,7 +469,7 @@ Active Resource was also extracted out to Active Model allowing you to use Activ
* Fix <tt>ActiveResource::ConnectionError#to_s</tt> when +@response+ does not respond to #code or #message, handles Ruby 1.9 compat.
* Add support for errors in JSON format.
* Ensure <tt>load</tt> works with numeric arrays.
-* Recognises a 410 response from remote resource as the resource has been deleted.
+* Recognizes a 410 response from remote resource as the resource has been deleted.
* Add ability to set SSL options on Active Resource connections.
* Setting connection timeout also affects +Net::HTTP+ <tt>open_timeout</tt>.
@@ -478,7 +486,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.
@@ -501,7 +509,7 @@ These are the main changes in Active Support:
* <tt>String#to_time</tt> and <tt>String#to_datetime</tt> handle fractional seconds.
* Added support to new callbacks for around filter object that respond to <tt>:before</tt> and <tt>:after</tt> used in before and after callbacks.
* The <tt>ActiveSupport::OrderedHash#to_a</tt> method returns an ordered set of arrays. Matches Ruby 1.9's <tt>Hash#to_a</tt>.
-* <tt>MissingSourceFile</tt> exists as a constant but it is now just equals to <tt>LoadError</tt>
+* <tt>MissingSourceFile</tt> exists as a constant but it is now just equals to <tt>LoadError</tt>.
* Added <tt>Class#class_attribute</tt>, to be able to declare a class-level attribute whose value is inheritable and overwritable by subclasses.
* Finally removed +DeprecatedCallbacks+ in <tt>ActiveRecord::Associations</tt>.
@@ -520,10 +528,10 @@ The security patch for REXML remains in Active Support because early patchlevels
The following methods have been removed because they are no longer used in the framework:
-* <tt>Object#remove_subclasses_of</tt>, <tt>Object#subclasses_of</tt>, <tt>Object#extend_with_included_modules_from</tt>, <tt>Object#extended_by</tt>
-* <tt>Class#subclasses</tt>, <tt>Class#reachable?</tt>, <tt>Class#remove_class</tt>
-* <tt>Regexp#number_of_captures</tt>
-* <tt>Regexp.unoptionalize</tt>, <tt>Regexp.optionalize</tt>, <tt>Regexp#number_of_captures</tt>
+* +Kernel#daemonize+
+* <tt>Object#remove_subclasses_of</tt> <tt>Object#extend_with_included_modules_from</tt>, <tt>Object#extended_by</tt>
+* <tt>Class#remove_class</tt>
+* <tt>Regexp#number_of_captures</tt>, <tt>Regexp.unoptionalize</tt>, <tt>Regexp.optionalize</tt>, <tt>Regexp#number_of_captures</tt>
h3. Action Mailer
@@ -532,11 +540,11 @@ Action Mailer has been given a new API with TMail being replaced out with the ne
* All mailers are now in <tt>app/mailers</tt> by default.
* Can now send email using new API with three methods: +attachments+, +headers+ and +mail+.
-* ActionMailer emailing methods now return Mail::Message objects, which can then be sent the +deliver+ message to send itself.
+* Action Mailer emailing methods now return <tt>Mail::Message</tt> objects, which can then be sent the +deliver+ message to send itself.
* All delivery methods are now abstracted out to the Mail gem.
* The mail delivery method can accept a hash of all valid mail header fields with their value pair.
-* The mail delivery method acts in a similar way to Action Controller's respond_to block, and you can explicitly or implicitly render templates. Action Mailer will turn the email into a multipart email as needed.
-* You can pass a proc to the <tt>format.mime_type</tt> calls within the mail block and explicitly render specific types of text, or add layouts or different templates. The +render+ call inside the proc is from Abstract Controller, so all the same options are available as they are in Action Controller.
+* The +mail+ delivery method acts in a similar way to Action Controller's +respond_to+, and you can explicitly or implicitly render templates. Action Mailer will turn the email into a multipart email as needed.
+* You can pass a proc to the <tt>format.mime_type</tt> calls within the mail block and explicitly render specific types of text, or add layouts or different templates. The +render+ call inside the proc is from Abstract Controller and supports the same options.
* What were mailer unit tests have been moved to functional tests.
Deprecations:
@@ -545,7 +553,7 @@ Deprecations:
* Mailer dynamic <tt>create_method_name</tt> and <tt>deliver_method_name</tt> are deprecated, just call <tt>method_name</tt> which now returns a <tt>Mail::Message</tt> object.
* <tt>ActionMailer.deliver(message)</tt> is deprecated, just call <tt>message.deliver</tt>.
* <tt>template_root</tt> is deprecated, pass options to a render call inside a proc from the <tt>format.mime_type</tt> method inside the <tt>mail</tt> generation block
-* The body method to define instance variables is deprecated (<tt>body {:ivar => value}</tt>), just declare instance variables in the method directly and they will be available in the view.
+* The +body+ method to define instance variables is deprecated (<tt>body {:ivar => value}</tt>), just declare instance variables in the method directly and they will be available in the view.
* Mailers being in <tt>app/models</tt> is deprecated, use <tt>app/mailers</tt> instead.
More Information:
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..3a62ee567d 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,22 @@ 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+
+* +offset+
+* +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 +81,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 +99,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 +114,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 +131,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 +151,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 +159,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 +225,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 +242,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 +250,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 +268,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 +279,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 +301,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 +322,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 +344,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 +358,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 +374,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 +383,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 +390,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 +440,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 +459,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 +473,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 +496,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 +509,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 +653,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 +666,7 @@ SELECT categories.* FROM categories
h5. Joining Multiple Associations
<ruby>
-Post.all :joins => [:category, :comments]
+Post.joins(:category, :comments)
</ruby>
This produces:
@@ -703,13 +680,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 +695,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 +727,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 +includes+ method of the +Model.find+ call. With +includes+, 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.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
@@ -772,12 +749,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 +includes+ method.
h5. Array of Multiple Associations
<ruby>
-Post.all :include => [:category, :comments]
+Post.includes(:category, :comments)
</ruby>
This loads all the posts and the associated category and comments for each post.
@@ -785,14 +762,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).includes(: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 +866,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 +includes+ 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'").includes("orders")
</ruby>
Which will execute:
@@ -957,5 +934,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 bf39ed4c9f..ce87919b89 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -323,6 +323,40 @@ TIP: Since +with_options+ forwards calls to its receiver they can be nested. Eac
NOTE: Defined in +active_support/core_ext/object/with_options.rb+.
+h5. +subclasses_of+
+
+The method +subclasses_of+ receives an arbitrary number of class objects and returns all their anonymous or reachable descendants as a single array:
+
+<ruby>
+class C; end
+subclasses_of(C) # => []
+
+subclasses_of(Integer) # => [Bignum, Fixnum]
+
+module M
+ class A; end
+ class B1 < A; end
+ class B2 < A; end
+end
+
+module N
+ class C < M::B1; end
+end
+
+subclasses_of(M::A) # => [N::C, M::B2, M::B1]
+</ruby>
+
+The order in which these classes are returned is unspecified. The returned collection may have duplicates:
+
+<ruby>
+subclasses_of(Numeric, Integer)
+# => [Bignum, Float, Fixnum, Integer, Date::Infinity, Rational, BigDecimal, Bignum, Fixnum]
+</ruby>
+
+See also +Class#subclasses+ in "Extensions to +Class+ FIX THIS LINK":FIXME.
+
+NOTE: Defined in +active_support/core_ext/object/extending.rb+.
+
h4. Instance Variables
Active Support provides several methods to ease access to instance variables.
@@ -341,7 +375,7 @@ end
C.new(0, 1).instance_variable_names # => ["@y", "@x"]
</ruby>
-WARNING: The order in which the names are returned is unespecified, and it indeed depends on the version of the interpreter.
+WARNING: The order in which the names are returned is unspecified, and it indeed depends on the version of the interpreter.
NOTE: Defined in +active_support/core_ext/object/instance_variables.rb+.
@@ -422,11 +456,23 @@ end
NOTE: Defined in +active_support/core_ext/kernel/reporting.rb+.
-h3. Extensions to +Module+
+h4. +require_library_or_gem+
+
+The convenience method +require_library_or_gem+ tries to load its argument with a regular +require+ first. If it fails loads +rubygems+ and tries again.
+
+If the first attempt is a failure and +rubygems+ can't be loaded the method raises +LoadError+. On the other hand, if +rubygems+ is available but the argument is not loadable as a gem, the method gives up and +LoadError+ is also raised.
+
+For example, that's the way the MySQL adapter loads the MySQL library:
-h4. Aliasing
+<ruby>
+require_library_or_gem('mysql')
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/kernel/requires.rb+.
+
+h3. Extensions to +Module+
-h5. +alias_method_chain+
+h4. +alias_method_chain+
Using plain Ruby you can wrap methods with other methods, that's called _alias chaining_.
@@ -476,6 +522,8 @@ Rails uses +alias_method_chain+ all over the code base. For example validations
NOTE: Defined in +active_support/core_ext/module/aliasing.rb+.
+h4. Attributes
+
h5. +alias_attribute+
Model attributes have a reader, a writer, and a predicate. You can aliase a model attribute having the corresponding three methods defined for you in one shot. As in other aliasing methods, the new name is the first argument, and the old name is the second (my mnemonic is they go in the same order as if you did an assignment):
@@ -490,13 +538,434 @@ end
NOTE: Defined in +active_support/core_ext/module/aliasing.rb+.
-h4. Delegation
+h5. +attr_accessor_with_default+
+
+The method +attr_accessor_with_default+ serves the same purpose as the Ruby macro +attr_accessor+ but allows you to set a default value for the attribute:
+
+<ruby>
+class Url
+ attr_accessor_with_default :port, 80
+end
+
+Url.new.port # => 80
+</ruby>
+
+The default value can be also specified with a block, which is called in the context of the corresponding object:
+
+<ruby>
+class User
+ attr_accessor :name, :surname
+ attr_accessor_with_default(:full_name) {
+ [name, surname].compact.join(" ")
+ }
+end
+
+u = User.new
+u.name = 'Xavier'
+u.surname = 'Noria'
+u.full_name # => "Xavier Noria"
+</ruby>
+
+The result is not cached, the block is invoked in each call to the reader.
+
+You can overwrite the default with the writer:
+
+<ruby>
+url = Url.new
+url.host # => 80
+url.host = 8080
+url.host # => 8080
+</ruby>
+
+The default value is returned as long as the attribute is unset. The reader does not rely on the value of the attribute to know whether it has to return the default. It rather monitors the writer: if there's any assignment the value is no longer considered to be unset.
+
+Active Resource uses this macro to set a default value for the +:primary_key+ attribute:
+
+<ruby>
+attr_accessor_with_default :primary_key, 'id'
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/module/attr_accessor_with_default.rb+.
+
+h5. Internal Attributes
+
+When you are defining an attribute in a class that is meant to be subclassed name collisions are a risk. That's remarkably important for libraries.
+
+Active Support defines the macros +attr_internal_reader+, +attr_internal_writer+, and +attr_internal_accessor+. They behave like their Ruby builtin +attr_*+ counterparts, except they name the unerlying instace variable in a way that makes collisions less likely.
+
+The macro +attr_internal+ is a synonim for +attr_internal_accessor+:
+
+<ruby>
+# library
+class ThirdPartyLibrary::Crawler
+ attr_internal :log_level
+end
+
+# client code
+class MyCrawler < ThirdPartyLibrary::Crawler
+ attr_accessor :log_level
+end
+</ruby>
+
+In the previous example it could be the case that +:log_level+ does not belong to the public interface of the library and it is only used for development. The client code, unaware of the potential conflict, subclasses and defines its own +:log_level+. Thanks to +attr_internal+ there's no collision.
+
+By default the internal instance variable is named with a leading underscore, +@_log_level+ in the example above. That's configurable via +Module.attr_internal_naming_format+ though, you can pass any +sprintf+-like format string with a leading +@+ and a +%s+ somewhere, which is where the name will be placed. The default is +"@_%s"+.
+
+Rails uses internal attributes in a few spots, for examples for views:
+
+<ruby>
+module ActionView
+ class Base
+ attr_internal :captures
+ attr_internal :request, :layout
+ attr_internal :controller, :template
+ end
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/module/attr_internal.rb+.
+
+h5. Module Attributes
+
+The macros +mattr_reader+, +mattr_writer+, and +mattr_accessor+ are analogous to the +cattr_*+ macros defined for class. Check "Class Attributes":#class-attributes.
+
+For example, the dependencies mechanism uses them:
+
+<ruby>
+module ActiveSupport
+ module Dependencies
+ mattr_accessor :warnings_on_first_load
+ mattr_accessor :history
+ mattr_accessor :loaded
+ mattr_accessor :mechanism
+ mattr_accessor :load_paths
+ mattr_accessor :load_once_paths
+ mattr_accessor :autoloaded_constants
+ mattr_accessor :explicitly_unloadable_constants
+ mattr_accessor :logger
+ mattr_accessor :log_activity
+ mattr_accessor :constant_watch_stack
+ mattr_accessor :constant_watch_stack_mutex
+ end
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/module/attribute_accessors.rb+.
+
+h4. Method Delegation
+
+The class method +delegate+ offers an easy way to forward methods.
+
+For example, if +User+ has some details like the age factored out to +Profile+, it could be handy to still be able to acces such attribute directly, <tt>user.age</tt>, instead of having to explicit the chain <tt>user.profile.age</tt>.
+
+That can be accomplished by hand:
+
+<ruby>
+class User
+ has_one :profile
+
+ def age
+ profile.age
+ end
+end
+</ruby>
+
+But with +delegate+ you can make that shorter and the intention even more obvious:
+
+<ruby>
+class User
+ has_one :profile
+
+ delegate :age, to => :profile
+end
+</ruby>
+
+The macro accepts more than one method:
+
+<ruby>
+class User
+ has_one :profile
+
+ delegate :age, :avatar, :twitter_username, to => :profile
+end
+</ruby>
+
+Methods can be delegated to objects returned by methods, as in the examples above, but also to instance variables, class variables, and constants. Just pass their names as symbols or strings, including the at signs in the last cases.
+
+For example, +ActionView::Base+ delegates +erb_trim_mode=+:
+
+<ruby>
+module ActionView
+ class Base
+ delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
+ end
+end
+</ruby>
+
+In fact, you can delegate to any expression passed as a string. It will be evaluated in the context of the receiver. Controllers for example delegate alerts and notices to the current flash:
+
+<ruby>
+delegate :alert, :notice, :to => "request.flash"
+</ruby>
+
+If the target is +nil+ calling any delegated method will raise an exception even if +nil+ responds to such method. You can override this behavior setting the option +:allow_nil+ to true, in which case the forwarded call will simply return +nil+.
+
+If the target is a method, the name of delegated methods can also be prefixed. If the +:prefix+ option is set to (exactly) the +true+ object, the value of the +:to+ option is prefixed:
+
+<ruby>
+class Invoice
+ belongs_to :customer
+
+ # defines a method called customer_name
+ delegate :name, :to => :customer, :prefix => true
+end
+</ruby>
+
+And a custom prefix can be set as well, in that case it does not matter wheter the target is a method or not:
+
+<ruby>
+class Account
+ belongs_to :user
+
+ # defines a method called admin_email
+ delegate :email, :to => :user, :prefix => 'admin'
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/module/delegation+.
+
+h4. Parents
+
+h5. +parent+
+
+The +parent+ method on a nested named module returns the module that contains its corresponding constant:
+
+<ruby>
+module X
+ module Y
+ module Z
+ end
+ end
+end
+M = X::Y::Z
+
+X::Y::Z.parent # => X::Y
+M.parent # => X::Y
+</ruby>
+
+If the module is anonymous or belongs to the top-level, +parent+ returns +Object+.
-The class method +delegate+
+WARNING: Note that in that case +parent_name+ returns +nil+.
+
+NOTE: Defined in +active_support/core_ext/module/introspection.rb+.
+
+h5. +parent_name+
+
+The +parent_name+ method on a nested named module returns the fully-qualified name of the module that contains its corresponding constant:
+
+<ruby>
+module X
+ module Y
+ module Z
+ end
+ end
+end
+M = X::Y::Z
+
+X::Y::Z.parent_name # => "X::Y"
+M.parent_name # => "X::Y"
+</ruby>
+
+For top-level or anonymous modules +parent_name+ returns +nil+.
+
+WARNING: Note that in that case +parent+ returns +Object+.
+
+NOTE: Defined in +active_support/core_ext/module/introspection.rb+.
+
+h5. +parents+
+
+The method +parents+ calls +parent+ on the receiver and upwards until +Object+ is reached. The chain is returned in an array, from bottom to top:
+
+<ruby>
+module X
+ module Y
+ module Z
+ end
+ end
+end
+M = X::Y::Z
+
+X::Y::Z.parents # => [X::Y, X, Object]
+M.parents # => [X::Y, X, Object]
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/module/introspection.rb+.
+
+h4. Constants
+
+The method +local_constants+ returns the names of the constants that have been defined in the receiver module:
+
+<ruby>
+module X
+ X1 = 1
+ X2 = 2
+ module Y
+ Y1 = :y1
+ X1 = :overrides_X1_above
+ end
+end
+
+X.local_constants # => ["X2", "X1", "Y"], assumes Ruby 1.8
+X::Y.local_constants # => ["X1", "Y1"], assumes Ruby 1.8
+</ruby>
+
+The names are returned as strings in Ruby 1.8, and as symbols in Ruby 1.9. The method +local_constant_names+ returns always strings.
+
+WARNING: This method is exact if running under Ruby 1.9. In previous versions it may miss some constants if their value in some ancestor stores the exact same object than in the receiver.
+
+NOTE: Defined in +active_support/core_ext/module/introspection.rb+.
+
+h4. Synchronization
+
+The +synchronize+ macro declares a method to be synchronized:
+
+<ruby>
+class Counter
+ @@mutex = Mutex.new
+ attr_reader :value
+
+ def initialize
+ @value = 0
+ end
+
+ def incr
+ @value += 1 # non-atomic
+ end
+ synchronize :incr, :with => '@@mutex'
+end
+</ruby>
+
+The method receives the name of an action, and a +:with+ option with code. The code is evaluated in the context of the receiver each time the method is invoked, and it should evaluate to a +Mutex+ instance or any other object that responds to +synchronize+ and accepts a block.
+
+NOTE: Defined in +active_support/core_ext/module/synchronization.rb+.
+
+h4. Reachable
+
+A named module is reachable if it is stored in its correspoding constant. It means you can reach the module object via the constant.
+
+That is what ordinarily happens, if a module is called "M", the +M+ constant exists and holds it:
+
+<ruby>
+module M
+end
+
+M.reachable? # => true
+</ruby>
+
+But since constants and modules are indeed kind of decoupled, module objects can become unreachable:
+
+<ruby>
+module M
+end
+
+orphan = Object.send(:remove_const, :M)
+
+# The module object is orphan now but it still has a name.
+orphan.name # => "M"
+
+# You cannot reach it via the constant M because it does not even exist.
+orphan.reachable? # => false
+
+# Let's define a module called "M" again.
+module M
+end
+
+# The constant M exists now again, and it stores a module
+# object called "M", but it is a new instance.
+orphan.reachable? # => false
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/module/reachable.rb+.
+
+h4. Anonymous
+
+A module may or may not have a name:
+
+<ruby>
+module M
+end
+M.name # => "M"
+
+N = Module.new
+N.name # => "N"
+
+Module.new.name # => "" in 1.8, nil in 1.9
+</ruby>
+
+You can check whether a module has a name with the predicate +anonymous?+:
+
+<ruby>
+module M
+end
+M.anonymous? # => false
+
+Module.new.anonymous? # => true
+</ruby>
+
+Note that being unreachable does not imply being anonymous:
+
+<ruby>
+module M
+end
+
+m = Object.send(:remove_const, :M)
+
+m.reachable? # => false
+m.anonymous? # => false
+</ruby>
+
+though an anonymous module is unreachable by definition.
+
+NOTE: Defined in +active_support/core_ext/module/anonymous.rb+.
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,9 +1056,11 @@ 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
+h4. Descendants
-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:
+h5. +subclasses+
+
+The +subclasses+ method returns the names of all the anonymous or reachable descendants of its receiver as an array of strings:
<ruby>
class C; end
@@ -614,56 +1085,76 @@ 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+.
+WARNING: This method is redefined in some Rails core classes.
-h4. Class Removal
+NOTE: Defined in +active_support/core_ext/class/subclasses.rb+.
-Roughly speaking, the +remove_class+ method removes the class objects passed as arguments:
+h3. Extensions to +String+
+
+h4. Output Safety
+
+Inserting data into HTML templates needs extra care. For example you can't just interpolate +@review.title+ verbatim into an HTML page. On one hand if the review title is "Flanagan & Matz rules!" the output won't be well-formed because an ampersand has to be escaped as "&amp;amp;". On the other hand, depending on the application that may be a big security hole because users can inject malicious HTML setting a hand-crafted review title. Check out the "section about cross-site scripting in the Security guide":security.html#cross-site-scripting-xss for further information about the risks.
+
+Active Support has the concept of <i>(html) safe</i> strings since Rails 3. A safe string is one that is marked as being insertable into HTML as is. It is trusted, no matter whether it has been escaped or not.
+
+Strings are considered to be <i>unsafe</i> by default:
<ruby>
-Class.remove_class(Hash, Dir) # => [Hash, Dir]
-Hash # => NameError: uninitialized constant Hash
-Dir # => NameError: uninitialized constant Dir
+"".html_safe? # => false
</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:
+You can obtain a safe string from a given one with the +html_safe+ method:
<ruby>
-module M
- class A; end
- class B < A; end
-end
-
-A2 = M::A
+s = "".html_safe
+s.html_safe? # => true
+</ruby>
-M::A.object_id # => 13053950
-Class.remove_class(M::A)
+It is important to understand that +html_safe+ performs no escaping whatsoever, it is just an assertion:
-M::B.superclass.object_id # => 13053950 (same object as before)
-A2.name # => "M::A" (name is hard-coded in object)
+<ruby>
+s = "<script>...</script>".html_safe
+s.html_safe? # => true
+s # => "<script>...</script>"
</ruby>
-WARNING: Removing fundamental classes like +String+ can result in really funky behaviour.
+It is your responsability to ensure calling +html_safe+ on a particular string is fine.
+
+NOTE: For performance reasons safe strings are implemented in a way that cannot offer an in-place +html_safe!+ variant.
-The method +remove_subclasses+ provides a shortcut for removing all descendants of a given class, where "removing" has the meaning explained above:
+If you append onto a safe string, either in-place with +concat+/<tt><<</tt>, or with <tt>+</tt>, the result is a safe string. Unsafe arguments are escaped:
<ruby>
-class A; end
-class B1 < A; end
-class B2 < A; end
-class C < A; end
+"".html_safe + "<" # => "&lt;"
+</ruby>
-A.subclasses # => ["C", "B2", "B1"]
-A.remove_subclasses
-A.subclasses # => []
-C # => NameError: uninitialized constant C
+Safe arguments are directly appended:
+
+<ruby>
+"".html_safe + "<".html_safe # => "<"
</ruby>
-See also +Object#remove_subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
+These methods should not be used in ordinary views. In Rails 3 unsafe values are automatically escaped:
-NOTE: Defined in +active_support/core_ext/class/removal.rb+.
+<erb>
+<%= @review.title %> <%# fine in Rails 3, escaped if needed %>
+</erb>
-h3. Extensions to +String+
+To insert something verbatim use the +raw+ helper rather than calling +html_safe+:
+
+<erb>
+<%= raw @cms.current_template %> <%# inserts @cms.current_template as is %>
+</erb>
+
+The +raw+ helper calls +html_safe+ for you:
+
+<ruby>
+def raw(stringish)
+ stringish.to_s.html_safe
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/string/output_safety.rb+.
h4. +squish+
@@ -816,7 +1307,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+
@@ -1765,7 +2264,7 @@ File.atomic_write(joined_asset_path) do |cache|
end
</ruby>
-To accomplish this +atomic_write+ creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed. If the target file exists +atomic_write+ overwrites it and keeps owners and permissions.
+To accomplish this +atomic_write+ creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed, which is an atomic operation on POSIX systems. If the target file exists +atomic_write+ overwrites it and keeps owners and permissions.
WARNING. Note you can't append with +atomic_write+.
@@ -1799,20 +2298,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)
-...
-$ rails runner 'require "nonexistent"'
-...: no such file to load -- nonexistent (MissingSourceFile)
-...
-</shell>
+Given a path name +is_missing?+ tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension).
-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).
-
-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 +2310,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 a6ac7f0f5b..8ddf0cf3cf 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -9,17 +9,17 @@ 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
+h3. Guide Assumptions
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
+* A working installation of the "SQLite3 Database":http://www.sqlite.org
-It is highly recommended that you *familiarize yourself with Ruby before diving into Rails*. You will find it much easier to follow what's going on with a Rails application if you understand basic Ruby syntax. Rails isn't going to magically revolutionize the way you write web applications if you have no experience with the language it uses. There are some good free resources on the internet for learning Ruby, including:
+Rails is a web application framework running on the Ruby programming language. If you have no prior experience with Ruby, you will find a very steep learning curve diving straight into Rails. There are some good free resources on the internet for learning Ruby, including:
* "Mr. Neighborly's Humble Little Ruby Book":http://www.humblelittlerubybook.com
* "Programming Ruby":http://www.ruby-doc.org/docs/ProgrammingRuby/
@@ -27,19 +27,19 @@ It is highly recommended that you *familiarize yourself with Ruby before diving
h3. What is Rails?
-Rails is a web development framework written in the Ruby language. It is designed to make programming web applications easier by making assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Longtime Rails developers also report that it makes web application development more fun.
+Rails is a web application development framework written in the Ruby language. It is designed to make programming web applications easier by making assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Experienced Rails developers also report that it makes web application development more fun.
-Rails is opinionated software. That is, it assumes that there is a best way to do things, and it's designed to encourage that best way - and in some cases to discourage alternatives. If you learn "The Rails Way" you'll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience.
+Rails is opinionated software. It makes the assumption that there is a "best" way to do things, and it's designed to encourage that way - and in some cases to discourage alternatives. If you learn "The Rails Way" you'll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience.
The Rails philosophy includes several guiding principles:
* DRY - "Don't Repeat Yourself" - suggests that writing the same code over and over again is a bad thing.
-* Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you're going to do it, rather than letting you tweak every little thing through endless configuration files.
+* Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you're going to do it, rather than requiring you to specify every little thing through endless configuration files.
* REST is the best pattern for web applications - organizing your application around resources and standard HTTP verbs is the fastest way to go.
h4. The MVC Architecture
-Rails is organized around the Model, View, Controller architecture, usually just called MVC. MVC benefits include:
+At the core of Rails is the Model, View, Controller architecture, usually just called MVC. MVC benefits include:
* Isolation of business logic from the user interface
* Ease of keeping code DRY
@@ -59,15 +59,23 @@ Controllers provide the "glue" between models and views. In Rails, controllers a
h4. The Components of Rails
-Rails provides a full stack of components for creating web applications, including:
+Rails ships as many individual components.
-* Action Controller
-* Action View
-* Active Record
+* Action Pack
+ ** Action Controller
+ ** Action Dispatch
+ ** Action View
* Action Mailer
+* Active Model
+* Active Record
* Active Resource
-* Railties
* Active Support
+* Railties
+
+
+h5. Action Pack
+
+Action Pack is a single gem that contains Action Controller, Action View and Action Dispatch. The "VC" part of "MVC".
h5. Action Controller
@@ -77,38 +85,46 @@ h5. Action View
Action View manages the views of your Rails application. It can create both HTML and XML output by default. Action View manages rendering templates, including nested and partial templates, and includes built-in AJAX support.
-h5. Active Record
+h5. Action Dispatch
-Active Record is the base for the models in a Rails application. It provides database independence, basic CRUD functionality, advanced finding capabilities, and the ability to relate models to one another, among other services.
+Action Dispatch handles routing of web requests and dispatches them as you want, either to your application, any other Rack application.
h5. Action Mailer
Action Mailer is a framework for building e-mail services. You can use Action Mailer to send emails based on flexible templates, or to receive and process incoming email.
-h5. Active Resource
+h5. Active Model
-Active Resource provides a framework for managing the connection between business objects and RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.
+Active Model provides a defined interface between the Action Pack gem services and Object Relationship Mapping gems such as Active Record. Active Model allows Rails to utilize other ORM frameworks in place of Active Record if your application needs this.
-h5. Railties
+h5. Active Record
+
+Active Record is the base for the models in a Rails application. It provides database independence, basic CRUD functionality, advanced finding capabilities, and the ability to relate models to one another, among other services.
-Railties is the core Rails code that builds new Rails applications and glues the various frameworks together in any Rails application.
+h5. Active Resource
+
+Active Resource provides a framework for managing the connection between business objects and RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.
h5. Active Support
Active Support is an extensive collection of utility classes and standard Ruby library extensions that are used in the Rails, both by the core code and by your applications.
+h5. Railties
+
+Railties is the core Rails code that builds new Rails applications and glues the various frameworks and plugins together in any Rails application.
+
h4. REST
-The foundation of the RESTful architecture is generally considered to be Roy Fielding's doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:
+Rest stands for Representational State Transfer and is the foundation of the RESTful architecture. This is generally considered to be Roy Fielding's doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. While you can read through the thesis, REST in terms of Rails boils down to two main principles:
-* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
+* Using resource identifiers such as URLs to represent resources.
* Transferring representations of the state of that resource between system components.
For example, to a Rails application a request such as this:
<tt>DELETE /photos/17</tt>
-would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities and browser quirks.
+would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails hooks into this shielding you from many of the RESTful complexities and browser quirks.
If you'd like more details on REST as an architectural style, these resources are more approachable than Fielding's thesis:
@@ -126,35 +142,25 @@ h4. Installing Rails
In most cases, the easiest way to install Rails is to take advantage of RubyGems:
<shell>
-$ gem install rails
+Usually run this as the root user:
+# gem install rails
</shell>
-NOTE. There are some special circumstances in which you might want to use an alternate installation strategy:
-
-* 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.
+NOTE. In the Rails 3.0.0-beta, you will need to manually install the dependencies for Rails itself as a bug in rubygems will cause these to not be installed, see the "3.0 Release Notes":http://guides.rails.info/3_0_release_notes.html for the commands to run.
-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.
+TIP. If you're working on Windows, you may find it easier to install "Instant Rails":http://instantrails.rubyforge.org/wiki/wiki.pl. Be aware, though, that Instant Rails 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.
h4. Creating the Blog Application
-Open a terminal, navigate to a folder where you have rights to create files, and type:
+The best way to use this guide is to follow each step as it happens, no code or step needed to make this example application has been left out, so you can literally follow along step by step. If you need to see the completed code, you can download it from "Getting Started Code":http://github.com/mikel/getting-started-code.
-<shell>
-$ rails blog
-</shell>
-
-This will create a Rails application that uses a SQLite database for data storage. If you prefer to use MySQL, run this command instead:
+To begin, open a terminal, navigate to a folder where you have rights to create files, and type:
<shell>
-$ rails blog -d mysql
+$ rails blog
</shell>
-And if you're using PostgreSQL for data storage, run this command:
-
-<shell>
-$ rails blog -d postgresql
-</shell>
+This will create a Rails application called Blog in a directory called blog.
TIP: You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>.
@@ -167,52 +173,62 @@ $ 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.|
|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).|
|log/|Application log files.|
|public/|The only folder seen to the world as-is. This is where your images, javascript, stylesheets (CSS), and other static files go.|
-|script/|Scripts provided by Rails to do recurring tasks, such as benchmarking, plugin installation, and starting the console or the web server.|
+|script/|Contains the rails script that starts your app and can contain other scripts you use to deploy or run your application.|
|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":http://www.github.com/carlhuda/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>
+As the root user:
+# gem install bundler
+# bundle install
+</shell>
+
+This will copy down the versions of all the gems you need to start a rails application.
h4. Configuring a Database
Just about every Rails application will interact with a database. The database to use is specified in a configuration file, +config/database.yml+.
-If you open this file in a new Rails application, you'll see a default database configuration using SQLite. The file contains sections for three different environments in which Rails can run by default:
+If you open this file in a new Rails application, you'll see a default database configuration using SQLite3. The file contains sections for three different environments in which Rails can run by default:
* The +development+ environment is used on your development computer as you interact manually with the application
* The +test+ environment is used to run automated tests
* The +production+ environment is used when you deploy your application for the world to use.
-h5. Configuring a SQLite Database
+h5. Configuring a SQLite3 Database
-Rails comes with built-in support for "SQLite":http://www.sqlite.org, which is a lightweight serverless database application. While a busy production environment may overload SQLite, it works well for development and testing. Rails defaults to using a SQLite database when creating a new project, but you can always change it later.
+Rails comes with built-in support for "SQLite3":http://www.sqlite.org, which is a lightweight serverless database application. While a busy production environment may overload SQLite, it works well for development and testing. Rails defaults to using a SQLite database when creating a new project, but you can always change it later.
-Here's the section of the default configuration file with connection information for the development environment:
+Here's the section of the default configuration file (<tt>config/database.yml</tt>) with connection information for the development environment:
<yaml>
development:
-adapter: sqlite3
-database: db/development.sqlite3
-pool: 5
-timeout: 5000
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
</yaml>
-If you don't have any database set up, SQLite is the easiest to get installed. If you're on OS X 10.5 or greater on a Mac, you already have it. Otherwise, you can install it using RubyGems:
-
-<shell>
-$ gem install sqlite3-ruby
-</shell>
+NOTE: In this guide we are using an SQLite3 database for data storage, this is because it is a zero configuration database that just works. Rails also supports MySQL and PostgreSQL "out of the box", and has plugins for many database systems, if you are using a database in a production environment, Rails most likely has an adapter for it.
h5. Configuring a MySQL Database
-If you choose to use MySQL, your +config/database.yml+ will look a little different. Here's the development section:
+If you choose to use MySQL instead of the shipped Sqlite3 database, your +config/database.yml+ will look a little different. Here's the development section:
<yaml>
development:
@@ -229,7 +245,7 @@ If your development computer's MySQL installation includes a root user with an e
h5. Configuring a PostgreSQL Database
-If you choose to use PostgreSQL, your +config/database.yml+ will be customized to use PostgreSQL databases:
+Finally if you choose to use PostgreSQL, your +config/database.yml+ will be customized to use PostgreSQL databases:
<yaml>
development:
@@ -251,66 +267,73 @@ Now that you have your database configured, it's time to have Rails create an em
$ rake db:create
</shell>
-NOTE. Rake is a general-purpose command-runner that Rails uses for many things. You can see the list of available rake commands in your application by running +rake -T+.
+This will create your development and test SQLite3 databases inside the <tt>db/</tt> folder.
+
+TIP: Rake is a general-purpose command-runner that Rails uses for many things. You can see the list of available rake commands in your application by running +rake -T+.
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:
+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.
+
+h4. Starting up the Web Server
+
+You actually have a functional Rails application already. To see it, you need to start a web server on your development machine. You can do this by running:
<shell>
-$ rails generate controller home index
+$ rails server
</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: +rails generate controller home index+.
+This will fire up an instance of the Mongrel web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to "http://localhost:3000":http://localhost:3000. You should see Rails' default information page:
-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:
+!images/rails_welcome.png(Welcome Aboard screenshot)!
-<code class="html">
-<h1>Hello, Rails!</h1>
-</code>
+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.
-h4. Starting up the Web 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. You can also click on the _About your application’s environment_ link to see a summary of your Application's environment.
-You actually have a functional Rails application already - after running only two commands! To see it, you need to start a web server on your development machine. You can do this by running another command:
+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>
-$ rails server
+$ rails generate controller home index
</shell>
-This will fire up an instance of the Mongrel web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to +http://localhost:3000+. You should see Rails' default information page:
-
-!images/rails_welcome.png(Welcome Aboard screenshot)!
+TIP: If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +rails+ commands to Ruby: +ruby \path\to\rails controller home index+.
-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.
+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:
-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+.
+<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://localhost:3000":http://localhost: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":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.
@@ -328,30 +351,31 @@ $ rails generate scaffold Post name:string title:string content:text
NOTE. While scaffolding will get you up and running quickly, the "one size fits all" code that it generates is unlikely to be a perfect fit for your application. In most cases, you'll need to customize the generated code. Many experienced Rails developers avoid scaffolding entirely, preferring to write all or most of their source code from scratch.
-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|
+The scaffold generator will build 15 files in your application, along with some folders, and edit one more. Here's a quick overview of what it creates:
+
+|_.File |_.Purpose|
+|db/migrate/20100207214725_create_posts.rb.rb |Migration to create the posts table in your database (your name will include a different timestamp)|
+|app/models/post.rb |The Post model|
+|test/unit/post_test.rb |Unit testing harness for the posts model|
+|test/fixtures/posts.yml |Dummy posts for use in testing|
+|app/controllers/posts_controller.rb |The Posts controller|
+|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|
+|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/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper|
+|public/stylesheets/scaffold.css |Cascading style sheet to make the scaffolded views look better|
h4. Running a Migration
One of the products of the +rails 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/20100207214725_create_posts.rb+ file (remember, yours will have a slightly different name), here's what you'll find:
<ruby>
class CreatePosts < ActiveRecord::Migration
@@ -371,7 +395,7 @@ class CreatePosts < ActiveRecord::Migration
end
</ruby>
-If you were to translate that into words, it says something like: when this migration is run, create a table named +posts+ with two string columns (+name+ and +title+) and a text column (+content+), and generate timestamp fields to track record creation and updating. You can learn the detailed syntax for migrations in the "Rails Database Migrations":migrations.html guide.
+The above migration creates two methods, +up+, called when you run this migration into the database, and +down+ in case you need to reverse the changes made by this migration at a later date. The +up+ command in this case creates a +posts+ table with two string columns and a text column. It also is creating two timestamp fields to track record creation and updating. More information about Rails migrations can be found in the "Rails Database Migrations":migrations.html guide.
At this point, you can use a rake command to run the migration:
@@ -379,7 +403,14 @@ At this point, you can use a rake command to run the migration:
$ rake db:migrate
</shell>
-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 it 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.
@@ -396,11 +427,11 @@ The +link_to+ method is one of Rails' built-in view helpers. It creates a hyperl
h4. Working with Posts in the Browser
-Now you're ready to start working with posts. To do that, navigate to +http://localhost:3000+ and then click the "My Blog" link:
+Now you're ready to start working with posts. To do that, navigate to "http://localhost:3000":http://localhost:3000/ and then click the "My Blog" link:
!images/posts_index.png(Posts Index screenshot)!
-This is the result of Rails rendering the +index+ view of your posts. There aren't currently any posts in the database, but if you click the +New Post+ link you can create one. After that, you'll find that you can edit posts, look at their details, or destroy them. All of the logic and HTML to handle this was built by the single +rails generate scaffold+ command.
+This is the result of Rails rendering the +index+ view of your posts. There aren't currently any posts in the database, but if you click the +New Post+ link you can create one. After that, you'll find that you can edit posts, look at their details, or destroy them. All of the logic and HTML to handle this was built by the single +script/generate scaffold+ command.
TIP: In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server.
@@ -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>
@@ -442,15 +474,15 @@ After the console loads, you can use it to work with your application's models:
<shell>
>> p = Post.create(:content => "A new post")
-=> #<Post id: nil, name: nil, title: nil, content: "A new post",
-created_at: nil, updated_at: nil>
+=> #<Post id: nil, name: nil, title: nil,
+ content: "A new post", 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,11 +504,11 @@ 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.
-The +respond_to+ block handles both HTML and XML calls to this action. If you browse to +http://localhost:3000/posts.xml+, you'll see all of the posts in XML format. The HTML format looks for a view in +app/views/posts/+ with a name that corresponds to the action name. Rails makes all of the instance variables from the action available to the view. Here's +app/views/posts/index.html.erb+:
+The +respond_to+ block handles both HTML and XML calls to this action. If you browse to "http://localhost:3000/posts.xml":http://localhost:3000/posts.xml, you'll see all of the posts in XML format. The HTML format looks for a view in +app/views/posts/+ with a name that corresponds to the action name. Rails makes all of the instance variables from the action available to the view. Here's +app/views/posts/index.html.erb+:
<erb>
<h1>Listing posts</h1>
@@ -486,17 +518,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 +542,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 +554,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 +rails 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 +592,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 basically identical 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 +641,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.
+
+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.
-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."
+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 +680,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 +713,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 +730,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 +744,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:
@@ -732,124 +762,6 @@ end
The +destroy+ method of an Active Record model instance removes the corresponding record from the database. After that's done, there isn't any record to display, so Rails redirects the user's browser to the index view for the model.
-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.
-
-h4. Using Filters to Eliminate Controller Duplication
-
-At this point, if you look at the controller for posts, you'll see some duplication:
-
-<ruby>
-class PostsController < ApplicationController
- # ...
- def show
- @post = Post.find(params[:id])
- # ...
- end
-
- def edit
- @post = Post.find(params[:id])
- end
-
- def update
- @post = Post.find(params[:id])
- # ...
- end
-
- def destroy
- @post = Post.find(params[:id])
- # ...
- end
-end
-</ruby>
-
-Four instances of the exact same line of code doesn't seem very DRY. Rails provides _filters_ as a way to address this sort of repeated code. In this case, you can DRY things up by using a +before_filter+:
-
-<ruby>
-class PostsController < ApplicationController
- before_filter :find_post,
- :only => [:show, :edit, :update, :destroy]
- # ...
- def show
- # ...
- end
-
- def edit
- end
-
- def update
- # ...
- end
-
- def destroy
- # ...
- end
-
-private
- def find_post
- @post = Post.find(params[:id])
- end
-end
-</ruby>
-
-Rails runs _before filters_ before any action in the controller. You can use the +:only+ clause to limit a before filter to only certain actions, or an +:except+ clause to specifically skip a before filter for certain actions. Rails also allows you to define _after filters_ that run after processing an action, as well as _around filters_ that surround the processing of actions. Filters can also be defined in external classes to make it easy to share them between controllers.
-
-For more information on filters, see the "Action Controller Overview":action_controller_overview.html guide.
-
h3. Adding a Second Model
Now that you've seen what's in a model built with scaffolding, it's time to add a second model to the application. The second model will handle comments on blog posts.
@@ -859,14 +771,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>
-$ rails generate model Comment commenter:string body:text
- post:references
+$ rails 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/20100207235629_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 +816,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.0017s
+== CreateComments: migrated (0.0018s) ========================================
+</shell>
h4. Associating Models
@@ -926,8 +844,10 @@ 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 +856,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.
@@ -953,249 +875,300 @@ h4. Generating a Controller
With the model in hand, you can turn your attention to creating a matching controller. Again, there's a generator for this:
<shell>
-$ rails generate controller Comments index show new edit
+$ rails generate controller Comments
</shell>
-This creates eight files:
+This creates four files:
* +app/controllers/comments_controller.rb+ - The controller
* +app/helpers/comments_helper.rb+ - A view helper file
-* +app/views/comments/index.html.erb+ - The view for the index action
-* +app/views/comments/show.html.erb+ - The view for the show action
-* +app/views/comments/new.html.erb+ - The view for the new action
-* +app/views/comments/edit.html.erb+ - The view for the edit action
* +test/functional/comments_controller_test.rb+ - The functional tests for the controller
* +test/unit/helpers/comments_helper_test.rb+ - The unit tests for the helper
-The controller will be generated with empty methods and views for each action that you specified in the call to +rails generate controller+:
+Like with any blog, our readers will create their comments directly after reading the post, and once they have added their comment, will be sent back to the post show page to see their comment now listed. Due to this, our +CommentsController+ is there to provide a method to create comments and delete SPAM comments when they arrive.
-<ruby>
-class CommentsController < ApplicationController
- def index
- end
-
- def show
- end
-
- def new
- end
+So first, we'll wire up the Post show template (+/app/views/posts/show.html.erb+) to let us make a new comment:
- def edit
- end
+<erb>
+<p>
+ <b>Name:</b>
+ <%= @post.name %>
+</p>
-end
-</ruby>
+<p>
+ <b>Title:</b>
+ <%= @post.title %>
+</p>
-You'll need to flesh this out with code to actually process requests appropriately in each method. Here's a version that (for simplicity's sake) only responds to requests that require HTML:
+<p>
+ <b>Content:</b>
+ <%= @post.content %>
+</p>
-<ruby>
-class CommentsController < ApplicationController
- before_filter :find_post
+<h2>Add a comment:</h2>
+<% form_for([@post, @post.comments.build]) do |f| %>
+ <%= f.error_messages %>
- def index
- @comments = @post.comments
- end
+ <div class="field">
+ <%= f.label :commenter %><br />
+ <%= f.text_field :commenter %>
+ </div>
+ <div class="field">
+ <%= f.label :body %><br />
+ <%= f.text_area :body %>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
+<% end %>
- def show
- @comment = @post.comments.find(params[:id])
- end
+<%= link_to 'Edit Post', edit_post_path(@post) %> |
+<%= link_to 'Back to Posts', posts_path %> |
+</erb>
- def new
- @comment = @post.comments.build
- end
+This adds a form on the Post show page that creates a new comment, which will call the +CommentsController+ +create+ action, so let's wire that up:
+<ruby>
+class CommentsController < ApplicationController
def create
- @comment = @post.comments.build(params[:comment])
- if @comment.save
- redirect_to post_comment_url(@post, @comment)
- else
- render :action => "new"
- end
- end
-
- def edit
- @comment = @post.comments.find(params[:id])
- end
-
- def update
- @comment = Comment.find(params[:id])
- if @comment.update_attributes(params[:comment])
- redirect_to post_comment_url(@post, @comment)
- else
- render :action => "edit"
- end
- end
-
- def destroy
- @comment = Comment.find(params[:id])
- @comment.destroy
- redirect_to post_comments_path(@post)
- end
-
-private
- def find_post
@post = Post.find(params[:post_id])
+ @comment = @post.comments.create(params[:comment])
+ redirect_to post_path(@post)
end
-
end
</ruby>
-You'll see a bit more complexity here than you did in the controller for posts. That's a side-effect of the nesting that you've set up; each request for a comment has to keep track of the post to which the comment is attached.
+You'll see a bit more complexity here than you did in the controller for posts. That's a side-effect of the nesting that you've set up; each request for a comment has to keep track of the post to which the comment is attached, thus the initial find action to the Post model to get the post in question.
In addition, the code takes advantage of some of the methods available for an association. For example, in the +new+ method, it calls
-<ruby>
-@comment = @post.comments.build
-</ruby>
-
-This creates a new +Comment+ object _and_ sets up the +post_id+ field to have the +id+ from the specified +Post+ object in a single operation.
-
-h4. Building Views
-
-Because you skipped scaffolding, you'll need to build views for comments "by hand". Invoking +rails generate controller+ will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.
-
-The +views/comments/index.html.erb+ view:
+Once we have made the new comment, we send the user back to the +post_path(@post)+ URL. This runs the +show+ action of the +PostsController+ which then renders the +show.html.erb+ template where we want the comment to show, so then, we'll add that to the +app/view/posts/show.html.erb+.
<erb>
-<h1>Comments for <%= @post.title %></h1>
-
-<table>
- <tr>
- <th>Commenter</th>
- <th>Body</th>
- </tr>
-
-<% for comment in @comments %>
- <tr>
- <td><%=h comment.commenter %></td>
- <td><%=h 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>
- </tr>
-<% end %>
-</table>
+<p>
+ <b>Name:</b>
+ <%= @post.name %>
+</p>
-<br />
+<p>
+ <b>Title:</b>
+ <%= @post.title %>
+</p>
-<%= link_to 'New comment', new_post_comment_path(@post) %>
-<%= link_to 'Back to Post', @post %>
-</erb>
+<p>
+ <b>Content:</b>
+ <%= @post.content %>
+</p>
-The +views/comments/new.html.erb+ view:
+<h2>Comments</h2>
+<% @post.comments.each do |comment| %>
+ <p>
+ <b>Commenter:</b>
+ <%= comment.commenter %>
+ </p>
-<erb>
-<h1>New comment</h1>
+ <p>
+ <b>Comment:</b>
+ <%= comment.body %>
+ </p>
+<% end %>
-<% form_for([@post, @comment]) do |f| %>
+<h2>Add a comment:</h2>
+<% form_for([@post, @post.comments.build]) 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) %>
+<br />
+
+<%= link_to 'Edit Post', edit_post_path(@post) %> |
+<%= link_to 'Back to Posts', posts_path %> |
</erb>
-The +views/comments/show.html.erb+ view:
+Now you can add posts and comments to your blog and have them show up in the right places.
-<erb>
-<h1>Comment on <%= @post.title %></h1>
+h3. Refactorization
+
+Now that we have Posts and Comments working, we can take a look at the +app/views/posts/show.html.erb+ template, it is getting long and awkward, we can use partials to clean this up.
+
+h4. Rendering Partial Collections
+
+First will make a comment partial to extract showing all the comments for the post, so make a file +app/views/comments/_comment.html.erb+ and put into it:
+<erb>
<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:
+Then in the +app/views/posts/show.html.erb+ you can change it to look like the following:
<erb>
-<h1>Editing comment</h1>
+<p>
+ <b>Name:</b>
+ <%= @post.name %>
+</p>
+
+<p>
+ <b>Title:</b>
+ <%= @post.title %>
+</p>
+
+<p>
+ <b>Content:</b>
+ <%= @post.content %>
+</p>
+
+<h2>Comments</h2>
+<%= render :partial => "comments/comment",
+ :collection => @post.comments %>
-<% form_for([@post, @comment]) do |f| %>
+<h2>Add a comment:</h2>
+<% form_for([@post, @post.comments.build]) 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 "Update" %>
- </p>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
<% end %>
-<%= link_to 'Show', post_comment_path(@post, @comment) %> |
-<%= link_to 'Back', post_comments_path(@post) %>
+<br />
+
+<%= link_to 'Edit Post', edit_post_path(@post) %> |
+<%= link_to 'Back to Posts', posts_path %> |
</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.
+This will now render the partial in +app/views/comments/_comment.html.erb+ once for each comment that is in the +@post.comments+ collection. As the +render+ method iterates over the <tt>@post.comments</tt> collection, it assigns each comment to a local variable named the same as the partial, in this case +comment+ which is then available in the partial for us to show.
-h4. Hooking Comments to Posts
+h4. Rendering a Partial Form
-As a next step, I'll modify the +views/posts/show.html.erb+ view to show the comments on that post, and to allow managing those comments:
+Lets also move that new comment section out to it's own partial, again, you create a file +app/views/comments/_form.html.erb+ and in it you put:
+
+<erb>
+<% form_for([@post, @post.comments.build]) do |f| %>
+ <%= f.error_messages %>
+
+ <div class="field">
+ <%= f.label :commenter %><br />
+ <%= f.text_field :commenter %>
+ </div>
+ <div class="field">
+ <%= f.label :body %><br />
+ <%= f.text_area :body %>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
+<% end %>
+</erb>
+
+Then you make the +app/views/posts/show.html.erb+ look like the following:
<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| %>
- <p>
- <b>Commenter:</b>
- <%=h c.commenter %>
- </p>
+<%= render :partial => "comments/comment",
+ :collection => @post.comments %>
- <p>
- <b>Comment:</b>
- <%=h c.body %>
- </p>
-<% end %>
+<h2>Add a comment:</h2>
+<%= render "comments/form" %>
+
+<br />
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
-<%= link_to 'Manage Comments', post_comments_path(@post) %>
</erb>
-Note that each post has its own individual comments collection, accessible as +@post.comments+. That's a consequence of the declarative associations in the models. Path helpers such as +post_comments_path+ come from the nested route declaration in +config/routes.rb+.
+The second render just defines the partial template we want to render, <tt>comments/form</tt>, Rails is smart enough to spot the forward slash in that string and realise that you want to render the <tt>_form.html.erb</tt> file in the <tt>app/views/comments</tt> directory.
+
+The +@post+ object is available any partials rendered in the view because we defined it as an instance variable.
+
+h3. Deleting Comments
+
+Another important feature on a blog is being able to delete SPAM comments. To do this, we need to implement a link of some sort in the view and a +DELETE+ action in the +CommentsController+.
+
+So first, let's add the delete link in the +app/views/comments/_comment.html.erb+ partial:
+
+<erb>
+<p>
+ <b>Commenter:</b>
+ <%= comment.commenter %>
+</p>
+
+<p>
+ <b>Comment:</b>
+ <%= comment.body %>
+</p>
+
+<p>
+ <%= link_to 'Destroy Comment', [comment.post, comment],
+ :confirm => 'Are you sure?',
+ :method => :delete %>
+</p>
+</erb>
+
+Clicking this new "Destroy Comment" link will fire off a <tt>DELETE /posts/:id/comments/:id</tt> to our +CommentsController+, which can then use this to find the comment we want to delete, so let's add a destroy action to our controller:
+
+<ruby>
+class CommentsController < ApplicationController
+
+ def create
+ @post = Post.find(params[:post_id])
+ @comment = @post.comments.create(params[:comment])
+ redirect_to post_path(@post)
+ end
+
+ def destroy
+ @post = Post.find(params[:post_id])
+ @comment = @post.comments.find(params[:id])
+ @comment.destroy
+ redirect_to post_path(@post)
+ end
+
+end
+</ruby>
+
+The +destroy+ action will find the post we are looking at, locate the comment within the <tt>@post.comments</tt> collection, and then remove it from the database and send us back to the show action for the post.
+
h4. Deleting Associated Objects
@@ -1203,21 +1176,81 @@ 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>
+</ruby>
+
+h3. Security
+
+Before you publish your blog online, you will most likely want to prevent just anyone from being able to add, edit and delete posts or delete comments.
+
+Rails provides a very simple http authentication system that will work nicely in this situation. First, we enable simple HTTP based authentication in our <tt>app/controllers/application_controller.rb</tt>:
+
+<ruby>
+class ApplicationController < ActionController::Base
+ protect_from_forgery
+
+ private
+
+ def authenticate
+ authenticate_or_request_with_http_basic do |user_name, password|
+ user_name == 'admin' && password == 'password'
+ end
+ end
+
+end
+</ruby>
+
+You can obviously change the username and password to whatever you want. We put this method inside of +ApplicationController+ so that it is available to all of our controllers.
+
+Then in the +PostsController+ we need to have a way to block access to the various actions if the person is not authenticated, here we can use the Rails <tt>before_filter</tt> method, which allows us to specify that Rails must run a method and only then allow access to the requested action if that method allows it.
+
+To use the before filter, we specify it at the top of our +PostsController+, in this case, we want the user to be authenticated on every action, except for +index+ and +show+, so we write that:
+
+<ruby>
+class PostsController < ApplicationController
+
+ before_filter :authenticate, :except => [:index, :show]
+
+ # GET /posts
+ # GET /posts.xml
+ def index
+ @posts = Post.all
+ respond_to do |format|
+# snipped for brevity
+</ruby>
+
+We also only want to allow authenticated users to delete comments, so in the +CommentsController+ we write:
+
+<ruby>
+class CommentsController < ApplicationController
+
+ before_filter :authenticate, :only => :destroy
+
+ def create
+ @post = Post.find(params[:post_id])
+# snipped for brevity
+</ruby>
+
+Now if you try to create a new post, you will be greeted with a basic HTTP Authentication challenge
+
+!images/challenge.png(Basic HTTP Authentication Challenge)!
+
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:
+Another piece of your average blog is the ability to tag posts. This requires that your application edits more than one thing on a single form. Rails offers support for nested forms.
+
+To demonstrate this, we will 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>
$ rails 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,9 +1260,11 @@ 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
- has_many :comments
+ validates :name, :presence => true
+ validates :title, :presence => true,
+ :length => { :minimum => 5 }
+
+ has_many :comments, :dependent => :destroy
has_many :tags
accepts_nested_attributes_for :tags, :allow_destroy => :true,
@@ -1239,54 +1274,150 @@ end
The +:allow_destroy+ option on the nested attribute declaration tells Rails to display a "remove" checkbox on the view that you'll build shortly. The +:reject_if+ option prevents saving new tags that do not have any attributes filled in.
-You'll also need to modify +views/posts/_form.html.erb+ to include the tags:
+We will modify +views/posts/_form.html.erb+ to render a partial to make a tag:
<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>
- <%= tag_form.label :name, 'Tag:' %>
- <%= tag_form.text_field :name %>
- </p>
- <% unless tag_form.object.nil? || tag_form.object.new_record? %>
- <p>
- <%= tag_form.label :_delete, 'Remove:' %>
- <%= tag_form.check_box :_delete %>
- </p>
- <% end %>
- <% end %>
+ <%= render :partial => 'tags/form',
+ :locals => {:form => post_form} %>
+ <div class="actions">
+ <%= post_form.submit %>
+ </div>
+<% end %>
+</erb>
- <p>
- <%= post_form.submit "Save" %>
- </p>
+This example shows another option of the render helper, being able to pass in local variables, in this case, we want the local variable +form+ in the partial to refer to the +post_form+ object.
+
+You will also note that we also have changed the +f+ in <tt>form_for(@post) do |f|</tt> to <tt>post_form</tt> to clarify what is going on somewhat.
+
+We also add a <tt>@post.tags.build</tt> at the top of this form, this is to make sure there is a new tag ready to have it's name filled in by the user. If you do not build the new tag, then the form will not appear as there is no new Tag object ready to create.
+
+Now create the folder <tt>app/views/tags</tt> and make a file in there called <tt>_form.html.erb</tt> which contains the form for the tag:
+
+<erb>
+<% form.fields_for :tags do |tag_form| %>
+ <div class="field">
+ <%= tag_form.label :name, 'Tag:' %>
+ <%= tag_form.text_field :name %>
+ </div>
+ <% unless tag_form.object.nil? || tag_form.object.new_record? %>
+ <div class="field">
+ <%= tag_form.label :_destroy, 'Remove:' %>
+ <%= tag_form.check_box :_destroy %>
+ </div>
+ <% end %>
<% end %>
</erb>
+Finally, we will edit the <tt>app/views/posts/show.html.erb</tt> template to show our tags.
+
+<erb>
+<p>
+ <b>Name:</b>
+ <%= @post.name %>
+</p>
+
+<p>
+ <b>Title:</b>
+ <%= @post.title %>
+</p>
+
+<p>
+ <b>Content:</b>
+ <%= @post.content %>
+</p>
+
+<p>
+ <b>Tags:</b>
+ <%= @post.tags.map { |t| t.name }.join(", ") %>
+</p>
+
+<h2>Comments</h2>
+<%= render :partial => "comments/comment",
+ :collection => @post.comments %>
+
+<h2>Add a comment:</h2>
+<%= render "comments/form" %>
+
+
+<%= link_to 'Edit Post', edit_post_path(@post) %> |
+<%= link_to 'Back to Posts', posts_path %> |
+</erb>
+
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.
+However, that method call <tt>@post.tags.map { |t| t.name }.join(", ")</tt> is awkward, we could handle this by making a helper method.
+
+h3. View Helpers
+
+View Helpers live in <tt>app/helpers</tt> and provide small snippets of reusable code for views. In our case, we want a method that strings a bunch of objects together using their name attribute and joining them with a comma. As this is for the Post show template, we put it in the PostsHelper.
+
+Open up <tt>app/helpers/posts_helper.rb</tt> and add the following:
+
+<erb>
+module PostsHelper
+ def join_tags(post)
+ post.tags.map { |t| t.name }.join(", ")
+ end
+end
+</erb>
+
+Now you can edit the view in <tt>app/views/posts/show.html.erb</tt> to look like this:
+
+<erb>
+<p>
+ <b>Name:</b>
+ <%= @post.name %>
+</p>
+
+<p>
+ <b>Title:</b>
+ <%= @post.title %>
+</p>
+
+<p>
+ <b>Content:</b>
+ <%= @post.content %>
+</p>
+
+<p>
+ <b>Tags:</b>
+ <%= join_tags(@post) %>
+</p>
+
+<h2>Comments</h2>
+<%= render :partial => "comments/comment",
+ :collection => @post.comments %>
+
+<h2>Add a comment:</h2>
+<%= render "comments/form" %>
+
+
+<%= link_to 'Edit Post', edit_post_path(@post) %> |
+<%= link_to 'Back to Posts', posts_path %> |
+</erb>
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/
@@ -1296,10 +1427,13 @@ Rails also comes with built-in help that you can generate using the rake command
* Running +rake doc:guides+ will put a full copy of the Rails Guides in the +doc/guides+ folder of your application. Open +doc/guides/index.html+ in your web browser to explore the Guides.
* Running +rake doc:rails+ will put a full copy of the API documentation for Rails in the +doc/api+ folder of your application. Open +doc/api/index.html+ in your web browser to explore the API documentation.
+
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2
+* February 8, 2010: Full re-write for Rails 3.0-beta, added helpers and before_filters, refactored code by "Mikel Lindsaar":credits:html#raasdnil
+* 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 +1441,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