dry-rb / dry-types

Flexible type system for Ruby with coercions and constraints
https://dry-rb.org/gems/dry-types
MIT License
859 stars 134 forks source link

symbolized hash schema not doing strict type checks anymore? #111

Closed andreaseger closed 8 years ago

andreaseger commented 8 years ago

I realize there are lots of moving parts in this gem right now and the documentation here is somewhat outdated. But I wonder why the type checking behaviour of the symbolized hash schema changed completely and won't do any type-checking at all now.

Here is how it behaved in v0.7.2 (had to lock dry-logic to 0.2.3 because of some internal changes)

#!/usr/bin/env ruby
require "bundler/inline"

gemfile true do
  source "https://rubygems.org"
  gem "dry-logic", "~> 0.2.3"
  gem "dry-types", "~> 0.7.2"
end

require 'dry-types'
module Types
  include Dry::Types.module
end

hash = Types::Hash.symbolized(name: Types::Strict::String, age: Types::Coercible::Int)

p hash['name' => 'Jane', 'age' => '21']
p hash['name' => 123, 'age' => '21']

__END__
Fetching gem metadata from https://rubygems.org/
Fetching version metadata from https://rubygems.org/
Resolving dependencies...
Using concurrent-ruby 1.0.2
Using dry-equalizer 0.2.0
Using dry-monads 0.0.2
Using inflecto 0.0.2
Using bundler 1.12.5
Using dry-configurable 0.1.6
Using dry-container 0.3.4
Using dry-logic 0.2.3
Using dry-types 0.7.2
{:name=>"Jane", :age=>21}
/home/ane/.rbenv/versions/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/dry-types-0.7.2/lib/dry/types/constrained.rb:20:in `block in call': 123 violates constraints (type?(String) failed) (Dry::Types::ConstraintError)
        from /home/ane/.rbenv/versions/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/dry-types-0.7.2/lib/dry/types/constrained.rb:31:in `try'
        from /home/ane/.rbenv/versions/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/dry-types-0.7.2/lib/dry/types/constrained.rb:19:in `call'
        from /home/ane/.rbenv/versions/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/dry-types-0.7.2/lib/dry/types/hash/schema.rb:56:in `block in call'
        from /home/ane/.rbenv/versions/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/dry-types-0.7.2/lib/dry/types/hash/schema.rb:49:in `each'
        from /home/ane/.rbenv/versions/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/dry-types-0.7.2/lib/dry/types/hash/schema.rb:49:in `each_with_object'
        from /home/ane/.rbenv/versions/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/dry-types-0.7.2/lib/dry/types/hash/schema.rb:49:in `call'
        from dry-test.rb:18:in `<main>'

As expected the second hash fails because the name isn't a string

using dry-types v0.8.0 I see the following

#!/usr/bin/env ruby
require "bundler/inline"

gemfile true do
  source "https://rubygems.org"
  gem "dry-types", "~> 0.8"
end

require 'dry-types'
module Types
  include Dry::Types.module
end

hash = Types::Hash.symbolized(name: Types::Strict::String, age: Types::Coercible::Int)

p hash['name' => 'Jane', 'age' => '21']
p hash['name' => 123, 'age' => '21']

__END__
Fetching gem metadata from https://rubygems.org/
Fetching version metadata from https://rubygems.org/
Resolving dependencies...
Using concurrent-ruby 1.0.2
Using dry-equalizer 0.2.0
Using dry-monads 0.0.2
Using ice_nine 0.11.2
Using inflecto 0.0.2
Using bundler 1.12.5
Using dry-configurable 0.1.6
Using dry-container 0.3.4
Using dry-logic 0.3.0
Using dry-types 0.8.0
{:name=>"Jane", :age=>21}
{:name=>123, :age=>21}

What I see now is that the coersion was done but the strict type check was skipped or ignored.

Is this intended behaviour?

solnic commented 8 years ago

Yes this is the new behavior. We can introduce a symbolized-strict version too although I'm not sure if that's a good idea. If you need to coerce input it's better/safer to use dry-validation. Could you tell me what your use-case is?

andreaseger commented 8 years ago

so the schema and the symbolized hash schema only do coersion but no type checking? Personally can't see a use_case right now where I would want that but OK.

An example where we used this was for validating and coercion (and symbolizing) of a nested hash in a request body. But I guess we could migrate this to dry-validation although we liked that we wouldn't need to symbolize known keys and remove unknown keys in the input ourself. ( unknown set of string-keys in -> known set of symbolized keys out + everything is is the type we want it to be )

solnic commented 8 years ago

An example where we used this was for validating and coercion (and symbolizing) of a nested hash in a request body.

dry-types is not a validation library. type-checking raises exceptions and it should be used in places where you do not expect invalid data.

we liked that we wouldn't need to symbolize known keys and remove unknown keys in the input ourself.

dry-validation rejects unknown keys automatically for you, it only cares about defined keys in a validation schema

unknown set of string-keys in -> known set of symbolized keys out + everything is is the type we want it to be

That's how dry-v works:

Dry::Validation.Form { required(:foo).filled(:str?) }.call(
  "oops" => "dooh", "foo" => "hello", "such" => "unexpected"
)
=> #<Dry::Validation::Result output={:foo=>"hello"} messages={}>