Permalink
Browse files

Abstract some common code from AssociationScope and JoinDependency::J…

…oinAssociation into a JoinHelper module
  • Loading branch information...
1 parent aef3629 commit e18679ab0428797369027fc549ef964c8c2038ba @jonleighton jonleighton committed Mar 11, 2011
@@ -144,6 +144,7 @@ module Builder #:nodoc:
autoload :JoinDependency, 'active_record/associations/join_dependency'
autoload :AssociationScope, 'active_record/associations/association_scope'
autoload :AliasTracker, 'active_record/associations/alias_tracker'
+ autoload :JoinHelper, 'active_record/associations/join_helper'
# Clears out the association cache.
def clear_association_cache #:nodoc:
@@ -1,10 +1,12 @@
module ActiveRecord
module Associations
class AssociationScope #:nodoc:
+ include JoinHelper
+
attr_reader :association, :alias_tracker
delegate :klass, :owner, :reflection, :interpolate, :to => :association
- delegate :chain, :conditions, :options, :source_options, :to => :reflection
+ delegate :chain, :conditions, :options, :source_options, :active_record, :to => :reflection
def initialize(association)
@association = association
@@ -56,8 +58,8 @@ def add_constraints(scope)
if reflection.source_macro == :has_and_belongs_to_many
join_table = tables.shift
- scope = scope.joins(inner_join(
- join_table, reflection,
+ scope = scope.joins(join(
+ join_table,
table[reflection.active_record_primary_key].
eq(join_table[reflection.association_foreign_key])
))
@@ -84,32 +86,19 @@ def add_constraints(scope)
scope = scope.where(interpolate(condition))
end
else
- constraint = table[key].eq foreign_table[foreign_key]
-
- join = inner_join(foreign_table, reflection, constraint, *conditions[i])
- scope = scope.joins(join)
+ scope = scope.joins(join(
+ foreign_table,
+ table[key].eq(foreign_table[foreign_key]),
+ *conditions[i]
+ ))
end
end
scope
end
- def construct_tables
- tables = []
- chain.each do |reflection|
- tables << alias_tracker.aliased_table_for(
- table_name_for(reflection),
- table_alias_for(reflection, reflection != self.reflection)
- )
-
- if reflection.source_macro == :has_and_belongs_to_many
- tables << alias_tracker.aliased_table_for(
- (reflection.source_reflection || reflection).options[:join_table],
- table_alias_for(reflection, true)
- )
- end
- end
- tables
+ def alias_suffix
+ reflection.name
end
def table_name_for(reflection)
@@ -123,27 +112,6 @@ def table_name_for(reflection)
end
end
- def table_alias_for(reflection, join = false)
- name = alias_tracker.pluralize(reflection.name)
- name << "_#{self.reflection.name}"
- name << "_join" if join
- name
- end
-
- def inner_join(table, reflection, *conditions)
- conditions = sanitize_conditions(reflection, conditions)
- table.create_join(table, table.create_on(conditions))
- end
-
- def sanitize_conditions(reflection, conditions)
- conditions = conditions.map do |condition|
- condition = reflection.klass.send(:sanitize_sql, interpolate(condition), reflection.table_name)
- condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
- condition
- end
-
- conditions.length == 1 ? conditions.first : Arel::Nodes::And.new(conditions)
- end
end
end
end
@@ -2,6 +2,8 @@ module ActiveRecord
module Associations
class JoinDependency # :nodoc:
class JoinAssociation < JoinPart # :nodoc:
+ include JoinHelper
+
# The reflection of the association represented
attr_reader :reflection
@@ -26,6 +28,8 @@ class JoinAssociation < JoinPart # :nodoc:
delegate :table, :table_name, :to => :parent, :prefix => :parent
delegate :alias_tracker, :to => :join_dependency
+ alias :alias_suffix :parent_table_name
+
def initialize(reflection, join_dependency, parent = nil)
reflection.check_validity!
@@ -40,8 +44,7 @@ def initialize(reflection, join_dependency, parent = nil)
@parent = parent
@join_type = Arel::InnerJoin
@aliased_prefix = "t#{ join_dependency.join_parts.size }"
-
- setup_tables
+ @tables = construct_tables.reverse
end
def ==(other)
@@ -86,14 +89,17 @@ def join_to(relation)
foreign_key = reflection.active_record_primary_key
end
- conditions = self.conditions[i].dup
- conditions << table[key].eq(foreign_table[foreign_key])
+ conditions = self.conditions[i]
if reflection.klass.finder_needs_type_condition?
- conditions << reflection.klass.send(:type_condition, table)
+ conditions += [reflection.klass.send(:type_condition, table)]
end
- relation = relation.from(join(table, *conditions))
+ relation = relation.from(join(
+ table,
+ table[key].eq(foreign_table[foreign_key]),
+ *conditions
+ ))
# The current table in this iteration becomes the foreign table in the next
foreign_table = table
@@ -121,53 +127,6 @@ def conditions
private
- def table_alias_for(reflection, join = false)
- name = alias_tracker.pluralize(reflection.name)
- name << "_#{parent_table_name}"
- name << "_join" if join
- name
- end
-
- # 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.
- def setup_tables
- @tables = []
- chain.each do |reflection|
- @tables << alias_tracker.aliased_table_for(
- reflection.table_name,
- table_alias_for(reflection, reflection != self.reflection)
- )
-
- if reflection.source_macro == :has_and_belongs_to_many
- @tables << alias_tracker.aliased_table_for(
- (reflection.source_reflection || reflection).options[:join_table],
- table_alias_for(reflection, true)
- )
- end
- end
-
- # We construct the tables in the forward order so that the aliases are generated
- # correctly, but then reverse the array because that is the order in which we will
- # iterate the chain.
- @tables.reverse!
- end
-
- def join(table, *conditions)
- conditions = sanitize_conditions(table, conditions)
- table.create_join(table, table.create_on(conditions), join_type)
- end
-
- def sanitize_conditions(table, conditions)
- conditions = conditions.map do |condition|
- condition = active_record.send(:sanitize_sql, interpolate(condition), table.table_alias || table.name)
- condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
- condition
- end
-
- conditions.length == 1 ? conditions.first : Arel::Nodes::And.new(conditions)
- end
-
def interpolate(conditions)
if conditions.respond_to?(:to_proc)
instance_eval(&conditions)
@@ -0,0 +1,58 @@
+module ActiveRecord
+ module Associations
+ # Helper class module which gets mixed into JoinDependency::JoinAssociation and AssociationScope
+ module JoinHelper #:nodoc:
+
+ def join_type
+ Arel::InnerJoin
+ end
+
+ private
+
+ def construct_tables
+ tables = []
+ chain.each do |reflection|
+ tables << alias_tracker.aliased_table_for(
+ table_name_for(reflection),
+ table_alias_for(reflection, reflection != self.reflection)
+ )
+
+ if reflection.source_macro == :has_and_belongs_to_many
+ tables << alias_tracker.aliased_table_for(
+ (reflection.source_reflection || reflection).options[:join_table],
+ table_alias_for(reflection, true)
+ )
+ end
+ end
+ tables
+ end
+
+ def table_name_for(reflection)
+ reflection.table_name
+ end
+
+ def table_alias_for(reflection, join = false)
+ name = alias_tracker.pluralize(reflection.name)
+ name << "_#{alias_suffix}"
+ name << "_join" if join
+ name
+ end
+
+ def join(table, *conditions)
+ table.create_join(table, table.create_on(sanitize(conditions)), join_type)
+ end
+
+ def sanitize(conditions)
+ table = conditions.first.left.relation
+
+ conditions = conditions.map do |condition|
+ condition = active_record.send(:sanitize_sql, interpolate(condition), table.table_alias || table.name)
+ condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
+ condition
+ end
+
+ conditions.length == 1 ? conditions.first : Arel::Nodes::And.new(conditions)
+ end
+ end
+ end
+end

0 comments on commit e18679a

Please sign in to comment.