diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 0ef11609ee..577670d84f 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,26 @@ *SVN* +* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 [Rick Olson] + + class CachedModel < ActiveRecord::Base + self.abstract_class = true + end + + class Post < CachedModel + end + + CachedModel.abstract_class? + => true + + Post.abstract_class? + => false + + Post.base_class + => Post + + Post.table_name + => 'posts' + * Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson] class Foo < ActiveRecord::Base diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index a4fc85b15e..d340cc30e3 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -77,7 +77,7 @@ def extract_options_from_args!(args) def set_belongs_to_association_for(record) if @reflection.options[:as] record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record? - record["#{@reflection.options[:as]}_type"] = ActiveRecord::Base.send(:class_name_of_active_record_descendant, @owner.class).to_s + record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s else record[@reflection.primary_key_name] = @owner.id unless @owner.new_record? end diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index 6c2b9b89fd..6d0704db74 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -9,7 +9,7 @@ def replace(record) unless record.new_record? @owner[@reflection.primary_key_name] = record.id - @owner[@reflection.options[:foreign_type]] = ActiveRecord::Base.send(:class_name_of_active_record_descendant, record.class).to_s + @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s end @updated = true diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index b861fde55d..8b471859c1 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -168,7 +168,7 @@ def construct_sql when @reflection.options[:as] @finder_sql = "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " + - "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, @owner.class).to_s}'" + "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote @owner.class.base_class.name.to_s}" @finder_sql << " AND (#{interpolate_sql(@conditions)})" if @conditions else 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 413f56857f..6db5eae0bc 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -67,7 +67,7 @@ def construct_conditions conditions = "#{@reflection.table_name}.#{@reflection.klass.primary_key} = #{through_reflection.table_name}.#{@reflection.klass.to_s.foreign_key} " + "AND #{through_reflection.table_name}.#{through_reflection.options[:as]}_id = #{@owner.quoted_id} " + - "AND #{through_reflection.table_name}.#{through_reflection.options[:as]}_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, @owner.class).to_s}'" + "AND #{through_reflection.table_name}.#{through_reflection.options[:as]}_type = #{@owner.class.quote @owner.class.base_class.name.to_s}" else conditions = "#{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{through_reflection.table_name}.#{@reflection.klass.to_s.foreign_key} " + diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 65f584c6f9..e0d6f64b27 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -69,7 +69,7 @@ def construct_sql when @reflection.options[:as] @finder_sql = "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " + - "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, @owner.class).to_s}'" + "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote @owner.class.base_class.name.to_s}" else @finder_sql = "#{@reflection.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8c1f615a37..a4505733de 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -599,7 +599,7 @@ def table_name end def reset_table_name - name = "#{table_name_prefix}#{undecorated_table_name(class_name_of_active_record_descendant(self))}#{table_name_suffix}" + name = "#{table_name_prefix}#{undecorated_table_name(base_class.name)}#{table_name_suffix}" set_table_name(name) name end @@ -614,9 +614,9 @@ def reset_primary_key key = 'id' case primary_key_prefix_type when :table_name - key = Inflector.foreign_key(class_name_of_active_record_descendant(self), false) + key = Inflector.foreign_key(base_class.name, false) when :table_name_with_underscore - key = Inflector.foreign_key(class_name_of_active_record_descendant(self)) + key = Inflector.foreign_key(base_class.name) end set_primary_key(key) key @@ -936,7 +936,15 @@ def base_class class_of_active_record_descendant(self) end - + # Set this to true if this is an abstract class (see #abstract_class?). + attr_accessor :abstract_class + + # Returns whether this class is a base AR class. If A is a base class and + # B descends from A, then B.base_class will return B. + def abstract_class? + abstract_class == true + end + private # Finder methods must instantiate through this method to work with the single-table inheritance model # that makes it possible to create objects of different types from the same table. @@ -1016,7 +1024,7 @@ def type_condition end # Guesses the table name, but does not decorate it with prefix and suffix information. - def undecorated_table_name(class_name = class_name_of_active_record_descendant(self)) + def undecorated_table_name(class_name = base_class.name) table_name = Inflector.underscore(Inflector.demodulize(class_name)) table_name = Inflector.pluralize(table_name) if pluralize_table_names table_name @@ -1172,7 +1180,7 @@ def compute_type(type_name) # Returns the class descending directly from ActiveRecord in the inheritance hierarchy. def class_of_active_record_descendant(klass) - if klass.superclass == Base + if klass.superclass == Base || klass.superclass.abstract_class? klass elsif klass.superclass.nil? raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord" @@ -1183,7 +1191,7 @@ def class_of_active_record_descendant(klass) # Returns the name of the class descending directly from ActiveRecord in the inheritance hierarchy. def class_name_of_active_record_descendant(klass) - class_of_active_record_descendant(klass).name + klass.base_class.name end # Accepts an array or string. The string is returned untouched, but the array has each value diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 6c5ae3edca..af976f532f 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -172,7 +172,7 @@ def execute_grouped_calculation(operation, column_name, column, aggregate, aggre if association key_ids = calculated_data.collect { |row| row[group_alias] } - key_records = ActiveRecord::Base.send(:class_of_active_record_descendant, association.klass).find(key_ids) + key_records = association.klass.base_class.find(key_ids) key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) } end diff --git a/activerecord/test/associations_join_model_test.rb b/activerecord/test/associations_join_model_test.rb index 2d74895664..58208ced65 100644 --- a/activerecord/test/associations_join_model_test.rb +++ b/activerecord/test/associations_join_model_test.rb @@ -38,7 +38,15 @@ def test_polymorphic_belongs_to def test_polymorphic_has_many_going_through_join_model assert_equal tags(:general), posts(:welcome).tags.first 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 + + tagging = tags(:misc).taggings.create(:taggable => post) + assert_equal "SubStiPost", tagging.taggable_type + end + def test_polymorphic_has_many_going_through_join_model_with_inheritance assert_equal tags(:general), posts(:thinking).tags.first end diff --git a/activerecord/test/base_test.rb b/activerecord/test/base_test.rb index d910215a0a..de08118240 100755 --- a/activerecord/test/base_test.rb +++ b/activerecord/test/base_test.rb @@ -21,6 +21,7 @@ class TestOracleDefault < ActiveRecord::Base; end class LoosePerson < ActiveRecord::Base attr_protected :credit_rating, :administrator + self.abstract_class = true end class LooseDescendant < LoosePerson @@ -1148,8 +1149,12 @@ def test_scoped_find_limit_offset end def test_base_class - assert_equal LoosePerson, LoosePerson.base_class - assert_equal LoosePerson, LooseDescendant.base_class + assert LoosePerson.abstract_class? + assert !LooseDescendant.abstract_class? + assert_equal LoosePerson, LoosePerson.base_class + assert_equal LooseDescendant, LooseDescendant.base_class + assert_equal TightPerson, TightPerson.base_class + assert_equal TightPerson, TightDescendant.base_class end def test_assert_queries diff --git a/activerecord/test/fixtures/post.rb b/activerecord/test/fixtures/post.rb index e5b95aff32..a00fd80671 100644 --- a/activerecord/test/fixtures/post.rb +++ b/activerecord/test/fixtures/post.rb @@ -39,5 +39,9 @@ def self.what_are_you class SpecialPost < Post; end; class StiPost < Post + self.abstract_class = true has_one :special_comment, :class_name => "SpecialComment" end + +class SubStiPost < StiPost +end