Skip to content
Browse files

bringing over latest from master

  • Loading branch information...
2 parents d8b9011 + 7171161 commit ee9c950f2fe0c7953f0a9ad6a53439da7a4e89bc @brianmario brianmario committed Aug 9, 2010
Showing with 2,061 additions and 1,283 deletions.
  1. +1 −1 Gemfile
  2. +28 −0 Rakefile
  3. +2 −0 actionmailer/lib/action_mailer/deprecated_api.rb
  4. +12 −0 actionmailer/test/base_test.rb
  5. +1 −1 actionpack/README.rdoc
  6. +12 −0 actionpack/lib/abstract_controller/base.rb
  7. +11 −2 actionpack/lib/action_dispatch/middleware/best_standards_support.rb
  8. +1 −0 actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
  9. +4 −1 actionpack/lib/action_dispatch/routing/mapper.rb
  10. +6 −3 actionpack/lib/action_dispatch/routing/route_set.rb
  11. +1 −0 actionpack/lib/action_dispatch/testing/integration.rb
  12. +1 −1 actionpack/lib/action_view/helpers/form_tag_helper.rb
  13. +2 −0 actionpack/lib/action_view/helpers/sanitize_helper.rb
  14. +3 −0 actionpack/lib/action_view/helpers/text_helper.rb
  15. +14 −0 actionpack/test/abstract/abstract_controller_test.rb
  16. +1 −0 actionpack/test/controller/resources_test.rb
  17. +25 −30 actionpack/test/dispatch/routing_test.rb
  18. +1 −1 actionpack/test/template/form_helper_test.rb
  19. +1 −1 actionpack/test/template/form_tag_helper_test.rb
  20. +1 −1 activemodel/lib/active_model/attribute_methods.rb
  21. +2 −7 activemodel/lib/active_model/dirty.rb
  22. +3 −7 activemodel/lib/active_model/errors.rb
  23. +2 −0 activemodel/lib/active_model/serialization.rb
  24. +9 −9 activemodel/lib/active_model/serializers/json.rb
  25. +4 −2 activemodel/lib/active_model/validations.rb
  26. +7 −4 activemodel/lib/active_model/validations/length.rb
  27. +1 −1 activemodel/lib/active_model/validator.rb
  28. +85 −5 activemodel/test/cases/dirty_test.rb
  29. +65 −0 activemodel/test/cases/errors_test.rb
  30. +14 −0 activemodel/test/cases/validations/length_validation_test.rb
  31. +64 −51 activerecord/lib/active_record/aggregations.rb
  32. +9 −7 activerecord/lib/active_record/association_preload.rb
  33. +274 −190 activerecord/lib/active_record/associations.rb
  34. +4 −3 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
  35. +1 −1 activerecord/lib/active_record/associations/has_many_association.rb
  36. +4 −3 activerecord/lib/active_record/associations/has_many_through_association.rb
  37. +2 −1 activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
  38. +2 −2 activerecord/lib/active_record/attribute_methods/write.rb
  39. +14 −20 activerecord/lib/active_record/autosave_association.rb
  40. +146 −101 activerecord/lib/active_record/base.rb
  41. +35 −31 activerecord/lib/active_record/callbacks.rb
  42. +5 −4 activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
  43. +1 −1 activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
  44. +1 −1 activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
  45. +7 −5 activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
  46. +2 −0 activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
  47. +2 −1 activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
  48. +8 −4 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
  49. +2 −2 activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
  50. +2 −2 activerecord/lib/active_record/dynamic_finder_match.rb
  51. +9 −5 activerecord/lib/active_record/errors.rb
  52. +30 −20 activerecord/lib/active_record/fixtures.rb
  53. +12 −9 activerecord/lib/active_record/named_scope.rb
  54. +2 −2 activerecord/lib/active_record/observer.rb
  55. +3 −3 activerecord/lib/active_record/persistence.rb
  56. +1 −1 activerecord/lib/active_record/railties/databases.rake
  57. +11 −7 activerecord/lib/active_record/relation.rb
  58. +4 −4 activerecord/lib/active_record/relation/batches.rb
  59. +53 −27 activerecord/lib/active_record/relation/calculations.rb
  60. +18 −9 activerecord/lib/active_record/relation/finder_methods.rb
  61. +3 −3 activerecord/lib/active_record/relation/query_methods.rb
  62. +1 −1 activerecord/lib/active_record/schema.rb
  63. +9 −9 activerecord/lib/active_record/schema_dumper.rb
  64. +2 −1 activerecord/lib/active_record/timestamp.rb
  65. +4 −3 activerecord/lib/active_record/validations/associated.rb
  66. +13 −9 activerecord/lib/active_record/validations/uniqueness.rb
  67. +22 −0 activerecord/test/cases/associations_test.rb
  68. +276 −2 activerecord/test/cases/attribute_methods_test.rb
  69. +0 −542 activerecord/test/cases/base_test.rb
  70. +8 −0 activerecord/test/cases/finder_test.rb
  71. +7 −0 activerecord/test/cases/method_scoping_test.rb
  72. +6 −0 activerecord/test/cases/named_scope_test.rb
  73. +113 −0 activerecord/test/cases/persistence_test.rb
  74. +15 −3 activerecord/test/cases/relations_test.rb
  75. +134 −0 activerecord/test/cases/serialization_test.rb
  76. +5 −1 activeresource/lib/active_resource/http_mock.rb
  77. +14 −4 activesupport/lib/active_support/callbacks.rb
  78. +35 −0 activesupport/lib/active_support/concern.rb
  79. +22 −0 activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
  80. +15 −7 activesupport/lib/active_support/dependencies.rb
  81. +10 −1 activesupport/lib/active_support/ordered_hash.rb
  82. +16 −0 activesupport/lib/active_support/ordered_options.rb
  83. +19 −12 activesupport/test/core_ext/date_time_ext_test.rb
  84. +25 −10 railties/guides/source/action_controller_overview.textile
  85. +1 −1 railties/guides/source/active_record_querying.textile
  86. +133 −16 railties/guides/source/active_support_core_extensions.textile
  87. +1 −1 railties/guides/source/api_documentation_guidelines.textile
  88. +1 −1 railties/guides/source/form_helpers.textile
  89. +2 −2 railties/guides/source/getting_started.textile
  90. +6 −0 railties/guides/source/routing.textile
  91. +1 −1 railties/lib/rails/application.rb
  92. +1 −1 railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
  93. +4 −0 railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
  94. +2 −5 railties/lib/rails/rack/log_tailer.rb
  95. +21 −21 railties/lib/rails/railtie.rb
  96. +65 −31 railties/test/application/routing_test.rb
  97. +1 −1 railties/test/generators/app_generator_test.rb
View
2 Gemfile
@@ -11,7 +11,7 @@ gem "rails", :path => File.dirname(__FILE__)
gem "rake", ">= 0.8.7"
gem "mocha", ">= 0.9.8"
gem "rdoc", ">= 2.5.9"
-gem "horo"
+gem "horo", ">= 1.0.1"
# AS
gem "memcache-client", ">= 1.8.5"
View
28 Rakefile
@@ -5,6 +5,31 @@ require 'rake'
require 'rdoc/task'
require 'rake/gempackagetask'
+# RDoc skips some files in the Rails tree due to its binary? predicate. This is a quick
+# hack for edge docs, until we decide which is the correct way to address this issue.
+# If not fixed in RDoc itself, via an option or something, we should probably move this
+# to railties and use it also in doc:rails.
+def hijack_rdoc!
+ require "rdoc/parser"
+ class << RDoc::Parser
+ def binary?(file)
+ s = File.read(file, 1024) or return false
+
+ if s[0, 2] == Marshal.dump('')[0, 2] then
+ true
+ elsif file =~ /erb\.rb$/ then
+ false
+ elsif s.index("\x00") then # ORIGINAL is s.scan(/<%|%>/).length >= 4 || s.index("\x00")
+ true
+ elsif 0.respond_to? :fdiv then
+ s.count("^ -~\t\r\n").fdiv(s.size) > 0.3
+ else # HACK 1.8.6
+ (s.count("^ -~\t\r\n").to_f / s.size) > 0.3
+ end
+ end
+ end
+end
+
PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties)
desc 'Run all tests by default'
@@ -63,6 +88,8 @@ end
desc "Generate documentation for the Rails framework"
RDoc::Task.new do |rdoc|
+ hijack_rdoc!
+
rdoc.rdoc_dir = 'doc/rdoc'
rdoc.title = "Ruby on Rails Documentation"
@@ -90,6 +117,7 @@ RDoc::Task.new do |rdoc|
rdoc.rdoc_files.include('actionpack/README.rdoc')
rdoc.rdoc_files.include('actionpack/CHANGELOG')
+ rdoc.rdoc_files.include('actionpack/lib/abstract_controller/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_view/**/*.rb')
View
2 actionmailer/lib/action_mailer/deprecated_api.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/try'
+
module ActionMailer
# This is the API which is deprecated and is going to be removed on Rails 3.1 release.
# Part of the old API will be deprecated after 3.1, for a smoother deprecation process.
View
12 actionmailer/test/base_test.rb
@@ -507,6 +507,18 @@ def self.delivering_email(mail)
assert_equal("Thanks for signing up this afternoon", mail.subject)
end
+ test "action methods should be refreshed after defining new method" do
+ class FooMailer < ActionMailer::Base
+ # this triggers action_methods
+ self.respond_to?(:foo)
+
+ def notify
+ end
+ end
+
+ assert_equal ["notify"], FooMailer.action_methods
+ end
+
protected
# Execute the block setting the given values and restoring old values after
View
2 actionpack/README.rdoc
@@ -1,6 +1,6 @@
= Action Pack -- From request to response
-Action Pack is a framework for handling and responding to web requests. It it
+Action Pack is a framework for handling and responding to web requests. It
provides mechanisms for *routing* (mapping request URLs to actions), defining
*controllers* that implement actions, and generating responses by rendering
*views*, which are templates of various formats. In short, Action Pack
View
12 actionpack/lib/abstract_controller/base.rb
@@ -72,6 +72,13 @@ def action_methods
end
end
+ # action_methods are cached and there is sometimes need to refresh
+ # them. clear_action_methods! allows you to do that, so next time
+ # you run action_methods, they will be recalculated
+ def clear_action_methods!
+ @action_methods = nil
+ end
+
# Returns the full controller name, underscored, without the ending Controller.
# For instance, MyApp::MyPostsController would return "my_app/my_posts" for
# controller_name.
@@ -81,6 +88,11 @@ def action_methods
def controller_path
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
end
+
+ def method_added(name)
+ super
+ clear_action_methods!
+ end
end
abstract!
View
13 actionpack/lib/action_dispatch/middleware/best_standards_support.rb
@@ -1,12 +1,21 @@
module ActionDispatch
class BestStandardsSupport
- def initialize(app)
+ def initialize(app, type = true)
@app = app
+
+ @header = case type
+ when true
+ "IE=Edge,chrome=1"
+ when :builtin
+ "IE=Edge"
+ when false
+ nil
+ end
end
def call(env)
status, headers, body = @app.call(env)
- headers["X-UA-Compatible"] = "IE=Edge,chrome=1"
+ headers["X-UA-Compatible"] = @header
[status, headers, body]
end
end
View
1 actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/with_options'
+require 'active_support/core_ext/object/try'
module ActionDispatch
module Routing
View
5 actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,3 +1,4 @@
+require 'erb'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/object/blank'
@@ -277,7 +278,6 @@ def redirect(*args, &block)
path = args.shift || block
path_proc = path.is_a?(Proc) ? path : proc { |params| path % params }
status = options[:status] || 301
- body = 'Moved Permanently'
lambda do |env|
req = Request.new(env)
@@ -290,11 +290,14 @@ def redirect(*args, &block)
uri.host ||= req.host
uri.port ||= req.port unless req.port == 80
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
+
headers = {
'Location' => uri.to_s,
'Content-Type' => 'text/html',
'Content-Length' => body.length.to_s
}
+
[ status, headers, [body] ]
end
end
View
9 actionpack/lib/action_dispatch/routing/route_set.rb
@@ -392,10 +392,9 @@ def handle_nil_action!
end
def generate
- error = ActionController::RoutingError.new("No route matches #{options.inspect}")
path, params = @set.set.generate(:path_info, named_route, options, recall, opts)
- raise error unless path
+ raise_routing_error unless path
params.reject! {|k,v| !v }
@@ -404,7 +403,7 @@ def generate
path << "?#{params.to_query}" if params.any?
"#{script_name}#{path}"
rescue Rack::Mount::RoutingError
- raise error
+ raise_routing_error
end
def opts
@@ -421,6 +420,10 @@ def opts
{:parameterize => parameterize}
end
+ def raise_routing_error
+ raise ActionController::RoutingError.new("No route matches #{options.inspect}")
+ end
+
def different_controller?
return false unless current_controller
controller.to_param != current_controller.to_param
View
1 actionpack/lib/action_dispatch/testing/integration.rb
@@ -1,6 +1,7 @@
require 'stringio'
require 'uri'
require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/object/try'
require 'rack/test'
require 'test/unit/assertions'
View
2 actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -538,7 +538,7 @@ def html_options_for_form(url_for_options, options, *parameters_for_url)
def extra_tags_for_form(html_options)
snowman_tag = tag(:input, :type => "hidden",
- :name => "_snowman", :value => "&#9731;".html_safe)
+ :name => "_e", :value => "&#9731;".html_safe)
method = html_options.delete("method").to_s
View
2 actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/object/try'
require 'action_controller/vendor/html-scanner'
require 'action_view/helpers/tag_helper'
@@ -7,6 +8,7 @@ module Helpers #:nodoc:
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
# These helper methods extend Action View making them callable within your template files.
module SanitizeHelper
+ extend ActiveSupport::Concern
# This +sanitize+ helper will html encode all tags and strip all attributes that
# aren't specifically allowed.
#
View
3 actionpack/lib/action_view/helpers/text_helper.rb
@@ -10,6 +10,9 @@ module Helpers #:nodoc:
# your views. These helper methods extend Action View making them callable
# within your template files.
module TextHelper
+ extend ActiveSupport::Concern
+
+ include SanitizeHelper
# The preferred method of outputting text in your views is to use the
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
# do not operate as expected in an eRuby code block. If you absolutely must
View
14 actionpack/test/abstract/abstract_controller_test.rb
@@ -250,5 +250,19 @@ def assert_dispatch(klass, body = "success", action = :index)
end
end
+ class Me6 < AbstractController::Base
+ self.action_methods
+
+ def index
+ end
+ end
+
+ class TestActionMethodsReloading < ActiveSupport::TestCase
+
+ test "action_methods should be reloaded after defining a new method" do
+ assert_equal ["index"], Me6.action_methods
+ end
+ end
+
end
end
View
1 actionpack/test/controller/resources_test.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/object/try'
require 'abstract_unit'
class ResourcesController < ActionController::Base
View
55 actionpack/test/dispatch/routing_test.rb
@@ -1,3 +1,4 @@
+require 'erb'
require 'abstract_unit'
require 'controller/fake_controllers'
@@ -56,7 +57,7 @@ def self.matches?(request)
match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" }
match 'account/proc_req' => redirect {|params, req| "/#{req.method}" }
- match 'account/google' => redirect('http://www.google.com/')
+ match 'account/google' => redirect('http://www.google.com/', :status => 302)
match 'openid/login', :via => [:get, :post], :to => "openid#login"
@@ -501,28 +502,22 @@ def test_login
def test_login_redirect
with_test_routes do
get '/account/login'
- assert_equal 301, @response.status
- assert_equal 'http://www.example.com/login', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.example.com/login'
end
end
def test_logout_redirect_without_to
with_test_routes do
assert_equal '/account/logout', logout_redirect_path
get '/account/logout'
- assert_equal 301, @response.status
- assert_equal 'http://www.example.com/logout', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.example.com/logout'
end
end
def test_namespace_redirect
with_test_routes do
get '/private'
- assert_equal 301, @response.status
- assert_equal 'http://www.example.com/private/index', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.example.com/private/index'
end
end
@@ -586,27 +581,21 @@ def test_member_on_resource
def test_redirect_modulo
with_test_routes do
get '/account/modulo/name'
- assert_equal 301, @response.status
- assert_equal 'http://www.example.com/names', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.example.com/names'
end
end
def test_redirect_proc
with_test_routes do
get '/account/proc/person'
- assert_equal 301, @response.status
- assert_equal 'http://www.example.com/people', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.example.com/people'
end
end
def test_redirect_proc_with_request
with_test_routes do
get '/account/proc_req'
- assert_equal 301, @response.status
- assert_equal 'http://www.example.com/GET', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.example.com/GET'
end
end
@@ -1203,22 +1192,18 @@ def test_convention_with_explicit_end
end
end
- def test_redirect_with_complete_url
+ def test_redirect_with_complete_url_and_status
with_test_routes do
get '/account/google'
- assert_equal 301, @response.status
- assert_equal 'http://www.google.com/', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.google.com/', 302
end
end
def test_redirect_with_port
previous_host, self.host = self.host, 'www.example.com:3000'
with_test_routes do
get '/account/login'
- assert_equal 301, @response.status
- assert_equal 'http://www.example.com:3000/login', @response.headers['Location']
- assert_equal 'Moved Permanently', @response.body
+ verify_redirect 'http://www.example.com:3000/login'
end
ensure
self.host = previous_host
@@ -1899,8 +1884,18 @@ def test_only_scope_should_override_parent_except_scope
end
end
- private
- def with_test_routes
- yield
- end
+private
+ def with_test_routes
+ yield
+ end
+
+ def verify_redirect(url, status=301)
+ assert_equal status, @response.status
+ assert_equal url, @response.headers['Location']
+ assert_equal expected_redirect_body(url), @response.body
+ end
+
+ def expected_redirect_body(url)
+ %(<html><body>You are being <a href="#{ERB::Util.h(url)}">redirected</a>.</body></html>)
+ end
end
View
2 actionpack/test/template/form_helper_test.rb
@@ -1513,7 +1513,7 @@ def test_form_for_with_labelled_builder
def snowman(method = nil)
txt = %{<div style="margin:0;padding:0;display:inline">}
- txt << %{<input name="_snowman" type="hidden" value="&#9731;" />}
+ txt << %{<input name="_e" type="hidden" value="&#9731;" />}
txt << %{<input name="_method" type="hidden" value="#{method}" />} if method
txt << %{</div>}
end
View
2 actionpack/test/template/form_tag_helper_test.rb
@@ -12,7 +12,7 @@ def snowman(options = {})
method = options[:method]
txt = %{<div style="margin:0;padding:0;display:inline">}
- txt << %{<input name="_snowman" type="hidden" value="&#9731;" />}
+ txt << %{<input name="_e" type="hidden" value="&#9731;" />}
txt << %{<input name="_method" type="hidden" value="#{method}" />} if method
txt << %{</div>}
end
View
2 activemodel/lib/active_model/attribute_methods.rb
@@ -283,7 +283,7 @@ def #{method_name}(*args)
@attribute_methods_generated = true
end
- # Removes all the preiously dynamically defined methods from the class
+ # Removes all the previously dynamically defined methods from the class
def undefine_attribute_methods
generated_attribute_methods.module_eval do
instance_methods.each { |m| undef_method(m) }
View
9 activemodel/lib/active_model/dirty.rb
@@ -37,12 +37,13 @@ module ActiveModel
# end
#
# def name=(val)
- # name_will_change!
+ # name_will_change! unless val == @name
# @name = val
# end
#
# def save
# @previously_changed = changes
+ # @changed_attributes.clear
# end
#
# end
@@ -77,12 +78,6 @@ module ActiveModel
# person.changed # => ['name']
# person.changes # => { 'name' => ['Bill', 'Bob'] }
#
- # Resetting an attribute returns it to its original state:
- # person.reset_name! # => 'Bill'
- # person.changed? # => false
- # person.name_changed? # => false
- # person.name # => 'Bill'
- #
# If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
# to mark that the attribute is changing. Otherwise ActiveModel can't track changes to
# in-place attributes.
View
10 activemodel/lib/active_model/errors.rb
@@ -37,11 +37,11 @@ module ActiveModel
# send(attr)
# end
#
- # def ErrorsPerson.human_attribute_name(attr, options = {})
+ # def Person.human_attribute_name(attr, options = {})
# attr
# end
#
- # def ErrorsPerson.lookup_ancestors
+ # def Person.lookup_ancestors
# [self]
# end
#
@@ -86,11 +86,7 @@ def initialize(base)
# p.errors[:name] # => ["can not be nil"]
# p.errors['name'] # => ["can not be nil"]
def [](attribute)
- if errors = get(attribute.to_sym)
- errors
- else
- set(attribute.to_sym, [])
- end
+ get(attribute.to_sym) || set(attribute.to_sym, [])
end
# Adds to the supplied attribute the supplied error message.
View
2 activemodel/lib/active_model/serialization.rb
@@ -61,6 +61,8 @@ module ActiveModel
# person.serializable_hash # => {"name"=>"Bob"}
# person.to_json # => "{\"name\":\"Bob\"}"
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
+ #
+ # Valid options are <tt>:only</tt>, <tt>:except</tt> and <tt>:methods</tt> .
module Serialization
def serializable_hash(options = nil)
options ||= {}
View
18 activemodel/lib/active_model/serializers/json.rb
@@ -19,8 +19,8 @@ module JSON
# passed through +options+.
#
# The option <tt>ActiveModel::Base.include_root_in_json</tt> controls the
- # top-level behavior of <tt>to_json</tt>. It is <tt>true</tt> by default. When it is <tt>true</tt>,
- # <tt>to_json</tt> will emit a single root node named after the object's type. For example:
+ # top-level behavior of +to_json+. If true (the default) +to_json+ will
+ # emit a single root node named after the object's type. For example:
#
# konata = User.find(1)
# konata.to_json
@@ -32,11 +32,11 @@ module JSON
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true}
#
- # The remainder of the examples in this section assume include_root_in_json is set to
- # <tt>false</tt>.
+ # The remainder of the examples in this section assume +include_root_in_json+
+ # is false.
#
- # Without any +options+, the returned JSON string will include all
- # the model's attributes. For example:
+ # Without any +options+, the returned JSON string will include all the model's
+ # attributes. For example:
#
# konata = User.find(1)
# konata.to_json
@@ -52,22 +52,22 @@ module JSON
# konata.to_json(:except => [ :id, :created_at, :age ])
# # => {"name": "Konata Izumi", "awesome": true}
#
- # To include any methods on the model, use <tt>:methods</tt>.
+ # To include the result of some method calls on the model use <tt>:methods</tt>:
#
# konata.to_json(:methods => :permalink)
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true,
# "permalink": "1-konata-izumi"}
#
- # To include associations, use <tt>:include</tt>.
+ # To include associations use <tt>:include</tt>:
#
# konata.to_json(:include => :posts)
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true,
# "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
# {"id": 2, author_id: 1, "title": "So I was thinking"}]}
#
- # 2nd level and higher order associations work as well:
+ # Second level and higher order associations work as well:
#
# konata.to_json(:include => { :posts => {
# :include => { :comments => {
View
6 activemodel/lib/active_model/validations.rb
@@ -118,11 +118,13 @@ def validates_each(*attr_names, &block)
# end
#
def validate(*args, &block)
- options = args.last
- if options.is_a?(Hash) && options.key?(:on)
+ options = args.extract_options!
+ if options.key?(:on)
+ options = options.dup
options[:if] = Array.wrap(options[:if])
options[:if] << "validation_context == :#{options[:on]}"
end
+ args << options
set_callback(:validate, *args, &block)
end
View
11 activemodel/lib/active_model/validations/length.rb
@@ -40,8 +40,6 @@ def validate_each(record, attribute, value)
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
- default_message = options[MESSAGES[key]]
- options[:message] ||= default_message if default_message
valid_value = if key == :maximum
value.nil? || value.size.send(validity_check, check_value)
@@ -51,8 +49,13 @@ def validate_each(record, attribute, value)
next if valid_value
- record.errors.add(attribute, MESSAGES[key],
- options.except(*RESERVED_OPTIONS).merge!(:count => check_value))
+ errors_options = options.except(*RESERVED_OPTIONS)
+ errors_options[:count] = check_value
+
+ default_message = options[MESSAGES[key]]
+ errors_options[:message] ||= default_message if default_message
+
+ record.errors.add(attribute, MESSAGES[key], errors_options)
end
end
end
View
2 activemodel/lib/active_model/validator.rb
@@ -111,7 +111,7 @@ def self.kind
# Accepts options that will be made available through the +options+ reader.
def initialize(options)
- @options = options
+ @options = options.freeze
end
# Return the kind for this validator.
View
90 activemodel/test/cases/dirty_test.rb
@@ -3,10 +3,11 @@
class DirtyTest < ActiveModel::TestCase
class DirtyModel
include ActiveModel::Dirty
- define_attribute_methods [:name]
+ define_attribute_methods [:name, :color]
def initialize
@name = nil
+ @color = nil
end
def name
@@ -17,13 +18,92 @@ def name=(val)
name_will_change!
@name = val
end
+
+ def color
+ @color
+ end
+
+ def color=(val)
+ color_will_change! unless val == @color
+ @color = val
+ end
+
+ def save
+ @previously_changed = changes
+ @changed_attributes.clear
+ end
+ end
+
+ setup do
+ @model = DirtyModel.new
+ end
+
+ test "setting attribute will result in change" do
+ assert !@model.changed?
+ assert !@model.name_changed?
+ @model.name = "Ringo"
+ assert @model.changed?
+ assert @model.name_changed?
+ end
+
+ test "list of changed attributes" do
+ assert_equal [], @model.changed
+ @model.name = "Paul"
+ assert_equal ['name'], @model.changed
+ end
+
+ test "changes to attribute values" do
+ assert !@model.changes['name']
+ @model.name = "John"
+ assert_equal [nil, "John"], @model.changes['name']
end
test "changes accessible through both strings and symbols" do
- model = DirtyModel.new
- model.name = "David"
- assert_not_nil model.changes[:name]
- assert_not_nil model.changes['name']
+ @model.name = "David"
+ assert_not_nil @model.changes[:name]
+ assert_not_nil @model.changes['name']
+ end
+
+ test "attribute mutation" do
+ @model.instance_variable_set("@name", "Yam")
+ assert !@model.name_changed?
+ @model.name.replace("Hadad")
+ assert !@model.name_changed?
+ @model.name_will_change!
+ @model.name.replace("Baal")
+ assert @model.name_changed?
+ end
+
+ test "resetting attribute" do
+ @model.name = "Bob"
+ @model.reset_name!
+ assert_nil @model.name
+ #assert !@model.name_changed #Doesn't work yet
+ end
+
+ test "setting color to same value should not result in change being recorded" do
+ @model.color = "red"
+ assert @model.color_changed?
+ @model.save
+ assert !@model.color_changed?
+ assert !@model.changed?
+ @model.color = "red"
+ assert !@model.color_changed?
+ assert !@model.changed?
+ end
+
+ test "saving should reset model's changed status" do
+ @model.name = "Alf"
+ assert @model.changed?
+ @model.save
+ assert !@model.changed?
+ assert !@model.name_changed?
+ end
+
+ test "saving should preserve previous changes" do
+ @model.name = "Jericho Cane"
+ @model.save
+ assert_equal [nil, "Jericho Cane"], @model.previous_changes['name']
end
end
View
65 activemodel/test/cases/errors_test.rb
@@ -0,0 +1,65 @@
+require "cases/helper"
+
+class ErrorsTest < ActiveModel::TestCase
+ class Person
+ extend ActiveModel::Naming
+ def initialize
+ @errors = ActiveModel::Errors.new(self)
+ end
+
+ attr_accessor :name
+ attr_reader :errors
+
+ def validate!
+ errors.add(:name, "can not be nil") if name == nil
+ end
+
+ def read_attribute_for_validation(attr)
+ send(attr)
+ end
+
+ def self.human_attribute_name(attr, options = {})
+ attr
+ end
+
+ def self.lookup_ancestors
+ [self]
+ end
+
+ end
+
+ test "method validate! should work" do
+ person = Person.new
+ person.validate!
+ assert_equal ["name can not be nil"], person.errors.full_messages
+ assert_equal ["can not be nil"], person.errors[:name]
+
+ end
+
+ test 'should be able to assign error' do
+ person = Person.new
+ person.errors[:name] = 'should not be nil'
+ assert_equal ["should not be nil"], person.errors[:name]
+ end
+
+ test 'should be able to add an error on an attribute' do
+ person = Person.new
+ person.errors.add(:name, "can not be blank")
+ assert_equal ["can not be blank"], person.errors[:name]
+ end
+
+ test 'should respond to size' do
+ person = Person.new
+ person.errors.add(:name, "can not be blank")
+ assert_equal 1, person.errors.size
+ end
+
+ test 'to_a should return an array' do
+ person = Person.new
+ person.errors.add(:name, "can not be blank")
+ person.errors.add(:name, "can not be nil")
+ assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a
+
+ end
+
+end
View
14 activemodel/test/cases/validations/length_validation_test.rb
@@ -229,6 +229,20 @@ def test_validates_length_of_custom_errors_for_maximum_with_too_long
assert_equal ["hoo 5"], t.errors["title"]
end
+ def test_validates_length_of_custom_errors_for_both_too_short_and_too_long
+ Topic.validates_length_of :title, :minimum => 3, :maximum => 5, :too_short => 'too short', :too_long => 'too long'
+
+ t = Topic.new(:title => 'a')
+ assert t.invalid?
+ assert t.errors[:title].any?
+ assert_equal ['too short'], t.errors['title']
+
+ t = Topic.new(:title => 'aaaaaa')
+ assert t.invalid?
+ assert t.errors[:title].any?
+ assert_equal ['too long'], t.errors['title']
+ end
+
def test_validates_length_of_custom_errors_for_is_with_message
Topic.validates_length_of( :title, :is=>5, :message=>"boo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
View
115 activerecord/lib/active_record/aggregations.rb
@@ -9,11 +9,13 @@ def clear_aggregation_cache #:nodoc:
end unless self.new_record?
end
- # Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
- # as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
- # composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
- # attributes of the entity object (when the entity is initialized either as a new object or from finding an existing object)
- # and how it can be turned back into attributes (when the entity is saved to the database). Example:
+ # Active Record implements aggregation through a macro-like class method called +composed_of+
+ # for representing attributes as value objects. It expresses relationships like "Account [is]
+ # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
+ # to the macro adds a description of how the value objects are created from the attributes of
+ # the entity object (when the entity is initialized either as a new object or from finding an
+ # existing object) and how it can be turned back into attributes (when the entity is saved to
+ # the database).
#
# class Customer < ActiveRecord::Base
# composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
@@ -68,9 +70,10 @@ def clear_aggregation_cache #:nodoc:
# end
# end
#
- # Now it's possible to access attributes from the database through the value objects instead. If you choose to name the
- # composition the same as the attribute's name, it will be the only way to access that attribute. That's the case with our
- # +balance+ attribute. You interact with the value objects just like you would any other attribute, though:
+ # Now it's possible to access attributes from the database through the value objects instead. If
+ # you choose to name the composition the same as the attribute's name, it will be the only way to
+ # access that attribute. That's the case with our +balance+ attribute. You interact with the value
+ # objects just like you would any other attribute, though:
#
# customer.balance = Money.new(20) # sets the Money value object and the attribute
# customer.balance # => Money value object
@@ -79,8 +82,8 @@ def clear_aggregation_cache #:nodoc:
# customer.balance == Money.new(20) # => true
# customer.balance < Money.new(5) # => false
#
- # Value objects can also be composed of multiple attributes, such as the case of Address. The order of the mappings will
- # determine the order of the parameters. Example:
+ # Value objects can also be composed of multiple attributes, such as the case of Address. The order
+ # of the mappings will determine the order of the parameters.
#
# customer.address_street = "Hyancintvej"
# customer.address_city = "Copenhagen"
@@ -91,38 +94,43 @@ def clear_aggregation_cache #:nodoc:
#
# == Writing value objects
#
- # Value objects are immutable and interchangeable objects that represent a given value, such as a Money object representing
- # $5. Two Money objects both representing $5 should be equal (through methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking
- # makes sense). This is unlike entity objects where equality is determined by identity. An entity class such as Customer can
- # easily have two different objects that both have an address on Hyancintvej. Entity identity is determined by object or
- # relational unique identifiers (such as primary keys). Normal ActiveRecord::Base classes are entity objects.
+ # Value objects are immutable and interchangeable objects that represent a given value, such as
+ # a Money object representing $5. Two Money objects both representing $5 should be equal (through
+ # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
+ # unlike entity objects where equality is determined by identity. An entity class such as Customer can
+ # easily have two different objects that both have an address on Hyancintvej. Entity identity is
+ # determined by object or relational unique identifiers (such as primary keys). Normal
+ # ActiveRecord::Base classes are entity objects.
#
- # It's also important to treat the value objects as immutable. Don't allow the Money object to have its amount changed after
- # creation. Create a new Money object with the new value instead. This is exemplified by the Money#exchange_to method that
- # returns a new value object instead of changing its own values. Active Record won't persist value objects that have been
- # changed through means other than the writer method.
+ # It's also important to treat the value objects as immutable. Don't allow the Money object to have
+ # its amount changed after creation. Create a new Money object with the new value instead. This
+ # is exemplified by the Money#exchange_to method that returns a new value object instead of changing
+ # its own values. Active Record won't persist value objects that have been changed through means
+ # other than the writer method.
#
- # The immutable requirement is enforced by Active Record by freezing any object assigned as a value object. Attempting to
- # change it afterwards will result in a ActiveSupport::FrozenObjectError.
+ # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
+ # object. Attempting to change it afterwards will result in a ActiveSupport::FrozenObjectError.
#
- # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not keeping value objects
- # immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
+ # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
+ # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
#
# == Custom constructors and converters
#
- # By default value objects are initialized by calling the <tt>new</tt> constructor of the value class passing each of the
- # mapped attributes, in the order specified by the <tt>:mapping</tt> option, as arguments. If the value class doesn't support
- # this convention then +composed_of+ allows a custom constructor to be specified.
+ # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
+ # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
+ # option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
+ # a custom constructor to be specified.
#
- # When a new value is assigned to the value object the default assumption is that the new value is an instance of the value
- # class. Specifying a custom converter allows the new value to be automatically converted to an instance of value class if
- # necessary.
+ # When a new value is assigned to the value object the default assumption is that the new value
+ # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
+ # converted to an instance of value class if necessary.
#
- # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be aggregated using the
- # NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor for the value class is called +create+ and it
- # expects a CIDR address string as a parameter. New values can be assigned to the value object using either another
- # NetAddr::CIDR object, a string or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to
- # meet these requirements:
+ # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
+ # should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor
+ # for the value class is called +create+ and it expects a CIDR address string as a parameter. New
+ # values can be assigned to the value object using either another NetAddr::CIDR object, a string
+ # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
+ # these requirements:
#
# class NetworkResource < ActiveRecord::Base
# composed_of :cidr,
@@ -149,9 +157,9 @@ def clear_aggregation_cache #:nodoc:
#
# == Finding records by a value object
#
- # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database by specifying an instance
- # of the value object in the conditions hash. The following example finds all customers with +balance_amount+ equal to 20 and
- # +balance_currency+ equal to "USD":
+ # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database
+ # by specifying an instance of the value object in the conditions hash. The following example
+ # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
#
# Customer.find(:all, :conditions => {:balance => Money.new(20, "USD")})
#
@@ -160,23 +168,28 @@ module ClassMethods
# <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
#
# Options are:
- # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name can't be inferred
- # from the part id. So <tt>composed_of :address</tt> will by default be linked to the Address class, but
- # if the real class name is CompanyAddress, you'll have to specify it with this option.
- # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value object. Each mapping
- # is represented as an array where the first item is the name of the entity attribute and the second item is the
- # name the attribute in the value object. The order in which mappings are defined determine the order in which
- # attributes are sent to the value class constructor.
+ # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
+ # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
+ # to the Address class, but if the real class name is CompanyAddress, you'll have to specify it
+ # with this option.
+ # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
+ # object. Each mapping is represented as an array where the first item is the name of the
+ # entity attribute and the second item is the name the attribute in the value object. The
+ # order in which mappings are defined determine the order in which attributes are sent to the
+ # value class constructor.
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
- # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all mapped attributes.
+ # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
+ # mapped attributes.
# This defaults to +false+.
- # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that is called to
- # initialize the value object. The constructor is passed all of the mapped attributes, in the order that they
- # are defined in the <tt>:mapping option</tt>, as arguments and uses them to instantiate a <tt>:class_name</tt> object.
+ # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
+ # is called to initialize the value object. The constructor is passed all of the mapped attributes,
+ # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
+ # to instantiate a <tt>:class_name</tt> object.
# The default is <tt>:new</tt>.
- # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt> or a Proc that is
- # called when a new value is assigned to the value object. The converter is passed the single value that is used
- # in the assignment and is only called if the new value is not an instance of <tt>:class_name</tt>.
+ # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
+ # or a Proc that is called when a new value is assigned to the value object. The converter is
+ # passed the single value that is used in the assignment and is only called if the new value is
+ # not an instance of <tt>:class_name</tt>.
#
# Option examples:
# composed_of :temperature, :mapping => %w(reading celsius)
View
16 activerecord/lib/active_record/association_preload.rb
@@ -9,8 +9,8 @@ module AssociationPreload #:nodoc:
# Implements the details of eager loading of Active Record associations.
# Application developers should not use this module directly.
#
- # ActiveRecord::Base is extended with this module. The source code in
- # ActiveRecord::Base references methods defined in this module.
+ # <tt>ActiveRecord::Base</tt> is extended with this module. The source code in
+ # <tt>ActiveRecord::Base</tt> references methods defined in this module.
#
# Note that 'eager loading' and 'preloading' are actually the same thing.
# However, there are two different eager loading strategies.
@@ -55,7 +55,7 @@ module ClassMethods
# == Parameters
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
# i.e. +records+ itself may also contain arrays of records. In any case,
- # +preload_associations+ will preload the associations all records by
+ # +preload_associations+ will preload the all associations records by
# flattening +records+.
#
# +associations+ specifies one or more associations that you want to
@@ -110,8 +110,8 @@ def preload_associations(records, associations, preload_options={})
def preload_one_association(records, association, preload_options={})
class_to_reflection = {}
# Not all records have the same class, so group then preload
- # group on the reflection itself so that if various subclass share the same association then we do not split them
- # unnecessarily
+ # group on the reflection itself so that if various subclass share the same association then
+ # we do not split them unnecessarily
records.group_by { |record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, _records|
raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
@@ -149,7 +149,8 @@ def set_association_single_records(id_to_record_map, reflection_name, associated
seen_keys = {}
associated_records.each do |associated_record|
#this is a has_one or belongs_to: there should only be one record.
- #Unfortunately we can't (in portable way) ask the database for 'all records where foo_id in (x,y,z), but please
+ #Unfortunately we can't (in portable way) ask the database for
+ #'all records where foo_id in (x,y,z), but please
# only one row per distinct foo_id' so this where we enforce that
next if seen_keys[associated_record[key].to_s]
seen_keys[associated_record[key].to_s] = true
@@ -304,7 +305,8 @@ def preload_belongs_to_association(records, reflection, preload_options={})
polymorph_type = options[:foreign_type]
klasses_and_ids = {}
- # Construct a mapping from klass to a list of ids to load and a mapping of those ids back to their parent_records
+ # Construct a mapping from klass to a list of ids to load and a mapping of those ids back
+ # to their parent_records
records.each do |record|
if klass = record.send(polymorph_type)
klass_id = record.send(primary_key_name)
View
464 activerecord/lib/active_record/associations.rb
@@ -114,15 +114,15 @@ module Associations # :nodoc:
autoload :HasOneAssociation, 'active_record/associations/has_one_association'
autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
- # Clears out the association cache
+ # Clears out the association cache.
def clear_association_cache #:nodoc:
self.class.reflect_on_all_associations.to_a.each do |assoc|
instance_variable_set "@#{assoc.name}", nil
end unless self.new_record?
end
private
- # Gets the specified association instance if it responds to :loaded?, nil otherwise.
+ # Returns the specified association instance if it responds to :loaded?, nil otherwise.
def association_instance_get(name)
ivar = "@#{name}"
if instance_variable_defined?(ivar)
@@ -136,10 +136,12 @@ def association_instance_set(name, association)
instance_variable_set("@#{name}", association)
end
- # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
- # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are
- # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt>
- # methods. Example:
+ # Associations are a set of macro-like class methods for tying objects together through
+ # foreign keys. They express relationships like "Project has one Project Manager"
+ # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
+ # class which are specialized according to the collection or association symbol and the
+ # options hash. It works much the same way as Ruby's own <tt>attr*</tt>
+ # methods.
#
# class Project < ActiveRecord::Base
# belongs_to :portfolio
@@ -148,7 +150,8 @@ def association_instance_set(name, association)
# has_and_belongs_to_many :categories
# end
#
- # The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships:
+ # The project class now has the following methods (and more) to ease the traversal and
+ # manipulation of its relationships:
# * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
# * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
# * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
@@ -159,8 +162,9 @@ def association_instance_set(name, association)
#
# === A word of warning
#
- # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association
- # adds a method with that name to its model, it will override the inherited method and break things.
+ # Don't create associations that have the same name as instance methods of
+ # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
+ # its model, it will override the inherited method and break things.
# For instance, +attributes+ and +connection+ would be bad choices for association names.
#
# == Auto-generated methods
@@ -270,8 +274,8 @@ def association_instance_set(name, association)
#
# == Is it a +belongs_to+ or +has_one+ association?
#
- # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class
- # declaring the +belongs_to+ relationship. Example:
+ # Both express a 1-1 relationship. The difference is mostly where to place the foreign
+ # key, which goes on the table for the class declaring the +belongs_to+ relationship.
#
# class User < ActiveRecord::Base
# # I reference an account.
@@ -300,8 +304,9 @@ def association_instance_set(name, association)
#
# == Unsaved objects and associations
#
- # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
- # aware of, mostly involving the saving of associated objects.
+ # You can manipulate objects and associations before they are saved to the database, but
+ # there is some special behavior you should be aware of, mostly involving the saving of
+ # associated objects.
#
# You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
# <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
@@ -310,26 +315,33 @@ def association_instance_set(name, association)
#
# === One-to-one associations
#
- # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
- # order to update their primary keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
- # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment
- # is cancelled.
- # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>association.build</tt> method (documented below).
- # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It
- # does not save the parent either.
+ # * Assigning an object to a +has_one+ association automatically saves that object and
+ # the object being replaced (if there is one), in order to update their primary
+ # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
+ # * If either of these saves fail (due to one of the objects being invalid) the assignment
+ # statement returns +false+ and the assignment is cancelled.
+ # * If you wish to assign an object to a +has_one+ association without saving it,
+ # use the <tt>association.build</tt> method (documented below).
+ # * Assigning an object to a +belongs_to+ association does not save the object, since
+ # the foreign key field belongs on the parent. It does not save the parent either.
#
# === Collections
#
- # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object
- # (the owner of the collection) is not yet stored in the database.
- # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar) fails, then <tt>push</tt> returns +false+.
- # * You can add an object to a collection without automatically saving it by using the <tt>collection.build</tt> method (documented below).
- # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically saved when the parent is saved.
+ # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically
+ # saves that object, except if the parent object (the owner of the collection) is not yet
+ # stored in the database.
+ # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
+ # fails, then <tt>push</tt> returns +false+.
+ # * You can add an object to a collection without automatically saving it by using the
+ # <tt>collection.build</tt> method (documented below).
+ # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
+ # saved when the parent is saved.
#
# === Association callbacks
#
- # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
- # triggered when you add an object to or remove an object from an association collection. Example:
+ # Similar to the normal callbacks that hook into the lifecycle of an Active Record object,
+ # you can also define callbacks that get triggered when you add an object to or remove an
+ # object from an association collection.
#
# class Project
# has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
@@ -342,19 +354,21 @@ def association_instance_set(name, association)
# It's possible to stack callbacks by passing them as an array. Example:
#
# class Project
- # has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
+ # has_and_belongs_to_many :developers,
+ # :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
# end
#
# Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
#
- # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with
- # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed.
+ # Should any of the +before_add+ callbacks throw an exception, the object does not get
+ # added to the collection. Same with the +before_remove+ callbacks; if an exception is
+ # thrown the object doesn't get removed.
#
# === Association extensions
#
- # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially
- # beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association.
- # Example:
+ # The proxy objects that control the access to associations can be extended through anonymous
+ # modules. This is especially beneficial for adding new finders, creators, and other
+ # factory-type methods that are only used as part of this association.
#
# class Account < ActiveRecord::Base
# has_many :people do
@@ -369,7 +383,8 @@ def association_instance_set(name, association)
# person.first_name # => "David"
# person.last_name # => "Heinemeier Hansson"
#
- # If you need to share the same extensions between many associations, you can use a named extension module. Example:
+ # If you need to share the same extensions between many associations, you can use a named
+ # extension module.
#
# module FindOrCreateByNameExtension
# def find_or_create_by_name(name)
@@ -386,9 +401,10 @@ def association_instance_set(name, association)
# has_many :people, :extend => FindOrCreateByNameExtension
# end
#
- # If you need to use multiple named extension modules, you can specify an array of modules with the <tt>:extend</tt> option.
- # In the case of name conflicts between methods in the modules, methods in modules later in the array supercede
- # those earlier in the array. Example:
+ # If you need to use multiple named extension modules, you can specify an array of modules
+ # with the <tt>:extend</tt> option.
+ # In the case of name conflicts between methods in the modules, methods in modules later
+ # in the array supercede those earlier in the array.
#
# class Account < ActiveRecord::Base
# has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
@@ -399,12 +415,14 @@ def association_instance_set(name, association)
#
# * +proxy_owner+ - Returns the object the association is part of.
# * +proxy_reflection+ - Returns the reflection object that describes the association.
- # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
+ # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or
+ # the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
#
# === Association Join Models
#
- # Has Many associations can be configured with the <tt>:through</tt> option to use an explicit join model to retrieve the data. This
- # operates similarly to a +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
+ # Has Many associations can be configured with the <tt>:through</tt> option to use an
+ # explicit join model to retrieve the data. This operates similarly to a
+ # +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
# callbacks, and extra attributes on the join model. Consider the following schema:
#
# class Author < ActiveRecord::Base
@@ -418,7 +436,7 @@ def association_instance_set(name, association)
# end
#
# @author = Author.find :first
- # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to.
+ # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
# @author.books # selects all books by using the Authorship join model
#
# You can also go through a +has_many+ association on the join model:
@@ -439,7 +457,7 @@ def association_instance_set(name, association)
#
# @firm = Firm.find :first
# @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
- # @firm.invoices # selects all invoices by going through the Client join model.
+ # @firm.invoices # selects all invoices by going through the Client join model
#
# Similarly you can go through a +has_one+ association on the join model:
#
@@ -461,16 +479,18 @@ def association_instance_set(name, association)
# @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group
# @group.avatars # selects all avatars by going through the User join model.
#
- # An important caveat with going through +has_one+ or +has_many+ associations on the join model is that these associations are
- # *read-only*. For example, the following would not work following the previous example:
+ # An important caveat with going through +has_one+ or +has_many+ associations on the
+ # join model is that these associations are *read-only*. For example, the following
+ # would not work following the previous example:
#
- # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around.
+ # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
# @group.avatars.delete(@group.avatars.last) # so would this
#
# === Polymorphic Associations
#
- # Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they
- # specify an interface that a +has_many+ association must adhere to.
+ # Polymorphic associations on models are not restricted on what types of models they
+ # can be associated with. Rather, they specify an interface that a +has_many+ association
+ # must adhere to.
#
# class Asset < ActiveRecord::Base
# belongs_to :attachable, :polymorphic => true
@@ -482,13 +502,16 @@ def association_instance_set(name, association)
#
# @asset.attachable = @post
#
- # This works by using a type column in addition to a foreign key to specify the associated record. In the Asset example, you'd need
- # an +attachable_id+ integer column and an +attachable_type+ string column.
+ # This works by using a type column in addition to a foreign key to specify the associated
+ # record. In the Asset example, you'd need an +attachable_id+ integer column and an
+ # +attachable_type+ string column.
#
- # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order
- # for the associations to work as expected, ensure that you store the base model for the STI models in the
- # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts
- # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table.
+ # Using polymorphic associations in combination with single table inheritance (STI) is
+ # a little tricky. In order for the associations to work as expected, ensure that you
+ # store the base model for the STI models in the type column of the polymorphic
+ # association. To continue with the asset example above, suppose there are guest posts
+ # and member posts that use the posts table for STI. In this case, there must be a +type+
+ # column in the posts table.
#
# class Asset < ActiveRecord::Base
# belongs_to :attachable, :polymorphic => true
@@ -511,9 +534,10 @@ def association_instance_set(name, association)
#
# == Caching
#
- # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically
- # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without
- # worrying too much about performance at the first go. Example:
+ # All of the methods are built on a simple caching principle that will keep the result
+ # of the last query around unless specifically instructed not to. The cache is even
+ # shared across methods to make it even cheaper to use the macro-added methods without
+ # worrying too much about performance at the first go.
#
# project.milestones # fetches milestones from the database
# project.milestones.size # uses the milestone cache
@@ -523,9 +547,10 @@ def association_instance_set(name, association)
#
# == Eager loading of associations
#
- # Eager loading is a way to find objects of a certain class and a number of named associations. This is
- # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author
- # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 2. Example:
+ # Eager loading is a way to find objects of a certain class and a number of named associations.
+ # This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100
+ # posts that each need to display their author triggers 101 database queries. Through the
+ # use of eager loading, the 101 queries can be reduced to 2.
#
# class Post < ActiveRecord::Base
# belongs_to :author
@@ -540,63 +565,76 @@ def association_instance_set(name, association)
# puts "Last comment on: " + post.comments.first.created_on
# end
#
- # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author:
+ # To iterate over these one hundred posts, we'll generate 201 database queries. Let's
+ # first just optimize it for retrieving the author:
#
# for post in Post.find(:all, :include => :author)
#
- # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol. After loading the posts, find
- # will collect the +author_id+ from each one and load all the referenced authors with one query. Doing so will cut down the number of queries from 201 to 102.
+ # This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
+ # symbol. After loading the posts, find will collect the +author_id+ from each one and load
+ # all the referenced authors with one query. Doing so will cut down the number of queries
+ # from 201 to 102.
#
# We can improve upon the situation further by referencing both associations in the finder with:
#
# for post in Post.find(:all, :include => [ :author, :comments ])
#
- # This will load all comments with a single query. This reduces the total number of queries to 3. More generally the number of queries
- # will be 1 plus the number of associations named (except if some of the associations are polymorphic +belongs_to+ - see below).
+ # This will load all comments with a single query. This reduces the total number of queries
+ # to 3. More generally the number of queries will be 1 plus the number of associations
+ # named (except if some of the associations are polymorphic +belongs_to+ - see below).
#
# To include a deep hierarchy of associations, use a hash:
#
# for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
#
- # That'll grab not only all the comments but all their authors and gravatar pictures. You can mix and match
- # symbols, arrays and hashes in any combination to describe the associations you want to load.
+ # That'll grab not only all the comments but all their authors and gravatar pictures.
+ # You can mix and match symbols, arrays and hashes in any combination to describe the
+ # associations you want to load.
#
- # All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced
- # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no
- # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
+ # All of this power shouldn't fool you into thinking that you can pull out huge amounts
+ # of data with no performance penalty just because you've reduced the number of queries.
+ # The database still needs to send all the data to Active Record and it still needs to
+ # be processed. So it's no catch-all for performance problems, but it's a great way to
+ # cut down on the number of queries in a situation as the one described above.
#
- # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case
- # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example
+ # Since only one table is loaded at a time, conditions or orders cannot reference tables
+ # other than the main one. If this is the case Active Record falls back to the previously
+ # used LEFT OUTER JOIN based strategy. For example
#
# Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])
#
- # This will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
- # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences.
- # In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole
- # and not just to the association. You must disambiguate column references for this fallback to happen, for example
+ # This will result in a single SQL query with joins along the lines of:
+ # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
+ # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
+ # like this can have unintended consequences.
+ # In the above example posts with no approved comments are not returned at all, because
+ # the conditions apply to the SQL statement as a whole and not just to the association.
+ # You must disambiguate column references for this fallback to happen, for example
# <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
#
- # If you do want eager load only some members of an association it is usually more natural to <tt>:include</tt> an association
- # which has conditions defined on it:
+ # If you do want eager load only some members of an association it is usually more natural
+ # to <tt>:include</tt> an association which has conditions defined on it:
#
# class Post < ActiveRecord::Base
# has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
# end
#
# Post.find(:all, :include => :approved_comments)
#
- # This will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
+ # This will load posts and eager load the +approved_comments+ association, which contains
+ # only those comments that have been approved.
#
- # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects:
+ # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
+ # returning all the associated objects:
#
# class Picture < ActiveRecord::Base
# has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
# end
#
# Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
#
- # When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
- # before the actual model exists.
+ # When eager loaded, conditions are interpolated in the context of the model class, not
+ # the model instance. Conditions are lazily interpolated before the actual model exists.
#
# Eager loading is supported with polymorphic associations.
#
@@ -608,17 +646,21 @@ def association_instance_set(name, association)
#
# Address.find(:all, :include => :addressable)
#
- # This will execute one query to load the addresses and load the addressables with one query per addressable type.
- # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of
- # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback
- # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
- # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query.
+ # This will execute one query to load the addresses and load the addressables with one
+ # query per addressable type.
+ # For example if all the addressables are either of class Person or Company then a total
+ # of 3 queries will be executed. The list of addressable types to load is determined on
+ # the back of the addresses loaded. This is not supported if Active Record has to fallback
+ # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
+ # The reason is that the parent model's type is a column value so its corresponding table
+ # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
#
# == Table Aliasing
#
- # Active Record uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
- # the standard table name is used. The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>. Indexes are appended
- # for any more successive uses of the table name.
+ # Active Record uses table aliasing in the case that a table is referenced multiple times
+ # in a join. If a table is referenced only once, the standard table name is used. The
+ # second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
+ # Indexes are appended for any more successive uses of the table name.
#
# Post.find :all, :joins => :comments
# # => SELECT ... FROM posts INNER JOIN comments ON ...
@@ -651,7 +693,8 @@ def association_instance_set(name, association)
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
# INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
#
- # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations:
+ # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table
+ # names will take precedence over the eager associations:
#
# Post.find :all, :joins => :comments, :joins => "inner join comments ..."
# # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
@@ -660,7 +703,8 @@ def association_instance_set(name, association)
# INNER JOIN comments special_comments_posts ...
# INNER JOIN comments ...
#
- # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database.
+ # Table aliases are automatically truncated according to the maximum length of table identifiers
+ # according to the specific database.
#
# == Modules
#
@@ -676,9 +720,10 @@ def association_instance_set(name, association)
# end
# end
#
- # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
- # If you want to associate with a class in another module scope, this can be done by specifying the complete class name.
- # Example:
+ # When <tt>Firm#clients</tt> is called, it will in turn call
+ # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
+ # If you want to associate with a class in another module scope, this can be done by
+ # specifying the complete class name.
#
# module MyApplication
# module Business
@@ -694,8 +739,8 @@ def association_instance_set(name, association)
#
# == Bi-directional associations
#
- # When you specify an association there is usually an association on the associated model that specifies the same
- # relationship in reverse. For example, with the following models:
+ # When you specify an association there is usually an association on the associated model
+ # that specifies the same relationship in reverse. For example, with the following models:
#
# class Dungeon < ActiveRecord::Base
# has_many :traps
@@ -710,19 +755,23 @@ def association_instance_set(name, association)
# belongs_to :dungeon
# end
#
- # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are the inverse of each other and the
- # inverse of the +dungeon+ association on +EvilWizard+ is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
- # Active Record doesn't know anything about these inverse relationships and so no object loading optimisation is possible. For example:
+ # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are
+ # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
+ # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
+ # Active Record doesn't know anything about these inverse relationships and so no object
+ # loading optimisation is possible. For example:
#
# d = Dungeon.first
# t = d.traps.first
# d.level == t.dungeon.level # => true
# d.level = 10
# d.level == t.dungeon.level # => false
#
- # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to the same object data from the database, but are
- # actually different in-memory copies of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
- # Active Record about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to:
+ # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
+ # the same object data from the database, but are actually different in-memory copies
+ # of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
+ # Active Record about inverse relationships and it will optimise object loading. For
+ # example, if we changed our model definitions to:
#
# class Dungeon < ActiveRecord::Base
# has_many :traps, :inverse_of => :dungeon
@@ -737,8 +786,8 @@ def association_instance_set(name, association)
# belongs_to :dungeon, :inverse_of => :evil_wizard
# end
#
- # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same in-memory instance and our final <tt>d.level == t.dungeon.level</tt>
- # will return +true+.
+ # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same
+ # in-memory instance and our final <tt>d.level == t.dungeon.level</tt> will return +true+.
#
# There are limitations to <tt>:inverse_of</tt> support:
#
@@ -748,13 +797,13 @@ def association_instance_set(name, association)
#
# == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
#
- # If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll
- # get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
+ # If you attempt to assign an object to an association that doesn't match the inferred
+ # or specified <tt>:class_name</tt>, you'll get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
#
# == Options
#
- # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
- # possible.
+ # All of the association macros can be specialized through options. This makes cases
+ # more complex than the simple and guessable ones possible.
module ClassMethods
# Specifies a one-to-many association. The following methods for retrieval and query of
# collections of associated objects will be added:
@@ -828,20 +877,22 @@ module ClassMethods
# === Supported options
# [:class_name]
# Specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>has_many :products</tt> will by default be linked to the Product class, but
- # if the real class name is SpecialProduct, you'll have to specify it with this option.
+ # from the association name. So <tt>has_many :products</tt> will by default be linked
+ # to the Product class, but if the real class name is SpecialProduct, you'll have to
+ # specify it with this option.
# [:conditions]
# Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
- # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from the association are scoped if a hash
- # is used. <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
- # or <tt>@blog.posts.build</tt>.
+ # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from
+ # the association are scoped if a hash is used.
+ # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published
+ # posts with <tt>@blog.posts.create</tt> or <tt>@blog.posts.build</tt>.
# [:order]
# Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
# such as <tt>last_name, first_name DESC</tt>.
# [:foreign_key]
# Specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id"
- # as the default <tt>:foreign_key</tt>.
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
+ # association will use "person_id" as the default <tt>:foreign_key</tt>.
# [:primary_key]
# Specify the method that returns the primary key used for the association. By default this is +id+.
# [:dependent]
@@ -855,36 +906,44 @@ module ClassMethods
#
# [:finder_sql]
# Specify a complete SQL statement to fetch the association. This is a good way to go for complex
- # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
+ # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+
+ # is _not_ added.
# [:counter_sql]
# Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
+ # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
+ # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
# [:extend]
# Specify a named module for extending the proxy. See "Association extensions".
# [:include]
# Specify second-order associations that should be eager loaded when the collection is loaded.
# [:group]
# An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
# [:having]
- # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
+ # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt>
+ # returns. Uses the <tt>HAVING</tt> SQL-clause.
# [:limit]
# An integer determining the limit on the number of rows that should be returned.
# [:offset]
- # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
+ # An integer determining the offset from where the rows should be fetched. So at 5,
+ # it would skip the first 4 rows.
# [:select]
- # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if
+ # you, for example, want to do a join but not include the joined columns. Do not forget
+ # to include the primary and foreign keys, otherwise it will raise an error.
# [:as]
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
# [:through]
- # Specifies a join model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
- # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
- # <tt>has_one</tt> or <tt>has_many</tt> association on the join model. The collection of join models can be managed via the collection
- # API. For example, new join models are created for newly associated objects, and if some are gone their rows are deleted (directly,
+ # Specifies a join model through which to perform the query. Options for <tt>:class_name</tt>
+ # and <tt>:foreign_key</tt> are ignored, as the association uses the source reflection. You
+ # can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>, <tt>has_one</tt>
+ # or <tt>has_many</tt> association on the join model. The collection of join models
+ # can be managed via the collection API. For example, new join models are created for
+ # newly associated objects, and if some are gone their rows are deleted (directly,
# no destroy callbacks are triggered).
# [:source]
- # Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
- # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
+ # Specifies the source association name used by <tt>has_many :through</tt> queries.
+ # Only use it if the name cannot be inferred from the association.
+ # <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
# <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
# [:source_type]
# Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
@@ -896,12 +955,14 @@ module ClassMethods
# [:validate]
# If false, don't validate the associated objects when saving the parent object. true by default.
# [:autosave]
- # If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object.
+ # If true, always save the associated objects or destroy them if marked for destruction,
+ # when saving the parent object.
# If false, never save or destroy the associated objects.
# By default, only save associated objects that are new records.
# [:inverse_of]
- # Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_many</tt>
- # association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options.
+ # Specifies the name of the <tt>belongs_to</tt> association on the associated object
+ # that is the inverse of this <tt>has_many</tt> association. Does not work in combination
+ # with <tt>:through</tt> or <tt>:as</tt> options.
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
#
# Option examples:
@@ -975,35 +1036,39 @@ def has_many(association_id, options = {}, &extension)
# [:conditions]
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>rank = 5</tt>. Record creation from the association is scoped if a hash
- # is used. <tt>has_one :account, :conditions => {:enabled => true}</tt> will create an enabled account with <tt>@company.create_account</tt>
- # or <tt>@company.build_account</tt>.
+ # is used. <tt>has_one :account, :conditions => {:enabled => true}</tt> will create
+ # an enabled account with <tt>@company.create_account</tt> or <tt>@company.build_account</tt>.
# [:order]
# Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
# such as <tt>last_name, first_name DESC</tt>.
# [:dependent]
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
- # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to <tt>:nullify</tt>, the associated
- # object's foreign key is set to +NULL+. Also, association is assigned.
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
+ # If set to <tt>:nullify</tt>, the associated object's foreign key is set to +NULL+.
+ # Also, association is assigned.
# [:foreign_key]
# Specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association will use "person_id"
- # as the default <tt>:foreign_key</tt>.
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
+ # will use "person_id" as the default <tt>:foreign_key</tt>.
# [:primary_key]
# Specify the method that returns the primary key used for the association. By default this is +id+.
# [:include]
# Specify second-order associations that should be eager loaded when this object is loaded.
# [:as]
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
# [:select]
- # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example,
+ # you want to do a join but not include the joined columns. Do not forget to include the
+ # primary and foreign keys, otherwise it will raise an error.
# [:through]
- # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
- # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
- # <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>
+ # and <tt>:foreign_key</tt> are ignored, as the association uses the source reflection. You
+ # can only use a <tt>:through</tt> query through a <tt>has_one</tt> or <tt>belongs_to</tt>
+ # association on the join model.
# [:source]
- # Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
- # inferred from the association. <tt>has_one :favorite, :through => :favorites</tt> will look for a
+ # Specifies the source association name used by <tt>has_one :through</tt> queries.
+ # Only use it if the name cannot be inferred from the association.
+ # <tt>has_one :favorite, :through => :favorites</tt> will look for a
# <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
# [:source_type]
# Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
@@ -1013,17 +1078,19 @@ def has_many(association_id, options = {}, &extension)
# [:validate]
# If false, don't validate the associated object when saving the parent object. +false+ by default.
# [:autosave]
- # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object.
- # If false, never save or destroy the associated object.
+ # If true, always save the associated object or destroy it if marked for destruction,
+