Цепочка ответственности и проблемы alias_method в Ruby

Я пытаюсь реализовать шаблон цепочки ответственности в Ruby и ActiveRecord для полиморфного объекта. У меня есть несколько проблем.

  • Иногда я получаю сообщение об ошибке, что метод не определен, когда я пытаюсь использовать alias_method, я думаю, это потому, что класс не загружен или что-то в этом роде, поэтому я явно делаю отправку, чтобы получить метод
  • Я получаю кучу бесконечных цепочек, где функция с псевдонимом (original_method) вызывает метод, который вызывает original_method. Мне интересно, потому ли это, что когда вы используете псевдоним для метода, который уже был перезаписан, вы, по сути, делаете "original_method" копию метода с псевдонимом.
  • В настоящее время я работаю над этим, имея такую ​​​​функцию, как «цепочка», возвращающую подкласс Setting со всеми определенными методами, но любопытно, почему было так много проблем с alias_method прямо в классе.

Вот пример:

class Hospital
  has_one :setting, :as => :settable
  belongs_to :administrative_area

  def next_link
    adminstrative_area
  end

  def usable_setting
    setting ? setting : next_link.usable_setting
  end
end

Затем у меня есть объект Setting:

class Setting < ActiveRecord::Base

belongs_to :settable, :polymorphic => true

def chained
  %w(api_key active_days).each do |method|

    # this is here because otherwise the method isn't defined,
    # it's almost as while it's going up, the metaclass doesn't have the columns
    # until it loads, probably will be fixed if we cache classes
    self.send method.to_sym

    (class << self; self; end).class_eval do

      define_method method do |*args|
        alias_method "original_#{method}", method
        my_setting = send("original_#{method}")
        if my_setting.nil? or my_setting.empty?
          settable.next_link.usable_setting.chained.send(method)
        else
          return my_setting
        end
      end
    end
  end
  self
end
end

person David    schedule 30.06.2010    source источник


Ответы (1)


Вы, кажется, слишком усложняете. Похоже, вы пытаетесь узнать, существуют ли api_key и active_days, а если нет, то получить их откуда-то еще.

Вот правильный способ сделать это, предполагая, что api_key и active_days являются столбцами в вашей таблице:

class Setting < ActiveRecord::Base

  belongs_to :settable, :polymorphic => true

  def api_key
    super || settable.next_link.usable_setting.api_key
  end

  def active_days
    super || settable.next_link.usable_setting.active_days
  end
end

Вы можете немного изменить его, чтобы сохранить ясность и удалить дублирование.

class Setting < ActiveRecord::Base

  belongs_to :settable, :polymorphic => true

  def api_key
    super || next_usable_setting.api_key
  end

  def active_days
    super || next_usable_setting.active_days
  end

  private
  def next_usable_setting
    settable.next_link.usable_setting
  end
end

Так что в этом случае обратите внимание — если у вас есть доступные api_key/active_days, они будут возвращены. В противном случае он выберет usable_setting из next_link. Если у него есть ключ api_key/active_days, он будет возвращен, в противном случае он получит usable_setting из next_link. И Т. Д.

person Max Chernyak    schedule 08.07.2010
comment
Эй, этот ответ отличный, но я думаю, что основная проблема, с которой я столкнулся, заключалась в метамагическом определении методов. Причина, по которой я хочу сделать это таким образом, заключается в том, что у меня есть много связанных функций, и как программист предпочел бы потратить 10 часов на выяснение того, как избежать ввода, а не 2 часа, просто набирая все это. В итоге я сделал это с помощью создание отдельного класса, представляющего цепочку, я думаю, что это было сложно сделать в activerecord из-за того, как он создает свои собственные функции с псевдонимами. - person David; 08.07.2010
comment
Если вы хотите сделать это волшебным образом, все, что вам нужно сделать, это: [:api_key, :active_days].each{|meth| define_method(meth){super || next_usable_setting.send(meth)}}. Но, честно говоря, я бы предпочел определить их отдельно, потому что так приятнее читается. - person Max Chernyak; 09.07.2010