Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy. closes #3704
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3882 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
0859779d6f
commit
def746030c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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} " +
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
4
activerecord/test/fixtures/post.rb
vendored
4
activerecord/test/fixtures/post.rb
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user