2

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

视图.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
4

3 回答 3

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]
于 2011-09-18T07:07:03.767 回答
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 在他的博客中提供的提示。

于 2012-08-31T15:17:54.177 回答
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"] 
于 2011-09-18T08:02:00.517 回答