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]
Всякий раз, когда метод вызывается на 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
,
Я вижу это 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.