diff options
author | Yves Senn <yves.senn@gmail.com> | 2016-01-19 17:27:21 +0100 |
---|---|---|
committer | Yves Senn <yves.senn@gmail.com> | 2016-01-19 17:31:16 +0100 |
commit | 3ea4476942d2ba5ddc0d3b2d1f3730455661b06a (patch) | |
tree | 4338c44719004fbd15a59783008de68b11321c20 | |
parent | f8234313feab83c65912b2d96a6dd8e0149b8642 (diff) | |
download | rails-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.md | 8 | ||||
-rw-r--r-- | activerecord/lib/active_record/inheritance.rb | 1 | ||||
-rw-r--r-- | activerecord/test/cases/inheritance_test.rb | 75 |
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 |