diff options
author | Jon Leighton <j@jonathanleighton.com> | 2010-10-31 11:21:28 +0000 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2010-10-31 11:21:28 +0000 |
commit | 9a1a32ac2b8a526f543367bc7e8258bbd7e6a164 (patch) | |
tree | a14e5d2b8d0b9f34766a91d3bac9bf78b445eb51 | |
parent | d010fb13ef622bdb781e3134005fc849db4c9bea (diff) | |
download | rails-9a1a32ac2b8a526f543367bc7e8258bbd7e6a164.tar.gz rails-9a1a32ac2b8a526f543367bc7e8258bbd7e6a164.tar.bz2 rails-9a1a32ac2b8a526f543367bc7e8258bbd7e6a164.zip |
Fix naughty trailing whitespace
21 files changed, 259 insertions, 259 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 diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 713c492f5e..4e398751d2 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -456,19 +456,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_polymorphic_with_primary_key_option assert_equal [categories(:general)], authors(:david).essay_categories - + authors = Author.joins(:essay_categories).where('categories.id' => categories(:general).id) assert_equal authors(:david), authors.first - + assert_equal [owners(:blackbeard)], authors(:david).essay_owners - + authors = Author.joins(:essay_owners).where("owners.name = 'blackbeard'") assert_equal authors(:david), authors.first end - + def test_has_many_through_with_primary_key_option assert_equal [categories(:general)], authors(:david).essay_categories_2 - + authors = Author.joins(:essay_categories_2).where('categories.id' => categories(:general).id) assert_equal authors(:david), authors.first end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 39e14b4bfd..1cf8c0539d 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -217,22 +217,22 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase minivan.dashboard end end - + def test_has_one_through_polymorphic_with_primary_key_option assert_equal categories(:general), authors(:david).essay_category - + authors = Author.joins(:essay_category).where('categories.id' => categories(:general).id) assert_equal authors(:david), authors.first - + assert_equal owners(:blackbeard), authors(:david).essay_owner - + authors = Author.joins(:essay_owner).where("owners.name = 'blackbeard'") assert_equal authors(:david), authors.first end - + def test_has_one_through_with_primary_key_option assert_equal categories(:general), authors(:david).essay_category_2 - + authors = Author.joins(:essay_category_2).where('categories.id' => categories(:general).id) assert_equal authors(:david), authors.first end diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index bfc290e877..db7c8b6c45 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -30,18 +30,18 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase :categorizations, :memberships, :essays # Through associations can either use the has_many or has_one macros. - # + # # has_many # - Source reflection can be has_many, has_one, belongs_to or has_and_belongs_to_many # - Through reflection can be has_many, has_one, belongs_to or has_and_belongs_to_many - # + # # has_one # - Source reflection can be has_one or belongs_to # - Through reflection can be has_one or belongs_to - # + # # Additionally, the source reflection and/or through reflection may be subject to # polymorphism and/or STI. - # + # # When testing these, we need to make sure it works via loading the association directly, or # joining the association, or including the association. We also need to ensure that associations # are readonly where relevant. @@ -51,18 +51,18 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase # Through: has_many def test_has_many_through_has_many_with_has_many_through_source_reflection general = tags(:general) - + assert_equal [general, general], authors(:david).tags - + assert_includes_and_joins_equal( Author.where('tags.id' => tags(:general).id), [authors(:david)], :tags ) - + # This ensures that the polymorphism of taggings is being observed correctly authors = Author.joins(:tags).where('taggings.taggable_type' => 'FakeModel') assert authors.empty? - + authors = assert_queries(5) { Author.includes(:tags).to_a } assert_no_queries do assert_equal [general, general], authors.first.tags @@ -74,236 +74,236 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase # Through: has_many through def test_has_many_through_has_many_through_with_has_many_source_reflection luke, david = subscribers(:first), subscribers(:second) - + author = authors(:david) assert_equal [luke, david, david], author.subscribers.order('subscribers.nick') - + # All authors with subscribers where one of the subscribers' nick is 'alterself' assert_includes_and_joins_equal( Author.where('subscribers.nick' => 'alterself'), [authors(:david)], :subscribers ) - + authors = assert_queries(4) { Author.includes(:subscribers).to_a } assert_no_queries do assert_equal [luke, david, david], authors.first.subscribers.sort_by(&:nick) end end - + # has_many through # Source: has_one through # Through: has_one def test_has_many_through_has_one_with_has_one_through_source_reflection founding = member_types(:founding) - + assert_equal [founding], members(:groucho).nested_member_types - + assert_includes_and_joins_equal( Member.where('member_types.id' => founding.id), [members(:groucho)], :nested_member_types ) - + members = assert_queries(4) { Member.includes(:nested_member_types).to_a } assert_no_queries do assert_equal [founding], members.first.nested_member_types end end - + # has_many through # Source: has_one # Through: has_one through def test_has_many_through_has_one_through_with_has_one_source_reflection mustache = sponsors(:moustache_club_sponsor_for_groucho) - + assert_equal [mustache], members(:groucho).nested_sponsors - + assert_includes_and_joins_equal( Member.where('sponsors.id' => mustache.id), [members(:groucho)], :nested_sponsors ) - + members = assert_queries(4) { Member.includes(:nested_sponsors).to_a } assert_no_queries do assert_equal [mustache], members.first.nested_sponsors end end - + # has_many through # Source: has_many through # Through: has_one def test_has_many_through_has_one_with_has_many_through_source_reflection groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) - + assert_equal [groucho_details, other_details], members(:groucho).organization_member_details.order('member_details.id') - + assert_includes_and_joins_equal( Member.where('member_details.id' => member_details(:groucho).id).order('member_details.id'), [members(:groucho), members(:some_other_guy)], :organization_member_details ) - + members = Member.joins(:organization_member_details). where('member_details.id' => 9) assert members.empty? - + members = assert_queries(4) { Member.includes(:organization_member_details).to_a.sort_by(&:id) } assert_no_queries do assert_equal [groucho_details, other_details], members.first.organization_member_details.sort_by(&:id) end end - + # has_many through # Source: has_many # Through: has_one through def test_has_many_through_has_one_through_with_has_many_source_reflection groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) - + assert_equal [groucho_details, other_details], members(:groucho).organization_member_details_2.order('member_details.id') - + assert_includes_and_joins_equal( Member.where('member_details.id' => groucho_details.id).order('member_details.id'), [members(:groucho), members(:some_other_guy)], :organization_member_details_2 ) - + members = Member.joins(:organization_member_details_2). where('member_details.id' => 9) assert members.empty? - + members = assert_queries(4) { Member.includes(:organization_member_details_2).to_a.sort_by(&:id) } assert_no_queries do assert_equal [groucho_details, other_details], members.first.organization_member_details_2.sort_by(&:id) end end - + # has_many through # Source: has_and_belongs_to_many # Through: has_many def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection general, cooking = categories(:general), categories(:cooking) - + assert_equal [general, cooking], authors(:bob).post_categories.order('categories.id') - + assert_includes_and_joins_equal( Author.where('categories.id' => cooking.id), [authors(:bob)], :post_categories ) - + authors = assert_queries(3) { Author.includes(:post_categories).to_a.sort_by(&:id) } assert_no_queries do assert_equal [general, cooking], authors[2].post_categories.sort_by(&:id) end end - + # has_many through # Source: has_many # Through: has_and_belongs_to_many def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection greetings, more = comments(:greetings), comments(:more_greetings) - + assert_equal [greetings, more], categories(:technology).post_comments.order('comments.id') - + assert_includes_and_joins_equal( Category.where('comments.id' => more.id).order('comments.id'), [categories(:general), categories(:technology)], :post_comments ) - + categories = assert_queries(3) { Category.includes(:post_comments).to_a.sort_by(&:id) } assert_no_queries do assert_equal [greetings, more], categories[1].post_comments.sort_by(&:id) end end - + # has_many through # Source: has_many through a habtm # Through: has_many through def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection greetings, more = comments(:greetings), comments(:more_greetings) - + assert_equal [greetings, more], authors(:bob).category_post_comments.order('comments.id') - + assert_includes_and_joins_equal( Author.where('comments.id' => comments(:does_it_hurt).id).order('comments.id'), [authors(:david), authors(:mary)], :category_post_comments ) - + authors = assert_queries(5) { Author.includes(:category_post_comments).to_a.sort_by(&:id) } assert_no_queries do assert_equal [greetings, more], authors[2].category_post_comments.sort_by(&:id) end end - + # has_many through # Source: belongs_to # Through: has_many through def test_has_many_through_has_many_through_with_belongs_to_source_reflection general = tags(:general) - + assert_equal [general, general], authors(:david).tagging_tags - + assert_includes_and_joins_equal( Author.where('tags.id' => tags(:general).id), [authors(:david)], :tagging_tags ) - + authors = assert_queries(5) { Author.includes(:tagging_tags).to_a } assert_no_queries do assert_equal [general, general], authors.first.tagging_tags end end - + # has_many through # Source: has_many through # Through: belongs_to def test_has_many_through_belongs_to_with_has_many_through_source_reflection welcome_general, thinking_general = taggings(:welcome_general), taggings(:thinking_general) - + assert_equal [welcome_general, thinking_general], categorizations(:david_welcome_general).post_taggings.order('taggings.id') - + assert_includes_and_joins_equal( Categorization.where('taggings.id' => welcome_general.id).order('taggings.id'), [categorizations(:david_welcome_general)], :post_taggings ) - + categorizations = assert_queries(4) { Categorization.includes(:post_taggings).to_a.sort_by(&:id) } assert_no_queries do assert_equal [welcome_general, thinking_general], categorizations.first.post_taggings.sort_by(&:id) end end - + # has_one through # Source: has_one through # Through: has_one def test_has_one_through_has_one_with_has_one_through_source_reflection founding = member_types(:founding) - + assert_equal founding, members(:groucho).nested_member_type - + assert_includes_and_joins_equal( Member.where('member_types.id' => founding.id), [members(:groucho)], :nested_member_type ) - + members = assert_queries(4) { Member.includes(:nested_member_type).to_a.sort_by(&:id) } assert_no_queries do assert_equal founding, members.first.nested_member_type end end - + # has_one through # Source: belongs_to # Through: has_one through def test_has_one_through_has_one_through_with_belongs_to_source_reflection general = categories(:general) - + assert_equal general, members(:groucho).club_category - + assert_includes_and_joins_equal( Member.where('categories.id' => categories(:technology).id), [members(:blarpy_winkup)], :club_category ) - + members = assert_queries(4) { Member.includes(:club_category).to_a.sort_by(&:id) } assert_no_queries do assert_equal general, members.first.club_category @@ -320,34 +320,34 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert_equal [subscribers(:first), subscribers(:second)], author.distinct_subscribers.order('subscribers.nick') end - + def test_nested_has_many_through_with_a_table_referenced_multiple_times author = authors(:bob) assert_equal [posts(:misc_by_bob), posts(:misc_by_mary), posts(:other_by_bob), posts(:other_by_mary)], author.similar_posts.sort_by(&:id) - + # Mary and Bob both have posts in misc, but they are the only ones. authors = Author.joins(:similar_posts).where('posts.id' => posts(:misc_by_bob).id) assert_equal [authors(:mary), authors(:bob)], authors.uniq.sort_by(&:id) - + # Check the polymorphism of taggings is being observed correctly (in both joins) authors = Author.joins(:similar_posts).where('taggings.taggable_type' => 'FakeModel') assert authors.empty? authors = Author.joins(:similar_posts).where('taggings_authors_join.taggable_type' => 'FakeModel') assert authors.empty? end - + def test_has_many_through_with_foreign_key_option_on_through_reflection assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order('posts.id') assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors - + references = Reference.joins(:agents_posts_authors).where('authors.id' => authors(:david).id) assert_equal [references(:david_unicyclist)], references end - + def test_has_many_through_with_foreign_key_option_on_source_reflection assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order('people.id') - + jobs = Job.joins(:agents) assert_equal [jobs(:unicyclist), jobs(:unicyclist)], jobs end @@ -355,7 +355,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_with_sti_on_through_reflection ratings = posts(:sti_comments).special_comments_ratings.sort_by(&:id) assert_equal [ratings(:special_comment_rating), ratings(:sub_special_comment_rating)], ratings - + # Ensure STI is respected in the join scope = Post.joins(:special_comments_ratings).where(:id => posts(:sti_comments).id) assert scope.where("comments.type" => "Comment").empty? @@ -366,101 +366,101 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_nested_has_many_through_writers_should_raise_error david = authors(:david) subscriber = subscribers(:first) - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do david.subscribers = [subscriber] end - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do david.subscriber_ids = [subscriber.id] end - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do david.subscribers << subscriber end - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do david.subscribers.delete(subscriber) end - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do david.subscribers.clear end - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do david.subscribers.build end - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do david.subscribers.create end end - + def test_nested_has_one_through_writers_should_raise_error groucho = members(:groucho) founding = member_types(:founding) - + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do groucho.nested_member_type = founding end end - + def test_nested_has_many_through_with_conditions_on_through_associations blue, bob = tags(:blue), authors(:bob) - + assert_equal [blue], bob.misc_post_first_blue_tags - + # Pointless condition to force single-query loading assert_includes_and_joins_equal( Author.where('tags.id = tags.id'), [bob], :misc_post_first_blue_tags ) - + assert Author.where('tags.id' => 100).joins(:misc_post_first_blue_tags).empty? - + authors = assert_queries(3) { Author.includes(:misc_post_first_blue_tags).to_a.sort_by(&:id) } assert_no_queries do assert_equal [blue], authors[2].misc_post_first_blue_tags end end - + def test_nested_has_many_through_with_conditions_on_source_associations blue, bob = tags(:blue), authors(:bob) - + assert_equal [blue], bob.misc_post_first_blue_tags_2 - + # Pointless condition to force single-query loading assert_includes_and_joins_equal( Author.where('tags.id = tags.id'), [bob], :misc_post_first_blue_tags_2 ) - + authors = assert_queries(4) { Author.includes(:misc_post_first_blue_tags_2).to_a.sort_by(&:id) } assert_no_queries do assert_equal [blue], authors[2].misc_post_first_blue_tags_2 end end - + def test_nested_has_many_through_with_foreign_key_option_on_the_source_reflection_through_reflection assert_equal [categories(:general)], organizations(:nsa).author_essay_categories - + organizations = Organization.joins(:author_essay_categories). where('categories.id' => categories(:general).id) assert_equal [organizations(:nsa)], organizations - + assert_equal categories(:general), organizations(:nsa).author_owned_essay_category - + organizations = Organization.joins(:author_owned_essay_category). where('categories.id' => categories(:general).id) assert_equal [organizations(:nsa)], organizations end - + private - + def assert_includes_and_joins_equal(query, expected, association) actual = assert_queries(1) { query.joins(association).to_a.uniq } assert_equal expected, actual - + actual = assert_queries(1) { query.includes(association).to_a.uniq } assert_equal expected, actual end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index a85ba623e1..66fe754046 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -200,7 +200,7 @@ class ReflectionTest < ActiveRecord::TestCase def test_has_many_through_reflection assert_kind_of ThroughReflection, Subscriber.reflect_on_association(:books) end - + def test_through_reflection_chain expected = [ Author.reflect_on_association(:essay_categories), @@ -208,10 +208,10 @@ class ReflectionTest < ActiveRecord::TestCase Organization.reflect_on_association(:authors) ] actual = Organization.reflect_on_association(:author_essay_categories).through_reflection_chain - + assert_equal expected, actual end - + def test_through_conditions expected = [ ["tags.name = 'Blue'"], @@ -220,7 +220,7 @@ class ReflectionTest < ActiveRecord::TestCase ] actual = Author.reflect_on_association(:misc_post_first_blue_tags).through_conditions assert_equal expected, actual - + expected = [ ["tags.name = 'Blue'", "taggings.comment = 'first'", "posts.title LIKE 'misc post%'"], [], @@ -229,27 +229,27 @@ class ReflectionTest < ActiveRecord::TestCase actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).through_conditions assert_equal expected, actual end - + def test_nested? assert !Author.reflect_on_association(:comments).nested? assert Author.reflect_on_association(:tags).nested? - + # Only goes :through once, but the through_reflection is a has_and_belongs_to_many, so this is # a nested through association assert Category.reflect_on_association(:post_comments).nested? end - + def test_association_primary_key # Normal association assert_equal "id", Author.reflect_on_association(:posts).association_primary_key.to_s assert_equal "name", Author.reflect_on_association(:essay).association_primary_key.to_s - + # Through association (uses the :primary_key option from the source reflection) assert_equal "nick", Author.reflect_on_association(:subscribers).association_primary_key.to_s assert_equal "name", Author.reflect_on_association(:essay_category).association_primary_key.to_s assert_equal "custom_primary_key", Author.reflect_on_association(:tags_with_primary_key).association_primary_key.to_s # nested end - + def test_active_record_primary_key assert_equal "nick", Subscriber.reflect_on_association(:subscriptions).active_record_primary_key.to_s assert_equal "name", Author.reflect_on_association(:essay).active_record_primary_key.to_s diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 7dcfbd268b..43bfd93e60 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -96,7 +96,7 @@ class Author < ActiveRecord::Base has_many :subscriptions, :through => :books has_many :subscribers, :through => :subscriptions, :order => "subscribers.nick" # through has_many :through (on through reflection) has_many :distinct_subscribers, :through => :subscriptions, :source => :subscriber, :select => "DISTINCT subscribers.*", :order => "subscribers.nick" - + has_one :essay, :primary_key => :name, :as => :writer has_one :essay_category, :through => :essay, :source => :category has_one :essay_owner, :through => :essay, :source => :owner @@ -107,7 +107,7 @@ class Author < ActiveRecord::Base has_many :essays, :primary_key => :name, :as => :writer has_many :essay_categories, :through => :essays, :source => :category has_many :essay_owners, :through => :essays, :source => :owner - + has_many :essays_2, :primary_key => :name, :class_name => 'Essay', :foreign_key => :author_id has_many :essay_categories_2, :through => :essays_2, :source => :category @@ -119,7 +119,7 @@ class Author < ActiveRecord::Base has_many :post_categories, :through => :posts, :source => :categories has_many :category_post_comments, :through => :categories, :source => :post_comments - + has_many :misc_posts, :class_name => 'Post', :conditions => "posts.title LIKE 'misc post%'" has_many :misc_post_first_blue_tags, :through => :misc_posts, :source => :first_blue_tags diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb index bddc1e5f0c..8e2fa96498 100644 --- a/activerecord/test/models/categorization.rb +++ b/activerecord/test/models/categorization.rb @@ -2,6 +2,6 @@ class Categorization < ActiveRecord::Base belongs_to :post belongs_to :category belongs_to :author - + has_many :post_taggings, :through => :author, :source => :taggings end diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb index c933943813..95825c72ef 100644 --- a/activerecord/test/models/category.rb +++ b/activerecord/test/models/category.rb @@ -23,7 +23,7 @@ class Category < ActiveRecord::Base has_many :categorizations has_many :authors, :through => :categorizations, :select => 'authors.*, categorizations.post_id' - + has_many :post_comments, :through => :posts, :source => :comments end diff --git a/activerecord/test/models/job.rb b/activerecord/test/models/job.rb index 46b1d87aa1..f7b0e787b1 100644 --- a/activerecord/test/models/job.rb +++ b/activerecord/test/models/job.rb @@ -2,6 +2,6 @@ class Job < ActiveRecord::Base has_many :references has_many :people, :through => :references belongs_to :ideal_reference, :class_name => 'Reference' - + has_many :agents, :through => :people end diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb index bed62f8b7f..fbf0b80164 100644 --- a/activerecord/test/models/member.rb +++ b/activerecord/test/models/member.rb @@ -9,15 +9,15 @@ class Member < ActiveRecord::Base has_one :member_detail has_one :organization, :through => :member_detail belongs_to :member_type - + has_many :nested_member_types, :through => :member_detail, :source => :member_type has_one :nested_member_type, :through => :member_detail, :source => :member_type - + has_many :nested_sponsors, :through => :sponsor_club, :source => :sponsor has_one :nested_sponsor, :through => :sponsor_club, :source => :sponsor - + has_many :organization_member_details, :through => :member_detail has_many :organization_member_details_2, :through => :organization, :source => :member_details - + has_one :club_category, :through => :club, :source => :category end diff --git a/activerecord/test/models/member_detail.rb b/activerecord/test/models/member_detail.rb index 0f53b69ced..fe619f8732 100644 --- a/activerecord/test/models/member_detail.rb +++ b/activerecord/test/models/member_detail.rb @@ -2,6 +2,6 @@ class MemberDetail < ActiveRecord::Base belongs_to :member belongs_to :organization has_one :member_type, :through => :member - + has_many :organization_member_details, :through => :organization, :source => :member_details end diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb index c18c28c696..4a4111833f 100644 --- a/activerecord/test/models/organization.rb +++ b/activerecord/test/models/organization.rb @@ -4,7 +4,7 @@ class Organization < ActiveRecord::Base has_many :authors, :primary_key => :name has_many :author_essay_categories, :through => :authors, :source => :essay_categories - + has_one :author, :primary_key => :name has_one :author_owned_essay_category, :through => :author, :source => :owned_essay_category diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index d35c51b660..5a5b6f9626 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -13,7 +13,7 @@ class Person < ActiveRecord::Base belongs_to :primary_contact, :class_name => 'Person' has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id' belongs_to :number1_fan, :class_name => 'Person' - + has_many :agents_posts, :through => :agents, :source => :posts has_many :agents_posts_authors, :through => :agents_posts, :source => :author diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 68d2b79a3b..e9c8c02e45 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -46,7 +46,7 @@ class Post < ActiveRecord::Base has_one :very_special_comment_with_post, :class_name => "VerySpecialComment", :include => :post has_many :special_comments has_many :nonexistant_comments, :class_name => 'Comment', :conditions => 'comments.id < 0' - + has_many :special_comments_ratings, :through => :special_comments, :source => :ratings has_and_belongs_to_many :categories @@ -65,7 +65,7 @@ class Post < ActiveRecord::Base has_many :super_tags, :through => :taggings has_many :tags_with_primary_key, :through => :taggings, :source => :tag_with_primary_key has_one :tagging, :as => :taggable - + has_many :first_taggings, :as => :taggable, :class_name => 'Tagging', :conditions => "taggings.comment = 'first'" has_many :first_blue_tags, :through => :first_taggings, :source => :tag, :conditions => "tags.name = 'Blue'" diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb index 2feb15d706..87d4a71963 100644 --- a/activerecord/test/models/reference.rb +++ b/activerecord/test/models/reference.rb @@ -1,7 +1,7 @@ class Reference < ActiveRecord::Base belongs_to :person belongs_to :job - + has_many :agents_posts_authors, :through => :person end |