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?
Является этим, по-прежнему отражающим лучшие практики для gems/plugins в Rails 3, которые хотят изменить ActiveRecord?
Ответы
Ответ 1
Нет, он был заменен умным использованием переопределения метода в модулях и ключевым словом 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 code. Я бы рекомендовал посмотреть его и другие его скринкасты. Они имеют право превращать вязальную бабушку в гуру Rails.
PS: Кредит отправляется Peeja для исправления фундаментальной ошибки в моем первоначальном ответе. Спасибо.
Ответ 2
В общем, модуль никогда не может переопределить метод в классе
он включен. Это связано с тем, что включение модуля работает просто
как подклассы. Суперкласс не может переопределять его подклассы,
методы, и вы не ожидали этого.
Когда модуль включен в класс, модуль вставлен
сразу после класса в классе предков. призвание
super
из класса вызовет реализацию модуля.
class Something
module PreExtension; end
module PostExtension; end
include PreExtension
include PostExtension
end
Something.ancestors # => [Something, Something::PostExtension, Something::PreExtension, Object, Kernel]
Всякий раз, когда метод вызывается на Something
, Ruby просматривает
этот список по порядку и вызывает первую найденную им реализацию.
Если реализация вызывает 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
.
Ответ 3
Я вижу, что 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#ticket-285-20
ОБНОВЛЕНИЕ: Как отмечено @ecoologic в комментариях, alias_method_chain
все еще присутствует в Rails 3.1.1.