Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add ActiveRecord option to store the full class name on STI's type co…
…lumn, allowing one to have STI subclasses in different namespaces [#114]

Signed-off-by: rick <technoweenie@gmail.com>
  • Loading branch information
divoxx authored and technoweenie committed May 13, 2008
1 parent 2d372d7 commit bca8751
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 3 deletions.
10 changes: 7 additions & 3 deletions activerecord/lib/active_record/base.rb
Expand Up @@ -439,6 +439,10 @@ def self.reset_subclasses #:nodoc:
cattr_accessor :schema_format , :instance_writer => false
@@schema_format = :ruby

# Determine whether to store the full constant name including namespace when using STI
superclass_delegating_accessor :store_full_sti_class
self.store_full_sti_class = false

class << self # Class methods
# Find operates with four different retrieval approaches:
#
Expand Down Expand Up @@ -1557,8 +1561,8 @@ def add_conditions!(sql, conditions, scope = :auto)

def type_condition
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{store_full_sti_class ? name : name.demodulize}' ") do |condition, subclass|
condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{store_full_sti_class ? subclass.name : subclass.name.demodulize}' "
end

" (#{type_condition}) "
Expand Down Expand Up @@ -2492,7 +2496,7 @@ def create
# Message class in that example.
def ensure_proper_type
unless self.class.descends_from_active_record?
write_attribute(self.class.inheritance_column, Inflector.demodulize(self.class.name))
write_attribute(self.class.inheritance_column, store_full_sti_class ? self.class.name : Inflector.demodulize(self.class.name))
end
end

Expand Down
28 changes: 28 additions & 0 deletions activerecord/test/cases/inheritance_test.rb
Expand Up @@ -5,6 +5,34 @@

class InheritanceTest < ActiveRecord::TestCase
fixtures :companies, :projects, :subscribers, :accounts

def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = false
item = Namespaced::Company.new
assert_equal 'Company', item[:type]
ensure
ActiveRecord::Base.store_full_sti_class = old
end

def test_should_store_full_class_name_with_store_full_sti_class_option_enabled
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = true
item = Namespaced::Company.new
assert_equal 'Namespaced::Company', item[:type]
ensure
ActiveRecord::Base.store_full_sti_class = old
end

def test_different_namespace_subclass_should_load_correctly_with_store_full_sti_class_option
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = true
item = Namespaced::Company.create :name => "Wolverine 2"
assert_not_nil Company.find(item.id)
assert_not_nil Namespaced::Company.find(item.id)
ensure
ActiveRecord::Base.store_full_sti_class = old
end

def test_company_descends_from_active_record
assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? }
Expand Down
4 changes: 4 additions & 0 deletions activerecord/test/models/company.rb
Expand Up @@ -15,6 +15,10 @@ def arbitrary_method
end
end

module Namespaced
class Company < ::Company
end
end

class Firm < Company
has_many :clients, :order => "id", :dependent => :destroy, :counter_sql =>
Expand Down
3 changes: 3 additions & 0 deletions railties/configs/initializers/new_rails_defaults.rb
Expand Up @@ -7,6 +7,9 @@
# Include ActiveRecord class name as root for JSON serialized output.
ActiveRecord::Base.include_root_in_json = true

# Store the full class name (including module namespace) in STI type column
ActiveRecord::Base.store_full_sti_class = true

# Use ISO 8601 format for JSON serialized times and dates
ActiveSupport.use_standard_json_time_format = true

Expand Down

2 comments on commit bca8751

@duncanbeevers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the common technique of future-proofing migrations by defining the class being migrated within the namespace of the migration itself?

The solution could be as simple as adding a sti_class_name method on ActiveRecord::Base, cleaning up the repetition of all the store_full_sti_class ? self.class.name :

@divoxx
Copy link
Contributor Author

@divoxx divoxx commented on bca8751 May 14, 2008

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@duncanbeevers

I’m planning to address this issue and some others asap.
I’ll try to solve every issue listed in the ticket1, so if there is anything else please post there.

1 http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/114-namespaced-models-and-sti

Please sign in to comment.