Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #7143: golden.diff

File golden.diff, 14.8 kB (added by protocool, 3 years ago)
  • activerecord/test/associations/join_model_test.rb

    old new  
    298298      assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggings.find(:all, :include => :taggable) 
    299299    end 
    300300  end 
     301   
     302  def test_has_many_polymorphic_with_source_type 
     303    assert_equal [posts(:welcome), posts(:thinking)], tags(:general).tagged_posts 
     304  end 
    301305 
     306  def test_eager_has_many_polymorphic_with_source_type 
     307    tag_with_include = Tag.find(tags(:general).id, :include => :tagged_posts) 
     308    desired = [posts(:welcome), posts(:thinking)] 
     309    assert_no_queries do 
     310      assert_equal desired, tag_with_include.tagged_posts 
     311    end 
     312  end 
     313 
    302314  def test_has_many_through_has_many_find_all 
    303315    assert_equal comments(:greetings), authors(:david).comments.find(:all, :order => 'comments.id').first 
    304316  end 
  • activerecord/test/fixtures/tag.rb

    old new  
    22  has_many :taggings 
    33  has_many :taggables, :through => :taggings 
    44  has_one  :tagging 
     5 
     6  has_many :tagged_posts, :through => :taggings, :source => :taggable, :source_type => 'Post' 
    57end 
  • activerecord/lib/active_record/reflection.rb

    old new  
    178178          if source_reflection.nil? 
    179179            raise HasManyThroughSourceAssociationNotFoundError.new(self) 
    180180          end 
     181 
     182          if options[:source_type] && source_reflection.options[:polymorphic].nil? 
     183            raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection) 
     184          end 
    181185           
    182           if source_reflection.options[:polymorphic] 
     186          if source_reflection.options[:polymorphic] && options[:source_type].nil? 
    183187            raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection) 
    184188          end 
    185189           
     
    193197        def derive_class_name 
    194198          # get the class_name of the belongs_to association of the through reflection 
    195199          if through_reflection 
    196             source_reflection.class_name 
     200            options[:source_type] || source_reflection.class_name 
    197201          else 
    198202            class_name = name.to_s.camelize 
    199203            class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro) 
  • activerecord/lib/active_record/associations/has_many_through_association.rb

    old new  
    138138 
    139139        # Construct attributes for :through pointing to owner and associate. 
    140140        def construct_join_attributes(associate) 
    141           construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.association_foreign_key => associate.id) 
     141          returning construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.association_foreign_key => associate.id) do |join_attributes| 
     142            if @reflection.options[:source_type] 
     143              join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s) 
     144            end 
     145          end 
    142146        end 
    143147 
    144148        # Associate attributes pointing to owner, quoted. 
     
    176180          if @reflection.through_reflection.options[:as] || @reflection.source_reflection.macro == :belongs_to 
    177181            reflection_primary_key = @reflection.klass.primary_key 
    178182            source_primary_key     = @reflection.source_reflection.primary_key_name 
     183            if @reflection.options[:source_type] 
     184              polymorphic_join = "AND %s.%s = %s" % [ 
     185                @reflection.through_reflection.table_name, "#{@reflection.source_reflection.options[:foreign_type]}", 
     186                @owner.class.quote_value(@reflection.options[:source_type]) 
     187              ] 
     188            end 
    179189          else 
    180190            reflection_primary_key = @reflection.source_reflection.primary_key_name 
    181191            source_primary_key     = @reflection.klass.primary_key 
  • activerecord/lib/active_record/associations.rb

    old new  
    2020      super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.") 
    2121    end 
    2222  end 
    23  
     23   
     24  class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc: 
     25    def initialize(owner_class_name, reflection, source_reflection) 
     26      super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic.  Try removing :source_type on your association.") 
     27    end 
     28  end 
     29   
    2430  class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc: 
    2531    def initialize(reflection) 
    2632      through_reflection      = reflection.through_reflection 
     
    593599      # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries.  Only use it if the name cannot be  
    594600      #   inferred from the association.  <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either +:subscribers+ or 
    595601      #   +:subscriber+ on +Subscription+, unless a +:source+ is given. 
     602      # * <tt>:source_type</tt>: Specifies type of the source association used by <tt>has_many :through</tt> queries where the source association 
     603      #   is a polymorphic belongs_to. 
    596604      # * <tt>:uniq</tt> - if set to true, duplicates will be omitted from the collection. Useful in conjunction with :through. 
    597605      # 
    598606      # Option examples: 
     
    11511159            :class_name, :table_name, :foreign_key, 
    11521160            :exclusively_dependent, :dependent, 
    11531161            :select, :conditions, :include, :order, :group, :limit, :offset, 
    1154             :as, :through, :source, 
     1162            :as, :through, :source, :source_type, 
    11551163            :uniq, 
    11561164            :finder_sql, :counter_sql,  
    11571165            :before_add, :after_add, :before_remove, :after_remove,  
     
    15551563                  case 
    15561564                    when reflection.macro == :has_many && reflection.options[:through] 
    15571565                      through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' 
     1566 
     1567                      jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil 
     1568                      first_key = second_key = as_extra = nil 
     1569                       
    15581570                      if through_reflection.options[:as] # has_many :through against a polymorphic join 
    1559                         polymorphic_foreign_key  = through_reflection.options[:as].to_s + '_id' 
    1560                         polymorphic_foreign_type = through_reflection.options[:as].to_s + '_type' 
    1561  
    1562                         " LEFT OUTER JOIN %s ON (%s.%s = %s.%s AND %s.%s = %s) "  % [ 
    1563                           table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), 
    1564                           aliased_join_table_name, polymorphic_foreign_key, 
    1565                           parent.aliased_table_name, parent.primary_key, 
    1566                           aliased_join_table_name, polymorphic_foreign_type, klass.quote_value(parent.active_record.base_class.name)] + 
    1567                         " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [table_name_and_alias, 
    1568                           aliased_table_name, primary_key, aliased_join_table_name, options[:foreign_key] || reflection.klass.to_s.classify.foreign_key 
     1571                        jt_foreign_key = through_reflection.options[:as].to_s + '_id' 
     1572                        jt_as_extra = " AND %s.%s = %s" % [ 
     1573                            aliased_join_table_name, reflection.active_record.connection.quote_column_name(through_reflection.options[:as].to_s + '_type'),  
     1574                            klass.quote_value(parent.active_record.base_class.name) 
    15691575                        ] 
    15701576                      else 
    1571                         if source_reflection.macro == :has_many && source_reflection.options[:as] 
    1572                           " LEFT OUTER JOIN %s ON %s.%s = %s.%s "  % [ 
    1573                             table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), aliased_join_table_name, 
    1574                             through_reflection.primary_key_name, 
    1575                             parent.aliased_table_name, parent.primary_key] + 
    1576                           " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s " % [ 
    1577                             table_name_and_alias, 
    1578                             aliased_table_name, "#{source_reflection.options[:as]}_id",  
    1579                             aliased_join_table_name, options[:foreign_key] || primary_key, 
    1580                             aliased_table_name, "#{source_reflection.options[:as]}_type",  
     1577                        jt_foreign_key = through_reflection.primary_key_name 
     1578                      end 
     1579                       
     1580                      case source_reflection.macro 
     1581                      when :has_many 
     1582                        if source_reflection.options[:as] 
     1583                          first_key   = "#{source_reflection.options[:as]}_id" 
     1584                          second_key  = options[:foreign_key] || primary_key 
     1585                          as_extra    = " AND %s.%s = %s" % [ 
     1586                            aliased_table_name, reflection.active_record.connection.quote_column_name("#{source_reflection.options[:as]}_type"),  
    15811587                            klass.quote_value(source_reflection.active_record.base_class.name) 
    15821588                          ] 
    15831589                        else 
    1584                           case source_reflection.macro 
    1585                             when :belongs_to 
    1586                               first_key  = primary_key 
    1587                               second_key = source_reflection.options[:foreign_key] || klass.to_s.classify.foreign_key 
    1588                               extra      = nil 
    1589                             when :has_many 
    1590                               first_key  = through_reflection.klass.base_class.to_s.classify.foreign_key 
    1591                               second_key = options[:foreign_key] || primary_key 
    1592                               extra      = through_reflection.klass.descends_from_active_record? ? nil : 
    1593                                 " AND %s.%s = %s" % [ 
    1594                                   aliased_join_table_name, 
    1595                                   reflection.active_record.connection.quote_column_name(through_reflection.active_record.inheritance_column), 
    1596                                   through_reflection.klass.quote_value(through_reflection.klass.name.demodulize)] 
    1597                           end 
    1598                           " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s) "  % [ 
    1599                             table_alias_for(through_reflection.klass.table_name, aliased_join_table_name),  
    1600                             aliased_join_table_name, through_reflection.primary_key_name, 
    1601                             parent.aliased_table_name, parent.primary_key, extra] + 
    1602                           " LEFT OUTER JOIN %s ON (%s.%s = %s.%s) " % [ 
    1603                             table_name_and_alias, 
    1604                             aliased_table_name, first_key,  
    1605                             aliased_join_table_name, second_key 
     1590                          first_key   = through_reflection.klass.base_class.to_s.classify.foreign_key 
     1591                          second_key  = options[:foreign_key] || primary_key 
     1592                        end 
     1593                         
     1594                        unless through_reflection.klass.descends_from_active_record? 
     1595                          jt_sti_extra = " AND %s.%s = %s" % [ 
     1596                            aliased_join_table_name, 
     1597                            reflection.active_record.connection.quote_column_name(through_reflection.active_record.inheritance_column), 
     1598                            through_reflection.klass.quote_value(through_reflection.klass.name.demodulize)] 
     1599                        end 
     1600                      when :belongs_to 
     1601                        first_key = primary_key 
     1602                        if reflection.options[:source_type] 
     1603                          second_key = source_reflection.association_foreign_key 
     1604                          jt_source_extra = " AND %s.%s = %s" % [ 
     1605                              aliased_join_table_name, reflection.active_record.connection.quote_column_name(reflection.source_reflection.options[:foreign_type]), 
     1606                              klass.quote_value(reflection.options[:source_type]) 
    16061607                          ] 
     1608                        else 
     1609                          second_key = source_reflection.options[:foreign_key] || klass.to_s.classify.foreign_key 
    16071610                        end 
    16081611                      end 
     1612                       
     1613                      " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s%s%s) " % [ 
     1614                        table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), 
     1615                        parent.aliased_table_name, reflection.active_record.connection.quote_column_name(parent.primary_key), 
     1616                        aliased_join_table_name, reflection.active_record.connection.quote_column_name(jt_foreign_key),  
     1617                        jt_as_extra, jt_source_extra, jt_sti_extra 
     1618                      ] + 
     1619                      " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s) " % [ 
     1620                        table_name_and_alias,  
     1621                        aliased_table_name, reflection.active_record.connection.quote_column_name(first_key), 
     1622                        aliased_join_table_name, reflection.active_record.connection.quote_column_name(second_key), 
     1623                        as_extra 
     1624                      ] 
    16091625                     
    16101626                    when reflection.macro == :has_many && reflection.options[:as] 
    16111627                      " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s" % [ 
     
    16521668            end 
    16531669             
    16541670            protected 
     1671 
    16551672              def pluralize(table_name) 
    16561673                ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name 
    16571674              end