aboutsummaryrefslogtreecommitdiffstats
path: root/railties
diff options
context:
space:
mode:
Diffstat (limited to 'railties')
-rw-r--r--railties/README91
-rw-r--r--railties/configs/seeds.rb2
-rwxr-xr-xrailties/guides/files/javascripts/guides.js1
-rw-r--r--railties/guides/images/fxn.jpgbin17773 -> 0 bytes
-rw-r--r--railties/guides/images/fxn.pngbin0 -> 20664 bytes
-rw-r--r--railties/guides/source/action_controller_overview.textile33
-rw-r--r--railties/guides/source/action_mailer_basics.textile30
-rw-r--r--railties/guides/source/active_record_basics.textile6
-rw-r--r--railties/guides/source/active_record_querying.textile17
-rw-r--r--railties/guides/source/active_support_overview.textile818
-rw-r--r--railties/guides/source/activerecord_validations_callbacks.textile8
-rw-r--r--railties/guides/source/ajax_on_rails.textile94
-rw-r--r--railties/guides/source/association_basics.textile53
-rw-r--r--railties/guides/source/caching_with_rails.textile262
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/contribute.textile2
-rw-r--r--railties/guides/source/contributing_to_rails.textile11
-rw-r--r--railties/guides/source/credits.erb.textile6
-rw-r--r--railties/guides/source/debugging_rails_applications.textile2
-rw-r--r--railties/guides/source/form_helpers.textile28
-rw-r--r--railties/guides/source/getting_started.textile129
-rw-r--r--railties/guides/source/i18n.textile83
-rw-r--r--railties/guides/source/index.erb.textile2
-rw-r--r--railties/guides/source/layouts_and_rendering.textile38
-rw-r--r--railties/guides/source/migrations.textile12
-rw-r--r--railties/guides/source/performance_testing.textile2
-rw-r--r--railties/guides/source/plugins.textile2
-rw-r--r--railties/guides/source/rails_application_templates.textile232
-rw-r--r--railties/guides/source/rails_on_rack.textile4
-rw-r--r--railties/guides/source/routing.textile8
-rw-r--r--railties/guides/source/security.textile6
-rw-r--r--railties/guides/source/testing.textile18
32 files changed, 1516 insertions, 486 deletions
diff --git a/railties/README b/railties/README
index 37ec8ea211..221e8486d1 100644
--- a/railties/README
+++ b/railties/README
@@ -28,7 +28,7 @@ link:files/vendor/rails/actionpack/README.html.
== Getting Started
1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
- and your application name. Ex: rails myapp
+ and your application name. Ex: <tt>rails myapp</tt>
2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!"
4. Follow the guidelines to start developing your application
@@ -36,61 +36,60 @@ link:files/vendor/rails/actionpack/README.html.
== Web Servers
-By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails
-with a variety of other web servers.
+By default, Rails will try to use Mongrel if it's installed when started with script/server, otherwise
+Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails with a variety of
+other web servers.
Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
More info at: http://mongrel.rubyforge.org
-Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or
-Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use
-FCGI or proxy to a pack of Mongrels/Thin/Ebb servers.
+Other ruby web servers exist which can run your rails application, however script/server does
+not search for them or start them. These include Thin, Ebb, and Apache with mod_rails.
+
+For production use, often a web/proxy server such as Apache, LiteSpeed, Lighttpd or IIS is
+deployed as the front-end server, with the chosen ruby web server running in the back-end
+and receiving the proxied requests via one of several protocols (HTTP, CGI, FCGI).
+
== Apache .htaccess example for FCGI/CGI
-# General Apache options
-AddHandler fastcgi-script .fcgi
-AddHandler cgi-script .cgi
-Options +FollowSymLinks +ExecCGI
-
-# If you don't want Rails to look in certain directories,
-# use the following rewrite rules so that Apache won't rewrite certain requests
-#
-# Example:
-# RewriteCond %{REQUEST_URI} ^/notrails.*
-# RewriteRule .* - [L]
-
-# Redirect all requests not available on the filesystem to Rails
-# By default the cgi dispatcher is used which is very slow
-#
-# For better performance replace the dispatcher with the fastcgi one
-#
-# Example:
-# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
-RewriteEngine On
-
-# If your Rails application is accessed via an Alias directive,
-# then you MUST also set the RewriteBase in this htaccess file.
-#
-# Example:
-# Alias /myrailsapp /path/to/myrailsapp/public
-# RewriteBase /myrailsapp
-
-RewriteRule ^$ index.html [QSA]
-RewriteRule ^([^.]+)$ $1.html [QSA]
-RewriteCond %{REQUEST_FILENAME} !-f
-RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
-
-# In case Rails experiences terminal errors
-# Instead of displaying this message you can supply a file here which will be rendered instead
-#
-# Example:
-# ErrorDocument 500 /500.html
-
-ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
+General Apache options
+
+ AddHandler fastcgi-script .fcgi
+ AddHandler cgi-script .cgi
+ Options +FollowSymLinks +ExecCGI
+
+If you don't want Rails to look in certain directories, use the following
+rewrite rules so that Apache won't rewrite certain requests.
+
+ RewriteCond %{REQUEST_URI} ^/notrails.*
+ RewriteRule .* - [L]
+
+Redirect all requests not available on the filesystem to Rails. By default the
+cgi dispatcher is used which is very slow, for better performance replace the
+dispatcher with the fastcgi one.
+
+ RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+ RewriteEngine On
+
+If your Rails application is accessed via an Alias directive, then you MUST also
+set the RewriteBase in this htaccess file.
+
+ Alias /myrailsapp /path/to/myrailsapp/public
+ RewriteBase /myrailsapp
+
+ RewriteRule ^$ index.html [QSA]
+ RewriteRule ^([^.]+)$ $1.html [QSA]
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
+
+Incase Rails experiences terminal errors instead of displaying those messages you
+can supply a file here which will be rendered instead.
+ ErrorDocument 500 /500.html
+ ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
== Debugging Rails
diff --git a/railties/configs/seeds.rb b/railties/configs/seeds.rb
index 3174d0cb8a..bc8695e6f0 100644
--- a/railties/configs/seeds.rb
+++ b/railties/configs/seeds.rb
@@ -4,4 +4,4 @@
# Examples:
#
# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
-# Major.create(:name => 'Daley', :city => cities.first)
+# Mayor.create(:name => 'Daley', :city => cities.first)
diff --git a/railties/guides/files/javascripts/guides.js b/railties/guides/files/javascripts/guides.js
index 81fc07e799..c4e4d459ea 100755
--- a/railties/guides/files/javascripts/guides.js
+++ b/railties/guides/files/javascripts/guides.js
@@ -1,5 +1,4 @@
function guideMenu(){
-
if (document.getElementById('guides').style.display == "none") {
document.getElementById('guides').style.display = "block";
} else {
diff --git a/railties/guides/images/fxn.jpg b/railties/guides/images/fxn.jpg
deleted file mode 100644
index 81999341f1..0000000000
--- a/railties/guides/images/fxn.jpg
+++ /dev/null
Binary files differ
diff --git a/railties/guides/images/fxn.png b/railties/guides/images/fxn.png
new file mode 100644
index 0000000000..9b531ee584
--- /dev/null
+++ b/railties/guides/images/fxn.png
Binary files differ
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index 054ca99985..756caea5fe 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -41,7 +41,7 @@ def new
end
</ruby>
-The "Layouts & rendering guide":layouts_and_rendering.html explains this in more detail.
+The "Layouts & Rendering Guide":layouts_and_rendering.html explains this in more detail.
+ApplicationController+ inherits from +ActionController::Base+, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.
@@ -191,7 +191,7 @@ Session values are stored using key/value pairs like a hash:
<ruby>
class ApplicationController < ActionController::Base
-private
+ private
# Finds the User with the ID stored in the session with the key
# :current_user_id This is a common way to handle user login in
@@ -350,7 +350,8 @@ Before filters may halt the request cycle. A common before filter is one which r
class ApplicationController < ActionController::Base
before_filter :require_login
-private
+ private
+
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
@@ -374,7 +375,7 @@ The method simply stores an error message in the flash and redirects to the logi
In this example the filter is added to +ApplicationController+ and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with +skip_before_filter+:
<ruby>
-class LoginsController < Application
+class LoginsController < ApplicationController
skip_before_filter :require_login, :only => [:new, :create]
end
</ruby>
@@ -390,10 +391,11 @@ Around filters are responsible for running the action, but they can choose not t
<ruby>
# Example taken from the Rails API filter documentation:
# http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html
-class ApplicationController < Application
+class ApplicationController < ActionController::Base
around_filter :catch_exceptions
-private
+ private
+
def catch_exceptions
yield
rescue => exception
@@ -442,7 +444,7 @@ The Rails API documentation has "more information on using filters":http://api.r
h3. Verification
-Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHTTPRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter".
+Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHttpRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter".
Here's an example of using verification to make sure the user supplies a username and a password in order to log in:
@@ -575,7 +577,8 @@ class AdminController < ApplicationController
before_filter :authenticate
-private
+ private
+
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == USERNAME &&
@@ -597,7 +600,8 @@ class AdminController < ApplicationController
before_filter :authenticate
-private
+ private
+
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
@@ -626,7 +630,7 @@ class ClientsController < ApplicationController
:type => "application/pdf")
end
-private
+ private
def generate_pdf(client)
Prawn::Document.new do
@@ -728,7 +732,8 @@ Here's how you can use +rescue_from+ to intercept all +ActiveRecord::RecordNotFo
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
-private
+ private
+
def record_not_found
render :text => "404 Not Found", :status => 404
end
@@ -741,7 +746,8 @@ Of course, this example is anything but elaborate and doesn't improve on the def
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, :with => :user_not_authorized
-private
+ private
+
def user_not_authorized
flash[:error] = "You don't have access to this section."
redirect_to :back
@@ -757,7 +763,8 @@ class ClientsController < ApplicationController
@client = Client.find(params[:id])
end
-private
+ private
+
# If the user is not authorized, just throw the exception.
def check_authorization
raise User::NotAuthorized unless current_user.admin?
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 9476635ae6..2e7f0e7fed 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -46,7 +46,7 @@ class UserMailer < ActionMailer::Base
from "My Awesome Site Notifications <notifications@example.com>"
subject "Welcome to My Awesome Site"
sent_on Time.now
- body {:user => user, :url => "http://example.com/login"}
+ body( {:user => user, :url => "http://example.com/login"})
end
end
</ruby>
@@ -137,9 +137,9 @@ Hence, if the method name starts with +deliver_+ followed by any combination of
h4. Complete List of Action Mailer User-Settable Attributes
-|bcc| The BCC addresses of the email|
+|bcc| The BCC addresses of the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
|body| The body of the email. This is either a hash (in which case it specifies the variables to pass to the template when it is rendered), or a string, in which case it specifies the actual body of the message|
-|cc| The CC addresses for the email|
+|cc| The CC addresses for the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
|charset| The charset to use for the email. This defaults to the +default_charset+ specified for ActionMailer::Base.|
|content_type| The content type for the email. This defaults to "text/plain" but the filename may specify it|
|from| The from address of the email|
@@ -165,10 +165,11 @@ class UserMailer < ActionMailer::Base
from "My Awesome Site Notifications<notifications@example.com>"
subject "Welcome to My Awesome Site"
sent_on Time.now
- body {:user => user, :url => "http://example.com/login"}
+ body( {:user => user, :url => "http://example.com/login"})
content_type "text/html"
# use some_other_template.text.(html|plain).erb instead
template "some_other_template"
+ end
end
</ruby>
@@ -264,9 +265,9 @@ end
h4. Sending Multipart Emails with Attachments
-Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename. You must declare which template you are using for each content type via the +part+ method.
+Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename, nor will it properly order the alternative parts. You must declare which template you are using for each content type via the +part+ method. And you must declare these templates in the proper order.
-In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder.
+In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder. The +text/plain+ part must be listed first for full compatibility with email clients. If +text/plain+ is listed after +text/html+, some clients may display both the HTML and plain text versions of the email. The text alternatives alone must be enclosed in a +multipart/alternative+ part. Do not set the entire message's +content_type+ to +multipart/alternative+ or some email clients may ignore the display of attachments such as PDF's.
<ruby>
class UserMailer < ActionMailer::Base
@@ -274,14 +275,15 @@ class UserMailer < ActionMailer::Base
recipients user.email_address
subject "New account information"
from "system@example.com"
- content_type "multipart/alternative"
- part "text/html" do |p|
- p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>")
- end
+ part "multipart/alternative" do |pt|
+ pt.part "text/plain" do |p|
+ p.body = render_message("welcome_email_plain", :message => "text content")
+ end
- part "text/plain" do |p|
- p.body = render_message("welcome_email_plain", :message => "text content")
+ pt.part "text/html" do |p|
+ p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>")
+ end
end
attachment :content_type => "image/jpeg",
@@ -369,9 +371,9 @@ ActionMailer::Base.default_charset = "iso-8859-1"
h4. Action Mailer Configuration for GMail
-Instructions copied from http://http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html
+Instructions copied from "this blog entry":http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html by Robert Zotter.
-First you must install the +action_mailer_tls+ plugin from http://code.openrain.com/rails/action_mailer_tls/, then all you have to do is configure action mailer.
+First you must install the "action_mailer_tls":http://github.com/openrain/action_mailer_tls plugin, then all you have to do is configure Action Mailer:
<ruby>
ActionMailer::Base.smtp_settings = {
diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile
index afff892fd4..bf6e3c8181 100644
--- a/railties/guides/source/active_record_basics.textile
+++ b/railties/guides/source/active_record_basics.textile
@@ -69,7 +69,7 @@ h4. Schema Conventions
ActiveRecord uses naming conventions for the columns in database tables, depending on the purpose of these columns.
* *Foreign keys* - These fields should be named following the pattern table_id i.e. (item_id, order_id). These are the fields that ActiveRecord will look for when you create associations between your models.
-* *Primary keys* - By default, ActiveRecord will use a integer column named "id" as the table's primary key. When using "Rails Migrations":http://guides.rails.info/migrations.html to create your tables, this column will be automatically created.
+* *Primary keys* - By default, ActiveRecord will use an integer column named "id" as the table's primary key. When using "Rails Migrations":migrations.html to create your tables, this column will be automatically created.
There are also some optional column names that will create additional features to ActiveRecord instances:
@@ -127,9 +127,9 @@ end
h3. Validations
-ActiveRecord gives the ability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the life-cycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_overview_of_activerecord_validation.
+ActiveRecord gives the ability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the life-cycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#validations-overview.
h3. Callbacks
-ActiveRecord callbacks allow you to attach code to certain events in the life-cycle of your models. This way you can add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_callbacks.
+ActiveRecord callbacks allow you to attach code to certain events in the life-cycle of your models. This way you can add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#callbacks-overview.
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index b112c4f5fb..302dad4f1a 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -6,7 +6,6 @@ This guide covers different ways to retrieve data from the database using Active
* Specify the order, retrieved attributes, grouping, and other properties of the found records
* Use eager loading to reduce the number of database queries needed for data retrieval
* Use dynamic finders methods
-* Create named scopes to add custom finding behavior to your models
* Check for the existence of particular records
* Perform various calculations on Active Record models
@@ -23,7 +22,6 @@ TIP: All of the following models uses +id+ as the primary key, unless specified
<ruby>
class Client < ActiveRecord::Base
has_one :address
- has_one :mailing_address
has_many :orders
has_and_belongs_to_many :roles
end
@@ -36,11 +34,6 @@ end
</ruby>
<ruby>
-class MailingAddress < Address
-end
-</ruby>
-
-<ruby>
class Order < ActiveRecord::Base
belongs_to :client, :counter_cache => true
end
@@ -245,7 +238,7 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t
h4. Array Conditions
-Now what if that number could vary, say as a argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like:
+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]])
@@ -465,7 +458,7 @@ Be careful because this also means you're initializing a model object with only
ActiveRecord::MissingAttributeError: missing attribute: <attribute>
</shell>
-Where +<attribute>+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
+Where +&lt;attribute&gt;+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
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:
@@ -539,7 +532,7 @@ This will return single order objects for each day, but only for the last month.
h4. Readonly Objects
-To explicitly disallow modification/destroyal 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 by +Model.find+, you could specify the +:readonly+ option 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:
@@ -807,12 +800,12 @@ For every field (also known as an attribute) you define in your table, Active Re
You can do +find_last_by_*+ methods too which will find the last record matching your argument.
-You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+
+You can specify an exclamation point (<tt>!</tt>) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+
If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked("Ryan", true)+.
-There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+:
+There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+:
<sql>
SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1
diff --git a/railties/guides/source/active_support_overview.textile b/railties/guides/source/active_support_overview.textile
new file mode 100644
index 0000000000..aea77c8d4e
--- /dev/null
+++ b/railties/guides/source/active_support_overview.textile
@@ -0,0 +1,818 @@
+h2. Active Support Overview
+
+Active Support is the Rails component responsible for providing Ruby language extensions, utilities, and other transversal stuff. It offers a richer bottom-line at the language level, targeted both at the development of Rails applications, and at the development of Rails itself.
+
+By referring to this guide you will learn:
+
+* The extensions to the Ruby core modules and classes provided by Rails.
+* The rest of fundamental libraries available in Rails.
+
+endprologue.
+
+h3. Extensions to All Objects
+
+h4. +blank?+ and +present?+
+
+The following values are considered to be blank in a Rails application:
+
+* +nil+ and +false+,
+
+* strings composed only of whitespace, i.e. matching +/\A\s*\z/+,
+
+* empty arrays and hashes, and
+
+* any other object that responds to +empty?+ and it is empty.
+
+WARNING: Note that numbers are not mentioned, in particular 0 and 0.0 are *not* blank.
+
+For example, this method from +ActionDispatch::Response+ uses +blank?+ to easily be robust to +nil+ and whitespace strings in one shot:
+
+<ruby>
+def charset
+ charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
+ charset.blank? ? nil : charset.strip.split("=")[1]
+end
+</ruby>
+
+That's a typical use case for +blank?+.
+
+Here, the method Rails runs to instantiate observers upon initialization has nothing to do if there are none:
+
+<ruby>
+def instantiate_observers
+ return if @observers.blank?
+ # ...
+end
+</ruby>
+
+The method +present?+ is equivalent to +!blank?+:
+
+<ruby>
+assert @response.body.present? # same as !@response.body.blank?
+</ruby>
+
+h4. +duplicable?+
+
+A few fundamental objects in Ruby are singletons. For example, in the whole live of a program the integer 1 refers always to the same instance:
+
+<ruby>
+1.object_id # => 3
+Math.cos(0).to_i.object_id # => 3
+</ruby>
+
+Hence, there's no way these objects can be duplicated through +dup+ or +clone+:
+
+<ruby>
+true.dup # => TypeError: can't dup TrueClass
+</ruby>
+
+Some numbers which are not singletons are not duplicable either:
+
+<ruby>
+0.0.clone # => allocator undefined for Float
+(2**1024).clone # => allocator undefined for Bignum
+</ruby>
+
+Active Support provides +duplicable?+ to programmatically query an object about this property:
+
+<ruby>
+"".duplicable? # => true
+false.duplicable? # => false
+</ruby>
+
+By definition all objects are +duplicable?+ except +nil+, +false+, +true+, symbols, numbers, and class objects.
+
+WARNING. Using +duplicable?+ is discouraged because it depends on a hard-coded list. Classes have means to disallow duplication like removing +dup+ and +clone+ or raising exceptions from them, only +rescue+ can tell.
+
+h4. +returning+
+
+The method +returning+ yields its argument to a block and returns it. You tipically use it with a mutable object that gets modified in the block:
+
+<ruby>
+def html_options_for_form(url_for_options, options, *parameters_for_url)
+ returning options.stringify_keys do |html_options|
+ html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
+ html_options["action"] = url_for(url_for_options, *parameters_for_url)
+ end
+end
+</ruby>
+
+See also "+Object#tap+":#tap.
+
+h4. +tap+
+
++Object#tap+ exists in Ruby 1.8.7 and 1.9, and it is defined by Active Support for previous versions. This method yields its receiver to a block and returns it.
+
+For example, the following class method from +ActionDispatch::TestResponse+ creates, initializes, and returns a new test response using +tap+:
+
+<ruby>
+def self.from_response(response)
+ new.tap do |resp|
+ resp.status = response.status
+ resp.headers = response.headers
+ resp.body = response.body
+ end
+end
+</ruby>
+
+See also "+Object#returning+":#returning.
+
+h4. +try+
+
+Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first.
+
+For instance, note how this method of +ActiveRecord::ConnectionAdapters::AbstractAdapter+ checks if there's a +@logger+:
+
+<ruby>
+def log_info(sql, name, ms)
+ if @logger && @logger.debug?
+ name = '%s (%.1fms)' % [name || 'SQL', ms]
+ @logger.debug(format_log_entry(name, sql.squeeze(' ')))
+ end
+end
+</ruby>
+
+You can shorten that using +Object#try+. This method is a synonim for +Object#send+ except that it returns +nil+ if sent to +nil+. The previous example could then be rewritten as:
+
+<ruby>
+def log_info(sql, name, ms)
+ if @logger.try(:debug?)
+ name = '%s (%.1fms)' % [name || 'SQL', ms]
+ @logger.debug(format_log_entry(name, sql.squeeze(' ')))
+ end
+end
+</ruby>
+
+h4. +metaclass+
+
+The method +metaclass+ returns the singleton class on any object:
+
+<ruby>
+String.metaclass # => #<Class:String>
+String.new.metaclass # => #<Class:#<String:0x17a1d1c>>
+</ruby>
+
+h4. +class_eval(*args, &block)+
+
+You can evaluate code in the context of any object's singleton class using +class_eval+:
+
+<ruby>
+class Proc
+ def bind(object)
+ block, time = self, Time.now
+ object.class_eval do
+ method_name = "__bind_#{time.to_i}_#{time.usec}"
+ define_method(method_name, &block)
+ method = instance_method(method_name)
+ remove_method(method_name)
+ method
+ end.bind(object)
+ end
+end
+</ruby>
+
+h4. +acts_like?(duck)+
+
+The method +acts_like+ provides a way to check whether some class acts like some other class based on a simple convention: a class that provides the same interface as +String+ defines
+
+<ruby>
+def acts_like_string?
+end
+</ruby>
+
+which is only a marker, its body or return value are irrelevant. Then, client code can query for duck-type-safeness this way:
+
+<ruby>
+some_klass.acts_like?(:string)
+</ruby>
+
+Rails has classes that act like +Date+ or +Time+ and follow this contract.
+
+h4. +to_param+
+
+All objects in Rails respond to the method +to_param+, which is meant to return something that represents them as values in a query string, or as a URL fragments.
+
+By default +to_param+ just calls +to_s+:
+
+<ruby>
+7.to_param # => "7"
+</ruby>
+
+The return value of +to_param+ should *not* be escaped:
+
+<ruby>
+"Tom & Jerry".to_param # => "Tom & Jerry"
+</ruby>
+
+Several classes in Rails overwrite this method.
+
+For example +nil+, +true+, and +false+ return themselves. +Array#to_param+ calls +to_param+ on the elements and joins the result with "/":
+
+<ruby>
+[0, true, String].to_param # => "0/true/String"
+</ruby>
+
+Notably, the Rails routing system calls +to_param+ on models to get a value for the +:id+ placeholder. +ActiveRecord::Base#to_param+ returns the +id+ of a model, but you can redefine that method in your models. For example, given
+
+<ruby>
+class User
+ def to_param
+ "#{id}-#{name.parameterize}"
+ end
+end
+</ruby>
+
+we get:
+
+<ruby>
+user_path(@user) # => "/users/357-john-smith"
+</ruby>
+
+WARNING. Controllers need to be aware of any redifinition of +to_param+ because when a request like that comes in "357-john-smith" is the value of +params[:id]+.
+
+h4. +to_query+
+
+Except for hashes, given an unescaped +key+ this method constructs the part of a query string that would map such key to what +to_param+ returns. For example, given
+
+<ruby>
+class User
+ def to_param
+ "#{id}-#{name.parameterize}"
+ end
+end
+</ruby>
+
+we get:
+
+<ruby>
+current_user.to_query('user') # => user=357-john-smith
+</ruby>
+
+This method escapes whatever is needed, both for the key and the value:
+
+<ruby>
+account.to_query('company[name]')
+# => "company%5Bname%5D=Johnson+%26+Johnson"
+</ruby>
+
+so its output is ready to be used in a query string.
+
+Arrays return the result of applying +to_query+ to each element with <tt>_key_[]</tt> as key, and join the result with "/":
+
+<ruby>
+[3.4, -45.6].to_query('sample')
+# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
+</ruby>
+
+Hashes also respond to +to_query+ but with a different signature. If no argument is passed a call generates a sorted series of key/value assigments calling +to_query(key)+ on its values. Then it joins the result with "&":
+
+<ruby>
+{:c => 3, :b => 2, :a => 1}.to_query # => "a=1&b=2&c=3"
+</ruby>
+
+The method +Hash#to_query+ accepts an optional namespace for the keys:
+
+<ruby>
+{:id => 89, :name => "John Smith"}.to_query('user')
+# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
+</ruby>
+
+h4. +with_options+
+
+The method +with_options+ provides a way to factor out common options in a series of method calls.
+
+Given a default options hash, +with_options+ yields a proxy object to a block. Within the block, methods called on the proxy are forwarded to the receiver with their options merged. For example, you get rid of the duplication in:
+
+<ruby>
+class Account < ActiveRecord::Base
+ has_many :customers, :dependent => :destroy
+ has_many :products, :dependent => :destroy
+ has_many :invoices, :dependent => :destroy
+ has_many :expenses, :dependent => :destroy
+end
+</ruby>
+
+this way:
+
+<ruby>
+class Account < ActiveRecord::Base
+ with_options :dependent => :destroy do |assoc|
+ assoc.has_many :customers
+ assoc.has_many :products
+ assoc.has_many :invoices
+ assoc.has_many :expenses
+ end
+end
+</ruby>
+
+That idiom may convey _grouping_ to the reader as well. For example, say you want to send a newsletter whose language depends on the user. Somewhere in the mailer you could group locale-dependent bits like this:
+
+<ruby>
+I18n.with_options :locale => user.locale, :scope => "newsletter" do |i18n|
+ subject i18n.t :subject
+ body i18n.t :body, :user_name => user.name
+end
+</ruby>
+
+TIP: Since +with_options+ forwards calls to its receiver they can be nested. Each nesting level will merge inherited defaults in addition to their own.
+
+h4. Instance Variables
+
+Active Support provides several methods to ease access to instance variables.
+
+h5. +instance_variable_defined?+
+
+The method +instance_variable_defined?+ exists in Ruby 1.8.6 and later, and it is defined for previous versions anyway:
+
+<ruby>
+class C
+ def initialize
+ @a = 1
+ end
+
+ def m
+ @b = 2
+ end
+end
+
+c = C.new
+
+c.instance_variable_defined?("@a") # => true
+c.instance_variable_defined?(:@a) # => true
+c.instance_variable_defined?("a") # => NameError: `a' is not allowed as an instance variable name
+
+c.instance_variable_defined?("@b") # => false
+c.m
+c.instance_variable_defined?("@b") # => true
+</ruby>
+
+h5. +instance_variable_names+
+
+Ruby 1.8 and 1.9 have a method called +instance_variables+ that returns the names of the defined instance variables. But they behave differently, in 1.8 it returns strings whereas in 1.9 it returns symbols. Active Support defines +instance_variable_names+ as a portable way to obtain them as strings:
+
+<ruby>
+class C
+ def initialize(x, y)
+ @x, @y = x, y
+ end
+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.
+
+h5. +instance_values+
+
+The method +instance_values+ returns a hash that maps instance variable names without "@" to their
+corresponding values. Keys are strings both in Ruby 1.8 and 1.9:
+
+<ruby>
+class C
+ def initialize(x, y)
+ @x, @y = x, y
+ end
+end
+
+C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
+</ruby>
+
+h5. +copy_instance_variables_from(object, exclude = [])+
+
+Copies the instance variables of +object+ into +self+.
+
+Instance variable names in the +exclude+ array are ignored. If +object+
+responds to +protected_instance_variables+ the ones returned are
+also ignored. For example, Rails controllers implement that method.
+
+In both arrays strings and symbols are understood, and they have to include
+the at sign.
+
+<ruby>
+class C
+ def initialize(x, y, z)
+ @x, @y, @z = x, y, z
+ end
+
+ def protected_instance_variables
+ %w(@z)
+ end
+end
+
+a = C.new(0, 1, 2)
+b = C.new(3, 4, 5)
+
+a.copy_instance_variables_from(b, [:@y])
+# a is now: @x = 3, @y = 1, @z = 2
+</ruby>
+
+In the example +object+ and +self+ are of the same type, but they don't need to.
+
+h4. Silencing Warnings, Streams, and Exceptions
+
+The methods +silence_warnings+ and +enable_warnings+ change the value of +$VERBOSE+ accordingly for the duration of their block, and reset it afterwards:
+
+<ruby>
+silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
+</ruby>
+
+You can silence any stream while a block runs with +silence_stream+:
+
+<ruby>
+silence_stream(STDOUT) do
+ # STDOUT is silent here
+end
+</ruby>
+
+Silencing exceptions is also possible with +suppress+. This method receives an arbitrary number of exception classes. If an exception is raised during the execution of the block and is +kind_of?+ any of the arguments, +suppress+ captures it and returns silently. Otherwise the exception is reraised:
+
+<ruby>
+# If the user is locked the increment is lost, no big deal.
+suppress(ActiveRecord::StaleObjectError) do
+ current_user.increment! :visits
+end
+</ruby>
+
+h3. Extensions to +Module+
+
+...
+
+h3. Extensions to +Class+
+
+h4. Class Attribute Accessors
+
+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:
+
+<ruby>
+class MysqlAdapter < AbstractAdapter
+ # Generates class methods to access @@emulate_booleans.
+ cattr_accessor :emulate_booleans
+ self.emulate_booleans = true
+end
+</ruby>
+
+Instance methods are created as well for convenience. For example given
+
+<ruby>
+module ActionController
+ class Base
+ cattr_accessor :logger
+ end
+end
+</ruby>
+
+we can access +logger+ in actions. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
+
+<ruby>
+module ActiveRecord
+ class Base
+ # No pluralize_table_names= instance writer is generated.
+ cattr_accessor :pluralize_table_names, :instance_writer => false
+ end
+end
+</ruby>
+
+h4. Class Inheritable Attributes
+
+Class variables are shared down the inheritance tree. Class instance variables are not shared, but they are not inherited either. The macros +class_inheritable_reader+, +class_inheritable_writer+, and +class_inheritable_accessor+ provide accesors for class-level data which is inherited but not shared with children:
+
+<ruby>
+module ActionController
+ class Base
+ # FIXME: REVISE/SIMPLIFY THIS COMMENT.
+ # The value of allow_forgery_protection is inherited,
+ # but its value in a particular class does not affect
+ # the value in the rest of the controllers hierarchy.
+ class_inheritable_accessor :allow_forgery_protection
+ end
+end
+</ruby>
+
+They accomplish this with class instance variables and cloning on subclassing, there are no class variables involved. Cloning is performed with +dup+ as long as the value is duplicable.
+
+There are some variants specialised in arrays and hashes:
+
+<ruby>
+class_inheritable_array
+class_inheritable_hash
+</ruby>
+
+Those writers take any inherited array or hash into account and extend them rather than overwrite them.
+
+As with vanilla class attribute accessors these macros create convenience instance methods for reading and writing. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
+
+<ruby>
+module ActiveRecord
+ class Base
+ class_inheritable_accessor :default_scoping, :instance_writer => false
+ end
+end
+</ruby>
+
+Since values are copied when a subclass is defined, if the base class changes the attribute after that, the subclass does not see the new value. That's the point.
+
+There's a related macro called +superclass_delegating_accessor+, however, that does not copy the value when the base class is subclassed. Instead, it delegates reading to the superclass as long as the attribute is not set via its own writer. For example, +ActionMailer::Base+ defines +delivery_method+ this way:
+
+<ruby>
+module ActionMailer
+ class Base
+ superclass_delegating_accessor :delivery_method
+ self.delivery_method = :smtp
+ end
+end
+</ruby>
+
+If for whatever reason an application loads the definition of a mailer class and after that sets +ActionMailer::Base.delivery_method+, the mailer class will still see the new value. In addition, the mailer class is able to change the +delivery_method+ without affecting the value in the parent using its own inherited class attribute writer.
+
+h4. Subclasses
+
+The +subclasses+ method returns the names of all subclasses of a given class as an array of strings. That comprises not only direct subclasses, but all descendants down the hierarchy:
+
+<ruby>
+class C; end
+C.subclasses # => []
+
+Integer.subclasses # => ["Bignum", "Fixnum"]
+
+module M
+ class A; end
+ class B1 < A; end
+ class B2 < A; end
+end
+
+module N
+ class C < M::B1; end
+end
+
+M::A.subclasses # => ["N::C", "M::B2", "M::B1"]
+</ruby>
+
+The order in which these class names are returned is unspecified.
+
+See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
+
+h4. Class Removal
+
+Roughly speaking, the +remove_class+ method removes the class objects passed as arguments:
+
+<ruby>
+Class.remove_class(Hash, Dir) # => [Hash, Dir]
+Hash # => NameError: uninitialized constant Hash
+Dir # => NameError: uninitialized constant Dir
+</ruby>
+
+More specifically, +remove_class+ attempts to remove constants with the same name as the passed class objects from their parent modules. So technically this method does not guarantee the class objects themselves are not still valid and alive somewhere after the method call:
+
+<ruby>
+module M
+ class A; end
+ class B < A; end
+end
+
+A2 = M::A
+
+M::A.object_id # => 13053950
+Class.remove_class(M::A)
+
+M::B.superclass.object_id # => 13053950 (same object as before)
+A2.name # => "M::A" (name is hard-coded in object)
+</ruby>
+
+WARNING: Removing fundamental classes like +String+ can result in really funky behaviour.
+
+The method +remove_subclasses+ provides a shortcut for removing all descendants of a given class, where "removing" has the meaning explained above:
+
+<ruby>
+class A; end
+class B1 < A; end
+class B2 < A; end
+class C < A; end
+
+A.subclasses # => ["C", "B2", "B1"]
+A.remove_subclasses
+A.subclasses # => []
+C # => NameError: uninitialized constant C
+</ruby>
+
+See also +Object#remove_subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
+
+h3. Extensions to +NilClass+
+
+...
+
+h3. Extensions to +TrueClass+
+
+...
+
+h3. Extensions to +FalseClass+
+
+...
+
+h3. Extensions to +Symbol+
+
+...
+
+h3. Extensions to +String+
+
+...
+
+h3. Extensions to +Numeric+
+
+...
+
+h3. Extensions to +Integer+
+
+...
+
+h3. Extensions to +Float+
+
+...
+
+h3. Extensions to +BigDecimal+
+
+...
+
+h3. Extensions to +Enumerable+
+
+...
+
+h3. Extensions to +Array+
+
+h4. Accessing
+
+Active Support augments the API of arrays to ease certain ways of accessing them. For example, +to+ returns the subarray of elements up to the one at the passed index:
+
+<ruby>
+%w(a b c d).to(2) # => %w(a b c)
+[].to(7) # => []
+</ruby>
+
+Similarly, +from+ returns the tail from the element at the passed index on:
+
+<ruby>
+%w(a b c d).from(2) # => %w(c d)
+%w(a b c d).from(10) # => nil
+[].from(0) # => nil
+</ruby>
+
+The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding element (+first+ is builtin). Thanks to social wisdom and positive constructiveness all around, +forty_two+ is also available.
+
+You can pick a random element with +rand+:
+
+<ruby>
+shape_type = [Circle, Square, Triangle].rand
+</ruby>
+
+h4. Grouping
+
+h5. +in_groups_of(number, fill_with = nil)+
+
+The method +in_groups_of+ splits an array into consecutive groups of a certain size. It returns an array with the groups:
+
+<ruby>
+[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
+</ruby>
+
+or yields them in turn if a block is passed:
+
+<ruby>
+<% sample.in_groups_of(3) do |a, b, c| %>
+ <tr>
+ <td><%=h a %></td>
+ <td><%=h b %></td>
+ <td><%=h c %></td>
+ </tr>
+<% end %>
+</ruby>
+
+The first example shows +in_groups_of+ fills the last group with as many +nil+ elements as needed to have the requested size. You can change this padding value using the second optional argument:
+
+<ruby>
+[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
+</ruby>
+
+And you can tell the method not to fill the last group passing +false+:
+
+<ruby>
+[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
+</ruby>
+
+As a consequence +false+ can't be a used as a padding value.
+
+h5. +in_groups(number, fill_with = nil)+
+
+The method +in_groups+ splits an array into a certain number of groups. The method returns and array with the groups:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3)
+# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
+</ruby>
+
+or yields them in turn if a block is passed:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
+["1", "2", "3"]
+["4", "5", nil]
+["6", "7", nil]
+</ruby>
+
+The examples above show that +in_groups+ fills some groups with a trailing +nil+ element as needed. A group can get at most one of these extra elements, the rightmost one if any. And the groups that have them are always the last ones.
+
+You can change this padding value using the second optional argument:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3, "0")
+# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
+</ruby>
+
+And you can tell the method not to fill the smaller groups passing +false+:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3, false)
+# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
+</ruby>
+
+As a consequence +false+ can't be a used as a padding value.
+
+h5. +split(value = nil)+
+
+The method +split+ divides an array by a separator and returns the resulting chunks.
+
+If a block is passed the separators are those elements of the array for which the block returns true:
+
+<ruby>
+(-5..5).to_a.split { |i| i.multiple_of?(4) }
+# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
+</ruby>
+
+Otherwise, the value received as argument, which defaults to +nil+, is the separator:
+
+<ruby>
+[0, 1, -5, 1, 1, "foo", "bar"].split(1)
+# => [[0], [-5], [], ["foo", "bar"]]
+</ruby>
+
+NOTE: Observe in the previous example that consecutive separators result in empty arrays.
+
+h3. Extensions to +Hash+
+
+...
+
+h3. Extensions to +Range+
+
+...
+
+h3. Extensions to +Proc+
+
+...
+
+h3. Extensions to +Date+
+
+...
+
+h3. Extensions to +DateTime+
+
+...
+
+h3. Extensions to +Time+
+
+...
+
+h3. Extensions to +Process+
+
+...
+
+h3. Extensions to +Pathname+
+
+...
+
+h3. Extensions to +File+
+
+...
+
+h3. Extensions to +Exception+
+
+...
+
+h3. Extensions to +NameError+
+
+...
+
+h3. Extensions to +LoadError+
+
+...
+
+h3. Extensions to +CGI+
+
+...
+
+h3. Extensions to +Benchmark+
+
+...
+
+h3. Changelog
+
+"Lighthouse ticket":https://rails.lighthouseapp.com/projects/16213/tickets/67
+
+* April 18, 2009: Initial version by "Xavier Noria":credits.html#fxn
diff --git a/railties/guides/source/activerecord_validations_callbacks.textile b/railties/guides/source/activerecord_validations_callbacks.textile
index 5ae4884297..03d521ea1f 100644
--- a/railties/guides/source/activerecord_validations_callbacks.textile
+++ b/railties/guides/source/activerecord_validations_callbacks.textile
@@ -338,7 +338,7 @@ end
Besides +:only_integer+, the +validates_numericality_of+ helper also accepts the following options to add constraints to acceptable values:
* +:greater_than+ - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than {{count}}_".
-* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}".
+* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}_".
* +:equal_to+ - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to {{count}}_".
* +:less_than+ - Specifies the value must be less than the supplied value. The default error message for this option is "_must be less than {{count}}_".
* +:less_than_or_equal_to+ - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less or equal to {{count}}_".
@@ -530,11 +530,11 @@ class Invoice < ActiveRecord::Base
end
</ruby>
-You can even create your own validation helpers and reuse them in several different models. Here is an example where we create a custom validation helper to validate the format of fields that represent email addresses:
+You can even create your own validation helpers and reuse them in several different models. For example, an application that manages surveys may find useful to express that a certain field corresponds to a set of choices:
<ruby>
ActiveRecord::Base.class_eval do
- def self.validates_as_radio(attr_name, n, options={})
+ def self.validates_as_choice(attr_name, n, options={})
validates_inclusion_of attr_name, {:in => 1..n}.merge(options)
end
end
@@ -544,7 +544,7 @@ Simply reopen +ActiveRecord::Base+ and define a class method like that. You'd ty
<ruby>
class Movie < ActiveRecord::Base
- validates_as_radio :rating, 5
+ validates_as_choice :rating, 5
end
</ruby>
diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile
new file mode 100644
index 0000000000..74e8dec5cf
--- /dev/null
+++ b/railties/guides/source/ajax_on_rails.textile
@@ -0,0 +1,94 @@
+h2. AJAX on Rails
+
+This guide covers the built-in Ajax/Javascript functionality of Rails (and more); it will enable you to create rich and dynamic AJAX applications with ease! We will cover the following topics:
+
+* Quick introduction to AJAX and related technologies
+* Handling Javascript the Rails way: Rails helpers, RJS, Prototype and script.aculo.us
+* Testing Javascript functionality
+* Becoming an Ajax Master on Rails: Plugins, Best Practices, Tips and Tricks
+
+endprologue.
+
+h3. Hello AJAX - a Quick Intro
+
+If you are a 'show me the code' type of person, you might want to skip this part and jump to the RJS section right away. However, I would really recommend to read it - you'll need the basics of DOM, http requests and other topics discussed here to really understand Ajax on Rails.
+
+h4. Asynchronous Javascript + XML
+
+Basic terminology, new style of creating web apps
+
+h4. The DOM
+
+basics of the DOM, how is it built, properties, features, why is it central to AJAX
+
+h4. Standard HTML communication vs AJAX
+
+How do 'standard' and AJAX requests differ, why does this matter for understanding AJAX on Rails (tie in for *_remote helpers, the next section)
+
+
+
+
+
+
+h3. Built-in Rails Helpers
+
+Mostly a reference to standard JS helpers like link_to_remote, remote_form_for etc + some explanation
+
+
+
+h3. Responding to AJAX the Rails way: RJS
+
+In the last section we sent some AJAX requests to the server; now we need to respond, and the standard Rails way to this is using RJS; RJS intro, function reference
+
+
+
+h3. I Want my Yellow Thingy: Prototype and Script.aculo.us
+
+Walk through prototype and script.aculo.us, most important functionality, method reference etc.
+
+
+
+h3. Testing Javascript
+
+Javascript testing reminds me the definition of the world 'classic' by Mark Twain: "A classic is something that everybody wants to have read and nobody wants to read." It's similar with Javascript testing: everyone would like to have it, yet it's not done by too much developers as it is tedious, complicated, there is a proliferation of tools and no consensus/accepted best practices, but we will nevertheless take a stab at it:
+
+* (Fire)Watir
+* Selenium
+* Celerity/Culerity
+* Cucumber+Webrat
+* Mention stuff like screw.unit/jsSpec
+
+Note to self: check out the RailsConf JS testing video
+
+h3. Useful Plugins
+
+This was in the ticket description, but at the moment I don't really have clue what to add here, so please tell me
+
+
+
+h3. Tips and Tricks
+
+* Unobtrusive Javascript (Prototype events, maybe the jQuery way (esp. jQeury.live()))
+
+* Minimize communication with the server - there does not have to be a communication at all!
+** If you absolutely don't have to, don't use Rails observers
+** Cache stuff on the client side, e.g. with auto-complete
+
+* Using AJAX to load stuff asynchronously
+** To avoid page blocking
+** Tricking page caching
+*** inserting user-specific info into a cached page
+*** anti-CSFR bit
+
+* Jumping to the top? Try event.stopPropagation
+
+* Performance
+** pack your javascript (minify, asset packager)
+** require your JS at the end of the file
+** other perf tricks and optimization
+
+* Don't overuse AJAX
+** Usability first, cool effects second
+** situations where AJAX is discouraged
+
+* Last but not least: Javascript is your friend :)
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index 03e22bd6fe..ca10014ee0 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -30,7 +30,7 @@ Now, suppose we wanted to add a new order for an existing customer. We'd need to
Or consider deleting a customer, and ensuring that all of its orders get deleted as well:
<ruby>
-@orders = Order.find_by_customer_id(@customer.id)
+@orders = Order.find_all_by_customer_id(@customer.id)
@orders.each do |order|
order.destroy
end
@@ -600,6 +600,7 @@ The +belongs_to+ association supports these options:
* +:polymorphic+
* +:readonly+
* +:select+
+* +:touch+
* +:validate+
h6. +:autosave+
@@ -736,6 +737,28 @@ The +:select+ option lets you override the SQL +SELECT+ clause that is used to r
TIP: If you set the +:select+ option on a +belongs_to+ association, you should also set the +foreign_key+ option to guarantee the correct results.
+h6. +:touch+
+
+If you set the +:touch+ option to +:true+, then the +updated_at+ or +updated_on+ timestamp on the associated object will be set to the current time whenever this object is saved or destroyed:
+
+<ruby>
+class Order < ActiveRecord::Base
+ belongs_to :customer, :touch => true
+end
+
+class Customer < ActiveRecord::Base
+ has_many :orders
+end
+</ruby>
+
+In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update:
+
+<ruby>
+class Order < ActiveRecord::Base
+ belongs_to :customer, :touch => :orders_updated_at
+end
+</ruby>
+
h6. +:validate+
If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved.
@@ -996,7 +1019,7 @@ When you declare a +has_many+ association, the declaring class automatically gai
* <tt><em>collection</em>.empty?</tt>
* <tt><em>collection</em>.size</tt>
* <tt><em>collection</em>.find(...)</tt>
-* <tt><em>collection</em>.exist?(...)</tt>
+* <tt><em>collection</em>.exists?(...)</tt>
* <tt><em>collection</em>.build(attributes = {}, ...)</tt>
* <tt><em>collection</em>.create(attributes = {})</tt>
@@ -1021,7 +1044,7 @@ orders.clear
orders.empty?
orders.size
orders.find(...)
-orders.exist?(...)
+orders.exists?(...)
orders.build(attributes = {}, ...)
orders.create(attributes = {})
</ruby>
@@ -1099,9 +1122,9 @@ The <tt><em>collection</em>.find</tt> method finds objects within the collection
@open_orders = @customer.orders.find(:all, :conditions => "open = 1")
</ruby>
-h6. <tt><em>collection</em>.exist?(...)</tt>
+h6. <tt><em>collection</em>.exists?(...)</tt>
-The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
+The <tt><em>collection</em>.exists?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
h6. <tt><em>collection</em>.build(attributes = {}, ...)</tt>
@@ -1196,6 +1219,17 @@ end
If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@customer.confirmed_orders.create+ or +@customer.confirmed_orders.build+ will create orders where the confirmed column has the value +true+.
+If you need to evaluate conditions dynamically at runtime, you could use string interpolation in single quotes:
+
+<ruby>
+class Customer < ActiveRecord::Base
+ has_many :latest_orders, :class_name => "Order",
+ :conditions => 'orders.created_at > #{10.hours.ago.to_s(:db).inspect}'
+end
+</ruby>
+
+Be sure to use single quotes.
+
h6. +:counter_sql+
Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
@@ -1361,7 +1395,7 @@ When you declare a +has_and_belongs_to_many+ association, the declaring class au
* <tt><em>collection</em>.empty?</tt>
* <tt><em>collection</em>.size</tt>
* <tt><em>collection</em>.find(...)</tt>
-* <tt><em>collection</em>.exist?(...)</tt>
+* <tt><em>collection</em>.exists?(...)</tt>
* <tt><em>collection</em>.build(attributes = {})</tt>
* <tt><em>collection</em>.create(attributes = {})</tt>
@@ -1386,7 +1420,7 @@ assemblies.clear
assemblies.empty?
assemblies.size
assemblies.find(...)
-assemblies.exist?(...)
+assemblies.exists?(...)
assemblies.build(attributes = {}, ...)
assemblies.create(attributes = {})
</ruby>
@@ -1471,9 +1505,9 @@ The <tt><em>collection</em>.find</tt> method finds objects within the collection
:conditions => ["created_at > ?", 2.days.ago])
</ruby>
-h6. <tt><em>collection</em>.exist?(...)</tt>
+h6. <tt><em>collection</em>.exists?(...)</tt>
-The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
+The <tt><em>collection</em>.exists?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
h6. <tt><em>collection</em>.build(attributes = {})</tt>
@@ -1775,6 +1809,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/11
+* April 19, 2009: Added +:touch+ option to +belongs_to+ associations by "Mike Gunderloy":credits.html#mgunderloy
* February 1, 2009: Added +:autosave+ option "Mike Gunderloy":credits.html#mgunderloy
* September 28, 2008: Corrected +has_many :through+ diagram, added polymorphic diagram, some reorganization by "Mike Gunderloy":credits.html#mgunderloy . First release version.
* September 22, 2008: Added diagrams, misc. cleanup by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index f1ad7b820d..2865bc504a 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -1,8 +1,6 @@
h2. Caching with Rails: An overview
-Everyone caches. This guide will teach you what you need to know about
-avoiding that expensive round-trip to your database and returning what you
-need to return to those hungry web clients in the shortest time possible.
+This guide will teach you what you need to know about avoiding that expensive round-trip to your database and returning what you need to return to the web clients in the shortest time possible.
After reading this guide, you should be able to use and configure:
@@ -15,14 +13,9 @@ endprologue.
h3. Basic Caching
-This is an introduction to the three types of caching techniques that Rails
-provides by default without the use of any third party plugins.
+This is an introduction to the three types of caching techniques that Rails provides by default without the use of any third party plugins.
-To start playing with testing you'll want to ensure that
-+config.action_controller.perform_caching+ is set
-to +true+ if you're running in development mode. This flag is normally set in the
-corresponding config/environments/*.rb and caching is disabled by default
- for development and test, and enabled for production.
+To start playing with testing you'll want to ensure that +config.action_controller.perform_caching+ is set to +true+ if you're running in development mode. This flag is normally set in the corresponding +config/environments/*.rb+ and caching is disabled by default for development and test, and enabled for production.
<ruby>
config.action_controller.perform_caching = true
@@ -30,16 +23,9 @@ config.action_controller.perform_caching = true
h4. Page Caching
-Page caching is a Rails mechanism which allows the request for a generated
-page to be fulfilled by the webserver (i.e. apache or nginx), without ever having to go through the
-Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be
-applied to every situation (such as pages that need authentication) and since
-the webserver is literally just serving a file from the filesystem, cache
-expiration is an issue that needs to be dealt with.
+Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with.
-So, how do you enable this super-fast cache behavior? Simple, let's say you
-have a controller called +ProductsController+ and an +index+ action that lists all
-the products
+So, how do you enable this super-fast cache behavior? Simple, let's say you have a controller called +ProductsController+ and an +index+ action that lists all the products
<ruby>
class ProductsController < ActionController
@@ -53,25 +39,13 @@ class ProductsController < ActionController
end
</ruby>
-The first time anyone requests +/products+, Rails will generate a file
-called +products.html+ and the webserver will then look for that file before it
-passes the next request for +/products+ to your Rails application.
+The first time anyone requests +/products+, Rails will generate a file called +products.html+ and the webserver will then look for that file before it passes the next request for +/products+ to your Rails application.
-By default, the page cache directory is set to +Rails.public_path+ (which is
-usually set to the +public+ folder) and this can be configured by
-changing the configuration setting +config.action_controller.page_cache_directory+.
-Changing the default from +public+ helps avoid naming conflicts, since you may
-want to put other static html in +public+, but changing this will require web
-server reconfiguration to let the web server know where to serve the cached
-files from.
+By default, the page cache directory is set to +Rails.public_path+ (which is usually set to the +public+ folder) and this can be configured by changing the configuration setting +config.action_controller.page_cache_directory+. Changing the default from +public+ helps avoid naming conflicts, since you may want to put other static html in +public+, but changing this will require web server reconfiguration to let the web server know where to serve the cached files from.
-The Page Caching mechanism will automatically add a +.html+ extension to
-requests for pages that do not have an extension to make it easy for the
-webserver to find those pages and this can be configured by changing the
-configuration setting +config.action_controller.page_cache_extension+.
+The Page Caching mechanism will automatically add a +.html+ extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages and this can be configured by changing the configuration setting +config.action_controller.page_cache_extension+.
-In order to expire this page when a new product is added we could extend our
-example controller like this:
+In order to expire this page when a new product is added we could extend our example controller like this:
<ruby>
class ProductsController < ActionController
@@ -83,26 +57,19 @@ class ProductsController < ActionController
end
def create
- expire_page :action => :index
+ expire_page :action => :list
end
end
</ruby>
-If you want a more complicated expiration scheme, you can use cache sweepers
-to expire cached objects when things change. This is covered in the section on Sweepers.
+If you want a more complicated expiration scheme, you can use cache sweepers to expire cached objects when things change. This is covered in the section on Sweepers.
Note: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL!
h4. Action Caching
-One of the issues with Page Caching is that you cannot use it for pages that
-require to restrict access somehow. This is where Action Caching comes in.
-Action Caching works like Page Caching except for the fact that the incoming
-web request does go from the webserver to the Rails stack and Action Pack so
-that before filters can be run on it before the cache is served. This allows
-authentication and other restriction to be run while still serving the
-result of the output from a cached copy.
+One of the issues with Page Caching is that you cannot use it for pages that require to restrict access somehow. This is where Action Caching comes in. Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy.
Clearing the cache works in the exact same way as with Page Caching.
@@ -125,37 +92,19 @@ class ProductsController < ActionController
end
</ruby>
-You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the
-action should be cached. Also, you can use +:layout => false+ to cache without
-layout so that dynamic information in the layout such as logged in user info
-or the number of items in the cart can be left uncached. This feature is
-available as of Rails 2.2.
+You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the action should be cached. Also, you can use +:layout => false+ to cache without layout so that dynamic information in the layout such as logged in user info or the number of items in the cart can be left uncached. This feature is available as of Rails 2.2.
-You can modify the default action cache path by passing a +:cache_path+ option.
-This will be passed directly to +ActionCachePath.path_for+. This is handy for
-actions with multiple possible routes that should be cached differently. If
-a block is given, it is called with the current controller instance.
+You can modify the default action cache path by passing a +:cache_path+ option. This will be passed directly to +ActionCachePath.path_for+. This is handy for actions with multiple possible routes that should be cached differently. If a block is given, it is called with the current controller instance.
-Finally, if you are using memcached, you can also pass +:expires_in+. In fact,
-all parameters not used by +caches_action+ are sent to the underlying cache
-store.
+Finally, if you are using memcached, you can also pass +:expires_in+. In fact, all parameters not used by +caches_action+ are sent to the underlying cache store.
h4. Fragment Caching
-Life would be perfect if we could get away with caching the entire contents of
-a page or action and serving it out to the world. Unfortunately, dynamic web
-applications usually build pages with a variety of components not all of which
-have the same caching characteristics. In order to address such a dynamically
-created page where different parts of the page need to be cached and expired
-differently Rails provides a mechanism called Fragment Caching.
+Life would be perfect if we could get away with caching the entire contents of a page or action and serving it out to the world. Unfortunately, dynamic web applications usually build pages with a variety of components not all of which have the same caching characteristics. In order to address such a dynamically created page where different parts of the page need to be cached and expired differently Rails provides a mechanism called Fragment Caching.
-Fragment Caching allows a fragment of view logic to be wrapped in a cache
-block and served out of the cache store when the next request comes in.
+Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.
-As an example, if you wanted to show all the orders placed on your website
-in real time and didn't want to cache that part of the page, but did want
-to cache the part of the page which lists all products available, you
-could use this piece of code:
+As an example, if you wanted to show all the orders placed on your website in real time and didn't want to cache that part of the page, but did want to cache the part of the page which lists all products available, you could use this piece of code:
<ruby>
<% Order.find_recent.each do |o| %>
@@ -170,9 +119,7 @@ could use this piece of code:
<% end %>
</ruby>
-The cache block in our example will bind to the action that called it and is
-written out to the same place as the Action Cache, which means that if you
-want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call:
+The cache block in our example will bind to the action that called it and is written out to the same place as the Action Cache, which means that if you want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call:
<ruby>
<% cache(:action => 'recent', :action_suffix => 'all_products') do %>
@@ -185,9 +132,7 @@ and you can expire it using the +expire_fragment+ method, like so:
expire_fragment(:controller => 'products', :action => 'recent', :action_suffix => 'all_products')
</ruby>
-If you don't want the cache block to bind to the action that called it, You can
-also use globally keyed fragments by calling the +cache+ method with a key, like
-so:
+If you don't want the cache block to bind to the action that called it, You can also use globally keyed fragments by calling the +cache+ method with a key, like so:
<ruby>
<% cache('all_available_products') do %>
@@ -195,8 +140,7 @@ so:
<% end %>
</ruby>
-This fragment is then available to all actions in the +ProductsController+ using
-the key and can be expired the same way:
+This fragment is then available to all actions in the +ProductsController+ using the key and can be expired the same way:
<ruby>
expire_fragment('all_available_products')
@@ -204,15 +148,9 @@ expire_fragment('all_available_products')
h4. Sweepers
-Cache sweeping is a mechanism which allows you to get around having a ton of
-+expire_{page,action,fragment}+ calls in your code. It does this by moving all the work
-required to expire cached content into a +ActionController::Caching::Sweeper+
-class. This class is an Observer and looks for changes to an object via callbacks,
-and when a change occurs it expires the caches associated with that object in
-an around or after filter.
+Cache sweeping is a mechanism which allows you to get around having a ton of +expire_{page,action,fragment}+ calls in your code. It does this by moving all the work required to expire cached content into a +ActionController::Caching::Sweeper+ class. This class is an Observer and looks for changes to an object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter.
-Continuing with our Product controller example, we could rewrite it with a
-sweeper like this:
+Continuing with our Product controller example, we could rewrite it with a sweeper like this:
<ruby>
class ProductSweeper < ActionController::Caching::Sweeper
@@ -244,18 +182,13 @@ class ProductSweeper < ActionController::Caching::Sweeper
end
</ruby>
-You may notice that the actual product gets passed to the sweeper, so if we
-were caching the edit action for each product, we could add a expire method
-which specifies the page we want to expire:
+You may notice that the actual product gets passed to the sweeper, so if we were caching the edit action for each product, we could add an expire method which specifies the page we want to expire:
<ruby>
expire_action(:controller => 'products', :action => 'edit', :id => product)
</ruby>
-Then we add it to our controller to tell it to call the sweeper when certain
-actions are called. So, if we wanted to expire the cached content for the
-list and edit actions when the create action was called, we could do the
-following:
+Then we add it to our controller to tell it to call the sweeper when certain actions are called. So, if we wanted to expire the cached content for the list and edit actions when the create action was called, we could do the following:
<ruby>
class ProductsController < ActionController
@@ -273,10 +206,7 @@ end
h4. SQL Caching
-Query caching is a Rails feature that caches the result set returned by each
-query so that if Rails encounters the same query again for that request, it
-will used the cached result set as opposed to running the query against the
-database again.
+Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again.
For example:
@@ -296,131 +226,85 @@ class ProductsController < ActionController
end
</ruby>
-The second time the same query is run against the database, it's not actually
-going to hit the database. The first time the result is returned from the query
-it is stored in the query cache (in memory) and the second time it's pulled from memory.
+The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory.
-However, it's important to note that query caches are created at the start of an action and destroyed at the end of
-that action and thus persist only for the duration of the action. If you'd like to store query results in a more
-persistent fashion, you can in Rails by using low level caching.
+However, it's important to note that query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action. If you'd like to store query results in a more persistent fashion, you can in Rails by using low level caching.
-h4. Cache stores
+h3. Cache Stores
-Rails (as of 2.1) provides different stores for the cached data created by action and
-fragment caches. Page caches are always stored on disk.
+Rails provides different stores for the cached data created by action and fragment caches. Page caches are always stored on disk.
-Rails 2.1 and above provide +ActiveSupport::Cache::Store+ which can be used to
-cache strings. Some cache store implementations, like MemoryStore, are able to
-cache arbitrary Ruby objects, but don't count on every cache store to be able
-to do that.
+Rails 2.1 and above provide +ActiveSupport::Cache::Store+ which can be used to cache strings. Some cache store implementations, like +MemoryStore+, are able to cache arbitrary Ruby objects, but don't count on every cache store to be able to do that.
The default cache stores provided with Rails include:
-1) ActiveSupport::Cache::MemoryStore: A cache store implementation which stores
-everything into memory in the same process. If you're running multiple Ruby on
-Rails server processes (which is the case if you're using mongrel_cluster or
-Phusion Passenger), then this means that your Rails server process instances
-won't be able to share cache data with each other. If your application never
-performs manual cache item expiry (e.g. when you‘re using generational cache
-keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you
-should be using this cache store.
+1) +ActiveSupport::Cache::MemoryStore+: A cache store implementation which stores everything into memory in the same process. If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then this means that your Rails server process instances won't be able to share cache data with each other. If your application never performs manual cache item expiry (e.g. when you‘re using generational cache keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you should be using this cache store.
-+MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
++MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
-+MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you
-need thread-safety.
++MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you need thread-safety.
-
<ruby>
ActionController::Base.cache_store = :memory_store
</ruby>
-2) ActiveSupport::Cache::FileStore: Cached data is stored on the disk, this is
-the default store and the default path for this store is: /tmp/cache. Works
-well for all types of environments and allows all processes running from the
-same application directory to access the cached content. If /tmp/cache does not
-exist, the default store becomes MemoryStore.
-
+2) +ActiveSupport::Cache::FileStore+: Cached data is stored on the disk, this is the default store and the default path for this store is +tmp/cache+. Works well for all types of environments and allows all processes running from the same application directory to access the cached content. If +tmp/cache+ does not exist, the default store becomes +MemoryStore+.
<ruby>
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
</ruby>
-3) ActiveSupport::Cache::DRbStore: Cached data is stored in a separate shared
-DRb process that all servers communicate with. This works for all environments
-and only keeps one cache around for all processes, but requires that you run
-and manage a separate DRb process.
-
+3) +ActiveSupport::Cache::DRbStore+: Cached data is stored in a separate shared DRb process that all servers communicate with. This works for all environments and only keeps one cache around for all processes, but requires that you run and manage a separate DRb process.
<ruby>
ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
</ruby>
-4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
-Rails uses the bundled memcached-client gem by default. This is currently the
-most popular cache store for production websites.
+4) +ActiveSupport::Cache::MemCacheStore+: Works like +DRbStore+, but uses Danga's +memcached+ instead. Rails uses the bundled +memcached-client+ gem by default. This is currently the most popular cache store for production websites.
Special features:
- * Clustering and load balancing. One can specify multiple memcached servers,
- and MemCacheStore will load balance between all available servers. If a
- server goes down, then MemCacheStore will ignore it until it goes back
- online.
- * Time-based expiry support. See +write+ and the +:expires_in+ option.
- * Per-request in memory cache for all communication with the MemCache server(s).
-It also accepts a hash of additional options:
+* Clustering and load balancing. One can specify multiple memcached servers, and +MemCacheStore+ will load balance between all available servers. If a server goes down, then +MemCacheStore+ will ignore it until it goes back online.
+* Time-based expiry support. See +write+ and the +:expires_in+ option.
+* Per-request in memory cache for all communication with the +memcached+ server(s).
- * +:namespace+- specifies a string that will automatically be prepended to keys when accessing the memcached store.
- * +:readonly+- a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
- * +:multithread+ - a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality.
+It also accepts a hash of additional options:
-The read and write methods of the MemCacheStore accept an options hash too.
-When reading you can specify +:raw => true+ to prevent the object being marshaled
-(by default this is false which means the raw value in the cache is passed to
-+Marshal.load+ before being returned to you.)
+* +:namespace+: specifies a string that will automatically be prepended to keys when accessing the memcached store.
+* +:readonly+: a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
+* +:multithread+: a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality.
-When writing to the cache it is also possible to specify +:raw => true+ means
-the value is not passed to +Marshal.dump+ before being stored in the cache (by
-default this is false).
+The read and write methods of the +MemCacheStore+ accept an options hash too. When reading you can specify +:raw => true+ to prevent the object being marshaled (by default this is false which means the raw value in the cache is passed to +Marshal.load+ before being returned to you.)
-The write method also accepts an +:unless_exist+ flag which determines whether
-the memcached add (when true) or set (when false) method is used to store the
-item in the cache and an +:expires_in+ option that specifies the time-to-live
-for the cached item in seconds.
+When writing to the cache it is also possible to specify +:raw => true+ means the value is not passed to +Marshal.dump+ before being stored in the cache (by default this is false).
+The write method also accepts an +:unless_exist+ flag which determines whether the memcached add (when true) or set (when false) method is used to store the item in the cache and an +:expires_in+ option that specifies the time-to-live for the cached item in seconds.
<ruby>
ActionController::Base.cache_store = :mem_cache_store, "localhost"
</ruby>
-5) ActiveSupport::Cache::SynchronizedMemoryStore: Like ActiveSupport::Cache::MemoryStore but thread-safe.
-
+5) +ActiveSupport::Cache::SynchronizedMemoryStore+: Like +MemoryStore+ but thread-safe.
<ruby>
ActionController::Base.cache_store = :synchronized_memory_store
</ruby>
-6) ActiveSupport::Cache::CompressedMemCacheStore: Works just like the regular
-MemCacheStore but uses GZip to decompress/compress on read/write.
-
+6) +ActiveSupport::Cache::CompressedMemCacheStore+: Works just like the regular +MemCacheStore+ but uses GZip to decompress/compress on read/write.
<ruby>
ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"
</ruby>
-7) Custom store: You can define your own cache store (new in Rails 2.1)
-
+7) Custom store: You can define your own cache store (new in Rails 2.1).
<ruby>
ActionController::Base.cache_store = MyOwnStore.new("parameter")
</ruby>
-+Note: +config.cache_store+ can be used in place of
-+ActionController::Base.cache_store+ in your +Rails::Initializer.run+ block in
-+environment.rb+
+NOTE: +config.cache_store+ can be used in place of +ActionController::Base.cache_store+ in your +Rails::Initializer.run+ block in +environment.rb+
-In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+
-method that generates a key using the class name, +id+ and +updated_at+ timestamp (if available).
+In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+ method that generates a key using the class name, +id+ and +updated_at+ timestamp (if available).
You can access these cache stores at a low level for storing queries and other objects. Here's an example:
@@ -432,21 +316,11 @@ Rails.cache.read("city") # => "Duckburgh"
h3. Conditional GET support
-Conditional GETs are a feature of the HTTP specification that provide a way for web
-servers to tell browsers that the response to a GET request hasn't changed
-since the last request and can be safely pulled from the browser cache.
+Conditional GETs are a feature of the HTTP specification that provide a way for web servers to tell browsers that the response to a GET request hasn't changed since the last request and can be safely pulled from the browser cache.
-They work by using the +HTTP_IF_NONE_MATCH+ and +HTTP_IF_MODIFIED_SINCE+ headers
-to pass back and forth both a unique content identifier and the timestamp of
-when the content was last changed. If the browser makes a request where the
-content identifier (etag) or last modified since timestamp matches the server’s
-version then the server only needs to send back an empty response with a not
-modified status.
+They work by using the +HTTP_IF_NONE_MATCH+ and +HTTP_IF_MODIFIED_SINCE+ headers to pass back and forth both a unique content identifier and the timestamp of when the content was last changed. If the browser makes a request where the content identifier (etag) or last modified since timestamp matches the server’s version then the server only needs to send back an empty response with a not modified status.
-It is the server's (i.e. our) responsibility to look for a last modified
-timestamp and the if-none-match header and determine whether or not to send
-back the full response. With conditional-get support in rails this is a pretty
-easy task:
+It is the server's (i.e. our) responsibility to look for a last modified timestamp and the if-none-match header and determine whether or not to send back the full response. With conditional-get support in Rails this is a pretty easy task:
<ruby>
class ProductsController < ApplicationController
@@ -469,9 +343,7 @@ class ProductsController < ApplicationController
end
</ruby>
-If you don't have any special response processing and are using the default
-rendering mechanism (i.e. you're not using respond_to or calling render
-yourself) then you’ve got an easy helper in fresh_when:
+If you don't have any special response processing and are using the default rendering mechanism (i.e. you're not using respond_to or calling render yourself) then you’ve got an easy helper in fresh_when:
<ruby>
class ProductsController < ApplicationController
@@ -481,18 +353,14 @@ class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
- fresh_when :last_modified => @product.published_at.utc, :etag => @article
+ fresh_when :last_modified => @product.published_at.utc, :etag => @product
end
end
</ruby>
h3. Advanced Caching
-Along with the built-in mechanisms outlined above, a number of excellent
-plugins exist to help with finer grained control over caching. These include
-Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's
-interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both
-of these plugins play nice with memcached and are a must-see for anyone
+Along with the built-in mechanisms outlined above, a number of excellent plugins exist to help with finer grained control over caching. These include Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both of these plugins play nice with memcached and are a must-see for anyone
seriously considering optimizing their caching needs.
Also the new "Cache money":http://github.com/nkallen/cache-money/tree/master plugin is supposed to be mad cool.
@@ -501,7 +369,7 @@ h3. References
* "Scaling Rails Screencasts":http://railslab.newrelic.com/scaling-rails
* "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/2/28/rails-caching-tutorial
-* "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2
+* "RailsEnvy, Rails Caching Tutorial, Part 2":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2
* "ActiveSupport::Cache documentation":http://api.rubyonrails.org/classes/ActiveSupport/Cache.html
* "Rails 2.1 integrated caching tutorial":http://thewebfellas.com/blog/2008/6/9/rails-2-1-now-with-better-integrated-caching
@@ -509,8 +377,10 @@ h3. References
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching
-April 1, 2009: Made a bunch of small fixes
-February 22, 2009: Beefed up the section on cache_stores
-December 27, 2008: Typo fixes
-November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
-September 15, 2008: Initial version by Aditya Chadha
+* May 02, 2009: Formatting cleanups
+* April 26, 2009: Clean up typos in submitted patch
+* April 1, 2009: Made a bunch of small fixes
+* February 22, 2009: Beefed up the section on cache_stores
+* December 27, 2008: Typo fixes
+* November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
+* September 15, 2008: Initial version by Aditya Chadha
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index d97ed56eaf..2711337d43 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -194,7 +194,7 @@ Active Model currently has a single configuration setting:
h3. Using Initializers
-After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code stored under +/config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded.
+After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded.
NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down.
diff --git a/railties/guides/source/contribute.textile b/railties/guides/source/contribute.textile
index 650004bd09..5087c2f385 100644
--- a/railties/guides/source/contribute.textile
+++ b/railties/guides/source/contribute.textile
@@ -40,7 +40,7 @@ For each completed guide, the lead contributor will receive all of the following
* $200 from Caboose Rails Documentation Project.
* 1 year of GitHub Micro account worth $84.
-* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x $10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product.
+* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x 10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product.
h3. Rules
diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
index 84778ed9ee..a5912643c0 100644
--- a/railties/guides/source/contributing_to_rails.textile
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -161,7 +161,7 @@ h4. Fork the Rails Source Code
Fork Rails. You’re not going to put your patches right into the master branch, OK? This is where you need that copy of Rails that you cloned earlier. Think of a name for your new branch and run
<shell>
-git checkout -b my_new_branch
+git checkout -b my_new_branch
</shell>
It doesn’t really matter what name you use, because this branch will only exist on your local computer.
@@ -175,6 +175,15 @@ Now get busy and add your code to Rails (or edit the existing code). You’re on
* Include tests that fail without your code, and pass with it
* Update the documentation
+h4. Follow the Coding Conventions
+
+Rails follows a simple set of coding style conventions.
+
+* Two spaces, no tabs
+* Prefer +&&+/+||+ over +and+/+or+
+* +MyClass.my_method(my_arg)+ not +my_method( my_arg )+ or +my_method my_arg+
+* Follow the conventions you see used in the source already
+
h4. Sanity Check
You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either.
diff --git a/railties/guides/source/credits.erb.textile b/railties/guides/source/credits.erb.textile
index b09a931fd6..49e390908f 100644
--- a/railties/guides/source/credits.erb.textile
+++ b/railties/guides/source/credits.erb.textile
@@ -8,15 +8,15 @@ p. We'd like to thank the following people for their tireless contributions to t
<h3 class="section">Rails Documentation Team</h3>
<% author('Mike Gunderloy', 'mgunderloy') do %>
- Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails activism team":http://rubyonrails.org/activists . He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much.
+ Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com. He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much.
<% end %>
<% author('Pratik Naik', 'lifo') do %>
Pratik Naik is a Ruby on Rails consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails core team":http://rubyonrails.org/core. He maintains a blog at "has_many :bugs, :through => :rails":http://m.onkey.org and has an active "twitter account":http://twitter.com/lifo.
<% end %>
-<% author('Xavier Noria', 'fxn', 'fxn.jpg') do %>
- Xavier Noria has been around dynamic languages since 2000. He fell in love with Rails in 2005, and cofounded Rails-based software company <a href="http://www.aspgems.com">ASPgems</a> in mid-2006. Xavier is president of the <a href="http://www.srug.org/">Spanish Ruby Users Group</a> and has been involved in Rails in several ways. He enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Oh, he also "tweets":http://twitter.com/fxn!
+<% author('Xavier Noria', 'fxn', 'fxn.png') do %>
+ Xavier has been into Rails since 2005, he is currently a Rails consultant. Xavier is president of the <a href="http://www.srug.org/">Spanish Ruby Users Group</a> and has been involved in Rails in several ways. He enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Oh, he also "tweets":http://twitter.com/fxn!
<% end %>
<h3 class="section">Rails Guides Designers</h3>
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index c059fdabf8..9c0f22724e 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -401,7 +401,7 @@ And then ask again for the instance_variables:
true
</shell>
-Now +@posts+ is a included in the instance variables, because the line defining it was executed.
+Now +@posts+ is included in the instance variables, because the line defining it was executed.
TIP: You can also step into *irb* mode with the command +irb+ (of course!). This way an irb session will be started within the context you invoked it. But be warned: this is an experimental feature.
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index 22d24b0903..ac0769e219 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -211,9 +211,7 @@ h4. Binding a Form to an Object
While this is an increase in comfort it is far from perfect. If Person has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object, which is exactly what +form_for+ does.
-Assume we have a controller for dealing with articles:
-
-articles_controller.rb:
+Assume we have a controller for dealing with articles +app/controllers/articles_controller.rb+:
<ruby>
def new
@@ -221,9 +219,7 @@ def new
end
</ruby>
-The corresponding view using +form_for+ looks like this
-
-articles/new.html.erb:
+The corresponding view +app/views/articles/new.html.erb+ using +form_for+ looks like this:
<erb>
<% form_for :article, @article, :url => { :action => "create" }, :html => {:class => "nifty_form"} do |f| %>
@@ -278,7 +274,13 @@ The object yielded by +fields_for+ is a form builder like the one yielded by +fo
h4. Relying on Record Identification
-The Article model is directly available to users of the application, so -- following the best practices for developing with Rails -- you should declare it *a resource*.
+The Article model is directly available to users of the application, so -- following the best practices for developing with Rails -- you should declare it *a resource*:
+
+<ruby>
+map.resources :articles
+</ruby>
+
+TIP: Declaring a resource has a number of side-affects. See "Rails Routing From the Outside In":routing.html#restful-routing-the-rails-default for more information on setting up and using resources.
When dealing with RESTful resources, calls to +form_for+ can get significantly easier if you rely on *record identification*. In short, you can just pass the model instance and have Rails figure out model name and the rest:
@@ -316,7 +318,7 @@ will create a form that submits to the articles controller inside the admin name
form_for [:admin, :management, @article]
</ruby>
-For more information on Rails' routing system and the associated conventions, please see the "routing guide":./routing_outside_in.html.
+For more information on Rails' routing system and the associated conventions, please see the "routing guide":routing.html.
h4. How do forms with PUT or DELETE methods work?
@@ -464,7 +466,7 @@ To leverage time zone support in Rails, you have to ask your users what time zon
There is also +time_zone_options_for_select+ helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.
-Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from rails).
+Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails).
h3. Using Date and Time Form Helpers
@@ -584,7 +586,7 @@ h4. Dealing with Ajax
Unlike other forms making an asynchronous file upload form is not as simple as replacing +form_for+ with +remote_form_for+. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission.
-h3. Customising Form Builders
+h3. Customizing Form Builders
As mentioned previously the object yielded by +form_for+ and +fields_for+ is an instance of FormBuilder (or a subclass thereof). Form builders encapsulate the notion of displaying form elements for a single object. While you can of course write helpers for your forms in the usual way you can also subclass FormBuilder and add the helpers there. For example
@@ -718,7 +720,7 @@ This will result in a +params+ hash that looks like
{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}}
</ruby>
-Rails knows that all these inputs should be part of the person hash because you called +fields_for+ on the first form builder. By specifying an +:index+ option you're telling rails that instead of naming the inputs +person[address][city]+ it should insert that index surrounded by [] between the address and the city. If you pass an Active Record object as we did then Rails will call +to_param+ on it, which by default returns the database id. This is often useful as it is then easy to locate which Address record should be modified. You can pass numbers with some other significance, strings or even +nil+ (which will result in an array parameter being created).
+Rails knows that all these inputs should be part of the person hash because you called +fields_for+ on the first form builder. By specifying an +:index+ option you're telling Rails that instead of naming the inputs +person[address][city]+ it should insert that index surrounded by [] between the address and the city. If you pass an Active Record object as we did then Rails will call +to_param+ on it, which by default returns the database id. This is often useful as it is then easy to locate which Address record should be modified. You can pass numbers with some other significance, strings or even +nil+ (which will result in an array parameter being created).
To create more intricate nestings, you can specify the first part of the input name (+person[address]+ in the previous example) explicitly, for example
@@ -736,7 +738,7 @@ will create inputs like
As a general rule the final input name is the concatenation of the name given to +fields_for+/+form_for+, the index value and the name of the attribute. You can also pass an +:index+ option directly to helpers such as +text_field+, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls.
-As a shortcut you can append [] to the name and omit the +:index+ option. This is the same as specifing +:index => address+ so
+As a shortcut you can append [] to the name and omit the +:index+ option. This is the same as specifying +:index => address+ so
<erb>
<% fields_for 'person[address][primary][]', address do |address_form| %>
@@ -750,7 +752,7 @@ h3. Building Complex Forms
Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include:
-* Ryan Bates' series of railscasts on "complex forms":http://railscasts.com/episodes/75
+* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75
* Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf
* Eloy Duran's "nested_params":http://github.com/alloy/complex-form-examples/tree/alloy-nested_params plugin
* Lance Ivy's "nested_assignment":http://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":http://github.com/cainlevy/complex-form-examples/tree/cainlevy
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 7c029762a3..5c05648f12 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -9,7 +9,7 @@ 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. Some of the code shown here will not work in older versions of Rails.
+WARNING. This Guide is based on Rails 2.3.3. Some of the code shown here will not work in other versions of Rails.
h3. This Guide Assumes
@@ -19,22 +19,22 @@ This guide is designed for beginners who want to get started with a Rails applic
* 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
-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:
+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:
-* "Mr. Neighborly’s Humble Little Ruby Book":http://www.humblelittlerubybook.com
+* "Mr. Neighborly's Humble Little Ruby Book":http://www.humblelittlerubybook.com
* "Programming Ruby":http://www.rubycentral.com/book
-* "Why’s (Poignant) Guide to Ruby":http://poignantguide.net/ruby/
+* "Why's (Poignant) Guide to Ruby":http://poignantguide.net/ruby/
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 several 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 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 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. 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.
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.
+* 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.
* 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
@@ -47,7 +47,7 @@ Rails is organized around the Model, View, Controller architecture, usually just
h5. Models
-A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application’s business logic will be concentrated in the models.
+A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application's business logic will be concentrated in the models.
h5. Views
@@ -87,7 +87,7 @@ Action Mailer is a framework for building e-mail services. You can use Action Ma
h5. Active Resource
-Active Resource provides a framework for managing the connection between business objects an RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.
+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. Railties
@@ -99,7 +99,7 @@ Active Support is an extensive collection of utility classes and standard Ruby l
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:
+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:
* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
* Transferring representations of the state of that resource between system components.
@@ -110,7 +110,7 @@ For example, to a Rails application a request such as this:
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.
-If you’d like more details on REST as an architectural style, these resources are more approachable than Fielding’s thesis:
+If you'd like more details on REST as an architectural style, these resources are more approachable than Fielding's thesis:
* "A Brief Introduction to REST":http://www.infoq.com/articles/rest-introduction by Stefan Tilkov
* "An Introduction to REST":http://bitworking.org/news/373/An-Introduction-to-REST (video tutorial) by Joe Gregorio
@@ -119,7 +119,7 @@ If you’d like more details on REST as an architectural style, these resources
h3. Creating a New Rails Project
-If you follow this guide, you’ll create a Rails project called <tt>blog</tt>, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed.
+If you follow this guide, you'll create a Rails project called <tt>blog</tt>, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed.
h4. Installing Rails
@@ -131,8 +131,10 @@ $ gem install rails
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.
+* If you're working on Windows, you may find it easier to install Instant Rails. Be aware, though, that "Instant Rails":http://instantrails.rubyforge.org/wiki/wiki.pl releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows.
+* If you want to keep up with cutting-edge changes to Rails, you'll want to clone the "Rails source code":http://github.com/rails/rails/tree/master from github. This is not recommended as an option for beginners, though.
+
+WARNING. As of mid-2009, cloning the master branch will get you preliminary Rails 3.0 code. To follow along with this guide, you should clone the 2-3-stable branch instead.
h4. Creating the Blog Application
@@ -148,13 +150,13 @@ This will create a Rails application that uses a SQLite database for data storag
$ rails blog -d mysql
</shell>
-And if you’re using PostgreSQL for data storage, run this command:
+And if you're using PostgreSQL for data storage, run this command:
<shell>
$ rails blog -d postgresql
</shell>
-TIP. You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>.
+TIP: You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>.
After you create the blog application, switch to its folder to continue work directly in that application:
@@ -162,7 +164,7 @@ After you create the blog application, switch to its folder to continue work dir
$ cd blog
</shell>
-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:
+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.|
@@ -259,7 +261,7 @@ One of the traditional places to start with a new language is by getting some te
$ script/generate controller home index
</shell>
-TIP. If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+.
+TIP: If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+.
Rails will create several files for you, including +app/views/home/index.html.erb+. This is the template that will be used to display the results of the +index+ action (method) in the +home+ controller. Open this file in your text editor and edit it to contain a single line of code:
@@ -279,7 +281,7 @@ This will fire up an instance of the Mongrel web server by default (Rails can al
!images/rails_welcome.png(Welcome Aboard screenshot)!
-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.
+TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.
The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to +http://localhost:3000/home/index+.
@@ -383,7 +385,7 @@ NOTE: Because you're working in the development environment by default, this com
h4. Adding a Link
-To hook the posts up to the home page you've already created, you can add a link to the home page. Open +/app/views/home/index.html.erb+ and modify it as follows:
+To hook the posts up to the home page you've already created, you can add a link to the home page. Open +app/views/home/index.html.erb+ and modify it as follows:
<code lang="ruby">
<h1>Hello, Rails!</h1>
@@ -400,7 +402,7 @@ Now you're ready to start working with posts. To do that, navigate to +http://lo
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.
+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.
Congratulations, you're riding the rails! Now it's time to see how it all works.
@@ -461,7 +463,7 @@ The easiest place to start looking at functionality is with the code that lists
<ruby>
def index
- @posts = Post.find(:all)
+ @posts = Post.all
respond_to do |format|
format.html # index.html.erb
@@ -486,7 +488,7 @@ The +respond_to+ block handles both HTML and XML calls to this action. If you br
<th>Content</th>
</tr>
-<% for post in @posts %>
+<% @posts.each do |post| %>
<tr>
<td><%=h post.name %></td>
<td><%=h post.title %></td>
@@ -508,9 +510,9 @@ This view iterates over the contents of the +@posts+ array to display content an
* +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.
+* +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.
-TIP. For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html.
+TIP: For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html.
h4. Customizing the Layout
@@ -582,7 +584,7 @@ The +new.html.erb+ view displays this empty Post to the user:
<%= link_to 'Back', posts_path %>
</erb>
-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 your 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 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.
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.
@@ -609,7 +611,7 @@ end
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.
-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."
+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."
h4. Showing an Individual Post
@@ -710,7 +712,7 @@ 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+.
+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
@@ -732,7 +734,7 @@ The +destroy+ method of an Active Record model instance removes the correspondin
h3. DRYing up the Code
-At this point, it’s worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views and _filters_ to help with duplication in controllers.
+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
@@ -740,13 +742,13 @@ As you saw earlier, the scaffold-generated views for the +new+ and +edit+ action
<tt>new.html.erb</tt>:
-<html>
+<erb>
<h1>New post</h1>
<%= render :partial => "form" %>
<%= link_to 'Back', posts_path %>
-</html>
+</erb>
<tt>edit.html.erb</tt>:
@@ -785,11 +787,11 @@ As you saw earlier, the scaffold-generated views for the +new+ and +edit+ action
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 Rending in Rails":layouts_and_rendering.html guide.
+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:
+At this point, if you look at the controller for posts, you'll see some duplication:
<ruby>
class PostsController < ApplicationController
@@ -815,7 +817,7 @@ class PostsController < ApplicationController
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+:
+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
@@ -837,10 +839,10 @@ class PostsController < ApplicationController
# ...
end
- private
- def find_post
- @post = Post.find(params[:id])
- end
+private
+ def find_post
+ @post = Post.find(params[:id])
+ end
end
</ruby>
@@ -944,7 +946,7 @@ map.resources :posts, :has_many => :comments
This creates +comments+ as a _nested resource_ within +posts+. This is another part of capturing the hierarchical relationship that exists between posts and comments.
-TIP: For more information on routing, see the "Rails Routing from the Outside In":routing_outside_in.html guide.
+TIP: For more information on routing, see the "Rails Routing from the Outside In":routing.html guide.
h4. Generating a Controller
@@ -954,7 +956,7 @@ With the model in hand, you can turn your attention to creating a matching contr
$ script/generate controller Comments index show new edit
</shell>
-This creates seven files:
+This creates eight files:
* +app/controllers/comments_controller.rb+ - The controller
* +app/helpers/comments_helper.rb+ - A view helper file
@@ -963,6 +965,7 @@ This creates seven files:
* +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 +script/generate controller+:
@@ -987,23 +990,21 @@ You'll need to flesh this out with code to actually process requests appropriate
<ruby>
class CommentsController < ApplicationController
+ before_filter :find_post
+
def index
- @post = Post.find(params[:post_id])
@comments = @post.comments
end
def show
- @post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
end
def new
- @post = Post.find(params[:post_id])
@comment = @post.comments.build
end
def create
- @post = Post.find(params[:post_id])
@comment = @post.comments.build(params[:comment])
if @comment.save
redirect_to post_comment_url(@post, @comment)
@@ -1013,12 +1014,10 @@ class CommentsController < ApplicationController
end
def edit
- @post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
end
def update
- @post = Post.find(params[:post_id])
@comment = Comment.find(params[:id])
if @comment.update_attributes(params[:comment])
redirect_to post_comment_url(@post, @comment)
@@ -1028,14 +1027,14 @@ class CommentsController < ApplicationController
end
def destroy
- @post = Post.find(params[:post_id])
@comment = Comment.find(params[:id])
@comment.destroy
+ redirect_to post_comments_path(@post)
+ end
- respond_to do |format|
- format.html { redirect_to post_comments_path(@post) }
- format.xml { head :ok }
- end
+private
+ def find_post
+ @post = Post.find(params[:post_id])
end
end
@@ -1198,6 +1197,18 @@ As a next step, I'll modify the +views/posts/show.html.erb+ view to show the com
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+.
+h4. Deleting Associated Objects
+
+If you decide at some point to delete a post, you likely want to delete the comments associated with that post as well. You can do so by taking advantage of the association option +dependent+. All you need to do is modify the +post.rb+ as follows:
+
+<ruby>
+class Post < ActiveRecord::Base
+ validates_presence_of :name, :title
+ validates_length_of :title, :minimum => 5
+ has_many :comments, :dependent => :destroy
+end
+</ruby>
+
h3. Building a Multi-Model Form
Comments and posts are edited on two separate forms - which makes sense, given the flow of this mini-application. But what if you want to edit more than one thing on a single form? Rails 2.3 offers new support for nested forms. Let's add support for giving each post multiple tags, right in the form where you create the post. First, create a new model to hold the tags:
@@ -1212,7 +1223,7 @@ Run the migration to create the database table:
$ rake db:migrate
</shell>
-Next, edit the +post.rb+ file to create the other side of the association, and to tell Rails that you intend to edit tags via posts:
+Next, edit the +post.rb+ file to create the other side of the association, and to tell Rails (via the +accepts_nested_attributes+ macro) that you intend to edit tags via posts:
<ruby>
class Post < ActiveRecord::Base
@@ -1221,7 +1232,7 @@ class Post < ActiveRecord::Base
has_many :comments
has_many :tags
- accepts_nested_attributes_for :tags, :allow_destroy => :true ,
+ accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end
</ruby>
@@ -1240,7 +1251,7 @@ You'll also need to modify +views/posts/_form.html.erb+ to include the tags:
<%= post_form.text_field :name %>
</p>
<p>
- <%= post_form.label :title, "title" %><br />
+ <%= post_form.label :title, "Title" %><br />
<%= post_form.text_field :title %>
</p>
<p>
@@ -1277,17 +1288,19 @@ Now that you've seen your first Rails application, you should feel free to updat
* The "Ruby On Rails guides":http://guides.rubyonrails.org
* The "Ruby on Rails mailing list":http://groups.google.com/group/rubyonrails-talk
-* The #rubyonrails channel on irc.freenode.net
+* The "#rubyonrails":irc://irc.freenode.net/#rubyonrails channel on irc.freenode.net
+* The "Rails Wiki":http://wiki.rubyonrails.org/
Rails also comes with built-in help that you can generate using the rake command-line utility:
-* 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.
+* 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
+* 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
* November 1, 2008: First approved version by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index 103ccb1c7a..b588656821 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -97,7 +97,7 @@ The *translations load path* (+I18n.load_path+) is just a Ruby Array of paths to
NOTE: The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced.
-The default +environment.rb+ files has instruction how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
+The default +environment.rb+ files has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
<ruby>
# The internationalization framework can be changed
@@ -114,7 +114,7 @@ For the sake of completeness, let's mention that if you do not want to use the +
To tell the I18n library where it can find your custom translation files you can specify the load path anywhere in your application - just make sure it gets run before any translations are actually looked up. You might also want to change the default locale. The simplest thing possible is to put the following into an initializer:
<ruby>
-# in config/initializer/locale.rb
+# in config/initializers/locale.rb
# tell the I18n library where to find your translations
I18n.load_path << Dir[ File.join(RAILS_ROOT, 'lib', 'locale',
@@ -142,61 +142,28 @@ def set_locale
end
</ruby>
-This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is, for example, Google's approach.) So +http://localhost:3000?locale=pt+ will load the Portugese localization, whereas +http://localhost:3000?locale=de+ would load the German localization, and so on. You may skip the next section and head over to the *Internationalize your application* section, if you want to try things out by manually placing the locale in the URL and reloading the page.
+This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is, for example, Google's approach.) So +http://localhost:3000?locale=pt+ will load the Portuguese localization, whereas +http://localhost:3000?locale=de+ would load the German localization, and so on. You may skip the next section and head over to the *Internationalize your application* section, if you want to try things out by manually placing the locale in the URL and reloading the page.
Of course, you probably don't want to manually include the locale in every URL all over your application, or want the URLs look differently, e.g. the usual +http://example.com/pt/books+ versus +http://example.com/en/books+. Let's discuss the different options you have.
-IMPORTANT: The following examples rely on having available locales loaded into your application as an array of strings like +["en", "es", "gr"]+. This is not included in the current version of Rails 2.2 -- the forthcoming Rails version 2.3 will contain the easy accessor +available_locales+. (See "this commit":http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4 and background at "Rails I18n Wiki":http://rails-i18n.org/wiki/pages/i18n-available_locales.)
-
-So, for having available locales easily accessible in Rails 2.2, we have to include this support manually in an initializer, like this:
-
-<ruby>
-# config/initializers/available_locales.rb
-#
-# Get loaded locales conveniently
-# See http://rails-i18n.org/wiki/pages/i18n-available_locales
-module I18n
- class << self
- def available_locales; backend.available_locales; end
- end
- module Backend
- class Simple
- def available_locales; translations.keys.collect { |l| l.to_s }.sort; end
- end
- end
-end
-
-# You need to "force-initialize" loaded locales
-I18n.backend.send(:init_translations)
-
-AVAILABLE_LOCALES = I18n.backend.available_locales
-RAILS_DEFAULT_LOGGER.debug "* Loaded locales: #{AVAILABLE_LOCALES.inspect}"
-</ruby>
-
-You can then wrap the constant for easy access in ApplicationController:
-
-<ruby>
-class ApplicationController < ActionController::Base
- def available_locales; AVAILABLE_LOCALES; end
-end
-</ruby>
-
h4. Setting the Locale from the Domain Name
One option you have is to set the locale from the domain name where your application runs. For example, we want +www.example.com+ to load the English (or default) locale, and +www.example.es+ to load the Spanish locale. Thus the _top-level domain name_ is used for locale setting. This has several advantages:
-* The locale is an _obvious_ part of the URL
-* People intuitively grasp in which language the content will be displayed
-* It is very trivial to implement in Rails
-* Search engines seem to like that content in different languages lives at different, inter-linked domains
+* The locale is an _obvious_ part of the URL.
+* People intuitively grasp in which language the content will be displayed.
+* It is very trivial to implement in Rails.
+* Search engines seem to like that content in different languages lives at different, inter-linked domains.
-You can implement it like this in your ApplicationController:
+You can implement it like this in your +ApplicationController+:
<ruby>
before_filter :set_locale
+
def set_locale
I18n.locale = extract_locale_from_uri
end
+
# Get locale from top-level domain or return nil if such locale is not available
# You have to put something like:
# 127.0.0.1 application.com
@@ -205,7 +172,7 @@ end
# in your /etc/hosts file to try this out locally
def extract_locale_from_tld
parsed_locale = request.host.split('.').last
- (available_locales.include? parsed_locale) ? parsed_locale : nil
+ I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
end
</ruby>
@@ -218,7 +185,7 @@ We can also set the locale from the _subdomain_ in a very similar way:
# in your /etc/hosts file to try this out locally
def extract_locale_from_subdomain
parsed_locale = request.subdomains.first
- (available_locales.include? parsed_locale) ? parsed_locale : nil
+ I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
end
</ruby>
@@ -276,11 +243,11 @@ map.dashboard '/:locale', :controller => "dashboard"
Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +map.root+ declaration.)
-IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitely, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
+IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
h4. Setting the Locale from the Client Supplied Information
-In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' prefered language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites -- see the box about _sessions_, _cookies_ and RESTful architecture above.
+In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' preferred language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites -- see the box about _sessions_, _cookies_ and RESTful architecture above.
h5. Using +Accept-Language+
@@ -305,7 +272,7 @@ Of course, in a production environment you would need much more robust code, and
h5. Using GeoIP (or Similar) Database
-Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query the database for the user's IP, and look up your prefered locale for the country/region/city returned.
+Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query the database for the user's IP, and look up your preferred locale for the country/region/city returned.
h5. User Profile
@@ -315,7 +282,7 @@ h3. Internationalizing your Application
OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. With that in place, you're now ready for the really interesting stuff.
-Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide neccessary translations for these abstracts.
+Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide necessary translations for these abstracts.
You most probably have something like this in one of your applications:
@@ -365,12 +332,12 @@ NOTE: Rails adds a +t+ (+translate+) helper method to your views so that you do
So let's add the missing translations into the dictionary files (i.e. do the "localization" part):
<ruby>
-# config/locale/en.yml
+# config/locales/en.yml
en:
hello_world: Hello World
hello_flash: Hello Flash
-# config/locale/pirate.yml
+# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash
@@ -378,7 +345,7 @@ pirate:
There you go. Because you haven't changed the default_locale, I18n will use English. Your application now shows:
-!images/i18n/demo_translated_en.png(rails i18n demo translated to english)!
+!images/i18n/demo_translated_en.png(rails i18n demo translated to English)!
And when you change the URL to pass the pirate locale (+http://localhost:3000?locale=pirate+), you'll get:
@@ -386,7 +353,7 @@ And when you change the URL to pass the pirate locale (+http://localhost:3000?lo
NOTE: You need to restart the server when you add new locale files.
-You may use YAML (+.yml+) or plain Ruby (+.rb+) files for storing your translations in SimpleStore. YAML is the prefered option among Rails developers. However, it has one big disadvantage. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.)
+You may use YAML (+.yml+) or plain Ruby (+.rb+) files for storing your translations in SimpleStore. YAML is the preferred option among Rails developers. However, it has one big disadvantage. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.)
h4. Adding Date/Time Formats
@@ -402,7 +369,7 @@ OK! Now let's add a timestamp to the view, so we can demo the *date/time localiz
And in our pirate translations file let's add a time format (it's already there in Rails' defaults for English):
<ruby>
-# config/locale/pirate.yml
+# config/locales/pirate.yml
pirate:
time:
formats:
@@ -413,7 +380,7 @@ So that would give you:
!images/i18n/demo_localized_pirate.png(rails i18n demo localized time to pirate)!
-TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails's defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locale/+ directory, they will automatically be ready for use.
+TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails' defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locales/+ directory, they will automatically be ready for use.
h4. Localized Views
@@ -425,7 +392,7 @@ h4. Organization of Locale Files
When you are using the default SimpleStore shipped with the i18n library, dictionaries are stored in plain-text files on the disc. Putting translations for all parts of your application in one file per locale could be hard to manage. You can store these files in a hierarchy which makes sense to you.
-For example, your +config/locale+ directory could look like this:
+For example, your +config/locales+ directory could look like this:
<pre>
|-defaults
@@ -463,7 +430,7 @@ Do check the "Rails i18n Wiki":http://rails-i18n.org/wiki for list of tools avai
h3. Overview of the I18n API Features
-You should have good understanding of using the i18n library now, knowing all neccessary aspects of internationalizing a basic Rails application. In the following chapters, we'll cover it's features in more depth.
+You should have good understanding of using the i18n library now, knowing all necessary aspects of internationalizing a basic Rails application. In the following chapters, we'll cover it's features in more depth.
Covered are features like these:
@@ -686,7 +653,7 @@ en:
# will translate User attribute "login" as "Handle"
</ruby>
-Then +User.human_name+ will return "Dude" and +User.human_attribute_name(:login)+ will return "Handle".
+Then +User.human_name+ will return "Dude" and +User.human_attribute_name("login")+ will return "Handle".
h5. Error Message Scopes
diff --git a/railties/guides/source/index.erb.textile b/railties/guides/source/index.erb.textile
index 4c8dd65a04..851d91da2f 100644
--- a/railties/guides/source/index.erb.textile
+++ b/railties/guides/source/index.erb.textile
@@ -1,5 +1,5 @@
<% content_for :header_section do %>
-h2. Ruby on Rails guides
+h2. Ruby on Rails Guides
These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together. There are two different versions of the Guides site, and you should be sure to use the one that applies to your situation:
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 809d2b2172..0cee413ac3 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -204,7 +204,7 @@ TIP: You don't need to call +to_xml+ on the object that you want to render. If y
h5. Rendering Vanilla JavaScript
-Rails can render vanilla JavaScript (as an alternative to using +update+ with n +.rjs+ file):
+Rails can render vanilla JavaScript (as an alternative to using +update+ with an +.rjs+ file):
<ruby>
render :js => "alert('Hello Rails');"
@@ -266,7 +266,7 @@ render :xml => photo, :location => photo_url(photo)
h5. Finding Layouts
-To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +/app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +/app/views/layouts/application.html.erb+ ot +/app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.
+To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +app/views/layouts/application.html.erb+ or +app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.
h6. Specifying Layouts on a per-Controller Basis
@@ -418,15 +418,15 @@ def show
end
</ruby>
-Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. So this code will work with problems:
+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>
- def show
- @book = Book.find(params[:id])
- if @book.special?
- render :action => "special_show"
- end
+def show
+ @book = Book.find(params[:id])
+ if @book.special?
+ render :action => "special_show"
end
+end
</ruby>
This will render a book with +special?+ set with the +special_show+ template, while other books will render with the default +show+ template.
@@ -447,7 +447,7 @@ redirect_to :back
h5. Getting a Different Redirect Status Code
-Rails uses HTTP status code 302 (permanent redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, temporary redirect), you can do so by using the +:status+ option:
+Rails uses HTTP status code 302 (temporary redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, permanent redirect), you can do so by using the +:status+ option:
<ruby>
redirect_to photos_path, :status => 301
@@ -519,10 +519,10 @@ 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:
-* auto_discovery_link_tag
-* javascript_include_tag
-* stylesheet_link_tag
-* image_tag
+* +auto_discovery_link_tag+
+* +javascript_include_tag+
+* +stylesheet_link_tag+
+* +image_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.
@@ -622,16 +622,16 @@ To include +public/stylesheets/main.css+ and +public/photos/columns.css+:
<%= stylesheet_link_tag "main", "/photos/columns" %>
</erb>
-To include +http://example.com/main.cs+:
+To include +http://example.com/main.css+:
<erb>
-<%= stylesheet_link_tag "http://example.com/main.cs" %>
+<%= stylesheet_link_tag "http://example.com/main.css" %>
</erb>
-By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (:media, :rel, or :type):
+By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (+:media+, +:rel+, or +:type+):
<erb>
-<%= stylesheet_link_tag "main_print", media => "print" %>
+<%= stylesheet_link_tag "main_print", :media => "print" %>
</erb>
The +all+ option links every CSS file in +public/stylesheets+:
@@ -958,13 +958,13 @@ On pages generated by +NewsController+, you want to hide the top menu and add a
<% content_for :content do %>
<div id="right_menu">Right menu items here</div>
<%= yield(:news_content) or yield %>
- <% end -%>
+<% end -%>
<%= render :file => 'layouts/application' %>
</erb>
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 ommit 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 one is sure she will not subtemplate the +News+ layout, she can omit the +yield(:news_content) or + part.
h3. Changelog
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile
index 5ed94c30b7..5f49ac41f5 100644
--- a/railties/guides/source/migrations.textile
+++ b/railties/guides/source/migrations.textile
@@ -1,10 +1,10 @@
h2. Migrations
-Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy.
+Migrations are a convenient way for you to alter your database in a structured and organized manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy.
Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database.
-Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more that you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
+Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more than you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
You'll learn all about migrations including:
@@ -78,7 +78,7 @@ Active Record provides methods that perform common data definition tasks in a da
If you need to perform tasks specific to your database (for example create a "foreign key":#active-record-and-referential-integrity constraint) then the +execute+ function allows you to execute arbitrary SQL. A migration is just a regular Ruby class so you're not limited to these functions. For example after adding a column you could write code to set the value of that column for existing records (if necessary using your models).
-On databases that support transactions with statements that change the schema (such as PostgreSQL), migrations are wrapped in a transaction. If the database does not support this (for example MySQL and SQLite) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.
+On databases that support transactions with statements that change the schema (such as PostgreSQL or SQLite3), migrations are wrapped in a transaction. If the database does not support this (for example MySQL) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.
h4. What's in a Name
@@ -508,7 +508,7 @@ The migration has its own minimal copy of the +Product+ model and no longer care
h4. Dealing with Changing Models
-For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try and use the old column information. You can force Active Record to re-read the column information with the +reset_column_information+ method, for example
+For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try to use the old column information. You can force Active Record to re-read the column information with the +reset_column_information+ method, for example
<ruby>
class AddPartNumberToProducts < ActiveRecord::Migration
@@ -538,7 +538,7 @@ There is no need (and it is error prone) to deploy a new instance of an app by r
For example, this is how the test database is created: the current development database is dumped (either to +db/schema.rb+ or +db/development.sql+) and then loaded into the test database.
-Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest.
+Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarizing the schema, may also be of interest.
h4. Types of Schema Dumps
@@ -582,7 +582,7 @@ The Active Record way claims that intelligence belongs in your models, not in th
Validations such as +validates_uniqueness_of+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.
-Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "redhillonrails":http://agilewebdevelopment.com/plugins/search?search=redhillonrails which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
+Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":http://github.com/harukizaemon/foreign_key_migrations/tree/master which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
h3. Changelog
diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile
index 320a5b8472..f0dc9acbb8 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -293,7 +293,7 @@ WARNING: Performance test configurability is not yet enabled in Rails. But it wi
h4. Performance Test Environment
-Performance tests are run in the +development+ environment. But running performance tests will set the following configuration parameters:
+Performance tests are run in the +test+ environment. But running performance tests will set the following configuration parameters:
<shell>
ActionController::Base.perform_caching = true
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index 55ecdcd3d1..a5d39c3d09 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -601,7 +601,7 @@ This is just a simple test to make sure the class is being loaded correctly. Af
end
</ruby>
-Adding directories to the load path makes them appear just like files in the the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser. Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server. This is particularly useful as you develop the plugin.
+Adding directories to the load path makes them appear just like files in the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser. Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server. This is particularly useful as you develop the plugin.
* *vendor/plugins/yaffle/lib/app/models/woodpecker.rb:*
diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile
index 49cd5bf5f5..fc178fa44b 100644
--- a/railties/guides/source/rails_application_templates.textile
+++ b/railties/guides/source/rails_application_templates.textile
@@ -1,18 +1,238 @@
h2. Rails Application Templates
-This guide covers the Rails application templates, By referring to this guide, you will be able to:
+Application templates are simple ruby files containing DSL for adding plugins/gems/initializers etc. to your freshly created Rails project or an existing Rails project.
+
+By referring to this guide, you will be able to:
-* Use existing templates to generate a customized Rails application
-* Write your own reusable Rails application templates
+* Use templates to generate/customize Rails applications
+* Write your own reusable application templates using the Rails template API
endprologue.
-h3. Introduction
+h3. Usage
-Application templates are simple ruby files containing DSL for adding plugins/gems/initializers etc. to your freshly created Rails project or an existing Rails project.
+To apply a template, you need to provide the Rails generator with the location of the template you wish to apply, using -m option :
+
+<shell>
+$ rails blog -m ~/template.rb
+</shell>
+
+It's also possible to apply a template using a URL :
+
+<shell>
+$ rails blog -m http://gist.github.com/31208.txt
+</shell>
+
+Alternatively, you can use the rake task +rails:template+ to apply a template to an existing Rails application :
+
+<shell>
+$ rake rails:template LOCATION=~/template.rb
+</shell>
+
+h3. Template API
+
+Rails templates API is very self explanatory and easy to understand. Here's an example of a typical Rails template :
+
+<ruby>
+# template.rb
+run "rm public/index.html"
+generate(:scaffold, "person name:string")
+route "map.root :controller => 'people'"
+rake("db:migrate")
+
+git :init
+git :add => "."
+git :commit => "-a -m 'Initial commit'"
+</ruby>
+
+The following sections outlines the primary methods provided by the API :
+
+h4. gem(name, options = {})
+
+Adds a +config.gem+ entry for the supplied gem to the generated application’s +config/environment.rb+.
+
+For example, if your application depends on the gems +bj+ and +hpricot+ :
+
+<ruby>
+gem "bj"
+gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
+</ruby>
+
+Please note that this will NOT install the gems for you. So you may want to run the +rake gems:install+ task too :
+
+<ruby>
+rake "gems:install"
+</ruby>
+
+And let Rails take care of installing the required gems if they’re not already installed.
+
+h4. plugin(name, options = {})
+
+Installs a plugin to the generated application.
+
+Plugin can be installed from Git :
+
+<ruby>
+plugin 'authentication', :git => 'git://github.com/foor/bar.git'
+</ruby>
+
+You can even install plugins as git submodules :
+
+<ruby>
+plugin 'authentication', :git => 'git://github.com/foor/bar.git',
+ :submodule => true
+</ruby>
+
+Please note that you need to +git :init+ before you can install a plugin as a submodule.
+
+Or use plain old SVN :
+
+<ruby>
+plugin 'wtfsvn', :svn => 'svn://crap.com/wtf/trunk'
+</ruby>
+
+h4. vendor/lib/file/initializer(filename, data = nil, &block)
+
+Adds an initializer to the generated application’s +config/initializers+ directory.
+
+Lets say you like using +Object#not_nil?+ and +Object#not_blank?+ :
+
+<ruby>
+initializer 'bloatlol.rb', <<-CODE
+class Object
+ def not_nil?
+ !nil?
+ end
+
+ def not_blank?
+ !blank?
+ end
+end
+CODE
+</ruby>
+
+Similarly +lib()+ creates a file in the +lib/+ directory and +vendor()+ creates a file in the +vendor/+ directory.
+
+There is even +file()+, which accepts a relative path from +RAILS_ROOT+ and creates all the directories/file needed :
+
+<ruby>
+file 'app/components/foo.rb', <<-CODE
+class Foo
+end
+CODE
+</ruby>
+
+That’ll create +app/components+ directory and put +foo.rb+ in there.
+
+h4. rakefile(filename, data = nil, &block)
+
+Creates a new rake file under +lib/tasks+ with the supplied tasks :
+
+<ruby>
+rakefile("bootstrap.rake") do
+ <<-TASK
+ namespace :boot do
+ task :strap do
+ puts "i like boots!"
+ end
+ end
+ TASK
+end
+</ruby>
+
+The above creates +lib/tasks/bootstrap.rake+ with a +boot:strap+ rake task.
+
+h4. generate(what, args)
+
+Runs the supplied rails generator with given arguments. For example, I love to scaffold some whenever I’m playing with Rails :
+
+<ruby>
+generate(:scaffold, "person", "name:string", "address:text", "age:number")
+</ruby>
+
+h4. run(command)
+
+Executes an arbitrary command. Just like the backticks. Let's say you want to remove the +public/index.html+ file :
+
+<ruby>
+run "rm public/index.html"
+</ruby>
+
+h4. rake(command, options = {})
+
+Runs the supplied rake tasks in the Rails application. Let's say you want to migrate the database :
+
+<ruby>
+rake "db:migrate"
+</ruby>
+
+You can also run rake tasks with a different Rails environment :
+
+<ruby>
+rake "db:migrate", :env => 'production'
+</ruby>
+
+Or even use sudo :
+
+<ruby>
+rake "gems:install", :sudo => true
+</ruby>
+
+h4. route(routing_code)
+
+This adds a routing entry to the +config/routes.rb+ file. In above steps, we generated a person scaffold and also removed +public/index.html+. Now to make +PeopleController#index+ as the default page for the application :
+
+<ruby>
+route "map.root :controller => :person"
+</ruby>
+
+h4. inside(dir)
+
+I have my edge rails lying at +~/commit-rails/rails+. So every time i have to manually symlink edge from my new app. But now :
+
+<ruby>
+inside('vendor') do
+ run "ln -s ~/commit-rails/rails rails"
+end
+</ruby>
+
+So +inside()+ runs the command from the given directory.
+
+h4. ask(question)
+
++ask()+ gives you a chance to get some feedback from the user and use it in your templates. Lets say you want your user to name the new shiny library you’re adding :
+
+<ruby>
+lib_name = ask("What do you want to call the shiny library ?")
+lib_name << ".rb" unless lib_name.index(".rb")
+
+lib lib_name, <<-CODE
+class Shiny
+end
+CODE
+</ruby>
+
+h4. yes?(question) or no?(question)
+
+These methods let you ask questions from templates and decide the flow based on the user’s answer. Lets say you want to freeze rails only if the user want to :
+
+<ruby>
+rake("rails:freeze:gems") if yes?("Freeze rails gems ?")
+no?(question) acts just the opposite.
+</ruby>
+
+h4. git(:must => "-a love")
+
+Rails templates let you run any git command :
+
+<ruby>
+git :init
+git :add => "."
+git :commit => "-a -m 'Initial commit'"
+</ruby>
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/78
-* April 17, 2009: Initial version by "Pratik":credits.html#lifo
+* April 29, 2009: Initial version by "Pratik":credits.html#lifo
diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile
index 1164ed821d..545aaa18e0 100644
--- a/railties/guides/source/rails_on_rack.textile
+++ b/railties/guides/source/rails_on_rack.textile
@@ -48,7 +48,7 @@ app = Rack::Builder.new {
}.to_app
</ruby>
-Middlewares used in the code above are primarily useful only in the development envrionment. The following table explains their usage:
+Middlewares used in the code above are primarily useful only in the development environment. The following table explains their usage:
|_.Middleware|_.Purpose|
|+Rails::Rack::LogTailer+|Appends log file output to console|
@@ -229,7 +229,7 @@ h3. Rails Metal Applications
Rails Metal applications are minimal Rack applications specially designed for integrating with a typical Rails application. As Rails Metal Applications skip all of the Action Controller stack, serving a request has no overhead from the Rails framework itself. This is especially useful for infrequent cases where the performance of the full stack Rails framework is an issue.
-Ryan Bates' "railscast on Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal.
+Ryan Bates' "Railscast on Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal.
h4. Generating a Metal Application
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index e9adb4b308..355f385d49 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -236,7 +236,7 @@ will recognize incoming URLs containing +photo+ but route the requests to the Im
|GET |/photos/new |Images |new |return an HTML form for creating a new image|
|POST |/photos |Images |create |create a new image|
|GET |/photos/1 |Images |show |display a specific image|
-|GET |/photos/1/edit |Images |edit |return an HTML form for editing a image|
+|GET |/photos/1/edit |Images |edit |return an HTML form for editing an image|
|PUT |/photos/1 |Images |update |update a specific image|
|DELETE |/photos/1 |Images |destroy |delete a specific image|
@@ -416,7 +416,7 @@ map.resources :magazines do |magazine|
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/>+map.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:
@@ -614,7 +614,7 @@ To add a new route (one that creates a new resource), use the +:new+ option:
map.resources :photos, :new => { :upload => :post }
</ruby>
-This will enable Rails to recognize URLs such as +/photos/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create the +upload_photos_path+ and +upload_photos_url+ route helpers.
+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.
@@ -695,7 +695,7 @@ Regular routes need not use the +connect+ method. You can use any other name her
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
</ruby>
-This will do two things. First, requests to +/logout+ will be sent to the +destroy+ method of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code.
+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.
h4. Route Requirements
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index 1b64cc1be7..c26bea5519 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -392,7 +392,7 @@ params[:user] #=> {:name => “ow3ned”, :admin => true}
So if you create a new user using mass-assignment, it may be too easy to become an administrator.
-Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in rails 2.3. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example:
+Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in Rails 2.3. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example:
<ruby>
class Person < ActiveRecord::Base
@@ -497,7 +497,7 @@ Depending on your web application, there may be more ways to hijack the user's a
h4. CAPTCHAs
--- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not to ask a user to proof that he is human, but reveal that a robot is a robot._
+-- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not for a user to prove that he is human, but reveal that a robot is a robot._
But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is "reCAPTCHA":http://recaptcha.net/ which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. "ReCAPTCHA":http://ambethia.com/recaptcha/ is also a Rails plug-in with the same name as the API.
@@ -967,7 +967,7 @@ Transfer-Encoding: chunked
Content-Type: text/html
</plain>
-Under certain circumstances this would present the malicious HTML to the victim. However, this seems to work with Keep-Alive connections, only (and many browsers are using one-time connections). But you can't rely on this. _(highlight)In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks._
+Under certain circumstances this would present the malicious HTML to the victim. However, this only seems to work with Keep-Alive connections (and many browsers are using one-time connections). But you can't rely on this. _(highlight)In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks._
h3. Additional Resources
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index 12fc836edf..8318146ed3 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -211,7 +211,7 @@ $ rake db:migrate
$ rake db:test:load
</shell>
-Above +rake db:migrate+ runs any pending migrations on the _developemnt_ environment and updates +db/schema.rb+. +rake db:test:load+ recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run +db:test:prepare+ as it first checks for pending migrations and warns you appropriately.
+Above +rake db:migrate+ runs any pending migrations on the _development_ environment and updates +db/schema.rb+. +rake db:test:load+ recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run +db:test:prepare+ as it first checks for pending migrations and warns you appropriately.
NOTE: +db:test:prepare+ will fail with an error if db/schema.rb doesn't exists.
@@ -372,7 +372,7 @@ NameError: undefined local variable or method `some_undefined_variable' for #<Po
Notice the 'E' in the output. It denotes a test with error.
-NOTE: The execution of each test method stops as soon as any error or a assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
+NOTE: The execution of each test method stops as soon as any error or an assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
h4. What to Include in Your Unit Tests
@@ -413,6 +413,8 @@ h4. Rails Specific Assertions
Rails adds some custom assertions of its own to the +test/unit+ framework:
+NOTE: +assert_valid(record)+ has been deprecated. Please use +assert(record.valid?)+ instead.
+
|_.Assertion |_.Purpose|
|+assert_valid(record)+ |Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.|
|+assert_difference(expressions, difference = 1, message = nil) {...}+ |Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.|
@@ -437,7 +439,7 @@ You should test for things such as:
* was the user redirected to the right page?
* was the user successfully authenticated?
* was the correct object stored in the response template?
-* was the appropriate message displayed to the user in the view
+* was the appropriate message displayed to the user in the view?
Now that we have used Rails scaffold generator for our +Post+ resource, it has already created the controller code and functional tests. You can take look at the file +posts_controller_test.rb+ in the +test/functional+ directory.
@@ -590,7 +592,7 @@ There are more assertions that are primarily used in testing views:
|_.Assertion |_.Purpose|
|+assert_select_email+ |Allows you to make assertions on the body of an e-mail. |
-|+assert_select_rjs+ |Allows you to make assertions on RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.|
+|+assert_select_rjs+ |Allows you to make assertions on an RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.|
|+assert_select_encoded+ |Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.|
|+css_select(selector)+ or +css_select(element, selector)+ |Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.|
@@ -699,7 +701,7 @@ class UserFlowsTest < ActionController::IntegrationTest
# User avs can browse site
avs.browses_site
- # User guest can browse site aswell
+ # User guest can browse site as well
guest.browses_site
# Continue with other assertions
@@ -730,7 +732,7 @@ end
h3. Rake Tasks for Running your Tests
-You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project.
+You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rails project.
|_.Tasks |_.Description|
|+rake test+ |Runs all unit, functional and integration tests. You can also simply run +rake+ as the _test_ target is the default.|
@@ -836,7 +838,7 @@ end
h3. Testing Routes
-Like everything else in you Rails application, it's recommended to test you routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like:
+Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like:
<ruby>
def test_should_route_to_post
@@ -860,7 +862,7 @@ The goals of testing your +ActionMailer+ classes are to ensure that:
h5. From All Sides
-There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a knownvalue (a fixture -- yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time.
+There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a known value (a fixture -- yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time.
h4. Unit Testing