Rails 3: alias_method_chain все еще используется?

Я только что читал о разработке Gems/Plugin для Rails 3 и наткнулся на этот пост, в котором говорится, что alias_method_chain больше не используется. Я вижу, что метод все еще существует в activesupport-3.0.0/lib/active_support/core_ext/module/aliasing.rb.

Должен ли я по-прежнему использовать alias_method_chain в Rails 3?

Это все еще отражает лучшие практики для гемов / плагинов в Rails 3, которые хотят изменить ActiveRecord?

3 ответа

Решение

Нет, это было заменено умным использованием переопределения метода в модулях и super ключевое слово.

По сути, вы определяете исходную функцию во включенном модуле и переопределяете ее в другом включенном модуле. Когда вы звоните super в переопределяющей функции он вызывает исходную функцию. Но есть одна загвоздка. Вы должны включить расширяющие модули после включения базового модуля и в том порядке, в котором вы хотите, чтобы произошло связывание.

class Something
  module Base  
    def my_method
      # (A) original functionality
    end
  end

  module PreExtension
    def my_method
      # (B) before the original
      super # calls whatever was my_method before this definition was made
    end
  end

  module PostExtension
    def my_method
      super # calls whatever was my_method before this definition was made
      # (C) after the original
    end
  end

  include Base # this is needed to place the base methods in the inheritance stack
  include PreExtension # this will override the original my_method
  include PostExtension # this will override my_method defined in PreExtension
end

s = Something.new
s.my_method 
#=> this is a twice extended method call that will execute code in this order:
#=> (B) before the original
#=> (A) the original
#=> (C) after the original

Райан Бейтс из Railscasts говорит о том, как это используется в коде Rails Routing. Я бы порекомендовал посмотреть его и другие его скринкасты. Они могут превратить вязаную бабушку в гуру Рейлса.

PS: Peeja идет в Peeja за исправление фундаментальной ошибки в моем первоначальном ответе. Благодарю.

В общем, модуль никогда не может переопределить метод в классе, в который он включен. Это потому, что включение модуля работает так же, как и создание подклассов. Суперкласс не может переопределить методы своих подклассов, и вы не ожидаете этого.

Когда модуль включен в класс, он вставляется сразу после класса в цепочке предков класса. призваниеsuper из класса вызовет реализацию модуля.

class Something
  module PreExtension; end
  module PostExtension; end

  include PreExtension
  include PostExtension
end

Something.ancestors # => [Something, Something::PostExtension, Something::PreExtension, Object, Kernel]

Всякий раз, когда метод вызывается на SomethingRuby просматривает этот список по порядку и вызывает первую найденную реализацию. Если реализация вызывает super, он продолжает искать и находит следующий.

Это означает, что модули, включенные позже, имеют приоритет над модулями, включенными ранее, и могут вызывать super чтобы получить более ранние реализации модулей. Это связано с тем, что включенные модули вставляются в цепочку предков непосредственно после класса. Вот как работает упомянутый код маршрутизации edgerunner. Этот код помещает все в модули, например:

class SomethingNew
  module Base
    def my_method
      puts "(A)"
    end
  end

  module Extension
    def my_method
      puts "(B)"
      super
    end
  end

  include Base
  include Extension
end

SomethingNew.new.my_method
# Output:
# >> (B)
# >> (A)

SomethingNew.ancestors # => [SomethingNew, SomethingNew::Extension, SomethingNew::Base, Object, Kernel]

Вот почему alias_method_chain существовал в первую очередь. Если поместить базовый код в модуль не вариант, я не уверен, как сделать эквивалент alias_method_chain,

Я вижу это alias_method_chain больше не присутствует в Rails 3.0.0. http://api.rubyonrails.org/ не сообщает об этом и rails console сообщает об этом undefined local variable or method,

Смотрите также - https://rails.lighthouseapp.com/projects/8994/tickets/285-alias_method_chain-limits-extensibility

ОБНОВЛЕНИЕ: Как отмечено @ecoologic в комментариях, alias_method_chain все еще присутствует в Rails 3.1.1.

Другие вопросы по тегам