aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/association_preload.rb30
-rw-r--r--activerecord/lib/active_record/associations.rb98
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb26
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/through_association_scope.rb64
-rw-r--r--activerecord/lib/active_record/reflection.rb42
7 files changed, 133 insertions, 133 deletions
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index c3ccb93ffd..8e7416472f 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -210,9 +210,9 @@ module ActiveRecord
return if records.first.send("loaded_#{reflection.name}?")
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
end
-
+
options = reflection.options
-
+
if options[:through]
records_with_through_records = preload_through_records(records, reflection, options[:through])
all_through_records = records_with_through_records.map(&:last).flatten
@@ -220,10 +220,10 @@ module ActiveRecord
unless all_through_records.empty?
source = reflection.source_reflection.name
all_through_records.first.class.preload_associations(all_through_records, source, options)
-
+
records_with_through_records.each do |record, through_records|
source_records = through_records.map(&source).flatten.compact
-
+
case reflection.macro
when :has_many, :has_and_belongs_to_many
add_preloaded_records_to_collection([record], reflection.name, source_records)
@@ -235,7 +235,7 @@ module ActiveRecord
else
id_to_record_map, ids = construct_id_map(records, reflection.options[:primary_key])
associated_records = find_associated_records(ids, reflection, preload_options)
-
+
if reflection.macro == :has_many
set_association_collection_records(
id_to_record_map, reflection.name,
@@ -249,7 +249,7 @@ module ActiveRecord
end
end
end
-
+
alias_method :preload_has_one_association, :preload_has_one_or_has_many_association
alias_method :preload_has_many_association, :preload_has_one_or_has_many_association
@@ -259,12 +259,12 @@ module ActiveRecord
# record. This is so that we can preload the source association for each record,
# and always be able to access the preloaded association regardless of where we
# refer to the record.
- #
+ #
# Suffices to say, if AR had an identity map built in then this would be unnecessary.
identity_map = {}
-
+
options = {}
-
+
if reflection.options[:source_type]
interface = reflection.source_reflection.options[:foreign_type]
options[:conditions] = ["#{connection.quote_column_name interface} = ?", reflection.options[:source_type]]
@@ -272,20 +272,20 @@ module ActiveRecord
else
if reflection.options[:conditions]
options[:include] = reflection.options[:include] ||
- reflection.options[:source]
+ reflection.options[:source]
options[:conditions] = reflection.options[:conditions]
end
-
+
options[:order] = reflection.options[:order]
end
-
+
records.first.class.preload_associations(records, through_association, options)
records.map do |record|
if reflection.options[:source_type]
# Dont cache the association - we would only be caching a subset
proxy = record.send(through_association)
-
+
if proxy.respond_to?(:target)
through_records = proxy.target
proxy.reset
@@ -295,11 +295,11 @@ module ActiveRecord
else
through_records = record.send(through_association)
end
-
+
through_records = Array.wrap(through_records).map do |through_record|
identity_map[through_record] ||= through_record
end
-
+
[record, through_records]
end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 37dbff4061..f061c8da0f 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -56,7 +56,7 @@ module ActiveRecord
super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
end
end
-
+
class HasManyThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc
def initialize(owner, reflection)
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
@@ -487,7 +487,7 @@ module ActiveRecord
# @group.avatars.delete(@group.avatars.last) # so would this
#
# === Nested Associations
- #
+ #
# You can actually specify *any* association with the <tt>:through</tt> option, including an
# association which has a <tt>:through</tt> option itself. For example:
#
@@ -496,15 +496,15 @@ module ActiveRecord
# has_many :comments, :through => :posts
# has_many :commenters, :through => :comments
# end
- #
+ #
# class Post < ActiveRecord::Base
# has_many :comments
# end
- #
+ #
# class Comment < ActiveRecord::Base
# belongs_to :commenter
# end
- #
+ #
# @author = Author.first
# @author.commenters # => People who commented on posts written by the author
#
@@ -514,19 +514,19 @@ module ActiveRecord
# has_many :posts
# has_many :commenters, :through => :posts
# end
- #
+ #
# class Post < ActiveRecord::Base
# has_many :comments
# has_many :commenters, :through => :comments
# end
- #
+ #
# class Comment < ActiveRecord::Base
# belongs_to :commenter
# end
#
# When using nested association, you will not be able to modify the association because there
# is not enough information to know what modification to make. For example, if you tried to
- # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
+ # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
# intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
#
# === Polymorphic Associations
@@ -2183,9 +2183,9 @@ module ActiveRecord
# What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
attr_accessor :join_type
-
+
attr_reader :aliased_prefix
-
+
delegate :options, :through_reflection, :source_reflection, :through_reflection_chain, :to => :reflection
delegate :table, :table_name, :to => :parent, :prefix => true
delegate :alias_tracker, :to => :join_dependency
@@ -2198,13 +2198,13 @@ module ActiveRecord
end
super(reflection.klass)
-
+
@reflection = reflection
@join_dependency = join_dependency
@parent = parent
@join_type = Arel::InnerJoin
@aliased_prefix = "t#{ join_dependency.join_parts.size }"
-
+
setup_tables
end
@@ -2221,17 +2221,17 @@ module ActiveRecord
end
def join_to(relation)
- # The chain starts with the target table, but we want to end with it here (makes
+ # The chain starts with the target table, but we want to end with it here (makes
# more sense in this context)
chain = through_reflection_chain.reverse
-
+
foreign_table = parent_table
index = 0
-
+
chain.each do |reflection|
table = @tables[index]
conditions = []
-
+
if reflection.source_reflection.nil?
case reflection.macro
when :belongs_to
@@ -2240,25 +2240,25 @@ module ActiveRecord
when :has_many, :has_one
key = reflection.primary_key_name
foreign_key = reflection.active_record_primary_key
-
+
conditions << polymorphic_conditions(reflection, table)
when :has_and_belongs_to_many
# For habtm, we need to deal with the join table at the same time as the
# target table (because unlike a :through association, there is no reflection
# to represent the join table)
table, join_table = table
-
+
join_key = reflection.primary_key_name
join_foreign_key = reflection.active_record.primary_key
-
+
relation = relation.join(join_table, join_type).on(
join_table[join_key].
eq(foreign_table[join_foreign_key])
)
-
+
# We've done the first join now, so update the foreign_table for the second
foreign_table = join_table
-
+
key = reflection.klass.primary_key
foreign_key = reflection.association_foreign_key
end
@@ -2267,41 +2267,41 @@ module ActiveRecord
when :belongs_to
key = reflection.association_primary_key
foreign_key = reflection.primary_key_name
-
+
conditions << source_type_conditions(reflection, foreign_table)
when :has_many, :has_one
key = reflection.primary_key_name
foreign_key = reflection.source_reflection.active_record_primary_key
when :has_and_belongs_to_many
table, join_table = table
-
+
join_key = reflection.primary_key_name
join_foreign_key = reflection.klass.primary_key
-
+
relation = relation.join(join_table, join_type).on(
join_table[join_key].
eq(foreign_table[join_foreign_key])
)
-
+
foreign_table = join_table
-
+
key = reflection.klass.primary_key
foreign_key = reflection.association_foreign_key
end
end
-
+
conditions << table[key].eq(foreign_table[foreign_key])
-
+
conditions << reflection_conditions(index, table)
conditions << sti_conditions(reflection, table)
-
+
relation = relation.join(table, join_type).on(*conditions.flatten.compact)
-
+
# The current table in this iteration becomes the foreign table in the next
foreign_table = table
index += 1
end
-
+
relation
end
@@ -2317,11 +2317,11 @@ module ActiveRecord
@tables.last
end
end
-
+
def aliased_table_name
table.table_alias || table.name
end
-
+
protected
def table_alias_for(reflection, join = false)
@@ -2336,7 +2336,7 @@ module ActiveRecord
end
private
-
+
# Generate aliases and Arel::Table instances for each of the tables which we will
# later generate joins for. We must do this in advance in order to correctly allocate
# the proper alias.
@@ -2346,44 +2346,44 @@ module ActiveRecord
reflection.table_name,
table_alias_for(reflection, reflection != self.reflection)
)
-
+
table = Arel::Table.new(
reflection.table_name, :engine => arel_engine,
:as => aliased_table_name, :columns => reflection.klass.columns
)
-
+
# For habtm, we have two Arel::Table instances related to a single reflection, so
# we just store them as a pair in the array.
if reflection.macro == :has_and_belongs_to_many ||
(reflection.source_reflection &&
reflection.source_reflection.macro == :has_and_belongs_to_many)
-
+
join_table_name = (reflection.source_reflection || reflection).options[:join_table]
-
+
aliased_join_table_name = alias_tracker.aliased_name_for(
join_table_name,
table_alias_for(reflection, true)
)
-
+
join_table = Arel::Table.new(
join_table_name, :engine => arel_engine,
:as => aliased_join_table_name
)
-
+
[table, join_table]
else
table
end
end
-
+
# The joins are generated from the through_reflection_chain in reverse order, so
# reverse the tables too (but it's important to generate the aliases in the 'forward'
# order, which is why we only do the reversal now.
@tables.reverse!
-
+
@tables
end
-
+
def reflection_conditions(index, table)
@reflection.through_conditions.reverse[index].map do |condition|
Arel.sql(interpolate_sql(sanitize_sql(
@@ -2392,28 +2392,28 @@ module ActiveRecord
)))
end
end
-
+
def sti_conditions(reflection, table)
unless reflection.klass.descends_from_active_record?
sti_column = table[reflection.klass.inheritance_column]
-
+
condition = sti_column.eq(reflection.klass.sti_name)
-
+
reflection.klass.descendants.each do |subclass|
condition = condition.or(sti_column.eq(subclass.sti_name))
end
-
+
condition
end
end
-
+
def source_type_conditions(reflection, foreign_table)
if reflection.options[:source_type]
foreign_table[reflection.source_reflection.options[:foreign_type]].
eq(reflection.options[:source_type])
end
end
-
+
def polymorphic_conditions(reflection, table)
if reflection.options[:as]
table["#{reflection.options[:as]}_type"].
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 10e90ec117..64582188b6 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -11,10 +11,10 @@ module ActiveRecord
@aliases = Hash.new
@other_sql = other_sql.to_s.downcase
end
-
+
def aliased_name_for(table_name, aliased_name = nil)
aliased_name ||= table_name
-
+
initialize_count_for(table_name) if @aliases[table_name].nil?
if @aliases[table_name].zero?
@@ -24,12 +24,12 @@ module ActiveRecord
else
# Otherwise, we need to use an alias
aliased_name = connection.table_alias_for(aliased_name)
-
+
initialize_count_for(aliased_name) if @aliases[aliased_name].nil?
-
+
# Update the count
@aliases[aliased_name] += 1
-
+
if @aliases[aliased_name] > 1
"#{truncate(aliased_name)}_#{@aliases[aliased_name]}"
else
@@ -41,30 +41,30 @@ module ActiveRecord
def pluralize(table_name)
ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
end
-
+
private
-
+
def initialize_count_for(name)
@aliases[name] = 0
-
+
unless @other_sql.blank?
# quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
quoted_name = connection.quote_table_name(name.downcase).downcase
-
+
# Table names
@aliases[name] += @other_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size
-
+
# Table aliases
@aliases[name] += @other_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size
end
-
+
@aliases[name]
end
-
+
def truncate(name)
name[0..connection.table_alias_length-3]
end
-
+
def connection
ActiveRecord::Base.connection
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 2c9fa3b447..c45f2ee224 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -66,7 +66,7 @@ module ActiveRecord
def insert_record(record, force = true, validate = true)
ensure_not_nested
-
+
if record.new_record?
if force
record.save!
@@ -83,7 +83,7 @@ module ActiveRecord
# TODO - add dependent option support
def delete_records(records)
ensure_not_nested
-
+
klass = @reflection.through_reflection.klass
records.each do |associate|
klass.delete_all(construct_join_attributes(associate))
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index de962e01b6..e9dc32efd3 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -15,7 +15,7 @@ module ActiveRecord
def create_through_record(new_value) #nodoc:
ensure_not_nested
-
+
klass = @reflection.through_reflection.klass
current_object = @owner.send(@reflection.through_reflection.name)
diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb
index abe7af418d..07ce6f1597 100644
--- a/activerecord/lib/active_record/associations/through_association_scope.rb
+++ b/activerecord/lib/active_record/associations/through_association_scope.rb
@@ -18,7 +18,7 @@ module ActiveRecord
:readonly => @reflection.options[:readonly]
}
end
-
+
def construct_create_scope
@reflection.nested? ? {} : construct_owner_attributes(@reflection)
end
@@ -26,18 +26,18 @@ module ActiveRecord
# Build SQL conditions from attributes, qualified by table name.
def construct_conditions
reflection = @reflection.through_reflection_chain.last
-
+
if reflection.macro == :has_and_belongs_to_many
table_alias = table_aliases[reflection].first
else
table_alias = table_aliases[reflection]
end
-
+
parts = construct_quoted_owner_attributes(reflection).map do |attr, value|
"#{table_alias}.#{attr} = #{value}"
end
parts += reflection_conditions(0)
-
+
"(" + parts.join(') AND (') + ")"
end
@@ -59,18 +59,18 @@ module ActiveRecord
distinct = "DISTINCT " if @reflection.options[:uniq]
selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*"
end
-
+
def construct_joins(custom_joins = nil)
"#{construct_through_joins} #{@reflection.options[:joins]} #{custom_joins}"
end
def construct_through_joins
joins, right_index = [], 1
-
+
# Iterate over each pair in the through reflection chain, joining them together
@reflection.through_reflection_chain.each_cons(2) do |left, right|
right_table_and_alias = table_name_and_alias(right.quoted_table_name, table_aliases[right])
-
+
if left.source_reflection.nil?
case left.macro
when :belongs_to
@@ -113,7 +113,7 @@ module ActiveRecord
else
right_table = table_aliases[right]
end
-
+
joins << inner_join_sql(
right_table_and_alias,
table_aliases[left], left.primary_key_name,
@@ -121,7 +121,7 @@ module ActiveRecord
polymorphic_conditions(left, left.source_reflection),
reflection_conditions(right_index)
)
-
+
if right.macro == :has_and_belongs_to_many
joins << inner_join_sql(
table_name_and_alias(
@@ -134,7 +134,7 @@ module ActiveRecord
end
when :has_and_belongs_to_many
join_table, left_table = table_aliases[left]
-
+
joins << inner_join_sql(
table_name_and_alias(
quote_table_name(left.source_reflection.options[:join_table]),
@@ -143,7 +143,7 @@ module ActiveRecord
left_table, left.klass.primary_key,
join_table, left.association_foreign_key
)
-
+
joins << inner_join_sql(
right_table_and_alias,
join_table, left.primary_key_name,
@@ -152,10 +152,10 @@ module ActiveRecord
)
end
end
-
+
right_index += 1
end
-
+
joins.join(" ")
end
@@ -170,77 +170,77 @@ module ActiveRecord
reflection.table_name,
table_alias_for(reflection, reflection != @reflection)
))
-
+
if reflection.macro == :has_and_belongs_to_many ||
(reflection.source_reflection &&
reflection.source_reflection.macro == :has_and_belongs_to_many)
-
+
join_table_alias = quote_table_name(alias_tracker.aliased_name_for(
(reflection.source_reflection || reflection).options[:join_table],
table_alias_for(reflection, true)
))
-
+
aliases[reflection] = [join_table_alias, table_alias]
else
aliases[reflection] = table_alias
end
-
+
aliases
end
end
end
-
+
def table_alias_for(reflection, join = false)
name = alias_tracker.pluralize(reflection.name)
name << "_#{@reflection.name}"
name << "_join" if join
name
end
-
+
def quote_table_name(table_name)
@reflection.klass.connection.quote_table_name(table_name)
end
-
+
def table_name_and_alias(table_name, table_alias)
"#{table_name} #{table_alias if table_alias != table_name}".strip
end
-
+
def inner_join_sql(table, on_left_table, on_left_key, on_right_table, on_right_key, *conditions)
conditions << "#{on_left_table}.#{on_left_key} = #{on_right_table}.#{on_right_key}"
conditions = conditions.flatten.compact
conditions = conditions.map { |sql| "(#{sql})" } * ' AND '
-
+
"INNER JOIN #{table} ON #{conditions}"
end
-
+
def reflection_conditions(index)
reflection = @reflection.through_reflection_chain[index]
reflection_conditions = @reflection.through_conditions[index]
-
+
conditions = []
-
+
if reflection.options[:as].nil? && # reflection.klass is a Module if :as is used
reflection.klass.finder_needs_type_condition?
conditions << reflection.klass.send(:type_condition).to_sql
end
-
+
reflection_conditions.each do |condition|
sanitized_condition = reflection.klass.send(:sanitize_sql, condition)
interpolated_condition = interpolate_sql(sanitized_condition)
-
+
if condition.is_a?(Hash)
interpolated_condition.gsub!(
@reflection.quoted_table_name,
reflection.quoted_table_name
)
end
-
+
conditions << interpolated_condition
end
-
+
conditions
end
-
+
def polymorphic_conditions(reflection, polymorphic_reflection)
if polymorphic_reflection.options[:as]
"%s.%s = %s" % [
@@ -249,7 +249,7 @@ module ActiveRecord
]
end
end
-
+
def source_type_conditions(reflection)
if reflection.options[:source_type]
"%s.%s = %s" % [
@@ -289,7 +289,7 @@ module ActiveRecord
join_attributes
end
-
+
def ensure_not_nested
if @reflection.nested?
raise HasManyThroughNestedAssociationsAreReadonly.new(@owner, @reflection)
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 6eb2057f66..ba37fed3c7 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -209,11 +209,11 @@ module ActiveRecord
def association_foreign_key
@association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
end
-
+
def association_primary_key
@association_primary_key ||= @options[:primary_key] || klass.primary_key
end
-
+
def active_record_primary_key
@active_record_primary_key ||= @options[:primary_key] || active_record.primary_key
end
@@ -249,11 +249,11 @@ module ActiveRecord
def through_reflection
false
end
-
+
def through_reflection_chain
[self]
end
-
+
def through_conditions
[Array.wrap(options[:conditions])]
end
@@ -340,7 +340,7 @@ module ActiveRecord
# in the Active Record class.
class ThroughReflection < AssociationReflection #:nodoc:
delegate :primary_key_name, :association_foreign_key, :to => :source_reflection
-
+
# Gets the source of the through reflection. It checks both a singularized
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
#
@@ -367,14 +367,14 @@ module ActiveRecord
def through_reflection
@through_reflection ||= active_record.reflect_on_association(options[:through])
end
-
+
# Returns an array of AssociationReflection objects which are involved in this through
# association. Each item in the array corresponds to a table which will be part of the
# query for this association.
- #
+ #
# If the source reflection is itself a ThroughReflection, then we don't include self in
# the chain, but just defer to the source reflection.
- #
+ #
# The chain is built by recursively calling through_reflection_chain on the source
# reflection and the through reflection. The base case for the recursion is a normal
# association, which just returns [self] for its through_reflection_chain.
@@ -389,31 +389,31 @@ module ActiveRecord
# to this reflection directly, and so start the chain here
chain = [self]
end
-
+
# Recursively build the rest of the chain
chain += through_reflection.through_reflection_chain
-
+
# Finally return the completed chain
chain
end
end
-
+
# Consider the following example:
- #
+ #
# class Person
# has_many :articles
# has_many :comment_tags, :through => :articles
# end
- #
+ #
# class Article
# has_many :comments
# has_many :comment_tags, :through => :comments, :source => :tags
# end
- #
+ #
# class Comment
# has_many :tags
# end
- #
+ #
# There may be conditions on Person.comment_tags, Article.comment_tags and/or Comment.tags,
# but only Comment.tags will be represented in the through_reflection_chain. So this method
# creates an array of conditions corresponding to the through_reflection_chain. Each item in
@@ -429,24 +429,24 @@ module ActiveRecord
else
conditions = [Array.wrap(source_reflection.options[:conditions])]
end
-
+
# Add to it the conditions from this reflection if necessary.
conditions.first << options[:conditions] if options[:conditions]
-
+
# Recursively fill out the rest of the array from the through reflection
conditions += through_reflection.through_conditions
-
+
# And return
conditions
end
end
-
+
# A through association is nested iff there would be more than one join table
def nested?
through_reflection_chain.length > 2 ||
through_reflection.macro == :has_and_belongs_to_many
end
-
+
# We want to use the klass from this reflection, rather than just delegate straight to
# the source_reflection, because the source_reflection may be polymorphic. We still
# need to respect the source_reflection's :primary_key option, though.
@@ -458,7 +458,7 @@ module ActiveRecord
while source_reflection.source_reflection
source_reflection = source_reflection.source_reflection
end
-
+
source_reflection.options[:primary_key] || klass.primary_key
end
end