=== test/associations/join_model_test.rb ================================================================== --- test/associations/join_model_test.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ test/associations/join_model_test.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -27,7 +27,7 @@ assert_equal 2, authors(:mary).categorized_posts.size assert_equal 1, authors(:mary).unique_categorized_posts.size end - + def test_polymorphic_has_many assert posts(:welcome).taggings.include?(taggings(:welcome_general)) end @@ -47,6 +47,13 @@ end end + def test_polymorphic_has_one_going_thorough_join_model + assert_equal tags(:general), tag = posts(:welcome).tag + assert_no_queries do + tag.tagging + end + end + def test_count_polymorphic_has_many assert_equal 1, posts(:welcome).taggings.count assert_equal 1, posts(:welcome).tags.count @@ -66,6 +73,13 @@ end end + def test_has_one_going_thorough_join_model_with_include_on_source_reflection + assert_equal tags(:general), tag = posts(:welcome).funky_tag + assert_no_queries do + tag.tagging + end + end + def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection_with_find assert_equal tags(:general), tag = posts(:welcome).funky_tags.find(:first) assert_no_queries do @@ -90,6 +104,11 @@ assert_equal tags(:misc), posts(:welcome).super_tags.first end + def test_polymorphic_has_one_going_through_join_model_with_custom_foreign_key + assert_equal tags(:misc), taggings(:welcome_general).super_tag + assert_equal tags(:misc), posts(:welcome).super_tag + end + def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body' assert_instance_of SubStiPost, post @@ -102,10 +121,18 @@ assert_equal tags(:general), posts(:thinking).tags.first end + def test_polymorphic_has_one_going_through_join_model_with_inheritance + assert_equal tags(:general), posts(:thinking).tag + end + def test_polymorphic_has_many_going_through_join_model_with_inheritance_with_custom_class_name assert_equal tags(:general), posts(:thinking).funky_tags.first end + def test_polymorphic_has_one_going_through_join_model_with_inheritance_with_custom_class_name + assert_equal tags(:general), posts(:thinking).funky_tag + end + def test_polymorphic_has_many_create_model_with_inheritance post = posts(:thinking) assert_instance_of SpecialPost, post @@ -220,6 +247,16 @@ end end + def test_include_has_one_through + posts = Post.find(:all, :order => 'posts.id') + posts_with_lead_author = Post.find(:all, :include => :lead_author, :order => 'posts.id') + assert_equal posts.length, posts_with_lead_author.length + posts.length.times do |i| + actual_lead_author = assert_no_queries { posts_with_lead_author[i].lead_author } + assert(actual_lead_author.nil? || posts[i].authors.include?(actual_lead_author)) + end + end + def test_include_polymorphic_has_one post = Post.find_by_id(posts(:welcome).id, :include => :tagging) tagging = taggings(:welcome_general) @@ -237,6 +274,16 @@ end end + def test_include_polymorphic_has_one_through + posts = Post.find(:all, :order => 'posts.id') + posts_with_tag = Post.find(:all, :include => :tag, :order => 'posts.id') + assert_equal posts.length, posts_with_tag.length + posts.length.times do |i| + actual_tag = assert_no_queries { posts_with_tag[i].tag } + assert(actual_tag.nil? || posts[i].tags.include?(actual_tag)) + end + end + def test_include_polymorphic_has_many posts = Post.find(:all, :order => 'posts.id') posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id') @@ -273,6 +320,11 @@ assert_equal [authors(:mary)], posts(:authorless).authors end + def test_has_one_going_through_join_model_with_custom_foreign_key + assert_nil posts(:thinking).lead_author + assert_equal authors(:mary), posts(:authorless).lead_author + end + def test_belongs_to_polymorphic_with_counter_cache assert_equal 0, posts(:welcome)[:taggings_count] tagging = posts(:welcome).taggings.create(:tag => tags(:general)) @@ -281,15 +333,24 @@ assert posts(:welcome, :reload)[:taggings_count].zero? end - def test_unavailable_through_reflection + def test_unavailable_has_many_through_reflection assert_raises (ActiveRecord::HasManyThroughAssociationNotFoundError) { authors(:david).nothings } end + def test_unavailable_has_one_through_reflection + assert_raises (ActiveRecord::HasOneThroughAssociationNotFoundError) { authors(:david).nothing } + end + def test_has_many_through_join_model_with_conditions assert_equal [], posts(:welcome).invalid_taggings assert_equal [], posts(:welcome).invalid_tags end + def test_has_one_through_join_model_with_conditions + assert_equal [], posts(:welcome).invalid_last_categorization + assert_equal [], posts(:welcome).invalid_author + end + def test_has_many_polymorphic assert_raises ActiveRecord::HasManyThroughAssociationPolymorphicError do assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggables @@ -324,10 +385,18 @@ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tagging } end + def test_has_one_through_polymorphic_has_one + assert_raise(ActiveRecord::HasOneThroughSourceAssociationMacroError) { authors(:david).last_tagging_through_polymorphic_has_one } + end + def test_has_many_through_polymorphic_has_many assert_equal [taggings(:welcome_general), taggings(:thinking_general)], authors(:david).taggings.uniq.sort_by { |t| t.id } end + def test_has_one_through_polymorphic_has_many + assert_equal taggings(:welcome_general), authors(:david).last_tagging + end + def test_include_has_many_through_polymorphic_has_many author = Author.find_by_id(authors(:david).id, :include => :taggings) expected_taggings = [taggings(:welcome_general), taggings(:thinking_general)] @@ -340,10 +409,26 @@ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags } end + def test_has_many_through_has_one_through + assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags_through_post } + end + + def test_has_one_through_has_many_through + assert_raise(ActiveRecord::HasOneThroughSourceAssociationMacroError) { authors(:david).tag_through_posts } + end + + def test_has_one_through_has_one_through + assert_raise(ActiveRecord::HasOneThroughSourceAssociationMacroError) { authors(:david).tag_through_post } + end + def test_has_many_through_habtm assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories } end + def test_has_one_through_habtm + assert_raise(ActiveRecord::HasOneThroughSourceAssociationMacroError) { authors(:david).post_category } + end + def test_eager_load_has_many_through_has_many author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id' SpecialComment.new; VerySpecialComment.new @@ -351,7 +436,7 @@ assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id) end end - + def test_eager_load_has_many_through_has_many_with_conditions post = Post.find(:first, :include => :invalid_tags) assert_no_queries do @@ -371,12 +456,23 @@ assert_equal [], authors(:mary).favorite_authors end + def test_self_referential_has_one_through + assert_equal authors(:mary), authors(:david).most_favorite_author + assert_nil authors(:mary).most_favorite_author + end + def test_add_to_self_referential_has_many_through new_author = Author.create(:name => "Bob") authors(:david).author_favorites.create :favorite_author => new_author assert_equal new_author, authors(:david).reload.favorite_authors.first end + def test_add_to_self_referential_has_one_through + new_author = Author.create(:name => "Bob") + authors(:david).author_favorites.create :favorite_author => new_author + assert_equal new_author, authors(:david).reload.most_favorite_author + end + def test_has_many_through_uses_correct_attributes assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"] end Property changes on: test/fixtures/flowers.jpg ___________________________________________________________________ Name: svn:mime-type -image/jpeg +application/octet-stream === test/fixtures/post.rb ================================================================== --- test/fixtures/post.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ test/fixtures/post.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -12,6 +12,7 @@ find(:first, :order => "id DESC") end end + has_one :comment1 has_one :very_special_comment has_one :very_special_comment_with_post, :class_name => "VerySpecialComment", :include => :post @@ -27,9 +28,12 @@ :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id' end end + has_one :tag, :through => :taggings, :include => :tagging has_many :funky_tags, :through => :taggings, :source => :tag + has_one :funky_tag, :through => :taggings, :source => :tag has_many :super_tags, :through => :taggings + has_one :super_tag, :through => :taggings has_one :tagging, :as => :taggable has_many :invalid_taggings, :as => :taggable, :class_name => "Tagging", :conditions => 'taggings.id < 0' @@ -37,10 +41,14 @@ has_many :categorizations, :foreign_key => :category_id has_many :authors, :through => :categorizations + has_one :lead_author, :through => :categorizations, :source => :author has_many :readers has_many :people, :through => :readers + has_many :invalid_last_categorization, :class_name => "Categorization", :conditions => 'categorizations.id < 0' + has_many :invalid_author, :through => :invalid_last_categorization, :source => :author + def self.what_are_you 'a post...' end === test/fixtures/author.rb ================================================================== --- test/fixtures/author.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ test/fixtures/author.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -1,5 +1,6 @@ class Author < ActiveRecord::Base has_many :posts + has_one :post # for has_(one|many) through has_one through has_many :posts_with_comments, :include => :comments, :class_name => "Post" has_many :posts_with_categories, :include => :categories, :class_name => "Post" has_many :posts_with_comments_and_categories, :include => [ :comments, :categories ], :order => "posts.id", :class_name => "Post" @@ -16,6 +17,7 @@ end has_many :comments, :through => :posts has_many :funky_comments, :through => :posts, :source => :comments + has_one :comment, :through => :posts, :source => :comment1 has_many :special_posts has_many :special_post_comments, :through => :special_posts, :source => :comments @@ -50,19 +52,29 @@ has_many :unique_categorized_posts, :through => :categorizations, :source => :post, :uniq => true has_many :nothings, :through => :kateggorisatons, :class_name => 'Category' + has_one :nothing, :through => :kateggorisaton, :class_name => 'Category' has_many :author_favorites has_many :favorite_authors, :through => :author_favorites, :order => 'name' + has_one :most_favorite_author, :through => :author_favorites, :source => :favorite_author, :order => :name has_many :tagging, :through => :posts # through polymorphic has_one + has_one :last_tagging_through_polymorphic_has_one, :through => :posts, :source => :tagging # through polymorphic has_one has_many :taggings, :through => :posts, :source => :taggings # through polymorphic has_many - has_many :tags, :through => :posts # through has_many :through + has_one :last_tagging, :through => :posts, :source => :taggings, :order => :id # through polymorphic has_many + has_many :tags, :through => :posts # through has_many :through + has_many :tags_through_post, :through => :post, :source => :tags # through has_one :through + has_one :tag_through_posts, :through => :posts, :source => :tag # through has_many :through + has_one :tag_through_post, :through => :post, :source => :tag # through has_one :through has_many :post_categories, :through => :posts, :source => :categories + has_one :post_category, :through => :posts, :source => :categories # through habtm belongs_to :author_address attr_accessor :post_log + has_one :last_categorization, :class_name => 'Categorization' + def after_initialize @post_log = [] end Property changes on: test/fixtures ___________________________________________________________________ Name: svn:ignore -fixture_database*.sqlite* - === lib/active_record/reflection.rb ================================================================== --- lib/active_record/reflection.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ lib/active_record/reflection.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -166,19 +166,19 @@ def check_validity! if options[:through] if through_reflection.nil? - raise HasManyThroughAssociationNotFoundError.new(active_record.name, self) + raise (self.macro == :has_one ? HasOneThroughAssociationNotFoundError : HasManyThroughAssociationNotFoundError).new(active_record.name, self) end if source_reflection.nil? - raise HasManyThroughSourceAssociationNotFoundError.new(self) + raise (self.macro == :has_one ? HasOneThroughSourceAssociationNotFoundError : HasManyThroughSourceAssociationNotFoundError).new(self) end if source_reflection.options[:polymorphic] - raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection) + raise (self.macro == :has_one ? HasOneThroughAssociationPolymorphicError : HasManyThroughAssociationPolymorphicError).new(active_record.name, self, source_reflection) end unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil? - raise HasManyThroughSourceAssociationMacroError.new(self) + raise (self.macro == :has_one ? HasOneThroughSourceAssociationMacroError : HasManyThroughSourceAssociationMacroError).new(self) end end end === lib/active_record/associations/has_one_through_association.rb ================================================================== --- lib/active_record/associations/has_one_through_association.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ lib/active_record/associations/has_one_through_association.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -0,0 +1,22 @@ +module ActiveRecord + module Associations + class HasOneThroughAssociation < ThroughAssociation #:nodoc + def initialize(owner, reflection) + super + reflection.check_validity! + end + + private + def find_target + @reflection.klass.find(:first, + :select => construct_select, + :conditions => construct_conditions, + :joins => construct_joins, + :order => @reflection.options[:order], + :group => @reflection.options[:group], + :include => @reflection.options[:include] || @reflection.source_reflection.options[:include] + ) + end + end + end +end === lib/active_record/associations/has_many_through_association.rb ================================================================== --- lib/active_record/associations/has_many_through_association.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ lib/active_record/associations/has_many_through_association.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -1,6 +1,6 @@ module ActiveRecord module Associations - class HasManyThroughAssociation < AssociationProxy #:nodoc: + class HasManyThroughAssociation < ThroughAssociation #:nodoc: def initialize(owner, reflection) super reflection.check_validity! @@ -141,60 +141,6 @@ construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.association_foreign_key => associate.id) end - # Associate attributes pointing to owner, quoted. - def construct_quoted_owner_attributes(reflection) - if as = reflection.options[:as] - { "#{as}_id" => @owner.quoted_id, - "#{as}_type" => reflection.klass.quote_value( - @owner.class.base_class.name.to_s, - reflection.klass.columns_hash["#{as}_type"]) } - else - { reflection.primary_key_name => @owner.quoted_id } - end - end - - # Build SQL conditions from attributes, qualified by table name. - def construct_conditions - table_name = @reflection.through_reflection.table_name - conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| - "#{table_name}.#{attr} = #{value}" - end - conditions << sql_conditions if sql_conditions - "(" + conditions.join(') AND (') + ")" - end - - def construct_from - @reflection.table_name - end - - def construct_select(custom_select = nil) - selected = custom_select || @reflection.options[:select] || "#{@reflection.table_name}.*" - end - - def construct_joins(custom_joins = nil) - polymorphic_join = nil - if @reflection.through_reflection.options[:as] || @reflection.source_reflection.macro == :belongs_to - reflection_primary_key = @reflection.klass.primary_key - source_primary_key = @reflection.source_reflection.primary_key_name - else - reflection_primary_key = @reflection.source_reflection.primary_key_name - source_primary_key = @reflection.klass.primary_key - if @reflection.source_reflection.options[:as] - polymorphic_join = "AND %s.%s = %s" % [ - @reflection.table_name, "#{@reflection.source_reflection.options[:as]}_type", - @owner.class.quote_value(@reflection.through_reflection.klass.name) - ] - end - end - - "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [ - @reflection.through_reflection.table_name, - @reflection.table_name, reflection_primary_key, - @reflection.through_reflection.table_name, source_primary_key, - polymorphic_join - ] - end - def construct_scope { :create => construct_owner_attributes(@reflection), :find => { :from => construct_from, @@ -222,16 +168,6 @@ @counter_sql = @finder_sql end end - - def conditions - @conditions ||= [ - (interpolate_sql(@reflection.klass.send(:sanitize_sql, @reflection.options[:conditions])) if @reflection.options[:conditions]), - (interpolate_sql(@reflection.active_record.send(:sanitize_sql, @reflection.through_reflection.options[:conditions])) if @reflection.through_reflection.options[:conditions]), - ("#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.name.demodulize)}" unless @reflection.through_reflection.klass.descends_from_active_record?) - ].compact.collect { |condition| "(#{condition})" }.join(' AND ') unless (!@reflection.options[:conditions] && !@reflection.through_reflection.options[:conditions] && @reflection.through_reflection.klass.descends_from_active_record?) - end - - alias_method :sql_conditions, :conditions end end end === lib/active_record/associations/through_association.rb ================================================================== --- lib/active_record/associations/through_association.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ lib/active_record/associations/through_association.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -0,0 +1,69 @@ +module ActiveRecord + module Associations + class ThroughAssociation < AssociationProxy #:nodoc: + # Associate attributes pointing to owner, quoted. + def construct_quoted_owner_attributes(reflection) + if as = reflection.options[:as] + { "#{as}_id" => @owner.quoted_id, + "#{as}_type" => reflection.klass.quote_value( + @owner.class.base_class.name.to_s, + reflection.klass.columns_hash["#{as}_type"]) } + else + { reflection.primary_key_name => @owner.quoted_id } + end + end + + # Build SQL conditions from attributes, qualified by table name. + def construct_conditions + table_name = @reflection.through_reflection.table_name + conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| + "#{table_name}.#{attr} = #{value}" + end + conditions << sql_conditions if sql_conditions + "(" + conditions.join(') AND (') + ")" + end + + def construct_from + @reflection.table_name + end + + def construct_select(custom_select = nil) + selected = custom_select || @reflection.options[:select] || "#{@reflection.table_name}.*" + end + + def construct_joins(custom_joins = nil) + polymorphic_join = nil + if @reflection.through_reflection.options[:as] || @reflection.source_reflection.macro == :belongs_to + reflection_primary_key = @reflection.klass.primary_key + source_primary_key = @reflection.source_reflection.primary_key_name + else + reflection_primary_key = @reflection.source_reflection.primary_key_name + source_primary_key = @reflection.klass.primary_key + if @reflection.source_reflection.options[:as] + polymorphic_join = "AND %s.%s = %s" % [ + @reflection.table_name, "#{@reflection.source_reflection.options[:as]}_type", + @owner.class.quote_value(@reflection.through_reflection.klass.name) + ] + end + end + + "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [ + @reflection.through_reflection.table_name, + @reflection.table_name, reflection_primary_key, + @reflection.through_reflection.table_name, source_primary_key, + polymorphic_join + ] + end + + def conditions + @conditions ||= [ + (interpolate_sql(@reflection.klass.send(:sanitize_sql, @reflection.options[:conditions])) if @reflection.options[:conditions]), + (interpolate_sql(@reflection.active_record.send(:sanitize_sql, @reflection.through_reflection.options[:conditions])) if @reflection.through_reflection.options[:conditions]), + ("#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.name.demodulize)}" unless @reflection.through_reflection.klass.descends_from_active_record?) + ].compact.collect { |condition| "(#{condition})" }.join(' AND ') unless (!@reflection.options[:conditions] && !@reflection.through_reflection.options[:conditions] && @reflection.through_reflection.klass.descends_from_active_record?) + end + + alias_method :sql_conditions, :conditions + end + end +end === lib/active_record/associations.rb ================================================================== --- lib/active_record/associations.rb (/mirror/edge-rails/activerecord) (revision 4497) +++ lib/active_record/associations.rb (/mirror/has_one_through/trunk) (revision 4497) @@ -1,8 +1,10 @@ require 'active_record/associations/association_proxy' require 'active_record/associations/association_collection' +require 'active_record/associations/through_association' require 'active_record/associations/belongs_to_association' require 'active_record/associations/belongs_to_polymorphic_association' require 'active_record/associations/has_one_association' +require 'active_record/associations/has_one_through_association' require 'active_record/associations/has_many_association' require 'active_record/associations/has_many_through_association' require 'active_record/associations/has_and_belongs_to_many_association' @@ -15,6 +17,12 @@ end end + class HasOneThroughAssociationNotFoundError < ActiveRecordError #:nodoc: + def initialize(owner_class_name, reflection) + super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}") + end + end + class HasManyThroughAssociationPolymorphicError < ActiveRecordError #:nodoc: def initialize(owner_class_name, reflection, source_reflection) super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.") @@ -30,6 +38,14 @@ end end + class HasOneThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc: + def initialize(reflection) + through_reflection = reflection.through_reflection + source_reflection_names = reflection.source_reflection_names + source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect } + super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :connector => 'or'} in model #{through_reflection.klass}. Try 'has_one #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => '. Is it one of #{source_associations.to_sentence :connector => 'or'}?") + end + end class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc def initialize(reflection) through_reflection = reflection.through_reflection @@ -38,6 +54,14 @@ end end + class HasOneThroughSourceAssociationMacroError < ActiveRecordError #:nodoc + def initialize(reflection) + through_reflection = reflection.through_reflection + source_reflection = reflection.source_reflection + super("Invalid source reflection macro :#{source_reflection.macro}#{" :through" if source_reflection.options[:through]} for has_one #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}. Use :source to specify the source reflection.") + end + end + class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc: def initialize(owner, reflection) super("Cannot associate 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 create the has_many :through record associating them.") @@ -607,26 +631,28 @@ # has_one :attachment, :as => :attachable def has_one(association_id, options = {}) reflection = create_has_one_reflection(association_id, options) - - module_eval do - after_save <<-EOF - association = instance_variable_get("@#{reflection.name}") - if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id) - association["#{reflection.primary_key_name}"] = id - association.save(true) - end - EOF + + if options[:through] + association_accessor_methods(reflection, HasOneThroughAssociation) + else + module_eval do + after_save <<-EOF + association = instance_variable_get("@#{reflection.name}") + if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id) + association["#{reflection.primary_key_name}"] = id + association.save(true) + end + EOF + end + association_accessor_methods(reflection, HasOneAssociation) + association_constructor_method(:build, reflection, HasOneAssociation) + association_constructor_method(:create, reflection, HasOneAssociation) + # deprecated api + deprecated_has_association_method(reflection.name) + deprecated_association_comparison_method(reflection.name, reflection.class_name) end - - association_accessor_methods(reflection, HasOneAssociation) - association_constructor_method(:build, reflection, HasOneAssociation) - association_constructor_method(:create, reflection, HasOneAssociation) configure_dependency_for_has_one(reflection) - - # deprecated api - deprecated_has_association_method(reflection.name) - deprecated_association_comparison_method(reflection.name, reflection.class_name) end # Adds the following methods for retrieval and query for a single associated object that this object holds an id to. @@ -1099,7 +1125,8 @@ def create_has_one_reflection(association_id, options) options.assert_valid_keys( - :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as + :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, + :as, :through, :source ) create_reflection(:has_one, association_id, options, self) @@ -1453,7 +1480,8 @@ join_dependency.table_aliases[aliased_table_name] += 1 end - if reflection.macro == :has_and_belongs_to_many || (reflection.macro == :has_many && reflection.options[:through]) + if reflection.macro == :has_and_belongs_to_many || + ((reflection.macro == :has_many || reflection.macro == :has_one) && reflection.options[:through]) @aliased_join_table_name = reflection.macro == :has_and_belongs_to_many ? reflection.options[:join_table] : reflection.through_reflection.klass.table_name unless join_dependency.table_aliases[aliased_join_table_name].zero? @aliased_join_table_name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}_join" @@ -1480,7 +1508,7 @@ ] when :has_many, :has_one case - when reflection.macro == :has_many && reflection.options[:through] + when reflection.options[:through] through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' if through_reflection.options[:as] # has_many :through against a polymorphic join polymorphic_foreign_key = through_reflection.options[:as].to_s + '_id' Property changes on: ___________________________________________________________________ Name: svk:merge -5ecf4fe2-1ee6-0310-87b1-e25e094e27de:/branches/performance/activerecord:1468 Name: svn:ignore -pkg -doc -debug.log -