aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/integration.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb2
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb2
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb14
-rwxr-xr-xactiverecord/lib/active_record/base.rb4
-rw-r--r--activerecord/lib/active_record/batches.rb38
-rw-r--r--activerecord/lib/active_record/validations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb2
-rw-r--r--railties/guides/files/stylesheets/main.css16
-rw-r--r--railties/guides/images/error_messages.pngbin8440 -> 14645 bytes
-rw-r--r--railties/guides/images/fxn.jpgbin0 -> 17868 bytes
-rw-r--r--railties/guides/images/i18n/demo_localized_pirate.pngbin36500 -> 15027 bytes
-rw-r--r--railties/guides/images/i18n/demo_translated_en.pngbin32877 -> 12057 bytes
-rw-r--r--railties/guides/images/i18n/demo_translated_pirate.pngbin34506 -> 13392 bytes
-rw-r--r--railties/guides/images/i18n/demo_translation_missing.pngbin34373 -> 13143 bytes
-rw-r--r--railties/guides/images/i18n/demo_untranslated.pngbin32793 -> 11925 bytes
-rw-r--r--railties/guides/rails_guides/generator.rb10
-rw-r--r--railties/guides/source/2_3_release_notes.textile85
-rw-r--r--railties/guides/source/action_mailer_basics.textile32
-rw-r--r--railties/guides/source/active_record_querying.textile41
-rw-r--r--railties/guides/source/activerecord_validations_callbacks.textile518
-rw-r--r--railties/guides/source/contribute.textile18
-rw-r--r--railties/guides/source/contributing_to_rails.textile239
-rw-r--r--railties/guides/source/credits.erb.textile31
-rw-r--r--railties/guides/source/form_helpers.textile4
-rw-r--r--railties/guides/source/getting_started.textile1
-rw-r--r--railties/guides/source/i18n.textile207
-rw-r--r--railties/guides/source/index.erb.textile4
-rw-r--r--railties/guides/source/layout.html.erb3
-rw-r--r--railties/guides/source/layouts_and_rendering.textile28
-rw-r--r--railties/guides/source/performance_testing.textile4
-rw-r--r--railties/guides/source/rails_on_rack.textile61
-rw-r--r--railties/guides/source/routing.textile2
-rw-r--r--railties/guides/source/security.textile28
-rw-r--r--railties/guides/source/testing.textile8
36 files changed, 1026 insertions, 384 deletions
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index 1c05ab0bf6..26b695570b 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -5,7 +5,7 @@ require 'active_support/test_case'
module ActionController
module Integration #:nodoc:
# An integration Session instance represents a set of requests and responses
- # performed sequentially by some virtual user. Becase you can instantiate
+ # performed sequentially by some virtual user. Because you can instantiate
# multiple sessions and run them side-by-side, you can also mimic (to some
# limited extent) multiple simultaneous users interacting with your system.
#
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index b7ef1fb90d..c74909a360 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -876,8 +876,8 @@ module ActionView
input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
end
- # Given an ordering of datetime components, create the selection html
- # and join them with their appropriate seperators
+ # Given an ordering of datetime components, create the selection HTML
+ # and join them with their appropriate separators.
def build_selects_from_types(order)
select = ''
order.reverse.each do |type|
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 539f43c6e3..dea958deaf 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -140,7 +140,7 @@ module ActionView
# number_with_delimiter(12345678) # => 12,345,678
# number_with_delimiter(12345678.05) # => 12,345,678.05
# number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
- # number_with_delimiter(12345678, :seperator => ",") # => 12,345,678
+ # number_with_delimiter(12345678, :separator => ",") # => 12,345,678
# number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
# # => 98 765 432,98
#
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 9b4cb64307..f288810d90 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -4,7 +4,7 @@ module ActiveModel
# Validates whether the value of the specified attribute is available in a particular enumerable object.
#
# class Person < ActiveRecord::Base
- # validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+ # validates_inclusion_of :gender, :in => %w( m f )
# validates_inclusion_of :age, :in => 0..99
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
# end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index ad375be184..3aef1b21e9 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -143,6 +143,8 @@ module ActiveRecord
end
# Remove all records from this association
+ #
+ # See delete for more info.
def delete_all
load_target
delete(@target)
@@ -200,11 +202,11 @@ module ActiveRecord
end
end
- # Destroy +records+ and remove from this association calling +before_remove+
- # and +after_remove+ callbacks.
+ # Destroy +records+ and remove them from this association calling
+ # +before_remove+ and +after_remove+ callbacks.
#
- # Note this method will always remove records from database ignoring the
- # +:dependent+ option.
+ # Note that this method will _always_ remove records from the database
+ # ignoring the +:dependent+ option.
def destroy(*records)
remove_records(records) do |records, old_records|
old_records.each { |record| record.destroy }
@@ -226,7 +228,9 @@ module ActiveRecord
self
end
- # Destory all the records from this association
+ # Destory all the records from this association.
+ #
+ # See destroy for more info.
def destroy_all
load_target
destroy(@target)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 4ef3cc698b..2a5385119d 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -736,12 +736,12 @@ module ActiveRecord #:nodoc:
# ==== Parameters
#
# * +id+ - This should be the id or an array of ids to be updated.
- # * +attributes+ - This should be a Hash of attributes to be set on the object, or an array of Hashes.
+ # * +attributes+ - This should be a hash of attributes to be set on the object, or an array of hashes.
#
# ==== Examples
#
# # Updating one record:
- # Person.update(15, { :user_name => 'Samuel', :group => 'expert' })
+ # Person.update(15, :user_name => 'Samuel', :group => 'expert')
#
# # Updating multiple records:
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb
index 03bd4f9f93..5a6cecd4ad 100644
--- a/activerecord/lib/active_record/batches.rb
+++ b/activerecord/lib/active_record/batches.rb
@@ -4,10 +4,12 @@ module ActiveRecord
base.extend(ClassMethods)
end
- # When processing large numbers of records, it's often a good idea to do so in batches to prevent memory ballooning.
+ # When processing large numbers of records, it's often a good idea to do
+ # so in batches to prevent memory ballooning.
module ClassMethods
- # Yields each record that was found by the find +options+. The find is performed by find_in_batches
- # with a batch size of 1000 (or as specified by the +batch_size+ option).
+ # Yields each record that was found by the find +options+. The find is
+ # performed by find_in_batches with a batch size of 1000 (or as
+ # specified by the <tt>:batch_size</tt> option).
#
# Example:
#
@@ -15,9 +17,10 @@ module ActiveRecord
# person.party_all_night!
# end
#
- # Note: This method is only intended to use for batch processing of large amounts of records that wouldn't fit in
- # memory all at once. If you just need to loop over less than 1000 records, it's probably better just to use the
- # regular find methods.
+ # Note: This method is only intended to use for batch processing of
+ # large amounts of records that wouldn't fit in memory all at once. If
+ # you just need to loop over less than 1000 records, it's probably
+ # better just to use the regular find methods.
def find_each(options = {})
find_in_batches(options) do |records|
records.each { |record| yield record }
@@ -26,17 +29,22 @@ module ActiveRecord
self
end
- # Yields each batch of records that was found by the find +options+ as an array. The size of each batch is
- # set by the +batch_size+ option; the default is 1000.
+ # Yields each batch of records that was found by the find +options+ as
+ # an array. The size of each batch is set by the <tt>:batch_size</tt>
+ # option; the default is 1000.
#
- # You can control the starting point for the batch processing by supplying the +start+ option. This is especially
- # useful if you want multiple workers dealing with the same processing queue. You can make worker 1 handle all the
- # records between id 0 and 10,000 and worker 2 handle from 10,000 and beyond (by setting the +start+ option on that
- # worker).
+ # You can control the starting point for the batch processing by
+ # supplying the <tt>:start</tt> option. This is especially useful if you
+ # want multiple workers dealing with the same processing queue. You can
+ # make worker 1 handle all the records between id 0 and 10,000 and
+ # worker 2 handle from 10,000 and beyond (by setting the <tt>:start</tt>
+ # option on that worker).
#
- # It's not possible to set the order. That is automatically set to ascending on the primary key ("id ASC")
- # to make the batch ordering work. This also mean that this method only works with integer-based primary keys.
- # You can't set the limit either, that's used to control the the batch sizes.
+ # It's not possible to set the order. That is automatically set to
+ # ascending on the primary key ("id ASC") to make the batch ordering
+ # work. This also mean that this method only works with integer-based
+ # primary keys. You can't set the limit either, that's used to control
+ # the the batch sizes.
#
# Example:
#
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 4e084f071c..d2d12b80c9 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -802,7 +802,7 @@ module ActiveRecord
# Validates whether the value of the specified attribute is available in a particular enumerable object.
#
# class Person < ActiveRecord::Base
- # validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+ # validates_inclusion_of :gender, :in => %w( m f )
# validates_inclusion_of :age, :in => 0..99
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list"
# end
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 43d70c7013..7f94da015b 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -1,7 +1,7 @@
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Date #:nodoc:
- # Enables the use of time calculations within Time itself
+ # Enables the use of time calculations within Date itself
module Calculations
def self.included(base) #:nodoc:
base.extend ClassMethods
diff --git a/railties/guides/files/stylesheets/main.css b/railties/guides/files/stylesheets/main.css
index 5061b130e3..d377628d73 100644
--- a/railties/guides/files/stylesheets/main.css
+++ b/railties/guides/files/stylesheets/main.css
@@ -337,7 +337,7 @@ h6 {
margin-top: 0.25em;
}
-#mainCol dd.warning, #subCol dd.warning {
+#mainCol div.warning, #subCol dd.warning {
background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
border: none;
padding: 1.25em 1.25em 1.25em 48px;
@@ -426,4 +426,16 @@ code {
.clearfix {display: inline-block;}
* html .clearfix {height: 1%;}
.clearfix {display: block;}
-.clear { clear:both; } \ No newline at end of file
+.clear { clear:both; }
+
+/* Same bottom margin for special boxes than for regular paragraphs, this way
+intermediate whitespace looks uniform. */
+div.code_container, div.important, div.caution, div.warning, div.note, div.info {
+ margin-bottom: 1.5em;
+}
+
+/* Remove bottom margin of paragraphs in special boxes, otherwise they get a
+spurious blank area below with the box background. */
+div.important p, div.caution p, div.warning p, div.note p, div.info p {
+ margin-bottom: 0px;
+}
diff --git a/railties/guides/images/error_messages.png b/railties/guides/images/error_messages.png
index 32de1cac21..428892194a 100644
--- a/railties/guides/images/error_messages.png
+++ b/railties/guides/images/error_messages.png
Binary files differ
diff --git a/railties/guides/images/fxn.jpg b/railties/guides/images/fxn.jpg
new file mode 100644
index 0000000000..b661a0e402
--- /dev/null
+++ b/railties/guides/images/fxn.jpg
Binary files differ
diff --git a/railties/guides/images/i18n/demo_localized_pirate.png b/railties/guides/images/i18n/demo_localized_pirate.png
index 22b93416a0..9134709573 100644
--- a/railties/guides/images/i18n/demo_localized_pirate.png
+++ b/railties/guides/images/i18n/demo_localized_pirate.png
Binary files differ
diff --git a/railties/guides/images/i18n/demo_translated_en.png b/railties/guides/images/i18n/demo_translated_en.png
index 7ea0c437a5..ecdd878d38 100644
--- a/railties/guides/images/i18n/demo_translated_en.png
+++ b/railties/guides/images/i18n/demo_translated_en.png
Binary files differ
diff --git a/railties/guides/images/i18n/demo_translated_pirate.png b/railties/guides/images/i18n/demo_translated_pirate.png
index 60ef370158..41c580923a 100644
--- a/railties/guides/images/i18n/demo_translated_pirate.png
+++ b/railties/guides/images/i18n/demo_translated_pirate.png
Binary files differ
diff --git a/railties/guides/images/i18n/demo_translation_missing.png b/railties/guides/images/i18n/demo_translation_missing.png
index 86a3121cc1..af9e2d0427 100644
--- a/railties/guides/images/i18n/demo_translation_missing.png
+++ b/railties/guides/images/i18n/demo_translation_missing.png
Binary files differ
diff --git a/railties/guides/images/i18n/demo_untranslated.png b/railties/guides/images/i18n/demo_untranslated.png
index e6717fb7d1..3603f43463 100644
--- a/railties/guides/images/i18n/demo_untranslated.png
+++ b/railties/guides/images/i18n/demo_untranslated.png
Binary files differ
diff --git a/railties/guides/rails_guides/generator.rb b/railties/guides/rails_guides/generator.rb
index 18fdb81810..dd147c4d5f 100644
--- a/railties/guides/rails_guides/generator.rb
+++ b/railties/guides/rails_guides/generator.rb
@@ -109,8 +109,8 @@ module RailsGuides
end
def textile(body)
- # If the issue with nontextile is fixed just remove the wrapper.
- with_workaround_for_nontextile(body) do |body|
+ # If the issue with notextile is fixed just remove the wrapper.
+ with_workaround_for_notextile(body) do |body|
t = RedCloth.new(body)
t.hard_breaks = false
t.to_html(:notestuff, :plusplus, :code, :tip)
@@ -120,18 +120,18 @@ module RailsGuides
# For some reason the notextile tag does not always turn off textile. See
# LH ticket of the security guide (#7). As a temporary workaround we deal
# with code blocks by hand.
- def with_workaround_for_nontextile(body)
+ def with_workaround_for_notextile(body)
code_blocks = []
body.gsub!(%r{<(yaml|shell|ruby|erb|html|sql|plain)>(.*?)</\1>}m) do |m|
es = ERB::Util.h($2)
css_class = ['erb', 'shell'].include?($1) ? 'html' : $1
code_blocks << %{<div class="code_container"><code class="#{css_class}">#{es}</code></div>}
- "dirty_workaround_for_nontextile_#{code_blocks.size - 1}"
+ "\ndirty_workaround_for_notextile_#{code_blocks.size - 1}\n"
end
body = yield body
- body.gsub(%r{<p>dirty_workaround_for_nontextile_(\d+)</p>}) do |_|
+ body.gsub(%r{<p>dirty_workaround_for_notextile_(\d+)</p>}) do |_|
code_blocks[$1.to_i]
end
end
diff --git a/railties/guides/source/2_3_release_notes.textile b/railties/guides/source/2_3_release_notes.textile
index c58cbc0b81..2acc07891f 100644
--- a/railties/guides/source/2_3_release_notes.textile
+++ b/railties/guides/source/2_3_release_notes.textile
@@ -1,7 +1,5 @@
h2. Ruby on Rails 2.3 Release Notes
-NOTE: These release notes refer to RC2 of Rails 2.3. This is a release candidate, and not the final version of Rails 2.3. It's intended to be a stable testing release, and we urge you to test your own applications and report any issues to the "Rails Lighthouse":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/overview.
-
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 "list of commits":http://github.com/rails/rails/commits/master in the main Rails repository on GitHub or review the +CHANGELOG+ files for the individual Rails components.
endprologue.
@@ -29,6 +27,7 @@ Here's a summary of the rack-related changes:
* +ActionController::CGIHandler+ is a backwards compatible CGI wrapper around Rack. The +CGIHandler+ is meant to take an old CGI object and convert 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).
+* You no longer need to use +CGI::Cookie.new+ in your tests for setting a cookie value. Assigning a +String+ value to request.cookies["foo"] now sets the cookie as expected.
* +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+
@@ -140,19 +139,19 @@ end
You can pass most of the +find+ options into +find_in_batches+. However, you cannot specify the order that records will be returned in (they will always be returned in ascending order of primary key, which must be an integer), or use the +:limit+ option. Instead, use the +:batch_size+ option, which defaults to 1000, to set the number of records that will be returned in each batch.
-The new +each+ method provides a wrapper around +find_in_batches+ that returns individual records, with the find itself being done in batches (of 1000 by default):
+The new +find_each+ method provides a wrapper around +find_in_batches+ that returns individual records, with the find itself being done in batches (of 1000 by default):
<ruby>
-Customer.each do |customer|
+Customer.find_each do |customer|
customer.update_account_balance!
end
</ruby>
Note that you should only use this method for batch processing: for small numbers of records (less than 1000), you should just use the regular find methods with your own loop.
-* More Information:
- - "Rails 2.3: Batch Finding":http://afreshcup.com/2009/02/23/rails-23-batch-finding/
- - "What's New in Edge Rails: Batched Find":http://ryandaigle.com/articles/2009/2/23/what-s-new-in-edge-rails-batched-find
+* More Information (at that point the convenience method was called just +each+):
+** "Rails 2.3: Batch Finding":http://afreshcup.com/2009/02/23/rails-23-batch-finding/
+** "What's New in Edge Rails: Batched Find":http://ryandaigle.com/articles/2009/2/23/what-s-new-in-edge-rails-batched-find
h4. Multiple Conditions for Callbacks
@@ -175,18 +174,6 @@ developers = Developer.find(:all, :group => "salary",
* Lead Contributor: "Emilio Tagua":http://github.com/miloops
-h4. Hash Conditions for has_many relationships
-
-You can once again use a hash in conditions for a +has_many+ relationship:
-
-<ruby>
-has_many :orders, :conditions => {:status => 'confirmed'}
-</ruby>
-
-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: "Frederick Cheung":http://www.spacevatican.org/
-
h4. 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.
@@ -207,6 +194,8 @@ h4. Other Active Record Changes
* 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)
+* Counts on scoped selects now work properly, so you can do things like +Account.scoped(:select => "DISTINCT credit_limit").count+
+* +ActiveRecord::Base#invalid?+ now works as the opposite of +ActiveRecord::Base#valid?+.
h3. Action Controller
@@ -299,7 +288,7 @@ In some of the first fruits of the Rails-Merb team merger, Rails 2.3 includes so
h4. 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.
+Rails now keeps a per-request local cache of read from the remote cache stores, 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: "Nahum Wild":http://www.motionstandingstill.com/
@@ -307,6 +296,8 @@ h4. 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.
+In addition, you can use the same scheme to localize the rescue files in the +public+ directory: +public/500.da.html+ or +public/404.en.html+ work, for example.
+
h4. Partial Scoping for Translations
A change to the translation API makes things easier and less repetitive to write key translations within partials. If you call +translate(".foo")+ from the +people/index.html.erb+ template, you'll actually be calling +I18n.translate("people.index.foo")+ If you don't prepend the key with a period, then the API doesn't scope, just as before.
@@ -321,6 +312,9 @@ h4. Other Action Controller Changes
* The +:only+ and +:except+ options for +map.resources+ are no longer inherited by nested resources.
* The bundled memcached client has been updated to version 1.6.4.99.
* The +expires_in+, +stale?+, and +fresh_when+ methods now accept a +:public+ option to make them work well with proxy caching.
+* The +:requirements+ option now works properly with additional RESTful member routes.
+* Shallow routes now properly respect namespaces.
+* +polymorphic_url+ does a better job of handling objects with irregular plural names.
h3. Action View
@@ -439,6 +433,34 @@ returns
</optgroup>
</ruby>
+h4. Disabled Option Tags for Form Select Helpers
+
+The form select helpers (such as +select+ and +options_for_select+) now support a +:disabled+ option, which can take a single value or an array of values to be disabled in the resulting tags:
+
+<ruby>
+select(:post, :category, Post::CATEGORIES, :disabled => ‘private‘)
+</ruby>
+
+returns
+
+<ruby>
+<select name=“post[category]“>
+<option>story</option>
+<option>joke</option>
+<option>poem</option>
+<option disabled=“disabled“>private</option>
+</select>
+</ruby>
+
+You can also use an anonymous function to determine at runtime which options from collections will be selected and/or disabled:
+
+<ruby>
+options_from_collection_for_select(@product.sizes, :name, :id, :disabled => lambda{|size| size.out_of_stock?})
+</ruby>
+
+* Lead Contributor: "Tekin Suleyman":http://tekin.co.uk/
+* More Information: "New in rails 2.3 - disabled option tags and lambdas for selecting and disabling options from collections":http://tekin.co.uk/2009/03/new-in-rails-23-disabled-option-tags-and-lambdas-for-selecting-and-disabling-options-from-collections/
+
h4. A Note About Template Loading
Rails 2.3 includes the ability to enable or disable cached templates for any particular environment. Cached templates give you a speed boost because they don't check for a new template file when they're rendered - but they also mean that you can't replace a template "on the fly" without restarting the server.
@@ -472,6 +494,17 @@ h4. 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.
+h4. Swappable Parsers for XMLmini
+
+The support for XML parsing in ActiveSupport has been made more flexible by allowing you to swap in different parsers. By default, it uses the standard REXML implementation, but you can easily specify the faster LibXML or Nokogiri implementations for your own applications, provided you have the appropriate gems installed:
+
+<ruby>
+XmlMini.backend = 'LibXML'
+</ruby>
+
+* Lead Contributor: "Bart ten Brinke":http://www.movesonrails.com/
+* Lead Contributor: "Aaron Patterson":http://tenderlovemaking.com/
+
h4. 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:
@@ -494,6 +527,10 @@ h4. Other Active Support Changes
* +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.
+* If you memoize a private method, the result will now be private.
+* +String#parameterize+ accepts an optional separator: +"Quick Brown Fox".parameterize('_') => "quick_brown_fox"+.
+* +number_to_phone+ accepts 7-digit phone numbers now.
+* +ActiveSupport::Json.decode+ now handles +\u0000+ style escape sequences.
h3. Railties
@@ -532,6 +569,12 @@ Quite a bit of work was done to make sure that bits of Rails (and its dependenci
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.
+h4. rake gem Task Rewrite
+
+The internals of the various <code>rake gem</code> tasks have been substantially revised, to make the system work better for a variety of cases. The gem system now knows the difference between development and runtime dependencies, has a more robust unpacking system, gives better information when querying for the status of gems, and is less prone to "chicken and egg" dependency issues when you're bringing things up from scratch. There are also fixes for using gem commands under JRuby and for dependencies that try to bring in external copies of gems that are already vendored.
+
+* Lead Contributor: "David Dollar":http://www.workingwithrails.com/person/12240-david-dollar
+
h4. Other Railties Changes
* The instructions for updating a CI server to build Rails have been updated and expanded.
@@ -543,6 +586,8 @@ h4. Other Railties Changes
* Rails Guides have been converted from AsciiDoc to Textile markup.
* Scaffolded views and controllers have been cleaned up a bit.
* +script/server+ now accepts a <tt>--path</tt> argument to mount a Rails application from a specific path.
+* If any configured gems are missing, the gem rake tasks will skip loading much of the environment. This should solve many of the "chicken-and-egg" problems where rake gems:install couldn't run because gems were missing.
+* Gems are now unpacked exactly once. This fixes issues with gems (hoe, for instance) which are packed with read-only permissions on the files.
h3. Deprecated
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 71398382be..0e52bf6f32 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -262,6 +262,38 @@ class UserMailer < ActionMailer::Base
end
</ruby>
+h4. Sending multipart emails with attachments
+
+Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename. You must declare which template you are using for each content type via the +part+ method.
+
+In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder.
+
+<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 "text/html" do |p|
+ p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>")
+ end
+
+ part "text/plain" do |p|
+ p.body = render_message("welcome_email_plain", :message => "text content")
+ end
+
+ 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
+</ruby>
+
h3. 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:
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 03e1b264b2..f66947e47d 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -783,6 +783,47 @@ h3. select_all
Client.connection.select_all("SELECT * FROM clients WHERE id = '1'")
</ruby>
+h3. Working with Large Amounts of Data
+
+Sometimes you need to iterate over a large set of records. For example to send a newsletter to all users, to export some data, etc. That may seem pretty easy:
+
+<ruby>
+ # Careful!
+ LegacySurvey.all.each do |legacy_survey|
+ Survey.migrate_legacy_survey(legacy_survey)
+ end
+</ruby>
+
+But if the number of rows is big, say more than a thousand, that approach may vary from being underperformant to just plain impossible.
+
+Reason is a call like +LegacySurvey.all.each+ makes Active Record fetch _the entire table_, build a model per row, and build an array with all the models. Sometimes that is just too many objects, it demands too much memory.
+
+To be able to iterate over big sets of rows like that Active Record provides +find_each+:
+
+<ruby>
+ # No prob.
+ LegacySurvey.find_each do |legacy_survey|
+ Survey.migrate_legacy_survey(legacy_survey)
+ end
+</ruby>
+
+Behind the scenes +find_each+ fetches rows in batches of 1000 and yields them one by one. The size of the underlying batches is configurable via the +:batch_size+ option.
+
+Records are fetched in ascending order on the primary key, which must be an integer. The +:start+ option allows you to configure the first ID of the sequence if the lowest is not the one you need. This may be useful for example to be able to resume an interrupted batch process if it saves the last processed ID as a checkpoint.
+
++find_each+ accepts the same options as +find+ except for +:order+ and +:limit+. Those two are needed internally and if the options argument include any of them an exception is raised.
+
+In addition, you can work by chunks instead of row by row using +find_in_batches+. This method is analogous to +find_each+, but it yields arrays of models instead:
+
+<ruby>
+ # Works in chunks of 1000 invoices at a time.
+ Invoice.find_in_batches(:include => :invoice_lines) do |invoices|
+ export.add_invoices(invoices)
+ end
+</ruby>
+
+In fact, +find_each+ is just a convenience wrapper over +find_in_batches+.
+
h3. Existence of Objects
If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or +false+.
diff --git a/railties/guides/source/activerecord_validations_callbacks.textile b/railties/guides/source/activerecord_validations_callbacks.textile
index 01e52bf01e..9c9aaf9ad2 100644
--- a/railties/guides/source/activerecord_validations_callbacks.textile
+++ b/railties/guides/source/activerecord_validations_callbacks.textile
@@ -16,7 +16,7 @@ endprologue.
h3. The Object Lifecycle
-During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this <em>object lifecycle</em> so that you can control your application and its data.
+During the normal operation of a Rails application objects may be created, updated, and destroyed. Active Record provides hooks into this <em>object lifecycle</em> so that you can control your application and its data.
Validations allow you to ensure that only valid data is stored in your database. Callbacks and observers allow you to trigger logic before or after an alteration of an object's state.
@@ -26,18 +26,18 @@ Before you dive into the detail of validations in Rails, you should understand a
h4. Why Use Validations?
-Validations are used to ensure that only valid data is saved into your database. For example, it may be important to your application to ensure that every user provides a valid email address and mailing address
+Validations are used to ensure that only valid data is saved into your database. For example, it may be important to your application to ensure that every user provides a valid email address and mailing address.
There are several ways to validate data before it is saved into your database, including native database constraints, client-side validations, controller-level validations, and model-level validations.
* Database constraints and/or stored procedures make the validation mechanisms database-dependent and can make testing and maintenance more difficult. However, if your database is used by other applications, it may be a good idea to use some constraints at the database level. Additionally, database-level validations can safely handle some things (such as uniqueness in heavily-used tables) that can be difficult to implement otherwise.
-* Client-side validations can be useful, but are generally unreliable if used alone. If they are implemented using Javascript, they may be bypassed if Javascript is turned off in the user's browser. However, if combined with other techniques, client-side validation can be a convenient way to provide users with immediate feedback as they use your site.
+* Client-side validations can be useful, but are generally unreliable if used alone. If they are implemented using JavaScript, they may be bypassed if JavaScript is turned off in the user's browser. However, if combined with other techniques, client-side validation can be a convenient way to provide users with immediate feedback as they use your site.
* Controller-level validations can be tempting to use, but often become unwieldy and difficult to test and maintain. Whenever possible, it's a good idea to "keep your controllers skinny":http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model, as it will make your application a pleasure to work with in the long run.
* Model-level validations are the best way to ensure that only valid data is saved into your database. They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain. Rails makes them easy to use, provides built-in helpers for common needs, and allows you to create your own validation methods as well.
h4. When Does Validation Happen?
-There are two kinds of Active Record objects: those that correspond to a row inside your database and those that do not. When you create a fresh object, using the +new+ method, that object does not belong to the database yet. Once you call +save+ upon that object it will be saved into the appropriate database table. Active Record uses the +new_record?+ instance method to determine whether an object is already in the database or not. Consider the following simple Active Record class:
+There are two kinds of Active Record objects: those that correspond to a row inside your database and those that do not. When you create a fresh object, for example using the +new+ method, that object does not belong to the database yet. Once you call +save+ upon that object it will be saved into the appropriate database table. Active Record uses the +new_record?+ instance method to determine whether an object is already in the database or not. Consider the following simple Active Record class:
<ruby>
class Person < ActiveRecord::Base
@@ -61,7 +61,7 @@ Creating and saving a new record will send an SQL +INSERT+ operation to the data
CAUTION: There are many ways to change the state of an object in the database. Some methods will trigger validations, but some will not. This means that it's possible to save an object in the database in an invalid state if you aren't careful.
-The following methods trigger validations, and will save the object to the database only if the object is valid. The bang versions (e.g. +save!+) will raise an exception if the record is invalid. The non-bang versions (e.g. +save+) simply return +false+.
+The following methods trigger validations, and will save the object to the database only if the object is valid:
* +create+
* +create!+
@@ -71,6 +71,8 @@ The following methods trigger validations, and will save the object to the datab
* +update_attributes+
* +update_attributes!+
+The bang versions (e.g. +save!+) raise an exception if the record is invalid. The non-bang versions don't: +save+ and +update_attributes+ return +false+, +create+ and +update+ just return the object/s.
+
h4. Skipping Validations
The following methods skip validations, and will save the object to the database regardless of its validity. They should be used with caution.
@@ -84,11 +86,11 @@ The following methods skip validations, and will save the object to the database
* +update_attribute+
* +update_counters+
-Note that +save+ also has the ability to skip validations (and callbacks!) if passed +false+. This technique should be used with caution.
+Note that +save+ also has the ability to skip validations if passed +false+ as argument. This technique should be used with caution.
* +save(false)+
-h4. Object#valid? and Object#invalid?
+h4. +valid?+ and +invalid?+
To verify whether or not an object is valid, Rails uses the +valid?+ method. You can also use this method on your own. +valid?+ triggers your validations and returns true if no errors were added to the object, and false otherwise.
@@ -101,7 +103,7 @@ Person.create(:name => "John Doe").valid? # => true
Person.create(:name => nil).valid? # => false
</ruby>
-When Active Record is performing validations, any errors found are collected into an +errors+ instance variable and can be accessed through an +errors+ instance method. An object is considered invalid if it has errors, and calling +save+ or +save!+ will not save it to the database.
+When Active Record is performing validations, any errors found can be accessed through the +errors+ instance method. By definition an object is valid if this collection is empty after running validations.
Note that an object instantiated with +new+ will not report errors even if it's technically invalid, because validations are not run when using +new+.
@@ -135,7 +137,11 @@ end
=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
</ruby>
-To verify whether or not a particular attribute of an object is valid, you can use the +invalid?+ method. This method is only useful _after_ validations have been run, because it only inspects the errors collection and does not trigger validations itself. It's different from the +valid?+ method because it doesn't verify the validity of the object as a whole, but only if there are errors found on an individual attribute of the object.
++invalid?+ is simply the inverse of +valid?+. +invalid?+ triggers your validations and returns true if any errors were added to the object, and false otherwise.
+
+h4. +errors.invalid?+
+
+To verify whether or not a particular attribute of an object is valid, you can use the +errors.invalid?+ method. This method is only useful _after_ validations have been run, because it only inspects the errors collection and does not trigger validations itself. It's different from the +ActiveRecord::Base#invalid?+ method explained above because it doesn't verify the validity of the object as a whole, but only if there are errors found on an individual attribute of the object.
<ruby>
class Person < ActiveRecord::Base
@@ -146,19 +152,19 @@ end
>> Person.create.errors.invalid?(:name) # => true
</ruby>
-We'll cover validation errors in greater depth in the *Working with Validation Errors* section. For now, let's turn to the built-in validation helpers that Rails provides by default.
+We'll cover validation errors in greater depth in the "Working with Validation Errors":#workingwith-validation-errors section. For now, let's turn to the built-in validation helpers that Rails provides by default.
h3. Validation Helpers
-Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers create validation rules that are commonly used. Every time a validation fails, an error message is added to the object's +errors+ collection, and this message is associated with the field being validated.
+Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers provide common validation rules. Every time a validation fails, an error message is added to the object's +errors+ collection, and this message is associated with the field being validated.
-Each helper accepts an arbitrary number of attributes identified by symbols, so with a single line of code you can add the same kind of validation to several attributes.
+Each helper accepts an arbitrary number of attribute names, so with a single line of code you can add the same kind of validation to several attributes.
-All these helpers accept the +:on+ and +:message+ options, which define when the validation should be applied and what message should be added to the +errors+ collection when it fails, respectively. The +:on+ option takes one of the values +:save+ (the default), +:create+ or +:update+. There is a default error message for each one of the validation helpers. These messages are used when the +:message+ option isn't used. Let's take a look at each one of the available helpers.
+All of them accept the +:on+ and +:message+ options, which define when the validation should be run and what message should be added to the +errors+ collection if it fails, respectively. The +:on+ option takes one of the values +:save+ (the default), +:create+ or +:update+. There is a default error message for each one of the validation helpers. These messages are used when the +:message+ option isn't specified. Let's take a look at each one of the available helpers.
-h4. validates_acceptance_of
+h4. +validates_acceptance_of+
-Validates that a checkbox on the user interface was checked when a form was submitted. This is normally used when the user needs to agree to your application's terms of service, confirm reading some text, or any similar concept. This validation is very specific to web applications and actually this 'acceptance' does not need to be recorded anywhere in your database (if you don't have a field for it, the helper will just create a virtual attribute).
+Validates that a checkbox on the user interface was checked when a form was submitted. This is typically used when the user needs to agree to your application's terms of service, confirm reading some text, or any similar concept. This validation is very specific to web applications and actually this 'acceptance' does not need to be recorded anywhere in your database (if you don't have a field for it, the helper will just create a virtual attribute).
<ruby>
class Person < ActiveRecord::Base
@@ -166,7 +172,7 @@ class Person < ActiveRecord::Base
end
</ruby>
-The default error message for +validates_acceptance_of+ is "_must be accepted_"
+The default error message for +validates_acceptance_of+ is "_must be accepted_".
+validates_acceptance_of+ can receive an +:accept+ option, which determines the value that will be considered acceptance. It defaults to "1", but you can change this.
@@ -176,7 +182,7 @@ class Person < ActiveRecord::Base
end
</ruby>
-h4. validates_associated
+h4. +validates_associated+
You should use this helper when your model has associations with other models and they also need to be validated. When you try to save your object, +valid?+ will be called upon each one of the associated objects.
@@ -189,13 +195,13 @@ end
This validation will work with all the association types.
-CAUTION: Don't use +validates_associated+ on both ends of your associations, because this will lead to several recursive calls and blow up the method calls' stack.
+CAUTION: Don't use +validates_associated+ on both ends of your associations, they would call each other in an infinite loop.
The default error message for +validates_associated+ is "_is invalid_". Note that each associated object will contain its own +errors+ collection; errors do not bubble up to the calling model.
-h4. validates_confirmation_of
+h4. +validates_confirmation_of+
-You should use this helper when you have two text fields that should receive exactly the same content. For example, you may want to confirm an email address or a password. This validation creates a virtual attribute, using the name of the field that has to be confirmed with '_confirmation' appended.
+You should use this helper when you have two text fields that should receive exactly the same content. For example, you may want to confirm an email address or a password. This validation creates a virtual attribute whose name is the name of the field that has to be confirmed with "_confirmation" appended.
<ruby>
class Person < ActiveRecord::Base
@@ -210,7 +216,7 @@ In your view template you could use something like
<%= text_field :person, :email_confirmation %>
</erb>
-NOTE: This check is performed only if +email_confirmation+ is not nil, and by default only on save. To require confirmation, make sure to add a presence check for the confirmation attribute (we'll take a look at +validates_presence_of+ later on this guide):
+This check is performed only if +email_confirmation+ is not +nil+. To require confirmation, make sure to add a presence check for the confirmation attribute (we'll take a look at +validates_presence_of+ later on this guide):
<ruby>
class Person < ActiveRecord::Base
@@ -219,54 +225,54 @@ class Person < ActiveRecord::Base
end
</ruby>
-The default error message for +validates_confirmation_of+ is "_doesn't match confirmation_"
+The default error message for +validates_confirmation_of+ is "_doesn't match confirmation_".
-h4. validates_exclusion_of
+h4. +validates_exclusion_of+
This helper validates that the attributes' values are not included in a given set. In fact, this set can be any enumerable object.
<ruby>
-class MovieFile < ActiveRecord::Base
- validates_exclusion_of :format, :in => %w(mov avi),
- :message => "Extension %s is not allowed"
+class Account < ActiveRecord::Base
+ validates_exclusion_of :subdomain, :in => %w(www),
+ :message => "Subdomain {{value}} is reserved."
end
</ruby>
-The +validates_exclusion_of+ helper has an option +:in+ that receives the set of values that will not be accepted for the validated attributes. The +:in+ option has an alias called +:within+ that you can use for the same purpose, if you'd like to. This example uses the +:message+ option to show how you can personalize it with the current attribute's value, through the +%s+ format mask.
+The +validates_exclusion_of+ helper has an option +:in+ that receives the set of values that will not be accepted for the validated attributes. The +:in+ option has an alias called +:within+ that you can use for the same purpose, if you'd like to. This example uses the +:message+ option to show how you can include the attribute's value.
The default error message for +validates_exclusion_of+ is "_is not included in the list_".
-h4. validates_format_of
+h4. +validates_format_of+
-This helper validates the attributes' values by testing whether they match a given pattern. This pattern must be specified using a Ruby regular expression, which is specified using the +:with+ option.
+This helper validates the attributes' values by testing whether they match a given regular expresion, which is specified using the +:with+ option.
<ruby>
class Product < ActiveRecord::Base
- validates_format_of :description, :with => /^[a-zA-Z]+$/,
+ validates_format_of :legacy_code, :with => /\A[a-zA-Z]+\z/,
:message => "Only letters allowed"
end
</ruby>
The default error message for +validates_format_of+ is "_is invalid_".
-h4. validates_inclusion_of
+h4. +validates_inclusion_of+
This helper validates that the attributes' values are included in a given set. In fact, this set can be any enumerable object.
<ruby>
class Coffee < ActiveRecord::Base
validates_inclusion_of :size, :in => %w(small medium large),
- :message => "%s is not a valid size"
+ :message => "{{value}} is not a valid size"
end
</ruby>
-The +validates_inclusion_of+ helper has an option +:in+ that receives the set of values that will be accepted. The +:in+ option has an alias called +:within+ that you can use for the same purpose, if you'd like to. The previous example uses the +:message+ option to show how you can personalize it with the current attribute's value, through the +%s+ format mask.
+The +validates_inclusion_of+ helper has an option +:in+ that receives the set of values that will be accepted. The +:in+ option has an alias called +:within+ that you can use for the same purpose, if you'd like to. The previous example uses the +:message+ option to show how you can include the attribute's value.
The default error message for +validates_inclusion_of+ is "_is not included in the list_".
-h4. validates_length_of
+h4. +validates_length_of+
-This helper validates the length of your attribute's value. It includes a variety of different options, so you can specify length constraints in different ways:
+This helper validates the length of the attributes' values. It provides a variety of options, so you can specify length constraints in different ways:
<ruby>
class Person < ActiveRecord::Base
@@ -281,24 +287,46 @@ The possible length constraint options are:
* +:minimum+ - The attribute cannot have less than the specified length.
* +:maximum+ - The attribute cannot have more than the specified length.
-* +:in+ (or +:within+) - The attribute length must be included in a given interval. The value for this option must be a Ruby range.
-* +:is+ - The attribute length must be equal to a given value.
+* +:in+ (or +:within+) - The attribute length must be included in a given interval. The value for this option must be a range.
+* +:is+ - The attribute length must be equal to the given value.
-The default error messages depend on the type of length validation being performed. You can personalize these messages, using the +:wrong_length+, +:too_long+ and +:too_short+ options and the +%d+ format mask as a placeholder for the number corresponding to the length constraint being used. You can still use the +:message+ option to specify an error message.
+The default error messages depend on the type of length validation being performed. You can personalize these messages using the +:wrong_length+, +:too_long+, and +:too_short+ options and <tt>{{count}}</tt> as a placeholder for the number corresponding to the length constraint being used. You can still use the +:message+ option to specify an error message.
<ruby>
class Person < ActiveRecord::Base
- validates_length_of :bio, :too_long => "you're writing too much. %d characters is the maximum allowed."
+ validates_length_of :bio, :maximum => 1000,
+ :too_long => "{{count}} characters is the maximum allowed"
+end
+</ruby>
+
+This helper counts characters by default, but you can split the value in a different way using the +:tokenizer+ option:
+
+<ruby>
+class Essay < ActiveRecord::Base
+ validates_length_of :content,
+ :minimum => 300,
+ :maximum => 400,
+ :tokenizer => lambda { |str| str.scan(/\w+/) },
+ :too_short => "must have at least {{count}} words",
+ :too_long => "must have at most {{count}} words"
end
</ruby>
The +validates_size_of+ helper is an alias for +validates_length_of+.
-h4. validates_numericality_of
+h4. +validates_numericality_of+
-This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by a integral or floating point number. Using the +:integer_only+ option set to true, you can specify that only integral numbers are allowed.
+This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by an integral or floating point number. To specify that only integral numbers are allowed set +:only_integer+ to true.
-If you set +:integer_only+ to +true+, then it will use the +$$/\A[+\-]?\d+\Z/+ regular expression to validate the attribute's value. Otherwise, it will try to convert the value to a number using +Kernel.Float+.
+If you set +:only_integer+ to +true+, then it will use the
+
+<ruby>
+/\A[+-]?\d+\Z/
+</ruby>
+
+regular expression to validate the attribute's value. Otherwise, it will try to convert the value to a number using +Float+.
+
+WARNING. Note that the regular expression above allows a trailing newline character.
<ruby>
class Player < ActiveRecord::Base
@@ -309,19 +337,19 @@ end
Besides +:only_integer+, the +validates_numericality_of+ helper also accepts the following options to add constraints to acceptable values:
-* +:greater_than+ - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than (value)_"
-* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal the supplied value. The default error message for this option is "_must be greater than or equal to (value)_"
-* +:equal_to+ - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to (value)_"
-* +:less_than+ - Specifies the value must be less than the supplied value. The default error message for this option is "_must e less than (value)_"
-* +:less_than_or_equal_to+ - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less or equal to (value)_"
-* +:odd+ - Specifies the value must be an odd number if set to true. The default error message for this option is "_must be odd_"
-* +:even+ - Specifies the value must be an even number if set to true. The default error message for this option is "_must be even_"
+* +:greater_than+ - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than {{count}}_".
+* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}".
+* +:equal_to+ - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to {{count}}_".
+* +:less_than+ - Specifies the value must be less than the supplied value. The default error message for this option is "_must be less than {{count}}_".
+* +:less_than_or_equal_to+ - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less or equal to {{count}}_".
+* +:odd+ - Specifies the value must be an odd number if set to true. The default error message for this option is "_must be odd_".
+* +:even+ - Specifies the value must be an even number if set to true. The default error message for this option is "_must be even_".
The default error message for +validates_numericality_of+ is "_is not a number_".
-h4. validates_presence_of
+h4. +validates_presence_of+
-This helper validates that the specified attributes are not empty. It uses the +blank?+ method to check if the value is either +nil+ or an empty string (if the string has only spaces, it will still be considered empty).
+This helper validates that the specified attributes are not empty. It uses the +blank?+ method to check if the value is either +nil+ or a blank string, that is, a string that is either empty or consists of whitespace.
<ruby>
class Person < ActiveRecord::Base
@@ -338,13 +366,13 @@ class LineItem < ActiveRecord::Base
end
</ruby>
-If you want to validate the presence of a boolean field (where the real values are true and false), you should use +validates_inclusion_of :field_name, :in => [true, false]+. This is due to the way that +Object#blank?+ handles boolean values (+false.blank? # => true+).
+Since +false.blank?+ is true, if you want to validate the presence of a boolean field you should use +validates_inclusion_of :field_name, :in => [true, false]+.
The default error message for +validates_presence_of+ is "_can't be empty_".
-h4. validates_uniqueness_of
+h4. +validates_uniqueness_of+
-This helper validates that the attribute's value is unique right before the object gets saved. It does not create a uniqueness constraint directly into your database, so it may happen that two different database connections create two records with the same value for a column that you intend to be unique. To avoid that, you must create an unique index in your database.
+This helper validates that the attribute's value is unique right before the object gets saved. It does not create a uniqueness constraint in the database, so it may happen that two different database connections create two records with the same value for a column that you intend to be unique. To avoid that, you must create an unique index in your database.
<ruby>
class Account < ActiveRecord::Base
@@ -352,14 +380,14 @@ class Account < ActiveRecord::Base
end
</ruby>
-The validation happens by performing a SQL query into the model's table, searching for a record where the attribute that must be validated is equal to the value in the object being validated.
+The validation happens by performing a SQL query into the model's table, searching for an existing record with the same value in that attribute.
There is a +:scope+ option that you can use to specify other attributes that are used to limit the uniqueness check:
<ruby>
class Holiday < ActiveRecord::Base
validates_uniqueness_of :name, :scope => :year,
- :message => "Should happen once per year"
+ :message => "should happen once per year"
end
</ruby>
@@ -371,16 +399,18 @@ class Person < ActiveRecord::Base
end
</ruby>
+WARNING. Note that some databases are configured to perform case-insensitive searches anyway.
+
The default error message for +validates_uniqueness_of+ is "_has already been taken_".
-h4. validates_each
+h4. +validates_each+
This helper validates attributes against a block. It doesn't have a predefined validation function. You should create one using a block, and every attribute passed to +validates_each+ will be tested against it. In the following example, we don't want names and surnames to begin with lower case.
<ruby>
class Person < ActiveRecord::Base
validates_each :name, :surname do |model, attr, value|
- model.errors.add(attr, 'Must start with upper case') if value =~ /^[a-z]/
+ model.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/
end
end
</ruby>
@@ -389,22 +419,22 @@ The block receives the model, the attribute's name and the attribute's value. Yo
h3. Common Validation Options
-There are some common options that all the validation helpers can use. Here they are, except for the +:if+ and +:unless+ options, which are discussed later in the conditional validation topic.
+There are some common options that all the validation helpers can use. Here they are, except for the +:if+ and +:unless+ options, which are discussed later in "Conditional Validation":#conditional-validation.
-h4. :allow_nil
+h4. +:allow_nil+
-The +:allow_nil+ option skips the validation when the value being validated is +nil+. You may be asking yourself if it makes any sense to use +:allow_nil+ and +validates_presence_of+ together. Well, it does. Remember, the validation will be skipped only for +nil+ attributes, but empty strings are not considered +nil+.
+The +:allow_nil+ option skips the validation when the value being validated is +nil+. Using +:allow_nil+ with +validates_presence_of+ allows for +nil+, but any other +blank?+ value will still be rejected.
<ruby>
class Coffee < ActiveRecord::Base
validates_inclusion_of :size, :in => %w(small medium large),
- :message => "%s is not a valid size", :allow_nil => true
+ :message => "{{value}} is not a valid size", :allow_nil => true
end
</ruby>
-h4. :allow_blank
+h4. +:allow_blank+
-The +:allow_blank+ option is similar to the +:allow_nil+ option. This option will let validation pass if the attribute's value is +nil+ or an empty string, i.e., any value that returns +true+ for +blank?+.
+The +:allow_blank+ option is similar to the +:allow_nil+ option. This option will let validation pass if the attribute's value is +blank?+, like +nil+ or an empty string for example.
<ruby>
class Topic < ActiveRecord::Base
@@ -415,32 +445,32 @@ Topic.create("title" => "").valid? # => true
Topic.create("title" => nil).valid? # => true
</ruby>
-h4. :message
+h4. +:message+
-As you've already seen, the +:message+ option lets you specify the message that will be added to the +errors+ collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper, together with the attribute name.
+As you've already seen, the +:message+ option lets you specify the message that will be added to the +errors+ collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper.
-h4. :on
+h4. +:on+
The +:on+ option lets you specify when the validation should happen. The default behavior for all the built-in validation helpers is to be ran on save (both when you're creating a new record and when you're updating it). If you want to change it, you can use +:on => :create+ to run the validation only when a new record is created or +:on => :update+ to run the validation only when a record is updated.
<ruby>
class Person < ActiveRecord::Base
- # => it will be possible to update email with a duplicated value
+ # it will be possible to update email with a duplicated value
validates_uniqueness_of :email, :on => :create
- # => it will be possible to create the record with a 'non-numerical age'
+ # it will be possible to create the record with a non-numerical age
validates_numericality_of :age, :on => :update
- # => the default (validates on both create and update)
+ # the default (validates on both create and update)
validates_presence_of :name, :on => :save
end
</ruby>
h3. Conditional Validation
-Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a Ruby Proc. You may use the +:if+ option when you want to specify when the validation *should* happen. If you want to specify when the validation *should not* happen, then you may use the +:unless+ option.
+Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a +Proc+. You may use the +:if+ option when you want to specify when the validation *should* happen. If you want to specify when the validation *should not* happen, then you may use the +:unless+ option.
-h4. Using a Symbol with :if and :unless
+h4. Using a Symbol with +:if+ and +:unless+
You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.
@@ -454,9 +484,9 @@ class Order < ActiveRecord::Base
end
</ruby>
-h4. Using a String with :if and :unless
+h4. Using a String with +:if+ and +:unless+
-You can also use a string that will be evaluated using +:eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
+You can also use a string that will be evaluated using +eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
<ruby>
class Person < ActiveRecord::Base
@@ -464,9 +494,9 @@ class Person < ActiveRecord::Base
end
</ruby>
-h4. Using a Proc with :if and :unless
+h4. Using a Proc with +:if+ and +:unless+
-Finally, it's possible to associate +:if+ and +:unless+ with a Ruby Proc object which will be called. Using a Proc object can give you the ability to write a condition that will be executed only when the validation happens and not when your code is loaded by the Ruby interpreter. This option is best suited when writing short validation methods, usually one-liners.
+Finally, it's possible to associate +:if+ and +:unless+ with a +Proc+ object which will be called. Using a +Proc+ object gives you the ability to write an inline condition instead of a separate method. This option is best suited for one-liners.
<ruby>
class Account < ActiveRecord::Base
@@ -481,12 +511,12 @@ When the built-in validation helpers are not enough for your needs, you can writ
Simply create methods that verify the state of your models and add messages to the +errors+ collection when they are invalid. You must then register these methods by using one or more of the +validate+, +validate_on_create+ or +validate_on_update+ class methods, passing in the symbols for the validation methods' names.
-You can pass more than one symbol for each class method and the respective validations will be ran in the same order as they were registered.
+You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.
<ruby>
class Invoice < ActiveRecord::Base
validate :expiration_date_cannot_be_in_the_past,
- :discount_cannot_be_more_than_total_value
+ :discount_cannot_be_greater_than_total_value
def expiration_date_cannot_be_in_the_past
errors.add(:expiration_date, "can't be in the past") if
@@ -494,8 +524,8 @@ class Invoice < ActiveRecord::Base
end
def discount_cannot_be_greater_than_total_value
- errors.add(:discount, "can't be greater than total value") unless
- discount <= total_value
+ errors.add(:discount, "can't be greater than total value") if
+ discount > total_value
end
end
</ruby>
@@ -503,25 +533,18 @@ end
You can even create your own validation helpers and reuse them in several different models. Here is an example where we create a custom validation helper to validate the format of fields that represent email addresses:
<ruby>
-module ActiveRecord
- module Validations
- module ClassMethods
- def validates_email_format_of(value)
- validates_format_of value,
- :with => /\A[\w\._%-]+@[\w\.-]+\.[a-zA-Z]{2,4}\z/,
- :if => Proc.new { |u| !u.email.blank? },
- :message => "Invalid format for email address"
- end
- end
+ActiveRecord::Base.class_eval do
+ def self.validates_as_radio(attr_name, n, options={})
+ validates_inclusion_of attr_name, {:in => 1..n}.merge(options)
end
end
</ruby>
-Simply create a new validation method inside the +ActiveRecord::Validations::ClassMethods+ module. You can put this code in a file inside your application's *lib* folder, and then requiring it from your *environment.rb* or any other file inside *config/initializers*. You can use this helper like this:
+Simply reopen +ActiveRecord::Base+ and define a class method like that. You'd typically put this code somewhere in +config/initializers+. You can use this helper like this:
<ruby>
-class Person < ActiveRecord::Base
- validates_email_format_of :email_address
+class Movie < ActiveRecord::Base
+ validates_as_radio :rating, 5
end
</ruby>
@@ -529,11 +552,11 @@ h3. Working with Validation Errors
In addition to the +valid?+ and +invalid?+ methods covered earlier, Rails provides a number of methods for working with the +errors+ collection and inquiring about the validity of objects.
-The following is a list of the most commonly used methods. Please refer to the ActiveRecord::Errors documentation for an exhaustive list that covers all of the available methods.
+The following is a list of the most commonly used methods. Please refer to the +ActiveRecord::Errors+ documentation for a list of all the available methods.
-h4. errors.add_to_base
+h4. +errors.add_to_base+
-+add_to_base+ lets you add errors messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of it's attributes. +add_to_base+ simply receives a string and uses this as the error message.
++add_to_base+ lets you add errors messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. +add_to_base+ simply receives a string and uses this as the error message.
<ruby>
class Person < ActiveRecord::Base
@@ -543,29 +566,29 @@ class Person < ActiveRecord::Base
end
</ruby>
-h4. errors.add
+h4. +errors.add+
-+add+ lets you manually add messages that are related to particular attributes. Note that Rails will prepend the name of the attribute to the error message you pass it. You can use the +full_messages+ method to view the messages in the form they might be displayed to a user. +add+ receives a symbol with the name of the attribute that you want to add the message to, and the message itself.
++add+ lets you manually add messages that are related to particular attributes. You can use the +full_messages+ method to view the messages in the form they might be displayed to a user. Those particular messages get the attribute name prepended (and capitalized). +add+ receives the name of the attribute you want to add the message to, and the message itself.
<ruby>
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
- errors.add(:name, "cannot contain the characters !@#$%*()_-+=")
+ errors.add(:name, "cannot contain the characters !@#%*()_-+=")
end
end
-person = Person.create(:name => "!@#$")
+person = Person.create(:name => "!@#")
person.errors.on(:name)
-# => "is too short (minimum is 3 characters)"
+# => "cannot contain the characters !@#%*()_-+="
person.errors.full_messages
-# => ["Name is too short (minimum is 3 characters)"]
+# => ["Name cannot contain the characters !@#%*()_-+="]
</ruby>
-h4. errors.on
+h4. +errors.on+
-+on+ is used when you want to check the error messages for a specific attribute. It will return different kinds of objects depending on the state of the +errors+ collection for the given attribute. If there are no errors related to the attribute, +on+ will return +nil+. If there is just one errors message for this attribute, +on+ will return a string with the message. When +errors+ holds two or more error messages for the attribute, +on+ will return an array of strings, each one with one error message.
++on+ is used when you want to check the error messages for a specific attribute. It returns different kinds of objects depending on the state of the +errors+ collection for the given attribute. If there are no errors related to the attribute +on+ returns +nil+. If there is just one error message for this attribute +on+ returns a string with the message. When +errors+ holds two or more error messages for the attribute, +on+ returns an array of strings, each one with one error message.
<ruby>
class Person < ActiveRecord::Base
@@ -588,7 +611,7 @@ person.errors.on(:name)
# => ["can't be blank", "is too short (minimum is 3 characters)"]
</ruby>
-h4. errors.clear
+h4. +errors.clear+
+clear+ is used when you intentionally want to clear all the messages in the +errors+ collection. Of course, calling +errors.clear+ upon an invalid object won't actually make it valid: the +errors+ collection will now be empty, but the next time you call +valid?+ or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the +errors+ collection will be filled again.
@@ -612,28 +635,33 @@ p.errors.on(:name)
# => ["can't be blank", "is too short (minimum is 3 characters)"]
</ruby>
-h4. errors.size
+h4. +errors.size+
-+size+ returns the total number of errors added. Two errors added to the same object will be counted as such.
++size+ returns the total number of error messages for the object.
<ruby>
class Person < ActiveRecord::Base
validates_presence_of :name
- validates_length_of :name, :minimum => 3
+ validates_length_of :name, :minimum => 3
+ validates_presence_of :email
end
person = Person.new
person.valid? # => false
-person.errors.size # => 2
+person.errors.size # => 3
+
+person = Person.new(:name => "Andrea", :email => "andrea@example.com")
+person.valid? # => true
+person.errors.size # => 0
</ruby>
h3. Displaying Validation Errors in the View
Rails provides built-in helpers to display the error messages of your models in your view templates.
-h4. error_messages and error_messages_for
+h4. +error_messages+ and +error_messages_for+
-When creating a form with the form_for helper, you can use the error_messages method on the form builder to render all failed validation messages for the current model instance.
+When creating a form with the +form_for+ helper, you can use the +error_messages+ method on the form builder to render all failed validation messages for the current model instance.
<ruby>
class Product < ActiveRecord::Base
@@ -659,6 +687,8 @@ end
<% end %>
</erb>
+To get the idea, if you submit the form with empty fields you typically get this back, though styles are indeed missing by default:
+
!images/error_messages.png(Error messages)!
You can also use the +error_messages_for+ helper to display the error messages of a model assigned to a view template. It's very similar to the previous example and will achieve exactly the same result.
@@ -685,53 +715,52 @@ If you pass +nil+ to any of these options, it will get rid of the respective sec
h4. Customizing the Error Messages CSS
-It's also possible to change the CSS classes used by the +error_messages+ helper. These classes are automatically defined at the *scaffold.css* file, generated by the scaffold script. If you're not using scaffolding, you can still define those CSS classes at your CSS files. Here is a list of the default CSS classes.
+The selectors to customize the style of error messages are:
-* +.fieldWithErrors+ - Style for the form fields with errors.
+* +.fieldWithErrors+ - Style for the form fields and labels with errors.
* +#errorExplanation+ - Style for the +div+ element with the error messages.
* +#errorExplanation h2+ - Style for the header of the +div+ element.
* +#errorExplanation p+ - Style for the paragraph that holds the message that appears right below the header of the +div+ element.
-* +#errorExplanation ul li+ - Style for the list of error messages.
+* +#errorExplanation ul li+ - Style for the list items with individual error messages.
+
+Scaffolding for example generates +public/stylesheets/scaffold.css+, which defines the red-based style you saw above.
+
+The name of the class and the id can be changed with the +:class+ and +:id+ options, accepted by both helpers.
h4. Customizing the Error Messages HTML
-By default, form fields with errors are displayed enclosed by a +div+ element with the +fieldWithErrors+ CSS class. However, it's possible to override the way Rails treats those fields by default.
+By default, form fields with errors are displayed enclosed by a +div+ element with the +fieldWithErrors+ CSS class. However, it's possible to override that.
+
+The way form fields with errors are treated is defined by +ActionView::Base.field_error_proc+. This is a +Proc+ that receives two parameters:
+
+* A string with the HTML tag
+* An instance of +ActionView::Helpers::InstanceTag+.
Here is a simple example where we change the Rails behaviour to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a +span+ element with a +validation-error+ CSS class. There will be no +div+ element enclosing the +input+ element, so we get rid of that red border around the text field. You can use the +validation-error+ CSS class to style it anyway you want.
<ruby>
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
if instance.error_message.kind_of?(Array)
- %(#{html_tag}<span class='validation-error'>&nbsp;
+ %(#{html_tag}<span class="validation-error">&nbsp;
#{instance.error_message.join(',')}</span>)
else
- %(#{html_tag}<span class='validation-error'>&nbsp;
+ %(#{html_tag}<span class="validation-error">&nbsp;
#{instance.error_message}</span>)
end
end
</ruby>
-This will result in something like the following content:
+This will result in something like the following:
!images/validation_error_messages.png(Validation error messages)!
-The way form fields with errors are treated is defined by the +ActionView::Base.field_error_proc+ Ruby Proc. This Proc receives two parameters:
-
-* A string with the HTML tag
-* An object of the +ActionView::Helpers::InstanceTag+ class.
-
h3. Callbacks Overview
-Callbacks are methods that get called at certain moments of an object's lifecycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted or loaded from the database.
-
-# TODO discuss what does/doesn't trigger callbacks, like we did in the validations section (e.g. destroy versus delete).
-# Consider moving up to the (new) intro overview section, before getting into details.
-# http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002220
-# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
+Callbacks are methods that get called at certain moments of an object's lifecycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database.
h4. Callback Registration
-In order to use the available callbacks, you need to register them. You can do that by implementing them as an ordinary methods, and then using a macro-style class method to register then as callbacks.
+In order to use the available callbacks, you need to register them. You can do that by implementing them as ordinary methods, and then using a macro-style class method to register them as callbacks.
<ruby>
class User < ActiveRecord::Base
@@ -741,7 +770,7 @@ class User < ActiveRecord::Base
protected
def ensure_login_has_a_value
- if self.login.nil?
+ if login.nil?
self.login = email unless email.blank?
end
end
@@ -760,13 +789,159 @@ end
It's considered good practice to declare callback methods as being protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation.
+h3. Available Callbacks
+
+Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations:
+
+h4. Creating an Object
+
+* +before_validation+
+* +before_validation_on_create+
+* +after_validation+
+* +after_validation_on_create+
+* +before_save+
+* +before_create+
+* INSERT OPERATION
+* +after_create+
+* +after_save+
+
+h4. Updating an Object
+
+* +before_validation+
+* +before_validation_on_update+
+* +after_validation+
+* +after_validation_on_update+
+* +before_save+
+* +before_update+
+* UPDATE OPERATION
+* +after_update+
+* +after_save+
+
+h4. Destroying an Object
+
+* +before_destroy+
+* DELETE OPERATION
+* +after_destroy+
+
+WARNING. +after_save+ runs both on create and update, but always _after_ the more specific callbacks +after_create+ and +after_update+, no matter the order in which the macro calls were executed.
+
+h4. +after_initialize+ and +after_find+
+
+The +after_initialize+ callback will be called whenever an Active Record object is instantiated, either by directly using +new+ or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record +initialize+ method.
+
+The +after_find+ callback will be called whenever Active Record loads a record from the database. +after_find+ is called before +after_initialize+ if both are defined.
+
+The +after_initialize+ and +after_find+ callbacks are a bit different from the others. They have no +before_*+ counterparts, and the only way to register them is by defining them as regular methods. If you try to register +after_initialize+ or +after_find+ using macro-style class methods, they will just be ignored. This behaviour is due to performance reasons, since +after_initialize+ and +after_find+ will both be called for each record found in the database, significantly slowing down the queries.
+
+<ruby>
+class User < ActiveRecord::Base
+ def after_initialize
+ puts "You have initialized an object!"
+ end
+
+ def after_find
+ puts "You have found an object!"
+ end
+end
+
+>> User.new
+You have initialized an object!
+=> #<User id: nil>
+
+>> User.first
+You have found an object!
+You have initialized an object!
+=> #<User id: 1>
+</ruby>
+
+h3. Running Callbacks
+
+The following methods trigger callbacks:
+
+* +create+
+* +create!+
+* +decrement!+
+* +destroy+
+* +destroy_all+
+* +increment!+
+* +save+
+* +save!+
+* +save(false)+
+* +toggle!+
+* +update+
+* +update_attribute+
+* +update_attributes+
+* +update_attributes!+
+* +valid?+
+
+Additionally, the +after_find+ callback is triggered by the following finder methods:
+
+* +all+
+* +first+
+* +find+
+* +find_all_by_<em>attribute</em>+
+* +find_by_<em>attribute</em>+
+* +find_by_<em>attribute</em>!+
+* +last+
+
+The +after_initialize+ callback is triggered every time a new object of the class is initialized.
+
+h3. Skipping Callbacks
+
+Just as with validations, it's also possible to skip callbacks. These methods should be used with caution, however, because important business rules and application logic may be kept in callbacks. Bypassing them without understanding the potential implications may lead to invalid data.
+
+* +decrement+
+* +decrement_counter+
+* +delete+
+* +delete_all+
+* +find_by_sql+
+* +increment+
+* +increment_counter+
+* +toggle+
+* +update_all+
+* +update_counters+
+
+h3. Halting Execution
+
+As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed.
+
+The whole callback chain is wrapped in a transaction. If any before callback method returns exactly +false+ or raises an exception the execution chain gets halted and a ROLLBACK is issued. After callbacks can only accomplish that by raising an exception.
+
+WARNING. Raising an arbitrary exception may break code that expects +save+ and friends not to fail like that. The +ActiveRecord::Rollback+ exception is thought precisely to tell Active Record a rollback is going on. That one is internally captured but not reraised.
+
+h3. Relational Callbacks
+
+Callbacks work through model relationships, and can even be defined by them. Let's take an example where a user has many posts. In our example, a user's posts should be destroyed if the user is destroyed. So, we'll add an +after_destroy+ callback to the +User+ model by way of its relationship to the +Post+ model.
+
+<ruby>
+class User < ActiveRecord::Base
+ has_many :posts, :dependent => :destroy
+end
+
+class Post < ActiveRecord::Base
+ after_destroy :log_destroy_action
+
+ def log_destroy_action
+ puts 'Post destroyed'
+ end
+end
+
+>> user = User.first
+=> #<User id: 1>
+>> user.posts.create!
+=> #<Post id: 1, user_id: 1>
+>> user.destroy
+Post destroyed
+=> #<User id: 1>
+</ruby>
+
h3. Conditional Callbacks
-Like in validations, we can also make our callbacks conditional, calling then only when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a Ruby Proc. You may use the +:if+ option when you want to specify when the callback *should* get called. If you want to specify when the callback *should not* be called, then you may use the +:unless+ option.
+Like in validations, we can also make our callbacks conditional, calling them only when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a +Proc+. You may use the +:if+ option when you want to specify when the callback *should* get called. If you want to specify when the callback *should not* be called, then you may use the +:unless+ option.
-h4. Using :if and :unless with a Symbol
+h4. Using +:if+ and +:unless+ with a Symbol
-You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. If this method returns +false+ the callback won't be executed. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check the if the callback should be executed.
+You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. If this method returns +false+ the callback won't be executed. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check if the callback should be executed.
<ruby>
class Order < ActiveRecord::Base
@@ -774,9 +949,9 @@ class Order < ActiveRecord::Base
end
</ruby>
-h4. Using :if and :unless with a String
+h4. Using +:if+ and +:unless+ with a String
-You can also use a string that will be evaluated using +:eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
+You can also use a string that will be evaluated using +eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
<ruby>
class Order < ActiveRecord::Base
@@ -784,9 +959,9 @@ class Order < ActiveRecord::Base
end
</ruby>
-h4. Using :if and :unless with a Proc
+h4. Using +:if+ and +:unless+ with a Proc
-Finally, it's possible to associate +:if+ and +:unless+ with a Ruby Proc object. This option is best suited when writing short validation methods, usually one-liners.
+Finally, it's possible to associate +:if+ and +:unless+ with a +Proc+ object. This option is best suited when writing short validation methods, usually one-liners.
<ruby>
class Order < ActiveRecord::Base
@@ -806,59 +981,11 @@ class Comment < ActiveRecord::Base
end
</ruby>
-h3. Available Callbacks
-
-Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations.
-
-h4. Creating and/or Updating an Object
-
-* +before_validation+
-* +after_validation+
-* +before_save+
-* INSERT OR UPDATE OPERATION
-* +after_save+
-
-h4. Creating an Object
-
-* +before_validation_on_create+
-* +after_validation_on_create+
-* +before_create+
-* INSERT OPERATION
-* +after_create+
-
-h4. Updating an Object
-
-* +before_validation_on_update+
-* +after_validation_on_update+
-* +before_update+
-* UPDATE OPERATION
-* +after_update+
-
-h4. Destroying an Object
-
-* +before_destroy+
-* DELETE OPERATION
-* +after_destroy+
-
-CAUTION: The +before_destroy+ and +after_destroy+ callbacks will only be called if you delete the model using either the +destroy+ instance method or one of the +destroy+ or +destroy_all+ class methods of your Active Record class. If you use +delete+ or +delete_all+ no callback operations will run, since Active Record will not instantiate any objects, accessing the records to be deleted directly in the database.
-
-h4. after_initialize and after_find
-
-The +after_initialize+ callback will be called whenever an Active Record object is instantiated, either by directly using +new+ or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record +initialize+ method.
-
-The +after_find+ callback will be called whenever Active Record loads a record from the database. When used together with +after_initialize+ it will run first, since Active Record will first read the record from the database and them create the model object that will hold it.
-
-The +after_initialize+ and +after_find+ callbacks are a bit different from the others, since the only way to register those callbacks is by defining them as methods. If you try to register +after_initialize+ or +after_find+ using macro-style class methods, they will just be ignored. This behaviour is due to performance reasons, since +after_initialize+ and +after_find+ will both be called for each record found in the database, significantly slowing down the queries.
-
-h3. Halting Execution
-
-As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the +before_create+, +before_save+, +before_update+ or +before_destroy+ callback methods returns a boolean +false+ (not +nil+) value or raise and exception, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on. It's because the whole callback chain is wrapped in a transaction, so raising an exception or returning +false+ fires a database ROLLBACK.
-
h3. Callback Classes
-Sometimes the callback methods that you'll write will be useful enough to be reused at other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them.
+Sometimes the callback methods that you'll write will be useful enough to be reused by other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them.
-Here's an example where we create a class with a after_destroy callback for a PictureFile model.
+Here's an example where we create a class with an +after_destroy+ callback for a +PictureFile+ model.
<ruby>
class PictureFileCallbacks
@@ -876,7 +1003,7 @@ class PictureFile < ActiveRecord::Base
end
</ruby>
-Note that we needed to instantiate a new PictureFileCallbacks object, since we declared our callback as an instance method. Sometimes it will make more sense to have it as a class method.
+Note that we needed to instantiate a new +PictureFileCallbacks+ object, since we declared our callback as an instance method. Sometimes it will make more sense to have it as a class method.
<ruby>
class PictureFileCallbacks
@@ -886,7 +1013,7 @@ class PictureFileCallbacks
end
</ruby>
-If the callback method is declared this way, it won't be necessary to instantiate a PictureFileCallbacks object.
+If the callback method is declared this way, it won't be necessary to instantiate a +PictureFileCallbacks+ object.
<ruby>
class PictureFile < ActiveRecord::Base
@@ -898,7 +1025,7 @@ You can declare as many callbacks as you want inside your callback classes.
h3. Observers
-Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn't directly related to its purpose, observers allow you to add functionality outside of a model. For example, it could be argued that a +User+ model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn't directly related to your model, you may want to consider creating an observer instead.
+Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn't directly related to its purpose, observers allow you to add the same functionality outside of a model. For example, it could be argued that a +User+ model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn't directly related to your model, you may want to consider creating an observer instead.
h4. Creating observers
@@ -916,18 +1043,18 @@ As with callback classes, the observer's methods receive the observed model as a
h4. Registering Observers
-Observers should be placed inside of your *app/models* directory and registered in your application's *config/environment.rb* file. For example, the +UserObserver+ above would be saved as *app/models/user_observer.rb* and registered in *config/environment.rb*.
+Observers are conventionally placed inside of your +app/models+ directory and registered in your application's +config/environment.rb+ file. For example, the +UserObserver+ above would be saved as +app/models/user_observer.rb+ and registered in +config/environment.rb+ this way:
<ruby>
# Activate observers that should always be running
config.active_record.observers = :user_observer
</ruby>
-As usual, settings in *config/environments/* take precedence over those in *config/environment.rb*. So, if you prefer that an observer not run in all environments, you can simply register it in a specific environment instead.
+As usual, settings in +config/environments+ take precedence over those in +config/environment.rb+. So, if you prefer that an observer not run in all environments, you can simply register it in a specific environment instead.
h4. Sharing Observers
-By default, Rails will simply strip 'observer' from an observer's name to find the model it should observe. However, observers can also be used to add behaviour to more than one model, and so it's possible to manually specify the models that our observer should observe.
+By default, Rails will simply strip "Observer" from an observer's name to find the model it should observe. However, observers can also be used to add behaviour to more than one model, and so it's possible to manually specify the models that our observer should observe.
<ruby>
class MailerObserver < ActiveRecord::Observer
@@ -939,7 +1066,7 @@ class MailerObserver < ActiveRecord::Observer
end
</ruby>
-In this example, the +after_create+ method would be called whenever a +Registration+ or +User+ was created. Note that this new +MailerObserver+ would also need to be registered in *config/environment.rb* in order to take effect.
+In this example, the +after_create+ method would be called whenever a +Registration+ or +User+ was created. Note that this new +MailerObserver+ would also need to be registered in +config/environment.rb+ in order to take effect.
<ruby>
# Activate observers that should always be running
@@ -950,6 +1077,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks
+* March 7, 2009: Callbacks revision by Trevor Turk
* February 10, 2009: Observers revision by Trevor Turk
* February 5, 2009: Initial revision by Trevor Turk
* January 9, 2009: Initial version by "Cássio Marques":credits.html#cmarques
diff --git a/railties/guides/source/contribute.textile b/railties/guides/source/contribute.textile
index 48f1a51d02..c8e3b9c202 100644
--- a/railties/guides/source/contribute.textile
+++ b/railties/guides/source/contribute.textile
@@ -44,26 +44,21 @@ For each completed guide, the lead contributor will receive all of the following
h3. Rules
-* Guides are licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License.
+* Guides are licensed under a Creative Commons Attribution-Share Alike 3.0 License.
* If you're not sure whether a guide is actively being worked on, stop by IRC and ask.
* If the same guide writer wants to write multiple guides, that's ideally the situation we'd love to be in! However, that guide writer will only receive the cash prize for all the subsequent guides (and not the GitHub or RPM prizes).
* Our review team will have the final say on whether the guide is complete and of good enough quality.
-h3. Reviewers
-
-These are the main reviewers and editors for the guides:
-
-* Hongli Lai
-* Mike Gunderloy
-* Pratik Naik
-* Xavier Noria
-
All authors should read and follow the "Rails Guides Conventions":http://wiki.github.com/lifo/docrails/rails-guides-conventions and the "Rails API Documentation Conventions":http://wiki.github.com/lifo/docrails/rails-api-documentation-conventions.
h3. Translations
The translation effort for the Rails Guides is just getting underway. We know about projects to translate the Guides into Spanish, Portuguese, Polish, and French. For more details or to get involved see the "Translating Rails Guides":http://wiki.github.com/lifo/docrails/translating-rails-guides page.
+h3. Mailing List
+
+"Ruby on Rails: Documentation":http://groups.google.com/group/rubyonrails-docs is the mailing list for all the guides/documentation related discussions.
+
h3. IRC Channel
==#docrails @ irc.freenode.net==
@@ -72,6 +67,5 @@ h3. Contact
If you have any questions or need any clarification, feel free to contact:
-* IRC : lifo, mikeg1a, fxn, or FooBarWidget in #docrails
+* IRC : lifo, mikeg1a or fxn in #docrails
* Email : pratiknaik aT gmail
-
diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
new file mode 100644
index 0000000000..84778ed9ee
--- /dev/null
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -0,0 +1,239 @@
+h2. Contributing to Rails
+
+This guide covers ways in which _you_ can become a part of the ongoing development of Rails. After reading it, you should be familiar with:
+
+* Using Lighthouse to report issues with Rails
+* Cloning edge Rails and running the test suite
+* Helping to resolve existing issues
+* Contributing to the Rails documentation
+* Contributing to the Rails code
+
+Rails is not "someone else's framework." Over the years, hundreds of people have contributed code ranging from a single character to massive architectural changes, all with the goal of making Rails better for everyone. Even if you don't feel up to writing code yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches to contributing documentation.
+
+endprologue.
+
+h3. Reporting a Rails Issue
+
+Rails uses a "Lighthouse project":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/ to track issues (primarily bugs and contributions of new code). If you've found a bug in Rails, this is the place to start.
+
+NOTE: Bugs in the most recent released version of Rails are likely to get the most attention. Also, the Rails core team is always interested in feedback from those who can take the time to test _edge Rails_ (the code for the version of Rails that is currently under development). Later in this Guide you'll find out how to get edge Rails for testing.
+
+h4. Creating a Bug Report
+
+If you've found a problem in Rails, you can start by "adding a new ticket":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/new to the Rails Lighthouse. At the minimum, your ticket needs a title and descriptive text. But that's only a minimum. You should include as much relevant information as possible. You need to at least post the code sample that has the issue. Even better is to include a unit test that shows how the expected behavior is not occurring. Your goal should be to make it easy for yourself - and others - to replicate the bug and figure out a fix.
+
+You shouldn't assign the bug to a particular core developer (through the *Who's Responsible* select list) unless you know for sure which developer will be handling any patch. The core team periodically reviews issues and assigns developers and milestones to them.
+
+You should set tags for your issue. Use the "bug" tag for a bug report, and add the "patch" tag if you are attaching a patch. Try to find some relevant tags from the existing tag list (which will appear as soon as you start typing in the *Choose some tags* textbox), rather than creating new tags.
+
+Then don't get your hopes up. Unless you have a "Code Red, Mission Critical, The World is Coming to an End" kind of bug, you're creating this ticket in the hope that others with the same problem will be able to collaborate with you on solving it. Do not expect that the ticket automatically will see any activity or that others will jump to fix it. Creating a ticket like this is mostly to help yourself start on the path of fixing the problem and for others to confirm it with a "I'm having this problem too" comment.
+
+h4. Special Treatment for Security Issues
+
+If you've found a security vulnerability in Rails, please do *not* report it via a Lighthouse ticket. Lighthouse tickets are public as soon as they are entered. Instead, you should use the dedicated email address "security@rubyonrails.org":mailto:security@rubyonrails.org to report any vulnerabilities. This alias is monitored and the core team will work with you to quickly and completely address any such vulnerabilities.
+
+h4. What About Feature Requests?
+
+Please don't put "feature request" tickets into Lighthouse. If there's a new feature that you want to see added to Rails, you'll need to write the code yourself - or convince someone else to partner with you to write the code. Later in this guide you'll find detailed instructions for proposing a patch to Rails. If you enter a wishlist item in Lighthouse with no code, you can expect it to be marked "invalid" as soon as it's reviewed.
+
+h3. Running the Rails Test Suite
+
+To move on from submitting bugs to helping resolve existing issues or contributing your own code to Rails, you _must_ be able to run the Rails test suite. In this section of the guide you'll learn how to set up the tests on your own computer.
+
+h4. Install git
+
+Rails uses git for source code control. You won’t be able to do anything without the Rails source code, and this is a prerequisite. The "git homepage":http://git-scm.com/ has installation instructions. If you’re on OS X, use the "Git for OS X":http://code.google.com/p/git-osx-installer/ installer. If you're unfamiliar with git, there are a variety of resources on the net that will help you learn more:
+
+* "Everyday Git":http://www.kernel.org/pub/software/scm/git/docs/everyday.html will teach you just enough about git to get by.
+* The "PeepCode screencast":https://peepcode.com/products/git on git ($9) is easier to follow.
+* "GitHub":http://github.com/guides/home offers links to a variety of git resources.
+
+h4. Get the Rails Source Code
+
+Don’t fork the main Rails repository. Instead, you want to clone it to your own computer. Navigate to the folder where you want the source code (it will create its own /rails subdirectory) and run:
+
+<shell>
+git clone git://github.com/rails/rails.git
+cd rails
+</shell>
+
+h4. Set up and Run the Tests
+
+All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. For the tests that touch the database, this means creating the databases. If you're using MySQL:
+
+<shell>
+mysql> create database activerecord_unittest;
+mysql> create database activerecord_unittest2;
+mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.*
+ to 'rails'@'localhost';
+mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.*
+ to 'rails'@'localhost';
+</shell>
+
+If you’re using another database, check the files under +activerecord/test/connections+ in the Rails source code for default connection information. You can edit these files if you _must_ on your machine to provide different credentials, but obviously you should not push any such changes back to Rails.
+
+Now if you go back to the root of the Rails source on your machine and run +rake+ with no parameters, you should see every test in all of the Rails components pass. If you want to run the all ActiveRecord tests (or just a single one) with another database adapter, enter this from the +activerecord+ directory:
+
+<shell>
+rake test_sqlite3
+rake test_sqlite3 TEST=test/cases/validations_test.rb
+</shell>
+
+You can change +sqlite3+ with +jdbcmysql+, +jdbcsqlite3+, +jdbcpostgresql+, +mysql+ or +postgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs.
+
+
+
+NOTE: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, SQLite 2, and SQLite 3. Subtle differences between the various Active Record database adapters have been behind the rejection of many patches that looked OK when tested only against MySQL.
+
+h3. Helping to Resolve Existing Issues
+
+As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "open tickets":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets?q=state%3Aopen list in Lighthouse, you'll find hundreds of issues already requiring attention. What can you do for these? Quite a bit, actually:
+
+h4. Verifying Bug Reports
+
+For starters, it helps to just verify bug reports. Can you reproduce the reported issue on your own computer? If so, you can add a comment to the ticket saying that you're seeing the same thing.
+
+If something is very vague, can you help squish it down into something specific? Maybe you can provide additional information to help reproduce a bug, or eliminate needless steps that aren't required to help demonstrate the problem.
+
+If you find a bug report without a test, it's very useful to contribute a failing test. This is also a great way to get started exploring the Rails source: looking at the existing test files will teach you how to write more tests for Rails. New tests are best contributed in the form of a patch, as explained later on in the "Contributing to the Rails Code" section.
+
+Anything you can do to make bug reports more succinct or easier to reproduce is a help to folks trying to write code to fix those bugs - whether you end up writing the code yourself or not.
+
+h4. Testing Patches
+
+You can also help out by examining patches that have been submitted to Rails via Lighthouse. To apply someone's changes you need to first create a branch of the Rails source code:
+
+<shell>
+git checkout -b testing_branch
+</shell>
+
+Then you can apply their patch:
+
+<shell>
+git am < their-patch-file.diff
+</shell>
+
+After applying a patch, test it out! Here are some things to think about:
+
+* Does the patch actually work?
+* Are you happy with the tests? Can you follow what they're testing? Are there any tests missing?
+* Does the documentation still seem right to you?
+* Do you like the implementation? Can you think of a nicer or faster way to implement a part of their change?
+
+Once you're happy that the patch contains a good change, comment on the Lighthouse ticket indicating your approval. Your comment should indicate that you like the change and what you like about it. Something like:
+
+<blockquote>
+I like the way you've restructured that code in generate_finder_sql, much nicer. The tests look good too.
+</blockquote>
+
+If your comment simply says "+1", then odds are that other reviewers aren't going to take it too seriously. Show that you took the time to review the patch. Once three people have approved it, add the "verified" tag. This will bring it to the attention of a core team member who will review the changes looking for the same kinds of things.
+
+h3. Contributing to the Rails Documentation
+
+Another area where you can help out if you're not yet ready to take the plunge to writing Rails core code is with Rails documentation. You can help with the Rails Guides or the Rails API documentation.
+
+TIP: "docrails":http://github.com/lifo/docrails/tree/master is the documentation branch for Rails with an *open commit policy*. You can simply PM "lifo":http://github.com/lifo on Github and ask for the commit rights. Documentation changes made as part of the "docrails":http://github.com/lifo/docrails/tree/master project, are merged back to the Rails master code from time to time. Check out the "original announcement":http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch for more details.
+
+h4. The Rails Guides
+
+The "Rails Guides":http://guides.rubyonrails.org/ are a set of online resources that are designed to make people productive with Rails and to understand how all of the pieces fit together. These guides (including this one!) are written as part of the "docrails":http://github.com/lifo/docrails/tree/master project. If you have an idea for a new guide, or improvements for an existing guide, you can refer to the "contribution page":contribute.html for instructions on getting involved.
+
+h4. The Rails API Documentation
+
+The "Rails API documentation":http://api.rubyonrails.org/ is automatically generated from the Rails source code via "RDoc":http://rdoc.rubyforge.org/. If you find some part of the documentation to be incomplete, confusing, or just plain wrong, you can step in and fix it.
+
+To contribute an update to the API documentation, you can contact "lifo":http://github.com/lifo on GitHub and ask for commit rights to the docrails repository and push your changes to the docrails repository. Please follow the "docrails RDoc conventions":http://wiki.github.com/lifo/docrails/rails-api-documentation-conventions when contributing the changes.
+
+h3. The Rails Wiki
+
+The "Rails wiki":http://wiki.rubyonrails.org/ is a collection of user-generated and freely-editable information about Rails. It covers everything from getting started to FAQs to how-tos and popular plugins. To contribute to the wiki, just find some useful information that isn't there already and add it. There are style guidelines to help keep the wiki a coherent resources; see the section on "contributing to the wiki":http://wiki.rubyonrails.org/#contributing_to_the_wiki for more details.
+
+h3. Contributing to the Rails Code
+
+When you're ready to take the plunge, one of the most helpful ways to contribute to Rails is to actually submit source code. Here's a step-by-step listing of the things you need to do to make this a successful experience.
+
+h4. Learn the Language and the Framework
+
+Learn at least _something_ about Ruby and Rails. If you don’t understand the syntax of the language, common Ruby idioms, and the code that already exists in Rails, you’re unlikely to be able to build a good patch (that is, one that will get accepted). You don’t have to know every in-and-out of the language and the framework; some of the Rails code is fiendishly complex. But Rails is probably not appropriate as the first place that you ever write Ruby code. You should at least understand (though not necessarily memorize) "The Ruby Programming Language":http://www.amazon.com/gp/product/0596516177?ie=UTF8&linkCode=as2&camp=1789&creative=390957&creativeASIN=0596516177 and have browsed the Rails source code.
+
+h4. Fork the Rails Source Code
+
+Fork Rails. You’re not going to put your patches right into the master branch, OK? This is where you need that copy of Rails that you cloned earlier. Think of a name for your new branch and run
+
+<shell>
+git checkout -b my_new_branch
+</shell>
+
+It doesn’t really matter what name you use, because this branch will only exist on your local computer.
+
+h4. Write Your Code
+
+Now get busy and add your code to Rails (or edit the existing code). You’re on your branch now, so you can write whatever you want (you can check to make sure you’re on the right branch with +git branch -a+). But if you’re planning to submit your change back for inclusion in Rails, keep a few things in mind:
+
+* Get the code right
+* Use Rails idioms and helpers
+* Include tests that fail without your code, and pass with it
+* Update the documentation
+
+h4. Sanity Check
+
+You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either.
+
+h4. Commit Your Changes
+
+When you're happy with the code on your computer, you need to commit the changes to git:
+
+<shell>
+git commit -a -m "Here is a commit message"
+</shell>
+
+h4. Update Rails
+
+Update your copy of Rails. It’s pretty likely that other changes to core Rails have happened while you were working. Go get them:
+
+<shell>
+git checkout master
+git pull
+</shell>
+
+Now reapply your patch on top of the latest changes:
+
+<shell>
+git checkout my_new_branch
+git rebase master
+</shell>
+
+No conflicts? Tests still pass? Change still seems reasonable to you? Then move on.
+
+h4. Create a Patch
+
+Now you can create a patch file to share with other developers (and with the Rails core team). Still in your branch, run
+
+<shell>
+git commit -a
+git format-patch master --stdout > my_new_patch.diff
+</shell>
+
+Sanity check the results of this operation: open the diff file in your text editor of choice and make sure that no unintended changes crept in.
+
+h4. Create a Lighthouse Ticket
+
+Now create a ticket with your patch. Go to the "new ticket":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/new page at Lighthouse. Fill in a reasonable title and description, remember to attach your patch file, and tag the ticket with the ‘patch’ tag and whatever other subject area tags make sense.
+
+h4. Get Some Feedback
+
+Now you need to get other people to look at your patch, just as you've looked at other people's patches. You can use the rubyonrails-core mailing list or the #rails-contrib channel on IRC freenode for this. You might also try just talking to Rails developers that you know.
+
+h4. Iterate as Necessary
+
+It’s entirely possible that the feedback you get will suggest changes. Don’t get discouraged: the whole point of contributing to an active open source project is to tap into community knowledge. If people are encouraging you to tweak your code, then it’s worth making the tweaks and resubmitting. If the feedback is that your code doesn’t belong in the core, you might still think about releasing it as a plugin.
+
+And then...think about your next contribution!
+
+h3. Changelog
+
+"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/64
+
+* March 2, 2009: Initial draft by "Mike Gunderloy":credits.html#mgunderloy
+
+
diff --git a/railties/guides/source/credits.erb.textile b/railties/guides/source/credits.erb.textile
index 441ea60ffe..2b1c02b9a9 100644
--- a/railties/guides/source/credits.erb.textile
+++ b/railties/guides/source/credits.erb.textile
@@ -5,6 +5,28 @@ p. We'd like to thank the following people for their tireless contributions to t
<% end %>
+<h3 class="section">Rails Documentation Team</h3>
+
+<% author('Mike Gunderloy', 'mgunderloy') do %>
+ Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails activism team":http://rubyonrails.org/activists . He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much.
+<% end %>
+
+<% author('Pratik Naik', 'lifo') do %>
+ Pratik Naik is a Ruby on Rails consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails core team":http://rubyonrails.com/core. He maintains a blog at "has_many :bugs, :through => :rails":http://m.onkey.org and has an active "twitter account":http://twitter.com/lifo.
+<% end %>
+
+<% author('Xavier Noria', 'fxn', 'fxn.jpg') do %>
+ Xavier Noria has been around dynamic languages since 2000. He fell in love with Rails in 2005, and cofounded Rails-based software company <a href="http://www.aspgems.com">ASPgems</a> in mid-2006. Xavier is president of the <a href="http://www.srug.org/">Spanish Ruby Users Group</a> and has been involved in Rails in several ways. He enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Oh, he also "tweets":http://twitter.com/fxn!
+<% end %>
+
+<h3 class="section">Rails Guides Designers</h3>
+
+<% author('Jason Zimdars', 'jz') do %>
+ Jason Zimdars is an experienced creative director and web designer who has lead UI and UX design for numerous websites and web applications. You can see more of his design and writing at <a href="http://www.thinkcage.com/">Thinkcage.com</a> or follow him on <a href="http://www.twitter.com/JZ">Twitter</a>.
+<% end %>
+
+<h3 class="section">Rails Guides Authors</h3>
+
<% author('Frederick Cheung', 'fcheung') do %>
Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006. He is based in Cambridge (UK) and when not consuming fine ales he blogs at "spacevatican.org":http://www.spacevatican.org.
<% end %>
@@ -17,17 +39,10 @@ p. We'd like to thank the following people for their tireless contributions to t
Jeff Dean is a software engineer with "Pivotal Labs":http://pivotallabs.com.
<% end %>
-<% author('Mike Gunderloy', 'mgunderloy') do %>
- Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails activism team":http://rubyonrails.org/activists . He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much.
-<% end %>
-
<% author('Cássio Marques', 'cmarques') do %>
Cássio Marques is a Brazilian software developer working with different programming languages such as Ruby, JavaScript, CPP and Java, as an independent consultant. He blogs at "/* CODIFICANDO */":http://cassiomarques.wordpress.com, which is mainly written in Portuguese, but will soon get a new section for posts with English translation.
<% end %>
-<% author('Pratik Naik', 'lifo') do %>
- Pratik Naik is a Ruby on Rails consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails core team":http://rubyonrails.com/core. He maintains a blog at "has_many :bugs, :through => :rails":http://m.onkey.org and has an active "twitter account":http://twitter.com/lifo.
-
<% author('Emilio Tagua', 'miloops') do %>
Emilio Tagua -- a.k.a. miloops -- is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist. Cofounder of "Eventioz":http://www.eventioz.com. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as miloops.
<% end %>
@@ -35,5 +50,3 @@ p. We'd like to thank the following people for their tireless contributions to t
<% author('Heiko Webers', 'hawe') do %>
Heiko Webers is the founder of "bauland42":http://www.bauland42.de, a German web application security consulting and development company focused on Ruby on Rails. He blogs at the "Ruby on Rails Security Project":http://www.rorsecurity.info. After 10 years of desktop application development, Heiko has rarely looked back.
<% end %>
-
-<% end %>
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index 41d8fba3dc..074aea300a 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -605,9 +605,9 @@ can be replaced with
by defining a LabellingFormBuilder class similar to the following:
<ruby>
-class LabellingFormBuilder < FormBuilder
+class LabellingFormBuilder < ActionView::Helpers::FormBuilder
def text_field(attribute, options={})
- label(attribute) + text_field(attribute, options)
+ label(attribute) + super
end
end
</ruby>
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 3d6c16f11c..6e02cfe1bd 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -115,6 +115,7 @@ If you’d like more details on REST as an architectural style, these resources
* "A Brief Introduction to REST":http://www.infoq.com/articles/rest-introduction by Stefan Tilkov
* "An Introduction to REST":http://bitworking.org/news/373/An-Introduction-to-REST (video tutorial) by Joe Gregorio
* "Representational State Transfer":http://en.wikipedia.org/wiki/Representational_State_Transfer article in Wikipedia
+* "How to GET a Cup of Coffee":http://www.infoq.com/articles/webber-rest-workflow by Jim Webber, Savas Parastatidis & Ian Robinson
h3. Creating a New Rails Project
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index bb445c0bf7..c369ce9a7d 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -12,8 +12,8 @@ So, in the process of _internationalizing_ your Rails application you have to:
In the process of _localizing_ your application you'll probably want to do following three things:
-* Replace or supplement Rails' 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
+* Replace or supplement Rails' default locale -- e.g. date and time formats, month names, Active Record model names, etc
+* Abstract texts in your application into keyed dictionaries -- e.g. 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.
@@ -24,12 +24,12 @@ NOTE: The Ruby I18n framework provides you with all neccessary means for interna
h3. How I18n in Ruby on Rails works
-Internationalization is a complex problem. Natural languages differ in so many ways (eg. in pluralization rules) that it is hard to provide tools for solving all problems at once. For that reason the Rails I18n API focuses on:
+Internationalization is a complex problem. Natural languages differ in so many ways (e.g. in pluralization rules) that it is hard to provide tools for solving all problems at once. For that reason the Rails I18n API focuses on:
* 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. Active Record 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* -- e.g. Active Record validation messages, time and date formats -- *has been internationalized*, so _localization_ of a Rails application means "over-riding" these defaults.
h4. The overall architecture of the library
@@ -89,15 +89,15 @@ en:
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 "+activerecord/lib/active_record/locale/en.yml+":http://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml file or time and date formats in the "+activesupport/lib/active_support/locale/en.yml+":http://github.com/rails/rails/blob/master/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.
+The I18n library will use *English* as a *default locale*, i.e. 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 "some discussion":http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en), 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.
+NOTE: The i18n library takes a *pragmatic approach* to locale keys (after "some discussion":http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en), 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". Many international applications use only the "language" element of a locale such as +:cz+, +:th+ or +:es+ (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the +:en-US+ locale you would have $ as a currency symbol, while in +:en-UK+, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a +:en-UK+ dictionary. Various "Rails I18n plugins":http://rails-i18n.org/wiki such as "Globalize2":http://github.com/joshmh/globalize2 may help you implement it.
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.
-The default +environment.rb+ files has instruction how to add locales from another directory and how to set different default locale. Just uncomment and edit the specific lines.
+The default +environment.rb+ files has instruction how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
<ruby>
# The internationalization framework can be changed
@@ -129,9 +129,9 @@ If you want to translate your Rails application to a *single language other than
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 "_RESTful_":http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about RESTful approach in "Stefan Tilkov's articles":http://www.infoq.com/articles/rest-introduction. There may be some exceptions to this rule, which are discussed below.
+WARNING: You may be tempted to store the chosen 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 "_RESTful_":http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in "Stefan Tilkov's articles":http://www.infoq.com/articles/rest-introduction. 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:
+The _setting part_ is easy. You can set the locale in a +before_filter+ in the ApplicationController like this:
<ruby>
before_filter :set_locale
@@ -141,13 +141,13 @@ def set_locale
end
</ruby>
-This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is 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.
+This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is, for example, Google's approach.) So +http://localhost:3000?locale=pt+ will load the Portugese localization, whereas +http://localhost:3000?locale=de+ would load the German localization, and so on. You may skip the next section and head over to the *Internationalize your application* section, if you want to try things out by manually placing the locale in the URL and reloading the page.
-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.
+Of course, you probably don't want to manually include the locale in every URL all over your application, or want the URLs look differently, e.g. the usual +http://example.com/pt/books+ versus +http://example.com/en/books+. Let's discuss the different options you have.
-IMPORTANT: 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 "this commit":http://github.com/svenfuchs/i18n/commit/411f8fe7 and background at "Rails I18n Wiki":http://rails-i18n.org/wiki/pages/i18n-available_locales.)
+IMPORTANT: The following examples rely on having available locales loaded into your application as an array of strings like +["en", "es", "gr"]+. This is not included in the current version of Rails 2.2 -- the forthcoming Rails version 2.3 will contain the easy accessor +available_locales+. (See "this commit":http://github.com/svenfuchs/i18n/commit/411f8fe7 and background at "Rails I18n Wiki":http://rails-i18n.org/wiki/pages/i18n-available_locales.)
-So, for having available locales easily available in Rails 2.2, we have to include this support manually in an initializer, like this:
+So, for having available locales easily accessible in Rails 2.2, we have to include this support manually in an initializer, like this:
<ruby>
# config/initializers/available_locales.rb
@@ -180,11 +180,11 @@ class ApplicationController < ActionController::Base
end
</ruby>
-h4. Setting locale from the domain name
+h4. Setting the locale from the domain name
-One option you have is to set the locale from the domain name where your application runs. For example, we want +www.example.com+ to load 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:
+One option you have is to set the locale from the domain name where your application runs. For example, we want +www.example.com+ to load the English (or default) locale, and +www.example.es+ to load the Spanish locale. Thus the _top-level domain name_ is used for locale setting. This has several advantages:
-* Locale is an _obvious_ part of the URL
+* The locale is an _obvious_ part of the URL
* People intuitively grasp in which language the content will be displayed
* It is very trivial to implement in Rails
* Search engines seem to like that content in different languages lives at different, inter-linked domains
@@ -208,7 +208,7 @@ def extract_locale_from_tld
end
</ruby>
-We can also set the locale from the _subdomain_ in very similar way:
+We can also set the locale from the _subdomain_ in a very similar way:
<ruby>
# Get locale code from request subdomain (like http://it.application.local:3000)
@@ -231,15 +231,15 @@ assuming you would set +APP_CONFIG[:deutsch_website_url]+ to some value like +ht
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).
-h4. Setting locale from the URL params
+h4. Setting the 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.
+The 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.
+This approach has almost the same set of advantages as setting the locale from the domain name: namely that it's RESTful and in accord with the 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.
+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 (e.g. +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 "+*ApplicationController#default_url_options*+":http://api.rubyonrails.org/classes/ActionController/Base.html#M000515, which is useful precisely in this scenario: it enables us to set "defaults" for "+url_for+":http://api.rubyonrails.org/classes/ActionController/Base.html#M000503 and helper methods dependent on it (by implementing/overriding this method).
+Rails contains infrastructure for "centralizing dynamic decisions about the URLs" in its "+ApplicationController#default_url_options+":http://api.rubyonrails.org/classes/ActionController/Base.html#M000515, which is useful precisely in this scenario: it enables us to set "defaults" for "+url_for+":http://api.rubyonrails.org/classes/ActionController/Base.html#M000503 and helper methods dependent on it (by implementing/overriding this method).
We can include something like this in our ApplicationController then:
@@ -251,20 +251,20 @@ def default_url_options(options={})
end
</ruby>
-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+.
+Every helper method dependent on +url_for+ (e.g. 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 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 the 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 "+path_prefix+":http://api.rubyonrails.org/classes/ActionController/Resources.html#M000354 option in this way:
+You probably want URLs to look like this: +www.example.com/en/books+ (which loads the English locale) and +www.example.com/nl/books+ (which loads the Netherlands locale). This is achievable with the "over-riding +default_url_options+" strategy from above: you just have to set up your routes with "+path_prefix+":http://api.rubyonrails.org/classes/ActionController/Resources.html#M000354 option in this way:
<ruby>
# config/routes.rb
map.resources :books, :path_prefix => '/:locale'
</ruby>
-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).
+Now, when you call the +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.)
+Of course, you need to take special care of the 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:
@@ -275,18 +275,18 @@ 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 "_routing_filter_":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "_translate_routes_":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
+IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitely, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
-h4. Setting locale from the client supplied information
+h4. Setting the 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.
+In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' prefered language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites -- see the box about _sessions_, _cookies_ and RESTful architecture above.
h5. Using Accept-Language
One source of client supplied information would be an +Accept-Language+ HTTP header. People may "set this in their browser":http://www.w3.org/International/questions/qa-lang-priorities or other clients (such as _curl_).
-A trivial implementation of using +Accept-Language+ header would be:
+A trivial implementation of using an +Accept-Language+ header would be:
<ruby>
def set_locale
@@ -300,21 +300,21 @@ def extract_locale_from_accept_language_header
end
</ruby>
-Of course, in production environment you would need much robust code, and could use a plugin such as Iaian Hecker's "http_accept_language":http://github.com/iain/http_accept_language or even Rack middleware such as Ryan Tomayko's "locale":http://github.com/rtomayko/rack-contrib/blob/master/lib/rack/locale.rb.
+Of course, in a production environment you would need much more robust code, and could use a plugin such as Iaian Hecker's "http_accept_language":http://github.com/iain/http_accept_language or even Rack middleware such as Ryan Tomayko's "locale":http://github.com/rtomayko/rack-contrib/blob/master/lib/rack/locale.rb.
h5. 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 "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query database for user's IP, and lookup your preffered locale for the country/region/city returned.
+Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query the database for the user's IP, and look up your prefered locale for the country/region/city returned.
h5. 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.
+You can also provide users of your application with means to set (and possibly over-ride) the 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 the database. Then you'd set the locale to this value.
h3. 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.
+OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. With that in place, you're now ready for the really interesting stuff.
-Let's _internationalize_ our application, ie. abstract every locale-specific parts, and that _localize_ it, ie. provide neccessary translations for these abstracts.
+Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide neccessary translations for these abstracts.
You most probably have something like this in one of your applications:
@@ -359,7 +359,7 @@ When you now render this view, it will show an error message which tells you tha
!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 +<span class="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;+.
So let's add the missing translations into the dictionary files (i.e. do the "localization" part):
@@ -385,7 +385,7 @@ And when you change the URL to pass the pirate locale (+http://localhost:3000?lo
NOTE: You need to restart the server when you add new locale files.
-You may use YAML (+.yml+) or plain Ruby (+.rb+) files for storing your translations in SimpleStore. YAML is the preffered option among Rails developers, has one big disadvantage, though. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into Ruby file.)
+You may use YAML (+.yml+) or plain Ruby (+.rb+) files for storing your translations in SimpleStore. YAML is the prefered option among Rails developers. However, it has one big disadvantage. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.)
h4. Adding Date/Time formats
@@ -412,11 +412,17 @@ So that would give you:
!images/i18n/demo_localized_pirate.png(rails i18n demo localized time to pirate)!
-TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected. Of course, there's a great chance that somebody already did all the work by *translating Rails's defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locale/+ directory, they will automatically ready for use.
+TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails's defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locale/+ directory, they will automatically be ready for use.
+
+h4. Localized views
+
+Rails 2.3 brings one convenient feature: localized views (templates). Let's say you have a _BooksController_ in your application. Your _index_ action renders content in +app/views/books/index.html.erb+ template. When you put a _localized variant_ of this template: *+index.es.html.erb+* in the same directory, Rails will render content in this template, when the locale is set to +:es+. When the locale is set to the default locale, the generic +index.html.erb+ view will be used. (Future Rails versions may well bring this _automagic_ localization to assets in +public+, etc.)
+
+You can make use of this feature, e.g. when working with a large amount of static content, which would be clumsy to put inside YAML or Ruby dictionaries. Bear in mind, though, that any change you would like to do later to the template must be propagated to all of them.
h4. 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.
+When you are using the default SimpleStore shipped with the i18n library, dictionaries are stored in plain-text files on the disc. Putting translations for all parts of your application in one file per locale could be hard to manage. You can store these files in a hierarchy which makes sense to you.
For example, your +config/locale+ directory could look like this:
@@ -443,9 +449,14 @@ For example, your +config/locale+ directory could look like this:
|-----en.rb
</pre>
-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).
+This way, you can separate model and model attribute names from text inside views, and all of this from the "defaults" (e.g. date and time formats). Other stores for the i18n library could provide different means of such separation.
-Other stores for the i18n library could provide different means of such separation.
+NOTE: The default locale loading mechanism in Rails does not load locale files in nested dictionaries, like we have here. So, for this to work, we must explicitely tell Rails to look further:
+
+<ruby>
+ # config/environment.rb
+ config.i18n.load_path += Dir[File.join(RAILS_ROOT, 'config', 'locales', '**', '*.{rb,yml}')]
+</ruby>
Do check the "Rails i18n Wiki":http://rails-i18n.org/wiki for list of tools available for managing translations.
@@ -458,7 +469,7 @@ Covered are features like these:
* looking up translations
* interpolating data into translations
* pluralizing translations
-* localizing dates, numbers, currency etc.
+* localizing dates, numbers, currency, etc.
h4. Looking up translations
@@ -471,41 +482,41 @@ I18n.t :message
I18n.t 'message'
</ruby>
-+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 more additional keys that will be used to specify a “namespace” or scope for a translation key:
<ruby>
-I18n.t :invalid, :scope => [:active_record, :error_messages]
+I18n.t :invalid, :scope => [:activerecord, :errors, :messages]
</ruby>
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:
+Additionally, both the key and scopes can be specified as dot-separated keys as in:
<ruby>
-I18n.translate :"active_record.error_messages.invalid"
+I18n.translate :"activerecord.errors.messages.invalid"
</ruby>
Thus the following calls are equivalent:
<ruby>
-I18n.t 'active_record.error_messages.invalid'
-I18n.t 'error_messages.invalid', :scope => :active_record
-I18n.t :invalid, :scope => 'active_record.error_messages'
-I18n.t :invalid, :scope => [:active_record, :error_messages]
+I18n.t 'activerecord.errors.messages.invalid'
+I18n.t 'errors.messages.invalid', :scope => :active_record
+I18n.t :invalid, :scope => 'activerecord.errors.messages'
+I18n.t :invalid, :scope => [:activerecord, :errors, :messages]
</ruby>
h5. Defaults
-When a default option is given its value will be returned if the translation is missing:
+When a +:default+ option is given, its value will be returned if the translation is missing:
<ruby>
I18n.t :missing, :default => 'Not here'
# => 'Not here'
</ruby>
-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.
+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:
<ruby>
I18n.t :missing, :default => [:also_missing, 'Not here']
@@ -514,20 +525,37 @@ I18n.t :missing, :default => [:also_missing, 'Not here']
h5. Bulk and namespace lookup
-To lookup multiple translations at once an array of keys can be passed:
+To look up multiple translations at once, an array of keys can be passed:
<ruby>
-I18n.t [:odd, :even], :scope => 'active_record.error_messages'
+I18n.t [:odd, :even], :scope => 'activerecord.errors.messages'
# => ["must be odd", "must be even"]
</ruby>
-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:
+Also, a key can translate to a (potentially nested) hash of grouped translations. E.g., one can receive _all_ Active Record error messages as a Hash with:
<ruby>
-I18n.t 'active_record.error_messages'
+I18n.t 'activerecord.errors.messages'
# => { :inclusion => "is not included in the list", :exclusion => ... }
</ruby>
+h5. "Lazy" lookup
+
+Rails 2.3 implements a convenient way to look up the locale inside _views_. When you have the following dictionary:
+
+<yaml>
+es:
+ books:
+ index:
+ title: "Título"
+</yaml>
+
+you can look up the +books.index.title+ value *inside* +app/views/books/index.html.erb+ template like this (note the dot):
+
+<ruby>
+<%= t '.title' %>
+</ruby>
+
h4. 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.
@@ -540,8 +568,7 @@ I18n.translate :thanks, :name => 'Jeremy'
# => 'Thanks Jeremy!'
</ruby>
-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.
-
+If a translation uses +:default+ or +:scope+ as an interpolation variable, an I+18n::ReservedInterpolationKey+ exception is raised. If a translation expects an interpolation variable, but this has not been passed to +#translate+, an +I18n::MissingInterpolationArgument+ exception is raised.
h4. Pluralization
@@ -566,13 +593,13 @@ 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).
-If the lookup for the key does not return an Hash suitable for pluralization an +18n::InvalidPluralizationData+ exception is raised.
+If the lookup for the key does not return a Hash suitable for pluralization, an +18n::InvalidPluralizationData+ exception is raised.
h4. 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+.
-If no locale is passed +I18n.locale+ is used:
+If no locale is passed, +I18n.locale+ is used:
<ruby>
I18n.locale = :de
@@ -595,7 +622,7 @@ I18n.default_locale = :de
h3. How to store your custom translations
-The shipped Simple backend allows you to store translations in both plain Ruby and YAML format. [2]
+The Simple backend shipped with Active Support allows you to store translations in both plain Ruby and YAML format. [2]
For example a Ruby Hash providing translations can look like this:
@@ -617,9 +644,9 @@ pt:
bar: baz
</ruby>
-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 Active Support +en.yml+ translations YAML file:
<ruby>
en:
@@ -639,11 +666,11 @@ I18n.t :short, :scope => 'date.formats'
I18n.t :short, :scope => [:date, :formats]
</ruby>
-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 formats.
h4. 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 look up translations for your model and attribute names.
For example when you add the following translations:
@@ -662,7 +689,7 @@ Then +User.human_name+ will return "Dude" and +User.human_attribute_name(:login)
h5. Error message scopes
-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.
+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.
@@ -674,23 +701,23 @@ class User < ActiveRecord::Base
end
</ruby>
-The key for the error message in this case is +:blank+. Active Record will lookup this key in the namespaces:
+The key for the error message in this case is +:blank+. Active Record will look up this key in the namespaces:
<ruby>
-activerecord.errors.messages.models.[model_name].attributes.[attribute_name]
-activerecord.errors.messages.models.[model_name]
+activerecord.errors.models.[model_name].attributes.[attribute_name]
+activerecord.errors.models.[model_name]
activerecord.errors.messages
</ruby>
Thus, in our example it will try the following keys in this order and return the first result:
<ruby>
-activerecord.errors.messages.models.user.attributes.name.blank
-activerecord.errors.messages.models.user.blank
+activerecord.errors.models.user.attributes.name.blank
+activerecord.errors.models.user.blank
activerecord.errors.messages.blank
</ruby>
-When your models are additionally using inheritance then the messages are looked up for the inherited model class names are looked up.
+When your models are additionally using inheritance then the messages are looked up in the inheritance chain.
For example, you might have an Admin model inheriting from User:
@@ -710,7 +737,7 @@ activerecord.errors.models.user.blank
activerecord.errors.messages.blank
</ruby>
-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.
+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.
h5. Error message interpolation
@@ -745,7 +772,7 @@ So, for example, instead of the default error message +"can not be blank"+ you c
h5. Translations for the Active Record error_messages_for helper
-If you are using the Active Record +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:
@@ -764,13 +791,13 @@ h4. 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. Here's a brief overview.
-h5. ActionView helper methods
+h5. Action View helper methods
-* +distance_of_time_in_words+ translates and pluralizes its result and interpolates the number of seconds, minutes, hours and so on. See "datetime.distance_in_words":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L51 translations.
+* +distance_of_time_in_words+ translates and pluralizes its result and interpolates the number of seconds, minutes, hours, and so on. See "datetime.distance_in_words":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L51 translations.
-* +datetime_select+ and +select_month+ use translated month names for populating the resulting select tag. See "date.month_names":http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L15 for translations. +datetime_select+ also looks up the order option from "date.order":http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L18 (unless you pass the option explicitely). All date select helpers translate the prompt using the translations in the "datetime.prompts":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L83 scope if applicable.
+* +datetime_select+ and +select_month+ use translated month names for populating the resulting select tag. See "date.month_names":http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L15 for translations. +datetime_select+ also looks up the order option from "date.order":http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L18 (unless you pass the option explicitely). All date selection helpers translate the prompt using the translations in the "datetime.prompts":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L83 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 "number":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L2 scope.
+* The +number_to_currency+, +number_with_precision+, +number_to_percentage+, +number_with_delimiter+, and +number_to_human_size+ helpers use the number format settings located in the "number":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L2 scope.
h5. Active Record methods
@@ -778,9 +805,9 @@ h5. Active Record methods
* +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 "activerecord.errors.format.separator":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L91 (and defaults to +' '+).
+*+ ActiveRecord::Errors#full_messages+ prepends the attribute name to the error message using a separator that will be looked up from "activerecord.errors.format.separator":http://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L91 (and which defaults to +'&nbsp;'+).
-h5. ActiveSupport methods
+h5. Active Support methods
* +Array#to_sentence+ uses format settings as given in the "support.array":http://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L30 scope.
@@ -789,9 +816,9 @@ h3. Customize your I18n setup
h4. Using different backends
-For several reasons the shipped Simple backend only does the "simplest thing that ever could work" _for Ruby on Rails_ [3] ... which means that it is only guaranteed to work for English and, as a side effect, languages that are very similar to English. Also, the simple backend is only capable of reading translations but can not dynamically store them to any format.
+For several reasons the Simple backend shipped with Active Support only does the "simplest thing that ever could work" _for Ruby on Rails_ [3] ... which means that it is only guaranteed to work for English and, as a side effect, languages that are very similar to English. Also, the simple backend is only capable of reading translations but can not dynamically store them to any format.
-That does not mean you're stuck with these limitations though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs. E.g. you could exchange it with Globalize's Static backend:
+That does not mean you're stuck with these limitations, though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs. E.g. you could exchange it with Globalize's Static backend:
<ruby>
I18n.backend = Globalize::Backend::Static.new
@@ -810,11 +837,11 @@ 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
</ruby>
-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 exception’s error message string containing the missing key/scope.
+The I18n API will catch all of these exceptions when they are 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 exception’s 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.
-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:
+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:
<ruby>
module I18n
@@ -828,9 +855,9 @@ I18n.exception_handler = :just_raise_that_exception
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:
<ruby>
I18n.t :foo, :raise => true # always re-raises exceptions from the backend
@@ -845,7 +872,7 @@ If you find anything missing or wrong in this guide please file a ticket on "our
h3. 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.
+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 only then cherry-picking the best-of-bread of most widely useful features for inclusion in 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 "mailinglist":http://groups.google.com/group/rails-i18n!)
@@ -876,7 +903,7 @@ fn1. Or, to quote "Wikipedia":http://en.wikipedia.org/wiki/Internationalization_
fn2. Other backends might allow or require to use other formats, e.g. a GetText backend might allow to read GetText files.
-fn3. 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.
+fn3. One of these reasons is that we don't want to imply 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.
h3. Changelog
diff --git a/railties/guides/source/index.erb.textile b/railties/guides/source/index.erb.textile
index 49d8cad404..4e6364baa4 100644
--- a/railties/guides/source/index.erb.textile
+++ b/railties/guides/source/index.erb.textile
@@ -117,4 +117,8 @@ h3. Digging Deeper
Various caching techniques provided by Rails.
<% end %>
+<% guide("Contributing to Rails", 'contributing_to_rails.html') do %>
+ Rails is not "somebody else's framework." This guide covers a variety of ways that you can get involved in the ongoing development of Rails.
+<% end %>
+
</dl>
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index cb02b90eb9..606f6a6cd1 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -63,6 +63,9 @@
<dd><a href="plugins.html">The Basics of Creating Rails Plugins</a></dd>
<dd><a href="configuring.html">Configuring Rails Applications</a></dd>
<dd><a href="rails_on_rack.html">Rails on Rack</a></dd>
+ <dd><a href="command_line.html">Rails Command Line Tools and Rake Tasks</a></dd>
+ <dd><a href="caching_with_rails.html">Caching with Rails</a></dd>
+ <dd><a href="contributing_to_rails.html">Contributing to Rails</a></dd>
</dl>
</div>
</li>
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 5e2cedcf0c..95c54a2295 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -316,7 +316,7 @@ Now, if the current user is a special user, they'll get a special layout when vi
<ruby>
class ProductsController < ApplicationController
- layout proc{ |controller| controller.
+ layout proc { |controller| controller.request.xhr? ? 'popup' : 'application' }
# ...
end
</ruby>
@@ -327,13 +327,12 @@ Layouts specified at the controller level support +:only+ and +:except+ options
<ruby>
class ProductsController < ApplicationController
- layout "inventory", :only => :index
layout "product", :except => [:index, :rss]
#...
end
</ruby>
-With those declarations, the +inventory+ layout would be used only for the +index+ method, the +product+ layout would be used for everything else except the +rss+ method, and the +rss+ method will have its layout determined by the automatic layout rules.
+With this declaration, the +product+ layout would be used for everything but the +rss+ and +index+ methods.
h6. Layout Inheritance
@@ -403,6 +402,7 @@ def show
if @book.special?
render :action => "special_show"
end
+ render :action => "regular_show"
end
</ruby>
@@ -414,9 +414,23 @@ def show
if @book.special?
render :action => "special_show" and return
end
+ render :action => "regular_show"
end
</ruby>
+Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. So this code will work with problems:
+
+<ruby>
+ def show
+ @book = Book.find(params[:id])
+ if @book.special?
+ render :action => "special_show"
+ end
+ end
+</ruby>
+
+This will render a book with +special?+ set with the +special_show+ template, while other books will render with the default +show+ template.
+
h4. Using redirect_to
Another way to handle returning responses to an HTTP request is with +redirect_to+. As you've seen, +render+ tells Rails which view (or other asset) to use in constructing a response. The +redirect_to+ method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:
@@ -455,7 +469,7 @@ end
def show
@book = Book.find(params[:id])
if @book.nil?
- render :action => "index" and return
+ render :action => "index"
end
end
</ruby>
@@ -470,7 +484,7 @@ end
def show
@book = Book.find(params[:id])
if @book.nil?
- redirect_to :action => "index" and return
+ redirect_to :action => "index"
end
end
</ruby>
@@ -915,7 +929,7 @@ You may find that your application requires a layout that differs slightly from
Suppose you have the follow +ApplicationController+ layout:
-* +app/views/layouts/application.erb+
+* +app/views/layouts/application.html.erb+
<erb>
<html>
@@ -934,7 +948,7 @@ Suppose you have the follow +ApplicationController+ layout:
On pages generated by +NewsController+, you want to hide the top menu and add a right menu:
-* +app/views/layouts/news.erb+
+* +app/views/layouts/news.html.erb+
<erb>
<% content_for :stylesheets do %>
diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile
index c2bf36c893..fa1ca8bde2 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -121,7 +121,7 @@ class PostPerformanceTest < ActionController::PerformanceTest
end
</ruby>
-You can find more details about the +get+ and +post+ methods in the link:../testing_rails_applications.html#mgunderloy[Testing Rails Applications] guide.
+You can find more details about the +get+ and +post+ methods in the "Testing Rails Applications":testing.html guide.
h5. Model example
@@ -283,7 +283,7 @@ Graph output shows how long each method takes to run, which methods call it and
h6. Tree
-Tree output is profiling information in calltree format for use by http://kcachegrind.sourceforge.net/html/Home.html[kcachegrind] and similar tools.
+Tree output is profiling information in calltree format for use by "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html and similar tools.
h4. Tuning test runs
diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile
index e300e047b4..0f3823e6f0 100644
--- a/railties/guides/source/rails_on_rack.textile
+++ b/railties/guides/source/rails_on_rack.textile
@@ -119,9 +119,7 @@ h5. Adding a Middleware
You can add a new middleware to the middleware stack using any of the following methods:
-* +config.middleware.add(new_middleware, args)+ - Adds the new middleware at the bottom of the middleware stack.
-
-* +config.middleware.insert(index, new_middleware, args)+ - Adds the new middleware at the position specified by +index+ in the middleware stack.
+* +config.middleware.use(new_middleware, args)+ - Adds the new middleware at the bottom of the middleware stack.
* +config.middleware.insert_before(existing_middleware, new_middleware, args)+ - Adds the new middleware before the specified existing middleware in the middleware stack.
@@ -153,6 +151,16 @@ You can swap an existing middleware in the middleware stack using +config.middle
config.middleware.swap ActionController::Failsafe, Lifo::Failsafe
</ruby>
+h5. Middleware stack is an array
+
+The middleware stack behaves just like a normal +Array+. You can use any +Array+ methods to insert, reorder, or remove items from the stack. Methods described in the section above are just convenience methods.
+
+For example, the following removes the middleware matching the supplied class name:
+
+<ruby>
+config.middleware.delete(middleware)
+</ruby>
+
h4. Internal Middleware Stack
Much of Action Controller's functionality is implemented as Middlewares. The following table explains the purpose of each of them:
@@ -197,10 +205,32 @@ use Rack::Head
run ActionController::Dispatcher.new
</shell>
+h4. Using Rack Builder
+
+The following shows how to replace use +Rack::Builder+ instead of the Rails supplied +MiddlewareStack+.
+
+<strong>Clear the existing Rails middleware stack</strong>
+
+<ruby>
+# environment.rb
+config.middleware.clear
+</ruby>
+
+<br />
+<strong>Add a +config.ru+ file to +RAILS_ROOT+</strong>
+
+<ruby>
+# config.ru
+use MyOwnStackFromStratch
+run ActionController::Dispatcher.new
+</ruby>
+
h3. Rails Metal Applications
Rails Metal applications are minimal Rack applications specially designed for integrating with a typical Rails application. As Rails Metal Applications skip all of the Action Controller stack, serving a request has no overhead from the Rails framework itself. This is especially useful for infrequent cases where the performance of the full stack Rails framework is an issue.
+Ryan Bates' railscast on the "Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal.
+
h4. Generating a Metal Application
Rails provides a generator called +metal+ for creating a new Metal application:
@@ -226,6 +256,8 @@ class Poller
end
</ruby>
+Metal applications within +app/metal+ folders in plugins will also be discovered and added to the list
+
Metal applications are an optimization. You should make sure to "understand the related performance implications":http://weblog.rubyonrails.org/2008/12/20/performance-of-rails-metal before using it.
h4. Execution Order
@@ -244,10 +276,31 @@ def call(env)
end
</ruby>
-In the code above, +@metals+ is an ordered ( alphabetical ) hash of metal applications. Due to the alphabetical ordering, +aaa.rb+ will come before +bbb.rb+ in the metal chain.
+In the code above, +@metals+ is an ordered hash of metal applications. Due to the default alphabetical ordering, +aaa.rb+ will come before +bbb.rb+ in the metal chain.
+
+It is, however, possible to override the default ordering in your environment. Simply add a line like the following to +config/environment.rb+
+
+<ruby>
+config.metals = ["Bbb", "Aaa"]
+</ruby>
+
+Each string in the array should be the name of your metal class. If you do this then be warned that any metal applications not listed will not be loaded.
WARNING: Metal applications cannot return the HTTP Status +404+ to a client, as it is used for continuing the Metal chain execution. Please use normal Rails controllers or a custom middleware if returning +404+ is a requirement.
+h3. Resources
+
+h4. Learning Rack
+
+* "Official Rack Website":http://rack.github.com
+* "Introducing Rack":http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html
+* "Ruby on Rack #1 - Hello Rack!":http://m.onkey.org/2008/11/17/ruby-on-rack-1
+* "Ruby on Rack #2 - The Builder":http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder
+
+h4. Understanding Middlewares
+
+* "Railscast on Rack Middlewares":http://railscasts.com/episodes/151-rack-middleware
+
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/58
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index c26a5cd6ee..26aa683710 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -282,7 +282,7 @@ TIP: Depending on the other code in your application, you may prefer to add addi
h5. Using :requirements
-You an use the +:requirements+ option in a RESTful route to impose a format on the implied +:id+ parameter in the singular routes. For example:
+You can use the +:requirements+ option in a RESTful route to impose a format on the implied +:id+ parameter in the singular routes. For example:
<ruby>
map.resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/}
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index 5797eb888b..98a42f1223 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -392,15 +392,31 @@ params[:user] #=> {:name => “ow3ned”, :admin => true}
So if you create a new user using mass-assignment, it may be too easy to become an administrator.
+Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in rails 2.3. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example:
+
+<ruby>
+ class Person < ActiveRecord::Base
+ has_many :credits
+
+ accepts_nested_attributes_for :children
+ end
+
+ class Child < ActiveRecord::Base
+ belongs_to :person
+ end
+</ruby>
+
+As a result, the vulnerability is extended beyond simply exposing column assignment, allowing attackers the ability to create entirely new records in referenced tables (children in this case).
+
h4. Countermeasures
-To avoid this, Rails provides two class methods in your ActiveRecord class to control access to your attributes. The attr_protected method takes a list of attributes that will not be accessible for mass-assignment. For example:
+To avoid this, Rails provides two class methods in your Active Record class to control access to your attributes. The +attr_protected+ method takes a list of attributes that will not be accessible for mass-assignment. For example:
<ruby>
attr_protected :admin
</ruby>
-A much better way, because it follows the whitelist-principle, is the +attr_accessible+ method. It is the exact opposite of attr_protected, because _(highlight)it takes a list of attributes that will be accessible_. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example:
+A much better way, because it follows the whitelist-principle, is the +attr_accessible+ method. It is the exact opposite of +attr_protected+, because _(highlight)it takes a list of attributes that will be accessible_. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example:
<ruby>
attr_accessible :name
@@ -416,6 +432,14 @@ params[:user] #=> {:name => "ow3ned", :admin => true}
@user.admin #=> true
</ruby>
+A more paranoid technique to protect your whole project would be to enforce that all models whitelist their accessible attributes. This can be easily achieved with a very simple initializer:
+
+<ruby>
+ActiveRecord::Base.send(:attr_accessible, nil)
+</ruby>
+
+This will create an empty whitelist of attributes available for mass assignment for all models in your app. As such, your models will need to explicitly whitelist accessible parameters by using an +attr_accessible+ declaration. This technique is best applied at the start of a new project. However, for an existing project with a thorough set of functional tests, it should be straightforward and relatively quick to insert this initializer, run your tests, and expose each attribute (via +attr_accessible+) as dictated by your failing tests.
+
h3. User management
-- _Almost every web application has to deal with authorization and authentication. Instead of rolling your own, it is advisable to use common plug-ins. But keep them up-to-date, too. A few additional precautions can make your application even more secure._
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index 9897fbab6f..1a6fd1eb2f 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -110,9 +110,9 @@ h5. Fixtures in Action
Rails by default automatically loads all fixtures from the 'test/fixtures' folder for your unit and functional test. Loading involves three steps:
- * Remove any existing data from the table corresponding to the fixture
- * Load the fixture data into the table
- * Dump the fixture data into a variable in case you want to access it directly
+* Remove any existing data from the table corresponding to the fixture
+* Load the fixture data into the table
+* Dump the fixture data into a variable in case you want to access it directly
h5. Hashes with Special Powers
@@ -140,7 +140,7 @@ h3. Unit Testing your Models
In Rails, unit tests are what you write to test your models.
-For this guide we will be using Rails _scaffolding_. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practises. I will be using examples from this generated code and would be supplementing it with additional examples where necessary.
+For this guide we will be using Rails _scaffolding_. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practices. I will be using examples from this generated code and would be supplementing it with additional examples where necessary.
NOTE: For more information on Rails _scaffolding_, refer to "Getting Started with Rails":../getting_started_with_rails.html