Rails 2.3 STI返回所有子类

时间:2011-09-18 06:13:32

标签: ruby-on-rails sti

我正在使用标准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

3 个答案:

答案 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中的提示。