Add special AssociationReflection methods for creating association objects, and modify the code base to use those methods instead of creating association objects directly. This allows plugins to hook into association object creation behavior.
[#986 state:resolved] Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
This commit is contained in:
parent
1692940441
commit
1398db0128
@ -1,6 +1,6 @@
|
||||
*Edge*
|
||||
|
||||
* Internal API: configurable association options so plugins may extend and override. #985 [Hongli Lai]
|
||||
* Internal API: configurable association options and build_association method for reflections so plugins may extend and override. #985 [Hongli Lai]
|
||||
|
||||
* Changed benchmarks to be reported in milliseconds [DHH]
|
||||
|
||||
|
@ -1266,7 +1266,7 @@ def association_accessor_methods(reflection, association_proxy_class)
|
||||
association = association_proxy_class.new(self, reflection)
|
||||
end
|
||||
|
||||
new_value = reflection.klass.new(new_value) if reflection.options[:accessible] && new_value.is_a?(Hash)
|
||||
new_value = reflection.build_association(new_value) if reflection.options[:accessible] && new_value.is_a?(Hash)
|
||||
|
||||
if association_proxy_class == HasOneThroughAssociation
|
||||
association.create_through_record(new_value)
|
||||
|
@ -110,7 +110,7 @@ def <<(*records)
|
||||
|
||||
@owner.transaction do
|
||||
flatten_deeper(records).each do |record|
|
||||
record = @reflection.klass.new(record) if @reflection.options[:accessible] && record.is_a?(Hash)
|
||||
record = @reflection.build_association(record) if @reflection.options[:accessible] && record.is_a?(Hash)
|
||||
|
||||
raise_on_type_mismatch(record)
|
||||
add_record_to_target_with_callbacks(record) do |r|
|
||||
@ -287,7 +287,7 @@ def uniq(collection = self)
|
||||
# This will perform a diff and delete/add only records that have changed.
|
||||
def replace(other_array)
|
||||
other_array.map! do |val|
|
||||
val.is_a?(Hash) ? @reflection.klass.new(val) : val
|
||||
val.is_a?(Hash) ? @reflection.build_association(val) : val
|
||||
end if @reflection.options[:accessible]
|
||||
|
||||
other_array.each { |val| raise_on_type_mismatch(val) }
|
||||
@ -377,7 +377,9 @@ def find_target
|
||||
def create_record(attrs)
|
||||
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
|
||||
ensure_owner_is_not_new
|
||||
record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.new(attrs) }
|
||||
record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
|
||||
@reflection.build_association(attrs)
|
||||
end
|
||||
if block_given?
|
||||
add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
|
||||
else
|
||||
@ -387,7 +389,7 @@ def create_record(attrs)
|
||||
|
||||
def build_record(attrs)
|
||||
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
|
||||
record = @reflection.klass.new(attrs)
|
||||
record = @reflection.build_association(attrs)
|
||||
if block_given?
|
||||
add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
|
||||
else
|
||||
|
@ -2,11 +2,11 @@ module ActiveRecord
|
||||
module Associations
|
||||
class BelongsToAssociation < AssociationProxy #:nodoc:
|
||||
def create(attributes = {})
|
||||
replace(@reflection.klass.create(attributes))
|
||||
replace(@reflection.create_association(attributes))
|
||||
end
|
||||
|
||||
def build(attributes = {})
|
||||
replace(@reflection.klass.new(attributes))
|
||||
replace(@reflection.build_association(attributes))
|
||||
end
|
||||
|
||||
def replace(record)
|
||||
|
@ -10,14 +10,14 @@ def initialize(owner, reflection)
|
||||
|
||||
def create!(attrs = nil)
|
||||
@reflection.klass.transaction do
|
||||
self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.klass.create! } : @reflection.klass.create!)
|
||||
self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association! } : @reflection.create_association!)
|
||||
object
|
||||
end
|
||||
end
|
||||
|
||||
def create(attrs = nil)
|
||||
@reflection.klass.transaction do
|
||||
self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.klass.create } : @reflection.klass.create)
|
||||
self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association } : @reflection.create_association)
|
||||
object
|
||||
end
|
||||
end
|
||||
@ -47,8 +47,9 @@ def insert_record(record, force=true)
|
||||
return false unless record.save
|
||||
end
|
||||
end
|
||||
klass = @reflection.through_reflection.klass
|
||||
@owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(record)) { klass.create! }
|
||||
through_reflection = @reflection.through_reflection
|
||||
klass = through_reflection.klass
|
||||
@owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(record)) { through_reflection.create_association! }
|
||||
end
|
||||
|
||||
# TODO - add dependent option support
|
||||
|
@ -7,15 +7,21 @@ def initialize(owner, reflection)
|
||||
end
|
||||
|
||||
def create(attrs = {}, replace_existing = true)
|
||||
new_record(replace_existing) { |klass| klass.create(attrs) }
|
||||
new_record(replace_existing) do |reflection|
|
||||
reflection.create_association(attrs)
|
||||
end
|
||||
end
|
||||
|
||||
def create!(attrs = {}, replace_existing = true)
|
||||
new_record(replace_existing) { |klass| klass.create!(attrs) }
|
||||
new_record(replace_existing) do |reflection|
|
||||
reflection.create_association!(attrs)
|
||||
end
|
||||
end
|
||||
|
||||
def build(attrs = {}, replace_existing = true)
|
||||
new_record(replace_existing) { |klass| klass.new(attrs) }
|
||||
new_record(replace_existing) do |reflection|
|
||||
reflection.build_association(attrs)
|
||||
end
|
||||
end
|
||||
|
||||
def replace(obj, dont_save = false)
|
||||
@ -91,7 +97,9 @@ def new_record(replace_existing)
|
||||
# instance. Otherwise, if the target has not previously been loaded
|
||||
# elsewhere, the instance we create will get orphaned.
|
||||
load_target if replace_existing
|
||||
record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { yield @reflection.klass }
|
||||
record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
|
||||
yield @reflection
|
||||
end
|
||||
|
||||
if replace_existing
|
||||
replace(record, true)
|
||||
|
@ -129,10 +129,45 @@ class AggregateReflection < MacroReflection #:nodoc:
|
||||
|
||||
# Holds all the meta-data about an association as it was specified in the Active Record class.
|
||||
class AssociationReflection < MacroReflection #:nodoc:
|
||||
# Returns the target association's class:
|
||||
#
|
||||
# class Author < ActiveRecord::Base
|
||||
# has_many :books
|
||||
# end
|
||||
#
|
||||
# Author.reflect_on_association(:books).klass
|
||||
# # => Book
|
||||
#
|
||||
# <b>Note:</b> do not call +klass.new+ or +klass.create+ to instantiate
|
||||
# a new association object. Use +build_association+ or +create_association+
|
||||
# instead. This allows plugins to hook into association object creation.
|
||||
def klass
|
||||
@klass ||= active_record.send(:compute_type, class_name)
|
||||
end
|
||||
|
||||
# Returns a new, unsaved instance of the associated class. +options+ will
|
||||
# be passed to the class's constructor.
|
||||
def build_association(*options)
|
||||
klass.new(*options)
|
||||
end
|
||||
|
||||
# Creates a new instance of the associated class, and immediates saves it
|
||||
# with ActiveRecord::Base#save. +options+ will be passed to the class's
|
||||
# creation method. Returns the newly created object.
|
||||
def create_association(*options)
|
||||
klass.create(*options)
|
||||
end
|
||||
|
||||
# Creates a new instance of the associated class, and immediates saves it
|
||||
# with ActiveRecord::Base#save!. +options+ will be passed to the class's
|
||||
# creation method. If the created record doesn't pass validations, then an
|
||||
# exception will be raised.
|
||||
#
|
||||
# Returns the newly created object.
|
||||
def create_association!(*options)
|
||||
klass.create!(*options)
|
||||
end
|
||||
|
||||
def table_name
|
||||
@table_name ||= klass.table_name
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user