Skip to content

Commit

Permalink
Backport of lazy evaluation of has_many ..., :dependent => :___
Browse files Browse the repository at this point in the history
[#2627 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
  • Loading branch information
pixeltrix authored and jeremy committed Apr 4, 2010
1 parent a72bcdb commit e617af1
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 25 deletions.
40 changes: 15 additions & 25 deletions activerecord/lib/active_record/associations.rb
Expand Up @@ -1415,14 +1415,6 @@ def find_with_associations(options = {})
# finder conditions.
def configure_dependency_for_has_many(reflection, extra_conditions = nil)
if reflection.options.include?(:dependent)
# Add polymorphic type if the :as option is present
dependent_conditions = []
dependent_conditions << "#{reflection.primary_key_name} = \#{record.#{reflection.name}.send(:owner_quoted_id)}"
dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.quoted_table_name) if reflection.options[:conditions]
dependent_conditions << extra_conditions if extra_conditions
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
dependent_conditions = dependent_conditions.gsub('@', '\@')
case reflection.options[:dependent]
when :destroy
method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
Expand All @@ -1431,24 +1423,22 @@ def configure_dependency_for_has_many(reflection, extra_conditions = nil)
end
before_destroy method_name
when :delete_all
module_eval %Q{
before_destroy do |record| # before_destroy do |record|
delete_all_has_many_dependencies(record, # delete_all_has_many_dependencies(record,
"#{reflection.name}", # "posts",
#{reflection.class_name}, # Post,
%@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
end # end
}
before_destroy do |record|
record.class.send(:delete_all_has_many_dependencies,
record,
reflection.name,
reflection.klass,
reflection.dependent_conditions(record, record.class, extra_conditions))
end
when :nullify
module_eval %Q{
before_destroy do |record| # before_destroy do |record|
nullify_has_many_dependencies(record, # nullify_has_many_dependencies(record,
"#{reflection.name}", # "posts",
#{reflection.class_name}, # Post,
"#{reflection.primary_key_name}", # "user_id",
%@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
end # end
}
before_destroy do |record|
record.class.send(:nullify_has_many_dependencies,
record,
reflection.name,
reflection.klass,
reflection.primary_key_name,
reflection.dependent_conditions(record, record.class, extra_conditions))
end
else
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
end
Expand Down
11 changes: 11 additions & 0 deletions activerecord/lib/active_record/reflection.rb
Expand Up @@ -277,6 +277,17 @@ def validate?
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
end

def dependent_conditions(record, base_class, extra_conditions)
dependent_conditions = []
dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}"
dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as]
dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
dependent_conditions << extra_conditions if extra_conditions
dependent_conditions = dependent_conditions.gsub('@', '\@')
dependent_conditions
end

private
def derive_class_name
class_name = name.to_s.camelize
Expand Down
18 changes: 18 additions & 0 deletions activerecord/test/cases/associations/has_many_associations_test.rb
Expand Up @@ -1138,5 +1138,23 @@ def test_normal_method_call_in_association_proxy
def test_instance_eval_in_association_proxy
assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.sort_by(&:id).first.instance_eval{title}
end

def test_defining_has_many_association_with_delete_all_dependency_lazily_evaluates_target_class
ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
class_eval <<-EOF
class DeleteAllModel < ActiveRecord::Base
has_many :nonentities, :dependent => :delete_all
end
EOF
end

def test_defining_has_many_association_with_nullify_dependency_lazily_evaluates_target_class
ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
class_eval <<-EOF
class NullifyModel < ActiveRecord::Base
has_many :nonentities, :dependent => :nullify
end
EOF
end
end

0 comments on commit e617af1

Please sign in to comment.