我正在使用标准STI,并希望在表单上创建一个输入选择,其选项都是父类的子类型。所以我希望Parent.select_options返回['Child1','Child2','Child3']
class Parent < ActiveRecord::Base
# kinda what I'd like except the descendants method is undefined in rails 2.3
def self.select_options
descendants.map{ |c| c.to_s }.sort
end
end
class Child1 < Parent
end
class Child2 < Parent
end
class Child3 < Parent
end
view.html.haml
= f.input :parent_id, :as => :select, :collection => Parent.select_options, :prompt => true
更新
感谢@nash和@jdeseno,只需要使用@jdeseno方法添加以下初始化程序:
%w[parent child1 child2 child3].each do |c|
require_dependency File.join("app","models","#{c}.rb")
end
答案 0 :(得分:3)
您可以通过挂钩Class.inherited
:
class Parent
@@descendants = []
def self.inherited(klass)
@@descendants << klass
end
def descendants
@@descendants
end
end
class A < Parent; end
class B < Parent; end
class C < Parent; end
例如:
irb> Parent.new.descendants
[A, B, C]
答案 1 :(得分:1)
当您调用Parent.select_options
方法时,您的子模型可能尚未加载。所以,你可以添加这样的东西:
class Parent < ActiveRecord::Base
Dir[File.join(File.dirname(__FILE__), "*.rb")].each do |f|
Parent.const_get(File.basename(f, '.rb').classify)
end
end
在Parent
模型中。现在您可以使用您的方法:
ruby-1.9.2-p290 :010 > Parent.descendants.map {|c| c.to_s}.sort
=> ["Child1", "Child2", "Child3"]
答案 2 :(得分:1)
实际上,即使在Rails 2.3中,自定义实现子类跟踪也不是必需的。已经存在一个由ActiveSupport注入到Class中的恰当命名的方法“子类”,它以词法排序顺序为您返回它们。所以你可以写
class Parent < ActiveRecord::Base
def self.select_options
subclasses.map{ |c| c.to_s }
end
end
class Child3 < Parent
end
class Child1 < Parent
end
class Child2 < Parent
end
或者您可以使用与在那里使用相同的技巧并使用
class Parent < ActiveRecord::Base
def self.select_options
Object.subclasses_of(self).map{ |c| c.to_s }.sort
end
end
在Rails 2.3.14(Ruby 1.8.7-p352)中验证了这一点,并在两种情况下得到了预期的结果:
>> Parent.select_options
=> ["Child1", "Child2", "Child3"]
在开发环境中预加载STI子类的必要性仍然适用。感谢Alex Reisner在blog中的提示。