如何处理 Rails 中的散列嵌套键验​​证?

How to handle hash nested keys validation in Rails?

我目前有一个模型,我想在其中使用点符号来添加错误,以更具体地说明散列属性中的哪个键有问题:

class MyFormObject
  include ActiveModel::Validations
  include ActiveModel::AttributeAssignment

  attr_accessor :name, :metadata

  validates :name, presence: true 
  validate address_is_present_in_metadata

  def initialize(**attributes)
    assign_attributes(attributes)
  end

  def validate_address_is_present_in_metadata
    errors.add("metadata.address", "can't be blank") if metadata[:address].blank?
  end
end

这可行,但如果我决定使用符号而不是如下消息:

errors.add("metadata.address", :blank) if metadata[:address].blank?

然后 Rails 抱怨,因为 metadata.address 不是一个属性。抛出错误的 ActiveModel::Errors 代码检查属性是否为 :base,如果不是,它会尝试从模型中读取属性以生成错误消息……然后 boom.

value = attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)

作为解决方法,我决定覆盖 read_attribute_for_validation

def read_attribute_for_validation(key)
  key_str = key.to_s
  return public_send(key) unless key_str.include?(".")

  key_path = key_str.split(".").map(&:to_sym)
  public_send(key_path.first).dig(*key_path[1, key_path.size])
end

是否有 better/supported 方法来验证我不知道的散列中的嵌套键?

来自documentation :

By default this is assumed to be an instance named after the attribute. Override this method in subclasses should you need to retrieve the value for a given attribute differently.

看来你的策略是有效的。

一些建议:

  • 你可以使用 public_send 而不是 send
  • 嵌套还有一个meaning,你可以使用另一个变量名
  • Array#[a,b]a 开始采用 b 个元素,因此 b 不应大于 Array#size。您可以只使用 array.drop(1)。您可能一直在寻找 array[a..b],它采用索引 ab.
  • 之间的元素