Hi all!
I’ve a schema that allows certain requests to send an object’s uid or a set of data for a new object. So you can send either:
{
customer: {
id: 2
}
}
xor:
{
customer: {
first_name: 'John',
last_name: 'Doe'
}
}
However it needs to be and exclusive disjunction. You are not allowed to send both id and data. So my first implementation looked like this:
required(:customer).schema do
optional(:id).filled(:int?)
optional(:first_name).filled(:str?)
optional(:last_name).filled(:str?)
end
# Example is simplified, but in real life customer is nested deeper.
# Complicated ruler names need to exist for proper error.yml extraction
# Force customer attributes to be filled if no id given
%i[first_name last_name].each do |key|
rule(
"customer__#{key}".to_sym => [
%i[customer id],
%i[customer].push(key)
]
) do |id, param|
id.none?.then(param.filled?)
end
end
# Force id to be empty if other credit card attributes are filled
rule(
customer__id: [
%i[customer first_name],
%i[customer last_name]
]
) do |id, first_name, last_name|
(first_name.filled? | last_name.filled?).then(uid.none?)
end
but it’s ugly AF. Do you have any other ideas how to implement it better?
It’d be perfect if it looked like this:
required(:customer).xor(
schema { required(:id).filled(:int?) },
schema do
required(:first_name).filled(:str?)
required(:last_name).filled(:str?)
end
end
It’d love more versatile solution, as I’ll have more cases like this in my code customer_data/customer_id, credit_card_data/credit_card_id etc.
EDIT 1: It can be either 0.13 or 1.0, but I prefer later, as I’ll be upgrading the gem soon.