Rails Presenter - 与 2 个具有相同属性的不同模型交互

Rails Presenter - Interacting with 2 different models that share the same attribute

我在大型 Rails 应用程序中有 2 个模型 roominquiry。他们都有一个 attribute/column cancellation_policy.

在进行查询(别名:预订)时,cancellation_policyroom.cancellation_policy 复制到 inquiry.cancellation_policy


我目前有一个 RoomPresenter,它使用 room 对象初始化,如下所示:

  def initialize(room, current_user = nil)
    @room = room
    @current_user = current_user
  end

这位主持人对 'present' 和 room 做了很多事情。然而,我最近添加了一堆方法,例如:

 def cancellation_policy_type
 def get_cancellation_policy_text_with_formatting
 etc.

在各种 RoomController(跨越不同的命名空间)中,我可以用 @room_presenter = RoomPresenter.new(@room) 实例化并在相关视图中调用方法,例如 @room_presenter. def cancellation_policy_type


我觉得可以采取以下做法

class RoomPresenter
  # gives me access to RoomPresenter#cancellation_policy (see below)
  include RoomPresenters::CancellationPolicy

  def initialize(room)
    @room = room
  end
end

# app/presenters/room_presenters/cancellation_policy.rb
module RoomPresenters
  module CancellationPolicy
    def cancellation_policy
      ###
    end
  end
end

这将以合乎逻辑的方式将 room presenter 方法与 room.cancellation_policy 分开,但这并不能解决 RoomInquiry 之间的问题以及一个愿望不要混淆这两个不同的 类.


然而,我的主要 question/cluelessness 出现在将其纳入查询模型和房间模型时。以下对我来说似乎都是错误的:

class InquiryPresenter (would be initialized with an inquiry).
  include RoomPresenters::CancellationPolicy

同样:

class InquiryPresenter
  #lots of duplicated code doing the same thing/same methods.

我试图了解如何最好地组织这种类型的逻辑,但不确定最佳方法。

底层输出非常简单 - 每个方法都只是输出一些纯文本或 html,但随着应用程序的进一步发展,我发现需要确保 Presenters 遵守 SRP .

如果需要进一步解释和示例,请告诉我。

我会首先为您的演示者创建一个基础 class 以减少重复的数量

class BasePresenter < Delegator

  def initialize(object)
    @object = object
  end

  # required by Delegator
  def __getobj__
    @object
  end

  def self.model_name
    self.name.chomp("Presenter")
  end

  def self.model_key
    self.model_name.underscore.to_sym
  end

  alias_method :__getobj__, :object

  # declares a getter based on the class name
  # UserPresenter -> #user
  alias_method :__getobj__, self.model_key
end

使用 stdlib Delegator 作为基础 class 意味着它将缺少的方法委托给包装的 object.

例如:

RoomPresenter.new(@room).id == @room.id

我们还创建了一个通用初始化程序,并使用 @object 作为内部存储。可以通过 #object 或从 class 名称派生的自定义 getter 访问内部存储的 object。

这会让您减少演示者的样板文件数量。

class RoomPresenter < BasePresenter
  include RoomPresenters::CancellationPolicy 
end

class InquiryPresenter < BasePresenter
  include RoomPresenters::CancellationPolicy
end

您还可以为您的模型创建一个 mixin,让您可以 @room.present 而不是 RoomPresenter.new(@room)

它还可以让您通过 @rooms.map(&:present).

获得礼物 collection
module Presentable
  def present
    "#{self.class.name}Presenter".constantize.new(self)
  end
end