我有几个继承了STI的类,用一种优雅的方式替换inheritance_column
:
module A
class Base < ActiveRecord::Base
self.inheritance_column = 'provider'
PROVIDERS = {
'a_b' => 'B',
'a_c' => 'C'
}
# STI helpers
def self.find_sti_class(type_name)
"A::#{PROVIDERS[type_name]}".constantize or super(type_name)
end
def self.sti_name
PROVIDERS.invert[name.split("::")[-1]]
end
# end STI helpers
end # class Base
end # module A
# b.rb
module A
class B < A; end
end
# c.rb
module A
class C < A; end
end
整个过程适用于现有对象,但在我尝试创建新对象时会出现问题:
A::Base.new(provider: 'a_b')
给了我
ActiveRecord::SubclassNotFound: Invalid single-table inheritance type: a_b is not a subclass of A::Base
from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/inheritance.rb:215:in `subclass_from_attributes'
from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/inheritance.rb:55:in `new'
from (irb):16
from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/console.rb:110:in `start'
from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/console.rb:9:in `start'
from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/commands_tasks.rb:68:in `console'
from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
可以清楚地看到,find_sti_class
来电时未调出new
。我还在我的self.find_sti_class
中插入了一些调试输出,但没有调用它。
好的,使用来源,Luke。 转到apidock并查看:
# File activerecord/lib/active_record/inheritance.rb, line 208
def subclass_from_attributes(attrs)
subclass_name = attrs.with_indifferent_access[inheritance_column]
if subclass_name.present? && subclass_name != self.name
subclass = subclass_name.safe_constantize
unless descendants.include?(subclass)
raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
end
subclass
end
end
因此,无论我们是否重新定义了sti_name
,它仍然希望attributes[inheritance_column]
的值与类名匹配!
似乎在Rails 5中修复了(正如我在github上看到的那样):它调用find_sti_class
,并且应该可以正常工作。
但在rails 4.2中,它似乎仍然令人困惑。
我误读了某些东西,或者是真的,在这种情况下,我可以创建一个继承类的对象的唯一方法是通过直接从这个类调用#new
来创建它,如下所示:
A::B.new
?
答案 0 :(得分:0)
你可以......
def self.new(options={})
options[:provider] = find_sti_class(options[:provider]) if options[:provider]
super(options)
end