Подтвердить что ты не робот

Рекомендуемый способ использования помощников вида 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!

4b9b3361

Ответ 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.