Rails 2.3 STI возвращает все дочерние классы - PullRequest
2 голосов
/ 18 сентября 2011

Я использую стандартную 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

UPDATE

Благодаря @nash и @jdeseno просто нужно добавить следующий инициализатор, используя метод @jdeseno:

%w[parent child1 child2 child3].each do |c|
  require_dependency File.join("app","models","#{c}.rb")
end

Ответы [ 3 ]

3 голосов
/ 18 сентября 2011

Вы можете добавить метод потомков, подключив 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 голос
/ 31 августа 2012

На самом деле, пользовательская реализация отслеживания подклассов не является необходимой даже в Rails 2.3. Уже существует метко названный метод «подклассы», внедренный в класс ActiveSupport, который возвращает их для вас в лексически отсортированном порядке. Таким образом, вы могли бы написать

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"]

Необходимость предварительной загрузки дочерних классов ИППП в среду разработки по-прежнему актуальна. Слава Алексею Райснеру за подсказку в его блоге .

1 голос
/ 18 сентября 2011

Когда вы вызываете ваш метод 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"] 
...