diff --git a/README.md b/README.md
index b2fb896..9414a62 100644
--- a/README.md
+++ b/README.md
@@ -4,12 +4,12 @@
Given the following class definitions:
```ruby
-class Address
+class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
end
-class Person
- has_many :addresses, :as => addressable
+class Person < ActiveRecord::Base
+ has_many :addresses, :as => :addressable
end
class Vendor < Person
@@ -33,14 +33,14 @@ will output:
#
```
-Notice that addressable_type column is Person even though the actual class is Vendor.
+Notice that `addressable_type` column is `Person` even though the actual class is `Vendor`.
Normally, this isn't a problem, however, it can have negative performance characteristics in certain circumstances. The most obvious one is that
-a join with persons or an extra query is required to find out the actual type of addressable.
+a join with persons or an extra query is required to find out the actual type of `addressable`.
-This gem adds the ActiveRecord::Base.store_base_sti_class configuration option. It defaults to true for backwards compatibility. Setting it to false will alter ActiveRecord's behavior to store the actual class in polymorphic _type columns when STI is used.
+This gem adds the ActiveRecord::Base.store_base_sti_class configuration option. It defaults to true for backwards compatibility. Setting it to false will alter ActiveRecord's behavior to store the actual class in polymorphic `_type` columns when STI is used.
-In the example above, if the ActiveRecord::Base.store_base_sti_class is false, the output will be,
+In the example above, if the `ActiveRecord::Base.store_base_sti_class is false`, the output will be,
```
#
#
@@ -54,18 +54,31 @@ Add the following line to your Gemfile,
gem 'store_base_sti_class'
```
-then bundle install. Once you have the gem installed, add the following to one of the initializers (or make a new one) in config/initializers,
+then bundle install. Once you have the gem installed, add the following to one of the initializers (or make a new one) in `config/initializers`,
- ActiveRecord::Base.store_base_sti_class = false
+```ruby
+ActiveRecord::Base.store_base_sti_class = false
+```
+
+When changing this behavior, you will have write a migration to update all of your existing `_type` columns accordingly. You may also need to change your application if it explicitly relies on the `_type` columns.
+
+If you only want to store the actual STI subclass type for certain classes and let all others use the
+default behavior of storing the STI base class, just set `store_sti_classes_for` to which classes should
+have the actual STI class stored.
-When changing this behavior, you will have write a migration to update all of your existing _type columns accordingly. You may also need to change your application if it explicitly relies on the _type columns.
+So in the example above, if you *only* wanted it to store the STI type for Person and its subclasses
+(Vendor), you would set:
+
+```ruby
+ActiveRecord::Base.store_sti_classes_for = ['Person']
+```
## Notes
This gem incorporates work from:
- https://github.com/codepodu/store_base_sti_class_for_4_0
-It currently works with ActiveRecord 4.0.x through 5.0.x. If you need support for ActiveRecord 3.x, use a pre-1.0 version of the gem.
+It currently works with ActiveRecord 4.0.x through 5.1.x. If you need support for ActiveRecord 3.x, use a pre-1.0 version of the gem.
## Copyright
diff --git a/lib/store_base_sti_class_for_5_1.rb b/lib/store_base_sti_class_for_5_1.rb
index 9e832df..7d44c89 100644
--- a/lib/store_base_sti_class_for_5_1.rb
+++ b/lib/store_base_sti_class_for_5_1.rb
@@ -4,8 +4,38 @@
module ActiveRecord
class Base
- class_attribute :store_base_sti_class
- self.store_base_sti_class = true
+ class_attribute :_store_sti_classes_for, instance_accessor: false
+ def self.store_sti_classes_for
+ _store_sti_classes_for
+ end
+ # Override the setter so we can validate the input
+ def self.store_sti_classes_for=(new)
+ raise ArgumentError, "store_sti_classes_for must be set to an array or :all but was #{new.inspect}" unless new == :all or new.is_a? Array
+ new.map(&:constantize).each { |klass|
+ if klass != klass.base_class
+ raise ArgumentError, " You tried to set store_sti_classes_for to #{klass}, but store_sti_classes_for should only be set to an array of *base* classes for which you want to store the STI class (itself or any of its STI subclasses) in any _type columns. Did you mean '#{klass.base_class}'?"
+ end
+ } if new.is_a? Array
+ self._store_sti_classes_for = new
+ end
+ self.store_sti_classes_for = []
+
+ def self.store_sti_class?(klass)
+ return true if store_sti_classes_for == :all
+ klass = klass.is_a?(Class) ? klass : klass.constantize
+ store_sti_classes_for.include? klass.base_class.name
+ end
+
+ # For backwards compatibility
+ def self.store_base_sti_class=(new)
+ if new == true
+ self.store_sti_classes_for = []
+ elsif new == false
+ self.store_sti_classes_for = :all
+ else
+ raise ArgumentError, "store_base_sti_class can only be set to true or false but tried setting to #{new}"
+ end
+ end
end
module Associations
@@ -23,7 +53,7 @@ def creation_attributes
# original:
# attributes[reflection.type] = owner.class.base_class.name
- attributes[reflection.type] = ActiveRecord::Base.store_base_sti_class ? owner.class.base_class.name : owner.class.name
+ attributes[reflection.type] = ActiveRecord::Base.store_sti_class?(owner.class) ? owner.class.name : owner.class.base_class.name
# END PATCH
end
end
@@ -70,7 +100,7 @@ def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
# START PATCH
# original:
# value = foreign_klass.base_class.name
- value = ActiveRecord::Base.store_base_sti_class ? foreign_klass.base_class.name : foreign_klass.name
+ value = ActiveRecord::Base.store_sti_class?(foreign_klass) ? foreign_klass.name : foreign_klass.base_class.name
# END PATCH
column = klass.columns_hash[reflection.type.to_s]
@@ -99,7 +129,7 @@ def replace_keys(record)
# original:
# owner[reflection.foreign_type] = record.class.base_class.name
- owner[reflection.foreign_type] = ActiveRecord::Base.store_base_sti_class ? record.class.base_class.name : record.class.name
+ owner[reflection.foreign_type] = ActiveRecord::Base.store_sti_class?(record.class) ? record.class.name : record.class.base_class.name
# END PATCH
end
@@ -145,7 +175,7 @@ def build_scope
# original:
# scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
- scope.where!(klass.table_name => { reflection.type => ActiveRecord::Base.store_base_sti_class ? model.base_class.sti_name : model.sti_name })
+ scope.where!(klass.table_name => { reflection.type => ActiveRecord::Base.store_sti_class?(model) ? model.sti_name : model.base_class.sti_name })
# END PATCH
end
@@ -166,10 +196,10 @@ def through_scope
# original: scope.where! reflection.foreign_type => options[:source_type]
adjusted_foreign_type =
- if ActiveRecord::Base.store_base_sti_class
- options[:source_type]
- else
+ if ActiveRecord::Base.store_sti_class?(options[:source_type])
([options[:source_type].constantize] + options[:source_type].constantize.descendants).map(&:to_s)
+ else
+ options[:source_type]
end
scope.where! reflection.foreign_type => adjusted_foreign_type
@@ -202,7 +232,7 @@ def self.get_bind_values(owner, chain)
if last_reflection.type
# START PATCH
# original: binds << owner.class.base_class.name
- binds << (ActiveRecord::Base.store_base_sti_class ? owner.class.base_class.name : owner.class.name)
+ binds << (ActiveRecord::Base.store_sti_class?(owner.class) ? owner.class.name : owner.class.base_class.name)
# END PATCH
end
@@ -210,7 +240,7 @@ def self.get_bind_values(owner, chain)
if reflection.type
# START PATCH
# original: binds << next_reflection.klass.base_class.name
- binds << (ActiveRecord::Base.store_base_sti_class ? next_reflection.klass.base_class.name : next_reflection.klass.name)
+ binds << (ActiveRecord::Base.store_sti_class?(next_reflection.klass) ? next_reflection.klass.name : next_reflection.klass.base_class.name)
# END PATCH
end
end
@@ -231,11 +261,11 @@ def next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
# original:
# value = transform_value(next_reflection.klass.base_class.name)
# scope = scope.where(table.name => { reflection.type => value })
- if ActiveRecord::Base.store_base_sti_class
- value = transform_value(next_reflection.klass.base_class.name)
- else
- klass = next_reflection.klass
+ klass = next_reflection.klass
+ if ActiveRecord::Base.store_sti_class?(klass)
value = ([klass] + klass.descendants).map(&:name)
+ else
+ value = transform_value(klass.base_class.name)
end
scope = scope.where(table.name => { reflection.type => value })
# END PATCH
@@ -255,7 +285,7 @@ def last_chain_scope(scope, table, reflection, owner)
if reflection.type
# BEGIN PATCH
# polymorphic_type = transform_value(owner.class.base_class.name)
- polymorphic_type = transform_value(ActiveRecord::Base.store_base_sti_class ? owner.class.base_class.name : owner.class.name)
+ polymorphic_type = transform_value(ActiveRecord::Base.store_sti_class?(owner.class) ? owner.class.name : owner.class.base_class.name)
# END PATCH
scope = scope.where(table.name => { reflection.type => polymorphic_type })
end
@@ -290,7 +320,7 @@ def construct_join_attributes(*records)
# records.map { |record| record.class.base_class.name }
join_attributes[source_reflection.foreign_type] =
- records.map { |record| ActiveRecord::Base.store_base_sti_class ? record.class.base_class.name : record.class.name }
+ records.map { |record| ActiveRecord::Base.store_sti_class?(record.class) ? record.class.name : record.class.base_class.name }
# END PATCH
end
@@ -314,10 +344,12 @@ def build_through_record(record)
through_record.send("#{source_reflection.name}=", record)
# START PATCH
- if ActiveRecord::Base.store_base_sti_class
- if options[:source_type]
- through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
- end
+ # original:
+ # if options[:source_type]
+ # through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
+ # end
+ if options[:source_type] && !ActiveRecord::Base.store_sti_class?(options[:source_type])
+ through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
end
# END PATCH
@@ -335,10 +367,10 @@ def source_type_info
# START PATCH
adjusted_source_type =
- if ActiveRecord::Base.store_base_sti_class
- source_type
- else
+ if ActiveRecord::Base.store_sti_class?(source_type)
([source_type.constantize] + source_type.constantize.descendants).map(&:to_s)
+ else
+ source_type
end
# END PATCH
diff --git a/store_base_sti_class.gemspec b/store_base_sti_class.gemspec
index 623fdd1..5e7705d 100644
--- a/store_base_sti_class.gemspec
+++ b/store_base_sti_class.gemspec
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
s.homepage = 'http://github.com/appfolio/store_base_sti_class'
s.licenses = ['MIT']
s.rubygems_version = '2.2.2'
- s.summary = 'Modifies ActiveRecord 4.0.x - 5.0.x with the ability to store the actual class (instead of the base class) in polymorhic _type columns when using STI'
+ s.summary = 'Modifies ActiveRecord 4.0.x - 5.1.x with the ability to store the actual class (instead of the base class) in polymorhic _type columns when using STI'
s.add_runtime_dependency(%q, ['>= 4.0'])
s.add_development_dependency(%q, ['>= 4.0'])
diff --git a/test/models.rb b/test/models.rb
index 06722f0..b846dab 100644
--- a/test/models.rb
+++ b/test/models.rb
@@ -48,3 +48,14 @@ class Tag < ActiveRecord::Base
class SpecialTag < Tag
end
+
+class Address < ActiveRecord::Base
+ belongs_to :addressable, :polymorphic => true
+end
+
+class Person < ActiveRecord::Base
+ has_many :addresses, :as => :addressable
+end
+
+class Vendor < Person
+end
diff --git a/test/schema.rb b/test/schema.rb
index fe21962..c088bfe 100644
--- a/test/schema.rb
+++ b/test/schema.rb
@@ -21,7 +21,7 @@
t.integer :polytag_id
t.string :polytag_type
-
+
t.string :taggable_type
t.integer :taggable_id
end
@@ -32,4 +32,15 @@
t.integer :taggings_count, :default => 0
end
+ create_table :addresses, :force => true do |t|
+ t.string :city
+
+ t.integer :addressable_id
+ t.string :addressable_type
+ end
+
+ create_table :people, :force => true do |t|
+ t.string :type
+ end
+
end
diff --git a/test/test_configuration_options.rb b/test/test_configuration_options.rb
new file mode 100644
index 0000000..37eb365
--- /dev/null
+++ b/test/test_configuration_options.rb
@@ -0,0 +1,41 @@
+require 'helper'
+
+class TestClassVariables < StoreBaseSTIClass::TestCase
+
+ def setup
+ @old_store_sti_classes_for = ActiveRecord::Base.store_sti_classes_for
+ end
+
+ def teardown
+ ActiveRecord::Base.store_sti_classes_for = @old_store_sti_classes_for
+ end
+
+ def test_setting_store_base_sti_class
+ ActiveRecord::Base.store_base_sti_class = false
+ assert_equal :all, ActiveRecord::Base.store_sti_classes_for
+
+ ActiveRecord::Base.store_base_sti_class = true
+ assert_equal [], ActiveRecord::Base.store_sti_classes_for
+ end
+
+ def test_setting_store_sti_classes_for
+ assert_nothing_raised do
+ ActiveRecord::Base.store_sti_classes_for = ['Post']
+ end
+ assert_equal ['Post'], ActiveRecord::Base.store_sti_classes_for
+
+ assert_raises(ArgumentError) do
+ ActiveRecord::Base.store_sti_classes_for = ['SpecialPost']
+ end
+
+ assert_nothing_raised do
+ ActiveRecord::Base.store_sti_classes_for = []
+ end
+ assert_equal [], ActiveRecord::Base.store_sti_classes_for
+
+ assert_raises(ArgumentError) do
+ ActiveRecord::Base.store_sti_classes_for = :none
+ end
+ end
+
+end
diff --git a/test/test_store_base_sti_class.rb b/test/test_store_base_sti_class.rb
index 49cc417..3e4250b 100644
--- a/test/test_store_base_sti_class.rb
+++ b/test/test_store_base_sti_class.rb
@@ -3,18 +3,42 @@
class TestStoreBaseStiClass < StoreBaseSTIClass::TestCase
def setup
- @old_store_base_sti_class = ActiveRecord::Base.store_base_sti_class
- ActiveRecord::Base.store_base_sti_class = false
+ @old_store_sti_classes_for = ActiveRecord::Base.store_sti_classes_for
+ ActiveRecord::Base.store_sti_classes_for = :all
@thinking_post = SpecialPost.create(:title => 'Thinking', :body => "the body")
@misc_tag = Tag.create(:name => 'Misc')
end
def teardown
- ActiveRecord::Base.store_base_sti_class = @old_store_base_sti_class
+ ActiveRecord::Base.store_sti_classes_for = @old_store_sti_classes_for
end
- def test_polymorphic_belongs_to_assignment_with_inheritance
+ def test_polymorphic_belongs_to_assignment_with_inheritance_Person
+ # should store correct addressable_type when assigning a saved record
+ vendor = Vendor.create!
+ address = vendor.addresses.create!(city: 'Springfield')
+ assert_equal vendor.id, address.addressable_id
+ assert_equal "Vendor", address.addressable_type
+
+ person = Person.create!
+ address = person.addresses.create!(city: 'Springfield')
+ assert_equal person.id, address.addressable_id
+ assert_equal "Person", address.addressable_type
+
+ # should store correct addressable_type when assigning a new record
+ vendor = Vendor.create!
+ address = vendor.addresses.build(city: 'Springfield')
+ assert_equal vendor.id, address.addressable_id
+ assert_equal "Vendor", address.addressable_type
+
+ person = Person.create!
+ address = person.addresses.build(city: 'Springfield')
+ assert_equal person.id, address.addressable_id
+ assert_equal "Person", address.addressable_type
+ end
+
+ def test_polymorphic_belongs_to_assignment_with_inheritance_Post
# should update when assigning a saved record
tagging = Tagging.new
post = SpecialPost.create(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
@@ -112,7 +136,8 @@ def test_has_many_through_polymorphic_has_one
special_post = SpecialPost.create!(:title => 'IBM Watson' 's Jeopardy play', :author => author, :body => "the body")
special_tag = SpecialTag.create!(:name => 'SpecialGeneral')
- taggings = [post.taggings.create(:tag => special_tag), special_post.taggings.create(:tag => special_tag)]
+ taggings = [ post.taggings.create!(:tag => special_tag),
+ special_post.taggings.create!(:tag => special_tag)]
assert_equal taggings.sort_by(&:id), author.tagging.sort_by(&:id)
end
@@ -128,13 +153,31 @@ def test_has_many_polymorphic_with_source_type
assert_equal 2, tag.polytagged_posts.size
end
- def test_polymorphic_has_many_through_with_double_sti_on_join_model
+ def test_polymorphic_has_many_through_with_double_sti_on_join_model__storing_base_class
+ ActiveRecord::Base.store_base_sti_class = true
+
tag = SpecialTag.create!(:name => 'Special')
post = @thinking_post
tag.polytagged_posts << post
+ tag.reload
+
+ assert_equal 1, tag.polytaggings.size
+
+ tagging = tag.polytaggings.first
+
+ assert_equal 'Tag', tagging.polytag_type
+ assert_equal 'Post', tagging.taggable_type
+ assert_equal tag, tagging.polytag
+ assert_equal post, tagging.taggable
+ end
+ def test_polymorphic_has_many_through_with_double_sti_on_join_model
+ tag = SpecialTag.create!(:name => 'Special')
+ post = @thinking_post
+
+ tag.polytagged_posts << post
tag.reload
assert_equal 1, tag.polytaggings.size
@@ -165,5 +208,4 @@ def test_finder_sql_is_supported
assert_equal [author], special_tag.authors
end
end
-
end
diff --git a/test/test_storing_sti_class_for_Tag_only.rb b/test/test_storing_sti_class_for_Tag_only.rb
new file mode 100644
index 0000000..591edd9
--- /dev/null
+++ b/test/test_storing_sti_class_for_Tag_only.rb
@@ -0,0 +1,211 @@
+require 'helper'
+
+class TestStoringStiClassForTagOnly < StoreBaseSTIClass::TestCase
+
+ def setup
+ @old_store_sti_classes_for = ActiveRecord::Base.store_sti_classes_for
+ ActiveRecord::Base.store_sti_classes_for = ['Tag']
+
+ @thinking_post = SpecialPost.create(:title => 'Thinking', :body => "the body")
+ @misc_tag = Tag.create(:name => 'Misc')
+ end
+
+ def teardown
+ ActiveRecord::Base.store_sti_classes_for = @old_store_sti_classes_for
+ end
+
+ def test_polymorphic_belongs_to_assignment_with_inheritance_Person
+ # should store correct addressable_type when assigning a saved record
+ vendor = Vendor.create!
+ address = vendor.addresses.create!(city: 'Springfield')
+ assert_equal vendor.id, address.addressable_id
+ assert_equal "Person", address.addressable_type
+
+ person = Person.create!
+ address = person.addresses.create!(city: 'Springfield')
+ assert_equal person.id, address.addressable_id
+ assert_equal "Person", address.addressable_type
+
+ # should store correct addressable_type when assigning a new record
+ vendor = Vendor.create!
+ address = vendor.addresses.build(city: 'Springfield')
+ assert_equal vendor.id, address.addressable_id
+ assert_equal "Person", address.addressable_type
+
+ person = Person.create!
+ address = person.addresses.build(city: 'Springfield')
+ assert_equal person.id, address.addressable_id
+ assert_equal "Person", address.addressable_type
+ end
+
+ def test_polymorphic_belongs_to_assignment_with_inheritance_Post
+ # should store correct taggable_type when assigning a saved record
+ tagging = Tagging.new
+ post = SpecialPost.create(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tagging.taggable = post
+ assert_equal post.id, tagging.taggable_id
+ assert_equal "Post", tagging.taggable_type
+
+ # should store correct taggable_type when assigning a new record
+ tagging = Tagging.new
+ post = SpecialPost.new(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tagging.taggable = post
+ assert_nil tagging.taggable_id
+ assert_equal "Post", tagging.taggable_type
+ end
+
+ def test_polymorphic_has_many_create_model_with_inheritance
+ post = SpecialPost.new(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+
+ tagging = @misc_tag.taggings.create(:taggable => post)
+ assert_equal "Post", tagging.taggable_type
+
+ post.reload
+ assert_equal [tagging], post.taggings
+ end
+
+ def test_polymorphic_has_one_create_model_with_inheritance
+ post = SpecialPost.new(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+
+ tagging = @misc_tag.create_tagging(:taggable => post)
+ assert_equal "Post", tagging.taggable_type
+
+ post.reload
+ assert_equal tagging, post.tagging
+ end
+
+ def test_polymorphic_has_many_create_via_association
+ tag = SpecialTag.create!(:name => 'Special')
+ tagging = tag.polytaggings.create!
+
+ assert_equal "SpecialTag", tagging.polytag_type
+ end
+
+ def test_polymorphic_has_many_through_create_via_association
+ tag = SpecialTag.create!(:name => 'Special')
+ tag.polytagged_posts.create!(:title => 'To Be or Not To Be?', :body => "the body")
+
+ assert_equal "SpecialTag", tag.polytaggings.first.polytag_type
+ end
+
+ def test_include_polymorphic_has_one
+ post = SpecialPost.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tagging = post.create_tagging(:tag => @misc_tag)
+
+ post = Post.includes(:tagging).find(post.id)
+ assert_equal tagging, assert_no_queries { post.tagging }
+ end
+
+ def test_include_polymorphic_has_many
+ tag = SpecialTag.create!(:name => 'Special')
+ tag.polytagged_posts << SpecialPost.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tag.polytagged_posts << @thinking_post
+
+ tag = Tag.includes(:polytaggings).find(tag.id)
+ assert_equal 2, assert_no_queries { tag.polytaggings.size }
+ end
+
+ def test_include_polymorphic_has_many_through
+ tag = SpecialTag.create!(:name => 'Special')
+ tag.polytagged_posts << SpecialPost.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tag.polytagged_posts << @thinking_post
+
+ tag = Tag.includes(:polytagged_posts).find(tag.id)
+ assert_equal 2, assert_no_queries { tag.polytagged_posts.size }
+ end
+
+ def test_join_polymorhic_has_many
+ tag = SpecialTag.create!(:name => 'Special')
+ tag.polytagged_posts << SpecialPost.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tag.polytagged_posts << @thinking_post
+
+ assert Tag.joins(:polytaggings).where('taggings.id' => tag.polytaggings.first.id, id: tag.id)
+ end
+
+ def test_join_polymorhic_has_many_through
+ tag = SpecialTag.create!(:name => 'Special')
+ tag.polytagged_posts << SpecialPost.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tag.polytagged_posts << @thinking_post
+
+ assert Tag.joins(:polytagged_posts).where('posts.id' => tag.polytaggings.first.taggable_id, id: tag.id)
+ end
+
+ def test_has_many_through_polymorphic_has_one
+ author = Author.create!(:name => 'Bob')
+ post = Post.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :author => author, :body => "the body")
+ special_post = SpecialPost.create!(:title => 'IBM Watson' 's Jeopardy play', :author => author, :body => "the body")
+ special_tag = SpecialTag.create!(:name => 'SpecialGeneral')
+
+ taggings = [ post.taggings.create!(:tag => special_tag),
+ special_post.taggings.create!(:tag => special_tag)]
+ assert_equal taggings.sort_by(&:id), author.tagging.sort_by(&:id)
+ end
+
+ def test_has_many_polymorphic_with_source_type
+ tag = SpecialTag.create!(:name => 'Special')
+ tag.polytagged_posts << SpecialPost.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :body => "the body")
+ tag.polytagged_posts << @thinking_post
+
+ tag.save!
+ tag.reload
+
+ tag = Tag.find(tag.id)
+ assert_equal 2, tag.polytagged_posts.size
+ end
+
+ def test_polymorphic_has_many_through_with_double_sti_on_join_model__storing_base_class
+ ActiveRecord::Base.store_base_sti_class = true
+
+ tag = SpecialTag.create!(:name => 'Special')
+ post = @thinking_post
+
+ tag.polytagged_posts << post
+ tag.reload
+
+ assert_equal 1, tag.polytaggings.size
+
+ tagging = tag.polytaggings.first
+
+ assert_equal 'Tag', tagging.polytag_type
+ assert_equal 'Post', tagging.taggable_type
+
+ assert_equal tag, tagging.polytag
+ assert_equal post, tagging.taggable
+ end
+
+ def test_polymorphic_has_many_through_with_double_sti_on_join_model
+ tag = SpecialTag.create!(:name => 'Special')
+ post = @thinking_post
+
+ tag.polytagged_posts << post
+ tag.reload
+
+ assert_equal 1, tag.polytaggings.size
+
+ tagging = tag.polytaggings.first
+
+ assert_equal 'SpecialTag', tagging.polytag_type
+ assert_equal 'Post', tagging.taggable_type
+
+ assert_equal tag, tagging.polytag
+ assert_equal post, tagging.taggable
+ end
+
+ def test_join_association
+ tag = SpecialTag.create!(:name => 'Special')
+ tag.polytaggings << Tagging.new
+
+ assert SpecialTag.joins(:polytaggings).where(id: tag.id).first
+ end
+
+ if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.1.0')
+ def test_finder_sql_is_supported
+ author = Author.create!(:name => 'Bob')
+ post = Post.create!(:title => 'Budget Forecasts Bigger 2011 Deficit', :author => author, :body => "the body")
+ special_tag = Tag.create!(:name => 'SpecialGeneral')
+ post.taggings.create(:tag => special_tag)
+
+ assert_equal [author], special_tag.authors
+ end
+ end
+end