Рекомендуемый способ использования помощников вида Rails в классе презентации
Я изучал "рекомендуемый" способ использования помощников вида Rails (например, link_to
, content_tag
) в обычном классе ruby, например, для презентатора. Кажется, на этом фронте очень мало информации, и я хотел получить представление о том, что думает сообщество Stack.
Итак, у нас есть варианты. (Заметьте, я использую Rails 4 и меньше беспокоюсь о более старых версиях)
Включить необходимые модули вручную
Это, пожалуй, самый чистый способ, поскольку включены только необходимые помощники. Однако я обнаружил, что этот метод не работает в некоторых случаях, поскольку обычный контекст представления, предоставляемый в простых помощниках Rails, настроен для текущего запроса. url_for
не будет знать о текущем запросе, например, хост может не совпадать.
class MyPresenter
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::CaptureHelper
def wrapped_link
content_tag :div, link_to('My link', root_url)
end
end
Использовать ActionController::Base.helpers
Так как Rails 3, ActionController::Base
включил метод helpers
для доступа к текущему контексту представления. Я считаю, что контекст представления, предоставляемый этим методом, настроен так, как это было бы в помощнике rails, но я мог ошибаться. Там нет никакой документации об этом, которая кажется тревожной, но на практике она работает довольно хорошо.
class MyPresenter
def wrapped_link
h.content_tag :div, h.link_to('My link', h.root_url)
end
protected
def h
ActionController::Base.helpers
end
end
Я считаю, что этот контекст представления также можно смешивать с include
, но у рельсов видят, что помощники имеют сотни методов, и он чувствует себя грязным, чтобы включить их все без разбора.
Ввод контекста представления при вызове ведущего
Наконец, мы могли бы просто передать контекст представления классу при его инициализации (или, альтернативно, методом render
)
class MyPresenter
attr_accessor :context
alias_method :h, :context
def initialize(context)
@context = context
end
def wrapped_link
h.content_tag :div, h.link_to('My link', h.root_url)
end
end
class MyController < ApplicationController
def show
# WARNING - `view_context` in a controller creates an object
@presenter = MyPresenter.new(view_context)
end
end
Лично я склоняюсь к двум последним вариантам, но без окончательного ответа команды Rails (которую я смог найти) я чувствовал себя немного неуверенным. Кто лучше спросить, чем Stack!
Ответы
Ответ 1
Я бы пошел со смесью второго и третьего вариантов, что-то вроде:
class MyPresenter
def initialize(helpers)
@h = helpers
end
def wrapped_link
h.content_tag :div, h.link_to('My link', h.root_url)
end
private
attr_reader :h
end
Второй вариант требует, чтобы все ваши модульные тесты были пронумерованы как ActionController::Base.helpers
, что, возможно, не является хорошим вариантом, и ваш третий вариант вы используете огромный контекст для доступа только к некоторым методам.
Ответ 2
Я бы действительно зависеть от того, какие методы вы используете. Если это просто основы, такие как content_tag
и т.д., Я бы пошел на путь ActionController::Base.helpers
. Также можно вызвать некоторых помощников непосредственно, например. для путей внутри моделей я почти всегда использую что-то вдоль линий Rails.application.routes.url_helpers.comment_path
.
Для элементов, специфичных для контроллера, третий вариант может быть полезен, но лично "чистый" способ кажется более приятным. У Draper тоже интересный подход: они сохраняют view_context
для текущего запроса и затем делегируют вызовы h
-helpers к нему: https://github.com/drapergem/draper/blob/master/lib/draper/view_context.rb
Это действительно вопрос предпочтения. Я бы никогда не включил всех помощников сразу, как вы уже сказали. Но второй вариант довольно хорош, если вы хотите самостоятельно создать слой презентации, не используя драгоценный камень, например Draper или Cells.