diff options
author | Emilio Tagua <miloops@gmail.com> | 2009-09-14 09:47:55 -0300 |
---|---|---|
committer | Emilio Tagua <miloops@gmail.com> | 2009-09-14 09:47:55 -0300 |
commit | 24260dc0c8338aef8e24407e00750fd72a0366ea (patch) | |
tree | 849b286decba65c1136947ef6f52235335df2383 /activerecord/lib | |
parent | 0489f0c582d2ab70595296f058545b102466bebd (diff) | |
parent | 181cd109d9812d371e2d554a4846f0b2b25b1690 (diff) | |
download | rails-24260dc0c8338aef8e24407e00750fd72a0366ea.tar.gz rails-24260dc0c8338aef8e24407e00750fd72a0366ea.tar.bz2 rails-24260dc0c8338aef8e24407e00750fd72a0366ea.zip |
Merge commit 'rails/master'
Diffstat (limited to 'activerecord/lib')
7 files changed, 77 insertions, 72 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d3baf0b7fe..c83fbc3508 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -280,9 +280,10 @@ module ActiveRecord # 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. # - # Unless you enable 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, - # in which case the members are always saved. + # Unless you 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 + # to +true+ will _always_ save the members, whereas setting it to +false+ will + # _never_ save the members. # # === One-to-one associations # diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index da1fbfe0b7..4672b0723e 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -24,8 +24,8 @@ module ActiveRecord def has_primary_key? return @has_primary_key unless @has_primary_key.nil? - @has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? && - ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table])) + @has_primary_key = (@owner.connection.supports_primary_key? && + @owner.connection.primary_key(@reflection.options[:join_table])) end protected diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index c1bc8423a9..8f37fcd515 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -158,7 +158,7 @@ module ActiveRecord 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 + force_validation = (reflection.options[:validate] == true || reflection.options[:autosave] == true) case reflection.macro when :has_many, :has_and_belongs_to_many @@ -169,7 +169,10 @@ module ActiveRecord after_create save_method after_update save_method - define_method(validation_method) { validate_collection_association(reflection) } + if force_validation || (reflection.macro == :has_many && reflection.options[:validate] != false) + define_method(validation_method) { validate_collection_association(reflection) } + validate validation_method + end else case reflection.macro when :has_one @@ -179,7 +182,11 @@ module ActiveRecord define_method(save_method) { save_belongs_to_association(reflection) } before_save save_method end - define_method(validation_method) { validate_single_association(reflection) } + + if force_validation + define_method(validation_method) { validate_single_association(reflection) } + validate validation_method + end end end end @@ -223,10 +230,8 @@ module ActiveRecord # 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 + if (association = association_instance_get(reflection.name)) && !association.target.nil? + association_valid?(reflection, association) end end @@ -234,7 +239,7 @@ module ActiveRecord # <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 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 @@ -243,15 +248,15 @@ module ActiveRecord # 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?. + # enabled records if they're marked_for_destruction? or destroyed. def association_valid?(reflection, association) + return true if association.destroyed? || association.marked_for_destruction? + unless valid = association.valid? if reflection.options[:autosave] - unless association.marked_for_destruction? - association.errors.each do |attribute, message| - attribute = "#{reflection.name}_#{attribute}" - errors[attribute] << message if errors[attribute].empty? - end + association.errors.each do |attribute, message| + attribute = "#{reflection.name}_#{attribute}" + errors[attribute] << message if errors[attribute].empty? end else errors.add(reflection.name) @@ -281,9 +286,11 @@ module ActiveRecord if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave) records.each do |record| + next if record.destroyed? + if autosave && record.marked_for_destruction? association.destroy(record) - elsif @new_record_before_save || record.new_record? + elsif autosave != false && (@new_record_before_save || record.new_record?) if autosave association.send(:insert_record, record, false, false) else @@ -309,14 +316,17 @@ module ActiveRecord # 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)) && !association.target.nil? + if (association = association_instance_get(reflection.name)) && !association.target.nil? && !association.destroyed? autosave = reflection.options[:autosave] if autosave && association.marked_for_destruction? association.destroy - elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave - association[reflection.primary_key_name] = id - association.save(!autosave) + else + key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id + if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave) + association[reflection.primary_key_name] = key + association.save(!autosave) + end end end end @@ -330,12 +340,12 @@ module ActiveRecord # 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 (association = association_instance_get(reflection.name)) && !association.destroyed? autosave = reflection.options[:autosave] if autosave && association.marked_for_destruction? association.destroy - else + elsif autosave != false association.save(!autosave) if association.new_record? || autosave if association.updated? diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index d3ca7c819f..ddcbba032f 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -8,7 +8,8 @@ module MysqlCompat #:nodoc: raise 'Mysql not loaded' unless defined?(::Mysql) target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes - return if target.instance_methods.include?('all_hashes') + return if target.instance_methods.include?('all_hashes') || + target.instance_methods.include?(:all_hashes) # Ruby driver has a version string and returns null values in each_hash # C driver >= 2.7 returns null values in each_hash diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb index 320659596f..fcc9ebb4af 100644 --- a/activerecord/lib/active_record/locking/pessimistic.rb +++ b/activerecord/lib/active_record/locking/pessimistic.rb @@ -1,25 +1,3 @@ -# Copyright (c) 2006 Shugo Maeda <shugo@ruby-lang.org> -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject -# to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - module ActiveRecord module Locking # Locking::Pessimistic provides support for row-level locking using diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index bc4cca7855..3c8140816c 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -66,10 +66,10 @@ module ActiveRecord # accepts_nested_attributes_for :avatar, :allow_destroy => true # end # - # Now, when you add the <tt>_delete</tt> key to the attributes hash, with a + # Now, when you add the <tt>_destroy</tt> key to the attributes hash, with a # value that evaluates to +true+, you will destroy the associated model: # - # member.avatar_attributes = { :id => '2', :_delete => '1' } + # member.avatar_attributes = { :id => '2', :_destroy => '1' } # member.avatar.marked_for_destruction? # => true # member.save # member.avatar #=> nil @@ -89,14 +89,14 @@ module ActiveRecord # the attribute hash. # # For each hash that does _not_ have an <tt>id</tt> key a new record will - # be instantiated, unless the hash also contains a <tt>_delete</tt> key + # be instantiated, unless the hash also contains a <tt>_destroy</tt> key # that evaluates to +true+. # # params = { :member => { # :name => 'joe', :posts_attributes => [ # { :title => 'Kari, the awesome Ruby documentation browser!' }, # { :title => 'The egalitarian assumption of the modern citizen' }, - # { :title => '', :_delete => '1' } # this will be ignored + # { :title => '', :_destroy => '1' } # this will be ignored # ] # }} # @@ -144,7 +144,7 @@ module ActiveRecord # By default the associated records are protected from being destroyed. If # you want to destroy any of the associated records through the attributes # hash, you have to enable it first using the <tt>:allow_destroy</tt> - # option. This will allow you to also use the <tt>_delete</tt> key to + # option. This will allow you to also use the <tt>_destroy</tt> key to # destroy existing records: # # class Member < ActiveRecord::Base @@ -153,7 +153,7 @@ module ActiveRecord # end # # params = { :member => { - # :posts_attributes => [{ :id => '2', :_delete => '1' }] + # :posts_attributes => [{ :id => '2', :_destroy => '1' }] # }} # # member.attributes = params['member'] @@ -176,14 +176,14 @@ module ActiveRecord # Supported options: # [:allow_destroy] # If true, destroys any members from the attributes hash with a - # <tt>_delete</tt> key and a value that evaluates to +true+ + # <tt>_destroy</tt> key and a value that evaluates to +true+ # (eg. 1, '1', true, or 'true'). This option is off by default. # [:reject_if] # Allows you to specify a Proc that checks whether a record should be # built for a certain attribute hash. The hash is passed to the Proc # and the Proc should return either +true+ or +false+. When no Proc # is specified a record will be built for all attribute hashes that - # do not have a <tt>_delete</tt> that evaluates to true. + # do not have a <tt>_destroy</tt> value that evaluates to true. # Passing <tt>:all_blank</tt> instead of a Proc will create a proc # that will reject a record where all the attributes are blank. # @@ -236,15 +236,25 @@ module ActiveRecord # destruction of this association. # # See ActionView::Helpers::FormHelper::fields_for for more info. - def _delete + def _destroy marked_for_destruction? end + # Deal with deprecated _delete. + # + def _delete #:nodoc: + ActiveSupport::Deprecation.warn "_delete is deprecated in nested attributes. Use _destroy instead." + _destroy + end + private # Attribute hash keys that should not be assigned as normal attributes. # These hash keys are nested attributes implementation details. - UNASSIGNABLE_KEYS = %w{ id _delete } + # + # TODO Remove _delete from UNASSIGNABLE_KEYS when deprecation warning are + # removed. + UNASSIGNABLE_KEYS = %w( id _destroy _delete ) # Assigns the given attributes to the association. # @@ -253,14 +263,19 @@ module ActiveRecord # record will be built. # # If the given attributes include a matching <tt>:id</tt> attribute _and_ a - # <tt>:_delete</tt> key set to a truthy value, then the existing record + # <tt>:_destroy</tt> key set to a truthy value, then the existing record # will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes, allow_destroy) attributes = attributes.stringify_keys if attributes['id'].blank? unless reject_new_record?(association_name, attributes) - send("build_#{association_name}", attributes.except(*UNASSIGNABLE_KEYS)) + method = "build_#{association_name}" + if respond_to?(method) + send(method, attributes.except(*UNASSIGNABLE_KEYS)) + else + raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?" + end end elsif (existing_record = send(association_name)) && existing_record.id.to_s == attributes['id'].to_s assign_to_or_mark_for_destruction(existing_record, attributes, allow_destroy) @@ -272,7 +287,7 @@ module ActiveRecord # Hashes with an <tt>:id</tt> value matching an existing associated record # will update that record. Hashes without an <tt>:id</tt> value will build # a new record for the association. Hashes with a matching <tt>:id</tt> - # value and a <tt>:_delete</tt> key set to a truthy value will mark the + # value and a <tt>:_destroy</tt> key set to a truthy value will mark the # matched record for destruction. # # For example: @@ -280,7 +295,7 @@ module ActiveRecord # assign_nested_attributes_for_collection_association(:people, { # '1' => { :id => '1', :name => 'Peter' }, # '2' => { :name => 'John' }, - # '3' => { :id => '2', :_delete => true } + # '3' => { :id => '2', :_destroy => true } # }) # # Will update the name of the Person with ID 1, build a new associated @@ -292,7 +307,7 @@ module ActiveRecord # assign_nested_attributes_for_collection_association(:people, [ # { :id => '1', :name => 'Peter' }, # { :name => 'John' }, - # { :id => '2', :_delete => true } + # { :id => '2', :_destroy => true } # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection, allow_destroy) unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) @@ -317,25 +332,26 @@ module ActiveRecord end # Updates a record with the +attributes+ or marks it for destruction if - # +allow_destroy+ is +true+ and has_delete_flag? returns +true+. + # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+. def assign_to_or_mark_for_destruction(record, attributes, allow_destroy) - if has_delete_flag?(attributes) && allow_destroy + if has_destroy_flag?(attributes) && allow_destroy record.mark_for_destruction else record.attributes = attributes.except(*UNASSIGNABLE_KEYS) end end - # Determines if a hash contains a truthy _delete key. - def has_delete_flag?(hash) - ConnectionAdapters::Column.value_to_boolean hash['_delete'] + # Determines if a hash contains a truthy _destroy key. + def has_destroy_flag?(hash) + ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) || + ConnectionAdapters::Column.value_to_boolean(hash['_delete']) # TODO Remove after deprecation. end # Determines if a new record should be build by checking for - # has_delete_flag? or if a <tt>:reject_if</tt> proc exists for this + # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this # association and evaluates to +true+. def reject_new_record?(association_name, attributes) - has_delete_flag?(attributes) || + has_destroy_flag?(attributes) || self.class.reject_new_nested_attributes_procs[association_name].try(:call, attributes) end end diff --git a/activerecord/lib/activerecord.rb b/activerecord/lib/activerecord.rb deleted file mode 100644 index cd62b2afdc..0000000000 --- a/activerecord/lib/activerecord.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_record' |