diff options
author | Jon Leighton <j@jonathanleighton.com> | 2011-03-02 21:24:56 +0000 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2011-03-04 09:30:27 +0000 |
commit | 735844db712c511dd8abf36a5279318fbc0ff9d0 (patch) | |
tree | 5fbd5d224ef85d8c878bf221db98b422c9345466 /activerecord/lib/active_record/associations/through_association_scope.rb | |
parent | 9a98c766e045aebc2ef6d5b716936b73407f095d (diff) | |
parent | b171b9e73dcc6a89b1da652da61c5127fe605b51 (diff) | |
download | rails-735844db712c511dd8abf36a5279318fbc0ff9d0.tar.gz rails-735844db712c511dd8abf36a5279318fbc0ff9d0.tar.bz2 rails-735844db712c511dd8abf36a5279318fbc0ff9d0.zip |
Merge branch 'master' into nested_has_many_through
Conflicts:
activerecord/CHANGELOG
activerecord/lib/active_record/association_preload.rb
activerecord/lib/active_record/associations.rb
activerecord/lib/active_record/associations/class_methods/join_dependency.rb
activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
activerecord/lib/active_record/associations/has_many_association.rb
activerecord/lib/active_record/associations/has_many_through_association.rb
activerecord/lib/active_record/associations/has_one_association.rb
activerecord/lib/active_record/associations/has_one_through_association.rb
activerecord/lib/active_record/associations/through_association_scope.rb
activerecord/lib/active_record/reflection.rb
activerecord/test/cases/associations/has_many_through_associations_test.rb
activerecord/test/cases/associations/has_one_through_associations_test.rb
activerecord/test/cases/reflection_test.rb
activerecord/test/cases/relations_test.rb
activerecord/test/fixtures/memberships.yml
activerecord/test/models/categorization.rb
activerecord/test/models/category.rb
activerecord/test/models/member.rb
activerecord/test/models/reference.rb
activerecord/test/models/tagging.rb
Diffstat (limited to 'activerecord/lib/active_record/associations/through_association_scope.rb')
-rw-r--r-- | activerecord/lib/active_record/associations/through_association_scope.rb | 300 |
1 files changed, 0 insertions, 300 deletions
diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb deleted file mode 100644 index f6d02a215f..0000000000 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ /dev/null @@ -1,300 +0,0 @@ -require 'enumerator' - -module ActiveRecord - # = Active Record Through Association Scope - module Associations - module ThroughAssociationScope - - protected - - def construct_find_scope - { - :conditions => construct_conditions, - :joins => construct_joins, - :include => @reflection.options[:include] || @reflection.source_reflection.options[:include], - :select => construct_select, - :order => @reflection.options[:order], - :limit => @reflection.options[:limit], - :readonly => @reflection.options[:readonly] - } - end - - def construct_create_scope - @reflection.nested? ? {} : construct_owner_attributes(@reflection) - end - - # 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 - - # Associate attributes pointing to owner, quoted. - def construct_quoted_owner_attributes(reflection) - if as = reflection.options[:as] - { "#{as}_id" => owner_quoted_id(reflection), - "#{as}_type" => reflection.klass.quote_value( - @owner.class.base_class.name.to_s, - reflection.klass.columns_hash["#{as}_type"]) } - elsif reflection.macro == :belongs_to - { reflection.klass.primary_key => @owner.class.quote_value(@owner[reflection.primary_key_name]) } - else - { reflection.primary_key_name => owner_quoted_id(reflection) } - end - end - - def construct_select(custom_select = nil) - distinct = "DISTINCT " if @reflection.options[:uniq] - 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 - joins << inner_join_sql( - right_table_and_alias, - table_aliases[left], left.association_primary_key, - table_aliases[right], left.primary_key_name, - reflection_conditions(right_index) - ) - when :has_many, :has_one - joins << inner_join_sql( - right_table_and_alias, - table_aliases[left], left.primary_key_name, - table_aliases[right], right.association_primary_key, - polymorphic_conditions(left, left), - reflection_conditions(right_index) - ) - when :has_and_belongs_to_many - joins << inner_join_sql( - right_table_and_alias, - table_aliases[left].first, left.primary_key_name, - table_aliases[right], right.klass.primary_key, - reflection_conditions(right_index) - ) - end - else - case left.source_reflection.macro - when :belongs_to - joins << inner_join_sql( - right_table_and_alias, - table_aliases[left], left.association_primary_key, - table_aliases[right], left.primary_key_name, - source_type_conditions(left), - reflection_conditions(right_index) - ) - when :has_many, :has_one - if right.macro == :has_and_belongs_to_many - join_table, right_table = table_aliases[right] - right_table_and_alias = table_name_and_alias(right.quoted_table_name, right_table) - else - right_table = table_aliases[right] - end - - joins << inner_join_sql( - right_table_and_alias, - table_aliases[left], left.primary_key_name, - right_table, left.source_reflection.active_record_primary_key, - 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( - quote_table_name(right.options[:join_table]), - join_table - ), - right_table, right.klass.primary_key, - join_table, right.association_foreign_key - ) - 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]), - join_table - ), - 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, - table_aliases[right], right.klass.primary_key, - reflection_conditions(right_index) - ) - end - end - - right_index += 1 - end - - joins.join(" ") - end - - def alias_tracker - @alias_tracker ||= AliasTracker.new - end - - def table_aliases - @table_aliases ||= begin - @reflection.through_reflection_chain.inject({}) do |aliases, reflection| - table_alias = quote_table_name(alias_tracker.aliased_name_for( - 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" % [ - table_aliases[reflection], "#{polymorphic_reflection.options[:as]}_type", - @owner.class.quote_value(polymorphic_reflection.active_record.base_class.name) - ] - end - end - - def source_type_conditions(reflection) - if reflection.options[:source_type] - "%s.%s = %s" % [ - table_aliases[reflection.through_reflection], - reflection.source_reflection.options[:foreign_type].to_s, - @owner.class.quote_value(reflection.options[:source_type]) - ] - end - end - - # Construct attributes for associate pointing to owner. - def construct_owner_attributes(reflection) - if as = reflection.options[:as] - { "#{as}_id" => @owner.id, - "#{as}_type" => @owner.class.base_class.name.to_s } - else - { reflection.primary_key_name => @owner.id } - end - end - - # Construct attributes for :through pointing to owner and associate. - # This method is used when adding records to the association. Since this only makes sense for - # non-nested through associations, that's the only case we have to worry about here. - def construct_join_attributes(associate) - # TODO: revisit this to allow it for deletion, supposing dependent option is supported - raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro) - - join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) - - if @reflection.options[:source_type] - join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s) - end - - if @reflection.through_reflection.options[:conditions].is_a?(Hash) - join_attributes.merge!(@reflection.through_reflection.options[:conditions]) - end - - join_attributes - end - - def ensure_not_nested - if @reflection.nested? - raise HasManyThroughNestedAssociationsAreReadonly.new(@owner, @reflection) - end - end - end - end -end |