используйте ActiveSupport `` Delegate to: '' to DRY методы экземпляров презентаторов - PullRequest
0 голосов
/ 17 апреля 2020

Я открываю шаблон презентатора (или декоратора) благодаря учебнику Райана Бейтса и внедряю его в учебный проект.

Мне интересно, есть ли способ использовать ActiveSupport delegate методы между пользовательскими объектами? После рефакторинга моей первой модели (Product) я хотел бы использовать некоторые ProductPresenter методы экземпляра внутри CartPresenter экземпляра. Если нет, может, мне следует использовать проблемы докладчика?

В настоящее время я создаю экземпляры презентаторов внутри представлений и обращаюсь к методам помощников, перенаправляя отсутствующие методы в шаблон, но, возможно, мне нужно создать экземпляры презентаторов внутри контроллеров (чтобы доступ к обоим CartPresenter & ProductPresenter) и определение метода получения для шаблона (чтобы он не запутывал метод method_missing)?


РЕДАКТИРОВАТЬ

Благодаря ответу jvillian, :product_presenter теперь относится к экземпляру ProductPresenter.

Поскольку у меня могут возникнуть другие ситуации, когда мне нужно делегировать методы докладчиков, я добавил :delegated_presenter к своим BasePresenter

Class BasePresenter
  def initialize(object, template)
    @object = object
    @template = template
  end

  def self.delegated_presenter(name)
    define_method("#{name}_presenter") do
      klass = "#{name.capitalize}Presenter".constantize
      @delegator ||= klass.new(@object.send(name), @template)
    end
  end
end

Теперь внутри подклассов докладчика:

class CartPresenter < BasePresenter
  delegated_presenter :product
  delegate :product_presenter_instance_method, to: :product_presenter
end

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

Вот как он используется внутри представления:

<% present product do |product_presenter| %>
  <div class="card" style="width: 14rem;">
    <%= product_presenter.display_card_image %>
    <div class="card-body">
      <%= product_presenter.display_link_to_product_name(class: 'card-title text-dark') %>
      <%= product_presenter.display_link_to_product_supplier(class: 'small text-right') %>
      <%= product_presenter.display_truncated_description(class: 'card-text') %>
      <%= render partial: 'product_buttons', locals: { product: product } %>
      <%= product_presenter.display_tags(class: 'badge badge-pill badge-secondary') %>
    </div>
  </div>
<% end %>

present вспомогательный метод, который возвращает объект презентатора.

1 Ответ

1 голос
/ 17 апреля 2020

Это:

delegate :my_instance_method, to: :product_presenter

... не работает, потому что :product_presenter является symbol, а не экземпляром ProductPresenter. Возможно, попробуйте что-то более похожее на:

class CartPresenter

  delegate :my_instance_method, to: product_presenter 

  def product_presenter
    @product_presenter ||= ProductPresenter.new 
  end

end

... и ...

class ProductPresenter

  def my_instance_method
    # do something
  end

end

Это утверждение:

Я сейчас создаю экземпляры докладчиков внутри views

... немного беспокоит меня, так как вы создаете тесную связь между представлением и докладчиком. Это более длинная топи c, но если бы я генерировал это представление, которое вы показываете в своем коде, оно выглядело бы примерно так:

<% @presenter = local_assigns[:presenter] if local_assigns[:presenter] %>

<div class="card" style="width: 14rem;">
  <%= @presenter.card_content %>
</div>

Тогда, естественно, любому докладчику, который вы передаете, используя locals, нужны реализовать card_content. Теперь ваш взгляд не знает ничего о presenter или его методах, выходящих за рамки одного этого метода card_content. Вы можете делать все, что захотите, в card_content и в будущем вносить изменения в product_presenter методы, не беспокоясь об обновлении своего представления. Отсоединенных!

...