aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYves Senn <yves.senn@gmail.com>2016-01-19 17:27:21 +0100
committerYves Senn <yves.senn@gmail.com>2016-01-19 17:31:16 +0100
commit3ea4476942d2ba5ddc0d3b2d1f3730455661b06a (patch)
tree4338c44719004fbd15a59783008de68b11321c20
parentf8234313feab83c65912b2d96a6dd8e0149b8642 (diff)
downloadrails-3ea4476942d2ba5ddc0d3b2d1f3730455661b06a.tar.gz
rails-3ea4476942d2ba5ddc0d3b2d1f3730455661b06a.tar.bz2
rails-3ea4476942d2ba5ddc0d3b2d1f3730455661b06a.zip
run `type` column through attribtues API type casting.
Closes #21986. This makes it possible to write custom types that define a different mapping for STI columns.
-rw-r--r--activerecord/CHANGELOG.md8
-rw-r--r--activerecord/lib/active_record/inheritance.rb1
-rw-r--r--activerecord/test/cases/inheritance_test.rb75
3 files changed, 84 insertions, 0 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 3d251f95cc..81027eab82 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,11 @@
+* Run `type` attributes through attributes API type-casting before
+ instantiating the corresponding subclass. This makes it possible to define
+ custom STI mappings.
+
+ Fixes #21986.
+
+ *Yves Senn*
+
* Add expression support on the schema default.
Example:
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 6259c4cd33..3a17f74b1d 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -167,6 +167,7 @@ module ActiveRecord
end
def find_sti_class(type_name)
+ type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
subclass = begin
if store_full_sti_class
ActiveSupport::Dependencies.constantize(type_name)
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 03bce547da..e97275a97b 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -7,6 +7,7 @@ require 'models/project'
require 'models/subscriber'
require 'models/vegetables'
require 'models/shop'
+require 'models/sponsor'
module InheritanceTestHelper
def with_store_full_sti_class(&block)
@@ -524,3 +525,77 @@ class InheritanceAttributeTest < ActiveRecord::TestCase
assert_instance_of Empire, empire
end
end
+
+class InheritanceAttributeMappingTest < ActiveRecord::TestCase
+ setup do
+ @old_registry = ActiveRecord::Type.registry
+ ActiveRecord::Type.registry = ActiveRecord::Type::AdapterSpecificRegistry.new
+ ActiveRecord::Type.register :omg_sti, InheritanceAttributeMappingTest::OmgStiType
+ Company.delete_all
+ Sponsor.delete_all
+ end
+
+ teardown do
+ ActiveRecord::Type.registry = @old_registry
+ end
+
+ class OmgStiType < ActiveRecord::Type::String
+ def cast_value(value)
+ if value =~ /\Aomg_(.+)\z/
+ $1.classify
+ else
+ value
+ end
+ end
+
+ def serialize(value)
+ if value
+ "omg_%s" % value.underscore
+ end
+ end
+ end
+
+ class Company < ActiveRecord::Base
+ self.table_name = 'companies'
+ attribute :type, :omg_sti
+ end
+
+ class Startup < Company; end
+ class Empire < Company; end
+
+ class Sponsor < ActiveRecord::Base
+ self.table_name = 'sponsors'
+ attribute :sponsorable_type, :omg_sti
+
+ belongs_to :sponsorable, polymorphic: true
+ end
+
+ def test_sti_with_custom_type
+ Startup.create! name: 'a Startup'
+ Empire.create! name: 'an Empire'
+
+ assert_equal [["a Startup", "omg_inheritance_attribute_mapping_test/startup"],
+ ["an Empire", "omg_inheritance_attribute_mapping_test/empire"]], ActiveRecord::Base.connection.select_rows('SELECT name, type FROM companies')
+ assert_equal [["a Startup", "InheritanceAttributeMappingTest::Startup"],
+ ["an Empire", "InheritanceAttributeMappingTest::Empire"]], Company.all.map { |a| [a.name, a.type] }
+
+ startup = Startup.first
+ startup.becomes! Empire
+ startup.save!
+
+ assert_equal [["a Startup", "omg_inheritance_attribute_mapping_test/empire"],
+ ["an Empire", "omg_inheritance_attribute_mapping_test/empire"]], ActiveRecord::Base.connection.select_rows('SELECT name, type FROM companies')
+ assert_equal [["a Startup", "InheritanceAttributeMappingTest::Empire"],
+ ["an Empire", "InheritanceAttributeMappingTest::Empire"]], Company.all.map { |a| [a.name, a.type] }
+ end
+
+ def test_polymorphic_associations_custom_type
+ startup = Startup.create! name: 'a Startup'
+ sponsor = Sponsor.create! sponsorable: startup
+
+ assert_equal ["omg_inheritance_attribute_mapping_test/company"], ActiveRecord::Base.connection.select_values('SELECT sponsorable_type FROM sponsors')
+
+ sponsor = Sponsor.first
+ assert_equal startup, sponsor.sponsorable
+ end
+end