如何根据特定字段限制 active_model_serializers 属性,而不必在每个属性上放置 `if:` 选项?

How to limit the active_model_serializers attributes depending on a specific field without having to put the `if:` option on every attribute?

我有一个模型可以有各种类型,这些类型会影响正在使用的字段。 我想从输出中删除未使用的字段。目前,这有效:

attribute :field1, if: :is_type1?
attribute :field2, if: :is_type1?
attribute :field3, if: :is_type2?
attribute :field4 # always shown

问题是我有很多属性,这可能会变得乏味且难以维护,尤其是当一个字段可以被某些但不是所有类型使用时。我知道也许可以将模型化更改为具有多个模型,从而具有多个序列化程序,但这不是我现在负担不起的,所以我正在寻找另一种解决方案。

我设想了两种方法:要么使用某种块来定义属性,例如:

given -> { object.type == 1 } do
  attributes :field1, :field2
end
given -> { object.type == 2 } do
  attributes :field3
end
attribute :field4

或者在 if: 中到处使用一些通用方法。就此而言,我已经在我的模型中定义了适用于每种类型的字段:

def allowed_attr
  allowed_attr = {
    field1: false,
    field2: false,
    field3: false
  }

  case type
  when 1
    allowed_attr[:field1] = true
    allowed_attr[:field2] = true
  when 2
    allowed_attr[:field3] = true
  end
  allowed_attr
end

我想在我的序列化程序中做的是这样的事情:

attribute :field1, if: -> { allowed(attribute_name) }
attribute :field2, if: -> { allowed(attribute_name) }
attribute :field3, if: -> { allowed(attribute_name) }
attribute :field4, if: -> { allowed(attribute_name) }

def allowed(attr)
  object.allowed_attr[attr] != false # allows everything that is not precisely set to false
end

问题是我似乎无法访问当前正在测试的字段 (attribute_name)

有什么方法可以完成我希望我忽略的事情吗?

编辑

这是我根据 Omnigazer 的回答使用的解决方案。

[
  :field1
  [:field1_attr, :field1],
  [:field2, Proc.new { object.field1.present? }]
].each do |attr|
  if attr.kind_of? Array
    name = attr.shift

    if attr[0].kind_of? Symbol
      allowed = attr.shift
    end

    if attr[0].kind_of? Proc
      cond = attr.shift
    end
  end

  name    ||= attr
  allowed ||= name
  cond    ||= Proc.new { true }

  attribute name, if: -> { instance_eval(&cond) && object.allowed_attribute?(allowed) }
end

你可以这样做:

[:field1, :field2, :field3, :field4].each do |attribute_name|
    attribute attribute_name, if: -> { allowed(attribute_name) }
end