dry-rb / dry-schema

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

Key is ignored if it ends with a question mark #259

Open pavelbonda opened 4 years ago

pavelbonda commented 4 years ago

Describe the bug

Recently I tried to use dry-schema for data validation in existing app, I have a couple of use cases when hash keys end with question mark, like { 'terms_of_usage?' => 'Y' }. When I validate such data these keys are ignored.

To Reproduce

MySchema = Dry::Schema.Params do
  required(:some?).filled(:string)
end

MySchema.('some?' => 'val')
=> #<Dry::Schema::Result{} errors={:some?=>["is missing"]}>

Expected behavior

MySchema = Dry::Schema.Params do
  required(:some?).filled(:string)
end

MySchema.('some?' => 'val')
=> #<Dry::Schema::Result{:some?=>"val"} errors={}>

Your environment

robhanlon22 commented 4 years ago

I stepped through your example and this looks like an issue caused by https://github.com/dry-rb/dry-types:

From: lib/dry/schema/value_coercer.rb @ line 21 :

    16:       param :type_schema
    17:
    18:       # @api private
    19:       def call(input)
    20:         if input.success?
 => 21:           binding.irb
    22:           type_schema[input.to_h]
    23:         else
    24:           type_schema.each_with_object(EMPTY_HASH.dup) do |key, hash|
    25:             name = key.name
    26:

irb(#<Dry::Schema::ValueCoercer:0x00007f895b25eec8>):001:0> type_schema
=> #<Dry::Types[Lax<Constructor<Schema<keys={some?: Nominal<String meta={required: false, maybe: false}>}> fn=Dry::Types::Coercions::Params.to_hash>>]>
irb(#<Dry::Schema::ValueCoercer:0x00007f895b25eec8>):002:0> type_schema[some?: 'val']
=> {}
irb(#<Dry::Schema::ValueCoercer:0x00007f895b25eec8>):003:0> type_schema[some: 'val']
=> {:some=>"val"}
robhanlon22 commented 4 years ago

After a little digging, it looks like this is intended behavior: https://dry-rb.org/gems/dry-types/1.2/hash-schemas/#optional-keys

v-kolesnikov commented 3 years ago

@solnic Is there any workaround for now to deal with attributes that ends with ??

3.0.1 :009 > s = Dry::Schema.Params { required(:'Enable for Sale?').maybe(:bool) }
 => #<Dry::Schema::Params keys=["Enable for Sale?"] rules={:"Enable for Sale?"=>"k...
3.0.1 :010 > s.('Enable for Sale?' => 'Yes')
 => #<Dry::Schema::Result{} errors={:"Enable for Sale?"=>["is missing"]} path=[]>
solnic commented 3 years ago

@v-kolesnikov if it's possible to turn this behavior off in dry-types, then that would be the workaround /cc @flash-gordon ?

flash-gordon commented 3 years ago

no clue. It's possible to create a custom schema type that transforms input keys

module Types
  include Dry::Types()

  FancyHash = Hash.schema({}).with_key_transform { |key| key.gsub('?', '').gsub(' ', '_').to_sym }
end

schema = Types::FancyHash.schema(foo_bar: 'integer')
schema.({ 'foo bar?' => 123 }) # => {:foo_bar=>123}

It should be possible to make such a type as a default hash type in dry-schema's type registry, there's no "public" 1-line API for that though.