Add a HasAssociation module for common code for has_* associations

This commit is contained in:
Jon Leighton 2010-12-26 19:37:35 +00:00
parent 0c272471fe
commit b0498372a1
8 changed files with 69 additions and 61 deletions

View File

@ -120,6 +120,8 @@ module ActiveRecord
# So there is no need to eager load them.
autoload :AssociationCollection, 'active_record/associations/association_collection'
autoload :AssociationProxy, 'active_record/associations/association_proxy'
autoload :HasAssociation, 'active_record/associations/has_association'
autoload :ThroughAssociation, 'active_record/associations/through_association'
autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'

View File

@ -18,6 +18,8 @@ module ActiveRecord
# If you need to work on all current children, new and existing records,
# +load_target+ and the +loaded+ flag are your friends.
class AssociationCollection < AssociationProxy #:nodoc:
include HasAssociation
delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
def select(select = nil)

View File

@ -4,17 +4,17 @@ module ActiveRecord
module Associations
# = Active Record Associations
#
# This is the root class of all association proxies:
# This is the root class of all association proxies ('+ Foo' signifies an included module Foo):
#
# AssociationProxy
# BelongsToAssociation
# HasOneAssociation
# BelongsToPolymorphicAssociation
# AssociationCollection
# AssociationCollection + HasAssociation
# HasAndBelongsToManyAssociation
# HasManyAssociation
# HasManyThroughAssociation
# HasOneThroughAssociation
# HasManyThroughAssociation + ThroughAssociation
# HasOneAssociation + HasAssociation
# HasOneThroughAssociation + ThroughAssociation
#
# Association proxies in Active Record are middlemen between the object that
# holds the association, known as the <tt>@owner</tt>, and the actual associated
@ -176,43 +176,6 @@ module ActiveRecord
@reflection.klass.send(:sanitize_sql, sql, table_name)
end
# Sets the owner attributes on the given record
# Note: does not really make sense for belongs_to associations, but this method is not
# used by belongs_to
def set_owner_attributes(record)
if @owner.persisted?
construct_owner_attributes.each { |key, value| record[key] = value }
end
end
# Returns a has linking the owner to the association represented by the reflection
def construct_owner_attributes(reflection = @reflection)
attributes = {}
if reflection.macro == :belongs_to
attributes[reflection.association_primary_key] = @owner.send(reflection.primary_key_name)
else
attributes[reflection.primary_key_name] = @owner.send(reflection.active_record_primary_key)
if reflection.options[:as]
attributes["#{reflection.options[:as]}_type"] = @owner.class.base_class.name
end
end
attributes
end
# Builds an array of arel nodes from the owner attributes hash
def construct_owner_conditions(table = aliased_table, reflection = @reflection)
construct_owner_attributes(reflection).map do |attr, value|
table[attr].eq(value)
end
end
def construct_conditions
conditions = construct_owner_conditions
conditions << Arel.sql(sql_conditions) if sql_conditions
aliased_table.create_and(conditions)
end
# Merges into +options+ the ones coming from the reflection.
def merge_options_from_reflection!(options)
options.reverse_merge!(
@ -312,18 +275,6 @@ module ActiveRecord
end
end
if RUBY_VERSION < '1.9.2'
# Array#flatten has problems with recursive arrays before Ruby 1.9.2.
# Going one level deeper solves the majority of the problems.
def flatten_deeper(array)
array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
end
else
def flatten_deeper(array)
array.flatten
end
end
# Returns the ID of the owner, quoted if needed.
def owner_quoted_id
@owner.quoted_id

View File

@ -0,0 +1,54 @@
module ActiveRecord
module Associations
# Included in all has_* associations (i.e. everything except belongs_to)
module HasAssociation #:nodoc:
protected
# Sets the owner attributes on the given record
def set_owner_attributes(record)
if @owner.persisted?
construct_owner_attributes.each { |key, value| record[key] = value }
end
end
# Returns a hash linking the owner to the association represented by the reflection
def construct_owner_attributes(reflection = @reflection)
attributes = {}
if reflection.macro == :belongs_to
attributes[reflection.association_primary_key] = @owner.send(reflection.primary_key_name)
else
attributes[reflection.primary_key_name] = @owner.send(reflection.active_record_primary_key)
if reflection.options[:as]
attributes["#{reflection.options[:as]}_type"] = @owner.class.base_class.name
end
end
attributes
end
# Builds an array of arel nodes from the owner attributes hash
def construct_owner_conditions(table = aliased_table, reflection = @reflection)
construct_owner_attributes(reflection).map do |attr, value|
table[attr].eq(value)
end
end
def construct_conditions
conditions = construct_owner_conditions
conditions << Arel.sql(sql_conditions) if sql_conditions
aliased_table.create_and(conditions)
end
if RUBY_VERSION < '1.9.2'
# Array#flatten has problems with recursive arrays before Ruby 1.9.2.
# Going one level deeper solves the majority of the problems.
def flatten_deeper(array)
array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
end
else
def flatten_deeper(array)
array.flatten
end
end
end
end
end

View File

@ -1,11 +1,10 @@
require "active_record/associations/through_association_scope"
require 'active_support/core_ext/object/blank'
module ActiveRecord
# = Active Record Has Many Through Association
module Associations
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
include ThroughAssociationScope
include ThroughAssociation
alias_method :new, :build

View File

@ -2,6 +2,8 @@ module ActiveRecord
# = Active Record Belongs To Has One Association
module Associations
class HasOneAssociation < AssociationProxy #:nodoc:
include HasAssociation
def create(attrs = {}, replace_existing = true)
new_record(replace_existing) do |reflection|
attrs = merge_with_conditions(attrs)

View File

@ -1,10 +1,8 @@
require "active_record/associations/through_association_scope"
module ActiveRecord
# = Active Record Has One Through Association
module Associations
class HasOneThroughAssociation < HasOneAssociation
include ThroughAssociationScope
include ThroughAssociation
def replace(new_value)
create_through_record(new_value)

View File

@ -1,7 +1,7 @@
module ActiveRecord
# = Active Record Through Association Scope
# = Active Record Through Association
module Associations
module ThroughAssociationScope
module ThroughAssociation
def scoped
with_scope(@scope) do