dry-types 0.13.2 → 1.5.1
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/CHANGELOG.md +763 -233
- data/LICENSE +17 -17
- data/README.md +15 -13
- data/dry-types.gemspec +28 -28
- data/lib/dry-types.rb +3 -1
- data/lib/dry/types.rb +156 -76
- data/lib/dry/types/any.rb +32 -12
- data/lib/dry/types/array.rb +19 -6
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +75 -16
- data/lib/dry/types/builder.rb +131 -15
- data/lib/dry/types/builder_methods.rb +49 -20
- data/lib/dry/types/coercions.rb +76 -22
- data/lib/dry/types/coercions/json.rb +43 -7
- data/lib/dry/types/coercions/params.rb +118 -31
- data/lib/dry/types/compat.rb +0 -2
- data/lib/dry/types/compiler.rb +56 -41
- data/lib/dry/types/constrained.rb +81 -32
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constraints.rb +18 -4
- data/lib/dry/types/constructor.rb +127 -54
- data/lib/dry/types/constructor/function.rb +216 -0
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/container.rb +7 -0
- data/lib/dry/types/core.rb +54 -21
- data/lib/dry/types/decorator.rb +38 -17
- data/lib/dry/types/default.rb +61 -16
- data/lib/dry/types/enum.rb +43 -20
- data/lib/dry/types/errors.rb +75 -9
- data/lib/dry/types/extensions.rb +7 -1
- data/lib/dry/types/extensions/maybe.rb +74 -16
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/fn_container.rb +6 -1
- data/lib/dry/types/hash.rb +86 -67
- data/lib/dry/types/hash/constructor.rb +33 -0
- data/lib/dry/types/inflector.rb +3 -1
- data/lib/dry/types/json.rb +18 -16
- data/lib/dry/types/lax.rb +75 -0
- data/lib/dry/types/map.rb +76 -33
- data/lib/dry/types/meta.rb +51 -0
- data/lib/dry/types/module.rb +120 -0
- data/lib/dry/types/nominal.rb +210 -0
- data/lib/dry/types/options.rb +13 -26
- data/lib/dry/types/params.rb +39 -25
- data/lib/dry/types/predicate_inferrer.rb +238 -0
- data/lib/dry/types/predicate_registry.rb +34 -0
- data/lib/dry/types/primitive_inferrer.rb +97 -0
- data/lib/dry/types/printable.rb +16 -0
- data/lib/dry/types/printer.rb +315 -0
- data/lib/dry/types/result.rb +29 -3
- data/lib/dry/types/schema.rb +408 -0
- data/lib/dry/types/schema/key.rb +156 -0
- data/lib/dry/types/spec/types.rb +103 -33
- data/lib/dry/types/sum.rb +84 -35
- data/lib/dry/types/type.rb +49 -0
- data/lib/dry/types/version.rb +3 -1
- metadata +68 -79
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.travis.yml +0 -29
- data/.yardopts +0 -5
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -24
- data/Rakefile +0 -20
- data/benchmarks/hash_schemas.rb +0 -51
- data/lib/dry/types/compat/form_types.rb +0 -27
- data/lib/dry/types/compat/int.rb +0 -14
- data/lib/dry/types/definition.rb +0 -113
- data/lib/dry/types/hash/schema.rb +0 -199
- data/lib/dry/types/hash/schema_builder.rb +0 -75
- data/lib/dry/types/safe.rb +0 -59
data/CONTRIBUTING.md
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
# Issue Guidelines
|
2
|
-
|
3
|
-
## Reporting bugs
|
4
|
-
|
5
|
-
If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated.
|
6
|
-
|
7
|
-
## Reporting feature requests
|
8
|
-
|
9
|
-
Report a feature request **only after discussing it first on [discourse.dry-rb.org](https://discourse.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed.
|
10
|
-
|
11
|
-
## Reporting questions, support requests, ideas, concerns etc.
|
12
|
-
|
13
|
-
**PLEASE DON'T** - use [discourse.dry-rb.org](https://discourse.dry-rb.org) instead.
|
14
|
-
|
15
|
-
# Pull Request Guidelines
|
16
|
-
|
17
|
-
A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
|
18
|
-
|
19
|
-
Other requirements:
|
20
|
-
|
21
|
-
1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue.
|
22
|
-
2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
|
23
|
-
3) Add API documentation if it's a new feature
|
24
|
-
4) Update API documentation if it changes an existing feature
|
25
|
-
5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides
|
26
|
-
|
27
|
-
# Asking for help
|
28
|
-
|
29
|
-
If these guidelines aren't helpful, and you're stuck, please post a message on [discourse.dry-rb.org](https://discourse.dry-rb.org).
|
data/Gemfile
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
gemspec
|
4
|
-
|
5
|
-
group :test do
|
6
|
-
platform :mri do
|
7
|
-
gem "codeclimate-test-reporter", require: false
|
8
|
-
gem 'simplecov', require: false
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
group :tools do
|
13
|
-
gem 'pry-byebug', platform: :mri
|
14
|
-
gem 'mutant'
|
15
|
-
gem 'mutant-rspec'
|
16
|
-
end
|
17
|
-
|
18
|
-
group :benchmarks do
|
19
|
-
gem 'benchmark-ips'
|
20
|
-
gem 'virtus'
|
21
|
-
gem 'fast_attributes'
|
22
|
-
gem 'attrio'
|
23
|
-
gem 'dry-struct'
|
24
|
-
end
|
data/Rakefile
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
|
-
require "rspec/core/rake_task"
|
3
|
-
|
4
|
-
task :run_specs do
|
5
|
-
require 'rspec/core'
|
6
|
-
|
7
|
-
types_result = RSpec::Core::Runner.run(['spec/dry'])
|
8
|
-
RSpec.clear_examples
|
9
|
-
|
10
|
-
Dry::Types.load_extensions(:maybe)
|
11
|
-
ext_result = RSpec::Core::Runner.run(['spec'])
|
12
|
-
|
13
|
-
exit [types_result, ext_result].max
|
14
|
-
end
|
15
|
-
|
16
|
-
task default: :run_specs
|
17
|
-
|
18
|
-
require 'yard'
|
19
|
-
require 'yard/rake/yardoc_task'
|
20
|
-
YARD::Rake::YardocTask.new(:doc)
|
data/benchmarks/hash_schemas.rb
DELETED
@@ -1,51 +0,0 @@
|
|
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['params.integer'],
|
11
|
-
admin: Dry::Types['params.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
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'dry/core/deprecations'
|
2
|
-
|
3
|
-
Dry::Core::Deprecations.warn('Form types were renamed to Params', tag: :'dry-types')
|
4
|
-
|
5
|
-
module Dry
|
6
|
-
module Types
|
7
|
-
container.keys.grep(/^params\./).each do |key|
|
8
|
-
next if key.start_with?('params.int')
|
9
|
-
register(key.sub('params.', 'form.'), container[key])
|
10
|
-
end
|
11
|
-
|
12
|
-
register('form.int', self['params.integer'])
|
13
|
-
register('form.integer', self['params.integer'])
|
14
|
-
|
15
|
-
class Compiler
|
16
|
-
def visit_form_hash(node)
|
17
|
-
schema, meta = node
|
18
|
-
merge_with('params.hash', :symbolized, schema).meta(meta)
|
19
|
-
end
|
20
|
-
|
21
|
-
def visit_form_array(node)
|
22
|
-
member, meta = node
|
23
|
-
registry['params.array'].of(visit(member)).meta(meta)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/dry/types/compat/int.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'dry/core/deprecations'
|
2
|
-
|
3
|
-
Dry::Core::Deprecations.warn('Int type was renamed to Integer', tag: :'dry-types')
|
4
|
-
|
5
|
-
module Dry
|
6
|
-
module Types
|
7
|
-
register('int', self['integer'])
|
8
|
-
register('strict.int', self['strict.integer'])
|
9
|
-
register('coercible.int', self['coercible.integer'])
|
10
|
-
register('optional.strict.int', self['optional.strict.integer'])
|
11
|
-
register('optional.coercible.int', self['optional.coercible.integer'])
|
12
|
-
register('params.int', self['params.integer'])
|
13
|
-
end
|
14
|
-
end
|
data/lib/dry/types/definition.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
require 'dry/types/builder'
|
2
|
-
require 'dry/types/result'
|
3
|
-
require 'dry/types/options'
|
4
|
-
|
5
|
-
module Dry
|
6
|
-
module Types
|
7
|
-
class Definition
|
8
|
-
include Type
|
9
|
-
include Options
|
10
|
-
include Builder
|
11
|
-
include Dry::Equalizer(:primitive, :options, :meta)
|
12
|
-
|
13
|
-
# @return [Class]
|
14
|
-
attr_reader :primitive
|
15
|
-
|
16
|
-
# @param [Class] primitive
|
17
|
-
# @return [Type]
|
18
|
-
def self.[](primitive)
|
19
|
-
if primitive == ::Array
|
20
|
-
Types::Array
|
21
|
-
elsif primitive == ::Hash
|
22
|
-
Types::Hash
|
23
|
-
else
|
24
|
-
self
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# @param [Type,Class] primitive
|
29
|
-
# @param [Hash] options
|
30
|
-
def initialize(primitive, options = {})
|
31
|
-
super
|
32
|
-
@primitive = primitive
|
33
|
-
freeze
|
34
|
-
end
|
35
|
-
|
36
|
-
# @return [String]
|
37
|
-
def name
|
38
|
-
primitive.name
|
39
|
-
end
|
40
|
-
|
41
|
-
# @return [false]
|
42
|
-
def default?
|
43
|
-
false
|
44
|
-
end
|
45
|
-
|
46
|
-
# @return [false]
|
47
|
-
def constrained?
|
48
|
-
false
|
49
|
-
end
|
50
|
-
|
51
|
-
# @return [false]
|
52
|
-
def optional?
|
53
|
-
false
|
54
|
-
end
|
55
|
-
|
56
|
-
# @param [BasicObject] input
|
57
|
-
# @return [BasicObject]
|
58
|
-
def call(input)
|
59
|
-
input
|
60
|
-
end
|
61
|
-
alias_method :[], :call
|
62
|
-
|
63
|
-
# @param [Object] input
|
64
|
-
# @param [#call,nil] block
|
65
|
-
# @yieldparam [Failure] failure
|
66
|
-
# @yieldreturn [Result]
|
67
|
-
# @return [Result,Logic::Result] when a block is not provided
|
68
|
-
# @return [nil] otherwise
|
69
|
-
def try(input, &block)
|
70
|
-
if valid?(input)
|
71
|
-
success(input)
|
72
|
-
else
|
73
|
-
failure = failure(input, "#{input.inspect} must be an instance of #{primitive}")
|
74
|
-
block ? yield(failure) : failure
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# @param (see Dry::Types::Success#initialize)
|
79
|
-
# @return [Result::Success]
|
80
|
-
def success(input)
|
81
|
-
Result::Success.new(input)
|
82
|
-
end
|
83
|
-
|
84
|
-
# @param (see Failure#initialize)
|
85
|
-
# @return [Result::Failure]
|
86
|
-
def failure(input, error)
|
87
|
-
Result::Failure.new(input, error)
|
88
|
-
end
|
89
|
-
|
90
|
-
# Checks whether value is of a #primitive class
|
91
|
-
# @param [Object] value
|
92
|
-
# @return [Boolean]
|
93
|
-
def primitive?(value)
|
94
|
-
value.is_a?(primitive)
|
95
|
-
end
|
96
|
-
alias_method :valid?, :primitive?
|
97
|
-
alias_method :===, :primitive?
|
98
|
-
|
99
|
-
# Return AST representation of a type definition
|
100
|
-
#
|
101
|
-
# @api public
|
102
|
-
#
|
103
|
-
# @return [Array]
|
104
|
-
def to_ast(meta: true)
|
105
|
-
[:definition, [primitive, meta ? self.meta : EMPTY_HASH]]
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
require 'dry/types/array'
|
112
|
-
require 'dry/types/hash'
|
113
|
-
require 'dry/types/map'
|
@@ -1,199 +0,0 @@
|
|
1
|
-
require 'dry/types/fn_container'
|
2
|
-
|
3
|
-
module Dry
|
4
|
-
module Types
|
5
|
-
class Hash < Definition
|
6
|
-
# The built-in Hash type has constructors that you can use to define
|
7
|
-
# hashes with explicit schemas and coercible values using the built-in types.
|
8
|
-
#
|
9
|
-
# Basic {Schema} evaluates default values for keys missing in input hash
|
10
|
-
# (see {Schema#resolve_missing_value})
|
11
|
-
#
|
12
|
-
# @see Dry::Types::Default#evaluate
|
13
|
-
# @see Dry::Types::Default::Callable#evaluate
|
14
|
-
class Schema < Hash
|
15
|
-
NO_TRANSFORM = Dry::Types::FnContainer.register { |x| x }
|
16
|
-
SYMBOLIZE_KEY = Dry::Types::FnContainer.register(:to_sym.to_proc)
|
17
|
-
|
18
|
-
# @return [Hash{Symbol => Definition}]
|
19
|
-
attr_reader :member_types
|
20
|
-
|
21
|
-
# @return [#call]
|
22
|
-
attr_reader :transform_key
|
23
|
-
|
24
|
-
# @param [Class] _primitive
|
25
|
-
# @param [Hash] options
|
26
|
-
# @option options [Hash{Symbol => Definition}] :member_types
|
27
|
-
# @option options [String] :key_transform_fn
|
28
|
-
def initialize(_primitive, options)
|
29
|
-
@member_types = options.fetch(:member_types)
|
30
|
-
|
31
|
-
meta = options[:meta] || EMPTY_HASH
|
32
|
-
key_fn = meta.fetch(:key_transform_fn, NO_TRANSFORM)
|
33
|
-
|
34
|
-
@transform_key = Dry::Types::FnContainer[key_fn]
|
35
|
-
|
36
|
-
super
|
37
|
-
end
|
38
|
-
|
39
|
-
# @param [Hash] hash
|
40
|
-
# @return [Hash{Symbol => Object}]
|
41
|
-
def call(hash)
|
42
|
-
coerce(hash)
|
43
|
-
end
|
44
|
-
alias_method :[], :call
|
45
|
-
|
46
|
-
# @param [Hash] hash
|
47
|
-
# @yieldparam [Failure] failure
|
48
|
-
# @yieldreturn [Result]
|
49
|
-
# @return [Logic::Result]
|
50
|
-
# @return [Object] if coercion fails and a block is given
|
51
|
-
def try(hash)
|
52
|
-
if hash.is_a?(::Hash)
|
53
|
-
success = true
|
54
|
-
output = {}
|
55
|
-
|
56
|
-
begin
|
57
|
-
result = try_coerce(hash) do |key, member_result|
|
58
|
-
success &&= member_result.success?
|
59
|
-
output[key] = member_result.input
|
60
|
-
|
61
|
-
member_result
|
62
|
-
end
|
63
|
-
rescue ConstraintError, UnknownKeysError, SchemaError => e
|
64
|
-
success = false
|
65
|
-
result = e
|
66
|
-
end
|
67
|
-
else
|
68
|
-
success = false
|
69
|
-
output = hash
|
70
|
-
result = "#{hash} must be a hash"
|
71
|
-
end
|
72
|
-
|
73
|
-
if success
|
74
|
-
success(output)
|
75
|
-
else
|
76
|
-
failure = failure(output, result)
|
77
|
-
block_given? ? yield(failure) : failure
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# @param meta [Boolean] Whether to dump the meta to the AST
|
82
|
-
# @return [Array] An AST representation
|
83
|
-
def to_ast(meta: true)
|
84
|
-
[
|
85
|
-
:hash_schema,
|
86
|
-
[
|
87
|
-
member_types.map { |name, member| [:member, [name, member.to_ast(meta: meta)]] },
|
88
|
-
meta ? self.meta : EMPTY_HASH
|
89
|
-
]
|
90
|
-
]
|
91
|
-
end
|
92
|
-
|
93
|
-
# @param [Hash] hash
|
94
|
-
# @return [Boolean]
|
95
|
-
def valid?(hash)
|
96
|
-
result = try(hash)
|
97
|
-
result.success?
|
98
|
-
end
|
99
|
-
alias_method :===, :valid?
|
100
|
-
|
101
|
-
# Whether the schema rejects unknown keys
|
102
|
-
# @return [Boolean]
|
103
|
-
def strict?
|
104
|
-
meta.fetch(:strict, false)
|
105
|
-
end
|
106
|
-
|
107
|
-
# Make the schema intolerant to unknown keys
|
108
|
-
# @return [Schema]
|
109
|
-
def strict
|
110
|
-
meta(strict: true)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Injects a key transformation function
|
114
|
-
# @param [#call,nil] proc
|
115
|
-
# @param [#call,nil] block
|
116
|
-
# @return [Schema]
|
117
|
-
def with_key_transform(proc = nil, &block)
|
118
|
-
fn = proc || block
|
119
|
-
|
120
|
-
if fn.nil?
|
121
|
-
raise ArgumentError, "a block or callable argument is required"
|
122
|
-
end
|
123
|
-
|
124
|
-
handle = Dry::Types::FnContainer.register(fn)
|
125
|
-
meta(key_transform_fn: handle)
|
126
|
-
end
|
127
|
-
|
128
|
-
# @param [{Symbol => Definition}] type_map
|
129
|
-
# @return [Schema]
|
130
|
-
def schema(type_map)
|
131
|
-
member_types = self.member_types.merge(transform_types(type_map))
|
132
|
-
Schema.new(primitive, **options, member_types: member_types, meta: meta)
|
133
|
-
end
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
def resolve(hash)
|
138
|
-
result = {}
|
139
|
-
|
140
|
-
hash.each do |key, value|
|
141
|
-
k = transform_key.(key)
|
142
|
-
|
143
|
-
if member_types.key?(k)
|
144
|
-
result[k] = yield(member_types[k], k, value)
|
145
|
-
elsif strict?
|
146
|
-
raise UnknownKeysError.new(*unexpected_keys(hash.keys))
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
if result.size < member_types.size
|
151
|
-
resolve_missing_keys(result, &Proc.new)
|
152
|
-
end
|
153
|
-
|
154
|
-
result
|
155
|
-
end
|
156
|
-
|
157
|
-
def resolve_missing_keys(result)
|
158
|
-
member_types.each do |k, type|
|
159
|
-
next if result.key?(k)
|
160
|
-
|
161
|
-
if type.default?
|
162
|
-
result[k] = yield(type, k, Undefined)
|
163
|
-
elsif !type.meta[:omittable]
|
164
|
-
raise MissingKeyError, k
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# @param keys [Array<Symbol>]
|
170
|
-
# @return [Array<Symbol>]
|
171
|
-
def unexpected_keys(keys)
|
172
|
-
keys.map(&transform_key) - member_types.keys
|
173
|
-
end
|
174
|
-
|
175
|
-
# @param [Hash] hash
|
176
|
-
# @return [Hash{Symbol => Object}]
|
177
|
-
def try_coerce(hash)
|
178
|
-
resolve(hash) do |type, key, value|
|
179
|
-
yield(key, type.try(value))
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# @param [Hash] hash
|
184
|
-
# @return [Hash{Symbol => Object}]
|
185
|
-
def coerce(hash)
|
186
|
-
resolve(hash) do |type, key, value|
|
187
|
-
begin
|
188
|
-
type.call(value)
|
189
|
-
rescue ConstraintError => e
|
190
|
-
raise SchemaError.new(key, value, e.result)
|
191
|
-
rescue TypeError, ArgumentError => e
|
192
|
-
raise SchemaError.new(key, value, e.message)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|