Skip to content
Browse files

Merge docrails

  • Loading branch information...
1 parent 4185a4a commit 18eb80ccc7e932f9a6c00462ceaeea648631b120 @lifo lifo committed Mar 16, 2009
Showing with 1,915 additions and 876 deletions.
  1. +1 −1 actionpack/lib/action_controller/integration.rb
  2. +2 −2 actionpack/lib/action_view/helpers/date_helper.rb
  3. +1 −1 actionpack/lib/action_view/helpers/number_helper.rb
  4. +1 −1 activemodel/lib/active_model/validations/inclusion.rb
  5. +9 −5 activerecord/lib/active_record/associations/association_collection.rb
  6. +2 −2 activerecord/lib/active_record/base.rb
  7. +23 −15 activerecord/lib/active_record/batches.rb
  8. +1 −1 activerecord/lib/active_record/validations.rb
  9. +1 −1 activesupport/lib/active_support/core_ext/date/calculations.rb
  10. +14 −2 railties/guides/files/stylesheets/main.css
  11. BIN railties/guides/images/error_messages.png
  12. BIN railties/guides/images/fxn.jpg
  13. BIN railties/guides/images/i18n/demo_localized_pirate.png
  14. BIN railties/guides/images/i18n/demo_translated_en.png
  15. BIN railties/guides/images/i18n/demo_translated_pirate.png
  16. BIN railties/guides/images/i18n/demo_translation_missing.png
  17. BIN railties/guides/images/i18n/demo_untranslated.png
  18. +1 −0 railties/guides/rails_guides.rb
  19. +27 −9 railties/guides/rails_guides/generator.rb
  20. +1 −1 railties/guides/rails_guides/indexer.rb
  21. +112 −0 railties/guides/rails_guides/levenshtein.rb
  22. +80 −35 railties/guides/source/2_3_release_notes.textile
  23. +16 −16 railties/guides/source/action_controller_overview.textile
  24. +48 −12 railties/guides/source/action_mailer_basics.textile
  25. +4 −4 railties/guides/source/active_record_basics.textile
  26. +119 −51 railties/guides/source/active_record_querying.textile
  27. +337 −206 railties/guides/source/activerecord_validations_callbacks.textile
  28. +40 −40 railties/guides/source/association_basics.textile
  29. +79 −71 railties/guides/source/caching_with_rails.textile
  30. +23 −20 railties/guides/source/command_line.textile
  31. +8 −14 railties/guides/source/contribute.textile
  32. +239 −0 railties/guides/source/contributing_to_rails.textile
  33. +23 −10 railties/guides/source/credits.erb.textile
  34. +6 −6 railties/guides/source/debugging_rails_applications.textile
  35. +28 −28 railties/guides/source/form_helpers.textile
  36. +7 −6 railties/guides/source/getting_started.textile
  37. +161 −133 railties/guides/source/i18n.textile
  38. +7 −3 railties/guides/source/index.erb.textile
  39. +12 −9 railties/guides/source/layout.html.erb
  40. +38 −24 railties/guides/source/layouts_and_rendering.textile
  41. +23 −23 railties/guides/source/migrations.textile
  42. +222 −0 railties/guides/source/nested_model_forms.textile
  43. +30 −30 railties/guides/source/performance_testing.textile
  44. +17 −17 railties/guides/source/plugins.textile
  45. +60 −7 railties/guides/source/rails_on_rack.textile
  46. +17 −17 railties/guides/source/routing.textile
  47. +61 −39 railties/guides/source/security.textile
  48. +14 −14 railties/guides/source/testing.textile
View
2 actionpack/lib/action_controller/integration.rb
@@ -5,7 +5,7 @@
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.
#
View
4 actionpack/lib/action_view/helpers/date_helper.rb
@@ -876,8 +876,8 @@ def input_id_from_type(type)
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|
View
2 actionpack/lib/action_view/helpers/number_helper.rb
@@ -140,7 +140,7 @@ def number_to_percentage(number, options = {})
# 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
#
View
2 activemodel/lib/active_model/validations/inclusion.rb
@@ -4,7 +4,7 @@ module ClassMethods
# 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
View
14 activerecord/lib/active_record/associations/association_collection.rb
@@ -143,6 +143,8 @@ def transaction(*args)
end
# Remove all records from this association
+ #
+ # See delete for more info.
def delete_all
load_target
delete(@target)
@@ -200,11 +202,11 @@ def delete(*records)
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 @@ def clear
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)
View
4 activerecord/lib/active_record/base.rb
@@ -736,12 +736,12 @@ def create(attributes = nil, &block)
# ==== 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" } }
View
38 activerecord/lib/active_record/batches.rb
@@ -4,20 +4,23 @@ def self.included(base)
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:
#
# Person.find_each(:conditions => "age > 21") do |person|
# 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 @@ def find_each(options = {})
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:
#
View
2 activerecord/lib/active_record/validations.rb
@@ -802,7 +802,7 @@ def validates_format_of(*attr_names)
# 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
View
2 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
View
16 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; }
+.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;
+}
View
BIN railties/guides/images/error_messages.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN railties/guides/images/fxn.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN railties/guides/images/i18n/demo_localized_pirate.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN railties/guides/images/i18n/demo_translated_en.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN railties/guides/images/i18n/demo_translated_pirate.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN railties/guides/images/i18n/demo_translation_missing.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN railties/guides/images/i18n/demo_untranslated.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
1 railties/guides/rails_guides.rb
@@ -22,6 +22,7 @@ module RailsGuides
autoload :Indexer, "rails_guides/indexer"
autoload :Helpers, "rails_guides/helpers"
autoload :TextileExtensions, "rails_guides/textile_extensions"
+ autoload :Levenshtein, "rails_guides/levenshtein"
end
RedCloth.send(:include, RailsGuides::TextileExtensions)
View
36 railties/guides/rails_guides/generator.rb
@@ -109,8 +109,8 @@ def set_index(body, view)
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,33 +120,51 @@ def textile(body)
# 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
def warn_about_broken_links(html)
+ anchors = extract_anchors(html)
+ check_fragment_identifiers(html, anchors)
+ end
+
+ def extract_anchors(html)
# Textile generates headers with IDs computed from titles.
- anchors = Set.new(html.scan(/<h\d\s+id="([^"]+)/).flatten)
+ anchors = Set.new
+ html.scan(/<h\d\s+id="([^"]+)/).flatten.each do |anchor|
+ if anchors.member?(anchor)
+ puts "*** DUPLICATE HEADER ID: #{anchor}, please consider rewording"
+ else
+ anchors << anchor
+ end
+ end
+
# Also, footnotes are rendered as paragraphs this way.
anchors += Set.new(html.scan(/<p\s+class="footnote"\s+id="([^"]+)/).flatten)
-
- # Check fragment identifiers.
+ return anchors
+ end
+
+ def check_fragment_identifiers(html, anchors)
html.scan(/<a\s+href="#([^"]+)/).flatten.each do |fragment_identifier|
next if fragment_identifier == 'mainCol' # in layout, jumps to some DIV
unless anchors.member?(fragment_identifier)
- puts "BROKEN LINK: ##{fragment_identifier}"
+ guess = anchors.min { |a, b|
+ Levenshtein.distance(fragment_identifier, a) <=> Levenshtein.distance(fragment_identifier, b)
+ }
+ puts "*** BROKEN LINK: ##{fragment_identifier}, perhaps you meant ##{guess}."
end
end
end
View
2 railties/guides/rails_guides/indexer.rb
@@ -29,7 +29,7 @@ def process(string, current_level= 3, counters = [1])
return level_hash
elsif level == current_level
index = counters.join(".")
- bookmark = '#' + title.gsub(/[^a-z0-9\-_]+/i, '').underscore.dasherize
+ bookmark = '#' + title.strip.downcase.gsub(/\s+|_/, '-').delete('^a-z0-9-')
raise "Parsing Fail" unless @result.sub!(matched, "h#{level}(#{bookmark}). #{index}#{title}")
View
112 railties/guides/rails_guides/levenshtein.rb
@@ -0,0 +1,112 @@
+#
+# Levenshtein distance algorithm implementation for Ruby, with UTF-8 support
+#
+# Author:: Paul BATTLEY (pbattley @ gmail.com)
+# Version:: 1.3
+# Date:: 2005-04-19
+#
+# == About
+#
+# The Levenshtein distance is a measure of how similar two strings s and t are,
+# calculated as the number of deletions/insertions/substitutions needed to
+# transform s into t. The greater the distance, the more the strings differ.
+#
+# The Levenshtein distance is also sometimes referred to as the
+# easier-to-pronounce-and-spell 'edit distance'.
+#
+# == Revision history
+#
+# * 2005-05-19 1.3 Repairing an oversight, distance can now be called via
+# Levenshtein.distance(s, t)
+# * 2005-05-04 1.2 Now uses just one 1-dimensional array. I think this is as
+# far as optimisation can go.
+# * 2005-05-04 1.1 Now storing only the current and previous rows of the matrix
+# instead of the whole lot.
+#
+# == Licence
+#
+# Copyright (c) 2005 Paul Battley
+#
+# Usage of the works is permitted provided that this instrument is retained
+# with the works, so that any entity that uses the works is notified of this
+# instrument.
+#
+# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
+#
+
+module Levenshtein
+
+ #
+ # Calculate the Levenshtein distance between two strings +str1+ and +str2+.
+ # +str1+ and +str2+ should be ASCII or UTF-8.
+ #
+ def distance(str1, str2)
+ s = str1.unpack('U*')
+ t = str2.unpack('U*')
+ n = s.length
+ m = t.length
+ return m if (0 == n)
+ return n if (0 == m)
+
+ d = (0..m).to_a
+ x = nil
+
+ (0...n).each do |i|
+ e = i+1
+ (0...m).each do |j|
+ cost = (s[i] == t[j]) ? 0 : 1
+ x = [
+ d[j+1] + 1, # insertion
+ e + 1, # deletion
+ d[j] + cost # substitution
+ ].min
+ d[j] = e
+ e = x
+ end
+ d[m] = x
+ end
+
+ return x
+ end
+
+ extend self
+end
+
+if (__FILE__ == $0)
+ require 'test/unit'
+
+ class LevenshteinTest < Test::Unit::TestCase
+ include Levenshtein
+
+ EXPECTED = [
+ # Easy ones
+ ['test', 'test', 0],
+ ['test', 'tent', 1],
+ ['gumbo', 'gambol', 2],
+ ['kitten', 'sitting', 3],
+ # Empty strings
+ ['foo', '', 3],
+ ['', '', 0],
+ ['a', '', 1],
+ # UTF-8
+ ["f\303\266o", 'foo', 1],
+ ["fran\303\247ais", 'francais', 1],
+ ["fran\303\247ais", "fran\303\246ais", 1],
+ ["\347\247\201\343\201\256\345\220\215\345\211\215\343\201\257"<<
+ "\343\203\235\343\203\274\343\203\253\343\201\247\343\201\231",
+ "\343\201\274\343\201\217\343\201\256\345\220\215\345\211\215\343\201"<<
+ "\257\343\203\235\343\203\274\343\203\253\343\201\247\343\201\231",
+ 2], # Japanese
+ # Edge cases
+ ['a', 'a', 0],
+ ['0123456789', 'abcdefghijklmnopqrstuvwxyz', 26]
+ ]
+
+ def test_known_distances
+ EXPECTED.each do |a,b,x|
+ assert_equal(x, distance(a, b))
+ assert_equal(x, distance(b, a))
+ end
+ end
+ end
+end
View
115 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.
@@ -22,24 +20,25 @@ Rails has now broken with its CGI past, and uses Rack everywhere. This required
Here's a summary of the rack-related changes:
* +script/server+ has been switched to use Rack, which means it supports any Rack compatible server. +script/server+ will also pick up a rackup configuration file if one exists. By default, it will look for a +config.ru+ file, but you can override this with the +-c+ switch.
-* The FCGI handler goes through Rack
-* +ActionController::Dispatcher+ maintains its own default middleware stack. Middlewares can be injected in, reordered, and removed. The stack is compiled into a chain on boot. You can configure the middleware stack in +environment.rb+
+* The FCGI handler goes through Rack.
+* +ActionController::Dispatcher+ maintains its own default middleware stack. Middlewares can be injected in, reordered, and removed. The stack is compiled into a chain on boot. You can configure the middleware stack in +environment.rb+.
* The +rake middleware+ task has been added to inspect the middleware stack. This is useful for debugging the order of the middleware stack.
* The integration test runner has been modified to execute the entire middleware and application stack. This makes integration tests perfect for testing Rack middleware.
* +ActionController::CGIHandler+ is a backwards compatible CGI wrapper around Rack. The +CGIHandler+ is meant to take an old CGI object and convert its environment information into a Rack compatible form.
-* +CgiRequest+ and +CgiResponse+ have been removed
+* +CgiRequest+ and +CgiResponse+ have been removed.
* Session stores are now lazy loaded. If you never access the session object during a request, it will never attempt to load the session data (parse the cookie, load the data from memcache, or lookup an Active Record object).
-* +CGI::Session::CookieStore+ has been replaced by +ActionController::Session::CookieStore+
-* +CGI::Session::MemCacheStore+ has been replaced by +ActionController::Session::MemCacheStore+
-* +CGI::Session::ActiveRecordStore+ has been replaced by +ActiveRecord::SessionStore+
-* You can still change your session store with +ActionController::Base.session_store = :active_record_store+
-* Default sessions options are still set with +ActionController::Base.session = { :key => "..." }+
-* The mutex that normally wraps your entire request has been moved into middleware, +ActionController::Lock+
+* 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+.
+* You can still change your session store with +ActionController::Base.session_store = :active_record_store+.
+* Default sessions options are still set with +ActionController::Base.session = { :key => "..." }+.
+* The mutex that normally wraps your entire request has been moved into middleware, +ActionController::Lock+.
* +ActionController::AbstractRequest+ and +ActionController::Request+ have been unified. The new +ActionController::Request+ inherits from +Rack::Request+. This affects access to +response.headers['type']+ in test requests. Use +response.content_type+ instead.
* +ActiveRecord::QueryCache+ middleware is automatically inserted onto the middleware stack if +ActiveRecord+ has been loaded. This middleware sets up and flushes the per-request Active Record query cache.
* The Rails router and controller classes follow the Rack spec. You can call a controller directly with +SomeController.call(env)+. The router stores the routing parameters in +rack.routing_args+.
-* +ActionController::Request+ inherits from +Rack::Request+
-* Instead of +config.action_controller.session = { :session_key => 'foo', ...+ use +config.action_controller.session = { :key => 'foo', ...+
+* +ActionController::Request+ inherits from +Rack::Request+.
+* Instead of +config.action_controller.session = { :session_key => 'foo', ...+ use +config.action_controller.session = { :key => 'foo', ...+.
* Using the +ParamsParser+ middleware preprocesses any XML, JSON, or YAML requests so they can be read normally with any +Rack::Request+ object after it.
h4. Renewed Support for Rails Engines
@@ -50,7 +49,7 @@ h3. Documentation
The "Ruby on Rails guides":http://guides.rubyonrails.org/ project has published several additional guides for Rails 2.3. In addition, a "separate site":http://guides.rails.info/ maintains updated copies of the Guides for Edge Rails. Other documentation efforts include a relaunch of the "Rails wiki":http://newwiki.rubyonrails.org/ and early planning for a Rails Book.
-* More Information: "Rails Documentation Projects":http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects
+* More Information: "Rails Documentation Projects":http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects.
h3. Ruby 1.9.1 Support
@@ -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.
@@ -198,15 +185,17 @@ MySQL supports a reconnect flag in its connections - if set to true, then the cl
h4. Other Active Record Changes
-* An extra +AS+ was removed from the generated SQL for has_and_belongs_to_many preloading, making it work better for some databases.
+* An extra +AS+ was removed from the generated SQL for +has_and_belongs_to_many+ preloading, making it work better for some databases.
* +ActiveRecord::Base#new_record?+ now returns +false+ rather than +nil+ when confronted with an existing record.
* A bug in quoting table names in some +has_many :through+ associations was fixed.
* You can now specify a particular timestamp for +updated_at+ timestamps: +cust = Customer.create(:name => "ABC Industries", :updated_at => 1.day.ago)+
* Better error messages on failed +find_by_attribute!+ calls.
* Active Record's +to_xml+ support gets just a little bit more flexible with the addition of a +:camelize+ option.
-* A bug in canceling callbacks from +before_update+ or +before_create_ was fixed.
+* 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)
+* +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,14 +288,16 @@ 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/
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
View
32 railties/guides/source/action_controller_overview.textile
@@ -20,7 +20,7 @@ For most conventional RESTful applications, the controller will receive the requ
A controller can thus be thought of as a middle man between models and views. It makes the model data available to the view so it can display that data to the user, and it saves or updates data from the user to the model.
-NOTE: For more details on the routing process, see "Rails Routing from the Outside In":routing_outside_in.html.
+NOTE: For more details on the routing process, see "Rails Routing from the Outside In":routing.html.
h3. Methods and Actions
@@ -83,7 +83,7 @@ class ClientsController < ActionController::Base
end
</ruby>
-h4. Hash and array parameters
+h4. Hash and Array Parameters
The +params+ hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append an empty pair of square brackets "[]" to the key name:
@@ -123,7 +123,7 @@ map.connect "/clients/:status",
In this case, when a user opens the URL +/clients/active+, +params[:status]+ will be set to "active". When this route is used, +params[:foo]+ will also be set to "bar" just like it was passed in the query string. In the same way +params[:action]+ will contain "index".
-h4. default_url_options
+h4. +default_url_options+
You can set global default parameters that will be used when generating URLs with +default_url_options+. To do this, define a method with that name in your controller:
@@ -180,7 +180,7 @@ ActionController::Base.session = {
NOTE: Changing the secret when using the CookieStore will invalidate all existing sessions.
-h4. Accessing the session
+h4. Accessing the Session
In your controller you can access the session through the +session+ instance method.
@@ -235,7 +235,7 @@ end
To reset the entire session, use +reset_session+.
-h4. The flash
+h4. The Flash
The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc. It is accessed in much the same way as the session, like a hash. Let's use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request:
@@ -288,7 +288,7 @@ class MainController < ApplicationController
end
</ruby>
-h5. flash.now
+h5. +flash.now+
By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the +create+ action fails to save a resource and you render the +new+ template directly, that's not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use +flash.now+ in the same way you use the normal +flash+:
@@ -381,7 +381,7 @@ end
Now, the +LoginsController+'s +new+ and +create+ actions will work as before without requiring the user to be logged in. The +:only+ option is used to only skip this filter for these actions, and there is also an +:except+ option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.
-h4. After filters and around filters
+h4. After Filters and Around Filters
In addition to before filters, you can run filters after an action has run or both before and after. The after filter is similar to the before filter, but because the action has already been run it has access to the response data that's about to be sent to the client. Obviously, after filters can not stop the action from running.
@@ -403,7 +403,7 @@ private
end
</ruby>
-h4. Other ways to use filters
+h4. Other Ways to Use Filters
While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing.
@@ -517,7 +517,7 @@ h3. The Request and Response Objects
In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The +request+ method contains an instance of +AbstractRequest+ and the +response+ method returns a response object representing what is going to be sent back to the client.
-h4. The +request+ object
+h4. The +request+ Object
The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the "API documentation":http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html. Among the properties that you can access on this object are:
@@ -538,7 +538,7 @@ h5. +path_parameters+, +query_parameters+, and +request_parameters+
Rails collects all of the parameters sent along with the request in the +params+ hash, whether they are sent as part of the query string or the post body. The request object has three accessors that give you access to these parameters depending on where they came from. The +query_parameters+ hash contains parameters that were sent as part of the query string while the +request_parameters+ hash contains parameters sent as part of the post body. The +path_parameters+ hash contains parameters that were recognized by the routing as being part of the path leading to this particular controller and action.
-h4. The response object
+h4. The +response+ Object
The response object is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values.
@@ -550,7 +550,7 @@ The response object is not usually used directly, but is built up during the exe
|charset|The character set being used for the response. Default is "utf-8".|
|headers|Headers used for the response.|
-h5. Setting custom headers
+h5. Setting Custom Headers
If you want to set custom headers for a response then +response.headers+ is the place to do it. The headers attribute is a hash which maps header names to their values, and Rails will set some of them automatically. If you want to add or change a header, just assign it to +response.headers+ this way:
@@ -565,7 +565,7 @@ Rails comes with two built-in HTTP authentication mechanisms:
* Basic Authentication
* Digest Authentication
-h4. HTTP basic authentication
+h4. HTTP Basic Authentication
HTTP basic authentication is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, +authenticate_or_request_with_http_basic+.
@@ -587,7 +587,7 @@ end
With this in place, you can create namespaced controllers that inherit from +AdminController+. The before filter will thus be run for all actions in those controllers, protecting them with HTTP basic authentication.
-h4. HTTP digest authentication
+h4. HTTP Digest Authentication
HTTP digest authentication is superior to the basic authentication as it does not require the client to send an unencrypted password over the network (though HTTP basic authentication is safe over HTTPS). Using digest authentication with Rails is quite easy and only requires using one method, +authenticate_or_request_with_http_digest+.
@@ -640,7 +640,7 @@ end
The +download_pdf+ action in the example above will call a private method which actually generates the PDF document and returns it as a string. This string will then be streamed to the client as a file download and a filename will be suggested to the user. Sometimes when streaming files to the user, you may not want them to download the file. Take images, for example, which can be embedded into HTML pages. To tell the browser a file is not meant to be downloaded, you can set the +:disposition+ option to "inline". The opposite and default value for this option is "attachment".
-h4. Sending files
+h4. Sending Files
If you want to send a file that already exists on disk, use the +send_file+ method.
@@ -662,7 +662,7 @@ WARNING: Be careful when using data coming from the client (params, cookies, etc
TIP: It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack. Although if you do need the request to go through Rails for some reason, you can set the +:x_sendfile+ option to true, and Rails will let the web server handle sending the file to the user, freeing up the Rails process to do other things. Note that your web server needs to support the +X-Sendfile+ header for this to work.
-h4. RESTful downloads
+h4. RESTful Downloads
While +send_data+ works just fine, if you are creating a RESTful application having separate actions for file downloads is usually not necessary. In REST terminology, the PDF file from the example above can be considered just another representation of the client resource. Rails provides an easy and quite sleek way of doing "RESTful downloads". Here's how you can rewrite the example so that the PDF download is a part of the +show+ action, without any streaming:
@@ -712,7 +712,7 @@ Most likely your application is going to contain bugs or otherwise throw an exce
Rails' default exception handling displays a "500 Server Error" message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application:
-h4. The default 500 and 404 templates
+h4. The Default 500 and 404 Templates
By default a production application will render either a 404 or a 500 error message. These messages are contained in static HTML files in the +public+ folder, in +404.html+ and +500.html+ respectively. You can customize these files to add some extra information and layout, but remember that they are static; i.e. you can't use RHTML or layouts in them, just plain HTML.
View
60 railties/guides/source/action_mailer_basics.textile
@@ -12,9 +12,9 @@ h3. Sending Emails
This section will provide a step-by-step guide to creating a mailer and its views.
-h4. Walkthrough to generating a mailer
+h4. Walkthrough to Generating a Mailer
-h5. Create the mailer:
+h5. Create the Mailer
<shell>
./script/generate mailer UserMailer
@@ -28,7 +28,7 @@ create test/unit/user_mailer_test.rb
So we got the model, the fixtures, and the tests.
-h5. Edit the model:
+h5. Edit the Model
+app/models/user_mailer.rb+ contains an empty mailer:
@@ -60,7 +60,7 @@ Here is a quick explanation of the options presented in the preceding method. Fo
The keys of the hash passed to +body+ become instance variables in the view. Thus, in our example the mailer view will have a +@user+ and a +@url+ instance variables available.
-h5. Create a mailer view
+h5. Create a Mailer View
Create a file called +welcome_email.text.html.erb+ in +app/views/user_mailer/+. This will be the template used for the email, formatted in HTML:
@@ -83,7 +83,7 @@ Create a file called +welcome_email.text.html.erb+ in +app/views/user_mailer/+.
Had we wanted to send text-only emails, the file would have been called +welcome_email.text.plain.erb+. Rails sets the content type of the email to be the one in the filename.
-h5. Wire it up so that the system sends the email when a user signs up
+h5. Wire It Up So That the System Sends the Email When a User Signs Up
There are three ways to achieve this. One is to send the email from the controller that sends the email, another is to put it in a +before_create+ callback in the user model, and the last one is to use an observer on the user model. Whether you use the second or third methods is up to you, but staying away from the first is recommended. Not because it's wrong, but because it keeps your controller clean, and keeps all logic related to the user model within the user model. This way, whichever way a user is created (from a web form, or from an API call, for example), we are guaranteed that the email will be sent.
@@ -112,7 +112,7 @@ end
Notice how we call +deliver_welcome_email+? In Action Mailer we send emails by calling +deliver_&lt;method_name&gt;+. In UserMailer, we defined a method called +welcome_email+, and so we deliver the email by calling +deliver_welcome_email+. The next section will go through how Action Mailer achieves this.
-h4. Action Mailer and dynamic deliver_&lt;method_name&gt; methods
+h4. Action Mailer and Dynamic +deliver_&lt;method_name&gt;+ methods
So how does Action Mailer understand this +deliver_welcome_email+ call? If you read the documentation (http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html), you will find this in the "Sending Emails" section:
@@ -135,7 +135,7 @@ end
Hence, if the method name starts with +deliver_+ followed by any combination of lowercase letters or underscore, +method_missing+ calls +new+ on your mailer class (+UserMailer+ in our example above), sending the combination of lower case letters or underscore, along with the parameters. The resulting object is then sent the +deliver!+ method, which well... delivers it.
-h4. Complete list of Action Mailer user-settable attributes
+h4. Complete List of Action Mailer User-Settable Attributes
|bcc| The BCC addresses of the email|
|body| The body of the email. This is either a hash (in which case it specifies the variables to pass to the template when it is rendered), or a string, in which case it specifies the actual body of the message|
@@ -184,7 +184,7 @@ end
Just like with controller views, use +yield+ to render the view inside the layout.
-h4. Generating URLs in Action Mailer views
+h4. Generating URLs in Action Mailer Views
URLs can be generated in mailer views using +url_for+ or named routes.
Unlike controllers, the mailer instance doesn't have any context about the incoming request so you'll need to provide the +:host+, +:controller+, and +:action+:
@@ -216,7 +216,7 @@ config.action_mailer.default_url_options = { :host => "example.com" }
If you set a default +:host+ for your mailers you need to pass +:only_path => false+ to +url_for+. Otherwise it doesn't get included.
-h4. Sending multipart emails
+h4. Sending Multipart Emails
Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.text.plain.erb+ and +welcome_email.text.html.erb+ in +app/views/user_mailer+, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
@@ -240,7 +240,7 @@ class UserMailer < ActionMailer::Base
end
</ruby>
-h4. Sending emails with attachments
+h4. Sending Emails with Attachments
Attachments can be added by using the +attachment+ method:
@@ -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:
@@ -320,7 +352,7 @@ The following configuration options are best made in one of the environment file
|default_implicit_parts_order|When a message is built implicitly (i.e. multiple parts are assembled from templates which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client and appear last in the mime encoded message. You can also pick a different order from inside a method with implicit_parts_order.|
-h4. Example Action Mailer configuration
+h4. Example Action Mailer Configuration
An example would be:
@@ -352,7 +384,7 @@ ActionMailer::Base.smtp_settings = {
}
</ruby>
-h4. Configure Action Mailer to recognize HAML templates
+h4. Configure Action Mailer to Recognize HAML Templates
In +config/environment.rb+, add the following line:
@@ -386,3 +418,7 @@ end
</ruby>
In the test we send the email and store the returned object in the +email+ variable. We then ensure that it was sent (the first assert), then, in the second batch of assertions, we ensure that the email does indeed contain the what we expect.
+
+h3. Changelog
+
+"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/25
View
8 railties/guides/source/active_record_basics.textile
@@ -29,7 +29,7 @@ h3. Object Relational Mapping
The relation between databases and object-oriented software is called ORM, which is short for "Object Relational Mapping". The purpose of an ORM framework is to minimize the mismatch existent between relational databases and object-oriented software. In applications with a domain model, we have objects that represent both the state of the system and the behavior of the real world elements that were modeled through these objects. Since we need to store the system's state somehow, we can use relational databases, which are proven to be an excellent approach to data management. Usually this may become a very hard thing to do, since we need to create an object-oriented model of everything that lives in the database, from simple columns to complicated relations between different tables. Doing this kind of thing by hand is a tedious and error prone job. This is where an ORM framework comes in.
-h3. ActiveRecord as an ORM framework
+h3. ActiveRecord as an ORM Framework
ActiveRecord gives us several mechanisms, being the most important ones the ability to:
@@ -41,7 +41,7 @@ ActiveRecord gives us several mechanisms, being the most important ones the abil
It's easy to see that the Rails Active Record implementation goes way beyond the basic description of the Active Record Pattern.
-h3. Active Record inside the MVC model
+h3. Active Record Inside the MVC Model
Active Record plays the role of model inside the MVC structure followed by Rails applications. Since model objects should encapsulate both state and logic of your applications, it's ActiveRecord responsibility to deliver you the easiest possible way to recover this data from the database.
@@ -81,7 +81,7 @@ There are also some optional column names that will create additional features t
NOTE: While these column names are optional they are in fact reserved by ActiveRecord. Steer clear of reserved keywords unless you want the extra functionality. For example, "type" is a reserved keyword used to designate a table using Single Table Inheritance. If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling.
-h3. Creating ActiveRecord models
+h3. Creating ActiveRecord Models
It's very easy to create ActiveRecord models. All you have to do is to subclass the ActiveRecord::Base class and you're good to go:
@@ -107,7 +107,7 @@ p.name = "Some Book"
puts p.name # "Some Book"
</ruby>
-h3. Overriding the naming conventions
+h3. Overriding the Naming Conventions
What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions.
View
170 railties/guides/source/active_record_querying.textile
@@ -54,7 +54,7 @@ end
Active Record will perform queries on the database for you and is compatible with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you're using, the Active Record method format will always be the same.
-h3. Retrieving objects from the database
+h3. Retrieving Objects from the Database
To retrieve objects from the database, Active Record provides a class method called +Model.find+. This method allows you to pass arguments into it to perform certain queries on your database without the need of writing raw SQL.
@@ -65,11 +65,11 @@ Primary operation of <tt>Model.find(options)</tt> can be summarized as:
* Instantiate the equivalent Ruby object of the appropriate model for every resulting row.
* Run +after_find+ callbacks if any.
-h4. Retrieving a single object
+h4. Retrieving a Single Object
Active Record lets you retrieve a single object using three different ways.
-h5. Using a primary key
+h5. Using a Primary Key
Using <tt>Model.find(primary_key, options = nil)</tt>, you can retrieve the object corresponding to the supplied _primary key_ and matching the supplied options (if any). For example:
@@ -87,7 +87,7 @@ SELECT * FROM clients WHERE (clients.id = 10)
<tt>Model.find(primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception if no matching record is found.
-h5. Find first
+h5. +first+
<tt>Model.first(options = nil)</tt> finds the first record matched by the supplied options. If no +options+ are supplied, the first matching record is returned. For example:
@@ -106,7 +106,7 @@ SELECT * FROM clients LIMIT 1
NOTE: +Model.find(:first, options)+ is equivalent to +Model.first(options)+
-h5. Find last
+h5. +last+
<tt>Model.last(options = nil)</tt> finds the last record matched by the supplied options. If no +options+ are supplied, the last matching record is returned. For example:
@@ -126,9 +126,9 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
NOTE: +Model.find(:last, options)+ is equivalent to +Model.last(options)+
-h4. Retrieving multiple objects
+h4. Retrieving Multiple Objects
-h5. Using multiple primary keys
+h5. Using Multiple Primary Keys
<tt>Model.find(array_of_primary_key, options = nil)</tt> also accepts an array of _primary keys_. An array of all the matching records for the supplied _primary keys_ is returned. For example:
@@ -166,17 +166,85 @@ SELECT * FROM clients
NOTE: +Model.find(:all, options)+ is equivalent to +Model.all(options)+
+h4. Retrieving Multiple Objects in Batches
+
+Sometimes you need to iterate over a large set of records. For example to send a newsletter to all users, to export some data, etc.
+
+The following may seem very straight forward at first:
+
+<ruby>
+# Very inefficient when users table has thousands of rows.
+User.all.each do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+But if the total number of rows in the table is very large, the above approach may vary from being under performant to just plain impossible.
+
+This is because +User.all+ makes Active Record fetch _the entire table_, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory.
+
+h5. +find_each+
+
+To efficiently iterate over a large table, Active Record provides a batch finder method called +find_each+:
+
+<ruby>
+User.find_each do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+*Configuring the batch size*
+
+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.
+
+To fetch +User+ records in batch size of +5000+:
+
+<ruby>
+User.find_each(:batch_size => 5000) do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+*Starting batch find from a specific primary key*
+
+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.
+
+To send newsletters only to users with the primary key starting from +2000+:
+
+<ruby>
+User.find_each(:batch_size => 5000, :start => 2000) do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+*Additional options*
+
++find_each+ accepts the same options as the regular +find+ method. However, +:order+ and +:limit+ are needed internally and hence not allowed to be passed explicitly.
+
+h5. +find_in_batches+
+
+You can also 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>
+
+The above will yield the supplied block with +1000+ invoices every time.
+
h3. Conditions
-The +find+ method allows you to specify conditions to limit the records returned, representing the WHERE-part of the SQL statement. Conditions can either be specified as a string, array, or hash.
+The +find+ method allows you to specify conditions to limit the records returned, representing the +WHERE+-part of the SQL statement. Conditions can either be specified as a string, array, or hash.
-h4. Pure string conditions
+h4. Pure String Conditions
If you'd like to add conditions to your find, you could just specify them in there, just like +Client.first(:conditions => "orders_count = '2'")+. This will find all clients where the +orders_count+ field's value is 2.
WARNING: Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, +Client.first(:conditions => "name LIKE '%#{params[:name]}%'")+ is not safe. See the next section for the preferred way to handle conditions using an array.
-h4. Array conditions
+h4. Array Conditions
Now what if that number could vary, say as a argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like:
@@ -208,11 +276,11 @@ Client.first(:conditions => "orders_count = #{params[:orders]}")
is because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
-TIP: For more information on the dangers of SQL injection, see the "Ruby on Rails Security Guide":../security.html#_sql_injection.
+TIP: For more information on the dangers of SQL injection, see the "Ruby on Rails Security Guide":security.html#sql-injection.
-h5. Placeholder conditions
+h5. Placeholder Conditions
-Similar to the +(?)+ replacement style of params, you can also specify keys/values hash in your Array conditions:
+Similar to the +(?)+ replacement style of params, you can also specify keys/values hash in your array conditions:
<ruby>
Client.all(:conditions =>
@@ -221,7 +289,7 @@ Client.all(:conditions =>
This makes for clearer readability if you have a large number of variable conditions.
-h5. Range conditions
+h5. Range Conditions
If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the +IN+ SQL statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:
@@ -243,7 +311,7 @@ SELECT * FROM users WHERE (created_at IN
'2008-12-27','2008-12-28','2008-12-29','2008-12-30','2008-12-31'))
</sql>
-h5. Time and Date conditions
+h5. Time and Date Conditions
Things can get *really* messy if you pass in Time objects as it will attempt to compare your field to *every second* in that range:
@@ -280,15 +348,15 @@ Client.all(:conditions =>
["created_at >= ? AND created_at <= ?", params[:start_date], params[:end_date]])
</ruby>
-Just like in Ruby. If you want a shorter syntax be sure to check out the "Hash Conditions":hash-conditions section later on in the guide.
+Just like in Ruby. If you want a shorter syntax be sure to check out the "Hash Conditions":#hash-conditions section later on in the guide.
-h4. Hash conditions
+h4. Hash Conditions
Active Record also allows you to pass in a hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them:
NOTE: Only equality, range and subset checking are possible with Hash conditions.
-h5. Equality conditions
+h5. Equality Conditions
<ruby>
Client.all(:conditions => { :locked => true })
@@ -300,7 +368,7 @@ The field name does not have to be a symbol it can also be a string:
Client.all(:conditions => { 'locked' => true })
</ruby>
-h5. Range conditions
+h5. Range Conditions
The good thing about this is that we can pass in a range for our fields without it generating a large query as shown in the preamble of this section.
@@ -314,9 +382,9 @@ This will find all clients created yesterday by using a +BETWEEN+ SQL statement:
SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
</sql>
-This demonstrates a shorter syntax for the examples in "Array Conditions":#arrayconditions
+This demonstrates a shorter syntax for the examples in "Array Conditions":#array-conditions
-h5. Subset conditions
+h5. Subset Conditions
If you want to find records using the +IN+ expression you can pass an array to the conditions hash:
@@ -330,7 +398,7 @@ This code will generate SQL like this:
SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
</sql>
-h3. Find options
+h3. Find Options
Apart from +:conditions+, +Model.find+ takes a variety of other options via the options hash for customizing the resulting record set.
@@ -370,13 +438,13 @@ Or ordering by multiple fields:
Client.all(:order => "orders_count ASC, created_at DESC")
</ruby>
-h4. Selecting specific fields
+h4. Selecting Specific Fields
By default, <tt>Model.find</tt> selects all the fields from the result set using +select *+.
To select only a subset of fields from the result set, you can specify the subset via +:select+ option on the +find+.
-NOTE: If the +:select+ option is used, all the returning objects will be "read only":#readonlyobjects.
+NOTE: If the +:select+ option is used, all the returning objects will be "read only":#readonly-objects.
<br />
@@ -470,7 +538,7 @@ SELECT * FROM orders GROUP BY date(created_at) HAVING created_at > '2009-01-15'
This will return single order objects for each day, but only for the last month.
-h4. Readonly objects
+h4. Readonly Objects
To explicitly disallow modification/destroyal of the matching records returned by +Model.find+, you could specify the +:readonly+ option as +true+ to the find call.
@@ -488,7 +556,7 @@ client.locked = false
client.save
</ruby>
-h4. Locking records for update
+h4. Locking Records for Update
Locking is helpful for preventing the race conditions when updating records in the database and ensuring atomic updated. Active Record provides two locking mechanism:
@@ -562,31 +630,31 @@ Item.transaction do
end
</ruby>
-h3. Joining tables
+h3. Joining Tables
<tt>Model.find</tt> provides a +:joins+ option for specifying +JOIN+ clauses on the resulting SQL. There multiple different ways to specify the +:joins+ option:
-h4. Using a string SQL fragment
+h4. Using a String SQL Fragment
You can just supply the raw SQL specifying the +JOIN+ clause to the +:joins+ option. For example:
<ruby>
-Client.all(:joins => 'LEFT OUTER JOIN addresses ON addresses.client_id = client.id')
+Client.all(:joins => 'LEFT OUTER JOIN addresses ON addresses.client_id = clients.id')
</ruby>
This will result in the following SQL:
<sql>
-SELECT clients.* FROM clients INNER JOIN addresses ON addresses.client_id = clients.id
+SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id
</sql>
-h4. Using Array/Hash of named associations
+h4. Using Array/Hash of Named Associations
WARNING: This method only works with +INNER JOIN+,
<br />
-Active Record lets you use the names of the "associations":association_basics.html defined on the Model, as a shortcut for specifying the +:joins+ option.
+Active Record lets you use the names of the "associations":association_basics.html defined on the model as a shortcut for specifying the +:joins+ option.
For example, consider the following +Category+, +Post+, +Comments+ and +Guest+ models:
@@ -613,7 +681,7 @@ end
Now all of the following will produce the expected join queries using +INNER JOIN+:
-h5. Joining a single association
+h5. Joining a Single Association
<ruby>
Category.all :joins => :posts
@@ -626,7 +694,7 @@ SELECT categories.* FROM categories
INNER JOIN posts ON posts.category_id = categories.id
</sql>
-h5. Joining multiple associations
+h5. Joining Multiple Associations
<ruby>
Post.all :joins => [:category, :comments]
@@ -640,21 +708,21 @@ SELECT posts.* FROM posts
INNER JOIN comments ON comments.post_id = posts.id
</sql>
-h5. Joining nested associations (single level)
+h5. Joining Nested Associations (Single Level)
<ruby>
Post.all :joins => {:comments => :guest}
</ruby>
-h5. Joining nested associations (multiple level)
+h5. Joining Nested Associations (Multiple Level)
<ruby>
Category.all :joins => {:posts => [{:comments => :guest}, :tags]}
</ruby>
-h4. Specifying conditions on the joined tables
+h4. Specifying Conditions on the Joined Tables
-You can specify conditions on the joined tables using the regular "Array":#arrayconditions and "String":#purestringconditions conditions. "Hash conditions":#hashconditions provides a special syntax for specifying conditions for the joined tables:
+You can specify conditions on the joined tables using the regular "Array":#array-conditions and "String":#pure-string-conditions conditions. "Hash conditions":#hash-conditions provides a special syntax for specifying conditions for the joined tables:
<ruby>
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
@@ -670,7 +738,7 @@ Client.all :joins => :orders, :conditions => {:orders => {:created_at => time_ra
This will find all clients who have orders that were created yesterday, again using a +BETWEEN+ SQL expression.
-h3. Eager loading associations
+h3. Eager Loading Associations
Eager loading is the mechanism for loading the associated records of the objects returned by +Model.find+ using as few queries as possible.
@@ -710,37 +778,37 @@ SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
</sql>
-h4. Eager loading multiple associations
+h4. Eager Loading Multiple Associations
-Active Record lets you eager load any possible number of associations with a single +Model.find+ call by using Array, Hash or a nested Hash of Array/Hash with +:include+ find option.
+Active Record lets you eager load any possible number of associations with a single +Model.find+ call by using an array, hash, or a nested hash of array/hash with the +:include+ option.
-h5. Array of multiple associations
+h5. Array of Multiple Associations
<ruby>
Post.all :include => [:category, :comments]
</ruby>
This loads all the posts and the associated category and comments for each post.
-h5. Nested assocaitions hash
+h5. Nested Associations Hash
<ruby>
Category.find 1, :include => {:posts => [{:comments => :guest}, :tags]}
</ruby>
The above code finds the category with id 1 and eager loads all the posts associated with the found category. Additionally, it will also eager load every posts' tags and comments. Every comment's guest association will get eager loaded as well.
-h4. Specifying conditions on eager loaded associations
+h4. Specifying Conditions on Eager Loaded Associations
-Even though Active Record lets you specify conditions on the eager loaded associations just like +:joins+, the recommended way is to use ":joins":#joiningtables instead.
+Even though Active Record lets you specify conditions on the eager loaded associations just like +:joins+, the recommended way is to use ":joins":#joining-tables instead.
-h3. Dynamic finders
+h3. Dynamic Finders
-For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your Client model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the Client model, you also get +find_by_locked+ and +find_all_by_locked+.
+For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your +Client+ model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+.
You can do +find_last_by_*+ methods too which will find the last record matching your argument.
-You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an ActiveRecord::RecordNotFound error if they do not return any records, like +Client.find_by_name!("Ryan")+
+You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+
If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked("Ryan", true)+.
@@ -761,9 +829,9 @@ COMMIT
client = Client.find_or_initialize_by_name('Ryan')
</ruby>
-will either assign an existing client object with the name 'Ryan' to the client local variable, or initialize a new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
+will either assign an existing client object with the name "Ryan" to the client local variable, or initialize a new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
-h3. Finding By SQL
+h3. Finding by SQL
If you'd like to use your own SQL to find records in a table you can use +find_by_sql+. The +find_by_sql+ method will return an array of objects even the underlying query returns just a single record. For example you could run this query:
@@ -775,7 +843,7 @@ Client.find_by_sql("SELECT * FROM clients
+find_by_sql+ provides you with a simple way of making custom calls to the database and retrieving instantiated objects.
-h3. select_all
+h3. +select_all+
<tt>find_by_sql</tt> has a close relative called +connection#select_all+. +select_all+ will retrieve objects from the database using custom SQL just like +find_by_sql+ but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
View
543 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
@@ -57,11 +57,11 @@ We can see how it works by looking at some script/console output:
=> false
</shell>
-Creating and saving a new record will send an SQL +INSERT+ operation to the database. Updating an existing record will send an SQL +UPDATE+ operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not trigger the +INSERT+ or +UPDATE+ operation. This helps to avoid storing an object in the database that's invalid. You can choose to have specific validations run when an object is created, saved, or updated.
+Creating and saving a new record will send an SQL +INSERT+ operation to the database. Updating an existing record will send an SQL +UPDATE+ operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the +INSERT+ or +UPDATE+ operation. This helps to avoid storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated.
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. It only checks to see whether there are errors found on an individual attribute of the object.
<ruby>
class Person < ActiveRecord::Base
@@ -146,27 +152,27 @@ 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":#working-with-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 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
validates_acceptance_of :terms_of_service
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.
@@ -187,15 +193,15 @@ class Library < ActiveRecord::Base
end
</ruby>
-This validation will work with all the association types.
+This validation will work with all of 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,28 +366,28 @@ 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
validates_uniqueness_of :email
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