Переопределить помощников рельсов с доступом к оригиналу

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

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. Исправление Monkey: повторное открытие вспомогательного модуля rails. Если команда rails когда-нибудь изменит название своего вспомогательного модуля, мой код станет источником нестабильности. Не непреодолимый, но не идеальный.
  2. Используйте разные имена методов: Попытка придерживаться интерфейса Common Rail может стать моей ошибкой. Мои изменения могут стать источником путаницы для других разработчиков
  3. Методы отсоединения (новые): не уверен, сработает ли это или будут ли у него те же недостатки, что и в 1. Изучу это, но это может быть хорошей отправной точкой.

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

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


person user208769    schedule 06.05.2012    source источник


Ответы (4)


Есть лучший способ, чем любой из перечисленных вами вариантов. Просто используйте 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 будет перемещать вещи на вас!

person Cade    schedule 09.05.2012
comment
Знаешь что... это настолько очевидно, что я пытаюсь понять, почему я думал, что это не сработает! Я попробую это сегодня вечером, и, если это сработает, у меня будут серьезные слова с моим мозгом, вероятно, с кирпичной стеной. После, конечно, принятия вашего ответа... :D - person user208769; 10.05.2012
comment
@ user208769 Хе-хе-хе. Это потрясающе. Насколько я понимаю, переопределение методов таким образом, как правило, предпочтительнее при любых обстоятельствах. Class#ancestors может быть очень полезным в выяснить хорошее место в пути поиска метода для перехвата диспетчеризации метода (или где ваш пользовательский модуль с переопределением должен быть включен для достижения наилучшего эффекта). - person Cade; 10.05.2012
comment
Какие? :) Вы должно быть шутите! Это ОГРОМНАЯ ошибка! По-вашему, вы должны включить своего помощника в КАЖДЫЙ класс, который включает AssetTagHelper. Время летит незаметно, и вы или кто-то другой можете забыть о необходимости включения вашего патча. Вы просто включаете AssetTagHelper и начинаете задаваться вопросом: почему мой сайт теперь выглядит иначе? Хорошо, когда ты и патчмейкер - одно и то же лицо. Но что, если нет? - person jdoe; 16.05.2012
comment
@jdoe Да, вы правы. Чтобы использовать свой код, его необходимо включить. :) Я не считаю это ошибкой. Использование AbstractController::Helpers::ClassMethods::helper в ApplicationController может помочь смягчить ваше беспокойство. - person Cade; 16.05.2012

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

Допустим, вы хотите регистрировать вызовы помощника 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.

person jdoe    schedule 06.05.2012
comment
Да, именно этот подход я имел в виду, говоря, что обезьяны исправляют определенные модули rails — это работает хорошо, но если ядро ​​​​rails меняет имена своих модулей, мой код ломается. Это может не иметь большого значения, но мне любопытно посмотреть, есть ли другие решения. Тем не менее, я забыл об alias_method_chain, спасибо, что напомнили мне об этом! - person user208769; 08.05.2012
comment
PS: обновили вопрос, чтобы удалить пример драгоценного камня. Надеюсь, этот макет менее запутанный! Спасибо. - person user208769; 08.05.2012
comment
Риск есть всегда! Если вы беспокоитесь о alias_method_chain, то вам не стоит: он существует с версии 1.4.0 (2007 год). Если вы беспокоитесь о других частях вашей программы, обеспечьте достойное покрытие тестами. - person jdoe; 08.05.2012
comment
Устарело. stackoverflow.com/questions/3689736 / - person Vadorequest; 14.05.2014

Попробуйте использовать 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
person gmile    schedule 14.05.2012

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

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

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

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

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