diff options
-rwxr-xr-x | activerecord/lib/active_record/base.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/reflection.rb | 62 | ||||
-rw-r--r-- | activerecord/test/associations_go_eager_test.rb | 20 | ||||
-rw-r--r-- | activerecord/test/reflection_test.rb | 33 |
4 files changed, 63 insertions, 54 deletions
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3b5f9b94db..6ff0e4a9e0 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -836,7 +836,7 @@ module ActiveRecord #:nodoc: # MyApp::Business::Account would be appear as MyApp::Business::AccountSubclass. def compute_type(type_name) type_name_with_module(type_name).split("::").inject(Object) do |final_type, part| - final_type = final_type.const_get(part) + final_type.const_get(part) end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 6b718704d2..9aec0ca34b 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -10,13 +10,13 @@ module ActiveRecord def composed_of_with_reflection(part_id, options = {}) composed_of_without_reflection(part_id, options) - write_inheritable_array "aggregations", [ AggregateReflection.new(:composed_of, part_id, options, self) ] + reflect_on_all_aggregations << AggregateReflection.new(:composed_of, part_id, options, self) end - alias_method :composed_of, :composed_of_with_reflection + alias_method :composed_of, :composed_of_with_reflection end end - + for association_type in %w( belongs_to has_one has_many has_and_belongs_to_many ) base.module_eval <<-"end_eval" class << self @@ -24,16 +24,16 @@ module ActiveRecord def #{association_type}_with_reflection(association_id, options = {}) #{association_type}_without_reflection(association_id, options) - write_inheritable_array "associations", [ AssociationReflection.new(:#{association_type}, association_id, options, self) ] + reflect_on_all_associations << AssociationReflection.new(:#{association_type}, association_id, options, self) end - alias_method :#{association_type}, :#{association_type}_with_reflection + alias_method :#{association_type}, :#{association_type}_with_reflection end end_eval end end - # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations. + # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations. # This information can for example be used in a form builder that took an Active Record object and created input # fields for all of the attributes depending on their type and displayed the associations to other objects. # @@ -41,9 +41,9 @@ module ActiveRecord module ClassMethods # Returns an array of AggregateReflection objects for all the aggregations in the class. def reflect_on_all_aggregations - read_inheritable_attribute "aggregations" + read_inheritable_attribute(:aggregations) or write_inheritable_attribute(:aggregations, []) end - + # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example: # Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection def reflect_on_aggregation(aggregation) @@ -52,47 +52,47 @@ module ActiveRecord # Returns an array of AssociationReflection objects for all the aggregations in the class. def reflect_on_all_associations - read_inheritable_attribute "associations" + read_inheritable_attribute(:associations) or write_inheritable_attribute(:associations, []) end - + # Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example: # Account.reflect_on_association(:owner) # returns the owner AssociationReflection def reflect_on_association(association) - reflect_on_all_associations.find { |reflection| reflection.name == association } unless reflect_on_all_associations.nil? + reflect_on_all_associations.find { |reflection| reflection.name == association } end end - - # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of + + # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of # those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods. class MacroReflection attr_reader :active_record def initialize(macro, name, options, active_record) @macro, @name, @options, @active_record = macro, name, options, active_record end - + # Returns the name of the macro, so it would return :balance for "composed_of :balance, :class_name => 'Money'" or # :clients for "has_many :clients". def name @name end - - # Returns the name of the macro, so it would return :composed_of for + + # Returns the name of the macro, so it would return :composed_of for # "composed_of :balance, :class_name => 'Money'" or :has_many for "has_many :clients". def macro @macro end - - # Returns the hash of options used for the macro, so it would return { :class_name => "Money" } for + + # Returns the hash of options used for the macro, so it would return { :class_name => "Money" } for # "composed_of :balance, :class_name => 'Money'" or {} for "has_many :clients". def options @options end - + # Returns the class for the macro, so "composed_of :balance, :class_name => 'Money'" would return the Money class and # "has_many :clients" would return the Client class. def klass() end - + def ==(other_aggregation) name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record end @@ -102,9 +102,9 @@ module ActiveRecord # Holds all the meta-data about an aggregation as it was specified in the Active Record class. class AggregateReflection < MacroReflection #:nodoc: def klass - Object.const_get(options[:class_name] || name_to_class_name(name.id2name)) + Object.const_get(options[:class_name] || name_to_class_name(name.id2name)) end - + private def name_to_class_name(name) name.capitalize.gsub(/_(.)/) { |s| $1.capitalize } @@ -116,21 +116,23 @@ module ActiveRecord def klass @klass ||= active_record.send(:compute_type, (name_to_class_name(name.id2name))) end - + def table_name @table_name ||= klass.table_name end private def name_to_class_name(name) - if name !~ /::/ - class_name = active_record.send( - :type_name_with_module, - (options[:class_name] || active_record.class_name(active_record.table_name_prefix + name + active_record.table_name_suffix)) - ) + if name =~ /::/ + name + else + unless class_name = options[:class_name] + class_name = name.to_s.camelize + class_name = class_name.singularize if [:has_many, :has_and_belongs_to_many].include?(macro) + end + active_record.send(:type_name_with_module, class_name) end - return class_name || name end end end -end
\ No newline at end of file +end diff --git a/activerecord/test/associations_go_eager_test.rb b/activerecord/test/associations_go_eager_test.rb index 110fb8e4ae..892a879283 100644 --- a/activerecord/test/associations_go_eager_test.rb +++ b/activerecord/test/associations_go_eager_test.rb @@ -29,7 +29,7 @@ class EagerAssociationTest < Test::Unit::TestCase assert_equal posts(:thinking), posts[4] assert_equal posts(:welcome), posts[5] end - + def test_loading_with_multiple_associations posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id") assert_equal 2, posts.first.comments.size @@ -61,23 +61,23 @@ class EagerAssociationTest < Test::Unit::TestCase assert posts[0].categories.include?(categories(:technology)) assert posts[1].categories.include?(categories(:general)) end - + def test_eager_with_inheritance posts = SpecialPost.find(:all, :include => [ :comments ]) - end + end def test_eager_has_one_with_association_inheritance post = Post.find(4, :include => [ :very_special_comment ]) assert_equal "VerySpecialComment", post.very_special_comment.class.to_s - end - + end + def test_eager_has_many_with_association_inheritance post = Post.find(4, :include => [ :special_comments ]) post.special_comments.each do |special_comment| assert_equal "SpecialComment", special_comment.class.to_s end - end - + end + def test_eager_habtm_with_association_inheritance post = Post.find(6, :include => [ :special_categories ]) assert_equal 1, post.special_categories.size @@ -90,7 +90,8 @@ class EagerAssociationTest < Test::Unit::TestCase assert_not_nil companies(:first_firm).account f = Firm.find(:first, :include => :account, :conditions => ["companies.name = ?", "37signals"]) - assert_not_nil companies(:first_firm, :reload).account + assert_not_nil f.account + assert_equal companies(:first_firm, :reload).account, f.account end def test_eager_with_invalid_association_reference @@ -103,6 +104,3 @@ class EagerAssociationTest < Test::Unit::TestCase end end - - - diff --git a/activerecord/test/reflection_test.rb b/activerecord/test/reflection_test.rb index 1658408d7a..0935b7e5a2 100644 --- a/activerecord/test/reflection_test.rb +++ b/activerecord/test/reflection_test.rb @@ -17,7 +17,7 @@ class ReflectionTest < Test::Unit::TestCase @first.attribute_names ) end - + def test_columns assert_equal 12, Topic.columns.length end @@ -30,7 +30,7 @@ class ReflectionTest < Test::Unit::TestCase def test_content_columns assert_equal 8, Topic.content_columns.length end - + def test_column_string_type_and_limit assert_equal :string, @first.column_for_attribute("title").type assert_equal 255, @first.column_for_attribute("title").limit @@ -39,10 +39,10 @@ class ReflectionTest < Test::Unit::TestCase def test_human_name_for_column assert_equal "Author name", @first.column_for_attribute("author_name").human_name end - + def test_integer_columns assert_equal :integer, @first.column_for_attribute("id").type - end + end def test_aggregation_reflection reflection_for_address = ActiveRecord::Reflection::AggregateReflection.new( @@ -61,23 +61,32 @@ class ReflectionTest < Test::Unit::TestCase [ reflection_for_address, reflection_for_balance, reflection_for_gps_location ], Customer.reflect_on_all_aggregations ) - + assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address) - + assert_equal Address, Customer.reflect_on_aggregation(:address).klass end - - def test_association_reflection - reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new( - :has_many, :clients, { :order => "id", :dependent => true }, Firm - ) + + def test_has_many_reflection + reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => true }, Firm) assert_equal reflection_for_clients, Firm.reflect_on_association(:clients) assert_equal Client, Firm.reflect_on_association(:clients).klass + assert_equal 'companies', Firm.reflect_on_association(:clients).table_name + assert_equal Client, Firm.reflect_on_association(:clients_of_firm).klass + assert_equal 'companies', Firm.reflect_on_association(:clients_of_firm).table_name + end + + def test_has_one_reflection + reflection_for_account = ActiveRecord::Reflection::AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => true }, Firm) + assert_equal reflection_for_account, Firm.reflect_on_association(:account) + + assert_equal Account, Firm.reflect_on_association(:account).klass + assert_equal 'accounts', Firm.reflect_on_association(:account).table_name end - + def test_association_reflection_in_modules assert_equal MyApplication::Business::Client, MyApplication::Business::Firm.reflect_on_association(:clients_of_firm).klass assert_equal MyApplication::Business::Firm, MyApplication::Billing::Account.reflect_on_association(:firm).klass |