I currently have a model in which I want to use dot notation to add errors to be more specific about which key in the hash attribute is faulty:
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
This works, but if I decide to use a symbol instead of a message like the following:
errors.add("metadata.address", :blank) if metadata[:address].blank?
then Rails complains because metadata.address
is not an attribute. The ActiveModel::Errors
code that throws the error checks if the attribute is :base
and if it's not, it tries to read the attribute from the model to generate the error message... and boom.
value = attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
As a workaround, I decided to override 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
Is there a better/supported way to validate nested keys in a hash that I'm not aware of?