1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge docrails

This commit is contained in:
Pratik Naik 2009-02-01 18:25:03 +00:00
parent 3be0ad60e4
commit 886124e688
31 changed files with 4705 additions and 953 deletions

View file

@ -1166,11 +1166,12 @@ module ActiveRecord
# [:foreign_key]
# Specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_and_belongs_to_many+ association
# will use "person_id" as the default <tt>:foreign_key</tt>.
# to Project will use "person_id" as the default <tt>:foreign_key</tt>.
# [:association_foreign_key]
# Specify the association foreign key used for the association. By default this is
# guessed to be the name of the associated class in lower-case and "_id" suffixed. So if the associated class is Project,
# the +has_and_belongs_to_many+ association will use "project_id" as the default <tt>:association_foreign_key</tt>.
# Specify the foreign key used for the association on the receiving side of the association.
# By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
# So if a Person class makes a +has_and_belongs_to_many+ association to Project,
# the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
# [:conditions]
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.

View file

@ -1,6 +1,9 @@
class Object
# Tries to send the method only if object responds to it. Return +nil+ otherwise.
# It will also forward any arguments and/or block like Object#send does.
# Invokes the method identified by the symbol +method+, passing it any arguments
# and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
#
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
#
# ==== Examples
#
@ -12,11 +15,13 @@ class Object
# With try
# @person.try(:name)
#
# Try also accepts arguments/blocks for the method it is trying
# +try+ also accepts arguments and/or a block, for the method it is trying
# Person.try(:find, 1)
# @people.try(:collect) {|p| p.name}
#--
# This method def is for rdoc only. The alias_method below overrides it as an optimization.
# This method definition below is for rdoc purposes only. The alias_method call
# below overrides it as an optimization since +try+ behaves like +Object#send+,
# unless called on +NilClass+.
def try(method, *args, &block)
send(method, *args, &block)
end

View file

@ -285,7 +285,7 @@ task :guides do
asciidoc_conf = 'doc/guides/asciidoc.conf'
ignore = ['..', 'icons', 'images', 'templates', 'stylesheets']
ignore << 'active_record_basics.txt'
#ignore << 'active_record_basics.txt'
indexless = ['index.txt', 'authors.txt']
@ -301,7 +301,7 @@ task :guides do
end
entries.each do |entry|
next if ignore.include?(entry)
next if ignore.include?(entry) or entry =~ /^\./ # because Vim is always saving hidden swap files
if File.directory?(File.join(source, entry))
# If the current entry is a directory, then we will want to compile

View file

@ -993,6 +993,11 @@ The <tt>%s</tt> and <tt>%d</tt> interpolation syntax for internationalization is
Durations of fractional months or fractional years are deprecated. Use Ruby&#8217;s core <tt>Date</tt> and <tt>Time</tt> class arithmetic instead.
</p>
</li>
<li>
<p>
<tt>Request#relative_url_root</tt> is deprecated. Use <tt>ActionController::Base.relative_url_root</tt> instead.
</p>
</li>
</ul></div>
</div>
<h2 id="_credits">12. Credits</h2>

File diff suppressed because it is too large Load diff

View file

@ -31,25 +31,54 @@
<h2>Chapters</h2>
<ol>
<li>
<a href="#_what_is_action_mailer">What is Action Mailer?</a>
<a href="#_introduction">Introduction</a>
</li>
<li>
<a href="#_quick_walkthrough_to_creating_a_mailer">Quick walkthrough to creating a Mailer</a>
<a href="#_sending_emails">Sending Emails</a>
<ul>
<li><a href="#_1_create_the_mailer">1. Create the mailer:</a></li>
<li><a href="#_walkthrough_to_generating_a_mailer">Walkthrough to generating a Mailer</a></li>
<li><a href="#_2_edit_the_model">2. Edit the model:</a></li>
<li><a href="#_action_mailer_and_dynamic_deliver_methods">Action Mailer and dynamic deliver_ methods</a></li>
<li><a href="#_3_create_the_mailer_view">3. Create the mailer view</a></li>
<li><a href="#_complete_list_of_actionmailer_user_settable_attributes">Complete List of ActionMailer user-settable attributes</a></li>
<li><a href="#_4_wire_it_up_so_that_the_system_sends_the_email_when_a_user_signs_up">4. Wire it up so that the system sends the email when a user signs up</a></li>
<li><a href="#_mailer_views">Mailer Views</a></li>
<li><a href="#_action_mailer_layouts">Action Mailer Layouts</a></li>
<li><a href="#_generating_url_8217_s_in_action_mailer_views">Generating URL&#8217;s in Action Mailer views</a></li>
<li><a href="#_sending_multipart_emails">Sending multipart emails</a></li>
<li><a href="#_sending_emails_with_attachments">Sending emails with attachments</a></li>
</ul>
</li>
<li>
<a href="#_receiving_emails">Receiving Emails</a>
</li>
<li>
<a href="#_using_action_mailer_helpers">Using Action Mailer Helpers</a>
</li>
<li>
<a href="#_action_mailer_configuration">Action Mailer Configuration</a>
<ul>
<li><a href="#_example_action_mailer_configuration">Example Action Mailer Configuration</a></li>
<li><a href="#_action_mailer_configuration_for_gmail">Action Mailer Configuration for GMail</a></li>
<li><a href="#_configure_action_mailer_to_recognize_haml_templates">Configure Action Mailer to recognize HAML templates</a></li>
</ul>
</li>
<li>
<a href="#_mailer_testing">Mailer Testing</a>
</li>
<li>
<a href="#_epilogue">Epilogue</a>
</li>
</ol>
</div>
@ -57,18 +86,19 @@
<h1>Action Mailer Basics</h1>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>This guide should provide you with all you need to get started in sending emails from your application, and will also cover how to test your mailers.</p></div>
<div class="paragraph"><p>This guide should provide you with all you need to get started in sending and receiving emails from/to your application, and many internals of the ActionMailer class. It will also cover how to test your mailers.</p></div>
</div>
</div>
<h2 id="_what_is_action_mailer">1. What is Action Mailer?</h2>
<h2 id="_introduction">1. Introduction</h2>
<div class="sectionbody">
<div class="paragraph"><p>Action Mailer allows you to send email from your application using a mailer model and views.
Yes, that is correct, in Rails, emails are used by creating Models that inherit from ActionMailer::Base. They live alongside other models in /app/models BUT they have views just like controllers that appear alongside other views in app/views.</p></div>
Yes, that is correct, in Rails, emails are used by creating models that inherit from ActionMailer::Base. They live alongside other models in /app/models BUT they have views just like controllers that appear alongside other views in app/views.</p></div>
</div>
<h2 id="_quick_walkthrough_to_creating_a_mailer">2. Quick walkthrough to creating a Mailer</h2>
<h2 id="_sending_emails">2. Sending Emails</h2>
<div class="sectionbody">
<div class="paragraph"><p>Let&#8217;s say you want to send a welcome email to a user after they signup. Here is how you would go about this:</p></div>
<h3 id="_1_create_the_mailer">2.1. 1. Create the mailer:</h3>
<h3 id="_walkthrough_to_generating_a_mailer">2.1. Walkthrough to generating a Mailer</h3>
<h4 id="_create_the_mailer">2.1.1. Create the mailer:</h4>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -82,7 +112,8 @@ create test/fixtures/user_mailer
create app/models/user_mailer<span style="color: #990000">.</span>rb
create test/unit/user_mailer_test<span style="color: #990000">.</span>rb</tt></pre></div></div>
<div class="paragraph"><p>So we got the model, the fixtures, and the tests all created for us</p></div>
<h3 id="_2_edit_the_model">2.2. 2. Edit the model:</h3>
<h4 id="_edit_the_model">2.1.2. Edit the model:</h4>
<div class="paragraph"><p>If you look at app/models/user_mailer.rb, you will see:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -109,14 +140,40 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>So what do we have here?
recipients: who the recipients are, put in an array for multiple, ie, @recipients = ["user1@example.com", "user2@example.com"]
from: Who the email will appear to come from in the recipients' mailbox
subject: The subject of the email
sent_on: Timestamp for the email
content_type: The content type, by default is text/plain</p></div>
<div class="paragraph"><p>So what do we have here?</p></div>
<div class="tableblock">
<table rules="all"
width="100%"
frame="border"
cellspacing="0" cellpadding="4">
<col width="20%" />
<col width="80%" />
<tbody valign="top">
<tr>
<td align="left"><p class="table">recipients</p></td>
<td align="left"><p class="table">who the recipients are, put in an array for multiple, ie, @recipients = ["user1@example.com", "user2@example.com"]</p></td>
</tr>
<tr>
<td align="left"><p class="table">from</p></td>
<td align="left"><p class="table">Who the email will appear to come from in the recipients' mailbox</p></td>
</tr>
<tr>
<td align="left"><p class="table">subject</p></td>
<td align="left"><p class="table">The subject of the email</p></td>
</tr>
<tr>
<td align="left"><p class="table">sent_on</p></td>
<td align="left"><p class="table">Timestamp for the email</p></td>
</tr>
<tr>
<td align="left"><p class="table">content_type</p></td>
<td align="left"><p class="table">The content type, by default is text/plain</p></td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph"><p>How about @body[:user]? Well anything you put in the @body hash will appear in the mailer view (more about mailer views below) as an instance variable ready for you to use, ie, in our example the mailer view will have a @user instance variable available for its consumption.</p></div>
<h3 id="_3_create_the_mailer_view">2.3. 3. Create the mailer view</h3>
<h4 id="_create_the_mailer_view">2.1.3. Create the mailer view</h4>
<div class="paragraph"><p></p></div>
<div class="paragraph"><p>The file can look like:</p></div>
<div class="listingblock">
@ -131,7 +188,6 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">&lt;/head&gt;</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">&lt;body&gt;</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>Welcome to example.com, &lt;%= @user.first_name %&gt;<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>
You have successfully signed up to example.com, and your username is: &lt;%= @user.login %&gt;.<span style="font-weight: bold"><span style="color: #0000FF">&lt;br/&gt;</span></span>
To login to the site, just follow this link: &lt;%= @url %&gt;.
@ -139,9 +195,10 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Thanks for joining and have a great day!<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">&lt;/body&gt;</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">&lt;/html&gt;</span></span></tt></pre></div></div>
<h3 id="_4_wire_it_up_so_that_the_system_sends_the_email_when_a_user_signs_up">2.4. 4. Wire it up so that the system sends the email when a user signs up</h3>
<div class="paragraph"><p>There are 3 was to achieve this. One is to send the email from the controller that sends the email, another is to put it in a before_create block in the user model, and the last one is to use an observer on the user model. Whether you use the second or third methods is up to you, but staying away from the first is recommended. Not because it&#8217;s wrong, but because it keeps your controller clean, and keeps all logic related to the user model within the user model. This way, whichever way a user is created (from a web form, or from an API call, for example), we are guaranteed that the email will be sent.</p></div>
<div class="paragraph"><p></p></div>
<h4 id="_wire_it_up_so_that_the_system_sends_the_email_when_a_user_signs_up">2.1.4. Wire it up so that the system sends the email when a user signs up</h4>
<div class="paragraph"><p>There are 3 ways to achieve this. One is to send the email from the controller that sends the email, another is to put it in a before_create block in the user model, and the last one is to use an observer on the user model. Whether you use the second or third methods is up to you, but staying away from the first is recommended. Not because it&#8217;s wrong, but because it keeps your controller clean, and keeps all logic related to the user model within the user model. This way, whichever way a user is created (from a web form, or from an API call, for example), we are guaranteed that the email will be sent.</p></div>
<div class="paragraph"><p>Let&#8217;s see how we would go about wiring it up using an observer:</p></div>
<div class="paragraph"><p>In config/environment.rb:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -156,7 +213,7 @@ Rails<span style="color: #990000">::</span>Initializer<span style="color: #99000
config<span style="color: #990000">.</span>active_record<span style="color: #990000">.</span>observers <span style="color: #990000">=</span> <span style="color: #990000">:</span>user_observer
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p></p></div>
<div class="paragraph"><p>There was a bit of a debate on where to put observers. Some people put them in app/models, but a cleaner method may be to create an app/observers folder to store all observers, and add that to your load path. Open config/environment.rb and make it look like:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -173,7 +230,8 @@ Rails<span style="color: #990000">::</span>Initializer<span style="color: #99000
config<span style="color: #990000">.</span>active_record<span style="color: #990000">.</span>observers <span style="color: #990000">=</span> <span style="color: #990000">:</span>user_observer
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>ALMOST THERE :) Now all we need is that danged observer, and we&#8217;re done:</p></div>
<div class="paragraph"><p>ALMOST THERE :) Now all we need is that danged observer, and we&#8217;re done:
Create a file called user_observer in app/models or app/observers depending on where you stored it, and make it look like:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -184,11 +242,495 @@ http://www.gnu.org/software/src-highlite -->
UserMailer<span style="color: #990000">.</span>deliver_welcome_email<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Notice how we call deliver_welcome_email? Where is that method? Well if you remember, we created a method called welcome_email in UserMailer, right? Well, as part of the "magic" of rails, we deliver the email identified by welcome_email by calling deliver_welcome_email.</p></div>
<div class="paragraph"><p>That&#8217;s it! Now whenever your users signup, they will be greeted with a nice welcome email. Next up, we&#8217;ll talk about how to test a mailer model.</p></div>
<div class="paragraph"><p>Notice how we call deliver_welcome_email? Where is that method? Well if you remember, we created a method called welcome_email in UserMailer, right? Well, as part of the "magic" of rails, we deliver the email identified by welcome_email by calling deliver_welcome_email. The next section will go through this in more detail.</p></div>
<div class="paragraph"><p>That&#8217;s it! Now whenever your users signup, they will be greeted with a nice welcome email.</p></div>
<h3 id="_action_mailer_and_dynamic_deliver_methods">2.2. Action Mailer and dynamic deliver_ methods</h3>
<div class="paragraph"><p>So how does Action Mailer understand this deliver_welcome_email call? If you read the documentation (<a href="http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html">http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html</a>), you will find this in the "Sending Emails" section:</p></div>
<div class="paragraph"><p>You never instantiate your mailer class. Rather, your delivery instance
methods are automatically wrapped in class methods that start with the word
deliver_ followed by the name of the mailer method that you would
like to deliver. The signup_notification method defined above is
delivered by invoking Notifier.deliver_signup_notification.</p></div>
<div class="paragraph"><p>So, how exactly does this work?</p></div>
<div class="paragraph"><p>In ActionMailer:Base, you will find this:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> method_missing<span style="color: #990000">(</span>method_symbol<span style="color: #990000">,</span> <span style="color: #990000">*</span>parameters<span style="color: #990000">)</span><span style="font-style: italic"><span style="color: #9A1900">#:nodoc:</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">case</span></span> method_symbol<span style="color: #990000">.</span>id2name
<span style="font-weight: bold"><span style="color: #0000FF">when</span></span> <span style="color: #FF6600">/^create_([_a-z]\w*)/</span> <span style="font-weight: bold"><span style="color: #0000FF">then</span></span> new<span style="color: #990000">(</span><span style="color: #009900">$1</span><span style="color: #990000">,</span> <span style="color: #990000">*</span>parameters<span style="color: #990000">).</span>mail
<span style="font-weight: bold"><span style="color: #0000FF">when</span></span> <span style="color: #FF6600">/^deliver_([_a-z]\w*)/</span> <span style="font-weight: bold"><span style="color: #0000FF">then</span></span> new<span style="color: #990000">(</span><span style="color: #009900">$1</span><span style="color: #990000">,</span> <span style="color: #990000">*</span>parameters<span style="color: #990000">).</span>deliver!
<span style="font-weight: bold"><span style="color: #0000FF">when</span></span> <span style="color: #FF0000">"new"</span> <span style="font-weight: bold"><span style="color: #0000FF">then</span></span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">else</span></span> <span style="font-weight: bold"><span style="color: #0000FF">super</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Ah, this makes things so much clearer :) so if the method name starts with deliver_ followed by any combination of lowercase letters or underscore, method missing calls new on your mailer class (UserMailer in our example above), sending the combination of lower case letters or underscore, along with the parameter. The resulting object is then sent the deliver! method, which well... delivers it.</p></div>
<h3 id="_complete_list_of_actionmailer_user_settable_attributes">2.3. Complete List of ActionMailer user-settable attributes</h3>
<div class="tableblock">
<table rules="all"
width="100%"
frame="border"
cellspacing="0" cellpadding="4">
<col width="20%" />
<col width="80%" />
<tbody valign="top">
<tr>
<td align="left"><p class="table">bcc</p></td>
<td align="left"><p class="table">Specify the BCC addresses for the message</p></td>
</tr>
<tr>
<td align="left"><p class="table">body</p></td>
<td align="left"><p class="table">Define the body of the message. 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 text of the message.</p></td>
</tr>
<tr>
<td align="left"><p class="table">cc</p></td>
<td align="left"><p class="table">Specify the CC addresses for the message.</p></td>
</tr>
<tr>
<td align="left"><p class="table">charset</p></td>
<td align="left"><p class="table">Specify the charset to use for the message. This defaults to the default_charset specified for ActionMailer::Base.</p></td>
</tr>
<tr>
<td align="left"><p class="table">content_type</p></td>
<td align="left"><p class="table">Specify the content type for the message. This defaults to &lt;text/plain in most cases, but can be automatically set in some situations.</p></td>
</tr>
<tr>
<td align="left"><p class="table">from</p></td>
<td align="left"><p class="table">Specify the from address for the message.</p></td>
</tr>
<tr>
<td align="left"><p class="table">reply_to</p></td>
<td align="left"><p class="table">Specify the address (if different than the "from" address) to direct replies to this message.</p></td>
</tr>
<tr>
<td align="left"><p class="table">headers</p></td>
<td align="left"><p class="table">Specify additional headers to be added to the message.</p></td>
</tr>
<tr>
<td align="left"><p class="table">implicit_parts_order</p></td>
<td align="left"><p class="table">Specify the order in which parts should be sorted, based on content-type. This defaults to the value for the default_implicit_parts_order.</p></td>
</tr>
<tr>
<td align="left"><p class="table">mime_version</p></td>
<td align="left"><p class="table">Defaults to "1.0", but may be explicitly given if needed.</p></td>
</tr>
<tr>
<td align="left"><p class="table">recipient</p></td>
<td align="left"><p class="table">The recipient addresses for the message, either as a string (for a single address) or an array (for multiple addresses).</p></td>
</tr>
<tr>
<td align="left"><p class="table">sent_on</p></td>
<td align="left"><p class="table">The date on which the message was sent. If not set (the default), the header will be set by the delivery agent.</p></td>
</tr>
<tr>
<td align="left"><p class="table">subject</p></td>
<td align="left"><p class="table">Specify the subject of the message.</p></td>
</tr>
<tr>
<td align="left"><p class="table">template</p></td>
<td align="left"><p class="table">Specify the template name to use for current message. This is the "base" template name, without the extension or directory, and may be used to have multiple mailer methods share the same template.</p></td>
</tr>
</tbody>
</table>
</div>
<h2 id="_mailer_testing">3. Mailer Testing</h2>
<h3 id="_mailer_views">2.4. Mailer Views</h3>
<div class="paragraph"><p>Mailer views are located in /app/views/name_of_mailer_class. The specific mailer view is known to the class because it&#8217;s name is the same as the mailer method. So for example, in our example from above, our mailer view for the welcome_email method will be in /app/views/user_mailer/welcome_email.html.erb for the html version and welcome_email.txt.erb for the plain text version.</p></div>
<div class="paragraph"><p>To change the default mailer view for your action you do something like:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserMailer <span style="color: #990000">&lt;</span> ActionMailer<span style="color: #990000">::</span>Base
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> welcome_email<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
recipients user<span style="color: #990000">.</span>email
from <span style="color: #FF0000">"My Awesome Site Notifications&lt;notifications@example.com&gt;"</span>
subject <span style="color: #FF0000">"Welcome to My Awesome Site"</span>
sent_on Time<span style="color: #990000">.</span>now
body <span style="color: #FF0000">{</span><span style="color: #990000">:</span>user <span style="color: #990000">=&gt;</span> user<span style="color: #990000">,</span> <span style="color: #990000">:</span>url <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"http://example.com/login"</span><span style="color: #FF0000">}</span>
content_type <span style="color: #FF0000">"text/html"</span>
<span style="font-style: italic"><span style="color: #9A1900"># change the default from welcome_email.[html, txt].erb</span></span>
template <span style="color: #FF0000">"some_other_template"</span> <span style="font-style: italic"><span style="color: #9A1900"># this will be in app/views/user_mailer/some_other_template.[html, txt].erb</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<h3 id="_action_mailer_layouts">2.5. Action Mailer Layouts</h3>
<div class="paragraph"><p>Just like controller views, you can also have mailer layouts. The layout name needs to end in _mailer to be automatically recognized by your mailer as a layout. So in our UserMailer example, we need to call our layout user_mailer.[html,txt].erb. In order to use a different file just use:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserMailer <span style="color: #990000">&lt;</span> ActionMailer<span style="color: #990000">::</span>Base
layout <span style="color: #FF0000">'awesome'</span> <span style="font-style: italic"><span style="color: #9A1900"># will use awesome.html.erb as the layout</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Just like with controller views, use yield to render the view inside the layout.</p></div>
<h3 id="_generating_url_8217_s_in_action_mailer_views">2.6. Generating URL&#8217;s in Action Mailer views</h3>
<div class="paragraph"><p>URLs can be generated in mailer views using url_for or named routes.
Unlike controllers from Action Pack, the mailer instance doesn&#8217;t have any context about the incoming request, so you&#8217;ll need to provide all of the details needed to generate a URL.</p></div>
<div class="paragraph"><p>When using url_for you&#8217;ll need to provide the :host, :controller, and :action:</p></div>
<div class="literalblock">
<div class="content">
<pre><tt>&lt;%= url_for(:host =&gt; "example.com", :controller =&gt; "welcome", :action =&gt; "greeting") %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>When using named routes you only need to supply the :host:</p></div>
<div class="literalblock">
<div class="content">
<pre><tt>&lt;%= users_url(:host =&gt; "example.com") %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>You will want to avoid using the name_of_route_path form of named routes because it doesn&#8217;t make sense to generate relative URLs in email messages. The reason that it doesn&#8217;t make sense is because the email is opened on a mail client outside of your environment. Since the email is not being served by your server, a URL like "/users/show/1", will have no context. In order for the email client to properly link to a URL on your server it needs something like "http://yourserver.com/users/show/1".</p></div>
<div class="paragraph"><p>It is also possible to set a default host that will be used in all mailers by setting the :host option in
the ActionMailer::Base.default_url_options hash as follows:</p></div>
<div class="literalblock">
<div class="content">
<pre><tt>ActionMailer::Base.default_url_options[:host] = "example.com"</tt></pre>
</div></div>
<div class="paragraph"><p>This can also be set as a configuration option in config/environment.rb:</p></div>
<div class="literalblock">
<div class="content">
<pre><tt>config.action_mailer.default_url_options = { :host =&gt; "example.com" }</tt></pre>
</div></div>
<div class="paragraph"><p>If you do decide to set a default :host for your mailers you will want to use the :only_path =&gt; false option when using url_for. This will ensure that absolute URLs are generated because the url_for view helper will, by default, generate relative URLs when a :host option isn&#8217;t explicitly provided.</p></div>
<h3 id="_sending_multipart_emails">2.7. Sending multipart emails</h3>
<div class="paragraph"><p>Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have welcome_email.txt.erb and welcome_email.html.erb in app/views/user_mailer, Action Mailer will automatically send a multipart email with the html and text versions setup as different parts.</p></div>
<div class="paragraph"><p>To explicitly specify multipart messages, you can do something like:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserMailer <span style="color: #990000">&lt;</span> ActionMailer<span style="color: #990000">::</span>Base
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> welcome_email<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
recipients user<span style="color: #990000">.</span>email_address
subject <span style="color: #FF0000">"New account information"</span>
from <span style="color: #FF0000">"system@example.com"</span>
content_type <span style="color: #FF0000">"multipart/alternative"</span>
part <span style="color: #990000">:</span>content_type <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"text/html"</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>body <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"&lt;p&gt;html content, can also be the name of an action that you call&lt;p&gt;"</span>
part <span style="color: #FF0000">"text/plain"</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>p<span style="color: #990000">|</span>
p<span style="color: #990000">.</span>body <span style="color: #990000">=</span> <span style="color: #FF0000">"text content, can also be the name of an action that you call"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<h3 id="_sending_emails_with_attachments">2.8. Sending emails with attachments</h3>
<div class="paragraph"><p>Attachments can be added by using the attachment method:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserMailer <span style="color: #990000">&lt;</span> ActionMailer<span style="color: #990000">::</span>Base
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> welcome_email<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
recipients user<span style="color: #990000">.</span>email_address
subject <span style="color: #FF0000">"New account information"</span>
from <span style="color: #FF0000">"system@example.com"</span>
content_type <span style="color: #FF0000">"multipart/alternative"</span>
attachment <span style="color: #990000">:</span>content_type <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"image/jpeg"</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>body <span style="color: #990000">=&gt;</span> File<span style="color: #990000">.</span>read<span style="color: #990000">(</span><span style="color: #FF0000">"an-image.jpg"</span><span style="color: #990000">)</span>
attachment <span style="color: #FF0000">"application/pdf"</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>a<span style="color: #990000">|</span>
a<span style="color: #990000">.</span>body <span style="color: #990000">=</span> generate_your_pdf_here<span style="color: #990000">()</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
</div>
<h2 id="_receiving_emails">3. Receiving Emails</h2>
<div class="sectionbody">
<div class="paragraph"><p>Receiving and parsing emails with Action Mailer can be a rather complex endeavour. Before your email reaches your Rails app, you would have had to configure your system to somehow forward emails to your app, which needs to be listening for that.
So, to receive emails in your Rails app you&#8217;ll need:</p></div>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Configure your email server to forward emails from the address(es) you would like your app to receive to /path/to/app/script/runner 'UserMailer.receive(STDIN.read)'
</p>
</li>
<li>
<p>
Implement a receive method in your mailer
</p>
</li>
</ol></div>
<div class="paragraph"><p>Once a method called receive is defined in any mailer, Action Mailer will parse the raw incoming email into an email object, decode it, instantiate a new mailer, and pass the email object to the mailer objects receive method. Here&#8217;s an example:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserMailer <span style="color: #990000">&lt;</span> ActionMailer<span style="color: #990000">::</span>Base
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> receive<span style="color: #990000">(</span>email<span style="color: #990000">)</span>
page <span style="color: #990000">=</span> Page<span style="color: #990000">.</span>find_by_address<span style="color: #990000">(</span>email<span style="color: #990000">.</span>to<span style="color: #990000">.</span>first<span style="color: #990000">)</span>
page<span style="color: #990000">.</span>emails<span style="color: #990000">.</span>create<span style="color: #990000">(</span>
<span style="color: #990000">:</span>subject <span style="color: #990000">=&gt;</span> email<span style="color: #990000">.</span>subject<span style="color: #990000">,</span>
<span style="color: #990000">:</span>body <span style="color: #990000">=&gt;</span> email<span style="color: #990000">.</span>body
<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">if</span></span> email<span style="color: #990000">.</span>has_attachments?
<span style="font-weight: bold"><span style="color: #0000FF">for</span></span> attachment <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> email<span style="color: #990000">.</span>attachments
page<span style="color: #990000">.</span>attachments<span style="color: #990000">.</span>create<span style="color: #990000">(</span><span style="color: #FF0000">{</span>
<span style="color: #990000">:</span>file <span style="color: #990000">=&gt;</span> attachment<span style="color: #990000">,</span>
<span style="color: #990000">:</span>description <span style="color: #990000">=&gt;</span> email<span style="color: #990000">.</span>subject
<span style="color: #FF0000">}</span><span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
</div>
<h2 id="_using_action_mailer_helpers">4. Using Action Mailer Helpers</h2>
<div class="sectionbody">
<div class="paragraph"><p>Action Mailer classes have 4 helper methods available to them:</p></div>
<div class="tableblock">
<table rules="all"
width="100%"
frame="border"
cellspacing="0" cellpadding="4">
<col width="20%" />
<col width="80%" />
<tbody valign="top">
<tr>
<td align="left"><p class="table">add_template_helper(helper_module)</p></td>
<td align="left"><p class="table">Makes all the (instance) methods in the helper module available to templates rendered through this controller.</p></td>
</tr>
<tr>
<td align="left"><p class="table">helper(*args, &amp;block)</p></td>
<td align="left"><p class="table">Declare a helper:
helper :foo
requires <em>foo_helper</em> and includes FooHelper in the template class.
helper FooHelper
includes FooHelper in the template class.
evaluates the block in the template class, adding method foo.
helper(:three, BlindHelper) { def mice() <em>mice</em> end }
does all three.</p></td>
</tr>
<tr>
<td align="left"><p class="table">helper_method</p></td>
<td align="left"><p class="table">Declare a controller method as a helper. For example,
helper_method :link_to
def link_to(name, options) ... end
makes the link_to controller method available in the view.</p></td>
</tr>
<tr>
<td align="left"><p class="table">helper_attr</p></td>
<td align="left"><p class="table">Declare a controller attribute as a helper. For example,
helper_attr :name
attr_accessor :name
makes the name and name= controller methods available in the view.
The is a convenience wrapper for helper_method.</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<h2 id="_action_mailer_configuration">5. Action Mailer Configuration</h2>
<div class="sectionbody">
<div class="paragraph"><p>The following configuration options are best made in one of the environment files (environment.rb, production.rb, etc...)</p></div>
<div class="tableblock">
<table rules="all"
width="100%"
frame="border"
cellspacing="0" cellpadding="4">
<col width="20%" />
<col width="80%" />
<tbody valign="top">
<tr>
<td align="left"><p class="table">template_root</p></td>
<td align="left"><div><div class="paragraph"><p>Determines the base from which template references will be made.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">logger</p></td>
<td align="left"><div><div class="paragraph"><p>the logger is used for generating information on the mailing run if available.
Can be set to nil for no logging. Compatible with both Ruby&#8217;s own Logger and Log4r loggers.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">smtp_settings</p></td>
<td align="left"><div><div class="paragraph"><p>Allows detailed configuration for :smtp delivery method:</p></div>
<div class="tableblock">
<table rules="all"
width="100%"
frame="border"
cellspacing="0" cellpadding="4">
<col width="20%" />
<col width="80%" />
<tbody valign="top">
<tr>
<td align="left"><p class="table">:address</p></td>
<td align="left"><p class="table">Allows you to use a remote mail server. Just change it from its default "localhost" setting.</p></td>
</tr>
<tr>
<td align="left"><p class="table">:port</p></td>
<td align="left"><p class="table">On the off chance that your mail server doesn&#8217;t run on port 25, you can change it.</p></td>
</tr>
<tr>
<td align="left"><p class="table">:domain</p></td>
<td align="left"><p class="table">If you need to specify a HELO domain, you can do it here.</p></td>
</tr>
<tr>
<td align="left"><p class="table">:user_name</p></td>
<td align="left"><p class="table">If your mail server requires authentication, set the username in this setting.</p></td>
</tr>
<tr>
<td align="left"><p class="table">:password</p></td>
<td align="left"><p class="table">If your mail server requires authentication, set the password in this setting.</p></td>
</tr>
<tr>
<td align="left"><p class="table">:authentication</p></td>
<td align="left"><p class="table">If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of :plain, :login, :cram_md5.</p></td>
</tr>
</tbody>
</table>
</div></div></td>
</tr>
<tr>
<td align="left"><p class="table">sendmail_settings</p></td>
<td align="left"><div><div class="paragraph"><p>Allows you to override options for the :sendmail delivery method.</p></div>
<div class="tableblock">
<table rules="all"
width="100%"
frame="border"
cellspacing="0" cellpadding="4">
<col width="20%" />
<col width="80%" />
<tbody valign="top">
<tr>
<td align="left"><p class="table">:location</p></td>
<td align="left"><p class="table">The location of the sendmail executable. Defaults to /usr/sbin/sendmail.</p></td>
</tr>
<tr>
<td align="left"><p class="table">:arguments</p></td>
<td align="left"><p class="table">The command line arguments. Defaults to -i -t.</p></td>
</tr>
</tbody>
</table>
</div></div></td>
</tr>
<tr>
<td align="left"><p class="table">raise_delivery_errors</p></td>
<td align="left"><div><div class="paragraph"><p>Whether or not errors should be raised if the email fails to be delivered.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">delivery_method</p></td>
<td align="left"><div><div class="paragraph"><p>Defines a delivery method. Possible values are :smtp (default), :sendmail, and :test.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">perform_deliveries</p></td>
<td align="left"><div><div class="paragraph"><p>Determines whether deliver_* methods are actually carried out. By default they are,
but this can be turned off to help functional testing.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">deliveries</p></td>
<td align="left"><div><div class="paragraph"><p>Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful
for unit and functional testing.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">default_charset</p></td>
<td align="left"><div><div class="paragraph"><p>The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
pick a different charset from inside a method with charset.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">default_content_type</p></td>
<td align="left"><div><div class="paragraph"><p>The default content type used for the main part of the message. Defaults to "text/plain". You
can also pick a different content type from inside a method with content_type.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">default_mime_version</p></td>
<td align="left"><div><div class="paragraph"><p>The default mime version used for the message. Defaults to 1.0. You
can also pick a different value from inside a method with mime_version.</p></div></div></td>
</tr>
<tr>
<td align="left"><p class="table">default_implicit_parts_order</p></td>
<td align="left"><div><div class="paragraph"><p>When a message is built implicitly (i.e. multiple parts are assembled from templates
which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client
and appear last in the mime encoded message. You can also pick a different order from inside a method with
implicit_parts_order.</p></div></div></td>
</tr>
</tbody>
</table>
</div>
<h3 id="_example_action_mailer_configuration">5.1. Example Action Mailer Configuration</h3>
<div class="paragraph"><p>An example would be:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>delivery_method <span style="color: #990000">=</span> <span style="color: #990000">:</span>sendmail
ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>sendmail_settings <span style="color: #990000">=</span> <span style="color: #FF0000">{</span>
<span style="color: #990000">:</span>location <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'/usr/sbin/sendmail'</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>arguments <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'-i -t'</span>
<span style="color: #FF0000">}</span>
ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>perform_deliveries <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>raise_delivery_errors <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>default_charset <span style="color: #990000">=</span> <span style="color: #FF0000">"iso-8859-1"</span></tt></pre></div></div>
<h3 id="_action_mailer_configuration_for_gmail">5.2. Action Mailer Configuration for GMail</h3>
<div class="paragraph"><p>Instructions copied from <a href="http://http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html">http://http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html</a></p></div>
<div class="paragraph"><p>First you must install the action_mailer_tls plugin from <a href="http://code.openrain.com/rails/action_mailer_tls/">http://code.openrain.com/rails/action_mailer_tls/</a>, then all you have to do is configure action mailer.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>smtp_settings <span style="color: #990000">=</span> <span style="color: #FF0000">{</span>
<span style="color: #990000">:</span>address <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"smtp.gmail.com"</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>port <span style="color: #990000">=&gt;</span> <span style="color: #993399">587</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>domain <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"domain.com"</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>user_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"user@domain.com"</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>password <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"password"</span><span style="color: #990000">,</span>
<span style="color: #990000">:</span>authentication <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>plain
<span style="color: #FF0000">}</span></tt></pre></div></div>
<h3 id="_configure_action_mailer_to_recognize_haml_templates">5.3. Configure Action Mailer to recognize HAML templates</h3>
<div class="paragraph"><p>In environment.rb, add the following line:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>register_template_extension<span style="color: #990000">(</span><span style="color: #FF0000">'haml'</span><span style="color: #990000">)</span></tt></pre></div></div>
</div>
<h2 id="_mailer_testing">6. Mailer Testing</h2>
<div class="sectionbody">
<div class="paragraph"><p>Testing mailers involves 2 things. One is that the mail was queued and the other that the body contains what we expect it to contain. With that in mind, we could test our example mailer from above like so:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserMailerTest <span style="color: #990000">&lt;</span> ActionMailer<span style="color: #990000">::</span>TestCase
tests UserMailer
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_welcome_email
user <span style="color: #990000">=</span> users<span style="color: #990000">(:</span>some_user_in_your_fixtures<span style="color: #990000">)</span>
<span style="font-style: italic"><span style="color: #9A1900"># Send the email, then test that it got queued</span></span>
email <span style="color: #990000">=</span> UserMailer<span style="color: #990000">.</span>deliver_welcome_email<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
assert <span style="color: #990000">!</span>ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>deliveries<span style="color: #990000">.</span>empty?
<span style="font-style: italic"><span style="color: #9A1900"># Test the body of the sent email contains what we expect it to</span></span>
assert_equal <span style="color: #990000">[</span><span style="color: #009900">@user</span><span style="color: #990000">.</span>email<span style="color: #990000">],</span> email<span style="color: #990000">.</span>to
assert_equal <span style="color: #FF0000">"Welcome to My Awesome Site"</span><span style="color: #990000">,</span> email<span style="color: #990000">.</span>subject
assert email<span style="color: #990000">.</span>body <span style="color: #990000">=~</span> <span style="color: #FF6600">/Welcome to example.com, #{user.first_name}/</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>What have we done? Well, we sent the email and stored the returned object in the email variable. We then ensured that it was sent (the first assert), then, in the second batch of assertion, we ensure that the email does indeed contain the values that we expect.</p></div>
</div>
<h2 id="_epilogue">7. Epilogue</h2>
<div class="sectionbody">
<div class="paragraph"><p>This guide presented how to create a mailer and how to test it. In reality, you may find that writing your tests before you actually write your code to be a rewarding experience. It may take some time to get used to TDD (Test Driven Development), but coding this way achieves two major benefits. Firstly, you know that the code does indeed work, because the tests fail (because there&#8217;s no code), then they pass, because the code that satisfies the tests was written. Secondly, when you start with the tests, you don&#8217;t have to make time AFTER you write the code, to write the tests, then never get around to it. The tests are already there and testing has now become part of your coding regimen.</p></div>
</div>
</div>

View file

@ -88,7 +88,14 @@
</ul>
</li>
<li>
<a href="#_http_basic_authentication">HTTP Basic Authentication</a>
<a href="#_http_authentications">HTTP Authentications</a>
<ul>
<li><a href="#_http_basic_authentication">HTTP Basic Authentication</a></li>
<li><a href="#_http_digest_authentication">HTTP Digest Authentication</a></li>
</ul>
</li>
<li>
<a href="#_streaming_and_file_downloads">Streaming and File Downloads</a>
@ -803,9 +810,23 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>response<span style="color: #990000">.</span>headers<span style="color: #990000">[</span><span style="color: #FF0000">"Content-Type"</span><span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"application/pdf"</span></tt></pre></div></div>
</div>
<h2 id="_http_basic_authentication">10. HTTP Basic Authentication</h2>
<h2 id="_http_authentications">10. HTTP Authentications</h2>
<div class="sectionbody">
<div class="paragraph"><p>Rails comes with built-in HTTP Basic authentication. This is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser&#8217;s HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, <tt>authenticate_or_request_with_http_basic</tt>.</p></div>
<div class="paragraph"><p>Rails comes with two built-in HTTP authentication mechanisms :</p></div>
<div class="ulist"><ul>
<li>
<p>
Basic Authentication
</p>
</li>
<li>
<p>
Digest Authentication
</p>
</li>
</ul></div>
<h3 id="_http_basic_authentication">10.1. HTTP Basic Authentication</h3>
<div class="paragraph"><p>HTTP Basic authentication is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser&#8217;s HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, <tt>authenticate_or_request_with_http_basic</tt>.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -817,7 +838,7 @@ http://www.gnu.org/software/src-highlite -->
before_filter <span style="color: #990000">:</span>authenticate
private
private
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> authenticate
authenticate_or_request_with_http_basic <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>username<span style="color: #990000">,</span> password<span style="color: #990000">|</span>
@ -827,6 +848,29 @@ private
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>With this in place, you can create namespaced controllers that inherit from AdminController. The before filter will thus be run for all actions in those controllers, protecting them with HTTP Basic authentication.</p></div>
<h3 id="_http_digest_authentication">10.2. HTTP Digest Authentication</h3>
<div class="paragraph"><p>HTTP Digest authentication is superior to the Basic authentication as it does not require the client to send unencrypted password over the network. Using Digest authentication with Rails is quite easy and only requires using one method, <tt>authenticate_or_request_with_http_digest</tt>.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AdminController <span style="color: #990000">&lt;</span> ApplicationController
USERS <span style="color: #990000">=</span> <span style="color: #FF0000">{</span> <span style="color: #FF0000">"lifo"</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"world"</span> <span style="color: #FF0000">}</span>
before_filter <span style="color: #990000">:</span>authenticate
private
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> authenticate
authenticate_or_request_with_http_digest <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>username<span style="color: #990000">|</span>
USERS<span style="color: #990000">[</span>username<span style="color: #990000">]</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>As seen in the example above, <tt>authenticate_or_request_with_http_digest</tt> block takes only one argument - the username. And the block returns the password. Returning <tt>false</tt> or <tt>nil</tt> from the <tt>authenticate_or_request_with_http_digest</tt> will cause authentication failure.</p></div>
</div>
<h2 id="_streaming_and_file_downloads">11. Streaming and File Downloads</h2>
<div class="sectionbody">

View file

@ -0,0 +1,342 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Active Record Basics</title>
<!--[if lt IE 8]>
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
<![endif]-->
<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
</head>
<body>
<div id="header" >
<div id="logo">
<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
</div>
<h1 id="site_title"><span>Ruby on Rails</span></h1>
<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
<ul id="navMain">
<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
</ul>
</div>
<div id="container">
<div id="sidebar">
<h2>Chapters</h2>
<ol>
<li>
<a href="#_what_8217_s_active_record">What&#8217;s Active Record</a>
</li>
<li>
<a href="#_object_relational_mapping">Object Relational Mapping</a>
</li>
<li>
<a href="#_activerecord_as_an_orm_framework">ActiveRecord as an ORM framework</a>
</li>
<li>
<a href="#_active_record_inside_the_mvc_model">Active Record inside the MVC model</a>
</li>
<li>
<a href="#_convention_over_configuration_in_activerecord">Convention over Configuration in ActiveRecord</a>
<ul>
<li><a href="#_naming_conventions">Naming Conventions</a></li>
<li><a href="#_schema_conventions">Schema Conventions</a></li>
</ul>
</li>
<li>
<a href="#_creating_activerecord_models">Creating ActiveRecord models</a>
</li>
<li>
<a href="#_overriding_the_naming_conventions">Overriding the naming conventions</a>
</li>
<li>
<a href="#_validations">Validations</a>
</li>
<li>
<a href="#_callbacks">Callbacks</a>
</li>
</ol>
</div>
<div id="content">
<h1>Active Record Basics</h1>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>This guide will give you a strong grasp of the Active Record pattern and how it can be used with or without Rails. Hopefully, some of the philosophical and theoretical intentions discussed here will also make you a stronger and better developer.</p></div>
<div class="paragraph"><p>After reading this guide we hope that you&#8217;ll be able to:</p></div>
<div class="ulist"><ul>
<li>
<p>
Understand the way Active Record fits into the MVC model.
</p>
</li>
<li>
<p>
Create basic Active Record models and map them with your database tables.
</p>
</li>
<li>
<p>
Use your models to execute CRUD (Create, Read, Update and Delete) database operations.
</p>
</li>
<li>
<p>
Follow the naming conventions used by Rails to make developing database applications easier and obvious.
</p>
</li>
<li>
<p>
Take advantage of the way Active Record maps it&#8217;s attributes with the database tables' columns to implement your application&#8217;s logic.
</p>
</li>
<li>
<p>
Use Active Record with legacy databases that do not follow the Rails naming conventions.
</p>
</li>
</ul></div>
</div>
</div>
<h2 id="_what_8217_s_active_record">1. What&#8217;s Active Record</h2>
<div class="sectionbody">
<div class="paragraph"><p>Rails' ActiveRecord is an implementation of Martin Fowler&#8217;s <a href="http://martinfowler.com/eaaCatalog/activeRecord.html">Active Record Design Pattern</a>. This pattern is based on the idea of creating relations between the database and the application in the following way:</p></div>
<div class="ulist"><ul>
<li>
<p>
Each database table is mapped to a class.
</p>
</li>
<li>
<p>
Each table column is mapped to an attribute of this class.
</p>
</li>
<li>
<p>
Each instance of this class is mapped to a single row in the database table.
</p>
</li>
</ul></div>
<div class="paragraph"><p>The definition of the Active Record pattern in Martin Fowler&#8217;s words:</p></div>
<div class="paragraph"><p>"<em>An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data."</em></p></div>
</div>
<h2 id="_object_relational_mapping">2. Object Relational Mapping</h2>
<div class="sectionbody">
<div class="paragraph"><p>The relation between databases and object-oriented software is called ORM, which is short for "Object Relational Mapping". The purpose of an ORM framework is to minimize the mismatch existent between relational databases and object-oriented software. In applications with a domain model, we have objects that represent both the state of the system and the behaviour of the real world elements that were modeled through these objects. Since we need to store the system&#8217;s state somehow, we can use relational databases, which are proven to be an excelent approach to data management. Usually this may become a very hard thing to do, since we need to create an object-oriented model of everything that lives in the database, from simple columns to complicated relations between different tables. Doing this kind of thing by hand is a tedious and error prone job. This is where an ORM framework comes in.</p></div>
</div>
<h2 id="_activerecord_as_an_orm_framework">3. ActiveRecord as an ORM framework</h2>
<div class="sectionbody">
<div class="paragraph"><p>ActiveRecord gives us several mechanisms, being the most important ones the hability to:</p></div>
<div class="ulist"><ul>
<li>
<p>
Represent models.
</p>
</li>
<li>
<p>
Represent associations between these models.
</p>
</li>
<li>
<p>
Represent inheritance hierarquies through related models.
</p>
</li>
<li>
<p>
Validate models before they get recorded to the database.
</p>
</li>
<li>
<p>
Perform database operations in an object-oriented fashion.
</p>
</li>
</ul></div>
<div class="paragraph"><p>It&#8217;s easy to see that the Rails Active Record implementation goes way beyond the basic description of the Active Record Pattern.</p></div>
</div>
<h2 id="_active_record_inside_the_mvc_model">4. Active Record inside the MVC model</h2>
<div class="sectionbody">
<div class="paragraph"><p>Active Record plays the role of model inside the MVC structure followed by Rails applications. Since model objects should encapsulate both state and logic of your applications, it&#8217;s ActiveRecord responsability to deliver you the easiest possible way to recover this data from the database.</p></div>
</div>
<h2 id="_convention_over_configuration_in_activerecord">5. Convention over Configuration in ActiveRecord</h2>
<div class="sectionbody">
<div class="paragraph"><p>When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particulary true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you&#8217;ll need to write very little configuration (in some case no configuration at all) when creating ActiveRecord models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicity configuration would be needed only in those cases where you can&#8217;t follow the conventions for any reason.</p></div>
<h3 id="_naming_conventions">5.1. Naming Conventions</h3>
<div class="paragraph"><p>By default, ActiveRecord uses some naming conventions to find out how the mapping between models and database tables should be created. Rails will pluralize your class names to find the respective database table. So, for a class <tt>Book</tt>, you should have a database table called <strong>books</strong>. The Rails pluralization mechanisms are very powerful, being capable to pluralize (and singularize) both regular and irregular words. When using class names composed of two or more words, the model class name should follow the Ruby conventions, using the camelCase form, while the table name must contain the words separated by underscores. Examples:</p></div>
<div class="ulist"><ul>
<li>
<p>
Database Table - Plural with underscores separating words i.e. (book_clubs)
</p>
</li>
<li>
<p>
Model Class - Singular with the first letter of each word capitalized i.e. (BookClub)
</p>
</li>
</ul></div>
<div class="tableblock">
<table rules="all"
width="60%"
frame="border"
cellspacing="0" cellpadding="4">
<col width="50%" />
<col width="50%" />
<thead valign="top">
<tr>
<th align="left">Model / Class </th>
<th align="left">Table / Schema</th>
</tr>
</thead>
<tbody valign="top">
<tr>
<td align="left"><p class="table">Post</p></td>
<td align="left"><p class="table">posts</p></td>
</tr>
<tr>
<td align="left"><p class="table">LineItem</p></td>
<td align="left"><p class="table">line_items</p></td>
</tr>
<tr>
<td align="left"><p class="table">Deer</p></td>
<td align="left"><p class="table">deer</p></td>
</tr>
<tr>
<td align="left"><p class="table">Mouse</p></td>
<td align="left"><p class="table">mice</p></td>
</tr>
<tr>
<td align="left"><p class="table">Person</p></td>
<td align="left"><p class="table">people</p></td>
</tr>
</tbody>
</table>
</div>
<h3 id="_schema_conventions">5.2. Schema Conventions</h3>
<div class="paragraph"><p>ActiveRecord uses naming conventions for the columns in database tables, depending on the purpose of these columns.</p></div>
<div class="ulist"><ul>
<li>
<p>
<strong>Foreign keys</strong> - 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.
</p>
</li>
<li>
<p>
<strong>Primary keys</strong> - By default, ActiveRecord will use a integer column named "id" as the table&#8217;s primary key. When using <a href="http://guides.rails.info/migrations.html">Rails Migrations</a> to create your tables, this column will be automaticaly created.
</p>
</li>
</ul></div>
<div class="paragraph"><p>There are also some optional column names that will create additional features to ActiveRecord instances:</p></div>
<div class="ulist"><ul>
<li>
<p>
<strong>created_at / created_on</strong> - ActiveRecord will store the current date and time to this field when creating the record.
</p>
</li>
<li>
<p>
<strong>updated_at / updated_on</strong> - ActiveRecord will store the current date and times to this field when updating the record.
</p>
</li>
<li>
<p>
<strong>lock_version</strong> - Adds <a href="http://api.rubyonrails.com/classes/ActiveRecord/Locking.html">optimistic locking</a> to a model.
</p>
</li>
<li>
<p>
<strong>type</strong> - Specifies that the model uses <a href="http://api.rubyonrails.com/classes/ActiveRecord/Base.html">Single Table Inheritance</a>
</p>
</li>
<li>
<p>
<strong>(table_name)_count</strong> - Used to cache the number of belonging objects on associations. For example, a <tt>comments_count</tt> column in a <tt>Post</tt> class that has many instances of <tt>Comment</tt> will cache the number of existent comments for each post.
</p>
</li>
</ul></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">While these column names are optional they are in fact reserved by ActiveRecord. Steer clear of reserved keywords unless you want the extra functionality. For example, "type" is a reserved keyword used to designate a table using Single Table Inheritance. If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling.</td>
</tr></table>
</div>
</div>
<h2 id="_creating_activerecord_models">6. Creating ActiveRecord models</h2>
<div class="sectionbody">
<div class="paragraph"><p>It&#8217;s very easy to create ActiveRecord models. All you have to do is to subclass the ActiveRecord::Base class and you&#8217;re good to go:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>This will create a <tt>Product</tt> model, mapped to a <strong>products</strong> table at the database. By doing this you&#8217;ll also have the hability to map the columns of each row in that table with the attributes of the instances of your model. So, suppose that the <strong>products</strong> table was created using a SQL sentence like:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">CREATE</span></span> <span style="font-weight: bold"><span style="color: #0000FF">TABLE</span></span> products <span style="color: #990000">(</span>
id <span style="color: #009900">int</span><span style="color: #990000">(</span><span style="color: #993399">11</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">NOT</span></span> <span style="font-weight: bold"><span style="color: #0000FF">NULL</span></span> <span style="font-weight: bold"><span style="color: #0000FF">auto_increment</span></span><span style="color: #990000">,</span>
name <span style="color: #009900">varchar</span><span style="color: #990000">(</span><span style="color: #993399">255</span><span style="color: #990000">),</span>
<span style="font-weight: bold"><span style="color: #0000FF">PRIMARY</span></span> <span style="font-weight: bold"><span style="color: #0000FF">KEY</span></span> <span style="color: #990000">(</span>id<span style="color: #990000">)</span>
<span style="color: #990000">);</span></tt></pre></div></div>
<div class="paragraph"><p>Following the table schema above, you would be able to write code like the following:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>p <span style="color: #990000">=</span> Product<span style="color: #990000">.</span>new
p<span style="color: #990000">.</span>name <span style="color: #990000">=</span> <span style="color: #FF0000">"Some Book"</span>
puts p<span style="color: #990000">.</span>name <span style="font-style: italic"><span style="color: #9A1900"># "Some Book"</span></span></tt></pre></div></div>
</div>
<h2 id="_overriding_the_naming_conventions">7. Overriding the naming conventions</h2>
<div class="sectionbody">
<div class="paragraph"><p>What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions.</p></div>
<div class="paragraph"><p>You can use the <tt>ActiveRecord::Base.set_table_name</tt> method to specify the table name that should be used:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
set_table_name <span style="color: #FF0000">"PRODUCT"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>It&#8217;s also possible to override the column that should be used as the table&#8217;s primary key. Use the <tt>ActiveRecord::Base.set_primary_key</tt> method for that:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
set_primary_key <span style="color: #FF0000">"product_id"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
</div>
<h2 id="_validations">8. Validations</h2>
<div class="sectionbody">
<div class="paragraph"><p>ActiveRecord gives the hability 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 lifecycle 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 <a href="http://guides.rails.info/activerecord_validations_callbacks.html#_overview_of_activerecord_validation">Active Record Validations and Callbacks guide</a>.</p></div>
</div>
<h2 id="_callbacks">9. Callbacks</h2>
<div class="sectionbody">
<div class="paragraph"><p>ActiveRecord callbacks allow you to attach code to certain events in the lifecycle of your models. This way you can add behaviour 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 <a href="http://guides.rails.info/activerecord_validations_callbacks.html#_callbacks">Active Record Validations and Callbacks guide</a>.</p></div>
</div>
</div>
</div>
</body>
</html>

View file

@ -768,7 +768,7 @@ ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #99000
| `-- yaffle.rb</tt></pre>
</div></div>
<div class="paragraph"><p>As always, start with a test:</p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/yaffle/woodpecker_test.rb:</strong></p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/test/woodpecker_test.rb:</strong></p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1212,7 +1212,7 @@ Rails<span style="color: #990000">::</span>Generator<span style="color: #990000"
Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Destroy<span style="color: #990000">.</span>send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> Yaffle<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Destroy
Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>List<span style="color: #990000">.</span>send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> Yaffle<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>List
Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Update<span style="color: #990000">.</span>send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> Yaffle<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Update</tt></pre></div></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/generators/yaffle/yaffle_route_generator.rb</strong></p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/generators/yaffle_route/yaffle_route_generator.rb</strong></p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1266,25 +1266,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Here are a few possibilities for how to allow developers to use your plugin migrations:</p></div>
<h3 id="_create_a_custom_rake_task">11.1. Create a custom rake task</h3>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/lib/db/migrate/20081116181115_create_birdhouses.rb:</strong></p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateBirdhouses <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
create_table <span style="color: #990000">:</span>birdhouses<span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
t<span style="color: #990000">.</span>timestamps
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
drop_table <span style="color: #990000">:</span>birdhouses
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/tasks/yaffle.rake:</strong></p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/tasks/yaffle_tasks.rake:</strong></p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1406,7 +1388,7 @@ http://www.gnu.org/software/src-highlite -->
</tr></table>
</div>
<div class="paragraph"><p>After running the test with <em>rake</em> you can make it pass with:</p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb</strong></p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/generators/yaffle_migration/yaffle_migration_generator.rb</strong></p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1463,9 +1445,9 @@ http://www.gnu.org/software/src-highlite -->
</div>
<h2 id="_rake_tasks">12. Rake tasks</h2>
<div class="sectionbody">
<div class="paragraph"><p>When you created the plugin with the built-in rails generator, it generated a rake file for you in <em>vendor/plugins/yaffle/tasks/yaffle.rake</em>. Any rake task you add here will be available to the app.</p></div>
<div class="paragraph"><p>When you created the plugin with the built-in rails generator, it generated a rake file for you in <em>vendor/plugins/yaffle/tasks/yaffle_tasks.rake</em>. Any rake task you add here will be available to the app.</p></div>
<div class="paragraph"><p>Many plugin authors put all of their rake tasks into a common namespace that is the same as the plugin, like so:</p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/tasks/yaffle.rake</strong></p></div>
<div class="paragraph"><p><strong>vendor/plugins/yaffle/tasks/yaffle_tasks.rake</strong></p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1577,7 +1559,7 @@ Warning, gotchas or tips that might help save users time
</li>
<li>
<p>
<a href="http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii">http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii</a>
<a href="http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-ii">http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-ii</a>
</p>
</li>
<li>

View file

@ -31,42 +31,36 @@
<h2>Chapters</h2>
<ol>
<li>
<a href="#_basic_forms">Basic forms</a>
<a href="#_dealing_with_basic_forms">Dealing With Basic Forms</a>
<ul>
<li><a href="#_generic_search_form">Generic search form</a></li>
<li><a href="#_a_generic_search_form">A Generic search form</a></li>
<li><a href="#_multiple_hashes_in_form_helper_attributes">Multiple hashes in form helper attributes</a></li>
<li><a href="#_multiple_hashes_in_form_helper_calls">Multiple hashes in form helper calls</a></li>
<li><a href="#_checkboxes_radio_buttons_and_other_controls">Checkboxes, radio buttons and other controls</a></li>
<li><a href="#_how_do_forms_with_put_or_delete_methods_work">How do forms with PUT or DELETE methods work?</a></li>
<li><a href="#_helpers_for_generating_form_elements">Helpers for generating form elements</a></li>
</ul>
</li>
<li>
<a href="#_different_families_of_helpers">Different Families of helpers</a>
<a href="#_dealing_with_model_objects">Dealing With Model Objects</a>
<ul>
<li><a href="#_barebones_helpers">Barebones helpers</a></li>
<li><a href="#_model_object_helpers">Model object helpers</a></li>
</ul>
</li>
<li>
<a href="#_forms_that_deal_with_model_attributes">Forms that deal with model attributes</a>
<ul>
<li><a href="#_binding_a_form_to_an_object">Binding a form to an object</a></li>
<li><a href="#_relying_on_record_identification">Relying on record identification</a></li>
<li><a href="#_how_do_forms_with_put_or_delete_methods_work">How do forms with PUT or DELETE methods work?</a></li>
</ul>
</li>
<li>
<a href="#_making_select_boxes_with_ease">Making select boxes with ease</a>
<ul>
<li><a href="#_the_select_tag_and_options">The select tag and options</a></li>
<li><a href="#_the_select_and_options_tag">The select and options tag</a></li>
<li><a href="#_select_boxes_for_dealing_with_models">Select boxes for dealing with models</a></li>
@ -77,27 +71,21 @@
</ul>
</li>
<li>
<a href="#_date_and_time_select_boxes">Date and time select boxes</a>
<a href="#_using_date_and_time_form_helpers">Using Date and Time Form Helpers</a>
<ul>
<li><a href="#_barebones_helpers_2">Barebones helpers</a></li>
<li><a href="#_barebones_helpers">Barebones helpers</a></li>
<li><a href="#_model_object_helpers_2">Model object helpers</a></li>
<li><a href="#_common_options">Common options</a></li>
</ul>
</li>
<li>
<a href="#_form_builders">Form builders</a>
<ul>
<li><a href="#_scoping_out_form_controls_with_tt_fields_for_tt">Scoping out form controls with <tt>fields_for</tt></a></li>
<li><a href="#_individual_components">Individual components</a></li>
</ul>
</li>
<li>
<a href="#_file_uploads">File Uploads</a>
<a href="#_uploading_files">Uploading Files</a>
<ul>
<li><a href="#_what_gets_uploaded">What gets uploaded</a></li>
@ -107,7 +95,10 @@
</ul>
</li>
<li>
<a href="#_parameter_names">Parameter Names</a>
<a href="#_customising_form_builders">Customising Form Builders</a>
</li>
<li>
<a href="#_understanding_parameter_naming_conventions">Understanding Parameter Naming Conventions</a>
<ul>
<li><a href="#_basic_structures">Basic structures</a></li>
@ -119,7 +110,7 @@
</ul>
</li>
<li>
<a href="#_complex_forms">Complex forms</a>
<a href="#_building_complex_forms">Building Complex forms</a>
</li>
<li>
<a href="#_changelog">Changelog</a>
@ -136,22 +127,32 @@
<div class="ulist"><ul>
<li>
<p>
Create search forms and similar kind of generic forms not representing any specific model in your application;
Create search forms and similar kind of generic forms not representing any specific model in your application
</p>
</li>
<li>
<p>
Make model-centric forms for creation and editing of specific database records;
Make model-centric forms for creation and editing of specific database records
</p>
</li>
<li>
<p>
Generate select boxes from multiple types of data;
Generate select boxes from multiple types of data
</p>
</li>
<li>
<p>
Learn what makes a file upload form different;
Understand the date and time helpers Rails provides
</p>
</li>
<li>
<p>
Learn what makes a file upload form different
</p>
</li>
<li>
<p>
Find out where to look for complex forms
</p>
</li>
</ul></div>
@ -165,7 +166,7 @@ Learn what makes a file upload form different;
</div>
</div>
</div>
<h2 id="_basic_forms">1. Basic forms</h2>
<h2 id="_dealing_with_basic_forms">1. Dealing With Basic Forms</h2>
<div class="sectionbody">
<div class="paragraph"><p>The most basic form helper is <tt>form_tag</tt>.</p></div>
<div class="listingblock">
@ -174,7 +175,7 @@ Learn what makes a file upload form different;
Form contents
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>When called without arguments like this, it creates a form element that has the current page for action attribute and "POST" as method (some line breaks added for readability):</p></div>
<div class="paragraph"><p>When called without arguments like this, it creates a form element that has the current page as its action and "post" as its method (some line breaks added for readability):</p></div>
<div class="listingblock">
<div class="title">Sample output from <tt>form_tag</tt></div>
<div class="content">
@ -185,7 +186,7 @@ Learn what makes a file upload form different;
Form contents
&lt;/form&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>If you carefully observe this output, you can see that the helper generated something you didn&#8217;t specify: a <tt>div</tt> element with a hidden input inside. This is a security feature of Rails called <strong>cross-site request forgery protection</strong> and form helpers generate it for every form which action isn&#8217;t "GET" (provided that this security feature is enabled).</p></div>
<div class="paragraph"><p>If you carefully observe this output, you can see that the helper generated something you didn&#8217;t specify: a <tt>div</tt> element with a hidden input inside. This is a security feature of Rails called <strong>cross-site request forgery protection</strong> and form helpers generate it for every form whose action is not "get" (provided that this security feature is enabled). You can read more about this in the <a href="./security.html#_cross_site_reference_forgery_csrf">Ruby On Rails Security Guide</a>.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@ -194,7 +195,7 @@ Learn what makes a file upload form different;
<td class="content">Throughout this guide, this <tt>div</tt> with the hidden input will be stripped away to have clearer code samples.</td>
</tr></table>
</div>
<h3 id="_generic_search_form">1.1. Generic search form</h3>
<h3 id="_a_generic_search_form">1.1. A Generic search form</h3>
<div class="paragraph"><p>Probably the most minimal form often seen on the web is a search form with a single text input for search terms. This form consists of:</p></div>
<div class="olist arabic"><ol class="arabic">
<li>
@ -223,10 +224,10 @@ a submit element.
<td class="icon">
<img src="./images/icons/important.png" alt="Important" />
</td>
<td class="content">Always use "GET" as the method for search forms. Benefits are many: users are able to bookmark a specific search and get back to it; browsers cache results of "GET" requests, but not "POST"; and others.</td>
<td class="content">Always use "GET" as the method for search forms. This allows users are able to bookmark a specific search and get back to it, more generally Rails encourages you to use the right HTTP verb for an action.</td>
</tr></table>
</div>
<div class="paragraph"><p>To create that, you will use <tt>form_tag</tt>, <tt>label_tag</tt>, <tt>text_field_tag</tt> and <tt>submit_tag</tt>, respectively.</p></div>
<div class="paragraph"><p>To create this form you will use <tt>form_tag</tt>, <tt>label_tag</tt>, <tt>text_field_tag</tt> and <tt>submit_tag</tt>, respectively.</p></div>
<div class="listingblock">
<div class="title">A basic search form</div>
<div class="content">
@ -269,8 +270,8 @@ a submit element.
<td class="content">For every form input, an ID attribute is generated from its name ("q" in the example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.</td>
</tr></table>
</div>
<h3 id="_multiple_hashes_in_form_helper_attributes">1.2. Multiple hashes in form helper attributes</h3>
<div class="paragraph"><p>By now you&#8217;ve seen that the <tt>form_tag</tt> helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element&#8217;s class.</p></div>
<h3 id="_multiple_hashes_in_form_helper_calls">1.2. Multiple hashes in form helper calls</h3>
<div class="paragraph"><p>By now you&#8217;ve seen that the <tt>form_tag</tt> helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element&#8217;s class.</p></div>
<div class="paragraph"><p>As with the &#8216;link_to` helper, the path argument doesn&#8217;t have to be given a string. It can be a hash of URL parameters that Rails&#8217; routing mechanism will turn into a valid URL. Still, you cannot simply write this:</p></div>
<div class="listingblock">
<div class="title">A bad way to pass multiple hashes as method arguments</div>
@ -294,7 +295,19 @@ a submit element.
<td class="content">Do not delimit the second hash without doing so with the first hash, otherwise your method invocation will result in an <tt>expecting tASSOC</tt> syntax error.</td>
</tr></table>
</div>
<h3 id="_checkboxes_radio_buttons_and_other_controls">1.3. Checkboxes, radio buttons and other controls</h3>
<h3 id="_helpers_for_generating_form_elements">1.3. Helpers for generating form elements</h3>
<div class="paragraph"><p>Rails provides a series of helpers for generating form elements such as checkboxes, text fields, radio buttons and so. These basic helpers, with names ending in _tag such as <tt>text_field_tag</tt>, <tt>check_box_tag</tt> just generate a single <tt>&lt;input&gt;</tt> element. The first parameter to these is always the name of the input. In the controller, this name will be the key in the <tt>params</tt> hash used to get the value entered by the user. For example if the form contains</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= text_field_tag(:query) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>then the controller code should use</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>params[:query]</tt></pre>
</div></div>
<div class="paragraph"><p>to retrieve the value entered by the user. When naming inputs be aware that Rails uses certain conventions that control whether values are at the top level of the <tt>params</tt> hash, inside an array or a nested hash and so on. You can read more about them in the <a href="#parameter_names">parameter names</a> section. For details on the precise usage of these helpers, please refer to the <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html">API documentation</a>.</p></div>
<h4 id="_checkboxes">1.3.1. Checkboxes</h4>
<div class="paragraph"><p>Checkboxes are form controls that give the user a set of options they can enable or disable:</p></div>
<div class="listingblock">
<div class="content">
@ -310,6 +323,8 @@ output:
&lt;input id="pet_cat" name="pet_cat" type="checkbox" value="1" /&gt;
&lt;label for="pet_cat"&gt;I own a cat&lt;/label&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>The second parameter to <tt>check_box_tag</tt> is the value of the input. This is the value that will be submitted by the browser if the checkbox is ticked (i.e. the value that will be present in the <tt>params</tt> hash). With the above form you would check the value of <tt>params[:pet_dog]</tt> and <tt>params[:pet_cat]</tt> to see which pets the user owns.</p></div>
<h4 id="_radio_buttons">1.3.2. Radio buttons</h4>
<div class="paragraph"><p>Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (user can only pick one):</p></div>
<div class="listingblock">
<div class="content">
@ -325,6 +340,7 @@ output:
&lt;input id="age_adult" name="age" type="radio" value="adult" /&gt;
&lt;label for="age_adult"&gt;I'm over 21&lt;/label&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>As with <tt>check_box_tag</tt> the second parameter to <tt>radio_button_tag</tt> is the value of the input. Because these two radio buttons share the same name (age) the user will only be able to select one and <tt>params[:age]</tt> will contain either "child" or "adult".</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@ -333,7 +349,8 @@ output:
<td class="content">Always use labels for each checkbox and radio button. They associate text with a specific option and provide a larger clickable region.</td>
</tr></table>
</div>
<div class="paragraph"><p>Other form controls worth mentioning are the text area, password input and hidden input:</p></div>
<h4 id="_other_helpers_of_interest">1.3.3. Other helpers of interest</h4>
<div class="paragraph"><p>Other form controls worth mentioning are the text area, password input and hidden input:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= text_area_tag(:message, "Hi, nice site", :size =&gt; "24x6") %&gt;
@ -346,7 +363,7 @@ output:
&lt;input id="password" name="password" type="password" /&gt;
&lt;input id="parent_id" name="parent_id" type="hidden" value="5" /&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>Hidden inputs are not shown to the user, but they hold data same as any textual input. Values inside them can be changed with JavaScript.</p></div>
<div class="paragraph"><p>Hidden inputs are not shown to the user, but they hold data like any textual input. Values inside them can be changed with JavaScript.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@ -355,42 +372,12 @@ output:
<td class="content">If you&#8217;re using password input fields (for any purpose), you might want to prevent their values showing up in application logs by activating <tt>filter_parameter_logging(:password)</tt> in your ApplicationController.</td>
</tr></table>
</div>
<h3 id="_how_do_forms_with_put_or_delete_methods_work">1.4. How do forms with PUT or DELETE methods work?</h3>
<div class="paragraph"><p>Rails framework encourages RESTful design of your applications, which means you&#8217;ll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers <em>don&#8217;t support</em> methods other than "GET" and "POST" when it comes to submitting forms. How does this work, then?</p></div>
<div class="paragraph"><p>Rails works around this issue by emulating other methods over POST with a hidden input named <tt>"_method"</tt> that is set to reflect the desired method:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>form_tag(search_path, :method =&gt; "put")
output:
&lt;form action="/search" method="post"&gt;
&lt;div style="margin:0;padding:0"&gt;
&lt;input name="_method" type="hidden" value="put" /&gt;
&lt;input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /&gt;
&lt;/div&gt;
...</tt></pre>
</div></div>
<div class="paragraph"><p>When parsing POSTed data, Rails will take into account the special <tt>_method</tt> parameter and act as if the HTTP method was the one specified inside it ("PUT" in this example).</p></div>
</div>
<h2 id="_different_families_of_helpers">2. Different Families of helpers</h2>
<h2 id="_dealing_with_model_objects">2. Dealing With Model Objects</h2>
<div class="sectionbody">
<div class="paragraph"><p>Most of Rails' form helpers are available in two forms.</p></div>
<h3 id="_barebones_helpers">2.1. Barebones helpers</h3>
<div class="paragraph"><p>These just generate the appropriate markup. These have names ending in _tag such as <tt>text_field_tag</tt>, <tt>check_box_tag</tt>. The first parameter to these is always the name of the input. This is the name under which value will appear in the <tt>params</tt> hash in the controller. For example if the form contains</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= text_field_tag(:query) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>then the controller code should use</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>params[:query]</tt></pre>
</div></div>
<div class="paragraph"><p>to retrieve the value entered by the user. When naming inputs be aware that Rails uses certain conventions that control whether values appear at the top level of the params hash, inside an array or a nested hash and so on. You can read more about them in the <a href="#parameter_names">parameter names</a> section. For details on the precise usage of these helpers, please refer to the <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html">API documentation</a>.</p></div>
<h3 id="_model_object_helpers">2.2. Model object helpers</h3>
<div class="paragraph"><p>These are designed to work with a model object (commonly an Active Record object but this need not be the case). These lack the _tag suffix, for example <tt>text_field</tt>, <tt>text_area</tt>.</p></div>
<div class="paragraph"><p>For these helpers the first arguement is the name of an instance variable and the second is the name a method (usually an attribute) to call on that object. Rails will set the value of the input control to the return value of that method for the object and set an appropriate input name. If your controller has defined <tt>@person</tt> and that person&#8217;s name is Henry then a form containing:</p></div>
<h3 id="_model_object_helpers">2.1. Model object helpers</h3>
<div class="paragraph"><p>A particularly common task for a form is editing or creating a model object. While the <tt>*_tag</tt> helpers can certainly be used for this task they are somewhat verbose as for each tag you would have to ensure the correct parameter name is used and set the default value of the input appropriately. Rails provides helpers tailored to this task. These helpers lack the _tag suffix, for example <tt>text_field</tt>, <tt>text_area</tt>.</p></div>
<div class="paragraph"><p>For these helpers the first argument is the name of an instance variable and the second is the name of a method (usually an attribute) to call on that object. Rails will set the value of the input control to the return value of that method for the object and set an appropriate input name. If your controller has defined <tt>@person</tt> and that person&#8217;s name is Henry then a form containing:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= text_field(:person, :name) %&gt;</tt></pre>
@ -400,7 +387,7 @@ output:
<div class="content">
<pre><tt>&lt;input id="person_name" name="person[name]" type="text" value="Henry"/&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>Upon form submission the value entered by the user will be stored in <tt>params[:person][:name]</tt>. The <tt>params[:person]</tt> hash is suitable for passing to <tt>Person.new</tt> or, if <tt>@person</tt> is an instance of Person, <tt>@person.update_attributes</tt>.</p></div>
<div class="paragraph"><p>Upon form submission the value entered by the user will be stored in <tt>params[:person][:name]</tt>. The <tt>params[:person]</tt> hash is suitable for passing to <tt>Person.new</tt> or, if <tt>@person</tt> is an instance of Person, <tt>@person.update_attributes</tt>. While the name of an attribute is the most common second parameter to these helpers this is not compulsory. In the example above, as long as person objects have a <tt>name</tt> and a <tt>name=</tt> method Rails will be happy.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@ -411,10 +398,10 @@ output:
</td>
</tr></table>
</div>
</div>
<h2 id="_forms_that_deal_with_model_attributes">3. Forms that deal with model attributes</h2>
<div class="sectionbody">
<div class="paragraph"><p>While the helpers seen so far are handy Rails can save you some work. For example typically a form is used to edit multiple attributes of a single object, so having to repeat the name of the object being edited is clumsy. The following examples will handle an Article model. First, have the controller create one:</p></div>
<div class="paragraph"><p>Rails provides helpers for displaying the validation errors associated with a model object. These are covered in detail by the <a href="./activerecord_validations_callbacks.html#_using_the_tt_errors_tt_collection_in_your_view_templates">Active Record Validations and Callbacks</a> guide.</p></div>
<h3 id="_binding_a_form_to_an_object">2.2. Binding a form to an object</h3>
<div class="paragraph"><p>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 <tt>form_for</tt> does.</p></div>
<div class="paragraph"><p>Assume we have a controller for dealing with articles:</p></div>
<div class="listingblock">
<div class="title">articles_controller.rb</div>
<div class="content">
@ -422,7 +409,7 @@ output:
@article = Article.new
end</tt></pre>
</div></div>
<div class="paragraph"><p>Now switch to the view. The first thing to remember is to use the <tt>form_for</tt> helper instead of <tt>form_tag</tt>, and that you should pass the model name and object as arguments:</p></div>
<div class="paragraph"><p>The corresponding view using <tt>form_for</tt> looks like this</p></div>
<div class="listingblock">
<div class="title">articles/new.html.erb</div>
<div class="content">
@ -436,7 +423,7 @@ end</tt></pre>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
<tt>:article</tt> is the name of the model and <tt>@article</tt> is the record.
<tt>:article</tt> is the name of the model and <tt>@article</tt> is the actual object being edited.
</p>
</li>
<li>
@ -446,7 +433,7 @@ There is a single hash of options. Routing options are passed inside <tt>:url</t
</li>
<li>
<p>
The <tt>form_for</tt> method yields <strong>a form builder</strong> object (the <tt>f</tt> variable).
The <tt>form_for</tt> method yields a <strong>form builder</strong> object (the <tt>f</tt> variable).
</p>
</li>
<li>
@ -464,10 +451,29 @@ Methods to create form controls are called <strong>on</strong> the form builder
&lt;input name="commit" type="submit" value="Create" /&gt;
&lt;/form&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>The name passed to <tt>form_for</tt> controls where in the params hash the form values will appear. Here the name is <tt>article</tt> and so all the inputs have names of the form <tt>article[attribute_name]</tt>. Accordingly, in the <tt>create</tt> action <tt>params[:article]</tt> will be a hash with keys <tt>:title</tt> and <tt>:body</tt>. You can read more about the significance of input names in the <a href="#parameter_names">parameter names</a> section.</p></div>
<div class="paragraph"><p>The name passed to <tt>form_for</tt> controls the key used in <tt>params</tt> to access the form&#8217;s values. Here the name is <tt>article</tt> and so all the inputs have names of the form <tt>article[attribute_name]</tt>. Accordingly, in the <tt>create</tt> action <tt>params[:article]</tt> will be a hash with keys <tt>:title</tt> and <tt>:body</tt>. You can read more about the significance of input names in the <a href="#parameter_names">parameter names</a> section.</p></div>
<div class="paragraph"><p>The helper methods called on the form builder are identical to the model object helpers except that it is not necessary to specify which object is being edited since this is already managed by the form builder.</p></div>
<h3 id="_relying_on_record_identification">3.1. Relying on record identification</h3>
<div class="paragraph"><p>In the previous chapter you handled the Article model. This model is directly available to users of our application, so&#8201;&#8212;&#8201;following the best practices for developing with Rails&#8201;&#8212;&#8201;you should declare it <strong>a resource</strong>.</p></div>
<div class="paragraph"><p>You can create a similar binding without actually creating <tt>&lt;form&gt;</tt> tags with the <tt>fields_for</tt> helper. This is useful for editing additional model objects with the same form. For example if you had a Person model with an associated ContactDetail model you could create a form for creating both like so:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% form_for :person, @person, :url =&gt; { :action =&gt; "create" } do |person_form| %&gt;
&lt;%= person_form.text_field :name %&gt;
&lt;% fields_for @person.contact_detail do |contact_details_form| %&gt;
&lt;%= contact_details_form.text_field :phone_number %&gt;
&lt;% end %&gt;
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>which produces the following output:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;form action="/people/create" class="new_person" id="new_person" method="post"&gt;
&lt;input id="person_name" name="person[name]" size="30" type="text" /&gt;
&lt;input id="contact_detail_phone_number" name="contact_detail[phone_number]" size="30" type="text" /&gt;
&lt;/form&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>The object yielded by <tt>fields_for</tt> is a form builder like the one yielded by <tt>form_for</tt> (in fact <tt>form_for</tt> calls <tt>fields_for</tt> internally).</p></div>
<h3 id="_relying_on_record_identification">2.3. Relying on record identification</h3>
<div class="paragraph"><p>The Article model is directly available to users of the application, so&#8201;&#8212;&#8201;following the best practices for developing with Rails&#8201;&#8212;&#8201;you should declare it <strong>a resource</strong>.</p></div>
<div class="paragraph"><p>When dealing with RESTful resources, calls to <tt>form_for</tt> can get significantly easier if you rely on <strong>record identification</strong>. In short, you can just pass the model instance and have Rails figure out model name and the rest:</p></div>
<div class="listingblock">
<div class="content">
@ -484,7 +490,7 @@ form_for(:article, @article, :url =&gt; article_path(@article), :method =&gt; "p
form_for(@article)</tt></pre>
</div></div>
<div class="paragraph"><p>Notice how the short-style <tt>form_for</tt> invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking <tt>record.new_record?</tt>. It also selects the correct path to submit to and the name based on the class of the object.</p></div>
<div class="paragraph"><p>Rails will also automatically set the class and id of the form appropriately: a form creating an article would have id and class <tt>new_article</tt>. If you were editing the article with id 23 the class would be set to <tt>edit_article</tt> and the id to <tt>edit_article_23</tt>. The attributes will be omitted or brevity in the rest of this guide.</p></div>
<div class="paragraph"><p>Rails will also automatically set the <tt>class</tt> and <tt>id</tt> of the form appropriately: a form creating an article would have <tt>id</tt> and <tt>class</tt> <tt>new_article</tt>. If you were editing the article with id 23 the <tt>class</tt> would be set to <tt>edit_article</tt> and the id to <tt>edit_article_23</tt>. These attributes will be omitted for brevity in the rest of this guide.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@ -493,7 +499,7 @@ form_for(@article)</tt></pre>
<td class="content">When you&#8217;re using STI (single-table inheritance) with your models, you can&#8217;t rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, <tt>:url</tt> and <tt>:method</tt> explicitly.</td>
</tr></table>
</div>
<h4 id="_dealing_with_namespaces">3.1.1. Dealing with namespaces</h4>
<h4 id="_dealing_with_namespaces">2.3.1. Dealing with namespaces</h4>
<div class="paragraph"><p>If you have created namespaced routes <tt>form_for</tt> has a nifty shorthand for that too. If your application has an admin namespace then</p></div>
<div class="listingblock">
<div class="content">
@ -504,80 +510,96 @@ form_for(@article)</tt></pre>
<div class="content">
<pre><tt>form_for [:admin, :management, @article]</tt></pre>
</div></div>
<div class="paragraph"><p>For more information on Rails' routing system and the associated conventions, please see the <a href="../routing_outside_in.html">routing guide</a>.</p></div>
<div class="paragraph"><p>For more information on Rails' routing system and the associated conventions, please see the <a href="./routing_outside_in.html">routing guide</a>.</p></div>
<h3 id="_how_do_forms_with_put_or_delete_methods_work">2.4. How do forms with PUT or DELETE methods work?</h3>
<div class="paragraph"><p>Rails framework encourages RESTful design of your applications, which means you&#8217;ll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers <em>don&#8217;t support</em> methods other than "GET" and "POST" when it comes to submitting forms.</p></div>
<div class="paragraph"><p>Rails works around this issue by emulating other methods over POST with a hidden input named <tt>"_method"</tt> that is set to reflect the desired method:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>form_tag(search_path, :method =&gt; "put")
output:
&lt;form action="/search" method="post"&gt;
&lt;div style="margin:0;padding:0"&gt;
&lt;input name="_method" type="hidden" value="put" /&gt;
&lt;input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /&gt;
&lt;/div&gt;
...</tt></pre>
</div></div>
<div class="paragraph"><p>When parsing POSTed data, Rails will take into account the special <tt>_method</tt> parameter and acts as if the HTTP method was the one specified inside it ("PUT" in this example).</p></div>
</div>
<h2 id="_making_select_boxes_with_ease">4. Making select boxes with ease</h2>
<h2 id="_making_select_boxes_with_ease">3. Making select boxes with ease</h2>
<div class="sectionbody">
<div class="paragraph"><p>Select boxes in HTML require a significant amount of markup (one <tt>OPTION</tt> element for each option to choose from), therefore it makes the most sense for them to be dynamically generated from data stored in arrays or hashes.</p></div>
<div class="paragraph"><p>Here is what our wanted markup might look like:</p></div>
<div class="paragraph"><p>Select boxes in HTML require a significant amount of markup (one <tt>OPTION</tt> element for each option to choose from), therefore it makes the most sense for them to be dynamically generated.</p></div>
<div class="paragraph"><p>Here is what the markup might look like:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;select name="city_id" id="city_id"&gt;
&lt;option value="1"&gt;Lisabon&lt;/option&gt;
&lt;option value="1"&gt;Lisbon&lt;/option&gt;
&lt;option value="2"&gt;Madrid&lt;/option&gt;
...
&lt;option value="12"&gt;Berlin&lt;/option&gt;
&lt;/select&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>Here you have a list of cities where their names are presented to the user, but internally the application only wants to handle their IDs so they are used as the options' value attributes. Let&#8217;s see how Rails can help out here.</p></div>
<h3 id="_the_select_tag_and_options">4.1. The select tag and options</h3>
<div class="paragraph"><p>Here you have a list of cities whose names are presented to the user. Internally the application only wants to handle their IDs so they are used as the options' value attribute. Let&#8217;s see how Rails can help out here.</p></div>
<h3 id="_the_select_and_options_tag">3.1. The select and options tag</h3>
<div class="paragraph"><p>The most generic helper is <tt>select_tag</tt>, which&#8201;&#8212;&#8201;as the name implies&#8201;&#8212;&#8201;simply generates the <tt>SELECT</tt> tag that encapsulates an options string:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= select_tag(:city_id, '&lt;option value="1"&gt;Lisabon&lt;/option&gt;...') %&gt;</tt></pre>
<pre><tt>&lt;%= select_tag(:city_id, '&lt;option value="1"&gt;Lisbon&lt;/option&gt;...') %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>This is a start, but it doesn&#8217;t dynamically create our option tags. You can generate option tags with the <tt>options_for_select</tt> helper:</p></div>
<div class="paragraph"><p>This is a start, but it doesn&#8217;t dynamically create the option tags. You can generate option tags with the <tt>options_for_select</tt> helper:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %&gt;
<pre><tt>&lt;%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %&gt;
output:
&lt;option value="1"&gt;Lisabon&lt;/option&gt;
&lt;option value="1"&gt;Lisbon&lt;/option&gt;
&lt;option value="2"&gt;Madrid&lt;/option&gt;
...</tt></pre>
</div></div>
<div class="paragraph"><p>For input data you use a nested array where each element has two elements: option text (city name) and option value (city id). The option value is what will get submitted to your controller. It is often true that the option value is the id of a corresponding database object but this does not have to be the case.</p></div>
<div class="paragraph"><p>The first argument to <tt>options_for_select</tt> is a nested array where each element has two elements: option text (city name) and option value (city id). The option value is what will be submitted to your controller. Often this will be the id of a corresponding database object but this does not have to be the case.</p></div>
<div class="paragraph"><p>Knowing this, you can combine <tt>select_tag</tt> and <tt>options_for_select</tt> to achieve the desired, complete markup:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= select_tag(:city_id, options_for_select(...)) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>Sometimes, depending on an application&#8217;s needs, you also wish a specific option to be pre-selected. The <tt>options_for_select</tt> helper supports this with an optional second argument:</p></div>
<div class="paragraph"><p><tt>options_for_select</tt> allows you to pre-select an option by passing its value.</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...], 2) %&gt;
<pre><tt>&lt;%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...], 2) %&gt;
output:
&lt;option value="1"&gt;Lisabon&lt;/option&gt;
&lt;option value="1"&gt;Lisbon&lt;/option&gt;
&lt;option value="2" selected="selected"&gt;Madrid&lt;/option&gt;
...</tt></pre>
</div></div>
<div class="paragraph"><p>So whenever Rails sees that the internal value of an option being generated matches this value, it will add the <tt>selected</tt> attribute to that option.</p></div>
<div class="paragraph"><p>Whenever Rails sees that the internal value of an option being generated matches this value, it will add the <tt>selected</tt> attribute to that option.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/tip.png" alt="Tip" />
</td>
<td class="content">
<div class="paragraph"><p>The second argument to <tt>options_for_select</tt> must be exactly equal to the desired internal value. In particular if the internal value is the integer 2 you cannot pass "2" to <tt>options_for_select</tt>&#8201;&#8212;&#8201;you must pass 2. Be aware of values extracted from the params hash as they are all strings.</p></div>
<div class="paragraph"><p>The second argument to <tt>options_for_select</tt> must be exactly equal to the desired internal value. In particular if the value is the integer 2 you cannot pass "2" to <tt>options_for_select</tt>&#8201;&#8212;&#8201;you must pass 2. Be aware of values extracted from the <tt>params</tt> hash as they are all strings.</p></div>
</td>
</tr></table>
</div>
<h3 id="_select_boxes_for_dealing_with_models">4.2. Select boxes for dealing with models</h3>
<div class="paragraph"><p>Until now you&#8217;ve seen how to make generic select boxes, but in most cases our form controls will be tied to a specific database model. So, to continue from our previous examples, let&#8217;s assume that you have a "Person" model with a <tt>city_id</tt> attribute.</p></div>
<div class="paragraph"><p>Consistent with other form helpers, when dealing with models you drop the <tt>_tag</tt> suffix from <tt>select_tag</tt>.</p></div>
<h3 id="_select_boxes_for_dealing_with_models">3.2. Select boxes for dealing with models</h3>
<div class="paragraph"><p>In most cases form controls will be tied to a specific database model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with models you drop the <tt>_tag</tt> suffix from <tt>select_tag</tt>:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt># controller:
@person = Person.new(:city_id =&gt; 2)
# view:
&lt;%= select(:person, :city_id, [['Lisabon', 1], ['Madrid', 2], ...]) %&gt;</tt></pre>
&lt;%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>Notice that the third parameter, the options array, is the same kind of argument you pass to <tt>options_for_select</tt>. One advantage here is that you don&#8217;t have to worry about pre-selecting the correct city if the user already has one&#8201;&#8212;&#8201;Rails will do this for you by reading from the <tt>@person.city_id</tt> attribute.</p></div>
<div class="paragraph"><p>As before, if you were to use <tt>select</tt> helper on a form builder scoped to <tt>@person</tt> object, the syntax would be:</p></div>
<div class="paragraph"><p>As with other helpers, if you were to use <tt>select</tt> helper on a form builder scoped to <tt>@person</tt> object, the syntax would be:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt># select on a form builder
@ -589,25 +611,22 @@ output:
<img src="./images/icons/warning.png" alt="Warning" />
</td>
<td class="content">
<div class="paragraph"><p>If you are using <tt>select</tt> (or similar helpers such as <tt>collection_select</tt>, <tt>select_tag</tt>) to set a <tt>belongs_to</tt> association you must pass the name of the foreign key (in the example above <tt>city_id</tt>), not the name of association itself. If you specify <tt>city</tt> instead of `city_id Active Record will raise an error along the lines of</p></div>
<div class="paragraph"><p>If you are using <tt>select</tt> (or similar helpers such as <tt>collection_select</tt>, <tt>select_tag</tt>) to set a <tt>belongs_to</tt> association you must pass the name of the foreign key (in the example above <tt>city_id</tt>), not the name of association itself.</p></div>
<div class="paragraph"><p>If you specify <tt>city</tt> instead of <tt>city_id</tt> Active Record will raise an error along the lines of</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got Fixnum(#1138750)</tt></pre>
<pre><tt>ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750)</tt></pre>
</div></div>
<div class="paragraph"><p>when you pass the params hash to <tt>Person.new</tt> or <tt>update_attributes</tt>. Another way of looking at this is that form helpers only edit attributes.</p></div>
<div class="paragraph"><p>when you pass the <tt>params</tt> hash to <tt>Person.new</tt> or <tt>update_attributes</tt>. Another way of looking at this is that form helpers only edit attributes.</p></div>
<div class="paragraph"><p>You should also be aware of the potential security ramifications of allowing users to edit foreign keys directly. You may wish to consider the use of <tt>attr_protected</tt> and <tt>attr_accessible</tt>. For further details on this, see the <a href="security.html#_mass_assignment">Ruby On Rails Security Guide</a>.</p></div>
</td>
</tr></table>
</div>
<h3 id="_option_tags_from_a_collection_of_arbitrary_objects">4.3. Option tags from a collection of arbitrary objects</h3>
<div class="paragraph"><p>Until now you were generating option tags from nested arrays with the help of <tt>options_for_select</tt> method. Data in our array were raw values:</p></div>
<h3 id="_option_tags_from_a_collection_of_arbitrary_objects">3.3. Option tags from a collection of arbitrary objects</h3>
<div class="paragraph"><p>Generating options tags with <tt>options_for_select</tt> requires that you create an array containing the text and value for each option. But what if you had a City model (perhaps an Active Record one) and you wanted to generate option tags from a collection of those objects? One solution would be to make a nested array by iterating over them:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>But what if you had a <strong>City</strong> model (perhaps an Active Record one) and you wanted to generate option tags from a collection of those objects? One solution would be to make a nested array by iterating over them:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% cities_array = City.find(:all).map { |city| [city.name, city.id] } %&gt;
<pre><tt>&lt;% cities_array = City.all.map { |city| [city.name, city.id] } %&gt;
&lt;%= options_for_select(cities_array) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>This is a perfectly valid solution, but Rails provides a less verbose alternative: <tt>options_from_collection_for_select</tt>. This helper expects a collection of arbitrary objects and two additional arguments: the names of the methods to read the option <strong>value</strong> and <strong>text</strong> from, respectively:</p></div>
@ -615,77 +634,87 @@ output:
<div class="content">
<pre><tt>&lt;%= options_from_collection_for_select(City.all, :id, :name) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>As the name implies, this only generates option tags. To generate a working select box you would need to use it in conjunction with <tt>select_tag</tt>, just as you would with <tt>options_for_select</tt>. A method to go along with it is <tt>collection_select</tt>:</p></div>
<div class="paragraph"><p>As the name implies, this only generates option tags. To generate a working select box you would need to use it in conjunction with <tt>select_tag</tt>, just as you would with <tt>options_for_select</tt>. When working with model objects, just as <tt>select</tt> combines <tt>select_tag</tt> and <tt>options_for_select</tt>, <tt>collection_select</tt> combines <tt>select_tag</tt> with <tt>options_from_collection_for_select</tt>.</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= collection_select(:person, :city_id, City.all, :id, :name) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>To recap, <tt>options_from_collection_for_select</tt> is to <tt>collection_select</tt> what <tt>options_for_select</tt> is to <tt>select</tt>.</p></div>
<h3 id="_time_zone_and_country_select">4.4. Time zone and country select</h3>
<div class="paragraph"><p>To leverage time zone support in Rails, you have to ask our users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using <tt>collection_select</tt>, but you can simply use the <tt>time_zone_select</tt> helper that already wraps this:</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">
<div class="paragraph"><p>Pairs passed to <tt>options_for_select</tt> should have the name first and the id second, however with <tt>options_from_collection_for_select</tt> the first argument is the value method and the second the text method.</p></div>
</td>
</tr></table>
</div>
<h3 id="_time_zone_and_country_select">3.4. Time zone and country select</h3>
<div class="paragraph"><p>To leverage time zone support in Rails, you have to ask your users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using <tt>collection_select</tt>, but you can simply use the <tt>time_zone_select</tt> helper that already wraps this:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= time_zone_select(:person, :city_id) %&gt;</tt></pre>
<pre><tt>&lt;%= time_zone_select(:person, :time_zone) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>There is also <tt>time_zone_options_for_select</tt> 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.</p></div>
<div class="paragraph"><p>Rails <em>used</em> to have a <tt>country_select</tt> helper for choosing countries but this has been extracted to the <a href="http://github.com/rails/country_select/tree/master">country_select plugin</a>. When using this do 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)</p></div>
<div class="paragraph"><p>Rails <em>used</em> to have a <tt>country_select</tt> helper for choosing countries but this has been extracted to the <a href="http://github.com/rails/country_select/tree/master">country_select plugin</a>. When using this do 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).</p></div>
</div>
<h2 id="_date_and_time_select_boxes">5. Date and time select boxes</h2>
<h2 id="_using_date_and_time_form_helpers">4. Using Date and Time Form Helpers</h2>
<div class="sectionbody">
<div class="paragraph"><p>The date and time helpers differ from all the other form helpers in two important respects:</p></div>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Unlike other attributes you might typically have, dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc...). So in particular, there is no single value in your params hash with your date or time.
Dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc.) and so there is no single value in your <tt>params</tt> hash with your date or time.
</p>
</li>
<li>
<p>
Other helpers use the _tag suffix to indicate whether a helper is a barebones helper or one that operates on model objects. With dates and times, <tt>select\_date</tt>, <tt>select\_time</tt> and <tt>select_datetime</tt> are the barebones helpers, <tt>date_select</tt>, <tt>time_select</tt> and <tt>datetime_select</tt> are the equivalent model object helpers
Other helpers use the _tag suffix to indicate whether a helper is a barebones helper or one that operates on model objects. With dates and times, <tt>select_date</tt>, <tt>select_time</tt> and <tt>select_datetime</tt> are the barebones helpers, <tt>date_select</tt>, <tt>time_select</tt> and <tt>datetime_select</tt> are the equivalent model object helpers.
</p>
</li>
</ol></div>
<div class="paragraph"><p>Both of these families of helpers will create a series of select boxes for the different components (year, month, day etc...).</p></div>
<h3 id="_barebones_helpers_2">5.1. Barebones helpers</h3>
<div class="paragraph"><p>Both of these families of helpers will create a series of select boxes for the different components (year, month, day etc.).</p></div>
<h3 id="_barebones_helpers">4.1. Barebones helpers</h3>
<div class="paragraph"><p>The <tt>select_*</tt> family of helpers take as their first argument an instance of Date, Time or DateTime that is used as the currently selected value. You may omit this parameter, in which case the current date is used. For example</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= select_date Date::today, :prefix =&gt; :start_date %&gt;</tt></pre>
<pre><tt>&lt;%= select_date Date.today, :prefix =&gt; :start_date %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>outputs (with the actual option values omitted for brevity)</p></div>
<div class="paragraph"><p>outputs (with actual option values omitted for brevity)</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;select id="start_date_year" name="start_date[year]"&gt; ... &lt;/select&gt;
&lt;select id="start_date_month" name="start_date[month]"&gt; ... &lt;/select&gt;
&lt;select id="start_date_day" name="start_date[day]"&gt; ... &lt;/select&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>The above inputs would result in <tt>params[:start_date]</tt> being a hash with keys :year, :month, :day. To get an actual Time or Date object you would have to extract these values and pass them to the appropriate constructor, for example</p></div>
<div class="paragraph"><p>The above inputs would result in <tt>params[:start_date]</tt> being a hash with keys <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>. To get an actual Time or Date object you would have to extract these values and pass them to the appropriate constructor, for example</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>Date::civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)</tt></pre>
<pre><tt>Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)</tt></pre>
</div></div>
<div class="paragraph"><p>The :prefix option controls where in the params hash the date components will be placed. Here it was set to <tt>start_date</tt>, if omitted it will default to <tt>date</tt>.</p></div>
<h3 id="_model_object_helpers_2">5.2. Model object helpers</h3>
<div class="paragraph"><p><tt>select_date</tt> does not work well with forms that update or create Active Record objects as Active Record expects each element of the params hash to correspond to one attribute.
The model object helpers for dates and times submit parameters with special names. When Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type. For example</p></div>
<div class="paragraph"><p>The <tt>:prefix</tt> option is the key used to retrieve the hash of date components from the <tt>params</tt> hash. Here it was set to <tt>start_date</tt>, if omitted it will default to <tt>date</tt>.</p></div>
<h3 id="_model_object_helpers_2">4.2. Model object helpers</h3>
<div class="paragraph"><p><tt>select_date</tt> does not work well with forms that update or create Active Record objects as Active Record expects each element of the <tt>params</tt> hash to correspond to one attribute.
The model object helpers for dates and times submit parameters with special names, when Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type. For example:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= date_select :person, :birth_date %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>outputs (with the actual option values omitted for brevity)</p></div>
<div class="paragraph"><p>outputs (with actual option values omitted for brevity)</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;select id="person_birth_date_1i" name="person[birth_date(1i)]"&gt; ... &lt;/select&gt;
&lt;select id="person_birth_date_2i" name="person[birth_date(2i)]"&gt; ... &lt;/select&gt;
&lt;select id="person_birth_date_3i" name="person[birth_date(3i)]"&gt; ... &lt;/select&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>which results in a params hash like</p></div>
<div class="paragraph"><p>which results in a <tt>params</tt> hash like</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>{:person =&gt; {'birth_date(1i)' =&gt; '2008', 'birth_date(2i)' =&gt; '11', 'birth_date(3i)' =&gt; '22'}}</tt></pre>
</div></div>
<div class="paragraph"><p>When this is passed to <tt>Person.new</tt>, Active Record spots that these parameters should all be used to construct the <tt>birth_date</tt> attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as <tt>Date::civil</tt>.</p></div>
<h3 id="_common_options">5.3. Common options</h3>
<div class="paragraph"><p>When this is passed to <tt>Person.new</tt> (or <tt>update_attributes</tt>), Active Record spots that these parameters should all be used to construct the <tt>birth_date</tt> attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as <tt>Date.civil</tt>.</p></div>
<h3 id="_common_options">4.3. Common options</h3>
<div class="paragraph"><p>Both families of helpers use the same core set of functions to generate the individual select tags and so both accept largely the same options. In particular, by default Rails will generate year options 5 years either side of the current year. If this is not an appropriate range, the <tt>:start_year</tt> and <tt>:end_year</tt> options override this. For an exhaustive list of the available options, refer to the <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html">API documentation</a>.</p></div>
<div class="paragraph"><p>As a rule of thumb you should be using <tt>date_select</tt> when working with model objects and <tt>select_date</tt> in others cases, such as a search form which filters results by date.</p></div>
<div class="admonitionblock">
@ -696,64 +725,19 @@ The model object helpers for dates and times submit parameters with special name
<td class="content">In many cases the built in date pickers are clumsy as they do not aid the user in working out the relationship between the date and the day of the week.</td>
</tr></table>
</div>
<h3 id="_individual_components">4.4. Individual components</h3>
<div class="paragraph"><p>Occasionally you need to display just a single date component such as a year or a month. Rails provides a series of helpers for this, one for each component <tt>select_year</tt>, <tt>select_month</tt>, <tt>select_day</tt>, <tt>select_hour</tt>, <tt>select_minute</tt>, <tt>select_second</tt>. These helpers are fairly straightforward. By default they will generate a input named after the time component (for example "year" for <tt>select_year</tt>, "month" for <tt>select_month</tt> etc.) although this can be overriden with the <tt>:field_name</tt> option. The <tt>:prefix</tt> option works in the same way that it does for <tt>select_date</tt> and <tt>select_time</tt> and has the same default value.</p></div>
<div class="paragraph"><p>The first parameter specifies which value should be selected and can either be an instance of a Date, Time or DateTime, in which case the relevant component will be extracted, or a numerical value. For example</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= select_year(2009) %&gt;
&lt;%= select_year(Time.now) %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>will produce the same output if the current year is 2009 and the value chosen by the user can be retrieved by <tt>params[:date][:year]</tt>.</p></div>
</div>
<h2 id="_form_builders">6. Form builders</h2>
<h2 id="_uploading_files">5. Uploading Files</h2>
<div class="sectionbody">
<div class="paragraph"><p>As mentioned previously the object yielded by <tt>form_for</tt> and <tt>fields_for</tt> is an instance of FormBuilder (or a subclass thereof). Form builders encapsulate the notion of displaying a 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</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% form_for @person do |f| %&gt;
&lt;%= text_field_with_label f, :first_name %&gt;
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>can be replaced with</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% form_for @person, :builder =&gt; LabellingFormBuilder do |f| %&gt;
&lt;%= f.text_field :first_name %&gt;
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>by defining a LabellingFormBuilder class similar to the following:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LabellingFormBuilder <span style="color: #990000">&lt;</span> FormBuilder
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> text_field attribute<span style="color: #990000">,</span> options<span style="color: #990000">=</span><span style="color: #FF0000">{}</span>
label<span style="color: #990000">(</span>attribute<span style="color: #990000">)</span> <span style="color: #990000">+</span> text_field<span style="color: #990000">(</span>attribute<span style="color: #990000">,</span> options<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>If you reuse this frequently you could define a <tt>labeled_form_for</tt> helper that automatically applies the <tt>:builder =&gt; LabellingFormBuilder</tt> option.</p></div>
<div class="paragraph"><p>The form builder used also determines what happens when you do</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= render :partial =&gt; f %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>If <tt>f</tt> is an instance of FormBuilder then this will render the <em>form</em> partial, setting the partial&#8217;s object to the form builder. If the form builder is of class LabellingFormBuilder then the <em>labelling_form</em> partial would be rendered instead.</p></div>
<h3 id="_scoping_out_form_controls_with_tt_fields_for_tt">6.1. Scoping out form controls with <tt>fields_for</tt></h3>
<div class="paragraph"><p><tt>fields_for</tt> creates a form builder in exactly the same way as <tt>form_for</tt> but doesn&#8217;t create the actual <tt>&lt;form&gt;</tt> tags. It creates a scope around a specific model object like <tt>form_for</tt>, which is useful for specifying additional model objects in the same form. For example if you had a Person model with an associated ContactDetail model you could create a form for editing both like so:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% form_for @person do |person_form| %&gt;
&lt;%= person_form.text_field :name %&gt;
&lt;% fields_for @person.contact_detail do |contact_details_form| %&gt;
&lt;%= contact_details_form.text_field :phone_number %&gt;
&lt;% end %&gt;
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>which produces the following output:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;form action="/people/1" class="edit_person" id="edit_person_1" method="post"&gt;
&lt;input id="person_name" name="person[name]" size="30" type="text" /&gt;
&lt;input id="contact_detail_phone_number" name="contact_detail[phone_number]" size="30" type="text" /&gt;
&lt;/form&gt;</tt></pre>
</div></div>
</div>
<h2 id="_file_uploads">7. File Uploads</h2>
<div class="sectionbody">
<div class="paragraph"><p>A common task is uploading some sort of file, whether it&#8217;s a picture of a person or a CSV file containing data to process. The most important thing to remember with file uploads is that the form&#8217;s encoding <strong>MUST</strong> be set to multipart/form-data. If you forget to do this the file will not be uploaded. This can be done by passing <tt>:multi_part =&gt; true</tt> as an HTML option. This means that in the case of <tt>form_tag</tt> it must be passed in the second options hash and in the case of <tt>form_for</tt> inside the <tt>:html</tt> hash.</p></div>
<div class="paragraph"><p>A common task is uploading some sort of file, whether it&#8217;s a picture of a person or a CSV file containing data to process. The most important thing to remember with file uploads is that the form&#8217;s encoding <strong>MUST</strong> be set to "multipart/form-data". If you forget to do this the file will not be uploaded. This can be done by passing <tt>:multi_part =&gt; true</tt> as an HTML option. This means that in the case of <tt>form_tag</tt> it must be passed in the second options hash and in the case of <tt>form_for</tt> inside the <tt>:html</tt> hash.</p></div>
<div class="paragraph"><p>The following two forms both upload a file.</p></div>
<div class="listingblock">
<div class="content">
@ -766,8 +750,8 @@ http://www.gnu.org/software/src-highlite -->
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>Rails provides the usual pair of helpers: the barebones <tt>file_field_tag</tt> and the model oriented <tt>file_field</tt>. The only difference with other helpers is that you cannot set a default value for file inputs as this would have no meaning. As you would expect in the first case the uploaded file is in <tt>params[:picture]</tt> and in the second case in <tt>params[:person][:picture]</tt>.</p></div>
<h3 id="_what_gets_uploaded">7.1. What gets uploaded</h3>
<div class="paragraph"><p>The object in the params hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file. In both cases the object will have an <tt>original_filename</tt> attribute containing the name the file had on the user&#8217;s computer and a <tt>content_type</tt> attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in <tt>#{RAILS_ROOT}/public/uploads</tt> under the same name as the original file (assuming the form was the one in the previous example).</p></div>
<h3 id="_what_gets_uploaded">5.1. What gets uploaded</h3>
<div class="paragraph"><p>The object in the <tt>params</tt> hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file. In both cases the object will have an <tt>original_filename</tt> attribute containing the name the file had on the user&#8217;s computer and a <tt>content_type</tt> attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in <tt>#{Rails.root}/public/uploads</tt> under the same name as the original file (assuming the form was the one in the previous example).</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -788,14 +772,49 @@ http://www.gnu.org/software/src-highlite -->
<td class="content">If the user has not selected a file the corresponding parameter will be an empty string.</td>
</tr></table>
</div>
<h3 id="_dealing_with_ajax">7.2. Dealing with Ajax</h3>
<div class="paragraph"><p>Unlike other forms making an asynchronous file upload form is not as simple as replacing <tt>form_for</tt> with <tt>remote_form_for</tt>. 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.</p></div>
<h3 id="_dealing_with_ajax">5.2. Dealing with Ajax</h3>
<div class="paragraph"><p>Unlike other forms making an asynchronous file upload form is not as simple as replacing <tt>form_for</tt> with <tt>remote_form_for</tt>. 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.</p></div>
</div>
<h2 id="_parameter_names">8. Parameter Names</h2>
<h2 id="_customising_form_builders">6. Customising Form Builders</h2>
<div class="sectionbody">
<div class="paragraph" id="parameter_names"><p>As you&#8217;ve seen in the previous sections values from forms can appear either at the top level of the params hash or may appear nested in another hash. For example in a standard create
action for a Person model, <tt>params[:model]</tt> would usually be a hash of all the attributes for the person to create. The params hash can also contain arrays, arrays of hashes and so on.</p></div>
<div class="paragraph"><p>Fundamentally HTML forms don&#8217;t know about any sort of structured data. All they know about is name-value pairs. Rails tacks some conventions onto parameter names which it uses to express some structure.</p></div>
<div class="paragraph"><p>As mentioned previously the object yielded by <tt>form_for</tt> and <tt>fields_for</tt> 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</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% form_for @person do |f| %&gt;
&lt;%= text_field_with_label f, :first_name %&gt;
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>can be replaced with</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% form_for @person, :builder =&gt; LabellingFormBuilder do |f| %&gt;
&lt;%= f.text_field :first_name %&gt;
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>by defining a LabellingFormBuilder class similar to the following:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LabellingFormBuilder <span style="color: #990000">&lt;</span> FormBuilder
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> text_field<span style="color: #990000">(</span>attribute<span style="color: #990000">,</span> options<span style="color: #990000">=</span><span style="color: #FF0000">{}</span><span style="color: #990000">)</span>
label<span style="color: #990000">(</span>attribute<span style="color: #990000">)</span> <span style="color: #990000">+</span> text_field<span style="color: #990000">(</span>attribute<span style="color: #990000">,</span> options<span style="color: #990000">)</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>If you reuse this frequently you could define a <tt>labeled_form_for</tt> helper that automatically applies the <tt>:builder =&gt; LabellingFormBuilder</tt> option.</p></div>
<div class="paragraph"><p>The form builder used also determines what happens when you do</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;%= render :partial =&gt; f %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>If <tt>f</tt> is an instance of FormBuilder then this will render the <tt>form</tt> partial, setting the partial&#8217;s object to the form builder. If the form builder is of class LabellingFormBuilder then the <tt>labelling_form</tt> partial would be rendered instead.</p></div>
</div>
<h2 id="_understanding_parameter_naming_conventions">7. Understanding Parameter Naming Conventions</h2>
<div class="sectionbody">
<div class="paragraph" id="parameter_names"><p>As you&#8217;ve seen in the previous sections, values from forms can be at the top level of the <tt>params</tt> hash or nested in another hash. For example in a standard <tt>create</tt>
action for a Person model, <tt>params[:model]</tt> would usually be a hash of all the attributes for the person to create. The <tt>params</tt> hash can also contain arrays, arrays of hashes and so on.</p></div>
<div class="paragraph"><p>Fundamentally HTML forms don&#8217;t know about any sort of structured data, all they generate is name-value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@ -805,38 +824,34 @@ action for a Person model, <tt>params[:model]</tt> would usually be a hash of al
<div class="paragraph"><p>You may find you can try out examples in this section faster by using the console to directly invoke Rails' parameter parser. For example</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>ActionController::RequestParser.parse_query_parameters "name=fred&amp;phone=0123456789"
#=&gt; {"name"=&gt;"fred", "phone"=&gt;"0123456789"}</tt></pre>
<pre><tt>ActionController::UrlEncodedPairParser.parse_query_parameters "name=fred&amp;phone=0123456789"
# =&gt; {"name"=&gt;"fred", "phone"=&gt;"0123456789"}</tt></pre>
</div></div>
</td>
</tr></table>
</div>
<h3 id="_basic_structures">8.1. Basic structures</h3>
<div class="paragraph"><p>The two basic structures are arrays and hashes. Hashes mirror the syntax used for accessing the value in the params. For example if a form contains</p></div>
<h3 id="_basic_structures">7.1. Basic structures</h3>
<div class="paragraph"><p>The two basic structures are arrays and hashes. Hashes mirror the syntax used for accessing the value in <tt>params</tt>. For example if a form contains</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;input id="person_name" name="person[name]" type="text" value="Henry"/&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>the params hash will contain</p></div>
<div class="paragraph"><p>the <tt>params</tt> hash will contain</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="color: #FF0000">{</span><span style="color: #FF0000">'person'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'name'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Henry'</span><span style="color: #FF0000">}}</span></tt></pre></div></div>
<div class="content">
<pre><tt>{'person' =&gt; {'name' =&gt; 'Henry'}}</tt></pre>
</div></div>
<div class="paragraph"><p>and <tt>params["name"]</tt> will retrieve the submitted value in the controller.</p></div>
<div class="paragraph"><p>Hashes can be nested as many levels as required, for example</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;input id="person_address_city" name="person[address][city]" type="text" value="New York"/&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>will result in the params hash being</p></div>
<div class="paragraph"><p>will result in the <tt>params</tt> hash being</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="color: #FF0000">{</span><span style="color: #FF0000">'person'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'address'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'city'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'New York'</span><span style="color: #FF0000">}}}</span></tt></pre></div></div>
<div class="content">
<pre><tt>{'person' =&gt; {'address' =&gt; {'city' =&gt; 'New York'}}}</tt></pre>
</div></div>
<div class="paragraph"><p>Normally Rails ignores duplicate parameter names. If the parameter name contains [] then they will be accumulated in an array. If you wanted people to be able to input multiple phone numbers, your could place this in the form:</p></div>
<div class="listingblock">
<div class="content">
@ -845,7 +860,7 @@ http://www.gnu.org/software/src-highlite -->
&lt;input name="person[phone_number][]" type="text"/&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>This would result in <tt>params[:person][:phone_number]</tt> being an array.</p></div>
<h3 id="_combining_them">8.2. Combining them</h3>
<h3 id="_combining_them">7.2. Combining them</h3>
<div class="paragraph"><p>We can mix and match these two concepts. For example, one element of a hash might be an array as in the previous example, or you can have an array of hashes. For example a form might let you create any number of addresses by repeating the following form fragment</p></div>
<div class="listingblock">
<div class="content">
@ -853,23 +868,23 @@ http://www.gnu.org/software/src-highlite -->
&lt;input name="addresses[][line2]" type="text"/&gt;
&lt;input name="addresses[][city]" type="text"/&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>This would result in <tt>params[:addresses]</tt> being an array of hashes with keys <tt>line1</tt>, <tt>line2</tt> and <tt>city</tt>. Rails decides to start accumulating values in a new hash whenever it encounters a input name that already exists in the current hash.</p></div>
<div class="paragraph"><p>The one restriction is that although hashes can be nested arbitrarily deep then can be only one level of "arrayness". Frequently arrays can be usually replaced by hashes, for example instead of having an array of model objects one can have a hash of model objects keyed by their id.</p></div>
<div class="paragraph"><p>This would result in <tt>params[:addresses]</tt> being an array of hashes with keys <tt>line1</tt>, <tt>line2</tt> and <tt>city</tt>. Rails decides to start accumulating values in a new hash whenever it encounters an input name that already exists in the current hash.</p></div>
<div class="paragraph"><p>There&#8217;s a restriction, however, while hashes can be nested arbitrarily, only one level of "arrayness" is allowed. Arrays can be usually replaced by hashes, for example instead of having an array of model objects one can have a hash of model objects keyed by their id, an array index or some other parameter.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/warning.png" alt="Warning" />
</td>
<td class="content">Array parameters do not play well with the <tt>check_box</tt> helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The <tt>check_box</tt> helper fakes this by creating a second hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted. If the checkbox is checked then both are submitted but the value submitted by the checkbox takes precedence. When working with array parameters this duplicate submission will confuse Rails since duplicate input names are how it decides when to start a new hash. It is preferable to either use <tt>check_box_tag</tt> or to use hashes instead of arrays.</td>
<td class="content">Array parameters do not play well with the <tt>check_box</tt> helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The <tt>check_box</tt> helper fakes this by creating a second hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted and if it is checked then both are submitted but the value submitted by the checkbox takes precedence. When working with array parameters this duplicate submission will confuse Rails since duplicate input names are how it decides when to start a new array element. It is preferable to either use <tt>check_box_tag</tt> or to use hashes instead of arrays.</td>
</tr></table>
</div>
<h3 id="_using_form_helpers">8.3. Using form helpers</h3>
<div class="paragraph"><p>The previous sections did not use the Rails form helpers at all. While you can craft the input names yourself and pass them directly to helpers such as <tt>text_field_tag</tt> Rails also provides higher level support. The two tools at your disposal here are the name parameter to <tt>form_for</tt>/<tt>fields_for</tt> and the <tt>:index</tt> option.</p></div>
<div class="paragraph"><p>You might want to render a form with a set of edit fields for each of a person&#8217;s addresses. Something a little like this will do the trick</p></div>
<h3 id="_using_form_helpers">7.3. Using form helpers</h3>
<div class="paragraph"><p>The previous sections did not use the Rails form helpers at all. While you can craft the input names yourself and pass them directly to helpers such as <tt>text_field_tag</tt> Rails also provides higher level support. The two tools at your disposal here are the name parameter to <tt>form_for</tt> and <tt>fields_for</tt> and the <tt>:index</tt> option that helpers take.</p></div>
<div class="paragraph"><p>You might want to render a form with a set of edit fields for each of a person&#8217;s addresses. For example:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;% form_for @person do |person_form| %&gt;
&lt;%= person_form.text_field :name%&gt;
&lt;%= person_form.text_field :name %&gt;
&lt;% for address in @person.addresses %&gt;
&lt;% person_form.fields_for address, :index =&gt; address do |address_form|%&gt;
&lt;%= address_form.text_field :city %&gt;
@ -877,7 +892,7 @@ http://www.gnu.org/software/src-highlite -->
&lt;% end %&gt;
&lt;% end %&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>Assuming our person had two addresses, with ids 23 and 45 this would create output similar to this:</p></div>
<div class="paragraph"><p>Assuming the person had two addresses, with ids 23 and 45 this would create output similar to this:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>&lt;form action="/people/1" class="edit_person" id="edit_person_1" method="post"&gt;
@ -886,14 +901,12 @@ http://www.gnu.org/software/src-highlite -->
&lt;input id="person_address_45_city" name="person[address][45][city]" size="30" type="text" /&gt;
&lt;/form&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>This will result in a params hash that looks like</p></div>
<div class="paragraph"><p>This will result in a <tt>params</tt> hash that looks like</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="color: #FF0000">{</span><span style="color: #FF0000">'person'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'name'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Bob'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'address'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #FF0000">'23'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'city'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Paris'</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #FF0000">'45'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'city'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'London'</span><span style="color: #FF0000">}</span> <span style="color: #FF0000">}}}</span></tt></pre></div></div>
<div class="paragraph"><p>Rails knows that all these inputs should be part of the person hash because you called <tt>fields_for</tt> on the first form builder. By specifying an <tt>:index</tt> option you&#8217;re telling rails that instead of naming the inputs <tt>person[address][city]</tt> 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 <tt>to_param</tt> on it, which by default returns the database id. This is often useful it is then easy to locate which Address record should be modified but you could pass numbers with some other significance, strings or even nil (which will result in an array parameter being created).</p></div>
<div class="content">
<pre><tt>{'person' =&gt; {'name' =&gt; 'Bob', 'address' =&gt; {'23' =&gt; {'city' =&gt; 'Paris'}, '45' =&gt; {'city' =&gt; 'London'}}}}</tt></pre>
</div></div>
<div class="paragraph"><p>Rails knows that all these inputs should be part of the person hash because you called <tt>fields_for</tt> on the first form builder. By specifying an <tt>:index</tt> option you&#8217;re telling rails that instead of naming the inputs <tt>person[address][city]</tt> 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 <tt>to_param</tt> 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 <tt>nil</tt> (which will result in an array parameter being created).</p></div>
<div class="paragraph"><p>To create more intricate nestings, you can specify the first part of the input name (<tt>person[address]</tt> in the previous example) explicitly, for example</p></div>
<div class="listingblock">
<div class="content">
@ -906,7 +919,7 @@ http://www.gnu.org/software/src-highlite -->
<div class="content">
<pre><tt>&lt;input id="person_address_primary_1_city" name="person[address][primary][1][city]" size="30" type="text" value="bologna" /&gt;</tt></pre>
</div></div>
<div class="paragraph"><p>As a general rule the final input name is the concatenation of the name given to <tt>fields_for</tt>/<tt>form_for</tt>, the index value and the name of the attribute. You can also pass an <tt>:index</tt> option directly to helpers such as <tt>text_field</tt>, but usually it is less repetitive to specify this at the form builder level rather than on individual input controls.</p></div>
<div class="paragraph"><p>As a general rule the final input name is the concatenation of the name given to <tt>fields_for</tt>/<tt>form_for</tt>, the index value and the name of the attribute. You can also pass an <tt>:index</tt> option directly to helpers such as <tt>text_field</tt>, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls.</p></div>
<div class="paragraph"><p>As a shortcut you can append [] to the name and omit the <tt>:index</tt> option. This is the same as specifing <tt>:index =&gt; address</tt> so</p></div>
<div class="listingblock">
<div class="content">
@ -916,9 +929,9 @@ http://www.gnu.org/software/src-highlite -->
</div></div>
<div class="paragraph"><p>produces exactly the same output as the previous example.</p></div>
</div>
<h2 id="_complex_forms">9. Complex forms</h2>
<h2 id="_building_complex_forms">8. Building Complex forms</h2>
<div class="sectionbody">
<div class="paragraph"><p>Many apps grow beyond simple forms editing a single object. For example when creating a Person instance 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:</p></div>
<div class="paragraph"><p>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:</p></div>
<div class="ulist"><ul>
<li>
<p>
@ -947,7 +960,7 @@ James Golick&#8217;s <a href="http://github.com/giraffesoft/attribute_fu/tree">a
</li>
</ul></div>
</div>
<h2 id="_changelog">10. Changelog</h2>
<h2 id="_changelog">9. Changelog</h2>
<div class="sectionbody">
<div class="paragraph"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/1">Lighthouse ticket</a></p></div>
<div class="ulist"><div class="title">Authors</div><ul>

View file

@ -129,6 +129,9 @@
</ul>
</li>
<li>
<a href="#_building_a_multi_model_form">Building a Multi-Model Form</a>
</li>
<li>
<a href="#_what_8217_s_next">What&#8217;s Next?</a>
</li>
<li>
@ -164,6 +167,14 @@ How to quickly generate the starting pieces of a Rails application.
</p>
</li>
</ul></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">This Guide is based on Rails 2.3. Some of the code shown here will not work in older versions of Rails.</td>
</tr></table>
</div>
</div>
</div>
<h2 id="_this_guide_assumes">1. This Guide Assumes</h2>
@ -186,7 +197,7 @@ A working installation of <a href="http://www.sqlite.org/">SQLite</a> (preferred
</p>
</li>
</ul></div>
<div class="paragraph"><p>It is highly recommended that you <strong>familiarize yourself with Ruby before diving into Rails</strong>. You will find it much easier to follow what&#8217;s going on with a Rails application if you understand basic Ruby syntax. Rails isn&#8217;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 net for learning Ruby, including:</p></div>
<div class="paragraph"><p>It is highly recommended that you <strong>familiarize yourself with Ruby before diving into Rails</strong>. You will find it much easier to follow what&#8217;s going on with a Rails application if you understand basic Ruby syntax. Rails isn&#8217;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:</p></div>
<div class="ulist"><ul>
<li>
<p>
@ -208,7 +219,7 @@ A working installation of <a href="http://www.sqlite.org/">SQLite</a> (preferred
<h2 id="_what_is_rails">2. What is Rails?</h2>
<div class="sectionbody">
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>Rails is <em>opinionated software</em>. That is, it assumes that there is a best way to do things, and it&#8217;s designed to encourage that best way - and in some cases discourage alternatives. If you learn "The Rails Way" you&#8217;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.</p></div>
<div class="paragraph"><p>Rails is <em>opinionated software</em>. That is, it assumes that there is a best way to do things, and it&#8217;s designed to encourage that best way - and in some cases to discourage alternatives. If you learn "The Rails Way" you&#8217;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.</p></div>
<div class="paragraph"><p>The Rails philosophy includes several guiding principles:</p></div>
<div class="ulist"><ul>
<li>
@ -321,7 +332,7 @@ Transferring representations of the state of that resource between system compon
</ul></div>
<div class="paragraph"><p>For example, to a Rails application a request such as this:</p></div>
<div class="paragraph"><p><tt>DELETE /photos/17</tt></p></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>If youd like more details on REST as an architectural style, these resources are more approachable than Fieldings thesis:</p></div>
<div class="ulist"><ul>
<li>
@ -394,6 +405,14 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ rails blog -d postgresql</tt></pre></div></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/tip.png" alt="Tip" />
</td>
<td class="content">You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>.</td>
</tr></table>
</div>
<div class="paragraph"><p>After you create the blog application, switch to its folder to continue work directly in that application:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@ -502,9 +521,9 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt>development<span style="color: #990000">:</span>
adapter<span style="color: #990000">:</span> sqlite3
database<span style="color: #990000">:</span> db<span style="color: #990000">/</span>development<span style="color: #990000">.</span>sqlite3
pool<span style="color: #990000">:</span> <span style="color: #993399">5</span>
timeout<span style="color: #990000">:</span> <span style="color: #993399">5000</span></tt></pre></div></div>
<div class="paragraph"><p>If you don&#8217;t have any database set up, SQLite is the easiest to get installed. If you&#8217;re on OS X 10.5 or greater on a Mac, you already have it. Otherwise, you can install it using RubyGems:</p></div>
<div class="paragraph"><p>If you&#8217;re not running OS X 10.5 or greater, you&#8217;ll need to install the SQLite gem. Similar to installing Rails you just need to run:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -522,6 +541,7 @@ http://www.gnu.org/software/src-highlite -->
adapter<span style="color: #990000">:</span> mysql
encoding<span style="color: #990000">:</span> utf8
database<span style="color: #990000">:</span> blog_development
pool<span style="color: #990000">:</span> <span style="color: #993399">5</span>
username<span style="color: #990000">:</span> root
password<span style="color: #990000">:</span>
socket<span style="color: #990000">:</span> <span style="color: #FF6600">/tmp/</span>mysql<span style="color: #990000">.</span>sock</tt></pre></div></div>
@ -537,6 +557,7 @@ http://www.gnu.org/software/src-highlite -->
adapter<span style="color: #990000">:</span> postgresql
encoding<span style="color: #990000">:</span> unicode
database<span style="color: #990000">:</span> blog_development
pool<span style="color: #990000">:</span> <span style="color: #993399">5</span>
username<span style="color: #990000">:</span> blog
password<span style="color: #990000">:</span></tt></pre></div></div>
<div class="paragraph"><p>Change the username and password in the <tt>development</tt> section as appropriate.</p></div>
@ -548,6 +569,14 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ rake db<span style="color: #990000">:</span>create</tt></pre></div></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">Rake is a general-purpose command-runner that Rails uses for many things. You can see the list of available rake commands in your application by running <tt>rake -T</tt>.</td>
</tr></table>
</div>
</div>
<h2 id="_hello_rails">4. Hello, Rails!</h2>
<div class="sectionbody">
@ -581,7 +610,7 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ script/server</tt></pre></div></div>
<div class="paragraph"><p>This will fire up the lightweight Webrick web server by default. To see your application in action, open a browser window and navigate to <tt>http://localhost:3000</tt>. You should see Rails' default information page:</p></div>
<div class="paragraph"><p>This will fire up an instance of the Mongrel web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to <tt>http://localhost:3000</tt>. You should see Rails' default information page:</p></div>
<div class="paragraph"><p><span class="image">
<img src="images/rails_welcome.png" alt="Welcome Aboard screenshot" title="Welcome Aboard screenshot" />
</span></p></div>
@ -593,7 +622,7 @@ http://www.gnu.org/software/src-highlite -->
<td class="content">To stop the web server, hit Ctrl+C in the terminal window where it&#8217;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.</td>
</tr></table>
</div>
<div class="paragraph"><p>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 <tt>http://localhost:3000/home/index</tt>.</p></div>
<div class="paragraph"><p>The "Welcome Aboard" page is the <em>smoke test</em> 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 <tt>http://localhost:3000/home/index</tt>.</p></div>
<h3 id="_setting_the_application_home_page">4.2. Setting the Application Home Page</h3>
<div class="paragraph"><p>You&#8217;d probably like to replace the "Welcome Aboard" page with your own application&#8217;s home page. The first step to doing this is to delete the default page from your application:</p></div>
<div class="listingblock">
@ -650,7 +679,7 @@ http://www.gnu.org/software/src-highlite -->
<td class="content">While scaffolding will get you up and running quickly, the "one size fits all" code that it generates is unlikely to be a perfect fit for your application. In most cases, you&#8217;ll need to customize the generated code. Many experienced Rails developers avoid scaffolding entirely, preferring to write all or most of their source code from scratch.</td>
</tr></table>
</div>
<div class="paragraph"><p>The scaffold generator will build 13 files in your application, along with some folders, and edit one more. Here&#8217;s a quick overview of what it creates:</p></div>
<div class="paragraph"><p>The scaffold generator will build 14 files in your application, along with some folders, and edit one more. Here&#8217;s a quick overview of what it creates:</p></div>
<div class="tableblock">
<table rules="all"
width="100%"
@ -670,7 +699,7 @@ cellspacing="0" cellpadding="4">
<td align="left"><p class="table">The Post model</p></td>
</tr>
<tr>
<td align="left"><p class="table">db/migrate/20081013124235_create_posts.rb</p></td>
<td align="left"><p class="table">db/migrate/20090113124235_create_posts.rb</p></td>
<td align="left"><p class="table">Migration to create the posts table in your database (your name will include a different timestamp)</p></td>
</tr>
<tr>
@ -721,12 +750,16 @@ cellspacing="0" cellpadding="4">
<td align="left"><p class="table">test/unit/post_test.rb</p></td>
<td align="left"><p class="table">Unit testing harness for the posts model</p></td>
</tr>
<tr>
<td align="left"><p class="table">test/unit/helpers/posts_helper_test.rb</p></td>
<td align="left"><p class="table">Unit testing harness for the posts helper</p></td>
</tr>
</tbody>
</table>
</div>
<h3 id="_running_a_migration">6.1. Running a Migration</h3>
<div class="paragraph"><p>One of the products of the <tt>script/generate scaffold</tt> command is a <em>database migration</em>. Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it&#8217;s possible to undo a migration after it&#8217;s been applied to your database. Migration filenames include a timestamp to ensure that they&#8217;re processed in the order that they were created.</p></div>
<div class="paragraph"><p>If you look in the <tt>db/migrate/20081013124235_create_posts.rb</tt> file (remember, yours will have a slightly different name), here&#8217;s what you&#8217;ll find:</p></div>
<div class="paragraph"><p>If you look in the <tt>db/migrate/20090113124235_create_posts.rb</tt> file (remember, yours will have a slightly different name), here&#8217;s what you&#8217;ll find:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -754,8 +787,8 @@ http://www.gnu.org/software/src-highlite -->
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ rake db<span style="color: #990000">:</span>create
$ rake db<span style="color: #990000">:</span>migrate</tt></pre></div></div>
<pre><tt>$ rake db<span style="color: #990000">:</span>migrate</tt></pre></div></div>
<div class="paragraph"><p>Remember, you can&#8217;t run migrations before running <tt>rake db:create</tt> to create your database, as we covered earlier.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@ -842,7 +875,7 @@ title<span style="color: #990000">:</span> nil<span style="color: #990000">,</sp
<td class="icon">
<img src="./images/icons/tip.png" alt="Tip" />
</td>
<td class="content">Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes, type <tt>reload!</tt> at the console prompt to load them.</td>
<td class="content">Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes to your models while the console is open, type <tt>reload!</tt> at the console prompt to load them.</td>
</tr></table>
</div>
<h3 id="_listing_all_posts">6.7. Listing All Posts</h3>
@ -1155,7 +1188,7 @@ http://www.gnu.org/software/src-highlite -->
<div class="sectionbody">
<div class="paragraph"><p>At this point, its worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use <em>partials</em> to clean up duplication in views and <em>filters</em> to help with duplication in controllers.</p></div>
<h3 id="_using_partials_to_eliminate_view_duplication">7.1. Using Partials to Eliminate View Duplication</h3>
<div class="paragraph"><p>As you saw earlier, the scaffold-generated views for the <tt>new</tt> and <tt>edit</tt> actions are largely identical. You can pull the shared code out into a <tt>partial</tt> template. This requires editing the new and edit views, and adding a new template. The new <tt>_form.html.erb</tt> template should be saved in the same <tt>app/views/posts</tt> folder as the files from which it is being extracted:</p></div>
<div class="paragraph"><p>As you saw earlier, the scaffold-generated views for the <tt>new</tt> and <tt>edit</tt> actions are largely identical. You can pull the shared code out into a partial template. This requires editing the new and edit views, and adding a new template. The new <tt>_form.html.erb</tt> template should be saved in the same <tt>app/views/posts</tt> folder as the files from which it is being extracted. Note that the name of this file begins with an underscore; that&#8217;s the Rails naming convention for partial templates.</p></div>
<div class="paragraph"><p><tt>new.html.erb</tt>:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@ -1264,7 +1297,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Rails runs <em>before filters</em> before any action in the controller. You can use the <tt>:only</tt> clause to limit a before filter to only certain actions, or an <tt>:except</tt> clause to specifically skip a before filter for certain actions. Rails also allows you to define <em>after filters</em> that run after processing an action, as well as <em>around filters</em> that surround the processing of actions. Filters can also be defined in external classes to make it easy to share them between controllers.</p></div>
<div class="paragraph"><p>For more information on filters, see the <a href="actioncontroller_basics.html">Action Controller Basics</a> guide.</p></div>
<div class="paragraph"><p>For more information on filters, see the <a href="../actioncontroller_basics.html">Action Controller Basics</a> guide.</p></div>
</div>
<h2 id="_adding_a_second_model">8. Adding a Second Model</h2>
<div class="sectionbody">
@ -1286,7 +1319,7 @@ http://www.gnu.org/software/src-highlite -->
</li>
<li>
<p>
+db/migrate/20081013214407_create_comments.rb - The migration
+db/migrate/20091013214407_create_comments.rb - The migration
</p>
</li>
<li>
@ -1333,7 +1366,7 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ rake db<span style="color: #990000">:</span>migrate</tt></pre></div></div>
<div class="paragraph"><p>Rails is smart enough to only execute the migrations that have not already been run against this particular database.</p></div>
<div class="paragraph"><p>Rails is smart enough to only execute the migrations that have not already been run against the current database.</p></div>
<h3 id="_associating_models">8.2. Associating Models</h3>
<div class="paragraph"><p>Active Record associations let you easily declare the relationship between two models. In the case of comments and posts, you could write out the relationships this way:</p></div>
<div class="ulist"><ul>
@ -1378,15 +1411,13 @@ http://www.gnu.org/software/src-highlite -->
</tr></table>
</div>
<h3 id="_adding_a_route">8.3. Adding a Route</h3>
<div class="paragraph"><p><em>Routes</em> are entries in the <tt>config/routes.rb</tt> file that tell Rails how to match incoming HTTP requests to controller actions. Open up that file and find the existing line referring to <tt>posts</tt>. Then edit it as follows:</p></div>
<div class="paragraph"><p><em>Routes</em> are entries in the <tt>config/routes.rb</tt> file that tell Rails how to match incoming HTTP requests to controller actions. Open up that file and find the existing line referring to <tt>posts</tt> (it will be right at the top of the file). Then edit it as follows:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>posts <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>post<span style="color: #990000">|</span>
post<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>comments
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>posts<span style="color: #990000">,</span> <span style="color: #990000">:</span>has_many <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>comments</tt></pre></div></div>
<div class="paragraph"><p>This creates <tt>comments</tt> as a <em>nested resource</em> within <tt>posts</tt>. This is another part of capturing the hierarchical relationship that exists between posts and comments.</p></div>
<div class="admonitionblock">
<table><tr>
@ -1442,7 +1473,7 @@ http://www.gnu.org/software/src-highlite -->
</p>
</li>
</ul></div>
<div class="paragraph"><p>The controller will be generated with empty methods for each action that you specified in the call to <tt>script/generate controller</tt>:</p></div>
<div class="paragraph"><p>The controller will be generated with empty methods and views for each action that you specified in the call to <tt>script/generate controller</tt>:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1509,6 +1540,17 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> destroy
<span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post_id<span style="color: #990000">])</span>
<span style="color: #009900">@comment</span> <span style="color: #990000">=</span> Comment<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
<span style="color: #009900">@comment</span><span style="color: #990000">.</span>destroy
respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
format<span style="color: #990000">.</span>html <span style="color: #FF0000">{</span> redirect_to post_comments_path<span style="color: #990000">(</span><span style="color: #009900">@post</span><span style="color: #990000">)</span> <span style="color: #FF0000">}</span>
format<span style="color: #990000">.</span>xml <span style="color: #FF0000">{</span> head <span style="color: #990000">:</span>ok <span style="color: #FF0000">}</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>You&#8217;ll see a bit more complexity here than you did in the controller for posts. That&#8217;s a side-effect of the nesting that you&#8217;ve set up; each request for a comment has to keep track of the post to which the comment is attached.</p></div>
<div class="paragraph"><p>In addition, the code takes advantage of some of the methods available for an association. For example, in the <tt>new</tt> method, it calls</p></div>
@ -1521,7 +1563,7 @@ http://www.gnu.org/software/src-highlite -->
<div class="paragraph"><p>This creates a new <tt>Comment</tt> object <em>and</em> sets up the <tt>post_id</tt> field to have the <tt>id</tt> from the specified <tt>Post</tt> object in a single operation.</p></div>
<h3 id="_building_views">8.5. Building Views</h3>
<div class="paragraph"><p>Because you skipped scaffolding, you&#8217;ll need to build views for comments "by hand." Invoking <tt>script/generate controller</tt> will give you skeleton views, but they&#8217;ll be devoid of actual content. Here&#8217;s a first pass at fleshing out the comment views.</p></div>
<div class="paragraph"><p>The <tt>index.html.erb</tt> view:</p></div>
<div class="paragraph"><p>The <tt>views/comments/index.html.erb</tt> view:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1550,7 +1592,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="color: #FF0000">&lt;%= link_to 'New comment', new_post_comment_path(@post) %&gt;</span>
<span style="color: #FF0000">&lt;%= link_to 'Back to Post', @post %&gt;</span></tt></pre></div></div>
<div class="paragraph"><p>The <tt>new.html.erb</tt> view:</p></div>
<div class="paragraph"><p>The <tt>views/comments/new.html.erb</tt> view:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1575,7 +1617,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="color: #FF0000">&lt;% end %&gt;</span>
<span style="color: #FF0000">&lt;%= link_to 'Back', post_comments_path(@post) %&gt;</span></tt></pre></div></div>
<div class="paragraph"><p>The <tt>show.html.erb</tt> view:</p></div>
<div class="paragraph"><p>The <tt>views/comments/show.html.erb</tt> view:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1595,7 +1637,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="color: #FF0000">&lt;%= link_to 'Edit', edit_post_comment_path(@post, @comment) %&gt;</span> <span style="color: #990000">|</span>
<span style="color: #FF0000">&lt;%= link_to 'Back', post_comments_path(@post) %&gt;</span></tt></pre></div></div>
<div class="paragraph"><p>The <tt>edit.html.erb</tt> view:</p></div>
<div class="paragraph"><p>The <tt>views/comments/edit.html.erb</tt> view:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1621,9 +1663,9 @@ http://www.gnu.org/software/src-highlite -->
<span style="color: #FF0000">&lt;%= link_to 'Show', post_comment_path(@post, @comment) %&gt;</span> <span style="color: #990000">|</span>
<span style="color: #FF0000">&lt;%= link_to 'Back', post_comments_path(@post) %&gt;</span></tt></pre></div></div>
<div class="paragraph"><p>Again, the added complexity here (compared to the views you saw for managing comments) comes from the necessity of juggling a post and its comments at the same time.</p></div>
<div class="paragraph"><p>Again, the added complexity here (compared to the views you saw for managing posts) comes from the necessity of juggling a post and its comments at the same time.</p></div>
<h3 id="_hooking_comments_to_posts">8.6. Hooking Comments to Posts</h3>
<div class="paragraph"><p>As a final step, I&#8217;ll modify the <tt>show.html.erb</tt> view for a post to show the comments on that post, and to allow managing those comments:</p></div>
<div class="paragraph"><p>As a next step, I&#8217;ll modify the <tt>views/posts/show.html.erb</tt> view to show the comments on that post, and to allow managing those comments:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -1657,12 +1699,94 @@ http://www.gnu.org/software/src-highlite -->
<span style="color: #FF0000">&lt;/p&gt;</span>
<span style="color: #FF0000">&lt;% end %&gt;</span>
<span style="color: #FF0000">&lt;%= link_to 'Edit', edit_post_path(@post) %&gt;</span> <span style="color: #990000">|</span>
<span style="color: #FF0000">&lt;%= link_to 'Back', posts_path %&gt;</span>
<span style="color: #FF0000">&lt;%= link_to 'Edit Post', edit_post_path(@post) %&gt;</span> <span style="color: #990000">|</span>
<span style="color: #FF0000">&lt;%= link_to 'Back to Posts', posts_path %&gt;</span> <span style="color: #990000">|</span>
<span style="color: #FF0000">&lt;%= link_to 'Manage Comments', post_comments_path(@post) %&gt;</span></tt></pre></div></div>
<div class="paragraph"><p>Note that each post has its own individual comments collection, accessible as <tt>@post.comments</tt>. That&#8217;s a consequence of the declarative associations in the models. Path helpers such as <tt>post_comments_path</tt> come from the nested route declaration in <tt>config/routes.rb</tt>.</p></div>
</div>
<h2 id="_what_8217_s_next">9. What&#8217;s Next?</h2>
<h2 id="_building_a_multi_model_form">9. Building a Multi-Model Form</h2>
<div class="sectionbody">
<div class="paragraph"><p>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&#8217;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:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ script/generate model tag name<span style="color: #990000">:</span>string post<span style="color: #990000">:</span>references</tt></pre></div></div>
<div class="paragraph"><p>Run the migration to create the database table:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ rake db<span style="color: #990000">:</span>migrate</tt></pre></div></div>
<div class="paragraph"><p>Next, edit the <tt>post.rb</tt> file to create the other side of the association, and to tell Rails that you intend to edit tags via posts:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Post <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
validates_presence_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>title
validates_length_of <span style="color: #990000">:</span>title<span style="color: #990000">,</span> <span style="color: #990000">:</span>minimum <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span>
has_many <span style="color: #990000">:</span>comments
has_many <span style="color: #990000">:</span>tags
accepts_nested_attributes_for <span style="color: #990000">:</span>tags<span style="color: #990000">,</span> <span style="color: #990000">:</span>allow_destroy <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #990000">,</span>
<span style="color: #990000">:</span>reject_if <span style="color: #990000">=&gt;</span> proc <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>attrs<span style="color: #990000">|</span> attrs<span style="color: #990000">.</span>all? <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>k<span style="color: #990000">,</span> v<span style="color: #990000">|</span> v<span style="color: #990000">.</span>blank? <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>The <tt>:allow_destroy</tt> option on the nested attribute declaration tells Rails to display a "remove" checkbox on the view that you&#8217;ll build shortly. The <tt>:reject_if</tt> option prevents saving new tags that do not have any attributes filled in.</p></div>
<div class="paragraph"><p>You&#8217;ll also need to modify <tt>views/posts/_form.html.erb</tt> to include the tags:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="color: #FF0000">&lt;% @post.tags.build if @post.tags.empty? %&gt;</span>
<span style="color: #FF0000">&lt;% form_for(@post) do |post_form| %&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.error_messages %&gt;</span>
<span style="color: #FF0000">&lt;p&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.label :name %&gt;&lt;br /&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.text_field :name %&gt;</span>
<span style="color: #FF0000">&lt;/p&gt;</span>
<span style="color: #FF0000">&lt;p&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.label :title, "title" %&gt;&lt;br /&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.text_field :title %&gt;</span>
<span style="color: #FF0000">&lt;/p&gt;</span>
<span style="color: #FF0000">&lt;p&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.label :content %&gt;&lt;br /&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.text_area :content %&gt;</span>
<span style="color: #FF0000">&lt;/p&gt;</span>
<span style="color: #FF0000">&lt;h2&gt;</span>Tags<span style="color: #FF0000">&lt;/h2&gt;</span>
<span style="color: #FF0000">&lt;% post_form.fields_for :tags do |tag_form| %&gt;</span>
<span style="color: #FF0000">&lt;p&gt;</span>
<span style="color: #FF0000">&lt;%= tag_form.label :name, 'Tag:' %&gt;</span>
<span style="color: #FF0000">&lt;%= tag_form.text_field :name %&gt;</span>
<span style="color: #FF0000">&lt;/p&gt;</span>
<span style="color: #FF0000">&lt;% unless tag_form.object.nil? || tag_form.object.new_record? %&gt;</span>
<span style="color: #FF0000">&lt;p&gt;</span>
<span style="color: #FF0000">&lt;%= tag_form.label :_delete, 'Remove:' %&gt;</span>
<span style="color: #FF0000">&lt;%= tag_form.check_box :_delete %&gt;</span>
<span style="color: #FF0000">&lt;/p&gt;</span>
<span style="color: #FF0000">&lt;% end %&gt;</span>
<span style="color: #FF0000">&lt;% end %&gt;</span>
<span style="color: #FF0000">&lt;p&gt;</span>
<span style="color: #FF0000">&lt;%= post_form.submit "Save" %&gt;</span>
<span style="color: #FF0000">&lt;/p&gt;</span>
<span style="color: #FF0000">&lt;% end %&gt;</span></tt></pre></div></div>
<div class="paragraph"><p>With these changes in place, you&#8217;ll find that you can edit a post and its tags directly on the same view.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">You may want to use javascript to dynamically add additional tags on a single form. For an example of this and other advanced techniques, see the <a href="http://github.com/alloy/complex-form-examples/tree/nested_attributes">nested model sample application</a>.</td>
</tr></table>
</div>
</div>
<h2 id="_what_8217_s_next">10. What&#8217;s Next?</h2>
<div class="sectionbody">
<div class="paragraph"><p>Now that you&#8217;ve seen your first Rails application, you should feel free to update it and experiment on your own. But you don&#8217;t have to do everything without help. As you need assistance getting up and running with Rails, feel free to consult these support resources:</p></div>
<div class="ulist"><ul>
@ -1701,41 +1825,16 @@ Running <tt>rake doc:rails</tt> will put a full copy of the API documentation fo
</li>
</ul></div>
</div>
<h2 id="_changelog">10. Changelog</h2>
<h2 id="_changelog">11. Changelog</h2>
<div class="sectionbody">
<div class="paragraph"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2">Lighthouse ticket</a></p></div>
<div class="ulist"><ul>
<li>
<p>
November 3, 2008: Formatting patch from Dave Rothlisberger
</p>
</li>
<li>
<p>
November 1, 2008: First approved version by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
</p>
</li>
<li>
<p>
October 16, 2008: Revised based on feedback from Pratik Naik by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
</p>
</li>
<li>
<p>
October 13, 2008: First complete draft by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
</p>
</li>
<li>
<p>
October 12, 2008: More detail, rearrangement, editing by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
</p>
</li>
<li>
<p>
September 8, 2008: initial version by James Miller (not yet approved for publication)
</p>
</li>
</ul></div>
<div class="paragraph"><p>*
* November 3, 2008: Formatting patch from Dave Rothlisberger
* November 1, 2008: First approved version by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
* October 16, 2008: Revised based on feedback from Pratik Naik by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
* October 13, 2008: First complete draft by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
* October 12, 2008: More detail, rearrangement, editing by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
* September 8, 2008: initial version by James Miller (not yet approved for publication)</p></div>
</div>
</div>

View file

@ -50,16 +50,24 @@
<li><a href="#_setting_and_passing_the_locale">Setting and passing the locale</a></li>
<li><a href="#_setting_locale_from_the_domain_name">Setting locale from the domain name</a></li>
<li><a href="#_setting_locale_from_the_url_params">Setting locale from the URL params</a></li>
<li><a href="#_setting_locale_from_the_client_supplied_information">Setting locale from the client supplied information</a></li>
</ul>
</li>
<li>
<a href="#_internationalize_your_application">Internationalize your application</a>
<a href="#_internationalizing_your_application">Internationalizing your application</a>
<ul>
<li><a href="#_adding_translations">Adding Translations</a></li>
<li><a href="#_adding_date_time_formats">Adding Date/Time formats</a></li>
<li><a href="#_organization_of_locale_files">Organization of locale files</a></li>
</ul>
</li>
<li>
@ -80,9 +88,9 @@
<a href="#_how_to_store_your_custom_translations">How to store your custom translations</a>
<ul>
<li><a href="#_translations_for_activerecord_models">Translations for ActiveRecord models</a></li>
<li><a href="#_translations_for_active_record_models">Translations for Active Record models</a></li>
<li><a href="#_other_translations_and_localizations">Other translations and localizations</a></li>
<li><a href="#_overview_of_other_built_in_methods_that_provide_i18n_support">Overview of other built-in methods that provide I18n support</a></li>
</ul>
</li>
@ -97,9 +105,18 @@
</ul>
</li>
<li>
<a href="#_conclusion">Conclusion</a>
</li>
<li>
<a href="#_contributing_to_rails_i18n">Contributing to Rails I18n</a>
</li>
<li>
<a href="#_resources">Resources</a>
</li>
<li>
<a href="#_authors">Authors</a>
</li>
<li>
<a href="#_footnotes">Footnotes</a>
</li>
<li>
@ -112,13 +129,51 @@
<h1>The Rails Internationalization (I18n) API</h1>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>The Ruby I18n (shorthand for <em>internationalization</em>) gem which is shipped with Ruby on Rails (starting from Rails 2.2) provides an easy-to-use and extensible framework for translating your application to a single custom language other than English or providing multi-language support in your application.</p></div>
<div class="paragraph"><p>The Ruby I18n (shorthand for <em>internationalization</em>) gem which is shipped with Ruby on Rails (starting from Rails 2.2) provides an easy-to-use and extensible framework for <strong>translating your application to a single custom language</strong> other than English or for <strong>providing multi-language support</strong> in your application.</p></div>
<div class="paragraph"><p>The process of "internationalization" usually means to abstract all strings and other locale specific bits (such as date or currency formats) out of your application. The process of "localization" means to provide translations and localized formats for these bits. <a href="#1">[1]</a></p></div>
<div class="paragraph"><p>So, in the process of <em>internationalizing</em> your Rails application you have to:</p></div>
<div class="ulist"><ul>
<li>
<p>
Ensure you have support for i18n
</p>
</li>
<li>
<p>
Tell Rails where to find locale dictionaries
</p>
</li>
<li>
<p>
Tell Rails how to set, preserve and switch locale
</p>
</li>
</ul></div>
<div class="paragraph"><p>In the process of <em>localizing</em> your application you&#8217;ll probably want to do following three things:</p></div>
<div class="ulist"><ul>
<li>
<p>
Replace or supplement Rail&#8217;s default locale&#8201;&#8212;&#8201;eg. date and time formats, month names, ActiveRecord model names, etc
</p>
</li>
<li>
<p>
Abstract texts in your application into keyed dictionaries&#8201;&#8212;&#8201;eg. flash messages, static texts in your views, etc
</p>
</li>
<li>
<p>
Store the resulting dictionaries somewhere
</p>
</li>
</ul></div>
<div class="paragraph"><p>This guide will walk you through the I18n API and contains a tutorial how to internationalize a Rails application from the start.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">The Ruby I18n framework provides you with all neccessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available. See Rails <a href="http://rails-i18n.org/wiki">I18n Wiki</a> for more information.</td>
<td class="content">The Ruby I18n framework provides you with all neccessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See Rails <a href="http://rails-i18n.org/wiki">I18n Wiki</a> for more information.</td>
</tr></table>
</div>
</div>
@ -138,7 +193,7 @@ making it easy to customize and extend everything for other languages
</p>
</li>
</ul></div>
<div class="paragraph"><p>As part of this solution, <strong>every static string in the Rails framework</strong>&#8201;&#8212;&#8201;eg. ActiveRecord validation messages, time and date formats&#8201;&#8212;&#8201;<strong>has been internationalized</strong>, so <em>localization</em> of a Rails application means "over-riding" these defaults.</p></div>
<div class="paragraph"><p>As part of this solution, <strong>every static string in the Rails framework</strong>&#8201;&#8212;&#8201;eg. Active Record validation messages, time and date formats&#8201;&#8212;&#8201;<strong>has been internationalized</strong>, so <em>localization</em> of a Rails application means "over-riding" these defaults.</p></div>
<h3 id="_the_overall_architecture_of_the_library">1.1. The overall architecture of the library</h3>
<div class="paragraph"><p>Thus, the Ruby I18n gem is split into two parts:</p></div>
<div class="ulist"><ul>
@ -206,8 +261,16 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>en<span style="color: #990000">:</span>
hello<span style="color: #990000">:</span> <span style="color: #FF0000">"Hello world"</span></tt></pre></div></div>
<div class="paragraph"><p>This means, that in the <tt>:en</tt> locale, the key <em>hello</em> will map to <em>Hello world</em> string. Every string inside Rails is internationalized in this way, see for instance ActiveRecord validation messages in the <a href="http://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml"><tt>activerecord/lib/active_record/locale/en.yml</tt></a> file or time and date formats in the <a href="http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml"><tt>activesupport/lib/active_support/locale/en.yml</tt></a> file. You can use YAML or standard Ruby Hashes to store translations in the default (Simple) backend.</p></div>
<div class="paragraph"><p>This means, that in the <tt>:en</tt> locale, the key <em>hello</em> will map to <em>Hello world</em> string. Every string inside Rails is internationalized in this way, see for instance Active Record validation messages in the <a href="http://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml"><tt>activerecord/lib/active_record/locale/en.yml</tt></a> file or time and date formats in the <a href="http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml"><tt>activesupport/lib/active_support/locale/en.yml</tt></a> file. You can use YAML or standard Ruby Hashes to store translations in the default (Simple) backend.</p></div>
<div class="paragraph"><p>The I18n library will use <strong>English</strong> as a <strong>default locale</strong>, ie. if you don&#8217;t set a different locale, <tt>:en</tt> will be used for looking up translations.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">The i18n library takes <strong>pragmatic approach</strong> to locale keys (after <a href="http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en">some discussion</a>), including only the <em>locale</em> ("language") part, like <tt>:en</tt>, <tt>:pl</tt>, not the <em>region</em> part, like <tt>:en-US</tt> or <tt>:en-UK</tt>, which are traditionally used for separating "languages" and "regional setting" or "dialects". (For instance, in the <tt>:en-US</tt> locale you would have $ as a currency symbol, while in <tt>:en-UK</tt>, you would have €. Also, insults would be different in American and British English :) Reason for this pragmatic approach is that most of the time, you usually care about making your application available in different "languages", and working with locales is much simpler this way. However, nothing stops you from separating regional and other settings in the traditional way. In this case, you could eg. inherit from the default <tt>en</tt> locale and then provide UK specific settings in a <tt>:en-UK</tt> dictionary.</td>
</tr></table>
</div>
<div class="paragraph"><p>The <strong>translations load path</strong> (<tt>I18n.load_path</tt>) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you.</p></div>
<div class="admonitionblock">
<table><tr>
@ -244,8 +307,17 @@ I18n<span style="color: #990000">.</span>load_path <span style="color: #990000">
<span style="font-style: italic"><span style="color: #9A1900"># set default locale to something else then :en</span></span>
I18n<span style="color: #990000">.</span>default_locale <span style="color: #990000">=</span> <span style="color: #990000">:</span>pt</tt></pre></div></div>
<h3 id="_setting_and_passing_the_locale">2.3. Setting and passing the locale</h3>
<div class="paragraph"><p>By default the I18n library will use :en (English) as a I18n.default_locale for looking up translations (if you do not specify a locale for a lookup).</p></div>
<div class="paragraph"><p>If you want to translate your Rails application to a single language other than English you can set I18n.default_locale to your locale. If you want to change the locale on a per-request basis though you can set it in a before_filter on the ApplicationController like this:</p></div>
<div class="paragraph"><p>If you want to translate your Rails application to a <strong>single language other than English</strong> (the default locale), you can set I18n.default_locale to your locale in <tt>environment.rb</tt> or an initializer as shown above, and it will persist through the requests.</p></div>
<div class="paragraph"><p>However, you would probably like to <strong>provide support for more locales</strong> in your application. In such case, you need to set and pass the locale between requests.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/warning.png" alt="Warning" />
</td>
<td class="content">You may be tempted to store choosed locale in a <em>session</em> or a <em>cookie</em>. <strong>Do not do so</strong>. The locale should be transparent and a part of the URL. This way you don&#8217;t break people&#8217;s basic assumptions about the web itself: if you send a URL of some page to a friend, she should see the same page, same content. A fancy word for this would be that you&#8217;re being <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"><em>RESTful</em></a>. Read more about RESTful approach in <a href="http://www.infoq.com/articles/rest-introduction">Stefan Tilkov&#8217;s articles</a>. There may be some exceptions to this rule, which are discussed below.</td>
</tr></table>
</div>
<div class="paragraph"><p>The <em>setting part</em> is easy. You can set locale in a <tt>before_filter</tt> in the ApplicationController like this:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -253,24 +325,196 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>before_filter <span style="color: #990000">:</span>set_locale
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> set_locale
<span style="font-style: italic"><span style="color: #9A1900"># if this is nil then I18n.default_locale will be used</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># if params[:locale] is nil then I18n.default_locale will be used</span></span>
I18n<span style="color: #990000">.</span>locale <span style="color: #990000">=</span> params<span style="color: #990000">[:</span>locale<span style="color: #990000">]</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>This will already work for URLs where you pass the locale as a query parameter as in example.com?locale=pt (which is what Google also does).</p></div>
<div class="paragraph"><p>This requires you to pass the locale as a URL query parameter as in <tt>http://example.com/books?locale=pt</tt>. (This is eg. Google&#8217;s approach). So <tt>http://localhost:3000?locale=pt</tt> will load the Portugese localization, whereas <tt>http://localhost:3000?locale=de</tt> would load the German localization, and so on. You may skip the next section and head over to the <strong>Internationalize your application</strong> section, if you want to try things out by manually placing locale in the URL and reloading the page.</p></div>
<div class="paragraph"><p>Of course, you probably don&#8217;t want to manually include locale in every URL all over your application, or want the URLs look differently, eg. the usual <tt>http://example.com/pt/books</tt> versus <tt>http://example.com/en/books</tt>. Let&#8217;s discuss the different options you have.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/tip.png" alt="Tip" />
<img src="./images/icons/important.png" alt="Important" />
</td>
<td class="content">For other URL designs, see <a href="http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url">How to encode the current locale in the URL</a>.</td>
<td class="content">Following examples rely on having locales loaded into your application available as an array of strings like <tt>["en", "es", "gr"]</tt>. This is not inclued in current version of Rails 2.2&#8201;&#8212;&#8201;forthcoming Rails version 2.3 will contain easy accesor <tt>available_locales</tt>. (See <a href="http://github.com/svenfuchs/i18n/commit/411f8fe7">this commit</a> and background at <a href="http://rails-i18n.org/wiki/pages/i18n-available_locales">Rails I18n Wiki</a>.)</td>
</tr></table>
</div>
<div class="paragraph"><p>Now you&#8217;ve initialized I18n support for your application and told it which locale should be used. With that in place you&#8217;re now ready for the really interesting stuff.</p></div>
<div class="paragraph"><p>So, for having available locales easily available in Rails 2.2, we have to include this support manually in an initializer, like this:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># config/initializers/available_locales.rb</span></span>
<span style="font-style: italic"><span style="color: #9A1900">#</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># Get loaded locales conveniently</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># See http://rails-i18n.org/wiki/pages/i18n-available_locales</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">module</span></span> I18n
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="color: #990000">&lt;&lt;</span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> available_locales<span style="color: #990000">;</span> backend<span style="color: #990000">.</span>available_locales<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Backend
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Simple
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> available_locales<span style="color: #990000">;</span> translations<span style="color: #990000">.</span>keys<span style="color: #990000">.</span>collect <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>l<span style="color: #990000">|</span> l<span style="color: #990000">.</span>to_s <span style="color: #FF0000">}</span><span style="color: #990000">.</span>sort<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># You need to "force-initialize" loaded locales</span></span>
I18n<span style="color: #990000">.</span>backend<span style="color: #990000">.</span>send<span style="color: #990000">(:</span>init_translations<span style="color: #990000">)</span>
AVAILABLE_LOCALES <span style="color: #990000">=</span> I18n<span style="color: #990000">.</span>backend<span style="color: #990000">.</span>available_locales
RAILS_DEFAULT_LOGGER<span style="color: #990000">.</span>debug <span style="color: #FF0000">"* Loaded locales: #{AVAILABLE_LOCALES.inspect}"</span></tt></pre></div></div>
<div class="paragraph"><p>You can then wrap the constant for easy access in ApplicationController:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> available_locales<span style="color: #990000">;</span> AVAILABLE_LOCALES<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<h3 id="_setting_locale_from_the_domain_name">2.4. Setting locale from the domain name</h3>
<div class="paragraph"><p>One option you have is to set the locale from the domain name where your application runs. For example, we want <tt>www.example.com</tt> to load English (or default) locale, and <tt>www.example.es</tt> to load Spanish locale. Thus the <em>top-level domain name</em> is used for locale setting. This has several advantages:</p></div>
<div class="ulist"><ul>
<li>
<p>
Locale is an <em>obvious</em> part of the URL
</p>
</li>
<li>
<p>
People intuitively grasp in which language the content will be displayed
</p>
</li>
<li>
<p>
It is very trivial to implement in Rails
</p>
</li>
<li>
<p>
Search engines seem to like that content in different languages lives at different, inter-linked domains
</p>
</li>
</ul></div>
<div class="paragraph"><p>You can implement it like this in your ApplicationController:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>before_filter <span style="color: #990000">:</span>set_locale
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> set_locale
I18n<span style="color: #990000">.</span>locale <span style="color: #990000">=</span> extract_locale_from_uri
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># Get locale from top-level domain or return nil if such locale is not available</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># You have to put something like:</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># 127.0.0.1 application.com</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># 127.0.0.1 application.it</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># 127.0.0.1 application.pl</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># in your /etc/hosts file to try this out locally</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> extract_locale_from_tld
parsed_locale <span style="color: #990000">=</span> request<span style="color: #990000">.</span>host<span style="color: #990000">.</span>split<span style="color: #990000">(</span><span style="color: #FF0000">'.'</span><span style="color: #990000">).</span>last
<span style="color: #990000">(</span>available_locales<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">?</span> parsed_locale<span style="color: #990000">)</span> <span style="color: #990000">?</span> parsed_locale <span style="color: #990000">:</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>We can also set the locale from the <em>subdomain</em> in very similar way:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># Get locale code from request subdomain (like http://it.application.local:3000)</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># You have to put something like:</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># 127.0.0.1 gr.application.local</span></span>
<span style="font-style: italic"><span style="color: #9A1900"># in your /etc/hosts file to try this out locally</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> extract_locale_from_subdomain
parsed_locale <span style="color: #990000">=</span> request<span style="color: #990000">.</span>subdomains<span style="color: #990000">.</span>first
<span style="color: #990000">(</span>available_locales<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">?</span> parsed_locale<span style="color: #990000">)</span> <span style="color: #990000">?</span> parsed_locale <span style="color: #990000">:</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>If your application includes a locale switching menu, you would then have something like this in it:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>link_to<span style="color: #990000">(</span><span style="color: #FF0000">"Deutsch"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"#{APP_CONFIG[:deutsch_website_url]}#{request.env['REQUEST_URI']}"</span><span style="color: #990000">)</span></tt></pre></div></div>
<div class="paragraph"><p>assuming you would set <tt>APP_CONFIG[:deutsch_website_url]</tt> to some value like <tt>http://www.application.de</tt>.</p></div>
<div class="paragraph"><p>This solution has aforementioned advantages, however, you may not be able or may not want to provide different localizations ("language versions") on different domains. The most obvious solution would be to include locale code in the URL params (or request path).</p></div>
<h3 id="_setting_locale_from_the_url_params">2.5. Setting locale from the URL params</h3>
<div class="paragraph"><p>Most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the <tt>I18n.locale = params[:locale]</tt> <em>before_filter</em> in the first example. We would like to have URLs like <tt>www.example.com/books?locale=ja</tt> or <tt>www.example.com/ja/books</tt> in this case.</p></div>
<div class="paragraph"><p>This approach has almost the same set of advantages as setting the locale from domain name: namely that it&#8217;s RESTful and in accord with rest of the World Wide Web. It does require a little bit more work to implement, though.</p></div>
<div class="paragraph"><p>Getting the locale from <tt>params</tt> and setting it accordingly is not hard; including it in every URL and thus <strong>passing it through the requests</strong> is. To include an explicit option in every URL (eg. <tt>link_to( books_url(:locale =&gt; I18n.locale) )</tt>) would be tedious and probably impossible, of course.</p></div>
<div class="paragraph"><p>Rails contains infrastructure for "centralizing dynamic decisions about the URLs" in its <a href="http://api.rubyonrails.org/classes/ActionController/Base.html#M000515"><tt><strong>ApplicationController#default_url_options</strong></tt></a>, which is useful precisely in this scenario: it enables us to set "defaults" for <a href="http://api.rubyonrails.org/classes/ActionController/Base.html#M000503"><tt>url_for</tt></a> and helper methods dependent on it (by implementing/overriding this method).</p></div>
<div class="paragraph"><p>We can include something like this in our ApplicationController then:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># app/controllers/application_controller.rb</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> default_url_options<span style="color: #990000">(</span>options<span style="color: #990000">=</span><span style="color: #FF0000">{}</span><span style="color: #990000">)</span>
logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"default_url_options is passed options: #{options.inspect}\n"</span>
<span style="color: #FF0000">{</span> <span style="color: #990000">:</span>locale <span style="color: #990000">=&gt;</span> I18n<span style="color: #990000">.</span>locale <span style="color: #FF0000">}</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Every helper method dependent on <tt>url_for</tt> (eg. helpers for named routes like <tt>root_path</tt> or <tt>root_url</tt>, resource routes like <tt>books_path</tt> or <tt>books_url</tt>, etc.) will now <strong>automatically include the locale in the query string</strong>, like this: <tt>http://localhost:3001/?locale=ja</tt>.</p></div>
<div class="paragraph"><p>You may be satisfied with this. It does impact the readability of URLs, though, when the locale "hangs" at the end of every URL in your application. Moreover, from the architectural standpoint, locale is usually hierarchically above the other parts of application domain: and URLs should reflect this.</p></div>
<div class="paragraph"><p>You probably want URLs look like this: <tt>www.example.com/en/books</tt> (which loads English locale) and <tt>www.example.com/nl/books</tt> (which loads Netherlands locale). This is achievable with the "over-riding <tt>default_url_options</tt>" strategy from above: you just have to set up your routes with <a href="http://api.rubyonrails.org/classes/ActionController/Resources.html#M000354"><tt>path_prefix</tt></a> option in this way:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># config/routes.rb</span></span>
map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>books<span style="color: #990000">,</span> <span style="color: #990000">:</span>path_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'/:locale'</span></tt></pre></div></div>
<div class="paragraph"><p>Now, when you call <tt>books_path</tt> method you should get <tt>"/en/books"</tt> (for the default locale). An URL like <tt>http://localhost:3001/nl/books</tt> should load the Netherlands locale, then, and following calls to <tt>books_path</tt> should return <tt>"/nl/books"</tt> (because the locale changed).</p></div>
<div class="paragraph"><p>Of course, you need to take special care of root URL (usually "homepage" or "dashboard") of your application. An URL like <tt>http://localhost:3001/nl</tt> will not work automatically, because the <tt>map.root :controller =&gt; "dashboard"</tt> declaration in your <tt>routes.rb</tt> doesn&#8217;t take locale into account. (And rightly so. There&#8217;s only one "root" URL.)</p></div>
<div class="paragraph"><p>You would probably need to map URLs like these:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># config/routes.rb</span></span>
map<span style="color: #990000">.</span>dashboard <span style="color: #FF0000">'/:locale'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"dashboard"</span></tt></pre></div></div>
<div class="paragraph"><p>Do take special care about the <strong>order of your routes</strong>, so this route declaration does not "eat" other ones. (You may want to add it directly before the <tt>map.root</tt> declaration.)</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/important.png" alt="Important" />
</td>
<td class="content">This solution has currently one rather big <strong>downside</strong>. Due to the <em>default_url_options</em> implementation, you have to pass the <tt>:id</tt> option explicitely, like this: <tt>link_to <em>Show</em>, book_url(:id =&gt; book)</tt> and not depend on Rails' magic in code like <tt>link_to <em>Show</em>, book</tt>. If this should be a problem, have a look on two plugins which simplify working with routes in this way: Sven Fuchs&#8217;s <a href="http://github.com/svenfuchs/routing-filter/tree/master"><em>routing_filter</em></a> and Raul Murciano&#8217;s <a href="http://github.com/raul/translate_routes/tree/master"><em>translate_routes</em></a>. See also the page <a href="http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url">How to encode the current locale in the URL</a> in the Rails i18n Wiki.</td>
</tr></table>
</div>
<h2 id="_internationalize_your_application">3. Internationalize your application</h2>
<h3 id="_setting_locale_from_the_client_supplied_information">2.6. Setting locale from the client supplied information</h3>
<div class="paragraph"><p>In specific cases, it would make sense to set locale from client supplied information, ie. not from URL. This information may come for example from users' preffered language (set in their browser), can be based on users' geographical location inferred from their IP, or users can provide it simply by choosing 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&#8201;&#8212;&#8201;see the box about <em>sessions</em>, <em>cookies</em> and RESTful architecture above.</p></div>
<h4 id="_using_accept_language">2.6.1. Using Accept-Language</h4>
<div class="paragraph"><p>One source of client supplied information would be an <tt>Accept-Language</tt> HTTP header. People may <a href="http://www.w3.org/International/questions/qa-lang-priorities">set this in their browser</a> or other clients (such as <em>curl</em>).</p></div>
<div class="paragraph"><p>A trivial implementation of using <tt>Accept-Language</tt> header would be:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> set_locale
logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"</span>
I18n<span style="color: #990000">.</span>locale <span style="color: #990000">=</span> extract_locale_from_accept_language_header
logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"* Locale set to '#{I18n.locale}'"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
private
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> extract_locale_from_accept_language_header
request<span style="color: #990000">.</span>env<span style="color: #990000">[</span><span style="color: #FF0000">'HTTP_ACCEPT_LANGUAGE'</span><span style="color: #990000">].</span>scan<span style="color: #990000">(</span><span style="color: #FF6600">/^[a-z]{2}/</span><span style="color: #990000">).</span>first
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Of course, in production environment you would need much robust code, and could use a plugin such as Iaian Hecker&#8217;s <a href="http://github.com/iain/http_accept_language">http_accept_language</a>.</p></div>
<h4 id="_using_geoip_or_similar_database">2.6.2. Using GeoIP (or similar) database</h4>
<div class="paragraph"><p>Another way of choosing the locale from client&#8217;s information would be to use a database for mapping client IP to region, such as <a href="http://www.maxmind.com/app/geolitecountry">GeoIP Lite Country</a>. The mechanics of the code would be very similar to the code above&#8201;&#8212;&#8201;you would need to query database for user&#8217;s IP, and lookup your preffered locale for the country/region/city returned.</p></div>
<h4 id="_user_profile">2.6.3. User profile</h4>
<div class="paragraph"><p>You can also provide users of your application with means to set (and possibly over-ride) locale in your application interface, as well. Again, mechanics for this approach would be very similar to the code above&#8201;&#8212;&#8201;you&#8217;d probably let users choose a locale from a dropdown list and save it to their profile in database. Then you&#8217;d set the locale to this value.</p></div>
</div>
<h2 id="_internationalizing_your_application">3. Internationalizing your application</h2>
<div class="sectionbody">
<div class="paragraph"><p>The process of "internationalization" usually means to abstract all strings and other locale specific bits out of your application. The process of "localization" means to then provide translations and localized formats for these bits. <a href="#1">[1]</a></p></div>
<div class="paragraph"><p>So, let&#8217;s internationalize something. You most probably have something like this in one of your applications:</p></div>
<div class="paragraph"><p>OK! Now you&#8217;ve initialized I18n support for your Ruby on Rails application and told it which locale should be used and how to preserve it between requests. With that in place, you&#8217;re now ready for the really interesting stuff.</p></div>
<div class="paragraph"><p>Let&#8217;s <em>internationalize</em> our application, ie. abstract every locale-specific parts, and that <em>localize</em> it, ie. provide neccessary translations for these abstracts.</p></div>
<div class="paragraph"><p>You most probably have something like this in one of your applications:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -295,7 +539,7 @@ ActionController<span style="color: #990000">::</span>Routing<span style="color:
<img src="images/i18n/demo_untranslated.png" alt="rails i18n demo untranslated" title="rails i18n demo untranslated" />
</span></p></div>
<h3 id="_adding_translations">3.1. Adding Translations</h3>
<div class="paragraph"><p>Obviously there are two strings that are localized to English. In order to internationalize this code replace these strings with calls to Rails' #t helper with a key that makes sense for the translation:</p></div>
<div class="paragraph"><p>Obviously there are <strong>two strings that are localized to English</strong>. In order to internationalize this code, <strong>replace these strings</strong> with calls to Rails' <tt>#t</tt> helper with a key that makes sense for the translation:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -311,7 +555,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-style: italic"><span style="color: #9A1900"># app/views/home/index.html.erb</span></span>
<span style="color: #FF0000">&lt;h1&gt;&lt;%=t :hello_world %&gt;&lt;/h1&gt;</span>
<span style="color: #FF0000">&lt;p&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;</span></tt></pre></div></div>
<div class="paragraph"><p>When you now render this view it will show an error message that tells you that the translations for the keys :hello_world and :hello_flash are missing.</p></div>
<div class="paragraph"><p>When you now render this view, it will show an error message which tells you that the translations for the keys <tt>:hello_world</tt> and <tt>:hello_flash</tt> are missing.</p></div>
<div class="paragraph"><p><span class="image">
<img src="images/i18n/demo_translation_missing.png" alt="rails i18n demo translation missing" title="rails i18n demo translation missing" />
</span></p></div>
@ -320,10 +564,10 @@ http://www.gnu.org/software/src-highlite -->
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">Rails adds a <tt>t</tt> (<tt>translate</tt>) helper method to your views so that you do not need to spell out <tt>I18n.t</tt> all the time. Additionally this helper will catch missing translations and wrap the resulting error message into a &lt;span class="translation_missing"&gt;.</td>
<td class="content">Rails adds a <tt>t</tt> (<tt>translate</tt>) helper method to your views so that you do not need to spell out <tt>I18n.t</tt> all the time. Additionally this helper will catch missing translations and wrap the resulting error message into a <tt>&lt;span class="translation_missing"&gt;</tt>.</td>
</tr></table>
</div>
<div class="paragraph"><p>So let&#8217;s add the missing translations (i.e. do the "localization" part):</p></div>
<div class="paragraph"><p>So let&#8217;s add the missing translations into the dictionary files (i.e. do the "localization" part):</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -338,17 +582,24 @@ en<span style="color: #990000">:</span>
pirate<span style="color: #990000">:</span>
hello_world<span style="color: #990000">:</span> Ahoy World
hello_flash<span style="color: #990000">:</span> Ahoy Flash</tt></pre></div></div>
<div class="paragraph"><p>There you go. Because you haven&#8217;t changed the default_locale I18n will use English. Your application now shows:</p></div>
<div class="paragraph"><p>There you go. Because you haven&#8217;t changed the default_locale, I18n will use English. Your application now shows:</p></div>
<div class="paragraph"><p><span class="image">
<img src="images/i18n/demo_translated_english.png" alt="rails i18n demo translated to english" title="rails i18n demo translated to english" />
<img src="images/i18n/demo_translated_en.png" alt="rails i18n demo translated to english" title="rails i18n demo translated to english" />
</span></p></div>
<div class="paragraph"><p>And when you change the URL to pass the pirate locale you get:</p></div>
<div class="paragraph"><p>And when you change the URL to pass the pirate locale (<tt>http://localhost:3000?locale=pirate</tt>), you&#8217;ll get:</p></div>
<div class="paragraph"><p><span class="image">
<img src="images/i18n/demo_translated_pirate.png" alt="rails i18n demo translated to pirate" title="rails i18n demo translated to pirate" />
</span></p></div>
<div class="paragraph"><p>NOTE You need to restart the server when you add new locale files.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
<td class="content">You need to restart the server when you add new locale files.</td>
</tr></table>
</div>
<h3 id="_adding_date_time_formats">3.2. Adding Date/Time formats</h3>
<div class="paragraph"><p>Ok, let&#8217;s add a timestamp to the view so we can demo the date/time localization feature as well. To localize the time format you pass the Time object to I18n.l or (preferably) use Rails' #l helper. You can pick a format by passing the :format option, by default the :default format is used.</p></div>
<div class="paragraph"><p>OK! Now let&#8217;s add a timestamp to the view, so we can demo the <strong>date/time localization</strong> feature as well. To localize the time format you pass the Time object to <tt>I18n.l</tt> or (preferably) use Rails' <tt>#l</tt> helper. You can pick a format by passing the <tt>:format</tt> option&#8201;&#8212;&#8201;by default the <tt>:default</tt> format is used.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -373,30 +624,67 @@ pirate<span style="color: #990000">:</span>
<div class="paragraph"><p><span class="image">
<img src="images/i18n/demo_localized_pirate.png" alt="rails i18n demo localized time to pirate" title="rails i18n demo localized time to pirate" />
</span></p></div>
<div class="paragraph"><p>NOTE Right now you might need to add some more date/time formats in order to make the I18n backend work as expected. See the <a href="http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale">rails-i18n repository</a> for starting points.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/tip.png" alt="Tip" />
</td>
<td class="content">Right now you might need to add some more date/time formats in order to make the I18n backend work as expected. Of course, there&#8217;s a great chance that somebody already did all the work by <strong>translating Rails&#8217;s defaults for your locale</strong>. See the <a href="http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale">rails-i18n repository at Github</a> for an archive of various locale files. When you put such file(s) in <tt>config/locale/</tt> directory, they will automatically ready for use.</td>
</tr></table>
</div>
<h3 id="_organization_of_locale_files">3.3. Organization of locale files</h3>
<div class="paragraph"><p>When you are using the default SimpleStore, shipped with the i18n library, you store dictionaries 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.</p></div>
<div class="paragraph"><p>For example, your <tt>config/locale</tt> directory could look like this:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>|-defaults
|---es.rb
|---en.rb
|-models
|---book
|-----es.rb
|-----en.rb
|-views
|---defaults
|-----es.rb
|-----en.rb
|---books
|-----es.rb
|-----en.rb
|---users
|-----es.rb
|-----en.rb
|---navigation
|-----es.rb
|-----en.rb</tt></pre>
</div></div>
<div class="paragraph"><p>This way, you can separate model and model attribute names from text inside views, and all of this from the "defaults" (eg. date and time formats).</p></div>
<div class="paragraph"><p>Other stores for the i18n library could provide different means of such separation.</p></div>
<div class="paragraph"><p>Do check the <a href="http://rails-i18n.org/wiki">Rails i18n Wiki</a> for list of tools available for managing translations.</p></div>
</div>
<h2 id="_overview_of_the_i18n_api_features">4. Overview of the I18n API features</h2>
<div class="sectionbody">
<div class="paragraph"><p>The following purposes are covered:</p></div>
<div class="paragraph"><p>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&#8217;ll cover it&#8217;s features in more depth.</p></div>
<div class="paragraph"><p>Covered are features like these:</p></div>
<div class="ulist"><ul>
<li>
<p>
lookup translations
looking up translations
</p>
</li>
<li>
<p>
interpolate data into translations
interpolating data into translations
</p>
</li>
<li>
<p>
pluralize translations
pluralizing translations
</p>
</li>
<li>
<p>
localize dates, numbers, currency etc.
localizing dates, numbers, currency etc.
</p>
</li>
</ul></div>
@ -410,14 +698,14 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>t <span style="color: #990000">:</span>message
I18n<span style="color: #990000">.</span>t <span style="color: #FF0000">'message'</span></tt></pre></div></div>
<div class="paragraph"><p>translate also takes a :scope option which can contain one or many additional keys that will be used to specify a “namespace” or scope for a translation key:</p></div>
<div class="paragraph"><p><tt>translate</tt> also takes a <tt>:scope</tt> option which can contain one or many additional keys that will be used to specify a “namespace” or scope for a translation key:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>t <span style="color: #990000">:</span>invalid<span style="color: #990000">,</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>active_record<span style="color: #990000">,</span> <span style="color: #990000">:</span>error_messages<span style="color: #990000">]</span></tt></pre></div></div>
<div class="paragraph"><p>This looks up the :invalid message in the ActiveRecord error messages.</p></div>
<div class="paragraph"><p>This looks up the <tt>:invalid</tt> message in the Active Record error messages.</p></div>
<div class="paragraph"><p>Additionally, both the key and scopes can be specified as dot separated keys as in:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@ -445,7 +733,7 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>t <span style="color: #990000">:</span>missing<span style="color: #990000">,</span> <span style="color: #990000">:</span>default <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Not here'</span>
<span style="font-style: italic"><span style="color: #9A1900"># =&gt; 'Not here'</span></span></tt></pre></div></div>
<div class="paragraph"><p>If the default value is a Symbol it will be used as a key and translated. One can provide multiple values as default. The first one that results in a value will be returned.</p></div>
<div class="paragraph"><p>E.g. the following first tries to translate the key :missing and then the key :also_missing. As both do not yield a result the string "Not here" will be returned:</p></div>
<div class="paragraph"><p>E.g. the following first tries to translate the key <tt>:missing</tt> and then the key <tt>:also_missing.</tt> As both do not yield a result the string "Not here" will be returned:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -462,7 +750,7 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>t <span style="color: #990000">[:</span>odd<span style="color: #990000">,</span> <span style="color: #990000">:</span>even<span style="color: #990000">],</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'active_record.error_messages'</span>
<span style="font-style: italic"><span style="color: #9A1900"># =&gt; ["must be odd", "must be even"]</span></span></tt></pre></div></div>
<div class="paragraph"><p>Also, a key can translate to a (potentially nested) hash as grouped translations. E.g. one can receive all ActiveRecord error messages as a Hash with:</p></div>
<div class="paragraph"><p>Also, a key can translate to a (potentially nested) hash as grouped translations. E.g. one can receive all Active Record error messages as a Hash with:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -471,8 +759,8 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>t <span style="color: #FF0000">'active_record.error_messages'</span>
<span style="font-style: italic"><span style="color: #9A1900"># =&gt; { :inclusion =&gt; "is not included in the list", :exclusion =&gt; ... }</span></span></tt></pre></div></div>
<h3 id="_interpolation">4.2. Interpolation</h3>
<div class="paragraph"><p>In many cases you want to abstract your translations so that variables can be interpolated into the translation. For this reason the I18n API provides an interpolation feature.</p></div>
<div class="paragraph"><p>All options besides :default and :scope that are passed to #translate will be interpolated to the translation:</p></div>
<div class="paragraph"><p>In many cases you want to abstract your translations so that <strong>variables can be interpolated into the translation</strong>. For this reason the I18n API provides an interpolation feature.</p></div>
<div class="paragraph"><p>All options besides <tt>:default</tt> and <tt>:scope</tt> that are passed to <tt>#translate</tt> will be interpolated to the translation:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -481,10 +769,10 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>backend<span style="color: #990000">.</span>store_translations <span style="color: #990000">:</span>en<span style="color: #990000">,</span> <span style="color: #990000">:</span>thanks <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Thanks {{name}}!'</span>
I18n<span style="color: #990000">.</span>translate <span style="color: #990000">:</span>thanks<span style="color: #990000">,</span> <span style="color: #990000">:</span>name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Jeremy'</span>
<span style="font-style: italic"><span style="color: #9A1900"># =&gt; 'Thanks Jeremy!'</span></span></tt></pre></div></div>
<div class="paragraph"><p>If a translation uses :default or :scope as a interpolation variable an I18n::ReservedInterpolationKey exception is raised. If a translation expects an interpolation variable but it has not been passed to #translate an I18n::MissingInterpolationArgument exception is raised.</p></div>
<div class="paragraph"><p>If a translation uses <tt>:default</tt> or <tt>:scope</tt> as a interpolation variable an I+18n::ReservedInterpolationKey+ exception is raised. If a translation expects an interpolation variable but it has not been passed to <tt>#translate</tt> an <tt>I18n::MissingInterpolationArgument</tt> exception is raised.</p></div>
<h3 id="_pluralization">4.3. Pluralization</h3>
<div class="paragraph"><p>In English there&#8217;s only a singular and a plural form for a given string, e.g. "1 message" and "2 messages". Other languages (<a href="http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ar">Arabic</a>, <a href="http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ja">Japanese</a>, <a href="http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ru">Russian</a> and many more) have different grammars that have additional or less <a href="http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html">plural forms</a>. Thus, the I18n API provides a flexible pluralization feature.</p></div>
<div class="paragraph"><p>The :count interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:</p></div>
<div class="paragraph"><p>The <tt>:count</tt> interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -496,18 +784,18 @@ http://www.gnu.org/software/src-highlite -->
<span style="color: #FF0000">}</span>
I18n<span style="color: #990000">.</span>translate <span style="color: #990000">:</span>inbox<span style="color: #990000">,</span> <span style="color: #990000">:</span>count <span style="color: #990000">=&gt;</span> <span style="color: #993399">2</span>
<span style="font-style: italic"><span style="color: #9A1900"># =&gt; '2 messages'</span></span></tt></pre></div></div>
<div class="paragraph"><p>The algorithm for pluralizations in :en is as simple as:</p></div>
<div class="paragraph"><p>The algorithm for pluralizations in <tt>:en</tt> is as simple as:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>entry<span style="color: #990000">[</span>count <span style="color: #990000">==</span> <span style="color: #993399">1</span> <span style="color: #990000">?</span> <span style="color: #993399">0</span> <span style="color: #990000">:</span> <span style="color: #993399">1</span><span style="color: #990000">]</span></tt></pre></div></div>
<div class="paragraph"><p>I.e. the translation denoted as :one is regarded as singular, the other is used as plural (including the count being zero).</p></div>
<div class="paragraph"><p>If the lookup for the key does not return an Hash suitable for pluralization an I18n::InvalidPluralizationData exception is raised.</p></div>
<div class="paragraph"><p>I.e. the translation denoted as <tt>:one</tt> is regarded as singular, the other is used as plural (including the count being zero).</p></div>
<div class="paragraph"><p>If the lookup for the key does not return an Hash suitable for pluralization an <tt>18n::InvalidPluralizationData</tt> exception is raised.</p></div>
<h3 id="_setting_and_passing_a_locale">4.4. Setting and passing a locale</h3>
<div class="paragraph"><p>The locale can be either set pseudo-globally to I18n.locale (which uses Thread.current like, e.g., Time.zone) or can be passed as an option to #translate and #localize.</p></div>
<div class="paragraph"><p>If no locale is passed I18n.locale is used:</p></div>
<div class="paragraph"><p>The locale can be either set pseudo-globally to <tt>I18n.locale</tt> (which uses <tt>Thread.current</tt> like, e.g., <tt>Time.zone</tt>) or can be passed as an option to <tt>#translate</tt> and <tt>#localize</tt>.</p></div>
<div class="paragraph"><p>If no locale is passed <tt>I18n.locale</tt> is used:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -524,7 +812,7 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>t <span style="color: #990000">:</span>foo<span style="color: #990000">,</span> <span style="color: #990000">:</span>locale <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>de
I18n<span style="color: #990000">.</span>l Time<span style="color: #990000">.</span>now<span style="color: #990000">,</span> <span style="color: #990000">:</span>locale <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>de</tt></pre></div></div>
<div class="paragraph"><p>I18n.locale defaults to I18n.default_locale which defaults to :en. The default locale can be set like this:</p></div>
<div class="paragraph"><p><tt>I18n.locale</tt> defaults to <tt>I18n.default_locale</tt> which defaults to :<tt>en</tt>. The default locale can be set like this:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -557,8 +845,8 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt>pt<span style="color: #990000">:</span>
foo<span style="color: #990000">:</span>
bar<span style="color: #990000">:</span> baz</tt></pre></div></div>
<div class="paragraph"><p>As you see in both cases the toplevel key is the locale. :foo is a namespace key and :bar is the key for the translation "baz".</p></div>
<div class="paragraph"><p>Here is a "real" example from the ActiveSupport en.yml translations YAML file:</p></div>
<div class="paragraph"><p>As you see in both cases the toplevel key is the locale. <tt>:foo</tt> is a namespace key and <tt>:bar</tt> is the key for the translation "baz".</p></div>
<div class="paragraph"><p>Here is a "real" example from the ActiveSupport <tt>en.yml</tt> translations YAML file:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -570,7 +858,7 @@ http://www.gnu.org/software/src-highlite -->
default<span style="color: #990000">:</span> <span style="color: #FF0000">"%Y-%m-%d"</span>
short<span style="color: #990000">:</span> <span style="color: #FF0000">"%b %d"</span>
long<span style="color: #990000">:</span> <span style="color: #FF0000">"%B %d, %Y"</span></tt></pre></div></div>
<div class="paragraph"><p>So, all of the following equivalent lookups will return the :short date format "%B %d":</p></div>
<div class="paragraph"><p>So, all of the following equivalent lookups will return the <tt>:short</tt> date format <tt>"%B %d"</tt>:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -580,9 +868,9 @@ http://www.gnu.org/software/src-highlite -->
I18n<span style="color: #990000">.</span>t <span style="color: #FF0000">'formats.short'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>date
I18n<span style="color: #990000">.</span>t <span style="color: #990000">:</span>short<span style="color: #990000">,</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'date.formats'</span>
I18n<span style="color: #990000">.</span>t <span style="color: #990000">:</span>short<span style="color: #990000">,</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>date<span style="color: #990000">,</span> <span style="color: #990000">:</span>formats<span style="color: #990000">]</span></tt></pre></div></div>
<div class="paragraph"><p>Generally we recommend using YAML as a format for storing translations. There are cases though where you want to store Ruby lambdas as part of your locale data, e.g. for special date</p></div>
<h3 id="_translations_for_activerecord_models">5.1. Translations for ActiveRecord models</h3>
<div class="paragraph"><p>You can use the methods Model.human_name and Model.human_attribute_name(attribute) to transparently lookup translations for your model and attribute names.</p></div>
<div class="paragraph"><p>Generally we recommend using YAML as a format for storing translations. There are cases though where you want to store Ruby lambdas as part of your locale data, e.g. for special date.</p></div>
<h3 id="_translations_for_active_record_models">5.1. Translations for Active Record models</h3>
<div class="paragraph"><p>You can use the methods <tt>Model.human_name</tt> and <tt>Model.human_attribute_name(attribute)</tt> to transparently lookup translations for your model and attribute names.</p></div>
<div class="paragraph"><p>For example when you add the following translations:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@ -597,11 +885,11 @@ http://www.gnu.org/software/src-highlite -->
user<span style="color: #990000">:</span>
login<span style="color: #990000">:</span> <span style="color: #FF0000">"Handle"</span>
<span style="font-style: italic"><span style="color: #9A1900"># will translate User attribute "login" as "Handle"</span></span></tt></pre></div></div>
<div class="paragraph"><p>Then User.human_name will return "Dude" and User.human_attribute_name(:login) will return "Handle".</p></div>
<div class="paragraph"><p>Then <tt>User.human_name</tt> will return "Dude" and <tt>User.human_attribute_name(:login)</tt> will return "Handle".</p></div>
<h4 id="_error_message_scopes">5.1.1. Error message scopes</h4>
<div class="paragraph"><p>ActiveRecord validation error messages can also be translated easily. ActiveRecord gives you a couple of namespaces where you can place your message translations in order to provide different messages and translation for certain models, attributes and/or validations. It also transparently takes single table inheritance into account.</p></div>
<div class="paragraph"><p>Active Record validation error messages can also be translated easily. Active Record gives you a couple of namespaces where you can place your message translations in order to provide different messages and translation for certain models, attributes and/or validations. It also transparently takes single table inheritance into account.</p></div>
<div class="paragraph"><p>This gives you quite powerful means to flexibly adjust your messages to your application&#8217;s needs.</p></div>
<div class="paragraph"><p>Consider a User model with a validates_presence_of validation for the name attribute like this:</p></div>
<div class="paragraph"><p>Consider a User model with a <tt>validates_presence_of</tt> validation for the name attribute like this:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -610,7 +898,7 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> User <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
validates_presence_of <span style="color: #990000">:</span>name
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>The key for the error message in this case is :blank. ActiveRecord will lookup this key in the namespaces:</p></div>
<div class="paragraph"><p>The key for the error message in this case is <tt>:blank</tt>. Active Record will lookup this key in the namespaces:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -638,7 +926,7 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Admin <span style="color: #990000">&lt;</span> User
validates_presence_of <span style="color: #990000">:</span>name
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>Then ActiveRecord will look for messages in this order:</p></div>
<div class="paragraph"><p>Then Active Record will look for messages in this order:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -651,9 +939,9 @@ activerecord<span style="color: #990000">.</span>errors<span style="color: #9900
activerecord<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>messages<span style="color: #990000">.</span>blank</tt></pre></div></div>
<div class="paragraph"><p>This way you can provide special translations for various error messages at different points in your models inheritance chain and in the attributes, models or default scopes.</p></div>
<h4 id="_error_message_interpolation">5.1.2. Error message interpolation</h4>
<div class="paragraph"><p>The translated model name and translated attribute name are always available for interpolation.</p></div>
<div class="paragraph"><p>The translated model name, translated attribute name, and value are always available for interpolation.</p></div>
<div class="paragraph"><p></p></div>
<div class="paragraph"><p>count and/or value are available where applicable. Count can be used for pluralization if present:</p></div>
<div class="paragraph"><p><tt>count</tt>, where available, can be used for pluralization if present:</p></div>
<div class="tableblock">
<table rules="all"
width="100%"
@ -722,55 +1010,85 @@ cellspacing="0" cellpadding="4">
<td align="left"><p class="table">validates_uniqueness_of</p></td>
<td align="left"><p class="table">-</p></td>
<td align="left"><p class="table">:taken</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_format_of</p></td>
<td align="left"><p class="table">-</p></td>
<td align="left"><p class="table">:invalid</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_inclusion_of</p></td>
<td align="left"><p class="table">-</p></td>
<td align="left"><p class="table">:inclusion</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_exclusion_of</p></td>
<td align="left"><p class="table">-</p></td>
<td align="left"><p class="table">:exclusion</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_associated</p></td>
<td align="left"><p class="table">-</p></td>
<td align="left"><p class="table">:invalid</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">-</p></td>
<td align="left"><p class="table">:not_a_number</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">:greater_than</p></td>
<td align="left"><p class="table">:greater_than</p></td>
<td align="left"><p class="table">count</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">:greater_than_or_equal_to</p></td>
<td align="left"><p class="table">:greater_than_or_equal_to</p></td>
<td align="left"><p class="table">count</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">:equal_to</p></td>
<td align="left"><p class="table">:equal_to</p></td>
<td align="left"><p class="table">count</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">:less_than</p></td>
<td align="left"><p class="table">:less_than</p></td>
<td align="left"><p class="table">count</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">:less_than_or_equal_to</p></td>
<td align="left"><p class="table">:less_than_or_equal_to</p></td>
<td align="left"><p class="table">count</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">:odd</p></td>
<td align="left"><p class="table">:odd</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
<tr>
<td align="left"><p class="table">validates_numericality_of</p></td>
<td align="left"><p class="table">:even</p></td>
<td align="left"><p class="table">:even</p></td>
<td align="left"><p class="table">value</p></td>
<td align="left"><p class="table">-</p></td>
</tr>
</tbody>
</table>
</div>
<h4 id="_translations_for_the_activerecord_error_messages_for_helper">5.1.3. Translations for the ActiveRecord error_messages_for helper</h4>
<div class="paragraph"><p>If you are using the ActiveRecord error_messages_for helper you will want to add translations for it.</p></div>
<h4 id="_translations_for_the_active_record_error_messages_for_helper">5.1.3. Translations for the Active Record error_messages_for helper</h4>
<div class="paragraph"><p>If you are using the Active Record <tt>error_messages_for</tt> helper you will want to add translations for it.</p></div>
<div class="paragraph"><p>Rails ships with the following translations:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@ -785,9 +1103,48 @@ http://www.gnu.org/software/src-highlite -->
one<span style="color: #990000">:</span> <span style="color: #FF0000">"1 error prohibited this {{model}} from being saved"</span>
other<span style="color: #990000">:</span> <span style="color: #FF0000">"{{count}} errors prohibited this {{model}} from being saved"</span>
body<span style="color: #990000">:</span> <span style="color: #FF0000">"There were problems with the following fields:"</span></tt></pre></div></div>
<h3 id="_other_translations_and_localizations">5.2. Other translations and localizations</h3>
<div class="paragraph"><p>Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers.</p></div>
<div class="paragraph"><p>TODO list helpers and available keys</p></div>
<h3 id="_overview_of_other_built_in_methods_that_provide_i18n_support">5.2. Overview of other built-in methods that provide I18n support</h3>
<div class="paragraph"><p>Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers. Here&#8217;s a brief overview.</p></div>
<h4 id="_actionview_helper_methods">5.2.1. ActionView helper methods</h4>
<div class="ulist"><ul>
<li>
<p>
<tt>distance_of_time_in_words</tt> translates and pluralizes its result and interpolates the number of seconds, minutes, hours and so on. See <a href="http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L51">datetime.distance_in_words</a> translations.
</p>
</li>
<li>
<p>
<tt>datetime_select</tt> and <tt>select_month</tt> use translated month names for populating the resulting select tag. See <a href="http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L15">date.month_names</a> for translations. <tt>datetime_select</tt> also looks up the order option from <a href="http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L18">date.order</a> (unless you pass the option explicitely). All date select helpers translate the prompt using the translations in the <a href="http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L83">datetime.prompts</a> scope if applicable.
</p>
</li>
<li>
<p>
The <tt>number_to_currency</tt>, <tt>number_with_precision</tt>, <tt>number_to_percentage</tt>, <tt>number_with_delimiter</tt> and <tt>humber_to_human_size</tt> helpers use the number format settings located in the <a href="http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L2">number</a> scope.
</p>
</li>
</ul></div>
<h4 id="_active_record_methods">5.2.2. Active Record methods</h4>
<div class="ulist"><ul>
<li>
<p>
<tt>human_name</tt> and <tt>human_attribute_name</tt> use translations for model names and attribute names if available in the <a href="http://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml#L43">activerecord.models</a> scope. They also support translations for inherited class names (e.g. for use with STI) as explained above in "Error message scopes".
</p>
</li>
<li>
<p>
<tt>ActiveRecord::Errors#generate_message</tt> (which is used by Active Record validations but may also be used manually) uses <tt>human_name</tt> and <tt>human_attribute_name</tt> (see above). It also translates the error message and supports translations for inherited class names as explained above in "Error message scopes".
</p>
</li>
</ul></div>
<div class="paragraph"><p>*<tt> ActiveRecord::Errors#full_messages</tt> prepends the attribute name to the error message using a separator that will be looked up from <a href="http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L91">activerecord.errors.format.separator</a> (and defaults to <tt>' '</tt>).</p></div>
<h4 id="_activesupport_methods">5.2.3. ActiveSupport methods</h4>
<div class="ulist"><ul>
<li>
<p>
<tt>Array#to_sentence</tt> uses format settings as given in the <a href="http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L30">support.array</a> scope.
</p>
</li>
</ul></div>
</div>
<h2 id="_customize_your_i18n_setup">6. Customize your I18n setup</h2>
<div class="sectionbody">
@ -813,7 +1170,7 @@ InvalidPluralizationData <span style="font-style: italic"><span style="color
MissingInterpolationArgument <span style="font-style: italic"><span style="color: #9A1900"># the translation expects an interpolation argument that has not been passed</span></span>
ReservedInterpolationKey <span style="font-style: italic"><span style="color: #9A1900"># the translation contains a reserved interpolation variable name (i.e. one of: scope, default)</span></span>
UnknownFileType <span style="font-style: italic"><span style="color: #9A1900"># the backend does not know how to handle a file type that was added to I18n.load_path</span></span></tt></pre></div></div>
<div class="paragraph"><p>The I18n API will catch all of these exceptions when they were thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for MissingTranslationData exceptions. When a MissingTranslationData exception has been caught it will return the exceptions error message string containing the missing key/scope.</p></div>
<div class="paragraph"><p>The I18n API will catch all of these exceptions when they were thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for <tt>MissingTranslationData</tt> exceptions. When a <tt>MissingTranslationData</tt> exception has been caught it will return the exceptions error message string containing the missing key/scope.</p></div>
<div class="paragraph"><p>The reason for this is that during development you&#8217;d usually want your views to still render even though a translation is missing.</p></div>
<div class="paragraph"><p>In other contexts you might want to change this behaviour though. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module:</p></div>
<div class="listingblock">
@ -828,9 +1185,9 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
I18n<span style="color: #990000">.</span>exception_handler <span style="color: #990000">=</span> <span style="color: #990000">:</span>just_raise_that_exception</tt></pre></div></div>
<div class="paragraph"><p>This would re-raise all caught exceptions including MissingTranslationData.</p></div>
<div class="paragraph"><p>Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides the method #t (as well as #translate). When a MissingTranslationData exception occurs in this context the helper wraps the message into a span with the css class translation_missing.</p></div>
<div class="paragraph"><p>To do so the helper forces I18n#translate to raise exceptions no matter what exception handler is defined by setting the :raise option:</p></div>
<div class="paragraph"><p>This would re-raise all caught exceptions including <tt>MissingTranslationData</tt>.</p></div>
<div class="paragraph"><p>Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides the method <tt>#t</tt> (as well as <tt>#translate</tt>). When a <tt>MissingTranslationData</tt> exception occurs in this context the helper wraps the message into a span with the CSS class <tt>translation_missing</tt>.</p></div>
<div class="paragraph"><p>To do so the helper forces <tt>I18n#translate</tt> to raise exceptions no matter what exception handler is defined by setting the <tt>:raise</tt> option:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@ -838,16 +1195,75 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>I18n<span style="color: #990000">.</span>t <span style="color: #990000">:</span>foo<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">raise</span></span> <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-style: italic"><span style="color: #9A1900"># always re-raises exceptions from the backend</span></span></tt></pre></div></div>
</div>
<h2 id="_resources">7. Resources</h2>
<h2 id="_conclusion">7. Conclusion</h2>
<div class="sectionbody">
<div class="paragraph"><p>At this point you hopefully have a good overview about how I18n support in Ruby on Rails works and are ready to start translating your project.</p></div>
<div class="paragraph"><p>If you find anything missing or wrong in this guide please file a ticket on <a href="http://i18n.lighthouseapp.com/projects/14948-rails-i18n/overview">our issue tracker</a>. If you want to discuss certain portions or have questions please sign up to our <a href="http://groups.google.com/group/rails-i18n">mailinglist</a>.</p></div>
</div>
<h2 id="_footnotes">8. Footnotes</h2>
<h2 id="_contributing_to_rails_i18n">8. Contributing to Rails I18n</h2>
<div class="sectionbody">
<div class="paragraph"><p>I18n support in Ruby on Rails was introduced in the release 2.2 and is still evolving. The project follows the good Ruby on Rails development tradition of evolving solutions in plugins and real applications first and then cherry-picking the best bread of most widely useful features second for inclusion to the core.</p></div>
<div class="paragraph"><p>Thus we encourage everybody to experiment with new ideas and features in plugins or other libraries and make them available to the community. (Don&#8217;t forget to announce your work on our <a href="http://groups.google.com/group/rails-i18n">mailinglist</a>!)</p></div>
<div class="paragraph"><p>If you find your own locale (language) missing from our <a href="http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale">example translations data</a> repository for Ruby on Rails, please <a href="http://github.com/guides/fork-a-project-and-submit-your-modifications"><em>fork</em></a> the repository, add your data and send a <a href="http://github.com/guides/pull-requests">pull request</a>.</p></div>
</div>
<h2 id="_resources">9. Resources</h2>
<div class="sectionbody">
<div class="ulist"><ul>
<li>
<p>
<a href="http://rails-i18n.org">rails-i18n.org</a> - Homepage of the rails-i18n project. You can find lots of useful resources on the <a href="http://rails-i18n.org/wiki">wiki</a>.
</p>
</li>
<li>
<p>
<a href="http://groups.google.com/group/rails-i18n">rails-i18n Google group</a> - The project&#8217;s mailing list.
</p>
</li>
<li>
<p>
<a href="http://github.com/svenfuchs/rails-i18n/tree/master">Github: rails-i18n</a> - Code repository for the rails-i18n project. Most importantly you can find lots of <a href="http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale">example translations</a> for Rails that should work for your application in most cases.
</p>
</li>
<li>
<p>
<a href="http://i18n.lighthouseapp.com/projects/14948-rails-i18n/overview">Lighthouse: rails-i18n</a> - Issue tracker for the rails-i18n project.
</p>
</li>
<li>
<p>
<a href="http://github.com/svenfuchs/i18n/tree/master">Github: i18n</a> - Code repository for the i18n gem.
</p>
</li>
<li>
<p>
<a href="http://i18n.lighthouseapp.com/projects/14947-ruby-i18n/overview">Lighthouse: i18n</a> - Issue tracker for the i18n gem.
</p>
</li>
</ul></div>
</div>
<h2 id="_authors">10. Authors</h2>
<div class="sectionbody">
<div class="ulist"><ul>
<li>
<p>
<a href="http://www.workingwithrails.com/person/9963-sven-fuchs">Sven Fuchs</a> (initial author)
</p>
</li>
<li>
<p>
<a href="http://www.workingwithrails.com/person/7476-karel-mina-k">Karel Minařík</a>
</p>
</li>
</ul></div>
<div class="paragraph"><p>If you found this guide useful please consider recommending its authors on <a href="http://www.workingwithrails.com">workingwithrails</a>.</p></div>
</div>
<h2 id="_footnotes">11. Footnotes</h2>
<div class="sectionbody">
<div class="paragraph"><p><a id="1"></a>[1] Or, to quote <a href="http://en.wikipedia.org/wiki/Internationalization_and_localization">Wikipedia</a>: <em>"Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes. Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text."</em></p></div>
<div class="paragraph"><p><a id="2"></a>[2] Other backends might allow or require to use other formats, e.g. a GetText backend might allow to read GetText files.</p></div>
<div class="paragraph"><p><a id="3"></a>[3] One of these reasons is that we don&#8217;t want to any unnecessary load for applications that do not need any I18n capabilities, so we need to keep the I18n library as simple as possible for English. Another reason is that it is virtually impossible to implement a one-fits-all solution for all problems related to I18n for all existing languages. So a solution that allows us to exchange the entire implementation easily is appropriate anyway. This also makes it much easier to experiment with custom features and extensions.</p></div>
</div>
<h2 id="_changelog">9. Changelog</h2>
<h2 id="_changelog">12. Changelog</h2>
<div class="sectionbody">
<div class="paragraph"><p><a href="http://rails.lighthouseapp.com/projects/16213/tickets/23">Lighthouse ticket</a></p></div>
</div>

View file

@ -136,20 +136,20 @@ understand how to use routing in your own Rails applications, start here.</p></d
<div class="sidebar-title"><a href="actioncontroller_basics.html">Basics of Action Controller</a></div>
<div class="paragraph"><p>This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.</p></div>
</div></div>
<h2>Digging Deeper</h2>
<div class="sidebarblock">
<div class="sidebar-content">
<div class="sidebar-title"><a href="caching_with_rails.html">Rails Caching</a></div>
<div class="sidebar-title"><a href="action_mailer_basics.html">Action Mailer Basics</a></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="./images/icons/caution.png" alt="Caution" />
</td>
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/10">Lighthouse Ticket</a></td>
<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/25">Lighthouse ticket</a></td>
</tr></table>
</div>
<div class="paragraph"><p>This guide covers the three types of caching that Rails provides by default.</p></div>
<div class="paragraph"><p>This guide describes how to use Action Mailer to send and receive emails.</p></div>
</div></div>
<h2>Digging Deeper</h2>
<div class="sidebarblock">
<div class="sidebar-content">
<div class="sidebar-title"><a href="testing_rails_applications.html">Testing Rails Applications</a></div>
@ -212,7 +212,7 @@ of your code.</p></div>
<div class="paragraph"><p>This guide covers the command line tools and rake tasks provided by Rails.</p></div>
</div></div>
<div class="paragraph"><p>Authors who have contributed to complete guides are listed <a href="authors.html">here</a>.</p></div>
<div class="paragraph"><p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-Noncommercial-Share Alike 3.0 License</a></p></div>
<div class="paragraph"><p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0">Creative Commons Attribution-Share Alike 3.0 License</a></p></div>
</div>
</div>

View file

@ -430,7 +430,8 @@ Previously the above code made available a local variable called +customer+ insi
* The +%s+ and +%d+ interpolation syntax for internationalization is deprecated.
* +String#chars+ has been deprecated in favor of +String#mb_chars+.
* Durations of fractional months or fractional years are deprecated. Use Ruby's core +Date+ and +Time+ class arithmetic instead.
* +Request#relative_url_root+ is deprecated. Use +ActionController::Base.relative_url_root+ instead.
== Credits
Release notes compiled by link:http://afreshcup.com[Mike Gunderloy]
Release notes compiled by link:http://afreshcup.com[Mike Gunderloy]

View file

@ -0,0 +1,514 @@
Ruby on Rails 2.3 Release Notes
===============================
Rails 2.3 delivers a variety of new and improved features, including pervasive Rack integration, refreshed support for Rails Engines, nested transactions for Active Record, dynamic and default scopes, unified rendering, more efficient routing, application templates, and quiet backtraces. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the link:http://github.com/rails/rails/commits/master[list of commits] in the main Rails repository on GitHub or review the +CHANGELOG+ files for the individual Rails components.
== Application Architecture
There are two major changes in the architecture of Rails applications: complete integration of the link:http://rack.rubyforge.org/[Rack] modular web server interface, and renewed support for Rails Engines.
=== Rack Integration
Rails has now broken with its CGI past, and uses Rack everywhere. This required and resulted in a tremendous number of internal changes (but if you use CGI, don't worry; Rails now supports CGI through a proxy interface.) Still, this is a major change to Rails internals. After upgrading to 2.3, you should test on your local environment and your production environment. Some things to test:
* Sessions
* Cookies
* File uploads
* JSON/XML APIs
Here's a summary of the rack-related changes:
* +script/server+ has been switched to use Rack, which means it supports any Rack compatible server. +script/server+ will also pick up a rackup configuration file if one exists. By default, it will look for a +config.ru+ file, but you can override this with the +-c+ switch.
* The FCGI handler goes through Rack
* +ActionController::Dispatcher+ maintains its own default middleware stack. Middlewares can be injected in, reordered, and removed. The stack is compiled into a chain on boot. You can configure the middleware stack in +environment.rb+
* The +rake middleware+ task has been added to inspect the middleware stack. This is useful for debugging the order of the middleware stack.
* The integration test runner has been modified to execute the entire middleware and application stack. This makes integration tests perfect for testing Rack middleware.
* +ActionController::CGIHandler+ is a backwards compatible CGI wrapper around Rack. The +CGIHandler+ is meant to take an old CGI object and converts its environment information into a Rack compatible form.
* +CgiRequest+ and +CgiResponse+ have been removed
* Session stores are now lazy loaded. If you never access the session object during a request, it will never attempt to load the session data (parse the cookie, load the data from memcache, or lookup an Active Record object).
* +CGI::Session::CookieStore+ has been replaced by +ActionController::Session::CookieStore+
* +CGI::Session::MemCacheStore+ has been replaced by +ActionController::Session::MemCacheStore+
* +CGI::Session::ActiveRecordStore+ has been replaced by +ActiveRecord::SessionStore+
* You can still change your session store with +ActionController::Base.session_store = :active_record_store+
* Default sessions options are still set with +ActionController::Base.session = { :key => "..." }+
* The mutex that normally wraps your entire request has been moved into middleware, +ActionController::Lock+
* +ActionController::AbstractRequest+ and +ActionController::Request+ have been unified. The new +ActionController::Request+ inherits from +Rack::Request+. This affects access to +response.headers['type']+ in test requests. Use +response.content_type+ instead.
* +ActiveRecord::QueryCache+ middleware is automatically inserted onto the middleware stack if +ActiveRecord+ has been loaded. This middleware sets up and flushes the per-request Active Record query cache.
* The Rails router and controller classes follow the Rack spec. You can call a controller directly with +SomeController.call(env)+. The router stores the routing parameters in +rack.routing_args+.
* +ActionController::Request+ inherits from +Rack::Request+
* Instead of +config.action_controller.session = { :session_key => 'foo', ...+ use +config.action_controller.session = { :key => 'foo', ...+
* Using the +ParamsParser+ middleware preprocesses any XML, JSON, or YAML requests so they can be read normally with any +Rack::Request+ object after it.
=== Renewed Support for Rails Engines
After some versions without an upgrade, Rails 2.3 offers some new features for Rails Engines (Rails applications that can be embedded within other applications). First, routing files in engines are automatically loaded and reloaded now, just like your +routes.rb+ file (this also applies to routing files in other plugins). Second, if your plugin has an app folder, then app/[models|controllers|helpers] will automatically be added to the Rails load path. Engines also support adding view paths now.
== Documentation
The link:http://guides.rubyonrails.org/[Ruby on Rails guides] project has published several additional guides for Rails 2.3. In addition, a link:http://guides.rails.info/[separate site] maintains updated copies of the Guides for Edge Rails. Other documentation efforts include a relaunch of the link:http://newwiki.rubyonrails.org/[Rails wiki] and early planning for a Rails Book.
* More Information: link:http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects[Rails Documentation Projects]
== Active Record
Active Record gets quite a number of new features and bug fixes in Rails 2.3. The highlights include nested attributes, nested transactions, dynamic scopes, and default scopes.
=== Nested Attributes
Active Record can now update the attributes on nested models directly, provided you tell it to do so:
[source, ruby]
-------------------------------------------------------
class Book < ActiveRecord::Base
has_one :author
has_many :pages
accepts_nested_attributes_for :author, :pages
end
-------------------------------------------------------
Turning on nested attributes enables a number of things: automatic (and atomic) saving of a record together with its associated children, child-aware validations, and support for nested forms (discussed later).
* Lead Contributor: link_to:http://www.superalloy.nl/blog/[Eloy Duran]
* More Information: link_to:http://weblog.rubyonrails.org/2009/1/26/nested-model-forms[Nested Model Forms]
=== Nested Transactions
Active Record now supports nested transactions, a much-requested feature. Now you can write code like this:
[source, ruby]
-------------------------------------------------------
User.transaction do
User.create(:username => 'Admin')
User.transaction(:requires_new => true) do
User.create(:username => 'Regular')
raise ActiveRecord::Rollback
end
end
User.find(:all) # => Returns only Admin
-------------------------------------------------------
Nested transactions let you roll back an inner transaction without affecting the state of the outer transaction. If you want a transaction to be nested, you must explicitly add the +:requires_new+ option; otherwise, a nested transaction simply becomes part of the parent transaction (as it does currently on Rails 2.2). Under the covers, nested transactions are link:http://rails.lighthouseapp.com/projects/8994/tickets/383[using savepoints], so they're supported even on databases that don't have true nested transactions. There is also a bit of magic going on to make these transactions play well with transactional fixtures during testing.
* Lead Contributors: link:http://www.workingwithrails.com/person/4985-jonathan-viney[Jonathan Viney] and link:http://izumi.plan99.net/blog/[Hongli Lai]
=== Dynamic Scopes
You know about dynamic finders in Rails (which allow you to concoct methods like +find_by_color_and_flavor+ on the fly) and named scopes (which allow you to encapsulate reusable query conditions into friendly names like +currently_active+). Well, now you can have dynamic scope methods. The idea is to put together syntax that allows filtering on the fly _and_ method chaining. For example:
[source, ruby]
-------------------------------------------------------
Order.scoped_by_customer_id(12)
Order.scoped_by_customer_id(12).find(:all,
:conditions => "status = 'open'")
Order.scoped_by_customer_id(12).scoped_by_status("open")
-------------------------------------------------------
There's nothing to define to use dynamic scopes: they just work.
* Lead Contributor: link:http://evilmartians.com/[Yaroslav Markin]
* More Information: link:http://ryandaigle.com/articles/2008/12/29/what-s-new-in-edge-rails-dynamic-scope-methods[What's New in Edge Rails: Dynamic Scope Methods].
=== Default Scopes
Rails 2.3 will introduce the notion of _default scopes_ similar to named scopes, but applying to all named scopes or find methods within the model. For example, you can write +default_scope :order => 'name ASC'+ and any time you retrieve records from that model they'll come out sorted by name (unless you override the option, of course).
* Lead Contributor: Paweł Kondzior
* More Information: link:http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping[What's New in Edge Rails: Default Scoping]
=== Multiple Conditions for Callbacks
When using Active Record callbacks, you can now combine +:if+ and +:unless+ options on the same callback, and supply multiple conditions as an array:
[source, ruby]
-------------------------------------------------------
before_save :update_credit_rating, :if => :active,
:unless => [:admin, :cash_only]
-------------------------------------------------------
* Lead Contributor: L. Caviola
=== Find with having
Rails now has a +:having+ option on find (as well as on +has_many+ and +has_and_belongs_to_many+ associations) for filtering records in grouped finds. As those with heavy SQL backgrounds know, this allows filtering based on grouped results:
[source, ruby]
-------------------------------------------------------
developers = Developer.find(:all, :group => "salary",
:having => "sum(salary) > 10000", :select => "salary")
-------------------------------------------------------
* Lead Contributor: link:http://github.com/miloops[Emilio Tagua]
=== Hash Conditions for +has_many+ relationships
You can once again use a hash in conditions for a +has_many+ relationship:
[source, ruby]
-------------------------------------------------------
has_many :orders, :conditions => {:status => 'confirmed'}
-------------------------------------------------------
That worked in Rails 2.1, fails in Rails 2.2, and will now work again in Rails 2.3 (if you're dealing with this issue in Rails 2.2, you can use a string rather than a hash to specify conditions).
* Lead Contributor: link:http://www.spacevatican.org/[Frederick Cheung]
=== Reconnecting MySQL Connections
MySQL supports a reconnect flag in its connections - if set to true, then the client will try reconnecting to the server before giving up in case of a lost connection. You can now set +reconnect = true+ for your MySQL connections in +database.yml+ to get this behavior from a Rails application. The default is +false+, so the behavior of existing applications doesn't change.
* Lead Contributor: link:http://twitter.com/dubek[Dov Murik]
* More information:
- link:http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html[Controlling Automatic Reconnection Behavior]
- link:http://groups.google.com/group/rubyonrails-core/browse_thread/thread/49d2a7e9c96cb9f4[MySQL auto-reconnect revisited]
=== Other Active Record Changes
* An extra +AS+ was removed from the generated SQL for has_and_belongs_to_many preloading, making it work better for some databases.
* +ActiveRecord::Base#new_record?+ now returns false rather than nil when confronted with an existing record.
* A bug in quoting table names in some +has_many :through+ associations was fixed.
* You can now specify a particular timestamp for +updated_at+ timestamps: +cust = Customer.create(:name => "ABC Industries", :updated_at => 1.day.ago)+
* Better error messages on failed +find_by_attribute!+ calls.
* Active Record's +to_xml+ support gets just a little bit more flexible with the addition of a +:camelize+ option.
* A bug in canceling callbacks from +before_update+ or +before_create_ was fixed.
* Rake tasks for testing databases via JDBC have been added.
* +validates_length_of+ will use a custom error message with the +:in+ or +:within+ options (if one is supplied)
== Action Controller
Action Controller rolls out some significant changes to rendering, as well as improvements in routing and other areas, in this release.
=== Unified Rendering
+ActionController::Base#render+ is a lot smarter about deciding what to render. Now you can just tell it what to render and expect to get the right results. In older versions of Rails, you often need to supply explicit information to render:
[source, ruby]
-------------------------------------------------------
render :file => '/tmp/random_file.erb'
render :template => 'other_controller/action'
render :action => 'show'
-------------------------------------------------------
Now in Rails 2.3, you can just supply what you want to render:
[source, ruby]
-------------------------------------------------------
render '/tmp/random_file.erb'
render 'other_controller/action'
render 'show'
render :show
-------------------------------------------------------
Rails chooses between file, template, and action depending on whether there is a leading slash, an embedded slash, or no slash at all in what's to be rendered. Note that you can also use a symbol instead of a string when rendering an action. Other rendering styles (+:inline+, +:text+, +:update+, +:nothing+, +:json+, +:xml+, +:js+) still require an explicit option.
=== Application Controller Renamed
If you're one of the people who has always been bothered by the special-case naming of +application.rb+, rejoice! It's been reworked to be application_controller.rb in Rails 2.3. In addition, there's a new rake task, +rake rails:update:application_controller+ to do this automatically for you - and it will be run as part of the normal +rake rails:update+ process.
* More Information:
- link:http://afreshcup.com/2008/11/17/rails-2x-the-death-of-applicationrb/[The Death of Application.rb]
- link:http://ryandaigle.com/articles/2008/11/19/what-s-new-in-edge-rails-application-rb-duality-is-no-more[What's New in Edge Rails: Application.rb Duality is no More]
=== HTTP Digest Authentication Support
Rails now has built-in support for HTTP digest authentication. To use it, you call +authenticate_or_request_with_http_digest+ with a block that returns the users password (which is then hashed and compared against the transmitted credentials):
[source, ruby]
-------------------------------------------------------
class PostsController < ApplicationController
Users = {"dhh" => "secret"}
before_filter :authenticate
def secret
render :text => "Password Required!"
end
private
def authenticate
realm = "Application"
authenticate_or_request_with_http_digest(realm) do |name|
Users[name]
end
end
end
-------------------------------------------------------
* Lead Contributor: link:http://www.kellogg-assoc.com/[Gregg Kellogg]
* More Information: link:http://ryandaigle.com/articles/2009/1/30/what-s-new-in-edge-rails-http-digest-authentication[What's New in Edge Rails: HTTP Digest Authentication]
=== More Efficient Routing
There are a couple of significant routing changes in Rails 2.3. The +formatted_+ route helpers are gone, in favor just passing in +:format+ as an option. This cuts down the route generation process by 50% for any resource - and can save a substantial amount of memory (up to 100MB on large applications). If your code uses the +formatted_+ helpers, it will still work for the time being - but that behavior is deprecated and your application will be more efficient if you rewrite those routes using the new standard. Another big change is that Rails now supports multiple routing files, not just routes.rb. You can use +RouteSet#add_configuration_file+ to bring in more routes at any time - without clearing the currently-loaded routes. While this change is most useful for Engines, you can use it in any application that needs to load routes in batches.
Lead Contributors: link:http://blog.hungrymachine.com/[Aaron Batalion]
=== Rack-based Lazy-loaded Sessions
A big change pushed the underpinnings of Action Controller session storage down to the Rack level. This involved a good deal of work in the code, though it should be completely transparent to your Rails applications (as a bonus, some icky patches around the old CGI session handler got removed). It's still significant, though, for one simple reason: non-Rails Rack applications have access to the same session storage handlers (and therefore the same session) as your Rails applications. In addition, sessions are now lazy-loaded (in line with the loading improvements to the rest of the framework). This means that you no longer need to explicitly disable sessions if you don't want them; just don't refer to them and they won't load.
=== MIME Type Handling Changes
There are a couple of changes to the code for handling MIME types in Rails. First, +MIME::Type+ now implements the +=~+ operator, making things much cleaner when you need to check for the presence of a type that has synonyms:
[source, ruby]
-------------------------------------------------------
if content_type && Mime::JS =~ content_type
# do something cool
end
Mime::JS =~ "text/javascript" => true
Mime::JS =~ "application/javascript" => true
-------------------------------------------------------
The other change is that the framework now uses the +Mime::JS+ when checking for javascript in various spots, making it handle those alternatives cleanly.
Lead Contributor: link:http://www.workingwithrails.com/person/5510-seth-fitzsimmons[Seth Fitzsimmons]
=== Optimization of +respond_to+
In some of the first fruits of the Rails-Merb team merger, Rails 2.3 includes some optimizations for the +respond_to+ method, which is of course heavily used in many Rails applications to allow your controller to format results differently based on the MIME type of the incoming request. After eliminating a call to +method_missing+ and some profiling and tweaking, we're seeing an 8% improvement in the number of requests per second served with a simple +respond_to+ that switches between three formats. The best part? No change at all required to the code of your application to take advantage of this speedup.
=== Improved Caching Performance
Rails now keeps a per-request local cache of requests, cutting down on unnecessary reads and leading to better site performance. While this work was originally limited to +MemCacheStore+, it is available to any remote store than implements the required methods.
* Lead Contributor: link:http://www.motionstandingstill.com/[Nahum Wild]
=== Localized Views
Rails can now provide localized views, depending on the locale that you have set. For example, suppose you have a +Posts+ controller with a +show+ action. By default, this will render +app/views/posts/show.html.erb+. But if you set +I18n.locale = :da+, it will render +app/views/posts/show.da.html.erb+. If the localized template isn't present, the undecorated version will be used. Rails also includes +I18n#available_locales+ and +I18n::SimpleBackend#available_locales+, which return an array of the translations that are available in the current Rails project.
=== Other Action Controller Changes
* ETag handling has been cleaned up a bit: Rails will now skip sending an ETag header when there's no body to the response or when sending files with +send_file+.
* The fact that Rails checks for IP spoofing can be a nuisance for sites that do heavy traffic with cell phones, because their proxies don't generally set things up right. If that's you, you can now set +ActionController::Base.ip_spoofing_check = false+ to disable the check entirely.
* +ActionController::Dispatcher+ now implements its own middleware stack, which you can see by running +rake middleware+.
* Cookie sessions now have persistent session identifiers, with API compatibility with the server-side stores.
* You can now use symbols for the +:type+ option of +send_file+ and +send_data+, like this: +send_file("fabulous.png", :type => :png)+.
* The +:only+ and +:except+ options for +map.resources+ are no longer inherited by nested resources.
== Action View
Action View in Rails 2.3 picks up nested model forms, improvements to +render+, more flexible prompts for the date select helpers, and a speedup in asset caching, among other things.
=== Nested Object Forms
Provided the parent model accepts nested attributes for the child objects (as discussed in the Active Record section), you can create nested forms using +form_for+ and +field_for+. These forms can be nested arbitrarily deep, allowing you to edit complex object hierarchies on a single view without excessive code. For example, given this model:
[source, ruby]
-------------------------------------------------------
class Customer < ActiveRecord::Base
has_many :orders
accepts_nested_attributes_for :orders, :allow_destroy => true
end
-------------------------------------------------------
You can write this view in Rails 2.3:
[source, ruby]
-------------------------------------------------------
<% form_for @customer do |customer_form| %>
<div>
<%= customer_form.label :name, 'Customer Name:' %>
<%= customer_form.text_field :name %>
</div>
<!-- Here we call fields_for on the customer_form builder instance.
The block is called for each member of the orders collection. -->
<% customer_form.fields_for :orders do |order_form| %>
<p>
<div>
<%= order_form.label :number, 'Order Number:' %>
<%= order_form.text_field :number %>
</div>
<!-- The allow_destroy option in the model enables deletion of
child records. -->
<% unless order_form.object.new_record? %>
<div>
<%= order_form.label :_delete, 'Remove:' %>
<%= order_form.check_box :_delete %>
</div>
<% end %>
</p>
<% end %>
<% end %>
<%= customer_form.submit %>
<% end %>
-------------------------------------------------------
* Lead Contributor: link_to:http://www.superalloy.nl/blog/[Eloy Duran]
* More Information:
- link_to:http://weblog.rubyonrails.org/2009/1/26/nested-model-forms[Nested Model Forms]
- link_to:http://github.com/alloy/complex-form-examples/tree/nested_attributes[complex-form-examples]
- link_to:http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes[What's New in Edge Rails: Nested Object Forms]
=== Smart Rendering of Partials
The render method has been getting smarter over the years, and it's even smarter now. If you have an object or a collection and an appropriate partial, and the naming matches up, you can now just render the object and things will work. For example, in Rails 2.3, these render calls will work in your view (assuming sensible naming):
[source, ruby]
-------------------------------------------------------
render @article # Equivalent of render :partial => 'articles/_article', :object => @article
render @articles # Equivalent of render :partial => 'articles/_article', :collection => @articles
-------------------------------------------------------
* More Information: link:http://ryandaigle.com/articles/2008/11/20/what-s-new-in-edge-rails-render-stops-being-high-maintenance[What's New in Edge Rails: render Stops Being High-Maintenance]
=== Prompts for Date Select Helpers
In Rails 2.3, you can supply custom prompts for the various date select helpers (+date_select+, +time_select+, and +datetime_select+), the same way you can with collection select helpers. You can supply a prompt string or a hash of individual prompt strings for the various components. You can also just set +:prompt+ to +true+ to use the custom generic prompt:
[source, ruby]
-------------------------------------------------------
select_datetime(DateTime.now, :prompt => true)
select_datetime(DateTime.now, :prompt => "Choose date and time")
select_datetime(DateTime.now, :prompt =>
{:day => 'Choose day', :month => 'Choose month',
:year => 'Choose year', :hour => 'Choose hour',
:minute => 'Choose minute'})
-------------------------------------------------------
Lead Contributor: link:http://samoliver.com/[Sam Oliver]
=== AssetTag Timestamp Caching
You're likely familiar with Rails' practice of adding timestamps to static asset paths as a "cache buster." This helps ensure that stale copies of things like images and stylesheets don't get served out of the user's browser cache when you change them on the server. You can now modify this behavior with the +cache_asset_timestamps+ configuration option for Action View. If you enable the cache, then Rails will calculate the timestamp once when it first serves an asset, and save that value. This means fewer (expensive) file system calls to serve static assets - but it also means that you can't modify any of the assets while the server is running and expect the changes to get picked up by clients.
=== Asset Hosts as Objects
Asset hosts get more flexible in edge Rails with the ability to declare an asset host as a specific object that responds to a call. This allows you to to implement any complex logic you need in your asset hosting.
* More Information: link:http://github.com/dhh/asset-hosting-with-minimum-ssl/tree/master[asset-hosting-with-minimum-ssl]
=== grouped_options_for_select Helper Method
Action View already haD a bunch of helpers to aid in generating select controls, but now there's one more: +grouped_options_for_select+. This one accepts an array or hash of strings, and converts them into a string of +option+ tags wrapped with +optgroup+ tags. For example:
[source, ruby]
-------------------------------------------------------
grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]],
"Cowboy Hat", "Choose a product...")
-------------------------------------------------------
returns
[source, ruby]
-------------------------------------------------------
<option value="">Choose a product...</option>
<optgroup label="Hats">
<option value="Baseball Cap">Baseball Cap</option>
<option selected="selected" value="Cowboy Hat">Cowboy Hat</option>
</optgroup>
-------------------------------------------------------
=== Other Action View Changes
* Token generation for CSRF protection has been simplified; now Rails uses a simple random string generated by +ActiveSupport::SecureRandom+ rather than mucking around with session IDs.
* +auto_link+ now properly applies options (such as :target and :class) to generated e-mail links.
* The +autolink+ helper has been refactored to make it a bit less messy and more intuitive.
== Active Support
Active Support has a few interesting changes, including the introduction of +Object#try+.
=== Object#try
A lot of folks have adopted the notion of using try() to attempt operations on objects - It's especially helpful in views where you can avoid nil-checking by writing code like +<%= @person.try(:name) %>+. Well, now it's baked right into Rails. As implemented in Rails, it raises +NoMethodError+ on private methods and always returns +nil+ if the object is nil.
* More Information: link:http://ozmm.org/posts/try.html[try()].
=== Object#tap Backport
+Object#tap+ is an addition to "Ruby 1.9":http://www.ruby-doc.org/core-1.9/classes/Object.html#M000309 and 1.8.7 that is similar to the +returning+ method that Rails has had for a while: it yields to a block, and then returns the object that was yielded. Rails now includes code to make this available under older versions of Ruby as well.
=== Fractional seconds for TimeWithZone
The +Time+ and +TimeWithZone+ classes include an +xmlschema+ method to return the time in an XML-friendly string. As of Rails 2.3, +TimeWithZone+ supports the same argument for specifying the number of digits in the fractional second part of the returned string that +Time+ does:
[source, ruby]
-------------------------------------------------------
>> Time.zone.now.xmlschema(6)
=> "2009-01-16T13:00:06.13653Z"
-------------------------------------------------------
Lead Contributor: link:http://www.workingwithrails.com/person/13536-nicholas-dainty[Nicholas Dainty]
=== JSON Key Quoting
If you look up the spec on the "json.org" site, you'll discover that all keys in a JSON structure must be strings, and they must be quoted with double quotes. Starting with Rails 2.3, we doe the right thing here, even with numeric keys.
=== Other Active Support Changes
* You can use +Enumerable#none?+ to check that none of the elements match the supplied block.
* If you're using Active Support link:http://afreshcup.com/2008/10/19/coming-in-rails-22-delegate-prefixes/[delegates], the new +:allow_nil+ option lets you return nil instead of raising an exception when the target object is nil.
* +ActiveSupport::OrderedHash+: now implements +each_key+ and +each_value+.
* +ActiveSupport::MessageEncryptor+ provides a simple way to encrypt information for storage in an untrusted location (like cookies).
* Active Support's +from_xml+ no longer depends on XmlSimple. Instead, Rails now includes its own XmlMini implementation, with just the functionality that it requires. This lets Rails dispense with the bundled copy of XmlSimple that it's been carting around.
== Railties
In addition to the Rack changes covered above, Railties (the core code of Rails itself) sports a number of significant changes, including Rails Metal, application templates, and quiet backtraces.
=== Rails Metal
Rails Metal is a new mechanism that provides superfast endpoints inside of your Rails applications. Metal classes bypass routing and Action Controller to give you raw speed (at the cost of all the things in Action Controller, of course). This builds on all of the recent foundation work to make Rails a Rack application with an exposed middleware stack.
* More Information:
- link:http://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal[Introducing Rails Metal]
- link:http://soylentfoo.jnewland.com/articles/2008/12/16/rails-metal-a-micro-framework-with-the-power-of-rails-m[Rails Metal: a micro-framework with the power of Rails]
- link:http://www.railsinside.com/deployment/180-metal-super-fast-endpoints-within-your-rails-apps.html[Metal: Super-fast Endpoints within your Rails Apps]
- link:http://ryandaigle.com/articles/2008/12/18/what-s-new-in-edge-rails-rails-metal[What's New in Edge Rails: Rails Metal]
=== Application Templates
Rails 2.3 incorporates Jeremy McAnally's link:http://github.com/jeremymcanally/rg/tree/master[rg] application generator. What this means is that we now have template-based application generation built right into Rails; if you have a set of plugins you include in every application (among many other use cases), you can just set up a template once and use it over and over again when you run the +rails+ command. There's also a rake task to apply a template to an existing application:
[source, ruby]
-------------------------------------------------------
rake rails:template LOCATION=~/template.rb
-------------------------------------------------------
This will layer the changes from the template on top of whatever code the project already contains.
* Lead Contributor: link:http://www.jeremymcanally.com/[Jeremy McAnally]
* More Info:http://m.onkey.org/2008/12/4/rails-templates[Rails templates]
=== Quieter Backtraces
Building on Thoughtbot's link:http://www.thoughtbot.com/projects/quietbacktrace[Quiet Backtrace] plugin, which allows you to selectively remove lines from +Test::Unit+ backtraces, Rails 2.3 implements +ActiveSupport::BacktraceCleaner+ and +Rails::BacktraceCleaner+ in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a +config/backtrace_silencers.rb+ file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace.
=== Faster Boot Time in Development Mode with Lazy Loading/Autoload
Quite a bit of work was done to make sure that bits of Rails (and its dependencies) are only brought into memory when they're actually needed. The core frameworks - Active Support, Active Record, Action Controller, Action Mailer and Action View - are now using +autoload+ to lazy-load their individual classes. This work should help keep the memory footprint down and improve overall Rails performance.
You can also specify (by using the new +preload_frameworks+ option) whether the core libraries should be autoloaded at startup. This defaults to +false+ so that Rails autoloads itself piece-by-piece, but there are some circumstances where you still need to bring in everything at once - Passenger and JRuby both want to see all of Rails loaded together.
=== Other Railties Changes
* The instructions for updating a CI server to build Rails have been updated and expanded.
* Internal Rails testing has been switched from +Test::Unit::TestCase+ to +ActiveSupport::TestCase+, and the Rails core requires Mocha to test.
* The default +environment.rb+ file has been decluttered.
* The dbconsole script now lets you use an all-numeric password without crashing.
* Rails.root now returns a Pathname object, which means you can use it directly with the join method to link:http://afreshcup.com/2008/12/05/a-little-rails_root-tidiness/[clean up existing code] that uses File.join.
* Various files in /public that deal with CGI and FCGI dispatching are no longer generated in every Rails application by default (you can still get them if you need them by adding +--with-dispatches+ when you run the rails command, or add them later with +rake rails:generate_dispatchers+).
== Deprecated
A few pieces of older code are deprecated in this release:
* If you're one of the (fairly rare) Rails developers who deploys in a fashion that depends on the inspector, reaper, and spawner scripts, you'll need to know that those scripts are no longer included in core Rails. If you need them, you'll be able to pick up copies via the link:http://github.com/rails/irs_process_scripts/tree[irs_process_scripts] plugin.
* +render_component+ goes from "deprecated" to "nonexistent" in Rails 2.3. If you still need it, you can install the link:http://github.com/rails/render_component/tree/master[render_component plugin].
* Support for Rails components has been removed.
* If you were one of the people who got used to running +script/performance/request+ to look at performance based on integration tests, you need to learn a new trick: that script has been removed from core Rails now. Theres a new request_profiler plugin that you can install to get the exact same functionality back.
* +ActionController::Base#session_enabled?+ is deprecated because sessions are lazy-loaded now.
* The +:digest+ and +:secret+ options to +protect_from_forgery+ are deprecated and have no effect.
* Some integration test helpers have been removed. +response.headers["Status"]+ and +headers["Status"]+ will no longer return anything. Rack does not allow "Status" in its return headers. However you can still use the +status+ and +status_message+ helpers. +response.headers["cookie"]+ and +headers["cookie"]+ will no longer return any CGI cookies. You can inspect +headers["Set-Cookie"]+ to see the raw cookie header or use the +cookies+ helper to get a hash of the cookies sent to the client.
* +formatted_polymorphic_url+ is deprecated. Use +polymorphic_url+ with +:format+ instead.
== Credits
Release notes compiled by link:http://afreshcup.com[Mike Gunderloy]

View file

@ -1,16 +1,17 @@
Action Mailer Basics
====================
This guide should provide you with all you need to get started in sending emails from your application, and will also cover how to test your mailers.
This guide should provide you with all you need to get started in sending and receiving emails from/to your application, and many internals of the ActionMailer class. It will also cover how to test your mailers.
== What is Action Mailer?
== Introduction
Action Mailer allows you to send email from your application using a mailer model and views.
Yes, that is correct, in Rails, emails are used by creating Models that inherit from ActionMailer::Base. They live alongside other models in /app/models BUT they have views just like controllers that appear alongside other views in app/views.
Yes, that is correct, in Rails, emails are used by creating models that inherit from ActionMailer::Base. They live alongside other models in /app/models BUT they have views just like controllers that appear alongside other views in app/views.
== Quick walkthrough to creating a Mailer
== Sending Emails
Let's say you want to send a welcome email to a user after they signup. Here is how you would go about this:
=== 1. Create the mailer:
=== Walkthrough to generating a Mailer
==== Create the mailer:
[source, shell]
-------------------------------------------------------
./script/generate mailer UserMailer
@ -24,7 +25,9 @@ create test/unit/user_mailer_test.rb
So we got the model, the fixtures, and the tests all created for us
=== 2. Edit the model:
==== Edit the model:
If you look at app/models/user_mailer.rb, you will see:
[source, ruby]
-------------------------------------------------------
class UserMailer < ActionMailer::Base
@ -33,7 +36,6 @@ end
-------------------------------------------------------
Lets add a method called welcome_email, that will send an email to the user's registered email address:
[source, ruby]
-------------------------------------------------------
class UserMailer < ActionMailer::Base
@ -51,15 +53,18 @@ end
-------------------------------------------------------
So what do we have here?
recipients: who the recipients are, put in an array for multiple, ie, @recipients = ["user1@example.com", "user2@example.com"]
from: Who the email will appear to come from in the recipients' mailbox
subject: The subject of the email
sent_on: Timestamp for the email
content_type: The content type, by default is text/plain
[width="100%", cols="20%,80%"]
|======================================================
|recipients| who the recipients are, put in an array for multiple, ie, @recipients = ["user1@example.com", "user2@example.com"]
|from| Who the email will appear to come from in the recipients' mailbox
|subject| The subject of the email
|sent_on| Timestamp for the email
|content_type| The content type, by default is text/plain
|======================================================
How about @body[:user]? Well anything you put in the @body hash will appear in the mailer view (more about mailer views below) as an instance variable ready for you to use, ie, in our example the mailer view will have a @user instance variable available for its consumption.
=== 3. Create the mailer view
==== Create the mailer view
Create a file called welcome_email.html.erb in #{RAILS_ROOT}/app/views/user_mailer/ . This will be the template used for the email. This file will be used for html formatted emails. Had we wanted to send text-only emails, the file would have been called welcome_email.txt.erb, and we would have set the content type to text/plain in the mailer model.
The file can look like:
@ -72,7 +77,6 @@ The file can look like:
</head>
<body>
<h1>Welcome to example.com, <%= @user.first_name %></h1>
<p>
You have successfully signed up to example.com, and your username is: <%= @user.login %>.<br/>
To login to the site, just follow this link: <%= @url %>.
@ -82,10 +86,12 @@ The file can look like:
</html>
-------------------------------------------------------
=== 4. Wire it up so that the system sends the email when a user signs up
There are 3 was to achieve this. One is to send the email from the controller that sends the email, another is to put it in a before_create block in the user model, and the last one is to use an observer on the user model. Whether you use the second or third methods is up to you, but staying away from the first is recommended. Not because it's wrong, but because it keeps your controller clean, and keeps all logic related to the user model within the user model. This way, whichever way a user is created (from a web form, or from an API call, for example), we are guaranteed that the email will be sent.
==== Wire it up so that the system sends the email when a user signs up
There are 3 ways to achieve this. One is to send the email from the controller that sends the email, another is to put it in a before_create block in the user model, and the last one is to use an observer on the user model. Whether you use the second or third methods is up to you, but staying away from the first is recommended. Not because it's wrong, but because it keeps your controller clean, and keeps all logic related to the user model within the user model. This way, whichever way a user is created (from a web form, or from an API call, for example), we are guaranteed that the email will be sent.
Edit #{RAILS_ROOT}/config/environment.rb
Let's see how we would go about wiring it up using an observer:
In config/environment.rb:
[source, ruby]
-------------------------------------------------------
# Code that already exists
@ -99,7 +105,7 @@ Rails::Initializer.run do |config|
end
-------------------------------------------------------
There was a bit of a debate on where to put observers. I put them in models, but you can create #{RAILS_ROOT}/app/observers if you like, and add that to your load path. Open #{RAILS_ROOT}/config/environment.rb and make it look like:
There was a bit of a debate on where to put observers. Some people put them in app/models, but a cleaner method may be to create an app/observers folder to store all observers, and add that to your load path. Open config/environment.rb and make it look like:
[source, ruby]
-------------------------------------------------------
# Code that already exists
@ -116,7 +122,7 @@ end
-------------------------------------------------------
ALMOST THERE :) Now all we need is that danged observer, and we're done:
Create a file called user_observer in #{RAILS_ROOT}/app/models or #{RAILS_ROOT}/app/observers, and make it look like:
Create a file called user_observer in app/models or app/observers depending on where you stored it, and make it look like:
[source, ruby]
-------------------------------------------------------
class UserObserver < ActiveRecord::Observer
@ -126,8 +132,352 @@ class UserObserver < ActiveRecord::Observer
end
-------------------------------------------------------
Notice how we call deliver_welcome_email? Where is that method? Well if you remember, we created a method called welcome_email in UserMailer, right? Well, as part of the "magic" of rails, we deliver the email identified by welcome_email by calling deliver_welcome_email.
Notice how we call deliver_welcome_email? Where is that method? Well if you remember, we created a method called welcome_email in UserMailer, right? Well, as part of the "magic" of rails, we deliver the email identified by welcome_email by calling deliver_welcome_email. The next section will go through this in more detail.
That's it! Now whenever your users signup, they will be greeted with a nice welcome email. Next up, we'll talk about how to test a mailer model.
That's it! Now whenever your users signup, they will be greeted with a nice welcome email.
== Mailer Testing
=== Action Mailer and dynamic deliver_ methods
So how does Action Mailer understand this deliver_welcome_email call? If you read the documentation (http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html), you will find this in the "Sending Emails" section:
You never instantiate your mailer class. Rather, your delivery instance
methods are automatically wrapped in class methods that start with the word
deliver_ followed by the name of the mailer method that you would
like to deliver. The signup_notification method defined above is
delivered by invoking Notifier.deliver_signup_notification.
So, how exactly does this work?
In ActionMailer:Base, you will find this:
[source, ruby]
-------------------------------------------------------
def method_missing(method_symbol, *parameters)#:nodoc:
case method_symbol.id2name
when /^create_([_a-z]\w*)/ then new($1, *parameters).mail
when /^deliver_([_a-z]\w*)/ then new($1, *parameters).deliver!
when "new" then nil
else super
end
end
-------------------------------------------------------
Ah, this makes things so much clearer :) so if the method name starts with deliver_ followed by any combination of lowercase letters or underscore, method missing calls new on your mailer class (UserMailer in our example above), sending the combination of lower case letters or underscore, along with the parameter. The resulting object is then sent the deliver! method, which well... delivers it.
=== Complete List of ActionMailer user-settable attributes
[width="100%", cols="20%,80%"]
|======================================================
|bcc| Specify the BCC addresses for the message
|body| Define the body of the message. 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 text of the message.
|cc| Specify the CC addresses for the message.
|charset| Specify the charset to use for the message. This defaults to the default_charset specified for ActionMailer::Base.
|content_type| Specify the content type for the message. This defaults to <text/plain in most cases, but can be automatically set in some situations.
|from| Specify the from address for the message.
|reply_to| Specify the address (if different than the "from" address) to direct replies to this message.
|headers| Specify additional headers to be added to the message.
|implicit_parts_order| Specify the order in which parts should be sorted, based on content-type. This defaults to the value for the default_implicit_parts_order.
|mime_version| Defaults to "1.0", but may be explicitly given if needed.
|recipient| The recipient addresses for the message, either as a string (for a single address) or an array (for multiple addresses).
|sent_on| The date on which the message was sent. If not set (the default), the header will be set by the delivery agent.
|subject| Specify the subject of the message.
|template| Specify the template name to use for current message. This is the "base" template name, without the extension or directory, and may be used to have multiple mailer methods share the same template.
|======================================================
=== Mailer Views
Mailer views are located in /app/views/name_of_mailer_class. The specific mailer view is known to the class because it's name is the same as the mailer method. So for example, in our example from above, our mailer view for the welcome_email method will be in /app/views/user_mailer/welcome_email.html.erb for the html version and welcome_email.txt.erb for the plain text version.
To change the default mailer view for your action you do something like:
[source, ruby]
-------------------------------------------------------
class UserMailer < ActionMailer::Base
def welcome_email(user)
recipients user.email
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"}
content_type "text/html"
# change the default from welcome_email.[html, txt].erb
template "some_other_template" # this will be in app/views/user_mailer/some_other_template.[html, txt].erb
end
end
-------------------------------------------------------
=== Action Mailer Layouts
Just like controller views, you can also have mailer layouts. The layout name needs to end in _mailer to be automatically recognized by your mailer as a layout. So in our UserMailer example, we need to call our layout user_mailer.[html,txt].erb. In order to use a different file just use:
[source, ruby]
-------------------------------------------------------
class UserMailer < ActionMailer::Base
layout 'awesome' # will use awesome.html.erb as the layout
end
-------------------------------------------------------
Just like with controller views, use yield to render the view inside the layout.
=== Generating URL's in Action Mailer views
URLs can be generated in mailer views using url_for or named routes.
Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need to provide all of the details needed to generate a URL.
When using url_for you'll need to provide the :host, :controller, and :action:
<%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
When using named routes you only need to supply the :host:
<%= users_url(:host => "example.com") %>
You will want to avoid using the name_of_route_path form of named routes because it doesn't make sense to generate relative URLs in email messages. The reason that it doesn't make sense is because the email is opened on a mail client outside of your environment. Since the email is not being served by your server, a URL like "/users/show/1", will have no context. In order for the email client to properly link to a URL on your server it needs something like "http://yourserver.com/users/show/1".
It is also possible to set a default host that will be used in all mailers by setting the :host option in
the ActionMailer::Base.default_url_options hash as follows:
ActionMailer::Base.default_url_options[:host] = "example.com"
This can also be set as a configuration option in config/environment.rb:
config.action_mailer.default_url_options = { :host => "example.com" }
If you do decide to set a default :host for your mailers you will want to use the :only_path => false option when using url_for. This will ensure that absolute URLs are generated because the url_for view helper will, by default, generate relative URLs when a :host option isn't explicitly provided.
=== Sending multipart emails
Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have welcome_email.txt.erb and welcome_email.html.erb in app/views/user_mailer, Action Mailer will automatically send a multipart email with the html and text versions setup as different parts.
To explicitly specify multipart messages, you can do something like:
[source, ruby]
-------------------------------------------------------
class UserMailer < ActionMailer::Base
def welcome_email(user)
recipients user.email_address
subject "New account information"
from "system@example.com"
content_type "multipart/alternative"
part :content_type => "text/html",
:body => "<p>html content, can also be the name of an action that you call<p>"
part "text/plain" do |p|
p.body = "text content, can also be the name of an action that you call"
end
end
end
-------------------------------------------------------
=== Sending emails with attachments
Attachments can be added by using the attachment method:
[source, ruby]
-------------------------------------------------------
class UserMailer < ActionMailer::Base
def welcome_email(user)
recipients user.email_address
subject "New account information"
from "system@example.com"
content_type "multipart/alternative"
attachment :content_type => "image/jpeg",
:body => File.read("an-image.jpg")
attachment "application/pdf" do |a|
a.body = generate_your_pdf_here()
end
end
end
-------------------------------------------------------
== Receiving Emails
Receiving and parsing emails with Action Mailer can be a rather complex endeavour. Before your email reaches your Rails app, you would have had to configure your system to somehow forward emails to your app, which needs to be listening for that.
So, to receive emails in your Rails app you'll need:
1. Configure your email server to forward emails from the address(es) you would like your app to receive to /path/to/app/script/runner \'UserMailer.receive(STDIN.read)'
2. Implement a receive method in your mailer
Once a method called receive is defined in any mailer, Action Mailer will parse the raw incoming email into an email object, decode it, instantiate a new mailer, and pass the email object to the mailer objects receive method. Here's an example:
[source, ruby]
-------------------------------------------------------
class UserMailer < ActionMailer::Base
def receive(email)
page = Page.find_by_address(email.to.first)
page.emails.create(
:subject => email.subject,
:body => email.body
)
if email.has_attachments?
for attachment in email.attachments
page.attachments.create({
:file => attachment,
:description => email.subject
})
end
end
end
end
-------------------------------------------------------
== Using Action Mailer Helpers
Action Mailer classes have 4 helper methods available to them:
[width="100%", cols="2,8"]
|======================================================
|add_template_helper(helper_module)|Makes all the (instance) methods in the helper module available to templates rendered through this controller.
|helper(*args, &block)|
Declare a helper:
helper :foo
requires 'foo_helper' and includes FooHelper in the template class.
helper FooHelper
includes FooHelper in the template class.
helper { def foo() "#{bar} is the very best" end }
evaluates the block in the template class, adding method foo.
helper(:three, BlindHelper) { def mice() 'mice' end }
does all three.
|helper_method|
Declare a controller method as a helper. For example,
helper_method :link_to
def link_to(name, options) ... end
makes the link_to controller method available in the view.
|helper_attr|
Declare a controller attribute as a helper. For example,
helper_attr :name
attr_accessor :name
makes the name and name= controller methods available in the view.
The is a convenience wrapper for helper_method.
|======================================================
== Action Mailer Configuration
The following configuration options are best made in one of the environment files (environment.rb, production.rb, etc...)
[width="100%", cols="2,8a"]
|======================================================
|template_root|Determines the base from which template references will be made.
|logger|the logger is used for generating information on the mailing run if available.
Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
|smtp_settings|Allows detailed configuration for :smtp delivery method:
[cols="20%,80%"]
!======================================================
!:address !Allows you to use a remote mail server. Just change it from its default "localhost" setting.
!:port !On the off chance that your mail server doesn't run on port 25, you can change it.
!:domain !If you need to specify a HELO domain, you can do it here.
!:user_name !If your mail server requires authentication, set the username in this setting.
!:password !If your mail server requires authentication, set the password in this setting.
!:authentication !If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of :plain, :login, :cram_md5.
!======================================================
|sendmail_settings|Allows you to override options for the :sendmail delivery method.
[cols="20%,80%"]
!======================================================
!:location!The location of the sendmail executable. Defaults to /usr/sbin/sendmail.
!:arguments!The command line arguments. Defaults to -i -t.
!======================================================
|raise_delivery_errors|Whether or not errors should be raised if the email fails to be delivered.
|delivery_method|Defines a delivery method. Possible values are :smtp (default), :sendmail, and :test.
|perform_deliveries|Determines whether deliver_* methods are actually carried out. By default they are,
but this can be turned off to help functional testing.
|deliveries|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful
for unit and functional testing.
|default_charset|The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
pick a different charset from inside a method with charset.
|default_content_type|The default content type used for the main part of the message. Defaults to "text/plain". You
can also pick a different content type from inside a method with content_type.
|default_mime_version|The default mime version used for the message. Defaults to 1.0. You
can also pick a different value from inside a method with mime_version.
|default_implicit_parts_order|When a message is built implicitly (i.e. multiple parts are assembled from templates
which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client
and appear last in the mime encoded message. You can also pick a different order from inside a method with
implicit_parts_order.
|======================================================
=== Example Action Mailer Configuration
An example would be:
[source, ruby]
-------------------------------------------------------
ActionMailer::Base.delivery_method = :sendmail
ActionMailer::Base.sendmail_settings = {
:location => '/usr/sbin/sendmail',
:arguments => '-i -t'
}
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.default_charset = "iso-8859-1"
-------------------------------------------------------
=== Action Mailer Configuration for GMail
Instructions copied from http://http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html
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.
[source, ruby]
-------------------------------------------------------
ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "domain.com",
:user_name => "user@domain.com",
:password => "password",
:authentication => :plain
}
-------------------------------------------------------
=== Configure Action Mailer to recognize HAML templates
In environment.rb, add the following line:
[source, ruby]
-------------------------------------------------------
ActionMailer::Base.register_template_extension('haml')
-------------------------------------------------------
== Mailer Testing
Testing mailers involves 2 things. One is that the mail was queued and the other that the body contains what we expect it to contain. With that in mind, we could test our example mailer from above like so:
[source, ruby]
-------------------------------------------------------
class UserMailerTest < ActionMailer::TestCase
tests UserMailer
def test_welcome_email
user = users(:some_user_in_your_fixtures)
# Send the email, then test that it got queued
email = UserMailer.deliver_welcome_email(user)
assert !ActionMailer::Base.deliveries.empty?
# Test the body of the sent email contains what we expect it to
assert_equal [@user.email], email.to
assert_equal "Welcome to My Awesome Site", email.subject
assert email.body =~ /Welcome to example.com, #{user.first_name}/
end
end
-------------------------------------------------------
What have we done? Well, we sent the email and stored the returned object in the email variable. We then ensured that it was sent (the first assert), then, in the second batch of assertion, we ensure that the email does indeed contain the values that we expect.
== Epilogue
This guide presented how to create a mailer and how to test it. In reality, you may find that writing your tests before you actually write your code to be a rewarding experience. It may take some time to get used to TDD (Test Driven Development), but coding this way achieves two major benefits. Firstly, you know that the code does indeed work, because the tests fail (because there's no code), then they pass, because the code that satisfies the tests was written. Secondly, when you start with the tests, you don't have to make time AFTER you write the code, to write the tests, then never get around to it. The tests are already there and testing has now become part of your coding regimen.

View file

@ -1,6 +1,13 @@
== HTTP Basic Authentication ==
== HTTP Authentications ==
Rails comes with built-in HTTP Basic authentication. This is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, `authenticate_or_request_with_http_basic`.
Rails comes with two built-in HTTP authentication mechanisms :
* Basic Authentication
* Digest Authentication
=== HTTP Basic Authentication ===
HTTP Basic authentication is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, `authenticate_or_request_with_http_basic`.
[source, ruby]
-------------------------------------
@ -10,7 +17,7 @@ class AdminController < ApplicationController
before_filter :authenticate
private
private
def authenticate
authenticate_or_request_with_http_basic do |username, password|
@ -22,3 +29,29 @@ end
-------------------------------------
With this in place, you can create namespaced controllers that inherit from AdminController. The before filter will thus be run for all actions in those controllers, protecting them with HTTP Basic authentication.
=== HTTP Digest Authentication ===
HTTP Digest authentication is superior to the Basic authentication as it does not require the client to send unencrypted password over the network. Using Digest authentication with Rails is quite easy and only requires using one method, +authenticate_or_request_with_http_digest+.
[source, ruby]
-------------------------------------
class AdminController < ApplicationController
USERS = { "lifo" => "world" }
before_filter :authenticate
private
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
end
end
end
-------------------------------------
As seen in the example above, +authenticate_or_request_with_http_digest+ block takes only one argument - the username. And the block returns the password. Returning +false+ or +nil+ from the +authenticate_or_request_with_http_digest+ will cause authentication failure.

View file

@ -1,154 +1,141 @@
Active Record Basics
====================
Active Record is a design pattern that mitigates the mind-numbing mental gymnastics often needed to get your application to communicate with a database. This guide uses a mix of real-world examples, metaphors and detailed explanations of the actual Rails source code to help you make the most of ActiveRecord.
This guide will give you a strong grasp of the Active Record pattern and how it can be used with or without Rails. Hopefully, some of the philosophical and theoretical intentions discussed here will also make you a stronger and better developer.
After reading this guide readers should have a strong grasp of the Active Record pattern and how it can be used with or without Rails. Hopefully, some of the philosophical and theoretical intentions discussed here will also make them a stronger and better developer.
After reading this guide we hope that you'll be able to:
== ORM The Blueprint of Active Record
* Understand the way Active Record fits into the MVC model.
* Create basic Active Record models and map them with your database tables.
* Use your models to execute CRUD (Create, Read, Update and Delete) database operations.
* Follow the naming conventions used by Rails to make developing database applications easier and obvious.
* Take advantage of the way Active Record maps it's attributes with the database tables' columns to implement your application's logic.
* Use Active Record with legacy databases that do not follow the Rails naming conventions.
If Active Record is the engine of Rails then ORM is the blueprint of that engine. ORM is short for “Object Relational Mapping” and is a programming concept used to make structures within a system relational. As a thought experiment imagine the components that make up a typical car. There are doors, seats, windows, engines etc. Viewed independently they are simple parts, yet when bolted together through the aid of a blueprint, the parts become a more complex device. ORM is the blueprint that describes how the individual parts relate to one another and in some cases infers the parts purpose through the way the associations are described.
== What's Active Record
== Active Record The Engine of Rails
Rails' ActiveRecord is an implementation of Martin Fowler's http://martinfowler.com/eaaCatalog/activeRecord.html[Active Record Design Pattern]. This pattern is based on the idea of creating relations between the database and the application in the following way:
Active Record is a design pattern used to access data within a database. The name “Active Record” was coined by Martin Fowler in his book “Patterns of Enterprise Application Architecture”. Essentially, when a record is returned from the database instead of being just the data it is wrapped in a class, which gives you methods to control that data with. The rails framework is built around the MVC (Model View Controller) design patten and the Active Record is used as the default Model.
* Each database table is mapped to a class.
* Each table column is mapped to an attribute of this class.
* Each instance of this class is mapped to a single row in the database table.
The Rails community added several useful concepts to their version of Active Record, including inheritance and associations, which are extremely useful for web applications. The associations are created by using a DSL (domain specific language) of macros, and inheritance is achieved through the use of STI (Single Table Inheritance) at the database level.
The definition of the Active Record pattern in Martin Fowler's words:
By following a few simple conventions the Rails Active Record will automatically map between:
"_An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data."_
* Classes & Database Tables
* Class attributes & Database Table Columns
== Object Relational Mapping
=== Rails Active Record Conventions
Here are the key conventions to consider when using Active Record.
The relation between databases and object-oriented software is called ORM, which is short for "Object Relational Mapping". The purpose of an ORM framework is to minimize the mismatch existent between relational databases and object-oriented software. In applications with a domain model, we have objects that represent both the state of the system and the behaviour of the real world elements that were modeled through these objects. Since we need to store the system's state somehow, we can use relational databases, which are proven to be an excelent approach to data management. Usually this may become a very hard thing to do, since we need to create an object-oriented model of everything that lives in the database, from simple columns to complicated relations between different tables. Doing this kind of thing by hand is a tedious and error prone job. This is where an ORM framework comes in.
==== Naming Conventions
Database Table - Plural with underscores separating words i.e. (book_clubs)
Model Class - Singular with the first letter of each word capitalized i.e. (BookClub)
Here are some additional Examples:
== ActiveRecord as an ORM framework
[grid="all"]
`-------------`---------------
Model / Class Table / Schema
----------------------------
Post posts
LineItem line_items
Deer deer
Mouse mice
Person people
----------------------------
ActiveRecord gives us several mechanisms, being the most important ones the hability to:
==== Schema Conventions
* Represent models.
* Represent associations between these models.
* Represent inheritance hierarquies through related models.
* Validate models before they get recorded to the database.
* Perform database operations in an object-oriented fashion.
To take advantage of some of the magic of Rails database tables must be modeled
to reflect the ORM decisions that Rails makes.
It's easy to see that the Rails Active Record implementation goes way beyond the basic description of the Active Record Pattern.
[grid="all"]
`-------------`---------------------------------------------------------------------------------
Convention
-------------------------------------------------------------------------------------------------
Foreign keys These fields are named table_id i.e. (item_id, order_id)
Primary Key Rails automatically creates a primary key column named "id" unless told otherwise.
-------------------------------------------------------------------------------------------------
== Active Record inside the MVC model
==== Magic Field Names
Active Record plays the role of model inside the MVC structure followed by Rails applications. Since model objects should encapsulate both state and logic of your applications, it's ActiveRecord responsability to deliver you the easiest possible way to recover this data from the database.
When these optional fields are used in your database table definition they give the Active Record
instance additional features.
== Convention over Configuration in ActiveRecord
NOTE: While these column names are optional they are in fact reserved by ActiveRecord. Steer clear of reserved keywords unless you want the extra functionality. For example, "type" is a reserved keyword
used to designate a table using Single Table Inheritance. If you are not using STI, try an analogous
keyword like "context", that may still accurately describe the data you are modeling.
When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particulary true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating ActiveRecord models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicity configuration would be needed only in those cases where you can't follow the conventions for any reason.
[grid="all"]
`------------------------`------------------------------------------------------------------------------
Attribute Purpose
------------------------------------------------------------------------------------------------------
created_at / created_on Rails stores the current date & time to this field when creating the record.
updated_at / updated_on Rails stores the current date & time to this field when updating the record.
lock_version Adds optimistic locking to a model link:http://api.rubyonrails.com/classes/ActiveRecord/Locking.html[more about optimistic locking].
type Specifies that the model uses Single Table Inheritance link:http://api.rubyonrails.com/classes/ActiveRecord/Base.html[more about STI].
id All models require an id. the default is name is "id" but can be changed using the "set_primary_key" or "primary_key" methods.
_table_name_\_count Can be used to caches the number of belonging objects on the associated class.
------------------------------------------------------------------------------------------------------
=== Naming Conventions
By default rails assumes all tables will use “id” as their primary key to identify each record. Though fortunately you wont have explicitly declare this, Rails will automatically create that field unless you tell it not to.
By default, ActiveRecord uses some naming conventions to find out how the mapping between models and database tables should be created. Rails will pluralize your class names to find the respective database table. So, for a class +Book+, you should have a database table called *books*. The Rails pluralization mechanisms are very powerful, being capable to pluralize (and singularize) both regular and irregular words. When using class names composed of two or more words, the model class name should follow the Ruby conventions, using the camelCase form, while the table name must contain the words separated by underscores. Examples:
For example suppose you created a database table called cars:
* Database Table - Plural with underscores separating words i.e. (book_clubs)
* Model Class - Singular with the first letter of each word capitalized i.e. (BookClub)
[width="60%", options="header"]
|==============================
|Model / Class |Table / Schema
|Post |posts
|LineItem |line_items
|Deer |deer
|Mouse |mice
|Person |people
|==============================
=== 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 http://guides.rails.info/migrations.html[Rails Migrations] to create your tables, this column will be automaticaly created.
There are also some optional column names that will create additional features to ActiveRecord instances:
* *created_at / created_on* - ActiveRecord will store the current date and time to this field when creating the record.
* *updated_at / updated_on* - ActiveRecord will store the current date and times to this field when updating the record.
* *lock_version* - Adds http://api.rubyonrails.com/classes/ActiveRecord/Locking.html[optimistic locking] to a model.
* *type* - Specifies that the model uses http://api.rubyonrails.com/classes/ActiveRecord/Base.html[Single Table Inheritance]
* *(table_name)_count* - Used to cache the number of belonging objects on associations. For example, a +comments_count+ column in a +Post+ class that has many instances of +Comment+ will cache the number of existent comments for each post.
NOTE: While these column names are optional they are in fact reserved by ActiveRecord. Steer clear of reserved keywords unless you want the extra functionality. For example, "type" is a reserved keyword used to designate a table using Single Table Inheritance. If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling.
== Creating ActiveRecord models
It's very easy to create ActiveRecord models. All you have to do is to subclass the ActiveRecord::Base class and you're good to go:
[source, ruby]
------------------------------------------------------------------
class Product < ActiveRecord::Base; end
------------------------------------------------------------------
This will create a +Product+ model, mapped to a *products* table at the database. By doing this you'll also have the hability to map the columns of each row in that table with the attributes of the instances of your model. So, suppose that the *products* table was created using a SQL sentence like:
[source, sql]
-------------------------------------------------------
mysql> CREATE TABLE cars (
id INT,
color VARCHAR(100),
doors INT,
horses INT,
model VARCHAR(100)
);
-------------------------------------------------------
------------------------------------------------------------------
CREATE TABLE products (
id int(11) NOT NULL auto_increment,
name varchar(255),
PRIMARY KEY (id)
);
------------------------------------------------------------------
Now you created a class named Car, which is to represent an instance of a record from your table.
Following the table schema above, you would be able to write code like the following:
[source, ruby]
-------------------------------------------------------
class Car
------------------------------------------------------------------
p = Product.new
p.name = "Some Book"
puts p.name # "Some Book"
------------------------------------------------------------------
== Overriding the naming conventions
What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions.
You can use the +ActiveRecord::Base.set_table_name+ method to specify the table name that should be used:
[source, ruby]
------------------------------------------------------------------
class Product < ActiveRecord::Base
set_table_name "PRODUCT"
end
-------------------------------------------------------
As you might expect without defining the explicit mappings between your class and the table it is impossible for Rails or any other program to correctly map those relationships.
------------------------------------------------------------------
It's also possible to override the column that should be used as the table's primary key. Use the +ActiveRecord::Base.set_primary_key+ method for that:
[source, ruby]
-------------------------------------------------------
>> c = Car.new
=> #<Class:0x11e1e90>
>> c.doors
NoMethodError: undefined method `doors' for #<Class:0x11e1e90>
from (irb):2
-------------------------------------------------------
Now you could define a door methods to write and read data to and from the database. In a nutshell this is what ActiveRecord does. According to the Rails API:
“Active Record objects dont specify their attributes directly, but rather infer them from the table definition with which theyre linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.”
Lets try our Car class again, this time inheriting from ActiveRecord.
[source, ruby]
-------------------------------------------------------
class Car < ActiveRecord::Base
------------------------------------------------------------------
class Product < ActiveRecord::Base
set_primary_key "product_id"
end
-------------------------------------------------------
------------------------------------------------------------------
Now if we try to access an attribute of the table ActiveRecord automatically handles the mappings for us, as you can see in the following example.
== Validations
[source, ruby]
-------------------------------------------------------
>> c = Car.new
=> #<Car id: nil, doors: nil, color: nil, horses: nil, model: nil>
>> c.doors
=> nil
-------------------------------------------------------
ActiveRecord gives the hability 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 lifecycle 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 http://guides.rails.info/activerecord_validations_callbacks.html#_overview_of_activerecord_validation[Active Record Validations and Callbacks guide].
Rails further extends this model by giving each ActiveRecord a way of describing the variety of ways records are associated with one another. We will touch on some of these associations later in the guide but I encourage readers who are interested to read the guide to ActiveRecord associations for an in-depth explanation of the variety of ways rails can model associations.
- Associations between objects controlled by meta-programming macros.
== Callbacks
== Philosophical Approaches & Common Conventions
Rails has a reputation of being a zero-config framework which means that it aims to get you off the ground with as little pre-flight checking as possible. This speed benefit is achieved by following “Convention over Configuration”, which is to say that if you agree to live with the defaults then you benefit from a the inherent speed-boost. As Courtneay Gasking put it to me once “You dont want to off-road on Rails”. ActiveRecord is no different, while its possible to override or subvert any of the conventions of AR, unless you have a good reason for doing so you will probably be happy with the defaults. The following is a list of the common conventions of ActiveRecord
ActiveRecord callbacks allow you to attach code to certain events in the lifecycle of your models. This way you can add behaviour 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 http://guides.rails.info/activerecord_validations_callbacks.html#_callbacks[Active Record Validations and Callbacks guide].
== ActiveRecord Magic
- timestamps
- updates
== How ActiveRecord Maps your Database.
- sensible defaults
- overriding conventions
== Growing Your Database Relationships Naturally
== Attributes
- attribute accessor method. How to override them?
- attribute?
- dirty records
-
== ActiveRecord handling the CRUD of your Rails application - Understanding the life-cycle of an ActiveRecord
== Validations & Callbacks
see the Validations & Callbacks guide for more info.

View file

@ -5,7 +5,7 @@ If you prefer to use RSpec instead of Test::Unit, you may be interested in the h
=== References ===
* http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i
* http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii
* http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-ii
* http://github.com/technoweenie/attachment_fu/tree/master
* http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html
* http://www.mbleigh.com/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins

View file

@ -0,0 +1,4 @@
# To change this template, choose Tools | Templates
# and open the template in the editor.
puts "Hello World"

View file

@ -120,7 +120,7 @@ Rails::Generator::Commands::List.send :include, Yaffle::Generator::Commands
Rails::Generator::Commands::Update.send :include, Yaffle::Generator::Commands::Update
-----------------------------------------------------------
*vendor/plugins/yaffle/generators/yaffle/yaffle_route_generator.rb*
*vendor/plugins/yaffle/generators/yaffle_route/yaffle_route_generator.rb*
[source, ruby]
-----------------------------------------------------------

View file

@ -28,26 +28,7 @@ Here are a few possibilities for how to allow developers to use your plugin migr
=== Create a custom rake task ===
*vendor/plugins/yaffle/lib/db/migrate/20081116181115_create_birdhouses.rb:*
[source, ruby]
----------------------------------------------
class CreateBirdhouses < ActiveRecord::Migration
def self.up
create_table :birdhouses, :force => true do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :birdhouses
end
end
----------------------------------------------
*vendor/plugins/yaffle/tasks/yaffle.rake:*
*vendor/plugins/yaffle/tasks/yaffle_tasks.rake:*
[source, ruby]
----------------------------------------------
@ -153,7 +134,7 @@ NOTE: the migration generator checks to see if a migation already exists, and it
After running the test with 'rake' you can make it pass with:
*vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb*
*vendor/plugins/yaffle/generators/yaffle_migration/yaffle_migration_generator.rb*
[source, ruby]
------------------------------------------------------------------

View file

@ -20,7 +20,7 @@ vendor/plugins/yaffle/
As always, start with a test:
*vendor/plugins/yaffle/yaffle/woodpecker_test.rb:*
*vendor/plugins/yaffle/test/woodpecker_test.rb:*
[source, ruby]
----------------------------------------------

View file

@ -1,10 +1,10 @@
== Rake tasks ==
When you created the plugin with the built-in rails generator, it generated a rake file for you in 'vendor/plugins/yaffle/tasks/yaffle.rake'. Any rake task you add here will be available to the app.
When you created the plugin with the built-in rails generator, it generated a rake file for you in 'vendor/plugins/yaffle/tasks/yaffle_tasks.rake'. Any rake task you add here will be available to the app.
Many plugin authors put all of their rake tasks into a common namespace that is the same as the plugin, like so:
*vendor/plugins/yaffle/tasks/yaffle.rake*
*vendor/plugins/yaffle/tasks/yaffle_tasks.rake*
[source, ruby]
---------------------------------------------------------

View file

@ -116,9 +116,6 @@ ActiveRecord::Schema.define(:version => 0) do
t.string :last_tweet
t.datetime :last_tweeted_at
end
create_table :woodpeckers, :force => true do |t|
t.string :name
end
end
----------------------------------------------

View file

@ -1,21 +1,22 @@
Rails form helpers
==================
Mislav Marohnić <mislav.marohnic@gmail.com>
Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of form control naming and their numerous attributes. Rails deals away with these complexities by providing view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use.
In this guide you will:
* Create search forms and similar kind of generic forms not representing any specific model in your application;
* Make model-centric forms for creation and editing of specific database records;
* Generate select boxes from multiple types of data;
* Learn what makes a file upload form different;
* Create search forms and similar kind of generic forms not representing any specific model in your application
* Make model-centric forms for creation and editing of specific database records
* Generate select boxes from multiple types of data
* Understand the date and time helpers Rails provides
* Learn what makes a file upload form different
* Find out where to look for complex forms
NOTE: This guide is not intended to be a complete documentation of available form helpers and their arguments. Please visit http://api.rubyonrails.org/[the Rails API documentation] for a complete reference.
Basic forms
-----------
Dealing With Basic Forms
------------------------
The most basic form helper is `form_tag`.
@ -25,7 +26,7 @@ The most basic form helper is `form_tag`.
<% end %>
----------------------------------------------------------------------------
When called without arguments like this, it creates a form element that has the current page for action attribute and "POST" as method (some line breaks added for readability):
When called without arguments like this, it creates a form element that has the current page as its action and "post" as its method (some line breaks added for readability):
.Sample output from `form_tag`
----------------------------------------------------------------------------
@ -37,12 +38,12 @@ When called without arguments like this, it creates a form element that has the
</form>
----------------------------------------------------------------------------
If you carefully observe this output, you can see that the helper generated something you didn't specify: a `div` element with a hidden input inside. This is a security feature of Rails called *cross-site request forgery protection* and form helpers generate it for every form which action isn't "GET" (provided that this security feature is enabled).
If you carefully observe this output, you can see that the helper generated something you didn't specify: a `div` element with a hidden input inside. This is a security feature of Rails called *cross-site request forgery protection* and form helpers generate it for every form whose action is not "get" (provided that this security feature is enabled). You can read more about this in the link:./security.html#_cross_site_reference_forgery_csrf[Ruby On Rails Security Guide].
NOTE: Throughout this guide, this `div` with the hidden input will be stripped away to have clearer code samples.
Generic search form
~~~~~~~~~~~~~~~~~~~
A Generic search form
~~~~~~~~~~~~~~~~~~~~~
Probably the most minimal form often seen on the web is a search form with a single text input for search terms. This form consists of:
@ -51,9 +52,9 @@ Probably the most minimal form often seen on the web is a search form with a sin
3. a text input element, and
4. a submit element.
IMPORTANT: Always use "GET" as the method for search forms. Benefits are many: users are able to bookmark a specific search and get back to it; browsers cache results of "GET" requests, but not "POST"; and others.
IMPORTANT: Always use "GET" as the method for search forms. This allows users are able to bookmark a specific search and get back to it, more generally Rails encourages you to use the right HTTP verb for an action.
To create that, you will use `form_tag`, `label_tag`, `text_field_tag` and `submit_tag`, respectively.
To create this form you will use `form_tag`, `label_tag`, `text_field_tag` and `submit_tag`, respectively.
.A basic search form
----------------------------------------------------------------------------
@ -88,10 +89,10 @@ Besides `text_field_tag` and `submit_tag`, there is a similar helper for _every_
TIP: For every form input, an ID attribute is generated from its name ("q" in the example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.
Multiple hashes in form helper attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Multiple hashes in form helper calls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By now you've seen that the `form_tag` helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element's class.
By now you've seen that the `form_tag` helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element's class.
As with the `link_to` helper, the path argument doesn't have to be given a string. It can be a hash of URL parameters that Rails' routing mechanism will turn into a valid URL. Still, you cannot simply write this:
@ -113,9 +114,23 @@ This is a common pitfall when using form helpers, since many of them accept mult
WARNING: Do not delimit the second hash without doing so with the first hash, otherwise your method invocation will result in an `expecting tASSOC` syntax error.
Checkboxes, radio buttons and other controls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Helpers for generating form elements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rails provides a series of helpers for generating form elements such as checkboxes, text fields, radio buttons and so. These basic helpers, with names ending in _tag such as `text_field_tag`, `check_box_tag` just generate a single `<input>` element. The first parameter to these is always the name of the input. In the controller, this name will be the key in the `params` hash used to get the value entered by the user. For example if the form contains
---------------------------
<%= text_field_tag(:query) %>
---------------------------
then the controller code should use
---------------------------
params[:query]
---------------------------
to retrieve the value entered by the user. When naming inputs be aware that Rails uses certain conventions that control whether values are at the top level of the `params` hash, inside an array or a nested hash and so on. You can read more about them in the <<parameter_names,parameter names>> section. For details on the precise usage of these helpers, please refer to the http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html[API documentation].
Checkboxes
^^^^^^^^^^
Checkboxes are form controls that give the user a set of options they can enable or disable:
----------------------------------------------------------------------------
@ -132,6 +147,10 @@ output:
<label for="pet_cat">I own a cat</label>
----------------------------------------------------------------------------
The second parameter to `check_box_tag` is the value of the input. This is the value that will be submitted by the browser if the checkbox is ticked (i.e. the value that will be present in the `params` hash). With the above form you would check the value of `params[:pet_dog]` and `params[:pet_cat]` to see which pets the user owns.
Radio buttons
^^^^^^^^^^^^^
Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (user can only pick one):
----------------------------------------------------------------------------
@ -148,9 +167,13 @@ output:
<label for="age_adult">I'm over 21</label>
----------------------------------------------------------------------------
As with `check_box_tag` the second parameter to `radio_button_tag` is the value of the input. Because these two radio buttons share the same name (age) the user will only be able to select one and `params[:age]` will contain either "child" or "adult".
IMPORTANT: Always use labels for each checkbox and radio button. They associate text with a specific option and provide a larger clickable region.
Other form controls worth mentioning are the text area, password input and hidden input:
Other helpers of interest
^^^^^^^^^^^^^^^^^^^^^^^^^
Other form controls worth mentioning are the text area, password input and hidden input:
----------------------------------------------------------------------------
<%= text_area_tag(:message, "Hi, nice site", :size => "24x6") %>
@ -164,55 +187,20 @@ output:
<input id="parent_id" name="parent_id" type="hidden" value="5" />
----------------------------------------------------------------------------
Hidden inputs are not shown to the user, but they hold data same as any textual input. Values inside them can be changed with JavaScript.
Hidden inputs are not shown to the user, but they hold data like any textual input. Values inside them can be changed with JavaScript.
TIP: If you're using password input fields (for any purpose), you might want to prevent their values showing up in application logs by activating `filter_parameter_logging(:password)` in your ApplicationController.
How do forms with PUT or DELETE methods work?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers _don't support_ methods other than "GET" and "POST" when it comes to submitting forms. How does this work, then?
Rails works around this issue by emulating other methods over POST with a hidden input named `"_method"` that is set to reflect the desired method:
----------------------------------------------------------------------------
form_tag(search_path, :method => "put")
output:
<form action="/search" method="post">
<div style="margin:0;padding:0">
<input name="_method" type="hidden" value="put" />
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
</div>
...
----------------------------------------------------------------------------
When parsing POSTed data, Rails will take into account the special `_method` parameter and act as if the HTTP method was the one specified inside it ("PUT" in this example).
Different Families of helpers
------------------------------
Most of Rails' form helpers are available in two forms.
Barebones helpers
~~~~~~~~~~~~~~~~~~
These just generate the appropriate markup. These have names ending in _tag such as `text_field_tag`, `check_box_tag`. The first parameter to these is always the name of the input. This is the name under which value will appear in the `params` hash in the controller. For example if the form contains
---------------------------
<%= text_field_tag(:query) %>
---------------------------
then the controller code should use
---------------------------
params[:query]
---------------------------
to retrieve the value entered by the user. When naming inputs be aware that Rails uses certain conventions that control whether values appear at the top level of the params hash, inside an array or a nested hash and so on. You can read more about them in the <<parameter_names,parameter names>> section. For details on the precise usage of these helpers, please refer to the http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html[API documentation].
Dealing With Model Objects
--------------------------
Model object helpers
~~~~~~~~~~~~~~~~~~~~~
These are designed to work with a model object (commonly an Active Record object but this need not be the case). These lack the _tag suffix, for example `text_field`, `text_area`.
~~~~~~~~~~~~~~~~~~~~~~
For these helpers the first arguement is the name of an instance variable and the second is the name a method (usually an attribute) to call on that object. Rails will set the value of the input control to the return value of that method for the object and set an appropriate input name. If your controller has defined `@person` and that person's name is Henry then a form containing:
A particularly common task for a form is editing or creating a model object. While the `*_tag` helpers can certainly be used for this task they are somewhat verbose as for each tag you would have to ensure the correct parameter name is used and set the default value of the input appropriately. Rails provides helpers tailored to this task. These helpers lack the _tag suffix, for example `text_field`, `text_area`.
For these helpers the first argument is the name of an instance variable and the second is the name of a method (usually an attribute) to call on that object. Rails will set the value of the input control to the return value of that method for the object and set an appropriate input name. If your controller has defined `@person` and that person's name is Henry then a form containing:
---------------------------
<%= text_field(:person, :name) %>
@ -221,16 +209,21 @@ will produce output similar to
---------------------------
<input id="person_name" name="person[name]" type="text" value="Henry"/>
---------------------------
Upon form submission the value entered by the user will be stored in `params[:person][:name]`. The `params[:person]` hash is suitable for passing to `Person.new` or, if `@person` is an instance of Person, `@person.update_attributes`.
Upon form submission the value entered by the user will be stored in `params[:person][:name]`. The `params[:person]` hash is suitable for passing to `Person.new` or, if `@person` is an instance of Person, `@person.update_attributes`. While the name of an attribute is the most common second parameter to these helpers this is not compulsory. In the example above, as long as person objects have a `name` and a `name=` method Rails will be happy.
[WARNING]
============================================================================
You must pass the name of an instance variable, i.e. `:person` or `"person"`, not an actual instance of your model object.
============================================================================
Forms that deal with model attributes
-------------------------------------
While the helpers seen so far are handy Rails can save you some work. For example typically a form is used to edit multiple attributes of a single object, so having to repeat the name of the object being edited is clumsy. The following examples will handle an Article model. First, have the controller create one:
Rails provides helpers for displaying the validation errors associated with a model object. These are covered in detail by the link:./activerecord_validations_callbacks.html#_using_the_tt_errors_tt_collection_in_your_view_templates[Active Record Validations and Callbacks] guide.
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
----------------------------------------------------------------------------
@ -239,7 +232,7 @@ def new
end
----------------------------------------------------------------------------
Now switch to the view. The first thing to remember is to use the `form_for` helper instead of `form_tag`, and that you should pass the model name and object as arguments:
The corresponding view using `form_for` looks like this
.articles/new.html.erb
----------------------------------------------------------------------------
@ -252,9 +245,9 @@ Now switch to the view. The first thing to remember is to use the `form_for` hel
There are a few things to note here:
1. `:article` is the name of the model and `@article` is the record.
1. `:article` is the name of the model and `@article` is the actual object being edited.
2. There is a single hash of options. Routing options are passed inside `:url` hash, HTML options are passed in the `:html` hash.
3. The `form_for` method yields *a form builder* object (the `f` variable).
3. The `form_for` method yields a *form builder* object (the `f` variable).
4. Methods to create form controls are called *on* the form builder object `f`
The resulting HTML is:
@ -266,14 +259,34 @@ The resulting HTML is:
<input name="commit" type="submit" value="Create" />
</form>
----------------------------------------------------------------------------
The name passed to `form_for` controls where in the params hash the form values will appear. Here the name is `article` and so all the inputs have names of the form `article[attribute_name]`. Accordingly, in the `create` action `params[:article]` will be a hash with keys `:title` and `:body`. You can read more about the significance of input names in the <<parameter_names,parameter names>> section.
The name passed to `form_for` controls the key used in `params` to access the form's values. Here the name is `article` and so all the inputs have names of the form `article[attribute_name]`. Accordingly, in the `create` action `params[:article]` will be a hash with keys `:title` and `:body`. You can read more about the significance of input names in the <<parameter_names,parameter names>> section.
The helper methods called on the form builder are identical to the model object helpers except that it is not necessary to specify which object is being edited since this is already managed by the form builder.
You can create a similar binding without actually creating `<form>` tags with the `fields_for` helper. This is useful for editing additional model objects with the same form. For example if you had a Person model with an associated ContactDetail model you could create a form for creating both like so:
-------------
<% form_for :person, @person, :url => { :action => "create" } do |person_form| %>
<%= person_form.text_field :name %>
<% fields_for @person.contact_detail do |contact_details_form| %>
<%= contact_details_form.text_field :phone_number %>
<% end %>
<% end %>
-------------
which produces the following output:
-------------
<form action="/people/create" class="new_person" id="new_person" method="post">
<input id="person_name" name="person[name]" size="30" type="text" />
<input id="contact_detail_phone_number" name="contact_detail[phone_number]" size="30" type="text" />
</form>
-------------
The object yielded by `fields_for` is a form builder like the one yielded by `form_for` (in fact `form_for` calls `fields_for` internally).
Relying on record identification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the previous chapter you handled the Article model. This model is directly available to users of our 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*.
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:
@ -293,7 +306,7 @@ form_for(@article)
Notice how the short-style `form_for` invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking `record.new_record?`. It also selects the correct path to submit to and the name based on the class of the object.
Rails will also automatically set the class and id of the form appropriately: a form creating an article would have id and class `new_article`. If you were editing the article with id 23 the class would be set to `edit_article` and the id to `edit_article_23`. The attributes will be omitted or brevity in the rest of this guide.
Rails will also automatically set the `class` and `id` of the form appropriately: a form creating an article would have `id` and `class` `new_article`. If you were editing the article with id 23 the `class` would be set to `edit_article` and the id to `edit_article_23`. These attributes will be omitted for brevity in the rest of this guide.
WARNING: When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, `:url` and `:method` explicitly.
@ -309,49 +322,71 @@ will create a form that submits to the articles controller inside the admin name
-------
form_for [:admin, :management, @article]
-------
For more information on Rails' routing system and the associated conventions, please see the link:../routing_outside_in.html[routing guide].
For more information on Rails' routing system and the associated conventions, please see the link:./routing_outside_in.html[routing guide].
How do forms with PUT or DELETE methods work?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers _don't support_ methods other than "GET" and "POST" when it comes to submitting forms.
Rails works around this issue by emulating other methods over POST with a hidden input named `"_method"` that is set to reflect the desired method:
----------------------------------------------------------------------------
form_tag(search_path, :method => "put")
output:
<form action="/search" method="post">
<div style="margin:0;padding:0">
<input name="_method" type="hidden" value="put" />
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
</div>
...
----------------------------------------------------------------------------
When parsing POSTed data, Rails will take into account the special `_method` parameter and acts as if the HTTP method was the one specified inside it ("PUT" in this example).
Making select boxes with ease
-----------------------------
Select boxes in HTML require a significant amount of markup (one `OPTION` element for each option to choose from), therefore it makes the most sense for them to be dynamically generated from data stored in arrays or hashes.
Select boxes in HTML require a significant amount of markup (one `OPTION` element for each option to choose from), therefore it makes the most sense for them to be dynamically generated.
Here is what our wanted markup might look like:
Here is what the markup might look like:
----------------------------------------------------------------------------
<select name="city_id" id="city_id">
<option value="1">Lisabon</option>
<option value="1">Lisbon</option>
<option value="2">Madrid</option>
...
<option value="12">Berlin</option>
</select>
----------------------------------------------------------------------------
Here you have a list of cities where their names are presented to the user, but internally the application only wants to handle their IDs so they are used as the options' value attributes. Let's see how Rails can help out here.
Here you have a list of cities whose names are presented to the user. Internally the application only wants to handle their IDs so they are used as the options' value attribute. Let's see how Rails can help out here.
The select tag and options
The select and options tag
~~~~~~~~~~~~~~~~~~~~~~~~~~
The most generic helper is `select_tag`, which -- as the name implies -- simply generates the `SELECT` tag that encapsulates an options string:
----------------------------------------------------------------------------
<%= select_tag(:city_id, '<option value="1">Lisabon</option>...') %>
<%= select_tag(:city_id, '<option value="1">Lisbon</option>...') %>
----------------------------------------------------------------------------
This is a start, but it doesn't dynamically create our option tags. You can generate option tags with the `options_for_select` helper:
This is a start, but it doesn't dynamically create the option tags. You can generate option tags with the `options_for_select` helper:
----------------------------------------------------------------------------
<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %>
output:
<option value="1">Lisabon</option>
<option value="1">Lisbon</option>
<option value="2">Madrid</option>
...
----------------------------------------------------------------------------
For input data you use a nested array where each element has two elements: option text (city name) and option value (city id). The option value is what will get submitted to your controller. It is often true that the option value is the id of a corresponding database object but this does not have to be the case.
The first argument to `options_for_select` is a nested array where each element has two elements: option text (city name) and option value (city id). The option value is what will be submitted to your controller. Often this will be the id of a corresponding database object but this does not have to be the case.
Knowing this, you can combine `select_tag` and `options_for_select` to achieve the desired, complete markup:
@ -359,71 +394,68 @@ Knowing this, you can combine `select_tag` and `options_for_select` to achieve t
<%= select_tag(:city_id, options_for_select(...)) %>
----------------------------------------------------------------------------
Sometimes, depending on an application's needs, you also wish a specific option to be pre-selected. The `options_for_select` helper supports this with an optional second argument:
`options_for_select` allows you to pre-select an option by passing its value.
----------------------------------------------------------------------------
<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...], 2) %>
<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...], 2) %>
output:
<option value="1">Lisabon</option>
<option value="1">Lisbon</option>
<option value="2" selected="selected">Madrid</option>
...
----------------------------------------------------------------------------
So whenever Rails sees that the internal value of an option being generated matches this value, it will add the `selected` attribute to that option.
Whenever Rails sees that the internal value of an option being generated matches this value, it will add the `selected` attribute to that option.
[TIP]
============================================================================
The second argument to `options_for_select` must be exactly equal to the desired internal value. In particular if the internal value is the integer 2 you cannot pass "2" to `options_for_select` -- you must pass 2. Be aware of values extracted from the params hash as they are all strings.
The second argument to `options_for_select` must be exactly equal to the desired internal value. In particular if the value is the integer 2 you cannot pass "2" to `options_for_select` -- you must pass 2. Be aware of values extracted from the `params` hash as they are all strings.
============================================================================
Select boxes for dealing with models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Until now you've seen how to make generic select boxes, but in most cases our form controls will be tied to a specific database model. So, to continue from our previous examples, let's assume that you have a "Person" model with a `city_id` attribute.
Consistent with other form helpers, when dealing with models you drop the `_tag` suffix from `select_tag`.
In most cases form controls will be tied to a specific database model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with models you drop the `_tag` suffix from `select_tag`:
----------------------------------------------------------------------------
# controller:
@person = Person.new(:city_id => 2)
# view:
<%= select(:person, :city_id, [['Lisabon', 1], ['Madrid', 2], ...]) %>
<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>
----------------------------------------------------------------------------
Notice that the third parameter, the options array, is the same kind of argument you pass to `options_for_select`. One advantage here is that you don't have to worry about pre-selecting the correct city if the user already has one -- Rails will do this for you by reading from the `@person.city_id` attribute.
As before, if you were to use `select` helper on a form builder scoped to `@person` object, the syntax would be:
As with other helpers, if you were to use `select` helper on a form builder scoped to `@person` object, the syntax would be:
----------------------------------------------------------------------------
# select on a form builder
<%= f.select(:city_id, ...) %>
----------------------------------------------------------------------------
[WARNING]
[WARNING]
=============================
If you are using `select` (or similar helpers such as `collection_select`, `select_tag`) to set a `belongs_to` association you must pass the name of the foreign key (in the example above `city_id`), not the name of association itself. If you specify `city` instead of `city_id Active Record will raise an error along the lines of
If you are using `select` (or similar helpers such as `collection_select`, `select_tag`) to set a `belongs_to` association you must pass the name of the foreign key (in the example above `city_id`), not the name of association itself.
If you specify `city` instead of `city_id` Active Record will raise an error along the lines of
--------
ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got Fixnum(#1138750)
ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750)
--------
when you pass the params hash to `Person.new` or `update_attributes`. Another way of looking at this is that form helpers only edit attributes.
when you pass the `params` hash to `Person.new` or `update_attributes`. Another way of looking at this is that form helpers only edit attributes.
You should also be aware of the potential security ramifications of allowing users to edit foreign keys directly. You may wish to consider the use of `attr_protected` and `attr_accessible`. For further details on this, see the link:security.html#_mass_assignment[Ruby On Rails Security Guide].
============================
Option tags from a collection of arbitrary objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Until now you were generating option tags from nested arrays with the help of `options_for_select` method. Data in our array were raw values:
Generating options tags with `options_for_select` requires that you create an array containing the text and value for each option. But what if you had a City model (perhaps an Active Record one) and you wanted to generate option tags from a collection of those objects? One solution would be to make a nested array by iterating over them:
----------------------------------------------------------------------------
<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
----------------------------------------------------------------------------
But what if you had a *City* model (perhaps an Active Record one) and you wanted to generate option tags from a collection of those objects? One solution would be to make a nested array by iterating over them:
----------------------------------------------------------------------------
<% cities_array = City.find(:all).map { |city| [city.name, city.id] } %>
<% cities_array = City.all.map { |city| [city.name, city.id] } %>
<%= options_for_select(cities_array) %>
----------------------------------------------------------------------------
@ -432,8 +464,7 @@ This is a perfectly valid solution, but Rails provides a less verbose alternativ
----------------------------------------------------------------------------
<%= options_from_collection_for_select(City.all, :id, :name) %>
----------------------------------------------------------------------------
As the name implies, this only generates option tags. To generate a working select box you would need to use it in conjunction with `select_tag`, just as you would with `options_for_select`. A method to go along with it is `collection_select`:
As the name implies, this only generates option tags. To generate a working select box you would need to use it in conjunction with `select_tag`, just as you would with `options_for_select`. When working with model objects, just as `select` combines `select_tag` and `options_for_select`, `collection_select` combines `select_tag` with `options_from_collection_for_select`.
----------------------------------------------------------------------------
<%= collection_select(:person, :city_id, City.all, :id, :name) %>
@ -441,67 +472,72 @@ As the name implies, this only generates option tags. To generate a working sele
To recap, `options_from_collection_for_select` is to `collection_select` what `options_for_select` is to `select`.
[NOTE]
=============================
Pairs passed to `options_for_select` should have the name first and the id second, however with `options_from_collection_for_select` the first argument is the value method and the second the text method.
=============================
Time zone and country select
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To leverage time zone support in Rails, you have to ask our users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using `collection_select`, but you can simply use the `time_zone_select` helper that already wraps this:
To leverage time zone support in Rails, you have to ask your users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using `collection_select`, but you can simply use the `time_zone_select` helper that already wraps this:
----------------------------------------------------------------------------
<%= time_zone_select(:person, :city_id) %>
<%= time_zone_select(:person, :time_zone) %>
----------------------------------------------------------------------------
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 http://github.com/rails/country_select/tree/master[country_select plugin]. When using this do 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 http://github.com/rails/country_select/tree/master[country_select plugin]. When using this do 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).
Date and time select boxes
--------------------------
Using Date and Time Form Helpers
--------------------------------
The date and time helpers differ from all the other form helpers in two important respects:
1. Unlike other attributes you might typically have, dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc...). So in particular, there is no single value in your params hash with your date or time.
2. Other helpers use the _tag suffix to indicate whether a helper is a barebones helper or one that operates on model objects. With dates and times, `select\_date`, `select\_time` and `select_datetime` are the barebones helpers, `date_select`, `time_select` and `datetime_select` are the equivalent model object helpers
1. Dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc.) and so there is no single value in your `params` hash with your date or time.
2. Other helpers use the _tag suffix to indicate whether a helper is a barebones helper or one that operates on model objects. With dates and times, `select_date`, `select_time` and `select_datetime` are the barebones helpers, `date_select`, `time_select` and `datetime_select` are the equivalent model object helpers.
Both of these families of helpers will create a series of select boxes for the different components (year, month, day etc...).
Both of these families of helpers will create a series of select boxes for the different components (year, month, day etc.).
Barebones helpers
~~~~~~~~~~~~~~~~~
The `select_*` family of helpers take as their first argument an instance of Date, Time or DateTime that is used as the currently selected value. You may omit this parameter, in which case the current date is used. For example
-----------
<%= select_date Date::today, :prefix => :start_date %>
<%= select_date Date.today, :prefix => :start_date %>
-----------
outputs (with the actual option values omitted for brevity)
outputs (with actual option values omitted for brevity)
-----------
<select id="start_date_year" name="start_date[year]"> ... </select>
<select id="start_date_month" name="start_date[month]"> ... </select>
<select id="start_date_day" name="start_date[day]"> ... </select>
-----------
The above inputs would result in `params[:start_date]` being a hash with keys :year, :month, :day. To get an actual Time or Date object you would have to extract these values and pass them to the appropriate constructor, for example
The above inputs would result in `params[:start_date]` being a hash with keys `:year`, `:month`, `:day`. To get an actual Time or Date object you would have to extract these values and pass them to the appropriate constructor, for example
-----------
Date::civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)
Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)
-----------
The :prefix option controls where in the params hash the date components will be placed. Here it was set to `start_date`, if omitted it will default to `date`.
The `:prefix` option is the key used to retrieve the hash of date components from the `params` hash. Here it was set to `start_date`, if omitted it will default to `date`.
Model object helpers
~~~~~~~~~~~~~~~~~~~~
`select_date` does not work well with forms that update or create Active Record objects as Active Record expects each element of the params hash to correspond to one attribute.
The model object helpers for dates and times submit parameters with special names. When Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type. For example
`select_date` does not work well with forms that update or create Active Record objects as Active Record expects each element of the `params` hash to correspond to one attribute.
The model object helpers for dates and times submit parameters with special names, when Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type. For example:
---------------
<%= date_select :person, :birth_date %>
---------------
outputs (with the actual option values omitted for brevity)
outputs (with actual option values omitted for brevity)
--------------
<select id="person_birth_date_1i" name="person[birth_date(1i)]"> ... </select>
<select id="person_birth_date_2i" name="person[birth_date(2i)]"> ... </select>
<select id="person_birth_date_3i" name="person[birth_date(3i)]"> ... </select>
--------------
which results in a params hash like
which results in a `params` hash like
--------------
{:person => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}}
--------------
When this is passed to `Person.new`, Active Record spots that these parameters should all be used to construct the `birth_date` attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as `Date::civil`.
When this is passed to `Person.new` (or `update_attributes`), Active Record spots that these parameters should all be used to construct the `birth_date` attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as `Date.civil`.
Common options
~~~~~~~~~~~~~~
@ -511,65 +547,23 @@ As a rule of thumb you should be using `date_select` when working with model obj
NOTE: In many cases the built in date pickers are clumsy as they do not aid the user in working out the relationship between the date and the day of the week.
Form builders
-------------
Individual components
~~~~~~~~~~~~~~~~~~~~~
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 a 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
Occasionally you need to display just a single date component such as a year or a month. Rails provides a series of helpers for this, one for each component `select_year`, `select_month`, `select_day`, `select_hour`, `select_minute`, `select_second`. These helpers are fairly straightforward. By default they will generate a input named after the time component (for example "year" for `select_year`, "month" for `select_month` etc.) although this can be overriden with the `:field_name` option. The `:prefix` option works in the same way that it does for `select_date` and `select_time` and has the same default value.
----------
<% form_for @person do |f| %>
<%= text_field_with_label f, :first_name %>
<% end %>
----------
can be replaced with
----------
<% form_for @person, :builder => LabellingFormBuilder do |f| %>
<%= f.text_field :first_name %>
<% end %>
----------
by defining a LabellingFormBuilder class similar to the following:
The first parameter specifies which value should be selected and can either be an instance of a Date, Time or DateTime, in which case the relevant component will be extracted, or a numerical value. For example
[source, ruby]
-------
class LabellingFormBuilder < FormBuilder
def text_field attribute, options={}
label(attribute) + text_field(attribute, options)
end
end
-------
If you reuse this frequently you could define a `labeled_form_for` helper that automatically applies the `:builder => LabellingFormBuilder` option.
---------------
<%= select_year(2009) %>
<%= select_year(Time.now) %>
---------------
The form builder used also determines what happens when you do
------
<%= render :partial => f %>
------
If `f` is an instance of FormBuilder then this will render the 'form' partial, setting the partial's object to the form builder. If the form builder is of class LabellingFormBuilder then the 'labelling_form' partial would be rendered instead.
will produce the same output if the current year is 2009 and the value chosen by the user can be retrieved by `params[:date][:year]`.
Scoping out form controls with `fields_for`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`fields_for` creates a form builder in exactly the same way as `form_for` but doesn't create the actual `<form>` tags. It creates a scope around a specific model object like `form_for`, which is useful for specifying additional model objects in the same form. For example if you had a Person model with an associated ContactDetail model you could create a form for editing both like so:
-------------
<% form_for @person do |person_form| %>
<%= person_form.text_field :name %>
<% fields_for @person.contact_detail do |contact_details_form| %>
<%= contact_details_form.text_field :phone_number %>
<% end %>
<% end %>
-------------
which produces the following output:
-------------
<form action="/people/1" class="edit_person" id="edit_person_1" method="post">
<input id="person_name" name="person[name]" size="30" type="text" />
<input id="contact_detail_phone_number" name="contact_detail[phone_number]" size="30" type="text" />
</form>
-------------
File Uploads
Uploading Files
--------------
A common task is uploading some sort of file, whether it's a picture of a person or a CSV file containing data to process. The most important thing to remember with file uploads is that the form's encoding *MUST* be set to multipart/form-data. If you forget to do this the file will not be uploaded. This can be done by passing `:multi_part => true` as an HTML option. This means that in the case of `form_tag` it must be passed in the second options hash and in the case of `form_for` inside the `:html` hash.
A common task is uploading some sort of file, whether it's a picture of a person or a CSV file containing data to process. The most important thing to remember with file uploads is that the form's encoding *MUST* be set to "multipart/form-data". If you forget to do this the file will not be uploaded. This can be done by passing `:multi_part => true` as an HTML option. This means that in the case of `form_tag` it must be passed in the second options hash and in the case of `form_for` inside the `:html` hash.
The following two forms both upload a file.
-----------
@ -585,7 +579,7 @@ Rails provides the usual pair of helpers: the barebones `file_field_tag` and the
What gets uploaded
~~~~~~~~~~~~~~~~~~
The object in the params hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file. In both cases the object will have an `original_filename` attribute containing the name the file had on the user's computer and a `content_type` attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in `#\{RAILS_ROOT\}/public/uploads` under the same name as the original file (assuming the form was the one in the previous example).
The object in the `params` hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file. In both cases the object will have an `original_filename` attribute containing the name the file had on the user's computer and a `content_type` attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in `#\{Rails.root\}/public/uploads` under the same name as the original file (assuming the form was the one in the previous example).
[source, ruby]
-----------------
@ -603,35 +597,69 @@ NOTE: If the user has not selected a file the corresponding parameter will be an
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.
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.
Customising 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
----------
<% form_for @person do |f| %>
<%= text_field_with_label f, :first_name %>
<% end %>
----------
can be replaced with
----------
<% form_for @person, :builder => LabellingFormBuilder do |f| %>
<%= f.text_field :first_name %>
<% end %>
----------
by defining a LabellingFormBuilder class similar to the following:
[source, ruby]
-------
class LabellingFormBuilder < FormBuilder
def text_field(attribute, options={})
label(attribute) + text_field(attribute, options)
end
end
-------
If you reuse this frequently you could define a `labeled_form_for` helper that automatically applies the `:builder => LabellingFormBuilder` option.
The form builder used also determines what happens when you do
------
<%= render :partial => f %>
------
If `f` is an instance of FormBuilder then this will render the `form` partial, setting the partial's object to the form builder. If the form builder is of class LabellingFormBuilder then the `labelling_form` partial would be rendered instead.
Understanding Parameter Naming Conventions
-----------------------------------------
Parameter Names
---------------
[[parameter_names]]
As you've seen in the previous sections values from forms can appear either at the top level of the params hash or may appear nested in another hash. For example in a standard create
action for a Person model, `params[:model]` would usually be a hash of all the attributes for the person to create. The params hash can also contain arrays, arrays of hashes and so on.
As you've seen in the previous sections, values from forms can be at the top level of the `params` hash or nested in another hash. For example in a standard `create`
action for a Person model, `params[:model]` would usually be a hash of all the attributes for the person to create. The `params` hash can also contain arrays, arrays of hashes and so on.
Fundamentally HTML forms don't know about any sort of structured data. All they know about is name-value pairs. Rails tacks some conventions onto parameter names which it uses to express some structure.
Fundamentally HTML forms don't know about any sort of structured data, all they generate is name-value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses.
[TIP]
========================
You may find you can try out examples in this section faster by using the console to directly invoke Rails' parameter parser. For example
-------------
ActionController::RequestParser.parse_query_parameters "name=fred&phone=0123456789"
#=> {"name"=>"fred", "phone"=>"0123456789"}
ActionController::UrlEncodedPairParser.parse_query_parameters "name=fred&phone=0123456789"
# => {"name"=>"fred", "phone"=>"0123456789"}
-------------
========================
Basic structures
~~~~~~~~~~~~~~~
The two basic structures are arrays and hashes. Hashes mirror the syntax used for accessing the value in the params. For example if a form contains
The two basic structures are arrays and hashes. Hashes mirror the syntax used for accessing the value in `params`. For example if a form contains
-----------------
<input id="person_name" name="person[name]" type="text" value="Henry"/>
-----------------
the params hash will contain
the `params` hash will contain
[source, ruby]
-----------------
{'person' => {'name' => 'Henry'}}
-----------------
@ -641,9 +669,8 @@ Hashes can be nested as many levels as required, for example
------------------
<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>
------------------
will result in the params hash being
will result in the `params` hash being
[source, ruby]
-----------------
{'person' => {'address' => {'city' => 'New York'}}}
-----------------
@ -664,22 +691,21 @@ We can mix and match these two concepts. For example, one element of a hash migh
<input name="addresses[][line2]" type="text"/>
<input name="addresses[][city]" type="text"/>
-----------------
This would result in `params[:addresses]` being an array of hashes with keys `line1`, `line2` and `city`. Rails decides to start accumulating values in a new hash whenever it encounters a input name that already exists in the current hash.
This would result in `params[:addresses]` being an array of hashes with keys `line1`, `line2` and `city`. Rails decides to start accumulating values in a new hash whenever it encounters an input name that already exists in the current hash.
The one restriction is that although hashes can be nested arbitrarily deep then can be only one level of "arrayness". Frequently arrays can be usually replaced by hashes, for example instead of having an array of model objects one can have a hash of model objects keyed by their id.
There's a restriction, however, while hashes can be nested arbitrarily, only one level of "arrayness" is allowed. Arrays can be usually replaced by hashes, for example instead of having an array of model objects one can have a hash of model objects keyed by their id, an array index or some other parameter.
[WARNING]
Array parameters do not play well with the `check_box` helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The `check_box` helper fakes this by creating a second hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted. If the checkbox is checked then both are submitted but the value submitted by the checkbox takes precedence. When working with array parameters this duplicate submission will confuse Rails since duplicate input names are how it decides when to start a new hash. It is preferable to either use `check_box_tag` or to use hashes instead of arrays.
Array parameters do not play well with the `check_box` helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The `check_box` helper fakes this by creating a second hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted and if it is checked then both are submitted but the value submitted by the checkbox takes precedence. When working with array parameters this duplicate submission will confuse Rails since duplicate input names are how it decides when to start a new array element. It is preferable to either use `check_box_tag` or to use hashes instead of arrays.
Using form helpers
~~~~~~~~~~~~~~~~~
The previous sections did not use the Rails form helpers at all. While you can craft the input names yourself and pass them directly to helpers such as `text_field_tag` Rails also provides higher level support. The two tools at your disposal here are the name parameter to `form_for`/`fields_for` and the `:index` option.
You might want to render a form with a set of edit fields for each of a person's addresses. Something a little like this will do the trick
The previous sections did not use the Rails form helpers at all. While you can craft the input names yourself and pass them directly to helpers such as `text_field_tag` Rails also provides higher level support. The two tools at your disposal here are the name parameter to `form_for` and `fields_for` and the `:index` option that helpers take.
You might want to render a form with a set of edit fields for each of a person's addresses. For example:
--------
<% form_for @person do |person_form| %>
<%= person_form.text_field :name%>
<%= person_form.text_field :name %>
<% for address in @person.addresses %>
<% person_form.fields_for address, :index => address do |address_form|%>
<%= address_form.text_field :city %>
@ -687,7 +713,7 @@ You might want to render a form with a set of edit fields for each of a person's
<% end %>
<% end %>
--------
Assuming our person had two addresses, with ids 23 and 45 this would create output similar to this:
Assuming the person had two addresses, with ids 23 and 45 this would create output similar to this:
--------
<form action="/people/1" class="edit_person" id="edit_person_1" method="post">
<input id="person_name" name="person[name]" size="30" type="text" />
@ -695,14 +721,13 @@ Assuming our person had two addresses, with ids 23 and 45 this would create outp
<input id="person_address_45_city" name="person[address][45][city]" size="30" type="text" />
</form>
--------
This will result in a params hash that looks like
This will result in a `params` hash that looks like
[source, ruby]
--------
{'person' => {'name' => 'Bob', 'address' => { '23' => {'city' => 'Paris'}, '45' => {'city' => 'London'} }}}
{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}}
--------
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 it is then easy to locate which Address record should be modified but you could 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
--------
@ -714,7 +739,7 @@ will create inputs like
--------
<input id="person_address_primary_1_city" name="person[address][primary][1][city]" size="30" type="text" value="bologna" />
--------
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 usually it is less repetitive to specify this at the form builder level rather than on individual input controls.
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
--------
@ -724,10 +749,10 @@ As a shortcut you can append [] to the name and omit the `:index` option. This i
--------
produces exactly the same output as the previous example.
Complex forms
-------------
Building Complex forms
----------------------
Many apps grow beyond simple forms editing a single object. For example when creating a Person instance 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:
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 http://railscasts.com/episodes/75[complex forms]
* Handle Multiple Models in One Form from http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf[Advanced Rails Recipes]

View file

@ -8,6 +8,8 @@ This guide covers getting up and running with Ruby on Rails. After reading it,
* The basic principles of MVC (Model, View Controller) and RESTful design
* How to quickly generate the starting pieces of a Rails application.
NOTE: This Guide is based on Rails 2.3. Some of the code shown here will not work in older versions of Rails.
== This Guide Assumes
This guide is designed for beginners who want to get started with a Rails application from scratch. It does not assume that you have any prior experience with Rails. However, to get the most out of it, you need to have some prerequisites installed:
@ -16,7 +18,7 @@ This guide is designed for beginners who want to get started with a Rails applic
* The link:http://rubyforge.org/frs/?group_id=126[RubyGems] packaging system
* A working installation of link:http://www.sqlite.org/[SQLite] (preferred), link:http://www.mysql.com/[MySQL], or link:http://www.postgresql.org/[PostgreSQL]
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 net 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:
* link:http://www.humblelittlerubybook.com/[Mr. Neigborlys Humble Little Ruby Book]
* link:http://www.rubycentral.com/book/[Programming Ruby]
@ -26,7 +28,7 @@ It is highly recommended that you *familiarize yourself with Ruby before diving
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 _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 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:
@ -105,7 +107,7 @@ For example, to a Rails application a request such as this:
+DELETE /photos/17+
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.
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 youd like more details on REST as an architectural style, these resources are more approachable than Fieldings thesis:
@ -154,6 +156,8 @@ And if you're using PostgreSQL for data storage, run this command:
$ rails blog -d postgresql
-------------------------------------------------------
TIP: You can see all of the switches that the Rails application builder accepts by running +rails -h+.
After you create the blog application, switch to its folder to continue work directly in that application:
[source, shell]
@ -201,13 +205,12 @@ Here's the section of the default configuration file with connection information
development:
adapter: sqlite3
database: db/development.sqlite3
pool: 5
timeout: 5000
-------------------------------------------------------
If you don't have any database set up, SQLite is the easiest to get installed. If you're on OS X 10.5 or greater on a Mac, you already have it. Otherwise, you can install it using RubyGems:
If you're not running OS X 10.5 or greater, you'll need to install the SQLite gem. Similar to installing Rails you just need to run:
[source, shell]
-------------------------------------------------------
$ gem install sqlite3-ruby
@ -223,6 +226,7 @@ development:
adapter: mysql
encoding: utf8
database: blog_development
pool: 5
username: root
password:
socket: /tmp/mysql.sock
@ -239,6 +243,7 @@ development:
adapter: postgresql
encoding: unicode
database: blog_development
pool: 5
username: blog
password:
-------------------------------------------------------
@ -254,6 +259,8 @@ Now that you have your database configured, it's time to have Rails create an em
$ rake db:create
-------------------------------------------------------
NOTE: Rake is a general-purpose command-runner that Rails uses for many things. You can see the list of available rake commands in your application by running +rake -T+.
== Hello, Rails!
One of the traditional places to start with a new language is by getting some text up on screen quickly. To do that in Rails, you need to create at minimum a controller and a view. Fortunately, you can do that in a single command. Enter this command in your terminal:
@ -281,13 +288,13 @@ You actually have a functional Rails application already - after running only tw
$ script/server
-------------------------------------------------------
This will fire up the lightweight Webrick web server by default. To see your application in action, open a browser window and navigate to +http://localhost:3000+. You should see Rails' default information page:
This will fire up an instance of the Mongrel web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to +http://localhost:3000+. You should see Rails' default information page:
image: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.
The "Welcome Aboard" page is the smoke test for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to +http://localhost:3000/home/index+.
The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to +http://localhost:3000/home/index+.
=== Setting the Application Home Page
@ -336,13 +343,13 @@ $ script/generate scaffold Post name:string title:string content:text
NOTE: While scaffolding will get you up and running quickly, the "one size fits all" code that it generates is unlikely to be a perfect fit for your application. In most cases, you'll need to customize the generated code. Many experienced Rails developers avoid scaffolding entirely, preferring to write all or most of their source code from scratch.
The scaffold generator will build 13 files in your application, along with some folders, and edit one more. Here's a quick overview of what it creates:
The scaffold generator will build 14 files in your application, along with some folders, and edit one more. Here's a quick overview of what it creates:
[options="header"]
|==========================================================================================================
|File |Purpose
|app/models/post.rb |The Post model
|db/migrate/20081013124235_create_posts.rb |Migration to create the posts table in your database (your name will include a different timestamp)
|db/migrate/20090113124235_create_posts.rb |Migration to create the posts table in your database (your name will include a different timestamp)
|app/views/posts/index.html.erb |A view to display an index of all posts
|app/views/posts/show.html.erb |A view to display a single post
|app/views/posts/new.html.erb |A view to create a new post
@ -355,13 +362,14 @@ The scaffold generator will build 13 files in your application, along with some
|config/routes.rb |Edited to include routing information for posts
|test/fixtures/posts.yml |Dummy posts for use in testing
|test/unit/post_test.rb |Unit testing harness for the posts model
|test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper
|==========================================================================================================
=== Running a Migration
One of the products of the +script/generate scaffold+ command is a _database migration_. Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it's possible to undo a migration after it's been applied to your database. Migration filenames include a timestamp to ensure that they're processed in the order that they were created.
If you look in the +db/migrate/20081013124235_create_posts.rb+ file (remember, yours will have a slightly different name), here's what you'll find:
If you look in the +db/migrate/20090113124235_create_posts.rb+ file (remember, yours will have a slightly different name), here's what you'll find:
[source, ruby]
-------------------------------------------------------
@ -388,10 +396,11 @@ At this point, you can use a rake command to run the migration:
[source, shell]
-------------------------------------------------------
$ rake db:create
$ rake db:migrate
-------------------------------------------------------
Remember, you can't run migrations before running +rake db:create+ to create your database, as we covered earlier.
NOTE: Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your +config/database.yml+ file.
=== Adding a Link
@ -472,7 +481,7 @@ title: nil, content: "A new post", created_at: nil, updated_at: nil>,
This code shows creating a new +Post+ instance, attempting to save it and getting +false+ for a return value (indicating that the save failed), and inspecting the +errors+ of the post.
TIP: Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes, type +reload!+ at the console prompt to load them.
TIP: Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes to your models while the console is open, type +reload!+ at the console prompt to load them.
=== Listing All Posts
@ -762,7 +771,7 @@ At this point, its worth looking at some of the tools that Rails provides to
=== Using Partials to Eliminate View Duplication
As you saw earlier, the scaffold-generated views for the +new+ and +edit+ actions are largely identical. You can pull the shared code out into a +partial+ template. This requires editing the new and edit views, and adding a new template. The new +_form.html.erb+ template should be saved in the same +app/views/posts+ folder as the files from which it is being extracted:
As you saw earlier, the scaffold-generated views for the +new+ and +edit+ actions are largely identical. You can pull the shared code out into a partial template. This requires editing the new and edit views, and adding a new template. The new +_form.html.erb+ template should be saved in the same +app/views/posts+ folder as the files from which it is being extracted. Note that the name of this file begins with an underscore; that's the Rails naming convention for partial templates.
+new.html.erb+:
@ -876,7 +885,7 @@ end
Rails runs _before filters_ before any action in the controller. You can use the +:only+ clause to limit a before filter to only certain actions, or an +:except+ clause to specifically skip a before filter for certain actions. Rails also allows you to define _after filters_ that run after processing an action, as well as _around filters_ that surround the processing of actions. Filters can also be defined in external classes to make it easy to share them between controllers.
For more information on filters, see the link:actioncontroller_basics.html[Action Controller Basics] guide.
For more information on filters, see the link:../actioncontroller_basics.html[Action Controller Basics] guide.
== Adding a Second Model
@ -894,7 +903,7 @@ $ script/generate model Comment commenter:string body:text post:references
This command will generate four files:
* +app/models/comment.rb+ - The model
* +db/migrate/20081013214407_create_comments.rb - The migration
* +db/migrate/20091013214407_create_comments.rb - The migration
* +test/unit/comment_test.rb+ and +test/fixtures/comments.yml+ - The test harness.
First, take a look at +comment.rb+:
@ -936,7 +945,7 @@ The +t.references+ line sets up a foreign key column for the association between
$ rake db:migrate
-------------------------------------------------------
Rails is smart enough to only execute the migrations that have not already been run against this particular database.
Rails is smart enough to only execute the migrations that have not already been run against the current database.
=== Associating Models
@ -971,13 +980,11 @@ TIP: For more information on Active Record associations, see the link:../associa
=== Adding a Route
_Routes_ are entries in the +config/routes.rb+ file that tell Rails how to match incoming HTTP requests to controller actions. Open up that file and find the existing line referring to +posts+. Then edit it as follows:
_Routes_ are entries in the +config/routes.rb+ file that tell Rails how to match incoming HTTP requests to controller actions. Open up that file and find the existing line referring to +posts+ (it will be right at the top of the file). Then edit it as follows:
[source, ruby]
-------------------------------------------------------
map.resources :posts do |post|
post.resources :comments
end
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.
@ -1003,7 +1010,7 @@ This creates seven files:
* +app/views/comments/edit.html.erb+ - The view for the edit action
* +test/functional/comments_controller_test.rb+ - The functional tests for the controller
The controller will be generated with empty methods for each action that you specified in the call to +script/generate controller+:
The controller will be generated with empty methods and views for each action that you specified in the call to +script/generate controller+:
[source, ruby]
-------------------------------------------------------
@ -1068,6 +1075,17 @@ class CommentsController < ApplicationController
end
end
def destroy
@post = Post.find(params[:post_id])
@comment = Comment.find(params[:id])
@comment.destroy
respond_to do |format|
format.html { redirect_to post_comments_path(@post) }
format.xml { head :ok }
end
end
end
-------------------------------------------------------
@ -1086,7 +1104,7 @@ This creates a new +Comment+ object _and_ sets up the +post_id+ field to have th
Because you skipped scaffolding, you'll need to build views for comments "by hand." Invoking +script/generate controller+ will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.
The +index.html.erb+ view:
The +views/comments/index.html.erb+ view:
[source, ruby]
-------------------------------------------------------
@ -1115,7 +1133,7 @@ The +index.html.erb+ view:
<%= link_to 'Back to Post', @post %>
-------------------------------------------------------
The +new.html.erb+ view:
The +views/comments/new.html.erb+ view:
[source, ruby]
-------------------------------------------------------
@ -1140,7 +1158,7 @@ The +new.html.erb+ view:
<%= link_to 'Back', post_comments_path(@post) %>
-------------------------------------------------------
The +show.html.erb+ view:
The +views/comments/show.html.erb+ view:
[source, ruby]
-------------------------------------------------------
@ -1160,7 +1178,7 @@ The +show.html.erb+ view:
<%= link_to 'Back', post_comments_path(@post) %>
-------------------------------------------------------
The +edit.html.erb+ view:
The +views/comments/edit.html.erb+ view:
[source, ruby]
-------------------------------------------------------
@ -1186,11 +1204,11 @@ The +edit.html.erb+ view:
<%= link_to 'Back', post_comments_path(@post) %>
-------------------------------------------------------
Again, the added complexity here (compared to the views you saw for managing comments) comes from the necessity of juggling a post and its comments at the same time.
Again, the added complexity here (compared to the views you saw for managing posts) comes from the necessity of juggling a post and its comments at the same time.
=== Hooking Comments to Posts
As a final step, I'll modify the +show.html.erb+ view for a post to show the comments on that post, and to allow managing those comments:
As a next step, I'll modify the +views/posts/show.html.erb+ view to show the comments on that post, and to allow managing those comments:
[source, ruby]
-------------------------------------------------------
@ -1222,13 +1240,90 @@ As a final step, I'll modify the +show.html.erb+ view for a post to show the com
</p>
<% end %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
<%= link_to 'Manage Comments', post_comments_path(@post) %>
-------------------------------------------------------
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+.
== 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:
[source, shell]
-------------------------------------------------------
$ script/generate model tag name:string post:references
-------------------------------------------------------
Run the migration to create the database table:
[source, shell]
-------------------------------------------------------
$ rake db:migrate
-------------------------------------------------------
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:
[source, ruby]
-------------------------------------------------------
class Post < ActiveRecord::Base
validates_presence_of :name, :title
validates_length_of :title, :minimum => 5
has_many :comments
has_many :tags
accepts_nested_attributes_for :tags, :allow_destroy => :true ,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end
-------------------------------------------------------
The +:allow_destroy+ option on the nested attribute declaration tells Rails to display a "remove" checkbox on the view that you'll build shortly. The +:reject_if+ option prevents saving new tags that do not have any attributes filled in.
You'll also need to modify +views/posts/_form.html.erb+ to include the tags:
[source, ruby]
-------------------------------------------------------
<% @post.tags.build if @post.tags.empty? %>
<% form_for(@post) do |post_form| %>
<%= post_form.error_messages %>
<p>
<%= post_form.label :name %><br />
<%= post_form.text_field :name %>
</p>
<p>
<%= post_form.label :title, "title" %><br />
<%= post_form.text_field :title %>
</p>
<p>
<%= post_form.label :content %><br />
<%= post_form.text_area :content %>
</p>
<h2>Tags</h2>
<% post_form.fields_for :tags do |tag_form| %>
<p>
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
</p>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<p>
<%= tag_form.label :_delete, 'Remove:' %>
<%= tag_form.check_box :_delete %>
</p>
<% end %>
<% end %>
<p>
<%= post_form.submit "Save" %>
</p>
<% end %>
-------------------------------------------------------
With these changes in place, you'll find that you can edit a post and its tags directly on the same view.
NOTE: You may want to use javascript to dynamically add additional tags on a single form. For an example of this and other advanced techniques, see the link:http://github.com/alloy/complex-form-examples/tree/nested_attributes[nested model sample application].
== What's Next?
Now that you've seen your first Rails application, you should feel free to update it and experiment on your own. But you don't have to do everything without help. As you need assistance getting up and running with Rails, feel free to consult these support resources:
@ -1247,6 +1342,7 @@ Rails also comes with built-in help that you can generate using the rake command
http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2[Lighthouse ticket]
*
* November 3, 2008: Formatting patch from Dave Rothlisberger
* November 1, 2008: First approved version by link:../authors.html#mgunderloy[Mike Gunderloy]
* October 16, 2008: Revised based on feedback from Pratik Naik by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)

View file

@ -1,9 +1,25 @@
The Rails Internationalization (I18n) API
=========================================
The Ruby I18n (shorthand for _internationalization_) gem which is shipped with Ruby on Rails (starting from Rails 2.2) provides an easy-to-use and extensible framework for translating your application to a single custom language other than English or providing multi-language support in your application.
The Ruby I18n (shorthand for _internationalization_) gem which is shipped with Ruby on Rails (starting from Rails 2.2) provides an easy-to-use and extensible framework for *translating your application to a single custom language* other than English or for *providing multi-language support* in your application.
NOTE: The Ruby I18n framework provides you with all neccessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available. See Rails http://rails-i18n.org/wiki[I18n Wiki] for more information.
The process of "internationalization" usually means to abstract all strings and other locale specific bits (such as date or currency formats) out of your application. The process of "localization" means to provide translations and localized formats for these bits. <<1>>
So, in the process of _internationalizing_ your Rails application you have to:
* Ensure you have support for i18n
* Tell Rails where to find locale dictionaries
* Tell Rails how to set, preserve and switch locale
In the process of _localizing_ your application you'll probably want to do following three things:
* Replace or supplement Rail's default locale -- eg. date and time formats, month names, ActiveRecord model names, etc
* Abstract texts in your application into keyed dictionaries -- eg. flash messages, static texts in your views, etc
* Store the resulting dictionaries somewhere
This guide will walk you through the I18n API and contains a tutorial how to internationalize a Rails application from the start.
NOTE: The Ruby I18n framework provides you with all neccessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See Rails http://rails-i18n.org/wiki[I18n Wiki] for more information.
== How I18n in Ruby on Rails works
@ -12,7 +28,7 @@ Internationalization is a complex problem. Natural languages differ in so many w
* providing support for English and similar languages out of the box
* making it easy to customize and extend everything for other languages
As part of this solution, *every static string in the Rails framework* -- eg. ActiveRecord validation messages, time and date formats -- *has been internationalized*, so _localization_ of a Rails application means "over-riding" these defaults.
As part of this solution, *every static string in the Rails framework* -- eg. Active Record validation messages, time and date formats -- *has been internationalized*, so _localization_ of a Rails application means "over-riding" these defaults.
=== The overall architecture of the library
@ -74,10 +90,12 @@ en:
hello: "Hello world"
-------------------------------------------------------
This means, that in the +:en+ locale, the key _hello_ will map to _Hello world_ string. Every string inside Rails is internationalized in this way, see for instance ActiveRecord validation messages in the http://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml[+activerecord/lib/active_record/locale/en.yml+] file or time and date formats in the http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml[+activesupport/lib/active_support/locale/en.yml+] file. You can use YAML or standard Ruby Hashes to store translations in the default (Simple) backend.
This means, that in the +:en+ locale, the key _hello_ will map to _Hello world_ string. Every string inside Rails is internationalized in this way, see for instance Active Record validation messages in the http://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml[+activerecord/lib/active_record/locale/en.yml+] file or time and date formats in the http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml[+activesupport/lib/active_support/locale/en.yml+] file. You can use YAML or standard Ruby Hashes to store translations in the default (Simple) backend.
The I18n library will use *English* as a *default locale*, ie. if you don't set a different locale, +:en+ will be used for looking up translations.
NOTE: The i18n library takes *pragmatic approach* to locale keys (after http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en[some discussion]), including only the _locale_ ("language") part, like +:en+, +:pl+, not the _region_ part, like +:en-US+ or +:en-UK+, which are traditionally used for separating "languages" and "regional setting" or "dialects". (For instance, in the +:en-US+ locale you would have $ as a currency symbol, while in +:en-UK+, you would have €. Also, insults would be different in American and British English :) Reason for this pragmatic approach is that most of the time, you usually care about making your application available in different "languages", and working with locales is much simpler this way. However, nothing stops you from separating regional and other settings in the traditional way. In this case, you could eg. inherit from the default +en+ locale and then provide UK specific settings in a +:en-UK+ dictionary.
The *translations load path* (+I18n.load_path+) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you.
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.
@ -112,30 +130,208 @@ I18n.default_locale = :pt
=== Setting and passing the locale
By default the I18n library will use :en (English) as a I18n.default_locale for looking up translations (if you do not specify a locale for a lookup).
If you want to translate your Rails application to a *single language other than English* (the default locale), you can set I18n.default_locale to your locale in +environment.rb+ or an initializer as shown above, and it will persist through the requests.
If you want to translate your Rails application to a single language other than English you can set I18n.default_locale to your locale. If you want to change the locale on a per-request basis though you can set it in a before_filter on the ApplicationController like this:
However, you would probably like to *provide support for more locales* in your application. In such case, you need to set and pass the locale between requests.
WARNING: You may be tempted to store choosed locale in a _session_ or a _cookie_. *Do not do so*. The locale should be transparent and a part of the URL. This way you don't break people's basic assumptions about the web itself: if you send a URL of some page to a friend, she should see the same page, same content. A fancy word for this would be that you're being http://en.wikipedia.org/wiki/Representational_State_Transfer[_RESTful_]. Read more about RESTful approach in http://www.infoq.com/articles/rest-introduction[Stefan Tilkov's articles]. There may be some exceptions to this rule, which are discussed below.
The _setting part_ is easy. You can set locale in a +before_filter+ in the ApplicationController like this:
[source, ruby]
-------------------------------------------------------
before_filter :set_locale
def set_locale
# if this is nil then I18n.default_locale will be used
I18n.locale = params[:locale]
# if params[:locale] is nil then I18n.default_locale will be used
I18n.locale = params[:locale]
end
-------------------------------------------------------
This will already work for URLs where you pass the locale as a query parameter as in example.com?locale=pt (which is what Google also does).
This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is eg. 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 locale in the URL and reloading the page.
TIP: For other URL designs, see http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url[How to encode the current locale in the URL].
Of course, you probably don't want to manually include locale in every URL all over your application, or want the URLs look differently, eg. the usual +http://example.com/pt/books+ versus +http://example.com/en/books+. Let's discuss the different options you have.
Now you've initialized I18n support for your application and told it which locale should be used. With that in place you're now ready for the really interesting stuff.
IMPORTANT: Following examples rely on having locales loaded into your application available as an array of strings like +["en", "es", "gr"]+. This is not inclued in current version of Rails 2.2 -- forthcoming Rails version 2.3 will contain easy accesor +available_locales+. (See http://github.com/svenfuchs/i18n/commit/411f8fe7[this commit] and background at http://rails-i18n.org/wiki/pages/i18n-available_locales[Rails I18n Wiki].)
== Internationalize your application
So, for having available locales easily available in Rails 2.2, we have to include this support manually in an initializer, like this:
The process of "internationalization" usually means to abstract all strings and other locale specific bits out of your application. The process of "localization" means to then provide translations and localized formats for these bits. <<1>>
[source, 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
So, let's internationalize something. You most probably have something like this in one of your applications:
# 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}"
-------------------------------------------------------
You can then wrap the constant for easy access in ApplicationController:
[source, ruby]
-------------------------------------------------------
class ApplicationController < ActionController::Base
def available_locales; AVAILABLE_LOCALES; end
end
-------------------------------------------------------
=== Setting 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 English (or default) locale, and +www.example.es+ to load Spanish locale. Thus the _top-level domain name_ is used for locale setting. This has several advantages:
* 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:
[source, 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
# 127.0.0.1 application.it
# 127.0.0.1 application.pl
# 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
end
-------------------------------------------------------
We can also set the locale from the _subdomain_ in very similar way:
[source, ruby]
-------------------------------------------------------
# Get locale code from request subdomain (like http://it.application.local:3000)
# You have to put something like:
# 127.0.0.1 gr.application.local
# 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
end
-------------------------------------------------------
If your application includes a locale switching menu, you would then have something like this in it:
[source, ruby]
-------------------------------------------------------
link_to("Deutsch", "#{APP_CONFIG[:deutsch_website_url]}#{request.env['REQUEST_URI']}")
-------------------------------------------------------
assuming you would set +APP_CONFIG[:deutsch_website_url]+ to some value like +http://www.application.de+.
This solution has aforementioned advantages, however, you may not be able or may not want to provide different localizations ("language versions") on different domains. The most obvious solution would be to include locale code in the URL params (or request path).
=== Setting locale from the URL params
Most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the +I18n.locale = params[:locale]+ _before_filter_ in the first example. We would like to have URLs like +www.example.com/books?locale=ja+ or +www.example.com/ja/books+ in this case.
This approach has almost the same set of advantages as setting the locale from domain name: namely that it's RESTful and in accord with rest of the World Wide Web. It does require a little bit more work to implement, though.
Getting the locale from +params+ and setting it accordingly is not hard; including it in every URL and thus *passing it through the requests* is. To include an explicit option in every URL (eg. +link_to( books_url(:locale => I18n.locale) )+) would be tedious and probably impossible, of course.
Rails contains infrastructure for "centralizing dynamic decisions about the URLs" in its http://api.rubyonrails.org/classes/ActionController/Base.html#M000515[+*ApplicationController#default_url_options*+], which is useful precisely in this scenario: it enables us to set "defaults" for http://api.rubyonrails.org/classes/ActionController/Base.html#M000503[+url_for+] and helper methods dependent on it (by implementing/overriding this method).
We can include something like this in our ApplicationController then:
[source, ruby]
-------------------------------------------------------
# app/controllers/application_controller.rb
def default_url_options(options={})
logger.debug "default_url_options is passed options: #{options.inspect}\n"
{ :locale => I18n.locale }
end
-------------------------------------------------------
Every helper method dependent on +url_for+ (eg. helpers for named routes like +root_path+ or +root_url+, resource routes like +books_path+ or +books_url+, etc.) will now *automatically include the locale in the query string*, like this: +http://localhost:3001/?locale=ja+.
You may be satisfied with this. It does impact the readability of URLs, though, when the locale "hangs" at the end of every URL in your application. Moreover, from the architectural standpoint, locale is usually hierarchically above the other parts of application domain: and URLs should reflect this.
You probably want URLs look like this: +www.example.com/en/books+ (which loads English locale) and +www.example.com/nl/books+ (which loads Netherlands locale). This is achievable with the "over-riding +default_url_options+" strategy from above: you just have to set up your routes with http://api.rubyonrails.org/classes/ActionController/Resources.html#M000354[+path_prefix+] option in this way:
[source, ruby]
-------------------------------------------------------
# config/routes.rb
map.resources :books, :path_prefix => '/:locale'
-------------------------------------------------------
Now, when you call +books_path+ method you should get +"/en/books"+ (for the default locale). An URL like +http://localhost:3001/nl/books+ should load the Netherlands locale, then, and following calls to +books_path+ should return +"/nl/books"+ (because the locale changed).
Of course, you need to take special care of root URL (usually "homepage" or "dashboard") of your application. An URL like +http://localhost:3001/nl+ will not work automatically, because the +map.root :controller => "dashboard"+ declaration in your +routes.rb+ doesn't take locale into account. (And rightly so. There's only one "root" URL.)
You would probably need to map URLs like these:
[source, ruby]
-------------------------------------------------------
# config/routes.rb
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 on two plugins which simplify working with routes in this way: Sven Fuchs's http://github.com/svenfuchs/routing-filter/tree/master[_routing_filter_] and Raul Murciano's http://github.com/raul/translate_routes/tree/master[_translate_routes_]. See also the page http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url[How to encode the current locale in the URL] in the Rails i18n Wiki.
=== Setting locale from the client supplied information
In specific cases, it would make sense to set locale from client supplied information, ie. not from URL. This information may come for example from users' preffered language (set in their browser), can be based on users' geographical location inferred from their IP, or users can provide it simply by choosing 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.
==== Using Accept-Language
One source of client supplied information would be an +Accept-Language+ HTTP header. People may http://www.w3.org/International/questions/qa-lang-priorities[set this in their browser] or other clients (such as _curl_).
A trivial implementation of using +Accept-Language+ header would be:
[source, ruby]
-------------------------------------------------------
def set_locale
logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
I18n.locale = extract_locale_from_accept_language_header
logger.debug "* Locale set to '#{I18n.locale}'"
end
private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
-------------------------------------------------------
Of course, in production environment you would need much robust code, and could use a plugin such as Iaian Hecker's http://github.com/iain/http_accept_language[http_accept_language].
==== Using GeoIP (or similar) database
Another way of choosing the locale from client's information would be to use a database for mapping client IP to region, such as http://www.maxmind.com/app/geolitecountry[GeoIP Lite Country]. The mechanics of the code would be very similar to the code above -- you would need to query database for user's IP, and lookup your preffered locale for the country/region/city returned.
==== User profile
You can also provide users of your application with means to set (and possibly over-ride) locale in your application interface, as well. Again, mechanics for this approach would be very similar to the code above -- you'd probably let users choose a locale from a dropdown list and save it to their profile in database. Then you'd set the locale to this value.
== Internationalizing your application
OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale should be used 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, ie. abstract every locale-specific parts, and that _localize_ it, ie. provide neccessary translations for these abstracts.
You most probably have something like this in one of your applications:
[source, ruby]
-------------------------------------------------------
@ -160,7 +356,7 @@ image:images/i18n/demo_untranslated.png[rails i18n demo untranslated]
=== Adding Translations
Obviously there are two strings that are localized to English. In order to internationalize this code replace these strings with calls to Rails' #t helper with a key that makes sense for the translation:
Obviously there are *two strings that are localized to English*. In order to internationalize this code, *replace these strings* with calls to Rails' +#t+ helper with a key that makes sense for the translation:
[source, ruby]
-------------------------------------------------------
@ -176,13 +372,13 @@ end
<p><%= flash[:notice] %></p>
-------------------------------------------------------
When you now render this view it will show an error message that tells you that the translations for the keys :hello_world and :hello_flash are missing.
When you now render this view, it will show an error message which tells you that the translations for the keys +:hello_world+ and +:hello_flash+ are missing.
image:images/i18n/demo_translation_missing.png[rails i18n demo translation missing]
NOTE: Rails adds a +t+ (+translate+) helper method to your views so that you do not need to spell out +I18n.t+ all the time. Additionally this helper will catch missing translations and wrap the resulting error message into a &lt;span class="translation_missing"&gt;.
NOTE: Rails adds a +t+ (+translate+) helper method to your views so that you do not need to spell out +I18n.t+ all the time. Additionally this helper will catch missing translations and wrap the resulting error message into a +<span class="translation_missing">+.
So let's add the missing translations (i.e. do the "localization" part):
So let's add the missing translations into the dictionary files (i.e. do the "localization" part):
[source, ruby]
-------------------------------------------------------
@ -197,19 +393,19 @@ pirate:
hello_flash: Ahoy Flash
-------------------------------------------------------
There you go. Because you haven't changed the default_locale I18n will use English. Your application now shows:
There you go. Because you haven't changed the default_locale, I18n will use English. Your application now shows:
image:images/i18n/demo_translated_english.png[rails i18n demo translated to english]
image:images/i18n/demo_translated_en.png[rails i18n demo translated to english]
And when you change the URL to pass the pirate locale you get:
And when you change the URL to pass the pirate locale (+http://localhost:3000?locale=pirate+), you'll get:
image:images/i18n/demo_translated_pirate.png[rails i18n demo translated to pirate]
NOTE You need to restart the server when you add new locale files.
NOTE: You need to restart the server when you add new locale files.
=== Adding Date/Time formats
Ok, let's add a timestamp to the view so we can demo the date/time localization feature as well. To localize the time format you pass the Time object to I18n.l or (preferably) use Rails' #l helper. You can pick a format by passing the :format option, by default the :default format is used.
OK! Now let's add a timestamp to the view, so we can demo the *date/time localization* feature as well. To localize the time format you pass the Time object to +I18n.l+ or (preferably) use Rails' +#l+ helper. You can pick a format by passing the +:format+ option -- by default the +:default+ format is used.
[source, ruby]
-------------------------------------------------------
@ -234,17 +430,53 @@ So that would give you:
image:images/i18n/demo_localized_pirate.png[rails i18n demo localized time to pirate]
NOTE Right now you might need to add some more date/time formats in order to make the I18n backend work as expected. See the http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale[rails-i18n repository] for starting points.
TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected. Of course, there's a great chance that somebody already did all the work by *translating Rails's defaults for your locale*. See the http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale[rails-i18n repository at Github] for an archive of various locale files. When you put such file(s) in +config/locale/+ directory, they will automatically ready for use.
=== Organization of locale files
When you are using the default SimpleStore, shipped with the i18n library, you store dictionaries 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:
-------------------------------------------------------
|-defaults
|---es.rb
|---en.rb
|-models
|---book
|-----es.rb
|-----en.rb
|-views
|---defaults
|-----es.rb
|-----en.rb
|---books
|-----es.rb
|-----en.rb
|---users
|-----es.rb
|-----en.rb
|---navigation
|-----es.rb
|-----en.rb
-------------------------------------------------------
This way, you can separate model and model attribute names from text inside views, and all of this from the "defaults" (eg. date and time formats).
Other stores for the i18n library could provide different means of such separation.
Do check the http://rails-i18n.org/wiki[Rails i18n Wiki] for list of tools available for managing translations.
== Overview of the I18n API features
The following purposes are covered:
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.
* lookup translations
* interpolate data into translations
* pluralize translations
* localize dates, numbers, currency etc.
Covered are features like these:
* looking up translations
* interpolating data into translations
* pluralizing translations
* localizing dates, numbers, currency etc.
=== Looking up translations
@ -258,14 +490,14 @@ I18n.t :message
I18n.t 'message'
-------------------------------------------------------
translate also takes a :scope option which can contain one or many additional keys that will be used to specify a “namespace” or scope for a translation key:
+translate+ also takes a +:scope+ option which can contain one or many additional keys that will be used to specify a “namespace” or scope for a translation key:
[source, ruby]
-------------------------------------------------------
I18n.t :invalid, :scope => [:active_record, :error_messages]
-------------------------------------------------------
This looks up the :invalid message in the ActiveRecord error messages.
This looks up the +:invalid+ message in the Active Record error messages.
Additionally, both the key and scopes can be specified as dot separated keys as in:
@ -296,7 +528,7 @@ I18n.t :missing, :default => 'Not here'
If the default value is a Symbol it will be used as a key and translated. One can provide multiple values as default. The first one that results in a value will be returned.
E.g. the following first tries to translate the key :missing and then the key :also_missing. As both do not yield a result the string "Not here" will be returned:
E.g. the following first tries to translate the key +:missing+ and then the key +:also_missing.+ As both do not yield a result the string "Not here" will be returned:
[source, ruby]
-------------------------------------------------------
@ -314,7 +546,7 @@ I18n.t [:odd, :even], :scope => 'active_record.error_messages'
# => ["must be odd", "must be even"]
-------------------------------------------------------
Also, a key can translate to a (potentially nested) hash as grouped translations. E.g. one can receive all ActiveRecord error messages as a Hash with:
Also, a key can translate to a (potentially nested) hash as grouped translations. E.g. one can receive all Active Record error messages as a Hash with:
[source, ruby]
-------------------------------------------------------
@ -324,9 +556,9 @@ I18n.t 'active_record.error_messages'
=== Interpolation
In many cases you want to abstract your translations so that variables can be interpolated into the translation. For this reason the I18n API provides an interpolation feature.
In many cases you want to abstract your translations so that *variables can be interpolated into the translation*. For this reason the I18n API provides an interpolation feature.
All options besides :default and :scope that are passed to #translate will be interpolated to the translation:
All options besides +:default+ and +:scope+ that are passed to +#translate+ will be interpolated to the translation:
[source, ruby]
-------------------------------------------------------
@ -335,14 +567,14 @@ I18n.translate :thanks, :name => 'Jeremy'
# => 'Thanks Jeremy!'
-------------------------------------------------------
If a translation uses :default or :scope as a interpolation variable an I18n::ReservedInterpolationKey exception is raised. If a translation expects an interpolation variable but it has not been passed to #translate an I18n::MissingInterpolationArgument exception is raised.
If a translation uses +:default+ or +:scope+ as a interpolation variable an I+18n::ReservedInterpolationKey+ exception is raised. If a translation expects an interpolation variable but it has not been passed to +#translate+ an +I18n::MissingInterpolationArgument+ exception is raised.
=== Pluralization
In English there's only a singular and a plural form for a given string, e.g. "1 message" and "2 messages". Other languages (http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ar[Arabic], http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ja[Japanese], http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ru[Russian] and many more) have different grammars that have additional or less http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html[plural forms]. Thus, the I18n API provides a flexible pluralization feature.
The :count interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:
The +:count+ interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:
[source, ruby]
-------------------------------------------------------
@ -354,22 +586,22 @@ I18n.translate :inbox, :count => 2
# => '2 messages'
-------------------------------------------------------
The algorithm for pluralizations in :en is as simple as:
The algorithm for pluralizations in +:en+ is as simple as:
[source, ruby]
-------------------------------------------------------
entry[count == 1 ? 0 : 1]
-------------------------------------------------------
I.e. the translation denoted as :one is regarded as singular, the other is used as plural (including the count being zero).
I.e. the translation denoted as +:one+ is regarded as singular, the other is used as plural (including the count being zero).
If the lookup for the key does not return an Hash suitable for pluralization an I18n::InvalidPluralizationData exception is raised.
If the lookup for the key does not return an Hash suitable for pluralization an +18n::InvalidPluralizationData+ exception is raised.
=== Setting and passing a locale
The locale can be either set pseudo-globally to I18n.locale (which uses Thread.current like, e.g., Time.zone) or can be passed as an option to #translate and #localize.
The locale can be either set pseudo-globally to +I18n.locale+ (which uses +Thread.current+ like, e.g., +Time.zone+) or can be passed as an option to +#translate+ and +#localize+.
If no locale is passed I18n.locale is used:
If no locale is passed +I18n.locale+ is used:
[source, ruby]
-------------------------------------------------------
@ -386,7 +618,7 @@ I18n.t :foo, :locale => :de
I18n.l Time.now, :locale => :de
-------------------------------------------------------
I18n.locale defaults to I18n.default_locale which defaults to :en. The default locale can be set like this:
+I18n.locale+ defaults to +I18n.default_locale+ which defaults to :+en+. The default locale can be set like this:
[source, ruby]
-------------------------------------------------------
@ -419,9 +651,9 @@ pt:
bar: baz
-------------------------------------------------------
As you see in both cases the toplevel key is the locale. :foo is a namespace key and :bar is the key for the translation "baz".
As you see in both cases the toplevel key is the locale. +:foo+ is a namespace key and +:bar+ is the key for the translation "baz".
Here is a "real" example from the ActiveSupport en.yml translations YAML file:
Here is a "real" example from the ActiveSupport +en.yml+ translations YAML file:
[source, ruby]
-------------------------------------------------------
@ -433,7 +665,7 @@ en:
long: "%B %d, %Y"
-------------------------------------------------------
So, all of the following equivalent lookups will return the :short date format "%B %d":
So, all of the following equivalent lookups will return the +:short+ date format +"%B %d"+:
[source, ruby]
-------------------------------------------------------
@ -443,11 +675,11 @@ I18n.t :short, :scope => 'date.formats'
I18n.t :short, :scope => [:date, :formats]
-------------------------------------------------------
Generally we recommend using YAML as a format for storing translations. There are cases though where you want to store Ruby lambdas as part of your locale data, e.g. for special date
Generally we recommend using YAML as a format for storing translations. There are cases though where you want to store Ruby lambdas as part of your locale data, e.g. for special date.
=== Translations for ActiveRecord models
=== Translations for Active Record models
You can use the methods Model.human_name and Model.human_attribute_name(attribute) to transparently lookup translations for your model and attribute names.
You can use the methods +Model.human_name+ and +Model.human_attribute_name(attribute)+ to transparently lookup translations for your model and attribute names.
For example when you add the following translations:
@ -463,15 +695,15 @@ en:
# will translate User attribute "login" as "Handle"
-------------------------------------------------------
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".
==== Error message scopes
ActiveRecord validation error messages can also be translated easily. ActiveRecord gives you a couple of namespaces where you can place your message translations in order to provide different messages and translation for certain models, attributes and/or validations. It also transparently takes single table inheritance into account.
Active Record validation error messages can also be translated easily. Active Record gives you a couple of namespaces where you can place your message translations in order to provide different messages and translation for certain models, attributes and/or validations. It also transparently takes single table inheritance into account.
This gives you quite powerful means to flexibly adjust your messages to your application's needs.
Consider a User model with a validates_presence_of validation for the name attribute like this:
Consider a User model with a +validates_presence_of+ validation for the name attribute like this:
[source, ruby]
-------------------------------------------------------
@ -480,7 +712,7 @@ class User < ActiveRecord::Base
end
-------------------------------------------------------
The key for the error message in this case is :blank. ActiveRecord will lookup this key in the namespaces:
The key for the error message in this case is +:blank+. Active Record will lookup this key in the namespaces:
[source, ruby]
-------------------------------------------------------
@ -509,7 +741,7 @@ class Admin < User
end
-------------------------------------------------------
Then ActiveRecord will look for messages in this order:
Then Active Record will look for messages in this order:
[source, ruby]
-------------------------------------------------------
@ -524,11 +756,11 @@ This way you can provide special translations for various error messages at diff
==== Error message interpolation
The translated model name and translated attribute name are always available for interpolation.
The translated model name, translated attribute name, and value are always available for interpolation.
So, for example, instead of the default error message "can not be blank" you could use the attribute name like this: "Please fill in your {{attribute}}".
So, for example, instead of the default error message +"can not be blank"+ you could use the attribute name like this:+ "Please fill in your {{attribute}}"+.
count and/or value are available where applicable. Count can be used for pluralization if present:
+count+, where available, can be used for pluralization if present:
|=====================================================================================================
| validation | with option | message | interpolation
@ -540,25 +772,25 @@ count and/or value are available where applicable. Count can be used for plurali
| validates_length_of | :is | :wrong_length | count
| validates_length_of | :minimum | :too_short | count
| validates_length_of | :maximum | :too_long | count
| validates_uniqueness_of | - | :taken | value
| validates_format_of | - | :invalid | value
| validates_inclusion_of | - | :inclusion | value
| validates_exclusion_of | - | :exclusion | value
| validates_associated | - | :invalid | value
| validates_numericality_of | - | :not_a_number | value
| validates_numericality_of | :greater_than | :greater_than | value
| validates_numericality_of | :greater_than_or_equal_to | :greater_than_or_equal_to | value
| validates_numericality_of | :equal_to | :equal_to | value
| validates_numericality_of | :less_than | :less_than | value
| validates_numericality_of | :less_than_or_equal_to | :less_than_or_equal_to | value
| validates_numericality_of | :odd | :odd | value
| validates_numericality_of | :even | :even | value
| validates_uniqueness_of | - | :taken | -
| validates_format_of | - | :invalid | -
| validates_inclusion_of | - | :inclusion | -
| validates_exclusion_of | - | :exclusion | -
| validates_associated | - | :invalid | -
| validates_numericality_of | - | :not_a_number | -
| validates_numericality_of | :greater_than | :greater_than | count
| validates_numericality_of | :greater_than_or_equal_to | :greater_than_or_equal_to | count
| validates_numericality_of | :equal_to | :equal_to | count
| validates_numericality_of | :less_than | :less_than | count
| validates_numericality_of | :less_than_or_equal_to | :less_than_or_equal_to | count
| validates_numericality_of | :odd | :odd | -
| validates_numericality_of | :even | :even | -
|=====================================================================================================
==== Translations for the ActiveRecord error_messages_for helper
==== Translations for the Active Record error_messages_for helper
If you are using the ActiveRecord error_messages_for helper you will want to add translations for it.
If you are using the Active Record +error_messages_for+ helper you will want to add translations for it.
Rails ships with the following translations:
@ -575,11 +807,29 @@ en:
-------------------------------------------------------
=== Other translations and localizations
=== Overview of other built-in methods that provide I18n support
Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers.
Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers. Here's a brief overview.
TODO list helpers and available keys
==== ActionView helper methods
* +distance_of_time_in_words+ translates and pluralizes its result and interpolates the number of seconds, minutes, hours and so on. See http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L51[datetime.distance_in_words] translations.
* +datetime_select+ and +select_month+ use translated month names for populating the resulting select tag. See http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L15[date.month_names] for translations. +datetime_select+ also looks up the order option from http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L18[date.order] (unless you pass the option explicitely). All date select helpers translate the prompt using the translations in the http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L83[datetime.prompts] scope if applicable.
* The +number_to_currency+, +number_with_precision+, +number_to_percentage+, +number_with_delimiter+ and +humber_to_human_size+ helpers use the number format settings located in the http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L2[number] scope.
==== Active Record methods
* +human_name+ and +human_attribute_name+ use translations for model names and attribute names if available in the http://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml#L43[activerecord.models] scope. They also support translations for inherited class names (e.g. for use with STI) as explained above in "Error message scopes".
* +ActiveRecord::Errors#generate_message+ (which is used by Active Record validations but may also be used manually) uses +human_name+ and +human_attribute_name+ (see above). It also translates the error message and supports translations for inherited class names as explained above in "Error message scopes".
*+ ActiveRecord::Errors#full_messages+ prepends the attribute name to the error message using a separator that will be looked up from http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L91[activerecord.errors.format.separator] (and defaults to +' '+).
==== ActiveSupport methods
* +Array#to_sentence+ uses format settings as given in the http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L30[support.array] scope.
== Customize your I18n setup
@ -609,7 +859,7 @@ ReservedInterpolationKey # the translation contains a reserved interpolation
UnknownFileType # the backend does not know how to handle a file type that was added to I18n.load_path
-------------------------------------------------------
The I18n API will catch all of these exceptions when they were thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for MissingTranslationData exceptions. When a MissingTranslationData exception has been caught it will return the exceptions error message string containing the missing key/scope.
The I18n API will catch all of these exceptions when they were thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for +MissingTranslationData+ exceptions. When a +MissingTranslationData+ exception has been caught it will return the exceptions error message string containing the missing key/scope.
The reason for this is that during development you'd usually want your views to still render even though a translation is missing.
@ -626,11 +876,11 @@ end
I18n.exception_handler = :just_raise_that_exception
-------------------------------------------------------
This would re-raise all caught exceptions including MissingTranslationData.
This would re-raise all caught exceptions including +MissingTranslationData+.
Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides the method #t (as well as #translate). When a MissingTranslationData exception occurs in this context the helper wraps the message into a span with the css class translation_missing.
Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides the method +#t+ (as well as +#translate+). When a +MissingTranslationData+ exception occurs in this context the helper wraps the message into a span with the CSS class +translation_missing+.
To do so the helper forces I18n#translate to raise exceptions no matter what exception handler is defined by setting the :raise option:
To do so the helper forces +I18n#translate+ to raise exceptions no matter what exception handler is defined by setting the +:raise+ option:
[source, ruby]
-------------------------------------------------------
@ -638,8 +888,39 @@ I18n.t :foo, :raise => true # always re-raises exceptions from the backend
-------------------------------------------------------
== Conclusion
At this point you hopefully have a good overview about how I18n support in Ruby on Rails works and are ready to start translating your project.
If you find anything missing or wrong in this guide please file a ticket on http://i18n.lighthouseapp.com/projects/14948-rails-i18n/overview[our issue tracker]. If you want to discuss certain portions or have questions please sign up to our http://groups.google.com/group/rails-i18n[mailinglist].
== Contributing to Rails I18n
I18n support in Ruby on Rails was introduced in the release 2.2 and is still evolving. The project follows the good Ruby on Rails development tradition of evolving solutions in plugins and real applications first and then cherry-picking the best bread of most widely useful features second for inclusion to the core.
Thus we encourage everybody to experiment with new ideas and features in plugins or other libraries and make them available to the community. (Don't forget to announce your work on our http://groups.google.com/group/rails-i18n[mailinglist]!)
If you find your own locale (language) missing from our http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale[example translations data] repository for Ruby on Rails, please http://github.com/guides/fork-a-project-and-submit-your-modifications[_fork_] the repository, add your data and send a http://github.com/guides/pull-requests[pull request].
== Resources
* http://rails-i18n.org[rails-i18n.org] - Homepage of the rails-i18n project. You can find lots of useful resources on the http://rails-i18n.org/wiki[wiki].
* http://groups.google.com/group/rails-i18n[rails-i18n Google group] - The project's mailing list.
* http://github.com/svenfuchs/rails-i18n/tree/master[Github: rails-i18n] - Code repository for the rails-i18n project. Most importantly you can find lots of http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale[example translations] for Rails that should work for your application in most cases.
* http://i18n.lighthouseapp.com/projects/14948-rails-i18n/overview[Lighthouse: rails-i18n] - Issue tracker for the rails-i18n project.
* http://github.com/svenfuchs/i18n/tree/master[Github: i18n] - Code repository for the i18n gem.
* http://i18n.lighthouseapp.com/projects/14947-ruby-i18n/overview[Lighthouse: i18n] - Issue tracker for the i18n gem.
== Authors
* http://www.workingwithrails.com/person/9963-sven-fuchs[Sven Fuchs] (initial author)
* http://www.workingwithrails.com/person/7476-karel-mina-k[Karel Minařík]
If you found this guide useful please consider recommending its authors on http://www.workingwithrails.com[workingwithrails].
== Footnotes
@ -649,6 +930,7 @@ I18n.t :foo, :raise => true # always re-raises exceptions from the backend
[[[3]]] One of these reasons is that we don't want to any unnecessary load for applications that do not need any I18n capabilities, so we need to keep the I18n library as simple as possible for English. Another reason is that it is virtually impossible to implement a one-fits-all solution for all problems related to I18n for all existing languages. So a solution that allows us to exchange the entire implementation easily is appropriate anyway. This also makes it much easier to experiment with custom features and extensions.
== Changelog ==
http://rails.lighthouseapp.com/projects/16213/tickets/23[Lighthouse ticket]

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -80,17 +80,17 @@ understand how to use routing in your own Rails applications, start here.
This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.
***********************************************************
.link:caching_with_rails.html[Rails Caching]
***********************************************************
CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/10[Lighthouse Ticket]
This guide covers the three types of caching that Rails provides by default.
***********************************************************
++++++++++++++++++++++++++++++++++++++
<h2>Digging Deeper</h2>
++++++++++++++++++++++++++++++++++++++
.link:action_mailer_basics.html[Action Mailer Basics]
***********************************************************
CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/25[Lighthouse ticket]
This guide describes how to use Action Mailer to send and receive emails.
***********************************************************
.link:testing_rails_applications.html[Testing Rails Applications]
***********************************************************
CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/8[Lighthouse Ticket]
@ -142,4 +142,4 @@ This guide covers the command line tools and rake tasks provided by Rails.
Authors who have contributed to complete guides are listed link:authors.html[here].
This work is licensed under a link:http://creativecommons.org/licenses/by-nc-sa/3.0/[Creative Commons Attribution-Noncommercial-Share Alike 3.0 License]
This work is licensed under a link:http://creativecommons.org/licenses/by-sa/3.0[Creative Commons Attribution-Share Alike 3.0 License]