dry-schema icon indicating copy to clipboard operation
dry-schema copied to clipboard

Inconsistent behavior when passing array schema to a property

Open lenart opened this issue 4 years ago • 1 comments

Describe the bug

There's an inconsistency that leads to some unexpected behavior when using nested schemas. The problem is best described by the example below.

To Reproduce & Expected behavior

FriendSchema = Dry::Schema.Params do
  optional(:nickname).filled(:string)
end

class UserSchema < Dry::Validation::Contract
  params do
    optional(:name)
    optional(:close_friends).maybe(:array, FriendSchema)
    optional(:friends).maybe do
      array(FriendSchema)
    end
  end
end

UserSchema.new.call({name: "John", friends: []}) # works
UserSchema.new.call({name: "John", close_friends: []}) # expects to work but does not

NoMethodError: undefined method `key?' for []:Array
from /Users/lenart/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/dry-logic-1.1.0/lib/dry/logic/predicates.rb:25:in `key?'

If filled(:string) is omitted from FriendSchema the error goes away but in that case, all params get through.

FriendSchema = Dry::Schema.Params do
  optional(:nickname)
end

# UserSchema same as in above example

UserSchema.new.call({name: "John", close_friends: [{any: 'value'}]})
# expected {name: "John"}
# actual {name: "John", close_friends: [{any: "value"}]}

UserSchema.new.call({name: "John", friends: [{any: 'value'}]})
# returns as expected {name: "John", friends: [{}]}

I'm not sure that the empty object is what I'd expect in the friends array.


The same syntax seems to work fine when dealing with hashes (instead of arrays).

FriendSchema = Dry::Schema.Params do
  optional(:nickname).filled(:string)
end

class UserSchema < Dry::Validation::Contract
  params do
    optional(:name)
    optional(:close_friends).maybe(:hash, FriendSchema)
    optional(:friends).maybe do
      hash(FriendSchema)
    end
  end
end

UserSchema.new.call({name: "John", close_friends: {any: 'value'}}) 
# returns as expected {name: "John", close_friends: {}}

UserSchema.new.call({name: "John", friends: {any: 'value'}}) 
# returns as expected {name: "John", friends: {}} 

My environment

  • Affects my production application: NO
  • Ruby version: 2.6.6
  • OS: MacOS Big Sur 11.2.1
  • dry-schema 1.6.1

other related gems in Gemfile.lock

    dry-configurable (0.12.1)
    dry-container (0.7.2)
    dry-core (0.5.0)
    dry-equalizer (0.3.0)
    dry-inflector (0.2.0)
    dry-initializer (3.0.4)
    dry-logic (1.1.0)
    dry-schema (1.6.1)
    dry-transformer (0.1.1)
    dry-types (1.5.1)
    dry-validation (1.6.0)

lenart avatar Mar 03 '21 07:03 lenart

I assigned it to 2.0.0 because all DSL inconsistencies will be easier to address after a couple of improvements/refactorings are done, and they are scheduled for 2.0.0.

solnic avatar Mar 04 '21 06:03 solnic