dry-rb / dry-schema

Coercion and validation for data structures
https://dry-rb.org/gems/dry-schema
MIT License
422 stars 110 forks source link

schemas with dry-logic operators API fail when dry/validators rule is added #277

Open idoa01 opened 4 years ago

idoa01 commented 4 years ago

Following issue #245, I tried to implement a similar situation inside a dry-validators contract, but the following code fails when there is at least one rule involved, see example below

require 'dry/validation'

HomeAddress = Dry::Schema.JSON do
  required(:id).filled(:string)
  required(:address).filled(:string)
end

WorkAddress = Dry::Schema.JSON do
  required(:id).filled(:string)
  required(:company).filled(:string)
end

class NewUserContract < Dry::Validation::Contract
  json do
    required(:first_name).filled(:string)
    required(:last_name).filled(:string)
    required(:email).filled(:string)
    required(:addresses).array(:hash, HomeAddress | WorkAddress)
  end

  rule(:first_name) do
    key.failure('should be John') unless value == 'John'
  end
end

valid_hash = {
  'first_name' => 'foo',
  'last_name' => 'baz',
  'email' => 'a@b.com',
  'addresses' => [
    { 'address' => 'blah' }, # doesn't include 'id' like it should
    { 'id' => 'work', 'company' => 'blah.com' }
  ]
}

contract = NewUserContract.new
result = contract.call(valid_hash)

puts result.errors.to_h.inspect

which crashes with the following stacktrace:

Traceback (most recent call last):
    14: from schema.rb:37:in `<main>'
    13: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/contract.rb:94:in `call'
    12: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/result.rb:25:in `new'
    11: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/contract.rb:95:in `block in call'
    10: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/contract.rb:95:in `each'
     9: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/contract.rb:96:in `block (2 levels) in call'
     8: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/contract.rb:96:in `any?'
     7: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/contract.rb:96:in `block (3 levels) in call'
     6: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/contract.rb:126:in `error?'
     5: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-validation-1.5.0/lib/dry/validation/result.rb:106:in `error?'
     4: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-schema-1.5.0/lib/dry/schema/result.rb:115:in `error?'
     3: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-schema-1.5.0/lib/dry/schema/result.rb:115:in `any?'
     2: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-schema-1.5.0/lib/dry/schema/message_set.rb:51:in `each'
     1: from /Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-schema-1.5.0/lib/dry/schema/message_set.rb:51:in `each'
/Users/ido/.rvm/gems/ruby-2.7.0@schema-test/gems/dry-schema-1.5.0/lib/dry/schema/result.rb:115:in `block in error?': undefined method `path' for #<Dry::Schema::Message::Or::MultiPath:0x00007f97880db7a0> (NoMethodError)

Expected behavior not to fail , produce the following output (like it does if the rule(:first_name) doesn't exist)

{:addresses=>{0=>{:or=>[{:id=>["is missing"]}, {:id=>["is missing"], :company=>["is missing"]}]}}}

Your environment

solnic commented 4 years ago

thanks for spotting this and reporting, it'll be fixed in 1.5.1

idoa01 commented 4 years ago

can you suggest a workaround for this issue?

solnic commented 4 years ago

@idoa01 yes, you can monkey-patch it like that:

class Dry::Schema::Message::Or::MultiPath
  def path
    []
  end
end

this should make it work

sirfilip commented 3 years ago

will pick this one up