2012-05-06 1 views
19

Я хочу использовать знакомых помощников рельсов, но с немного измененными функциональными возможностями. Как я вижу это, я хочу, чтобы иметь возможность сделать что-то вроде:Замена помощников рельсов с доступом к оригиналу

module AwesomeHelper 
    #... create alias of stylesheet_link_tag to old_stylesheet_link_tag 
    def stylesheet_link_tag(*args) 
    if @be_awesome 
     awesome_stylesheet_link_tag *args 
    else 
     old_stylesheet_link_tag *args 
    end 
    end 
end 

Я вижу это, у меня есть три варианта:

  1. Обезьяна зашивка: Переоткрытия рельсов вспомогательного модуля , Если команда rails когда-либо изменяет имя своего вспомогательного модуля, мой код становится источником хрупкости. Не непреодолимый, но не идеальный.
  2. Использовать разные имена методов: Попытка придерживаться общего интерфейса рельсов может быть моим падением. Мое изменение может стать источником путаницы для других разработчиков
  3. Способы удаления (новый): Не уверен, будет ли это работать, или будут ли те же недостатки, что и 1. Будет ли это исследовать, но это может быть хорошим отправная точка.

Итак, вопрос в том, что я застрял в одном из этих субоптимальных решений или есть другой способ, который я не рассматривал? Если я перейду на вариант 3, есть ли способ сделать это без прямого обращения к вспомогательному модулю rails?

(Примечание:. Я удалил контекст, так как он ничего не добавляет к вопросу)

ответ

30

Существует лучший способ, чем любой из перечисленных вами вариантов. Просто используйте super:

module AwesomeHelper 
    def stylesheet_link_tag(*sources) 
    if @be_awesome 
     awesome_stylesheet_link_tag *sources 
    else 
     super 
    end 
    end 
end 

Переопределение stylesheet_link_tag в AwesomeHelper будет гарантировать, что, когда stylesheet_link_tag получает вызывается, Ruby будет столкнуться с ним в пути метод поиска, прежде чем он попадает ActionView::Helpers::AssetTagHelper. Если @be_awesome - true, вы можете взять на себя ответственность и прекратить все прямо там, а если нет, вызов super без круглых скобок будет прозрачно проходить через все аргументы до реализации Rails. Таким образом, вам не нужно беспокоиться о том, что основная команда Rails перемещает вас на вас!

+0

Вы знаете, что ... это так безумно очевидно, я пытаюсь ломать себе голову, чтобы понять, почему Я думал, что это не сработает! Я собираюсь попробовать это сегодня вечером, и если это сработает, у меня будут серьезные слова с моим мозгом, возможно, с кирпичной стеной. После, конечно, принимая ваш ответ ...: D – user208769

+1

@ user208769 Hehehe. Это потрясающе. Насколько я понимаю, переопределяющие методы таким образом, как правило, предпочтительнее при любых обстоятельствах. [Предки класса #] (http://ruby-doc.org/core-1.9.3/Module.html#method-i-ancestors) могут быть очень полезны при поиске хорошего места в пути поиска метода для метода захвата диспетчеризация (или когда ваш настраиваемый модуль с переопределением должен быть включен для лучшего эффекта). – Cade

+0

Что? :) Вы должно быть шутите! Это ОГРОМНАЯ! Ваш способ включить вас в помощника в КАЖДОМ классе, который включает AssetTagHelper. Время летит, и вы или кто-то еще можете забыть о необходимости включения патча. Вы просто включаете AssetTagHelper и начинаете удивляться: почему мой сайт выглядит по-другому? Это хорошо, когда вы и разработчик патчей - это один и тот же человек. Но что, если нет? – jdoe

6

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

Предположим, вы хотите регистрировать звонки на link_to помощник (да, надуманный пример, но показывает идею). Глядя в API, вы понимаете, что link_to находится внутри модуля ActionView::Helpers::UrlHelper. Таким образом, вы создаете какой-то файл в, скажем, config/initializers каталоге со следующим содержимым:

# like in config/initializers/link_to_log.rb 
module ActionView::Helpers::UrlHelper 

    def link_to_with_log(*args, &block) 
     logger.info '**** LINK_TO CALL ***' 
     link_to_without_log(*args, &block) # calling the original helper 
    end 

    alias_method_chain :link_to, :log 
end 

Ядром этой функции - alias_method_chain (интерактивное). Используйте его ПОСЛЕ определения метода xxx_with_feature.

+0

Да, этот подход был то, что я имел в виду «обезьяна латания конкретных рельсов модулей» - который работает хорошо, но если рельсы ядро ​​меняет свои имена модулей, мой код брейки. Это не может быть большой проблемой, но мне любопытно узнать, есть ли другие решения. Тем не менее, забыл про alias_method_chain, спасибо, что напомнил мне об этом! – user208769

+0

P.S: Обновился вопрос, чтобы удалить пример драгоценного камня. Надеюсь, этот макет будет более запутанным! Спасибо. – user208769

+0

Риск всегда есть! Если вы беспокоитесь о 'alias_method_chain', то вы не должны: он существует с версии 1.4.0 (год 2007). Если вы беспокоитесь о других частях вашей программы, тогда обеспечьте достойное тестирование. – jdoe

2

Я бы очень хотел, чтобы вы рассмотрели свой вариант №2, переопределив поведение методов рельсов так, как это очевидно для вызывающего.

Ваш новый метод следует называть awesome_stylesheet_link_tag, чтобы другие разработчики Rails могли прочитать ваш код и задать вопрос «Что такого удивительного в теге ссылки?».

Как меньшее изменение вы можете сделать переопределение, но передать в :awesome => true в качестве аргумента, чтобы они, по крайней мере, имели представление о том, что что-то происходит.

Изменение поведения широко используемого метода, такого как stylesheet_link_tag, создает потенциальное будущее недоразумение, когда не требуется.

+0

Спасибо за ввод. Хотя обычно я согласен, в этом конкретном случае я думаю, что согласованность оправдана - я делаю это, чтобы использовать wicked_pdf и иметь точно такой же код, генерирующий PDF-файл или веб-страницу. Хотя wicked_pdf по умолчанию делает, как вы говорите (wicked_pdf_stylesheet_link_tag), для этого требуется слишком много повторений, и я думаю, что приемлемо ожидать, что функциональность может измениться, если вы создаете PDF-файл. Но вы делаете хороший момент, и некоторые полезные советы, так что спасибо. – user208769

4

Попробуйте использовать alias_method:

module AwesomeHelper 
    alias_method :original_stylesheet_link_tag, :stylesheet_link_tag 

    def stylesheet_link_tag(*sources) 
    if @be_awesome 
     awesome_stylesheet_link_tag *sources 
    else 
     original_stylesheet_link_tag *sources 
    end 
    end 
end