如何动态地向 ROAR 代表添加属性?

How to dynamically add properties to a ROAR representer?

我正在使用 ROAR 为 rails 应用程序实施 API。此应用程序处理的票证可以具有 subjectdescription 等属性,但也可以具有用户定义的属性。为简单起见,我们假设一张票看起来像:

class Ticket
  attr_accessor :subject, :description

  def custom_attributes
    # in reality these attributes depend on the current ticket instance
    # they are not hard-coded into the class
    [['priority', 'high'], ['Operating System', 'Ubuntu']]
  end
end

此类工单所需的 JSON 输出如下所示:

{
  "subject": "Foo",
  "description": "Bar",
  "customField1": "high",
  "customField2": "Ubuntu"
}

现在您可能已经看到问题了。所有属性都是根对象的直接子对象,这意味着我不能将其写为代表:

class TicketRepresenter
  property :subject
  property :description

  # Need to iterate over instance members on the class level here...
end

ROAR 是否提供了一些机制来实现这一点?例如。在实际实例的上下文中执行的回调,例如

def call_me_on_write
  represented.custom_attributes.each do |attribute|
    add_property('customField1', attribute[1])
  end
end

在 ROAR 中是否有类似的东西是我为了完成这个而忽略的?

我查看了 ROAR and the docs for representable 的两个文档,但找不到任何内容。

免责声明

我尽量简化实际情况,使问题更易读。如果您认为缺少重要信息,请告诉我。谢天谢地,我会提供更多详细信息。

超出范围

请不要讨论选择的JSON格式是否是good/bad想法,我想评估ROAR是否支持它。

我最终从我的基本代表动态创建了 classes:

class TicketRepresenter
  property :subject
  property :description

  def self.create(ticket, context = {})
    klass = Class.new(TicketRepresenter) # create a subclass of my representer
    ticket.custom_attributes.each do |attribute|
      # for each custom attribute in the actual instance insert a property into the created class
      property "customField#{attribute.id}".to_sym
               getter: -> (*) { attribute.value }
    end

    # return an instance of the class created above
    klass.new(ticket, context)
  end
end

基本上这意味着用于创建 JSON 的实际代表 class 对于每个 Ticket.

都是不同的代表

如果您想从 JSON 读取 Ticket 返回,则必须正确初始化表示器,以便创建的表示器 class 知道您的自定义字段并定义二传手。

您现在需要按常规调用新的 create 方法而不是 new。 如果您需要通过 ROAR 创建您的代表(例如,对于一个集合),您可以使用 ROAR 的 Polymorphic Object Creation 机制。

注意:上面的代码并不完全符合我问题中发布的自定义属性示例,但您应该明白了(在示例中,属性没有成员像 idvalue,但是由键和值组成的列表)。

我认为解决该问题的最佳方法是使用 Roar 的 writer:。它通过将它调用的一些值 options 传递给提供的 lambda 来完全将对输出的控制权交给你。

例如:

property :desired_property_name, writer: -> (represented:, doc:, **) do
  doc[:desired_key] = represented.desired_value
end

github 自述文件未涵盖但在 Trailblazer 网站上记录了很多用途。这个可以在 http://trailblazer.to/gems/representable/3.0/function-api.html#writer 找到。

干杯!