dry-types 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +5 -10
- data/CHANGELOG.md +25 -1
- data/Gemfile +1 -3
- data/README.md +1 -1
- data/Rakefile +11 -2
- data/benchmarks/hash_schemas.rb +51 -0
- data/dry-types.gemspec +4 -3
- data/lib/dry/types.rb +10 -5
- data/lib/dry/types/array/member.rb +4 -0
- data/lib/dry/types/builder.rb +6 -7
- data/lib/dry/types/coercions.rb +6 -1
- data/lib/dry/types/coercions/form.rb +4 -4
- data/lib/dry/types/constrained.rb +10 -5
- data/lib/dry/types/constraints.rb +2 -1
- data/lib/dry/types/core.rb +3 -13
- data/lib/dry/types/decorator.rb +2 -2
- data/lib/dry/types/definition.rb +1 -1
- data/lib/dry/types/errors.rb +15 -22
- data/lib/dry/types/extensions.rb +3 -0
- data/lib/dry/types/extensions/maybe.rb +64 -0
- data/lib/dry/types/hash.rb +14 -0
- data/lib/dry/types/hash/schema.rb +95 -40
- data/lib/dry/types/options.rb +0 -2
- data/lib/dry/types/result.rb +26 -3
- data/lib/dry/types/safe.rb +7 -1
- data/lib/dry/types/sum.rb +9 -1
- data/lib/dry/types/version.rb +1 -1
- data/lib/spec/dry/types.rb +56 -0
- metadata +38 -42
- data/benchmarks/basic.rb +0 -57
- data/benchmarks/constrained.rb +0 -37
- data/lib/dry/types/hashify.rb +0 -12
- data/lib/dry/types/maybe.rb +0 -34
- data/lib/dry/types/struct.rb +0 -25
- data/lib/dry/types/struct/class_interface.rb +0 -107
- data/lib/dry/types/value.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f08f7240bc96724af80fa080ce0086e3274927e1
|
4
|
+
data.tar.gz: 61952ddec432653f82f5d1f8edf37dd0dd8f6c55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e6fdc5887495ff175ea35397c0f912b97264485d4796c43ab85d13ff99a06e5c81ab8d8cacea3875764142a2408d348a21d980e2965002fdc9d52499dbca910
|
7
|
+
data.tar.gz: 758ed4045287102f4475a2b12228ae41da0c8f3f88c780589e4a2d8be945941ebd46e079b307bcf3844d788b4c706fec1ef0c7cb74708937592495a79ec7ba8a
|
data/.travis.yml
CHANGED
@@ -3,14 +3,13 @@ sudo: false
|
|
3
3
|
cache: bundler
|
4
4
|
bundler_args: --without benchmarks tools
|
5
5
|
script:
|
6
|
-
- bundle exec rake
|
6
|
+
- bundle exec rake
|
7
7
|
rvm:
|
8
|
-
- 2.
|
9
|
-
- 2.
|
10
|
-
- 2.
|
11
|
-
- 2.3.0
|
8
|
+
- 2.1.10
|
9
|
+
- 2.2.5
|
10
|
+
- 2.3.1
|
12
11
|
- rbx-2
|
13
|
-
- jruby-
|
12
|
+
- jruby-9.1.1.0
|
14
13
|
- ruby-head
|
15
14
|
env:
|
16
15
|
global:
|
@@ -18,10 +17,6 @@ env:
|
|
18
17
|
matrix:
|
19
18
|
allow_failures:
|
20
19
|
- rvm: ruby-head
|
21
|
-
- rvm: jruby-head
|
22
|
-
include:
|
23
|
-
- rvm: jruby-head
|
24
|
-
before_install: gem install bundler --no-ri --no-rdoc
|
25
20
|
notifications:
|
26
21
|
email: false
|
27
22
|
webhooks:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
# v0.9.0 2016-09-21
|
2
|
+
|
3
|
+
## Added
|
4
|
+
|
5
|
+
* `Hash#strict_with_defaults` which validates presence of all required keys and respects default types for missing *values* (backus)
|
6
|
+
* `Type#constrained?` method (flash-gordon)
|
7
|
+
|
8
|
+
## Fixed
|
9
|
+
|
10
|
+
* Summing two constrained types works correctly (flash-gordon)
|
11
|
+
* `Types::Array::Member#valid?` in cases where member type is a constraint (solnic)
|
12
|
+
* `Hash::Schema#try` handles exceptions properly and returns a failure object (solnic)
|
13
|
+
|
14
|
+
## Changed
|
15
|
+
|
16
|
+
* [BREAKING] Renamed `Hash##{schema=>permissive}` (backus)
|
17
|
+
* [BREAKING] `dry-monads` dependency was made optional, Maybe types are available after `Dry::Types.load_extension(:maybe)` (flash-gordon)
|
18
|
+
* [BREAKING] `Dry::Types::Struct` and `Dry::Types::Value` have been extracted to [`dry-struct`](https://github.com/dry-rb/dry-struct) (backus)
|
19
|
+
* `Types::Form::Bool` supports upcased true/false values (kirs)
|
20
|
+
* `Types::Form::{Date,DateTime,Time}` fail gracefully for invalid input (padde)
|
21
|
+
* ice_nine dependency has been dropped as it was required by Struct only (flash-gordon)
|
22
|
+
|
23
|
+
[Compare v0.8.1...v0.9.0](https://github.com/dryrb/dry-types/compare/v0.8.1...v0.9.0)
|
24
|
+
|
1
25
|
# v0.8.1 2016-07-13
|
2
26
|
|
3
27
|
## Fixed
|
@@ -34,7 +58,7 @@
|
|
34
58
|
- `Bool#default` gladly accepts `false` as its value (solnic)
|
35
59
|
- Creating an empty schema with input processor no longer fails (lasseebert)
|
36
60
|
|
37
|
-
##
|
61
|
+
## Changed
|
38
62
|
|
39
63
|
- Allow multiple calls to meta (solnic)
|
40
64
|
- Allow capitalised versions of true and false values for boolean coercions (nil0bject)
|
data/Gemfile
CHANGED
@@ -14,11 +14,9 @@ end
|
|
14
14
|
|
15
15
|
group :benchmarks do
|
16
16
|
gem 'sqlite3'
|
17
|
-
gem 'activerecord'
|
17
|
+
gem 'activerecord', platform: %i(jruby mri)
|
18
18
|
gem 'benchmark-ips'
|
19
19
|
gem 'virtus'
|
20
20
|
gem 'fast_attributes'
|
21
21
|
gem 'attrio'
|
22
22
|
end
|
23
|
-
|
24
|
-
gem "dry-logic", git: 'https://github.com/dry-rb/dry-logic.git', branch: "master"
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
|
21
21
|
## Development
|
22
22
|
|
23
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
23
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
24
24
|
|
25
25
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
26
26
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rspec/core/rake_task"
|
3
3
|
|
4
|
-
|
4
|
+
task :run_specs do
|
5
|
+
require 'rspec/core'
|
5
6
|
|
6
|
-
|
7
|
+
RSpec::Core::Runner.run(['spec/dry'])
|
8
|
+
RSpec.clear_examples
|
9
|
+
|
10
|
+
load 'spec/dry/types.rb'
|
11
|
+
Dry::Types.load_extensions(:maybe)
|
12
|
+
RSpec::Core::Runner.run(['spec'])
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: :run_specs
|
@@ -0,0 +1,51 @@
|
|
1
|
+
$LOAD_PATH.unshift('lib')
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'dry-types'
|
5
|
+
|
6
|
+
module SchemaBench
|
7
|
+
def self.hash_schema(type)
|
8
|
+
Dry::Types['hash'].public_send(type,
|
9
|
+
email: Dry::Types['string'],
|
10
|
+
age: Dry::Types['form.int'],
|
11
|
+
admin: Dry::Types['form.bool'],
|
12
|
+
address: Dry::Types['hash'].public_send(type,
|
13
|
+
city: Dry::Types['string'],
|
14
|
+
street: Dry::Types['string']
|
15
|
+
)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
private_class_method(:hash_schema)
|
20
|
+
|
21
|
+
SCHEMAS =
|
22
|
+
Dry::Types::Hash
|
23
|
+
.public_instance_methods(false)
|
24
|
+
.map { |schema_type| [schema_type, hash_schema(schema_type)] }
|
25
|
+
.to_h
|
26
|
+
|
27
|
+
INPUT = {
|
28
|
+
email: 'jane@doe.org',
|
29
|
+
age: '20',
|
30
|
+
admin: '1',
|
31
|
+
address: { city: 'NYC', street: 'Street 1/2' }
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'benchmark/ips'
|
36
|
+
|
37
|
+
Benchmark.ips do |x|
|
38
|
+
SchemaBench::SCHEMAS.each do |schema_type, schema|
|
39
|
+
x.report("#{schema_type}#call") do
|
40
|
+
schema.call(SchemaBench::INPUT)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
SchemaBench::SCHEMAS.each do |schema_type, schema|
|
45
|
+
x.report("#{schema_type}#try") do
|
46
|
+
schema.try(SchemaBench::INPUT)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
x.compare!
|
51
|
+
end
|
data/dry-types.gemspec
CHANGED
@@ -26,17 +26,18 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.bindir = "exe"
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
|
+
spec.required_ruby_version = ">= 2.1.0"
|
29
30
|
|
30
31
|
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
32
|
+
spec.add_runtime_dependency 'dry-core', '~> 0.1'
|
31
33
|
spec.add_runtime_dependency 'dry-container', '~> 0.3'
|
32
34
|
spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
|
33
35
|
spec.add_runtime_dependency 'dry-configurable', '~> 0.1'
|
34
|
-
spec.add_runtime_dependency 'dry-logic', '~> 0.
|
36
|
+
spec.add_runtime_dependency 'dry-logic', '~> 0.4', '>= 0.4.0'
|
35
37
|
spec.add_runtime_dependency 'inflecto', '~> 0.0.0', '>= 0.0.2'
|
36
|
-
spec.add_runtime_dependency 'dry-monads', '>= 0.0.1'
|
37
|
-
spec.add_runtime_dependency 'ice_nine', '~> 0.11'
|
38
38
|
|
39
39
|
spec.add_development_dependency "bundler", "~> 1.6"
|
40
40
|
spec.add_development_dependency "rake", "~> 11.0"
|
41
41
|
spec.add_development_dependency "rspec", "~> 3.3"
|
42
|
+
spec.add_development_dependency 'dry-monads', '~> 0.2'
|
42
43
|
end
|
data/lib/dry/types.rb
CHANGED
@@ -7,21 +7,19 @@ require 'concurrent'
|
|
7
7
|
|
8
8
|
require 'dry-container'
|
9
9
|
require 'dry-equalizer'
|
10
|
+
require 'dry/core/extensions'
|
10
11
|
|
11
12
|
require 'dry/types/version'
|
12
13
|
require 'dry/types/container'
|
13
14
|
require 'dry/types/definition'
|
14
15
|
require 'dry/types/constructor'
|
15
|
-
require 'dry/types/struct'
|
16
|
-
require 'dry/types/value'
|
17
16
|
|
18
17
|
require 'dry/types/errors'
|
19
18
|
|
20
19
|
module Dry
|
21
20
|
module Types
|
22
21
|
extend Dry::Configurable
|
23
|
-
|
24
|
-
Undefined = Object.new.freeze
|
22
|
+
extend Dry::Core::Extensions
|
25
23
|
|
26
24
|
setting :namespace, self
|
27
25
|
|
@@ -67,7 +65,13 @@ module Dry
|
|
67
65
|
container[name]
|
68
66
|
end
|
69
67
|
when Class
|
70
|
-
|
68
|
+
type_name = identifier(name)
|
69
|
+
|
70
|
+
if container.key?(type_name)
|
71
|
+
self[type_name]
|
72
|
+
else
|
73
|
+
name
|
74
|
+
end
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
@@ -102,3 +106,4 @@ module Dry
|
|
102
106
|
end
|
103
107
|
|
104
108
|
require 'dry/types/core' # load built-in types
|
109
|
+
require 'dry/types/extensions'
|
data/lib/dry/types/builder.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
+
require 'dry/core/constants'
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
3
5
|
module Builder
|
6
|
+
include Dry::Core::Constants
|
7
|
+
|
4
8
|
def constrained_type
|
5
9
|
Constrained
|
6
10
|
end
|
7
11
|
|
8
12
|
def |(other)
|
9
|
-
klass =
|
13
|
+
klass = constrained? && other.constrained? ? Sum::Constrained : Sum
|
10
14
|
klass.new(self, other)
|
11
15
|
end
|
12
16
|
|
@@ -14,10 +18,6 @@ module Dry
|
|
14
18
|
Types['strict.nil'] | self
|
15
19
|
end
|
16
20
|
|
17
|
-
def maybe
|
18
|
-
Maybe.new(Types['strict.nil'] | self)
|
19
|
-
end
|
20
|
-
|
21
21
|
def constrained(options)
|
22
22
|
constrained_type.new(self, rule: Types.Rule(options))
|
23
23
|
end
|
@@ -28,7 +28,7 @@ module Dry
|
|
28
28
|
if value.is_a?(Proc) || valid?(value)
|
29
29
|
Default[value].new(self, value)
|
30
30
|
else
|
31
|
-
raise ConstraintError
|
31
|
+
raise ConstraintError.new("default value #{value.inspect} violates constraints", value)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -50,6 +50,5 @@ end
|
|
50
50
|
require 'dry/types/default'
|
51
51
|
require 'dry/types/constrained'
|
52
52
|
require 'dry/types/enum'
|
53
|
-
require 'dry/types/maybe'
|
54
53
|
require 'dry/types/safe'
|
55
54
|
require 'dry/types/sum'
|
data/lib/dry/types/coercions.rb
CHANGED
@@ -1,25 +1,30 @@
|
|
1
|
+
require 'dry/core/constants'
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
3
5
|
module Coercions
|
4
|
-
|
6
|
+
include Dry::Core::Constants
|
5
7
|
|
6
8
|
def to_nil(input)
|
7
9
|
input unless empty_str?(input)
|
8
10
|
end
|
9
11
|
|
10
12
|
def to_date(input)
|
13
|
+
return input unless input.respond_to?(:to_str)
|
11
14
|
Date.parse(input)
|
12
15
|
rescue ArgumentError
|
13
16
|
input
|
14
17
|
end
|
15
18
|
|
16
19
|
def to_date_time(input)
|
20
|
+
return input unless input.respond_to?(:to_str)
|
17
21
|
DateTime.parse(input)
|
18
22
|
rescue ArgumentError
|
19
23
|
input
|
20
24
|
end
|
21
25
|
|
22
26
|
def to_time(input)
|
27
|
+
return input unless input.respond_to?(:to_str)
|
23
28
|
Time.parse(input)
|
24
29
|
rescue ArgumentError
|
25
30
|
input
|
@@ -5,18 +5,18 @@ module Dry
|
|
5
5
|
module Types
|
6
6
|
module Coercions
|
7
7
|
module Form
|
8
|
-
TRUE_VALUES = %w[1 on On ON t true True TRUE
|
9
|
-
FALSE_VALUES = %w[0 off Off OFF f false False FALSE n no No NO].freeze
|
8
|
+
TRUE_VALUES = %w[1 on On ON t true True TRUE T y yes Yes YES].freeze
|
9
|
+
FALSE_VALUES = %w[0 off Off OFF f false False FALSE F n no No NO].freeze
|
10
10
|
BOOLEAN_MAP = ::Hash[TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])].freeze
|
11
11
|
|
12
12
|
extend Coercions
|
13
13
|
|
14
14
|
def self.to_true(input)
|
15
|
-
BOOLEAN_MAP.fetch(input, input)
|
15
|
+
BOOLEAN_MAP.fetch(input.to_s, input)
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.to_false(input)
|
19
|
-
BOOLEAN_MAP.fetch(input, input)
|
19
|
+
BOOLEAN_MAP.fetch(input.to_s, input)
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.to_int(input)
|
@@ -18,29 +18,34 @@ module Dry
|
|
18
18
|
|
19
19
|
def call(input)
|
20
20
|
try(input) do |result|
|
21
|
-
raise ConstraintError,
|
21
|
+
raise ConstraintError.new(result, input)
|
22
22
|
end.input
|
23
23
|
end
|
24
24
|
alias_method :[], :call
|
25
25
|
|
26
26
|
def try(input, &block)
|
27
|
-
|
27
|
+
result = rule.(input)
|
28
28
|
|
29
|
-
if
|
29
|
+
if result.success?
|
30
30
|
type.try(input, &block)
|
31
31
|
else
|
32
|
-
|
32
|
+
failure = failure(input, result)
|
33
|
+
block ? yield(failure) : failure
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
37
|
def valid?(value)
|
37
|
-
rule.(value).success?
|
38
|
+
rule.(value).success? && type.valid?(value)
|
38
39
|
end
|
39
40
|
|
40
41
|
def constrained(options)
|
41
42
|
with(rule: rule & Types.Rule(options))
|
42
43
|
end
|
43
44
|
|
45
|
+
def constrained?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
44
49
|
private
|
45
50
|
|
46
51
|
def decorate?(response)
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'dry/logic/rule_compiler'
|
2
2
|
require 'dry/logic/predicates'
|
3
|
+
require 'dry/logic/rule/predicate'
|
3
4
|
|
4
5
|
module Dry
|
5
6
|
module Types
|
6
7
|
def self.Rule(options)
|
7
8
|
rule_compiler.(
|
8
|
-
options.map { |key, val|
|
9
|
+
options.map { |key, val| Logic::Rule::Predicate.new(Logic::Predicates[:"#{key}?"]).curry(val).to_ast }
|
9
10
|
).reduce(:and)
|
10
11
|
end
|
11
12
|
|
data/lib/dry/types/core.rb
CHANGED
@@ -22,6 +22,8 @@ module Dry
|
|
22
22
|
|
23
23
|
ALL_PRIMITIVES = COERCIBLE.merge(NON_COERCIBLE).freeze
|
24
24
|
|
25
|
+
NON_NIL = ALL_PRIMITIVES.reject { |name, _| name == :nil }.freeze
|
26
|
+
|
25
27
|
# Register built-in types that are non-coercible through kernel methods
|
26
28
|
ALL_PRIMITIVES.each do |name, primitive|
|
27
29
|
register(name.to_s, Definition[primitive].new(primitive))
|
@@ -37,20 +39,8 @@ module Dry
|
|
37
39
|
register("coercible.#{name}", self[name.to_s].constructor(Kernel.method(primitive.name)))
|
38
40
|
end
|
39
41
|
|
40
|
-
# Register non-coercible maybe types
|
41
|
-
ALL_PRIMITIVES.each_key do |name|
|
42
|
-
next if name == :nil
|
43
|
-
register("maybe.strict.#{name}", self["strict.#{name}"].maybe)
|
44
|
-
end
|
45
|
-
|
46
|
-
# Register coercible maybe types
|
47
|
-
COERCIBLE.each_key do |name|
|
48
|
-
register("maybe.coercible.#{name}", self["coercible.#{name}"].maybe)
|
49
|
-
end
|
50
|
-
|
51
42
|
# Register non-coercible optional types
|
52
|
-
|
53
|
-
next if name == :nil
|
43
|
+
NON_NIL.each_key do |name|
|
54
44
|
register("optional.strict.#{name}", self["strict.#{name}"].optional)
|
55
45
|
end
|
56
46
|
|