Rails Presenter - 与2个共享相同属性的不同模型进行交互

时间:2016-02-19 06:49:58

标签: ruby-on-rails encapsulation single-responsibility-principle presenter

我在大型Rails应用roominquiry中有2个模型。它们共享一个属性/列cancellation_policy

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

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

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

此演示者会做各种事情来“呈现”room。然而,我最近添加了一些方法,例如:

 def cancellation_policy_type
 def get_cancellation_policy_text_with_formatting
 etc.

在各种RoomControllers中(跨不同的名称空间)我可以使用@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演示者方法与room.cancellation_policy分开,但这并不能解决RoomInquiry之间的问题以及不希望混淆了两个不同的类。

然而,当涉及到在查询模型和房间模型中加入这个时,我的主要问题/无知。以下一切对我来说都是非常错误的:

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。

如果需要进一步说明,请告诉我。

1 个答案:

答案 0 :(得分:0)

首先,我要为演示者创建一个基类,以减少重复量

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作为基类意味着它会将缺少的方法委托给包装对象。

例如:

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

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

这将让您减少演示者中的样板量。

class RoomPresenter < BasePresenter
  include RoomPresenters::CancellationPolicy 
end

class InquiryPresenter < BasePresenter
  include RoomPresenters::CancellationPolicy
end

您还可以为模型创建mixin,让您@room.present代替RoomPresenter.new(@room)

它还可以让您通过@rooms.map(&:present)获得展示的收藏品。

module Presentable
  def present
    "#{self.class.name}Presenter".constantize.new(self)
  end
end