One of the more interesting rules it has is that only one piece of card-related info is provided: the post data should have only one of the card, card_token or customer_token keys.
Each one of those keys would have it’s own related validation rules, of course. For example:
Dry::Validation.Schema do
optional(:card).maybe do
schema do
required(:number).filled(size?: 16)
end
end
optional(:card_token).maybe(format?: /^card_/)
optional(:customer_token).maybe(format?: /^cus_/)
end
What I’m not sure about is how to write a rule that requires only one of these keys to be actually present. Would any of you know how to do this (and whether it’s currently possible with dry-validation)?
I initially went to create a high-level rule that would operation on the values of card, card_token and customer_token, and then I wanted to somehow put those values into an array, strip away the empty ones, and then check that the array’s length is only one, but I wasn’t sure how I could bundle up all three of those values and pass them to a single predicate.
I guess the new input macro could be used in this case, so ie:
Dry::Validation.Schema do
configure do
def valid_keys?(input)
input.keys.size == 1
end
end
input :hash?, :valid_keys?
optional(:card).maybe do
schema do
required(:number).filled(size?: 16)
end
end
optional(:card_token).maybe(format?: /^card_/)
optional(:customer_token).maybe(format?: /^cus_/)
end
Thanks for the pointer to the input macro here, @solnic, it makes perfect sense!
However, the example code you’ve posted is crashing:
require "dry-validation"
schema = Dry::Validation.Schema do
configure do
def valid_keys?(input)
input.keys.size == 1
end
end
input :hash?, :valid_keys?
optional(:card).maybe do
schema do
required(:number).filled(size?: 16)
end
end
optional(:card_token).maybe(format?: /^card_/)
optional(:customer_token).maybe(format?: /^cus_/)
end
input = {
card: {number: "4200000000000000"},
customer_token: "cus_1234",
}
result = schema.(input)
p result.messages
Running this gives the following error:
/Users/tim/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/dry-validation-3d090eeafac9/lib/dry/validation/schema/rule.rb:141:in `and': undefined method `to_ast' for #<UnboundMethod: #<Class:0x007ff15c11e7e8>#valid_keys?> (NoMethodError)
Did you mean? to_s
from /Users/tim/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/dry-validation-3d090eeafac9/lib/dry/validation/schema/value.rb:185:in `each'
from /Users/tim/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/dry-validation-3d090eeafac9/lib/dry/validation/schema/value.rb:185:in `reduce'
from /Users/tim/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/dry-validation-3d090eeafac9/lib/dry/validation/schema/value.rb:185:in `infer_predicates'
from /Users/tim/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/dry-validation-3d090eeafac9/lib/dry/validation/schema/class_interface.rb:51:in `define'
from /Users/tim/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/dry-validation-3d090eeafac9/lib/dry/validation.rb:39:in `Schema'
from test2.rb:3:in `<main>'