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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +763 -233
  3. data/LICENSE +17 -17
  4. data/README.md +15 -13
  5. data/dry-types.gemspec +28 -28
  6. data/lib/dry-types.rb +3 -1
  7. data/lib/dry/types.rb +156 -76
  8. data/lib/dry/types/any.rb +32 -12
  9. data/lib/dry/types/array.rb +19 -6
  10. data/lib/dry/types/array/constructor.rb +32 -0
  11. data/lib/dry/types/array/member.rb +75 -16
  12. data/lib/dry/types/builder.rb +131 -15
  13. data/lib/dry/types/builder_methods.rb +49 -20
  14. data/lib/dry/types/coercions.rb +76 -22
  15. data/lib/dry/types/coercions/json.rb +43 -7
  16. data/lib/dry/types/coercions/params.rb +118 -31
  17. data/lib/dry/types/compat.rb +0 -2
  18. data/lib/dry/types/compiler.rb +56 -41
  19. data/lib/dry/types/constrained.rb +81 -32
  20. data/lib/dry/types/constrained/coercible.rb +36 -6
  21. data/lib/dry/types/constraints.rb +18 -4
  22. data/lib/dry/types/constructor.rb +127 -54
  23. data/lib/dry/types/constructor/function.rb +216 -0
  24. data/lib/dry/types/constructor/wrapper.rb +94 -0
  25. data/lib/dry/types/container.rb +7 -0
  26. data/lib/dry/types/core.rb +54 -21
  27. data/lib/dry/types/decorator.rb +38 -17
  28. data/lib/dry/types/default.rb +61 -16
  29. data/lib/dry/types/enum.rb +43 -20
  30. data/lib/dry/types/errors.rb +75 -9
  31. data/lib/dry/types/extensions.rb +7 -1
  32. data/lib/dry/types/extensions/maybe.rb +74 -16
  33. data/lib/dry/types/extensions/monads.rb +29 -0
  34. data/lib/dry/types/fn_container.rb +6 -1
  35. data/lib/dry/types/hash.rb +86 -67
  36. data/lib/dry/types/hash/constructor.rb +33 -0
  37. data/lib/dry/types/inflector.rb +3 -1
  38. data/lib/dry/types/json.rb +18 -16
  39. data/lib/dry/types/lax.rb +75 -0
  40. data/lib/dry/types/map.rb +76 -33
  41. data/lib/dry/types/meta.rb +51 -0
  42. data/lib/dry/types/module.rb +120 -0
  43. data/lib/dry/types/nominal.rb +210 -0
  44. data/lib/dry/types/options.rb +13 -26
  45. data/lib/dry/types/params.rb +39 -25
  46. data/lib/dry/types/predicate_inferrer.rb +238 -0
  47. data/lib/dry/types/predicate_registry.rb +34 -0
  48. data/lib/dry/types/primitive_inferrer.rb +97 -0
  49. data/lib/dry/types/printable.rb +16 -0
  50. data/lib/dry/types/printer.rb +315 -0
  51. data/lib/dry/types/result.rb +29 -3
  52. data/lib/dry/types/schema.rb +408 -0
  53. data/lib/dry/types/schema/key.rb +156 -0
  54. data/lib/dry/types/spec/types.rb +103 -33
  55. data/lib/dry/types/sum.rb +84 -35
  56. data/lib/dry/types/type.rb +49 -0
  57. data/lib/dry/types/version.rb +3 -1
  58. metadata +68 -79
  59. data/.gitignore +0 -10
  60. data/.rspec +0 -2
  61. data/.travis.yml +0 -29
  62. data/.yardopts +0 -5
  63. data/CONTRIBUTING.md +0 -29
  64. data/Gemfile +0 -24
  65. data/Rakefile +0 -20
  66. data/benchmarks/hash_schemas.rb +0 -51
  67. data/lib/dry/types/compat/form_types.rb +0 -27
  68. data/lib/dry/types/compat/int.rb +0 -14
  69. data/lib/dry/types/definition.rb +0 -113
  70. data/lib/dry/types/hash/schema.rb +0 -199
  71. data/lib/dry/types/hash/schema_builder.rb +0 -75
  72. 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)
@@ -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
@@ -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
@@ -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