Skip to content
Browse files

Merge branch 'master' of git@github.com:rails/rails

  • Loading branch information...
2 parents 884068a + 943d64b commit df2adc4c516f9f16942fbc799666c99bfd20d17c @jeremy jeremy committed Feb 27, 2009
Showing with 910 additions and 643 deletions.
  1. +1 −1 actionmailer/CHANGELOG
  2. +1 −1 actionmailer/Rakefile
  3. +1 −1 actionmailer/lib/action_mailer/version.rb
  4. +3 −1 actionpack/CHANGELOG
  5. +1 −1 actionpack/Rakefile
  6. +2 −2 actionpack/lib/action_controller/base.rb
  7. +1 −1 actionpack/lib/action_controller/request.rb
  8. +1 −1 actionpack/lib/action_pack/version.rb
  9. +83 −49 actionpack/lib/action_view/helpers/form_helper.rb
  10. +41 −0 actionpack/test/template/form_helper_test.rb
  11. +1 −1 activerecord/CHANGELOG
  12. +1 −1 activerecord/Rakefile
  13. +3 −119 activerecord/lib/active_record/associations.rb
  14. +2 −2 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
  15. +2 −2 activerecord/lib/active_record/associations/has_many_association.rb
  16. +2 −2 activerecord/lib/active_record/associations/has_many_through_association.rb
  17. +182 −58 activerecord/lib/active_record/autosave_association.rb
  18. +3 −2 activerecord/lib/active_record/named_scope.rb
  19. +1 −1 activerecord/lib/active_record/version.rb
  20. +0 −129 activerecord/test/cases/associations/belongs_to_associations_test.rb
  21. +0 −129 activerecord/test/cases/associations/has_many_associations_test.rb
  22. +0 −87 activerecord/test/cases/associations/has_one_associations_test.rb
  23. +466 −8 activerecord/test/cases/autosave_association_test.rb
  24. +5 −0 activeresource/CHANGELOG
  25. +1 −1 activeresource/Rakefile
  26. +1 −1 activeresource/lib/active_resource/version.rb
  27. +3 −1 activesupport/CHANGELOG
  28. +2 −3 activesupport/lib/active_support/core_ext/array/conversions.rb
  29. +1 −1 activesupport/lib/active_support/duration.rb
  30. +2 −2 activesupport/lib/active_support/vendor.rb
  31. +0 −3 activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore
  32. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/MIT-LICENSE
  33. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/README.textile
  34. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/Rakefile
  35. +2 −2 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/i18n.gemspec
  36. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/lib/i18n.rb
  37. +1 −9 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/lib/i18n/backend/simple.rb
  38. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/lib/i18n/exceptions.rb
  39. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/test/all.rb
  40. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/test/i18n_exceptions_test.rb
  41. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/test/i18n_test.rb
  42. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/test/locale/en.rb
  43. 0 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/test/locale/en.yml
  44. +45 −1 activesupport/lib/active_support/vendor/{i18n-0.1.1 → i18n-0.1.3}/test/simple_backend_test.rb
  45. +1 −1 activesupport/lib/active_support/version.rb
  46. +7 −0 railties/CHANGELOG
  47. +5 −5 railties/Rakefile
  48. +2 −0 railties/lib/initializer.rb
  49. +6 −2 railties/lib/rails/plugin.rb
  50. +5 −2 railties/lib/rails/plugin/loader.rb
  51. +9 −6 railties/lib/rails/rack/metal.rb
  52. +1 −1 railties/lib/rails/version.rb
  53. +1 −1 railties/lib/tasks/testing.rake
  54. +10 −0 railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb
  55. +2 −2 railties/test/plugin_loader_test.rb
View
2 actionmailer/CHANGELOG
@@ -1,4 +1,4 @@
-*Edge*
+*2.3.1 [RC2] (February 27th, 2009)*
* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]
View
2 actionmailer/Rakefile
@@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "actionmailer"
s.homepage = "http://www.rubyonrails.org"
- s.add_dependency('actionpack', '= 2.3.0' + PKG_BUILD)
+ s.add_dependency('actionpack', '= 2.3.1' + PKG_BUILD)
s.has_rdoc = true
s.requirements << 'none'
View
2 actionmailer/lib/action_mailer/version.rb
@@ -2,7 +2,7 @@ module ActionMailer
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
- TINY = 0
+ TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
View
4 actionpack/CHANGELOG
@@ -1,4 +1,6 @@
-*Edge*
+*2.3.1 [RC2] (February 27th, 2009)*
+
+* Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger]
* Added partial scoping to TranslationHelper#translate, so if you call translate(".foo") from the people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo") [DHH]
View
2 actionpack/Rakefile
@@ -80,7 +80,7 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.requirements << 'none'
- s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
+ s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
s.add_dependency('rack', '>= 0.9.0')
s.require_path = 'lib'
View
4 actionpack/lib/action_controller/base.rb
@@ -22,7 +22,7 @@ class MethodNotAllowed < ActionControllerError #:nodoc:
attr_reader :allowed_methods
def initialize(*allowed_methods)
- super("Only #{allowed_methods.to_sentence} requests are allowed.")
+ super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
@allowed_methods = allowed_methods
end
@@ -1298,7 +1298,7 @@ def perform_action
rescue ActionView::MissingTemplate => e
# Was the implicit template missing, or was it another template?
if e.path == default_template_name
- raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
+ raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence(:locale => :en)}", caller
else
raise e
end
View
2 actionpack/lib/action_controller/request.rb
@@ -32,7 +32,7 @@ def key?(key)
# <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS
# constant above, an UnknownHttpMethod exception is raised.
def request_method
- @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
+ @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
end
# Returns the HTTP request \method used for action processing as a
View
2 actionpack/lib/action_pack/version.rb
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
- TINY = 0
+ TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
View
132 actionpack/lib/action_view/helpers/form_helper.rb
@@ -5,17 +5,24 @@
module ActionView
module Helpers
- # Form helpers are designed to make working with models much easier compared to using just standard HTML
- # elements by providing a set of methods for creating forms based on your models. This helper generates the HTML
- # for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form
- # is submitted (i.e., when the user hits the submit button or <tt>form.submit</tt> is called via JavaScript), the form inputs will be bundled into the <tt>params</tt> object and passed back to the controller.
+ # Form helpers are designed to make working with models much easier
+ # compared to using just standard HTML elements by providing a set of
+ # methods for creating forms based on your models. This helper generates
+ # the HTML for forms, providing a method for each sort of input
+ # (e.g., text, password, select, and so on). When the form is submitted
+ # (i.e., when the user hits the submit button or <tt>form.submit</tt> is
+ # called via JavaScript), the form inputs will be bundled into the
+ # <tt>params</tt> object and passed back to the controller.
#
- # There are two types of form helpers: those that specifically work with model attributes and those that don't.
- # This helper deals with those that work with model attributes; to see an example of form helpers that don't work
- # with model attributes, check the ActionView::Helpers::FormTagHelper documentation.
+ # There are two types of form helpers: those that specifically work with
+ # model attributes and those that don't. This helper deals with those that
+ # work with model attributes; to see an example of form helpers that don't
+ # work with model attributes, check the ActionView::Helpers::FormTagHelper
+ # documentation.
#
- # The core method of this helper, form_for, gives you the ability to create a form for a model instance;
- # for example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
+ # The core method of this helper, form_for, gives you the ability to create
+ # a form for a model instance; for example, let's say that you have a model
+ # <tt>Person</tt> and want to create a new instance of it:
#
# # Note: a @person variable will have been created in the controller.
# # For example: @person = Person.new
@@ -40,41 +47,51 @@ module Helpers
# <%= submit_tag 'Create' %>
# <% end %>
#
- # This example will render the <tt>people/_form</tt> partial, setting a local variable called <tt>form</tt> which references the yielded FormBuilder.
- #
- # The <tt>params</tt> object created when this form is submitted would look like:
+ # This example will render the <tt>people/_form</tt> partial, setting a
+ # local variable called <tt>form</tt> which references the yielded
+ # FormBuilder. The <tt>params</tt> object created when this form is
+ # submitted would look like:
#
# {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
#
- # The params hash has a nested <tt>person</tt> value, which can therefore be accessed with <tt>params[:person]</tt> in the controller.
- # If were editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than <tt>Person.new</tt> in the controller), the objects
- # attribute values are filled into the form (e.g., the <tt>person_first_name</tt> field would have that person's first name in it).
+ # The params hash has a nested <tt>person</tt> value, which can therefore
+ # be accessed with <tt>params[:person]</tt> in the controller. If were
+ # editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than
+ # <tt>Person.new</tt> in the controller), the objects attribute values are
+ # filled into the form (e.g., the <tt>person_first_name</tt> field would
+ # have that person's first name in it).
#
- # If the object name contains square brackets the id for the object will be inserted. For example:
+ # If the object name contains square brackets the id for the object will be
+ # inserted. For example:
#
# <%= text_field "person[]", "name" %>
#
# ...will generate the following ERb.
#
# <input type="text" id="person_<%= @person.id %>_name" name="person[<%= @person.id %>][name]" value="<%= @person.name %>" />
#
- # If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
- # used by <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may come in handy. Example:
+ # If the helper is being used to generate a repetitive sequence of similar
+ # form elements, for example in a partial used by
+ # <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may
+ # come in handy. Example:
#
# <%= text_field "person", "name", "index" => 1 %>
#
# ...becomes...
#
# <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
#
- # An <tt>index</tt> option may also be passed to <tt>form_for</tt> and <tt>fields_for</tt>. This automatically applies
- # the <tt>index</tt> to all the nested fields.
+ # An <tt>index</tt> option may also be passed to <tt>form_for</tt> and
+ # <tt>fields_for</tt>. This automatically applies the <tt>index</tt> to
+ # all the nested fields.
#
- # There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
- # link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
+ # There are also methods for helping to build form tags in
+ # link:classes/ActionView/Helpers/FormOptionsHelper.html,
+ # link:classes/ActionView/Helpers/DateHelper.html, and
+ # link:classes/ActionView/Helpers/ActiveRecordHelper.html
module FormHelper
- # Creates a form and a scope around a specific model object that is used as
- # a base for questioning about values for the fields.
+ # Creates a form and a scope around a specific model object that is used
+ # as a base for questioning about values for the fields.
#
# Rails provides succinct resource-oriented form generation with +form_for+
# like this:
@@ -86,13 +103,15 @@ module FormHelper
# <%= f.text_field :author %><br />
# <% end %>
#
- # There, +form_for+ is able to generate the rest of RESTful form parameters
- # based on introspection on the record, but to understand what it does we
- # need to dig first into the alternative generic usage it is based upon.
+ # There, +form_for+ is able to generate the rest of RESTful form
+ # parameters based on introspection on the record, but to understand what
+ # it does we need to dig first into the alternative generic usage it is
+ # based upon.
#
# === Generic form_for
#
- # The generic way to call +form_for+ yields a form builder around a model:
+ # The generic way to call +form_for+ yields a form builder around a
+ # model:
#
# <% form_for :person, :url => { :action => "update" } do |f| %>
# <%= f.error_messages %>
@@ -103,8 +122,8 @@ module FormHelper
# <% end %>
#
# There, the first argument is a symbol or string with the name of the
- # object the form is about, and also the name of the instance variable the
- # object is stored in.
+ # object the form is about, and also the name of the instance variable
+ # the object is stored in.
#
# The form builder acts as a regular form helper that somehow carries the
# model. Thus, the idea is that
@@ -137,17 +156,18 @@ module FormHelper
# In any of its variants, the rightmost argument to +form_for+ is an
# optional hash of options:
#
- # * <tt>:url</tt> - The URL the form is submitted to. It takes the same fields
- # you pass to +url_for+ or +link_to+. In particular you may pass here a
- # named route directly as well. Defaults to the current action.
+ # * <tt>:url</tt> - The URL the form is submitted to. It takes the same
+ # fields you pass to +url_for+ or +link_to+. In particular you may pass
+ # here a named route directly as well. Defaults to the current action.
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
#
- # Worth noting is that the +form_for+ tag is called in a ERb evaluation block,
- # not an ERb output block. So that's <tt><% %></tt>, not <tt><%= %></tt>.
+ # Worth noting is that the +form_for+ tag is called in a ERb evaluation
+ # block, not an ERb output block. So that's <tt><% %></tt>, not
+ # <tt><%= %></tt>.
#
# Also note that +form_for+ doesn't create an exclusive scope. It's still
- # possible to use both the stand-alone FormHelper methods and methods from
- # FormTagHelper. For example:
+ # possible to use both the stand-alone FormHelper methods and methods
+ # from FormTagHelper. For example:
#
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
# First name: <%= f.text_field :first_name %>
@@ -156,16 +176,16 @@ module FormHelper
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
# <% end %>
#
- # This also works for the methods in FormOptionHelper and DateHelper that are
- # designed to work with an object as base, like FormOptionHelper#collection_select
- # and DateHelper#datetime_select.
+ # This also works for the methods in FormOptionHelper and DateHelper that
+ # are designed to work with an object as base, like
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
#
# === Resource-oriented style
#
- # As we said above, in addition to manually configuring the +form_for+ call,
- # you can rely on automated resource identification, which will use the conventions
- # and named routes of that approach. This is the preferred way to use +form_for+
- # nowadays.
+ # As we said above, in addition to manually configuring the +form_for+
+ # call, you can rely on automated resource identification, which will use
+ # the conventions and named routes of that approach. This is the
+ # preferred way to use +form_for+ nowadays.
#
# For example, if <tt>@post</tt> is an existing record you want to edit
#
@@ -205,8 +225,10 @@ module FormHelper
#
# === Customized form builders
#
- # You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers,
- # then use your custom builder. For example, let's say you made a helper to automatically add labels to form inputs.
+ # You can also build forms using a customized FormBuilder class. Subclass
+ # FormBuilder and override or define some more helpers, then use your
+ # custom builder. For example, let's say you made a helper to
+ # automatically add labels to form inputs.
#
# <% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
# <%= f.text_field :first_name %>
@@ -219,16 +241,23 @@ module FormHelper
#
# <%= render :partial => f %>
#
- # The rendered template is <tt>people/_labelling_form</tt> and the local variable referencing the form builder is called <tt>labelling_form</tt>.
+ # The rendered template is <tt>people/_labelling_form</tt> and the local
+ # variable referencing the form builder is called
+ # <tt>labelling_form</tt>.
+ #
+ # The custom FormBuilder class is automatically merged with the options
+ # of a nested fields_for call, unless it's explicitely set.
#
- # In many cases you will want to wrap the above in another helper, so you could do something like the following:
+ # In many cases you will want to wrap the above in another helper, so you
+ # could do something like the following:
#
# def labelled_form_for(record_or_name_or_array, *args, &proc)
# options = args.extract_options!
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
# end
#
- # If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.
+ # If you don't need to attach a form to a model instance, then check out
+ # FormTagHelper#form_tag.
def form_for(record_or_name_or_array, *args, &proc)
raise ArgumentError, "Missing block" unless block_given?
@@ -910,6 +939,11 @@ def fields_for(record_or_name_or_array, *args, &block)
index = ""
end
+ if options[:builder]
+ args << {} unless args.last.is_a?(Hash)
+ args.last[:builder] ||= options[:builder]
+ end
+
case record_or_name_or_array
when String, Symbol
if nested_attributes_association?(record_or_name_or_array)
View
41 actionpack/test/template/form_helper_test.rb
@@ -1001,6 +1001,47 @@ def test_fields_for_with_labelled_builder
assert_dom_equal expected, output_buffer
end
+ def test_form_for_with_labelled_builder_with_nested_fields_for_without_options_hash
+ klass = nil
+
+ form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ f.fields_for(:comments, Comment.new) do |nested_fields|
+ klass = nested_fields.class
+ ''
+ end
+ end
+
+ assert_equal LabelledFormBuilder, klass
+ end
+
+ def test_form_for_with_labelled_builder_with_nested_fields_for_with_options_hash
+ klass = nil
+
+ form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ f.fields_for(:comments, Comment.new, :index => 'foo') do |nested_fields|
+ klass = nested_fields.class
+ ''
+ end
+ end
+
+ assert_equal LabelledFormBuilder, klass
+ end
+
+ class LabelledFormBuilderSubclass < LabelledFormBuilder; end
+
+ def test_form_for_with_labelled_builder_with_nested_fields_for_with_custom_builder
+ klass = nil
+
+ form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ f.fields_for(:comments, Comment.new, :builder => LabelledFormBuilderSubclass) do |nested_fields|
+ klass = nested_fields.class
+ ''
+ end
+ end
+
+ assert_equal LabelledFormBuilderSubclass, klass
+ end
+
def test_form_for_with_html_options_adds_options_to_form_tag
form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\"></form>"
View
2 activerecord/CHANGELOG
@@ -1,4 +1,4 @@
-*Edge*
+*2.3.1 [RC2] (February 27th, 2009)*
* Added ActiveRecord::Base.each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck]
View
2 activerecord/Rakefile
@@ -177,7 +177,7 @@ spec = Gem::Specification.new do |s|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end
- s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
+ s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
View
122 activerecord/lib/active_record/associations.rb
@@ -22,7 +22,7 @@ def initialize(reflection)
through_reflection = reflection.through_reflection
source_reflection_names = reflection.source_reflection_names
source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
- super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '}?")
+ super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
end
end
@@ -786,11 +786,7 @@ module ClassMethods
# 'ORDER BY p.first_name'
def has_many(association_id, options = {}, &extension)
reflection = create_has_many_reflection(association_id, options, &extension)
-
configure_dependency_for_has_many(reflection)
-
- add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
- add_multiple_associated_save_callbacks(reflection.name)
add_association_callbacks(reflection.name, reflection.options)
if options[:through]
@@ -872,10 +868,10 @@ def has_many(association_id, options = {}, &extension)
# [: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
- # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
+ # <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
- # association is a polymorphic +belongs_to+.
+ # association is a polymorphic +belongs_to+.
# [:readonly]
# If true, the associated object is readonly through the association.
# [:validate]
@@ -898,22 +894,9 @@ def has_one(association_id, options = {})
association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
else
reflection = create_has_one_reflection(association_id, options)
-
- method_name = "has_one_after_save_for_#{reflection.name}".to_sym
- define_method(method_name) do
- association = association_instance_get(reflection.name)
- if association && (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
- association[reflection.primary_key_name] = id
- association.save(true)
- end
- end
- after_save method_name
-
- add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
association_accessor_methods(reflection, HasOneAssociation)
association_constructor_method(:build, reflection, HasOneAssociation)
association_constructor_method(:create, reflection, HasOneAssociation)
-
configure_dependency_for_has_one(reflection)
end
end
@@ -1006,40 +989,10 @@ def belongs_to(association_id, options = {})
if reflection.options[:polymorphic]
association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
-
- method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym
- define_method(method_name) do
- association = association_instance_get(reflection.name)
- if association && association.target
- if association.new_record?
- association.save(true)
- end
-
- if association.updated?
- self[reflection.primary_key_name] = association.id
- self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
- end
- end
- end
- before_save method_name
else
association_accessor_methods(reflection, BelongsToAssociation)
association_constructor_method(:build, reflection, BelongsToAssociation)
association_constructor_method(:create, reflection, BelongsToAssociation)
-
- method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym
- define_method(method_name) do
- if association = association_instance_get(reflection.name)
- if association.new_record?
- association.save(true)
- end
-
- if association.updated?
- self[reflection.primary_key_name] = association.id
- end
- end
- end
- before_save method_name
end
# Create the callbacks to update counter cache
@@ -1067,8 +1020,6 @@ def belongs_to(association_id, options = {})
)
end
- add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
-
configure_dependency_for_belongs_to(reflection)
end
@@ -1234,9 +1185,6 @@ def belongs_to(association_id, options = {})
# 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
def has_and_belongs_to_many(association_id, options = {}, &extension)
reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
-
- add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
- add_multiple_associated_save_callbacks(reflection.name)
collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
# Don't use a before_destroy callback since users' before_destroy
@@ -1358,70 +1306,6 @@ def collection_accessor_methods(reflection, association_proxy_class, writer = tr
end
end
- def add_single_associated_validation_callbacks(association_name)
- method_name = "validate_associated_records_for_#{association_name}".to_sym
- define_method(method_name) do
- if association = association_instance_get(association_name)
- errors.add association_name unless association.target.nil? || association.valid?
- end
- end
-
- validate method_name
- end
-
- def add_multiple_associated_validation_callbacks(association_name)
- method_name = "validate_associated_records_for_#{association_name}".to_sym
- define_method(method_name) do
- association = association_instance_get(association_name)
-
- if association
- if new_record?
- association
- elsif association.loaded?
- association.select { |record| record.new_record? }
- else
- association.target.select { |record| record.new_record? }
- end.each do |record|
- errors.add association_name unless record.valid?
- end
- end
- end
-
- validate method_name
- end
-
- def add_multiple_associated_save_callbacks(association_name)
- method_name = "before_save_associated_records_for_#{association_name}".to_sym
- define_method(method_name) do
- @new_record_before_save = new_record?
- true
- end
- before_save method_name
-
- method_name = "after_create_or_update_associated_records_for_#{association_name}".to_sym
- define_method(method_name) do
- association = association_instance_get(association_name)
-
- records_to_save = if @new_record_before_save
- association
- elsif association && association.loaded?
- association.select { |record| record.new_record? }
- elsif association && !association.loaded?
- association.target.select { |record| record.new_record? }
- else
- []
- end
- records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
-
- # reconstruct the SQL queries now that we know the owner's id
- association.send(:construct_sql) if association.respond_to?(:construct_sql)
- end
-
- # Doesn't use after_save as that would save associations added in after_create/after_update twice
- after_create method_name
- after_update method_name
- end
-
def association_constructor_method(constructor, reflection, association_proxy_class)
define_method("#{constructor}_#{reflection.name}") do |*params|
attributees = params.first unless params.empty?
View
4 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -28,12 +28,12 @@ def count_records
load_target.size
end
- def insert_record(record, force=true)
+ def insert_record(record, force = true, validate = true)
if record.new_record?
if force
record.save!
else
- return false unless record.save
+ return false unless record.save(validate)
end
end
View
4 activerecord/lib/active_record/associations/has_many_association.rb
@@ -56,9 +56,9 @@ def cached_counter_attribute_name
"#{@reflection.name}_count"
end
- def insert_record(record)
+ def insert_record(record, force = false, validate = true)
set_belongs_to_association_for(record)
- record.save
+ force ? record.save! : record.save(validate)
end
# Deletes the records according to the <tt>:dependent</tt> option.
View
4 activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -47,12 +47,12 @@ def construct_find_options!(options)
options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil?
end
- def insert_record(record, force=true)
+ def insert_record(record, force = true, validate = true)
if record.new_record?
if force
record.save!
else
- return false unless record.save
+ return false unless record.save(validate)
end
end
through_reflection = @reflection.through_reflection
View
240 activerecord/lib/active_record/autosave_association.rb
@@ -125,79 +125,63 @@ module ActiveRecord
# post.author.name = ''
# post.save(false) # => true
module AutosaveAssociation
+ ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
+
def self.included(base)
base.class_eval do
+ base.extend(ClassMethods)
alias_method_chain :reload, :autosave_associations
- alias_method_chain :save, :autosave_associations
- alias_method_chain :save!, :autosave_associations
- alias_method_chain :valid?, :autosave_associations
- %w{ has_one belongs_to has_many has_and_belongs_to_many }.each do |type|
+ ASSOCIATION_TYPES.each do |type|
base.send("valid_keys_for_#{type}_association") << :autosave
end
end
end
- # Saves the parent, <tt>self</tt>, and any loaded autosave associations.
- # In addition, it destroys all children that were marked for destruction
- # with mark_for_destruction.
- #
- # This all happens inside a transaction, _if_ the Transactions module is included into
- # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
- def save_with_autosave_associations(perform_validation = true)
- returning(save_without_autosave_associations(perform_validation)) do |valid|
- if valid
- self.class.reflect_on_all_autosave_associations.each do |reflection|
- if (association = association_instance_get(reflection.name)) && association.loaded?
- if association.is_a?(Array)
- association.proxy_target.each do |child|
- child.marked_for_destruction? ? child.destroy : child.save(perform_validation)
- end
- else
- association.marked_for_destruction? ? association.destroy : association.save(perform_validation)
- end
- end
+ module ClassMethods
+ private
+
+ # def belongs_to(name, options = {})
+ # super
+ # add_autosave_association_callbacks(reflect_on_association(name))
+ # end
+ ASSOCIATION_TYPES.each do |type|
+ module_eval %{
+ def #{type}(name, options = {})
+ super
+ add_autosave_association_callbacks(reflect_on_association(name))
end
- end
+ }
end
- end
- # Attempts to save the record just like save_with_autosave_associations but
- # will raise a RecordInvalid exception instead of returning false if the
- # record is not valid.
- def save_with_autosave_associations!
- if valid_with_autosave_associations?
- save_with_autosave_associations(false) || raise(RecordNotSaved)
- else
- raise RecordInvalid.new(self)
- end
- end
+ # Adds a validate and save callback for the association as specified by
+ # the +reflection+.
+ def add_autosave_association_callbacks(reflection)
+ save_method = "autosave_associated_records_for_#{reflection.name}"
+ validation_method = "validate_associated_records_for_#{reflection.name}"
+ validate validation_method
- # Returns whether or not the parent, <tt>self</tt>, and any loaded autosave associations are valid.
- def valid_with_autosave_associations?
- if valid_without_autosave_associations?
- self.class.reflect_on_all_autosave_associations.all? do |reflection|
- if (association = association_instance_get(reflection.name)) && association.loaded?
- if association.is_a?(Array)
- association.proxy_target.all? { |child| autosave_association_valid?(reflection, child) }
- else
- autosave_association_valid?(reflection, association)
- end
- else
- true # association not loaded yet, so it should be valid
+ case reflection.macro
+ when :has_many, :has_and_belongs_to_many
+ before_save :before_save_collection_association
+
+ define_method(save_method) { save_collection_association(reflection) }
+ # Doesn't use after_save as that would save associations added in after_create/after_update twice
+ after_create save_method
+ after_update save_method
+
+ define_method(validation_method) { validate_collection_association(reflection) }
+ else
+ case reflection.macro
+ when :has_one
+ define_method(save_method) { save_has_one_association(reflection) }
+ after_save save_method
+ when :belongs_to
+ define_method(save_method) { save_belongs_to_association(reflection) }
+ before_save save_method
end
+ define_method(validation_method) { validate_single_association(reflection) }
end
- else
- false # self was not valid
- end
- end
-
- # Returns whether or not the association is valid and applies any errors to the parent, <tt>self</tt>, if it wasn't.
- def autosave_association_valid?(reflection, association)
- returning(association.valid?) do |valid|
- association.errors.each do |attribute, message|
- errors.add "#{reflection.name}_#{attribute}", message
- end unless valid
end
end
@@ -221,5 +205,145 @@ def mark_for_destruction
def marked_for_destruction?
@marked_for_destruction
end
+
+ private
+
+ # Returns the record for an association collection that should be validated
+ # or saved. If +autosave+ is +false+ only new records will be returned,
+ # unless the parent is/was a new record itself.
+ def associated_records_to_validate_or_save(association, new_record, autosave)
+ if new_record
+ association
+ elsif association.loaded?
+ autosave ? association : association.select { |record| record.new_record? }
+ else
+ autosave ? association.target : association.target.select { |record| record.new_record? }
+ end
+ end
+
+ # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
+ # turned on for the association specified by +reflection+.
+ def validate_single_association(reflection)
+ if reflection.options[:validate] == true || reflection.options[:autosave] == true
+ if (association = association_instance_get(reflection.name)) && !association.target.nil?
+ association_valid?(reflection, association)
+ end
+ end
+ end
+
+ # Validate the associated records if <tt>:validate</tt> or
+ # <tt>:autosave</tt> is turned on for the association specified by
+ # +reflection+.
+ def validate_collection_association(reflection)
+ if reflection.options[:validate] != false && association = association_instance_get(reflection.name)
+ if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
+ records.each { |record| association_valid?(reflection, record) }
+ end
+ end
+ end
+
+ # Returns whether or not the association is valid and applies any errors to
+ # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
+ # enabled records if they're marked_for_destruction?.
+ def association_valid?(reflection, association)
+ unless valid = association.valid?
+ if reflection.options[:autosave]
+ unless association.marked_for_destruction?
+ association.errors.each do |attribute, message|
+ attribute = "#{reflection.name}_#{attribute}"
+ errors.add(attribute, message) unless errors.on(attribute)
+ end
+ end
+ else
+ errors.add(reflection.name)
+ end
+ end
+ valid
+ end
+
+ # Is used as a before_save callback to check while saving a collection
+ # association whether or not the parent was a new record before saving.
+ def before_save_collection_association
+ @new_record_before_save = new_record?
+ true
+ end
+
+ # Saves any new associated records, or all loaded autosave associations if
+ # <tt>:autosave</tt> is enabled on the association.
+ #
+ # In addition, it destroys all children that were marked for destruction
+ # with mark_for_destruction.
+ #
+ # This all happens inside a transaction, _if_ the Transactions module is included into
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
+ def save_collection_association(reflection)
+ if association = association_instance_get(reflection.name)
+ autosave = reflection.options[:autosave]
+
+ if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
+ records.each do |record|
+ if autosave && record.marked_for_destruction?
+ record.destroy
+ elsif @new_record_before_save || record.new_record?
+ if autosave
+ association.send(:insert_record, record, false, false)
+ else
+ association.send(:insert_record, record)
+ end
+ elsif autosave
+ record.save(false)
+ end
+ end
+ end
+
+ # reconstruct the SQL queries now that we know the owner's id
+ association.send(:construct_sql) if association.respond_to?(:construct_sql)
+ end
+ end
+
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
+ # on the association.
+ #
+ # In addition, it will destroy the association if it was marked for
+ # destruction with mark_for_destruction.
+ #
+ # This all happens inside a transaction, _if_ the Transactions module is included into
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
+ def save_has_one_association(reflection)
+ if association = association_instance_get(reflection.name)
+ if reflection.options[:autosave] && association.marked_for_destruction?
+ association.destroy
+ elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
+ association[reflection.primary_key_name] = id
+ association.save(false)
+ end
+ end
+ end
+
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
+ # on the association.
+ #
+ # In addition, it will destroy the association if it was marked for
+ # destruction with mark_for_destruction.
+ #
+ # This all happens inside a transaction, _if_ the Transactions module is included into
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
+ def save_belongs_to_association(reflection)
+ if association = association_instance_get(reflection.name)
+ if reflection.options[:autosave] && association.marked_for_destruction?
+ association.destroy
+ else
+ association.save(false) if association.new_record? || reflection.options[:autosave]
+
+ if association.updated?
+ self[reflection.primary_key_name] = association.id
+ # TODO: Removing this code doesn't seem to matter…
+ if reflection.options[:polymorphic]
+ self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
+ end
+ end
+ end
+ end
+ end
end
end
View
5 activerecord/lib/active_record/named_scope.rb
@@ -1,11 +1,12 @@
module ActiveRecord
module NamedScope
- # All subclasses of ActiveRecord::Base have two named \scopes:
- # * <tt>all</tt> - which is similar to a <tt>find(:all)</tt> query, and
+ # All subclasses of ActiveRecord::Base have one named scope:
# * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
#
# These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
# intermediate values (scopes) around as first-class objects is convenient.
+ #
+ # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
def self.included(base)
base.class_eval do
extend ClassMethods
View
2 activerecord/lib/active_record/version.rb
@@ -2,7 +2,7 @@ module ActiveRecord
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
- TINY = 0
+ TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
View
129 activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -190,19 +190,6 @@ def test_belongs_to_counter_after_update_attributes
assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
end
- def test_assignment_before_parent_saved
- client = Client.find(:first)
- apple = Firm.new("name" => "Apple")
- client.firm = apple
- assert_equal apple, client.firm
- assert apple.new_record?
- assert client.save
- assert apple.save
- assert !apple.new_record?
- assert_equal apple, client.firm
- assert_equal apple, client.firm(true)
- end
-
def test_assignment_before_child_saved
final_cut = Client.new("name" => "Final Cut")
firm = Firm.find(1)
@@ -215,19 +202,6 @@ def test_assignment_before_child_saved
assert_equal firm, final_cut.firm(true)
end
- def test_assignment_before_either_saved
- final_cut = Client.new("name" => "Final Cut")
- apple = Firm.new("name" => "Apple")
- final_cut.firm = apple
- assert final_cut.new_record?
- assert apple.new_record?
- assert final_cut.save
- assert !final_cut.new_record?
- assert !apple.new_record?
- assert_equal apple, final_cut.firm
- assert_equal apple, final_cut.firm(true)
- end
-
def test_new_record_with_foreign_key_but_no_object
c = Client.new("firm_id" => 1)
assert_equal Firm.find(:first), c.firm_with_basic_id
@@ -274,90 +248,6 @@ def test_custom_counter_cache
assert_equal 17, reply.replies.size
end
- def test_store_two_association_with_one_save
- num_orders = Order.count
- num_customers = Customer.count
- order = Order.new
-
- customer1 = order.billing = Customer.new
- customer2 = order.shipping = Customer.new
- assert order.save
- assert_equal customer1, order.billing
- assert_equal customer2, order.shipping
-
- order.reload
-
- assert_equal customer1, order.billing
- assert_equal customer2, order.shipping
-
- assert_equal num_orders +1, Order.count
- assert_equal num_customers +2, Customer.count
- end
-
-
- def test_store_association_in_two_relations_with_one_save
- num_orders = Order.count
- num_customers = Customer.count
- order = Order.new
-
- customer = order.billing = order.shipping = Customer.new
- assert order.save
- assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
- order.reload
-
- assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
- assert_equal num_orders +1, Order.count
- assert_equal num_customers +1, Customer.count
- end
-
- def test_store_association_in_two_relations_with_one_save_in_existing_object
- num_orders = Order.count
- num_customers = Customer.count
- order = Order.create
-
- customer = order.billing = order.shipping = Customer.new
- assert order.save
- assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
- order.reload
-
- assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
- assert_equal num_orders +1, Order.count
- assert_equal num_customers +1, Customer.count
- end
-
- def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
- num_orders = Order.count
- num_customers = Customer.count
- order = Order.create
-
- customer = order.billing = order.shipping = Customer.new
- assert order.save
- assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
- order.reload
-
- customer = order.billing = order.shipping = Customer.new
-
- assert order.save
- order.reload
-
- assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
- assert_equal num_orders +1, Order.count
- assert_equal num_customers +2, Customer.count
- end
-
-
def test_association_assignment_sticks
post = Post.find(:first)
@@ -410,25 +300,6 @@ def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_recor
assert_equal nil, sponsor.sponsorable_id
end
- def test_save_fails_for_invalid_belongs_to
- assert log = AuditLog.create(:developer_id=>0,:message=>"")
-
- log.developer = Developer.new
- assert !log.developer.valid?
- assert !log.valid?
- assert !log.save
- assert_equal "is invalid", log.errors.on("developer")
- end
-
- def test_save_succeeds_for_invalid_belongs_to_with_validate_false
- assert log = AuditLog.create(:developer_id=>0,:message=>"")
-
- log.unvalidated_developer = Developer.new
- assert !log.unvalidated_developer.valid?
- assert log.valid?
- assert log.save
- end
-
def test_belongs_to_proxy_should_not_respond_to_private_methods
assert_raises(NoMethodError) { companies(:first_firm).private_method }
assert_raises(NoMethodError) { companies(:second_client).firm.private_method }
View
129 activerecord/test/cases/associations/has_many_associations_test.rb
@@ -317,81 +317,6 @@ def test_adding_a_collection
assert_equal 3, companies(:first_firm).clients_of_firm(true).size
end
- def test_adding_before_save
- no_of_firms = Firm.count
- no_of_clients = Client.count
-
- new_firm = Firm.new("name" => "A New Firm, Inc")
- c = Client.new("name" => "Apple")
-
- new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
- assert_equal 1, new_firm.clients_of_firm.size
- new_firm.clients_of_firm << c
- assert_equal 2, new_firm.clients_of_firm.size
-
- assert_equal no_of_firms, Firm.count # Firm was not saved to database.
- assert_equal no_of_clients, Client.count # Clients were not saved to database.
- assert new_firm.save
- assert !new_firm.new_record?
- assert !c.new_record?
- assert_equal new_firm, c.firm
- assert_equal no_of_firms+1, Firm.count # Firm was saved to database.
- assert_equal no_of_clients+2, Client.count # Clients were saved to database.
-
- assert_equal 2, new_firm.clients_of_firm.size
- assert_equal 2, new_firm.clients_of_firm(true).size
- end
-
- def test_invalid_adding
- firm = Firm.find(1)
- assert !(firm.clients_of_firm << c = Client.new)
- assert c.new_record?
- assert !firm.valid?
- assert !firm.save
- assert c.new_record?
- end
-
- def test_invalid_adding_before_save
- no_of_firms = Firm.count
- no_of_clients = Client.count
- new_firm = Firm.new("name" => "A New Firm, Inc")
- new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
- assert c.new_record?
- assert !c.valid?
- assert !new_firm.valid?
- assert !new_firm.save
- assert c.new_record?
- assert new_firm.new_record?
- end
-
- def test_invalid_adding_with_validate_false
- firm = Firm.find(:first)
- client = Client.new
- firm.unvalidated_clients_of_firm << client
-
- assert firm.valid?
- assert !client.valid?
- assert firm.save
- assert client.new_record?
- end
-
- def test_valid_adding_with_validate_false
- no_of_clients = Client.count
-
- firm = Firm.find(:first)
- client = Client.new("name" => "Apple")
-
- assert firm.valid?
- assert client.valid?
- assert client.new_record?
-
- firm.unvalidated_clients_of_firm << client
-
- assert firm.save
- assert !client.new_record?
- assert_equal no_of_clients+1, Client.count
- end
-
def test_build
company = companies(:first_firm)
new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
@@ -400,10 +325,6 @@ def test_build
assert_equal "Another Client", new_client.name
assert new_client.new_record?
assert_equal new_client, company.clients_of_firm.last
- company.name += '-changed'
- assert_queries(2) { assert company.save }
- assert !new_client.new_record?
- assert_equal 2, company.clients_of_firm(true).size
end
def test_collection_size_after_building
@@ -428,11 +349,7 @@ def test_collection_size_twice_for_regressions
def test_build_many
company = companies(:first_firm)
new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
-
assert_equal 2, new_clients.size
- company.name += '-changed'
- assert_queries(3) { assert company.save }
- assert_equal 3, company.clients_of_firm(true).size
end
def test_build_followed_by_save_does_not_load_target
@@ -463,10 +380,6 @@ def test_build_via_block
assert_equal "Another Client", new_client.name
assert new_client.new_record?
assert_equal new_client, company.clients_of_firm.last
- company.name += '-changed'
- assert_queries(2) { assert company.save }
- assert !new_client.new_record?
- assert_equal 2, company.clients_of_firm(true).size
end
def test_build_many_via_block
@@ -480,10 +393,6 @@ def test_build_many_via_block
assert_equal 2, new_clients.size
assert_equal "changed", new_clients.first.name
assert_equal "changed", new_clients.last.name
-
- company.name += '-changed'
- assert_queries(3) { assert company.save }
- assert_equal 3, company.clients_of_firm(true).size
end
def test_create_without_loading_association
@@ -501,16 +410,6 @@ def test_create_without_loading_association
assert_equal 2, first_firm.clients_of_firm.size
end
- def test_invalid_build
- new_client = companies(:first_firm).clients_of_firm.build
- assert new_client.new_record?
- assert !new_client.valid?
- assert_equal new_client, companies(:first_firm).clients_of_firm.last
- assert !companies(:first_firm).save
- assert new_client.new_record?
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
- end
-
def test_create
force_signal37_to_load_all_clients_of_firm
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
@@ -843,15 +742,6 @@ def test_replace_with_new
assert !firm.clients.include?(:first_client)
end
- def test_replace_on_new_object
- firm = Firm.new("name" => "New Firm")
- firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
- assert firm.save
- firm.reload
- assert_equal 2, firm.clients.length
- assert firm.clients.include?(Client.find_by_name("New Client"))
- end
-
def test_get_ids
assert_equal [companies(:first_client).id, companies(:second_client).id], companies(:first_firm).client_ids
end
@@ -879,15 +769,6 @@ def test_get_ids_for_unloaded_finder_sql_associations_loads_them
assert company.clients_using_sql.loaded?
end
- def test_assign_ids
- firm = Firm.new("name" => "Apple")
- firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
- firm.save
- firm.reload
- assert_equal 2, firm.clients.length
- assert firm.clients.include?(companies(:second_client))
- end
-
def test_assign_ids_ignoring_blanks
firm = Firm.create!(:name => 'Apple')
firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
@@ -910,16 +791,6 @@ def test_modifying_a_through_a_has_many_should_raise
].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection, &block) }
end
-
- def test_assign_ids_for_through_a_belongs_to
- post = Post.new(:title => "Assigning IDs works!", :body => "You heared it here first, folks!")
- post.person_ids = [people(:david).id, people(:michael).id]
- post.save
- post.reload
- assert_equal 2, post.people.length
- assert post.people.include?(people(:david))
- end
-
def test_dynamic_find_should_respect_association_order_for_through
assert_equal Comment.find(10), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'")
assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
View
87 activerecord/test/cases/associations/has_one_associations_test.rb
@@ -193,28 +193,6 @@ def test_build
assert_equal account, firm.account
end
- def test_build_before_child_saved
- firm = Firm.find(1)
-
- account = firm.account.build("credit_limit" => 1000)
- assert_equal account, firm.account
- assert account.new_record?
- assert firm.save
- assert_equal account, firm.account
- assert !account.new_record?
- end
-
- def test_build_before_either_saved
- firm = Firm.new("name" => "GlobalMegaCorp")
-
- firm.account = account = Account.new("credit_limit" => 1000)
- assert_equal account, firm.account
- assert account.new_record?
- assert firm.save
- assert_equal account, firm.account
- assert !account.new_record?
- end
-
def test_failing_build_association
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
@@ -253,16 +231,6 @@ def test_dependence_with_missing_association_and_nullify
firm.destroy
end
- def test_assignment_before_parent_saved
- firm = Firm.new("name" => "GlobalMegaCorp")
- firm.account = a = Account.find(1)
- assert firm.new_record?
- assert_equal a, firm.account
- assert firm.save
- assert_equal a, firm.account
- assert_equal a, firm.account(true)
- end
-
def test_finding_with_interpolated_condition
firm = Firm.find(:first)
superior = firm.clients.create(:name => 'SuperiorCo')
@@ -279,61 +247,6 @@ def test_assignment_before_child_saved
assert_equal a, firm.account
assert_equal a, firm.account(true)
end
-
- def test_save_fails_for_invalid_has_one
- firm = Firm.find(:first)
- assert firm.valid?
-
- firm.account = Account.new
-
- assert !firm.account.valid?
- assert !firm.valid?
- assert !firm.save
- assert_equal "is invalid", firm.errors.on("account")
- end
-
-
- def test_save_succeeds_for_invalid_has_one_with_validate_false
- firm = Firm.find(:first)
- assert firm.valid?
-
- firm.unvalidated_account = Account.new
-
- assert !firm.unvalidated_account.valid?
- assert firm.valid?
- assert firm.save
- end
-
- def test_assignment_before_either_saved
- firm = Firm.new("name" => "GlobalMegaCorp")
- firm.account = a = Account.new("credit_limit" => 1000)
- assert firm.new_record?
- assert a.new_record?
- assert_equal a, firm.account
- assert firm.save
- assert !firm.new_record?
- assert !a.new_record?
- assert_equal a, firm.account
- assert_equal a, firm.account(true)
- end
-
- def test_not_resaved_when_unchanged
- firm = Firm.find(:first, :include => :account)
- firm.name += '-changed'
- assert_queries(1) { firm.save! }
-
- firm = Firm.find(:first)
- firm.account = Account.find(:first)
- assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
-
- firm = Firm.find(:first).clone
- firm.account = Account.find(:first)
- assert_queries(2) { firm.save! }
-
- firm = Firm.find(:first).clone
- firm.account = Account.find(:first).clone
- assert_queries(2) { firm.save! }
- end
def test_save_still_works_after_accessing_nil_has_one
jp = Company.new :name => 'Jaded Pixel'
View
474 activerecord/test/cases/autosave_association_test.rb
@@ -1,10 +1,17 @@
-require "cases/helper"
-require "models/pirate"
-require "models/ship"
-require "models/ship_part"
-require "models/bird"
-require "models/parrot"
-require "models/treasure"
+require 'cases/helper'
+require 'models/bird'
+require 'models/company'
+require 'models/customer'
+require 'models/developer'
+require 'models/order'
+require 'models/parrot'
+require 'models/person'
+require 'models/pirate'
+require 'models/post'
+require 'models/reader'
+require 'models/ship'
+require 'models/ship_part'
+require 'models/treasure'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_should_be_a_valid_option_for_has_one
@@ -30,6 +37,383 @@ def base
end
end
+class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
+ def test_save_fails_for_invalid_has_one
+ firm = Firm.find(:first)
+ assert firm.valid?
+
+ firm.account = Account.new
+
+ assert !firm.account.valid?
+ assert !firm.valid?
+ assert !firm.save
+ assert_equal "is invalid", firm.errors.on("account")
+ end
+
+ def test_save_succeeds_for_invalid_has_one_with_validate_false
+ firm = Firm.find(:first)
+ assert firm.valid?
+
+ firm.unvalidated_account = Account.new
+
+ assert !firm.unvalidated_account.valid?
+ assert firm.valid?
+ assert firm.save
+ end
+
+ def test_build_before_child_saved
+ firm = Firm.find(1)
+
+ account = firm.account.build("credit_limit" => 1000)
+ assert_equal account, firm.account
+ assert account.new_record?
+ assert firm.save
+ assert_equal account, firm.account
+ assert !account.new_record?
+ end
+
+ def test_build_before_either_saved
+ firm = Firm.new("name" => "GlobalMegaCorp")
+
+ firm.account = account = Account.new("credit_limit" => 1000)
+ assert_equal account, firm.account
+ assert account.new_record?
+ assert firm.save
+ assert_equal account, firm.account
+ assert !account.new_record?
+ end
+
+ def test_assignment_before_parent_saved
+ firm = Firm.new("name" => "GlobalMegaCorp")
+ firm.account = a = Account.find(1)
+ assert firm.new_record?
+ assert_equal a, firm.account
+ assert firm.save
+ assert_equal a, firm.account
+ assert_equal a, firm.account(true)
+ end
+
+ def test_assignment_before_either_saved
+ firm = Firm.new("name" => "GlobalMegaCorp")
+ firm.account = a = Account.new("credit_limit" => 1000)
+ assert firm.new_record?
+ assert a.new_record?
+ assert_equal a, firm.account
+ assert firm.save
+ assert !firm.new_record?
+ assert !a.new_record?
+ assert_equal a, firm.account
+ assert_equal a, firm.account(true)
+ end
+
+ def test_not_resaved_when_unchanged
+ firm = Firm.find(:first, :include => :account)
+ firm.name += '-changed'
+ assert_queries(1) { firm.save! }
+
+ firm = Firm.find(:first)
+ firm.account = Account.find(:first)
+ assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
+
+ firm = Firm.find(:first).clone
+ firm.account = Account.find(:first)
+ assert_queries(2) { firm.save! }
+
+ firm = Firm.find(:first).clone
+ firm.account = Account.find(:first).clone
+ assert_queries(2) { firm.save! }
+ end
+end
+
+class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
+ def test_save_fails_for_invalid_belongs_to
+ assert log = AuditLog.create(:developer_id => 0, :message => "")
+
+ log.developer = Developer.new
+ assert !log.developer.valid?
+ assert !log.valid?
+ assert !log.save
+ assert_equal "is invalid", log.errors.on("developer")
+ end
+
+ def test_save_succeeds_for_invalid_belongs_to_with_validate_false
+ assert log = AuditLog.create(:developer_id => 0, :message=> "")
+
+ log.unvalidated_developer = Developer.new
+ assert !log.unvalidated_developer.valid?
+ assert log.valid?
+ assert log.save
+ end
+
+ def test_assignment_before_parent_saved
+ client = Client.find(:first)
+ apple = Firm.new("name" => "Apple")
+ client.firm = apple
+ assert_equal apple, client.firm
+ assert apple.new_record?
+ assert client.save
+ assert apple.save
+ assert !apple.new_record?
+ assert_equal apple, client.firm
+ assert_equal apple, client.firm(true)
+ end
+
+ def test_assignment_before_either_saved
+ final_cut = Client.new("name" => "Final Cut")
+ apple = Firm.new("name" => "Apple")
+ final_cut.firm = apple
+ assert final_cut.new_record?
+ assert apple.new_record?
+ assert final_cut.save
+ assert !final_cut.new_record?
+ assert !apple.new_record?
+ assert_equal apple, final_cut.firm
+ assert_equal apple, final_cut.firm(true)
+ end
+
+ def test_store_two_association_with_one_save
+ num_orders = Order.count
+ num_customers = Customer.count
+ order = Order.new
+
+ customer1 = order.billing = Customer.new
+ customer2 = order.shipping = Customer.new
+ assert order.save
+ assert_equal customer1, order.billing
+ assert_equal customer2, order.shipping
+
+ order.reload
+
+ assert_equal customer1, order.billing
+ assert_equal customer2, order.shipping
+
+ assert_equal num_orders +1, Order.count
+ assert_equal num_customers +2, Customer.count
+ end
+
+ def test_store_association_in_two_relations_with_one_save
+ num_orders = Order.count
+ num_customers = Customer.count
+ order = Order.new
+
+ customer = order.billing = order.shipping = Customer.new
+ assert order.save
+ assert_equal customer, order.billing
+ assert_equal customer, order.shipping
+
+ order.reload
+
+ assert_equal customer, order.billing
+ assert_equal customer, order.shipping
+
+ assert_equal num_orders +1, Order.count
+ assert_equal num_customers +1, Customer.count
+ end
+
+ def test_store_association_in_two_relations_with_one_save_in_existing_object
+ num_orders = Order.count
+ num_customers = Customer.count
+ order = Order.create
+
+ customer = order.billing = order.shipping = Customer.new
+ assert order.save
+ assert_equal customer, order.billing
+ assert_equal customer, order.shipping
+
+ order.reload
+
+ assert_equal customer, order.billing
+ assert_equal customer, order.shipping
+
+ assert_equal num_orders +1, Order.count
+ assert_equal num_customers +1, Customer.count
+ end
+
+ def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
+ num_orders = Order.count
+ num_customers = Customer.count
+ order = Order.create
+
+ customer = order.billing = order.shipping = Customer.new
+ assert order.save
+ assert_equal customer, order.billing
+ assert_equal customer, order.shipping
+
+ order.reload
+
+ customer = order.billing = order.shipping = Customer.new
+
+ assert order.save
+ order.reload
+
+ assert_equal customer, order.billing
+ assert_equal customer, order.shipping
+
+ assert_equal num_orders +1, Order.count
+ assert_equal num_customers +2, Customer.count
+ end
+end
+
+class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
+ fixtures :companies, :people
+
+ def test_invalid_adding
+ firm = Firm.find(1)
+ assert !(firm.clients_of_firm << c = Client.new)
+ assert c.new_record?
+ assert !firm.valid?
+ assert !firm.save
+ assert c.new_record?
+ end
+
+ def test_invalid_adding_before_save
+ no_of_firms = Firm.count
+ no_of_clients = Client.count
+ new_firm = Firm.new("name" => "A New Firm, Inc")
+ new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
+ assert c.new_record?
+ assert !c.valid?
+ assert !new_firm.valid?
+ assert !new_firm.save
+ assert c.new_record?
+ assert new_firm.new_record?
+ end
+
+ def test_invalid_adding_with_validate_false
+ firm = Firm.find(:first)
+ client = Client.new
+ firm.unvalidated_clients_of_firm << client
+
+ assert firm.valid?
+ assert !client.valid?
+ assert firm.save
+ assert client.new_record?
+ end
+
+ def test_valid_adding_with_validate_false
+ no_of_clients = Client.count
+
+ firm = Firm.find(:first)
+ client = Client.new("name" => "Apple")
+
+ assert firm.valid?
+ assert client.valid?
+ assert client.new_record?
+
+ firm.unvalidated_clients_of_firm << client
+
+ assert firm.save
+ assert !client.new_record?
+ assert_equal no_of_clients+1, Client.count
+ end
+
+ def test_invalid_build
+ new_client = companies(:first_firm).clients_of_firm.build
+ assert new_client.new_record?
+ assert !new_client.valid?
+ assert_equal new_client, companies(:first_firm).clients_of_firm.last
+ assert !companies(:first_firm).save
+ assert new_client.new_record?
+ assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ end
+
+ def test_adding_before_save
+ no_of_firms = Firm.count
+ no_of_clients = Client.count
+
+ new_firm = Firm.new("name" => "A New Firm, Inc")
+ c = Client.new("name" => "Apple")
+
+ new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
+ assert_equal 1, new_firm.clients_of_firm.size
+ new_firm.clients_of_firm << c
+ assert_equal 2, new_firm.clients_of_firm.size
+
+ assert_equal no_of_firms, Firm.count # Firm was not saved to database.
+ assert_equal no_of_clients, Client.count # Clients were not saved to database.
+ assert new_firm.save
+ assert !new_firm.new_record?
+ assert !c.new_record?
+ assert_equal new_firm, c.firm
+ assert_equal no_of_firms+1, Firm.count # Firm was saved to database.
+ assert_equal no_of_clients+2, Client.count # Clients were saved to database.
+
+ assert_equal 2, new_firm.clients_of_firm.size
+ assert_equal 2, new_firm.clients_of_firm(true).size
+ end
+
+ def test_assign_ids
+ firm = Firm.new("name" => "Apple")
+ firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
+ firm.save
+ firm.reload
+ assert_equal 2, firm.clients.length
+ assert firm.clients.include?(companies(:second_client))
+ end
+
+ def test_assign_ids_for_through_a_belongs_to
+ post = Post.new(:title => "Assigning IDs works!", :body => "You heared it here first, folks!")
+ post.person_ids = [people(:david).id, people(:michael).id]
+ post.save
+ post.reload
+ assert_equal 2, post.people.length
+ assert post.people.include?(people(:david))
+ end
+
+ def test_build_before_save
+ company = companies(:first_firm)
+ new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
+ assert !company.clients_of_firm.loaded?
+
+ company.name += '-changed'
+ assert_queries(2) { assert company.save }
+ assert !new_client.new_record?
+ assert_equal 2, company.clients_of_firm(true).size
+ end
+
+ def test_build_many_before_save
+ company = companies(:first_firm)
+ new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
+
+ company.name += '-changed'
+ assert_queries(3) { assert company.save }
+ assert_equal 3, company.clients_of_firm(true).size
+ end
+
+ def test_build_via_block_before_save
+ company = companies(:first_firm)
+ new_client = assert_no_queries { company.clients_of_firm.build {|client| client.name = "Another Client" } }
+ assert !company.clients_of_firm.loaded?
+
+ company.name += '-changed'
+ assert_queries(2) { assert company.save }
+ assert !new_client.new_record?
+ assert_equal 2, company.clients_of_firm(true).size
+ end
+
+ def test_build_many_via_block_before_save
+ company = companies(:first_firm)
+ new_clients = assert_no_queries do
+ company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
+ client.name = "changed"
+ end
+ end
+
+ company.name += '-changed'
+ assert_queries(3) { assert company.save }
+ assert_equal 3, company.clients_of_firm(true).size
+ end
+
+ def test_replace_on_new_object
+ firm = Firm.new("name" => "New Firm")
+ firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
+ assert firm.save
+ firm.reload
+ assert_equal 2, firm.clients.length
+ assert firm.clients.include?(Client.find_by_name("New Client"))
+ end
+end
+
class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
@@ -62,6 +446,14 @@ def test_should_destroy_a_child_association_as_part_of_the_save_transaction_if_i
assert_nil Ship.find_by_id(id)
end
+ def test_should_skip_validation_on_a_child_association_if_marked_for_destruction
+ @pirate.ship.name = ''
+ assert !@pirate.valid?
+
+ @pirate.ship.mark_for_destruction
+ assert_difference('Ship.count', -1) { @pirate.save! }
+ end
+
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child
# Stub the save method of the @pirate.ship instance to destroy and then raise an exception
class << @pirate.ship
@@ -91,6 +483,14 @@ def test_should_destroy_a_parent_association_as_part_of_the_save_transaction_if_
assert_nil Pirate.find_by_id(id)
end
+ def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
+ @ship.pirate.catchphrase = ''
+ assert !@ship.valid?
+
+ @ship.pirate.mark_for_destruction
+ assert_difference('Pirate.count', -1) { @ship.save! }
+ end
+
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent
# Stub the save method of the @ship.pirate instance to destroy and then raise an exception
class << @ship.pirate
@@ -124,6 +524,17 @@ def save(*args)
ids.each { |id| assert_nil klass.find_by_id(id) }
end
+ define_method("test_should_skip_validation_on_the_#{association_name}_association_if_marked_for_destruction") do
+ 2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
+ children = @pirate.send(association_name)
+
+ children.each { |child| child.name = '' }
+ assert !@pirate.valid?
+
+ children.each { |child| child.mark_for_destruction }
+ assert_difference("#{association_name.classify}.count", -2) { @pirate.save! }
+ end
+
define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do
2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
before = @pirate.send(association_name).map { |c| c }
@@ -181,6 +592,14 @@ def test_should_automatically_validate_the_associated_model
assert !@pirate.errors.on(:ship_name).blank?
end
+ def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
+ @pirate.ship.name = nil
+ @pirate.catchphrase = nil
+ assert !@pirate.valid?
+ assert !@pirate.errors.on(:ship_name).blank?
+ assert !@pirate.errors.on(:catchphrase).blank?
+ end
+
def test_should_still_allow_to_bypass_validations_on_the_associated_model
@pirate.catchphrase = ''
@pirate.ship.name = ''
@@ -263,6 +682,14 @@ def test_should_automatically_validate_the_associated_model
assert !@ship.errors.on(:pirate_catchphrase).blank?
end
+ def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
+ @ship.name = nil
+ @ship.pirate.catchphrase = nil
+ assert !@ship.valid?
+ assert !@ship.errors.on(:name).blank?
+ assert !@ship.errors.on(:pirate_catchphrase).blank?
+ end
+
def test_should_still_allow_to_bypass_validations_on_the_associated_model
@ship.pirate.catchphrase = ''
@ship.name = ''
@@ -326,7 +753,24 @@ def test_should_automatically_validate_the_associated_models
assert @pirate.errors.on(@association_name).blank?
end
- def test_should_still_allow_to_bypass_validations_on_the_associated_models
+ def test_should_not_use_default_invalid_error_on_associated_models
+ @pirate.send(@association_name).build(:name => '')
+
+ assert !@pirate.valid?
+ assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name")
+ assert @pirate.errors.on(@association_name).blank?
+ end
+
+ def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
+ @pirate.send(@association_name).each { |child| child.name = '' }
+ @pirate.catchphrase = nil
+
+ assert !@pirate.valid?
+ assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name")
+ assert !@pirate.errors.on(:catchphrase).blank?
+ end
+
+ def test_should_allow_to_bypass_validations_on_the_associated_models_on_update
@pirate.catchphrase = ''
@pirate.send(@association_name).each { |child| child.name = '' }
@@ -338,6 +782,20 @@ def test_should_still_allow_to_bypass_validations_on_the_associated_models
]
end
+ def test_should_validation_the_associated_models_on_create
+ assert_no_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count") do
+ 2.times { @pirate.send(@association_name).build }
+ @pirate.save(true)
+ end
+ end
+
+ def test_should_allow_to_bypass_validations_on_the_associated_models_on_create
+ assert_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count", +2) do
+ 2.times { @pirate.send(@association_name).build }
+ @pirate.save(false)
+ end
+ end
+
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
before = [@pirate.catchphrase, *@pirate.send(@association_name).map(&:name)]
new_names = ['Grace OMalley', 'Privateers Greed']
View
5 activeresource/CHANGELOG
@@ -1,3 +1,8 @@
+*2.3.1 [RC2] (February 27th, 2009)*
+
+* Nothing new, just included in 2.3.1
+
+
*2.3.0 [RC1] (February 1st, 2009)*
* Nothing new, just included in 2.3.0
View
2 activeresource/Rakefile
@@ -67,7 +67,7 @@ spec = Gem::Specification.new do |s|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end
- s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
+ s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
s.require_path = 'lib'
s.autorequire = 'active_resource'
View
2 activeresource/lib/active_resource/version.rb
@@ -2,7 +2,7 @@ module ActiveResource
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
- TINY = 0
+ TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
View
4 activesupport/CHANGELOG
@@ -1,4 +1,6 @@
-*Edge*
+*2.3.1 [RC2] (February 27th, 2009)*
+
+* Vendorize i18n 0.1.3 gem (fixes issues with incompatible character encodings in Ruby 1.9) #2038 [Akira Matsuda]
* Update bundled memcache-client from 1.5.0.5 to 1.6.4.99. See http://www.mikeperham.com/2009/02/15/memcache-client-performance/ [Mike Perham]
View
5 activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -7,10 +7,9 @@ module Conversions
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ")
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ")
def to_sentence(options = {})
-
- default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale])
+ default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale])
default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale])
- default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
+ default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
# Try to emulate to_senteces previous to 2.3
if options.has_key?(:connector) || options.has_key?(:skip_last_comma)
View
2 activesupport/lib/active_support/duration.rb
@@ -70,7 +70,7 @@ def inspect #:nodoc:
[:years, :months, :days, :minutes, :seconds].map do |length|
n = consolidated[length]
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
- end.compact.to_sentence
+ end.compact.to_sentence(:locale => :en)
end
protected
View
4 activesupport/lib/active_support/vendor.rb