diff options
Diffstat (limited to 'activerecord/lib/active_record/associations/class_methods/join_dependency')
3 files changed, 0 insertions, 364 deletions
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb deleted file mode 100644 index 5cc96a7aef..0000000000 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ /dev/null @@ -1,258 +0,0 @@ -module ActiveRecord - module Associations - module ClassMethods - class JoinDependency # :nodoc: - class JoinAssociation < JoinPart # :nodoc: - # The reflection of the association represented - attr_reader :reflection - - # The JoinDependency object which this JoinAssociation exists within. This is mainly - # relevant for generating aliases which do not conflict with other joins which are - # part of the query. - attr_reader :join_dependency - - # A JoinBase instance representing the active record we are joining onto. - # (So in Author.has_many :posts, the Author would be that base record.) - attr_reader :parent - - # 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 => :parent - delegate :alias_tracker, :to => :join_dependency - - def initialize(reflection, join_dependency, parent = nil) - reflection.check_validity! - - if reflection.options[:polymorphic] - raise EagerLoadPolymorphicError.new(reflection) - 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 - - def ==(other) - other.class == self.class && - other.reflection == reflection && - other.parent == parent - end - - def find_parent_in(other_join_dependency) - other_join_dependency.join_parts.detect do |join_part| - parent == join_part - end - end - - def join_to(relation) - # 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 - key = reflection.association_primary_key - foreign_key = reflection.primary_key_name - 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 - else - case reflection.source_reflection.macro - 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) - - ands = relation.create_and(conditions.flatten.compact) - - join = relation.create_join( - relation.froms.first, - table, - relation.create_on(ands), - join_type) - - relation = relation.from(join) - - # The current table in this iteration becomes the foreign table in the next - foreign_table = table - index += 1 - end - - relation - end - - def join_relation(joining_relation) - self.join_type = Arel::OuterJoin - joining_relation.joins(self) - end - - def table - if @tables.last.is_a?(Array) - @tables.last.first - else - @tables.last - end - end - - def aliased_table_name - table.table_alias || table.name - end - - protected - - def table_alias_for(reflection, join = false) - name = alias_tracker.pluralize(reflection.name) - name << "_#{parent_table_name}" - name << "_join" if join - name - 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. - def setup_tables - @tables = through_reflection_chain.map do |reflection| - aliased_table_name = alias_tracker.aliased_name_for( - reflection.table_name, - table_alias_for(reflection, reflection != self.reflection) - ) - - table = Arel::Table.new(reflection.table_name, :as => aliased_table_name) - - # 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, :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(sanitize_sql(condition, table.table_alias || table.name)) - end - end - - def sanitize_sql(condition, table_name) - active_record.send(:sanitize_sql, condition, table_name) - end - - def sti_conditions(reflection, table) - unless reflection.klass.descends_from_active_record? - sti_column = table[reflection.klass.inheritance_column] - sti_condition = sti_column.eq(reflection.klass.sti_name) - subclasses = reflection.klass.descendants - - subclasses.inject(sti_condition) { |attr,subclass| - attr.or(sti_column.eq(subclass.sti_name)) - } - 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"]. - eq(reflection.active_record.base_class.name) - end - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb deleted file mode 100644 index 67567f06df..0000000000 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb +++ /dev/null @@ -1,26 +0,0 @@ -module ActiveRecord - module Associations - module ClassMethods - class JoinDependency # :nodoc: - class JoinBase < JoinPart # :nodoc: - def ==(other) - other.class == self.class && - other.active_record == active_record - end - - def aliased_prefix - "t0" - end - - def table - Arel::Table.new(table_name, arel_engine) - end - - def aliased_table_name - active_record.table_name - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb deleted file mode 100644 index cd16ae5a8b..0000000000 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb +++ /dev/null @@ -1,80 +0,0 @@ -module ActiveRecord - module Associations - module ClassMethods - class JoinDependency # :nodoc: - # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited - # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which - # everything else is being joined onto. A JoinAssociation represents an association which - # is joining to the base. A JoinAssociation may result in more than one actual join - # operations (for example a has_and_belongs_to_many JoinAssociation would result in - # two; one for the join table and one for the target table). - class JoinPart # :nodoc: - # The Active Record class which this join part is associated 'about'; for a JoinBase - # this is the actual base model, for a JoinAssociation this is the target model of the - # association. - attr_reader :active_record - - delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :active_record - - def initialize(active_record) - @active_record = active_record - @cached_record = {} - @column_names_with_alias = nil - end - - def aliased_table - Arel::Nodes::TableAlias.new aliased_table_name, table - end - - def ==(other) - raise NotImplementedError - end - - # An Arel::Table for the active_record - def table - raise NotImplementedError - end - - # The prefix to be used when aliasing columns in the active_record's table - def aliased_prefix - raise NotImplementedError - end - - # The alias for the active_record's table - def aliased_table_name - raise NotImplementedError - end - - # The alias for the primary key of the active_record's table - def aliased_primary_key - "#{aliased_prefix}_r0" - end - - # An array of [column_name, alias] pairs for the table - def column_names_with_alias - unless @column_names_with_alias - @column_names_with_alias = [] - - ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| - @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] - end - end - @column_names_with_alias - end - - def extract_record(row) - Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}] - end - - def record_id(row) - row[aliased_primary_key] - end - - def instantiate(row) - @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row)) - end - end - end - end - end -end |