dry-types 0.9.0 → 0.15.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.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +43 -0
  5. data/.travis.yml +15 -14
  6. data/.yardopts +5 -0
  7. data/CHANGELOG.md +494 -88
  8. data/CONTRIBUTING.md +29 -0
  9. data/Gemfile +7 -6
  10. data/README.md +1 -3
  11. data/Rakefile +8 -3
  12. data/benchmarks/hash_schemas.rb +7 -7
  13. data/dry-types.gemspec +11 -9
  14. data/lib/dry/types/any.rb +36 -0
  15. data/lib/dry/types/array/member.rb +29 -4
  16. data/lib/dry/types/array.rb +6 -4
  17. data/lib/dry/types/builder.rb +48 -6
  18. data/lib/dry/types/builder_methods.rb +111 -0
  19. data/lib/dry/types/coercions/json.rb +3 -0
  20. data/lib/dry/types/coercions/{form.rb → params.rb} +23 -3
  21. data/lib/dry/types/coercions.rb +16 -3
  22. data/lib/dry/types/compat.rb +0 -0
  23. data/lib/dry/types/compiler.rb +66 -39
  24. data/lib/dry/types/constrained/coercible.rb +7 -1
  25. data/lib/dry/types/constrained.rb +42 -3
  26. data/lib/dry/types/constraints.rb +3 -0
  27. data/lib/dry/types/constructor.rb +98 -16
  28. data/lib/dry/types/container.rb +2 -0
  29. data/lib/dry/types/core.rb +30 -14
  30. data/lib/dry/types/decorator.rb +31 -5
  31. data/lib/dry/types/default.rb +34 -8
  32. data/lib/dry/types/enum.rb +71 -14
  33. data/lib/dry/types/errors.rb +23 -6
  34. data/lib/dry/types/extensions/maybe.rb +35 -16
  35. data/lib/dry/types/fn_container.rb +34 -0
  36. data/lib/dry/types/hash/constructor.rb +20 -0
  37. data/lib/dry/types/hash.rb +103 -23
  38. data/lib/dry/types/inflector.rb +7 -0
  39. data/lib/dry/types/json.rb +7 -7
  40. data/lib/dry/types/map.rb +98 -0
  41. data/lib/dry/types/module.rb +115 -0
  42. data/lib/dry/types/nominal.rb +119 -0
  43. data/lib/dry/types/options.rb +29 -7
  44. data/lib/dry/types/params.rb +53 -0
  45. data/lib/dry/types/printable.rb +12 -0
  46. data/lib/dry/types/printer.rb +309 -0
  47. data/lib/dry/types/result.rb +12 -2
  48. data/lib/dry/types/safe.rb +27 -1
  49. data/lib/dry/types/schema/key.rb +130 -0
  50. data/lib/dry/types/schema.rb +298 -0
  51. data/lib/dry/types/spec/types.rb +102 -0
  52. data/lib/dry/types/sum.rb +75 -13
  53. data/lib/dry/types/type.rb +6 -0
  54. data/lib/dry/types/version.rb +1 -1
  55. data/lib/dry/types.rb +104 -38
  56. data/log/.gitkeep +0 -0
  57. metadata +81 -50
  58. data/lib/dry/types/definition.rb +0 -79
  59. data/lib/dry/types/form.rb +0 -53
  60. data/lib/dry/types/hash/schema.rb +0 -156
  61. data/lib/spec/dry/types.rb +0 -56
@@ -0,0 +1,115 @@
1
+ require 'dry/core/deprecations'
2
+ require 'dry/types/builder_methods'
3
+
4
+ module Dry
5
+ module Types
6
+ # Export types registered in a container as module constants.
7
+ # @example
8
+ # module Types
9
+ # include Dry::Types.module(:strict, :coercible, :nominal, default: :strict)
10
+ # end
11
+ # # Types.constants
12
+ # # => [:Class, :Strict, :Symbol, :Integer, :Float, :String, :Array, :Hash,
13
+ # # :Decimal, :Nil, :True, :False, :Bool, :Date, :Nominal, :DateTime, :Range,
14
+ # # :Coercible, :Time]
15
+ class Module < ::Module
16
+ def initialize(registry, *args)
17
+ @registry = registry
18
+ check_parameters(*args)
19
+ constants = type_constants(*args)
20
+ define_constants(constants)
21
+ extend(BuilderMethods)
22
+
23
+ if constants.key?(:Nominal)
24
+ singleton_class.send(:define_method, :included) do |base|
25
+ super(base)
26
+ base.instance_exec(const_get(:Nominal, false)) do |nominal|
27
+ extend Dry::Core::Deprecations[:'dry-types']
28
+ const_set(:Definition, nominal)
29
+ deprecate_constant(:Definition, message: "Nominal")
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ # @api private
36
+ def type_constants(*namespaces, default: Undefined, **aliases)
37
+ if namespaces.empty? && aliases.empty? && Undefined.equal?(default)
38
+ default_ns = :Strict
39
+ elsif Undefined.equal?(default)
40
+ default_ns = Undefined
41
+ else
42
+ default_ns = Inflector.camelize(default).to_sym
43
+ end
44
+
45
+ tree = registry_tree
46
+
47
+ if namespaces.empty? && aliases.empty?
48
+ modules = tree.select { |_, v| v.is_a?(::Hash) }.map(&:first)
49
+ else
50
+ modules = (namespaces + aliases.keys).map { |n| Inflector.camelize(n).to_sym }
51
+ end
52
+
53
+ tree.each_with_object({}) do |(key, value), constants|
54
+ if modules.include?(key)
55
+ name = aliases.fetch(Inflector.underscore(key).to_sym, key)
56
+ constants[name] = value
57
+ end
58
+
59
+ constants.update(value) if key == default_ns
60
+ end
61
+ end
62
+
63
+ # @api private
64
+ def registry_tree
65
+ @registry_tree ||= @registry.keys.each_with_object({}) { |key, tree|
66
+ type = @registry[key]
67
+ *modules, const_name = key.split('.').map { |part|
68
+ Inflector.camelize(part).to_sym
69
+ }
70
+ next if modules.empty?
71
+
72
+ modules.reduce(tree) { |br, name| br[name] ||= {} }[const_name] = type
73
+ }.freeze
74
+ end
75
+
76
+ private
77
+
78
+ # @api private
79
+ def check_parameters(*namespaces, default: Undefined, **aliases)
80
+ referenced = namespaces.dup
81
+ referenced << default unless false.equal?(default) || Undefined.equal?(default)
82
+ referenced.concat(aliases.keys)
83
+
84
+ known = @registry.keys.map { |k|
85
+ ns, *path = k.split('.')
86
+ ns.to_sym unless path.empty?
87
+ }.compact.uniq
88
+
89
+ (referenced.uniq - known).each do |name|
90
+ raise ArgumentError,
91
+ "#{ name.inspect } is not a known type namespace. "\
92
+ "Supported options are #{ known.map(&:inspect).join(', ') }"
93
+ end
94
+ end
95
+
96
+ # @api private
97
+ def define_constants(constants, mod = self)
98
+ constants.each do |name, value|
99
+ case value
100
+ when ::Hash
101
+ if mod.const_defined?(name, false)
102
+ define_constants(value, mod.const_get(name, false))
103
+ else
104
+ m = ::Module.new
105
+ mod.const_set(name, m)
106
+ define_constants(value, m)
107
+ end
108
+ else
109
+ mod.const_set(name, value)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,119 @@
1
+ require 'dry/core/deprecations'
2
+ require 'dry/types/builder'
3
+ require 'dry/types/result'
4
+ require 'dry/types/options'
5
+
6
+ module Dry
7
+ module Types
8
+ class Nominal
9
+ include Type
10
+ include Options
11
+ include Builder
12
+ include Printable
13
+ include Dry::Equalizer(:primitive, :options, :meta, inspect: false)
14
+
15
+ # @return [Class]
16
+ attr_reader :primitive
17
+
18
+ # @param [Class] primitive
19
+ # @return [Type]
20
+ def self.[](primitive)
21
+ if primitive == ::Array
22
+ Types::Array
23
+ elsif primitive == ::Hash
24
+ Types::Hash
25
+ else
26
+ self
27
+ end
28
+ end
29
+
30
+ # @param [Type,Class] primitive
31
+ # @param [Hash] options
32
+ def initialize(primitive, **options)
33
+ super
34
+ @primitive = primitive
35
+ freeze
36
+ end
37
+
38
+ # @return [String]
39
+ def name
40
+ primitive.name
41
+ end
42
+
43
+ # @return [false]
44
+ def default?
45
+ false
46
+ end
47
+
48
+ # @return [false]
49
+ def constrained?
50
+ false
51
+ end
52
+
53
+ # @return [false]
54
+ def optional?
55
+ false
56
+ end
57
+
58
+ # @param [BasicObject] input
59
+ # @return [BasicObject]
60
+ def call(input)
61
+ input
62
+ end
63
+ alias_method :[], :call
64
+
65
+ # @param [Object] input
66
+ # @param [#call,nil] block
67
+ # @yieldparam [Failure] failure
68
+ # @yieldreturn [Result]
69
+ # @return [Result,Logic::Result] when a block is not provided
70
+ # @return [nil] otherwise
71
+ def try(input, &block)
72
+ if valid?(input)
73
+ success(input)
74
+ else
75
+ failure = failure(input, "#{input.inspect} must be an instance of #{primitive}")
76
+ block ? yield(failure) : failure
77
+ end
78
+ end
79
+
80
+ # @param (see Dry::Types::Success#initialize)
81
+ # @return [Result::Success]
82
+ def success(input)
83
+ Result::Success.new(input)
84
+ end
85
+
86
+ # @param (see Failure#initialize)
87
+ # @return [Result::Failure]
88
+ def failure(input, error)
89
+ Result::Failure.new(input, error)
90
+ end
91
+
92
+ # Checks whether value is of a #primitive class
93
+ # @param [Object] value
94
+ # @return [Boolean]
95
+ def primitive?(value)
96
+ value.is_a?(primitive)
97
+ end
98
+ alias_method :valid?, :primitive?
99
+ alias_method :===, :primitive?
100
+
101
+ # Return AST representation of a type nominal
102
+ #
103
+ # @api public
104
+ #
105
+ # @return [Array]
106
+ def to_ast(meta: true)
107
+ [:nominal, [primitive, meta ? self.meta : EMPTY_HASH]]
108
+ end
109
+ end
110
+
111
+ extend Dry::Core::Deprecations[:'dry-types']
112
+ Definition = Nominal
113
+ deprecate_constant(:Definition, message: "Nominal")
114
+ end
115
+ end
116
+
117
+ require 'dry/types/array'
118
+ require 'dry/types/hash'
119
+ require 'dry/types/map'
@@ -1,20 +1,42 @@
1
1
  module Dry
2
2
  module Types
3
3
  module Options
4
+ # @return [Hash]
4
5
  attr_reader :options
5
6
 
6
- def initialize(*args, **options)
7
- @__args__ = args
8
- @options = options
9
- @meta = options.fetch(:meta, {})
7
+ # @see Nominal#initialize
8
+ def initialize(*args, meta: EMPTY_HASH, **options)
9
+ @__args__ = args.freeze
10
+ @options = options.freeze
11
+ @meta = meta.freeze
10
12
  end
11
13
 
12
- def with(new_options)
13
- self.class.new(*@__args__, options.merge(new_options))
14
+ # @param [Hash] new_options
15
+ # @return [Type]
16
+ def with(**new_options)
17
+ self.class.new(*@__args__, **options, meta: @meta, **new_options)
14
18
  end
15
19
 
20
+ # @overload meta
21
+ # @return [Hash] metadata associated with type
22
+ #
23
+ # @overload meta(data)
24
+ # @param [Hash] new metadata to merge into existing metadata
25
+ # @return [Type] new type with added metadata
16
26
  def meta(data = nil)
17
- data ? with(meta: @meta.merge(data)) : @meta
27
+ if !data
28
+ @meta
29
+ elsif data.empty?
30
+ self
31
+ else
32
+ with(meta: @meta.merge(data))
33
+ end
34
+ end
35
+
36
+ # Resets meta
37
+ # @return [Dry::Types::Type]
38
+ def pristine
39
+ with(meta: EMPTY_HASH)
18
40
  end
19
41
  end
20
42
  end
@@ -0,0 +1,53 @@
1
+ require 'dry/types/coercions/params'
2
+
3
+ module Dry
4
+ module Types
5
+ register('params.nil') do
6
+ self['nominal.nil'].constructor(Coercions::Params.method(:to_nil))
7
+ end
8
+
9
+ register('params.date') do
10
+ self['nominal.date'].constructor(Coercions::Params.method(:to_date))
11
+ end
12
+
13
+ register('params.date_time') do
14
+ self['nominal.date_time'].constructor(Coercions::Params.method(:to_date_time))
15
+ end
16
+
17
+ register('params.time') do
18
+ self['nominal.time'].constructor(Coercions::Params.method(:to_time))
19
+ end
20
+
21
+ register('params.true') do
22
+ self['nominal.true'].constructor(Coercions::Params.method(:to_true))
23
+ end
24
+
25
+ register('params.false') do
26
+ self['nominal.false'].constructor(Coercions::Params.method(:to_false))
27
+ end
28
+
29
+ register('params.bool') do
30
+ (self['params.true'] | self['params.false']).safe
31
+ end
32
+
33
+ register('params.integer') do
34
+ self['nominal.integer'].constructor(Coercions::Params.method(:to_int))
35
+ end
36
+
37
+ register('params.float') do
38
+ self['nominal.float'].constructor(Coercions::Params.method(:to_float))
39
+ end
40
+
41
+ register('params.decimal') do
42
+ self['nominal.decimal'].constructor(Coercions::Params.method(:to_decimal))
43
+ end
44
+
45
+ register('params.array') do
46
+ self['nominal.array'].constructor(Coercions::Params.method(:to_ary)).safe
47
+ end
48
+
49
+ register('params.hash') do
50
+ self['nominal.hash'].constructor(Coercions::Params.method(:to_hash)).safe
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,12 @@
1
+ module Dry
2
+ module Types
3
+ module Printable
4
+ # @return [String]
5
+ # @api public
6
+ def to_s
7
+ PRINTER.(self) { super }
8
+ end
9
+ alias_method :inspect, :to_s
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,309 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # @api private
6
+ class Printer
7
+ MAPPING = {
8
+ Nominal => :visit_nominal,
9
+ Constructor => :visit_constructor,
10
+ Hash::Constructor => :visit_constructor,
11
+ Constrained => :visit_constrained,
12
+ Constrained::Coercible => :visit_constrained,
13
+ Hash => :visit_hash,
14
+ Schema => :visit_schema,
15
+ Schema::Key => :visit_key,
16
+ Map => :visit_map,
17
+ Array => :visit_array,
18
+ Array::Member => :visit_array_member,
19
+ Safe => :visit_safe,
20
+ Enum => :visit_enum,
21
+ Default => :visit_default,
22
+ Default::Callable => :visit_default,
23
+ Sum => :visit_sum,
24
+ Sum::Constrained => :visit_sum,
25
+ Any.class => :visit_any
26
+ }
27
+
28
+ def call(type)
29
+ output = "".dup
30
+ visit(type) { |str| output << str }
31
+ "#<Dry::Types[#{ output }]>"
32
+ end
33
+
34
+ def visit(type, &block)
35
+ print_with = MAPPING.fetch(type.class) do
36
+ if type.is_a?(Type)
37
+ return yield type.inspect
38
+ else
39
+ raise ArgumentError, "Do not know how to print #{ type.class }"
40
+ end
41
+ end
42
+ send(print_with, type, &block)
43
+ end
44
+
45
+ def visit_any(_)
46
+ yield "Any"
47
+ end
48
+
49
+ def visit_array(_)
50
+ yield "Array"
51
+ end
52
+
53
+ def visit_array_member(array)
54
+ visit(array.member) do |type|
55
+ yield "Array<#{ type }>"
56
+ end
57
+ end
58
+
59
+ def visit_constructor(constructor)
60
+ visit(constructor.type) do |type|
61
+ visit_callable(constructor.fn) do |fn|
62
+ options = constructor.options.dup
63
+ options.delete(:fn)
64
+
65
+ visit_options(options, constructor.meta) do |opts|
66
+ yield "Constructor<#{ type } fn=#{ fn }#{ opts }>"
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def visit_constrained(constrained)
73
+ visit(constrained.type) do |type|
74
+ options = constrained.options.dup
75
+ rule = options.delete(:rule)
76
+
77
+ visit_options(options, constrained.meta) do |opts|
78
+ yield "Constrained<#{ type } rule=[#{ rule.to_s }]>"
79
+ end
80
+ end
81
+ end
82
+
83
+ def visit_schema(schema)
84
+ options = schema.options.dup
85
+ size = schema.count
86
+ key_fn_str = ""
87
+ type_fn_str = ""
88
+ strict_str = ""
89
+
90
+ strict_str = "strict " if options.delete(:strict)
91
+
92
+ if key_fn = options.delete(:key_transform_fn)
93
+ visit_callable(key_fn) do |fn|
94
+ key_fn_str = "key_fn=#{ fn } "
95
+ end
96
+ end
97
+
98
+ if type_fn = options.delete(:type_transform_fn)
99
+ visit_callable(type_fn) do |fn|
100
+ type_fn_str = "type_fn=#{ fn } "
101
+ end
102
+ end
103
+
104
+ keys = options.delete(:keys)
105
+
106
+ visit_options(options, schema.meta) do |opts|
107
+ schema_parameters = "#{ key_fn_str }#{ type_fn_str }#{ strict_str }#{ opts }"
108
+
109
+ header = "Schema<#{ schema_parameters }keys={"
110
+
111
+ if size.zero?
112
+ yield "#{ header}}>"
113
+ else
114
+ yield header.dup << keys.map { |key|
115
+ visit(key) { |type| type }
116
+ }.join(" ") << "}>"
117
+ end
118
+ end
119
+ end
120
+
121
+ def visit_map(map)
122
+ visit(map.key_type) do |key|
123
+ visit(map.value_type) do |value|
124
+ options = map.options.dup
125
+ options.delete(:key_type)
126
+ options.delete(:value_type)
127
+
128
+ visit_options(options, map.meta) do |opts|
129
+ yield "Map<#{ key } => #{ value }>"
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def visit_key(key)
136
+ visit(key.type) do |type|
137
+ if key.required?
138
+ yield "#{ key.name }: #{ type }"
139
+ else
140
+ yield "#{ key.name }?: #{ type }"
141
+ end
142
+ end
143
+ end
144
+
145
+ def visit_sum(sum)
146
+ visit_sum_constructors(sum) do |constructors|
147
+ visit_options(sum.options, sum.meta) do |opts|
148
+ yield "Sum<#{ constructors }#{ opts }>"
149
+ end
150
+ end
151
+ end
152
+
153
+ def visit_sum_constructors(sum)
154
+ case sum.left
155
+ when Sum
156
+ visit_sum_constructors(sum.left) do |left|
157
+ case sum.right
158
+ when Sum
159
+ visit_sum_constructors(sum.right) do |right|
160
+ yield "#{ left } | #{ right }"
161
+ end
162
+ else
163
+ visit(sum.right) do |right|
164
+ yield "#{ left } | #{ right }"
165
+ end
166
+ end
167
+ end
168
+ else
169
+ visit(sum.left) do |left|
170
+ case sum.right
171
+ when Sum
172
+ visit_sum_constructors(sum.right) do |right|
173
+ yield "#{ left } | #{ right }"
174
+ end
175
+ else
176
+ visit(sum.right) do |right|
177
+ yield "#{ left } | #{ right }"
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ def visit_enum(enum)
185
+ visit(enum.type) do |type|
186
+ options = enum.options.dup
187
+ mapping = options.delete(:mapping)
188
+
189
+ visit_options(options, enum.meta) do |opts|
190
+ if mapping == enum.inverted_mapping
191
+ values = mapping.values.map(&:inspect).join(", ")
192
+ yield "Enum<#{ type } values={#{ values }}#{ opts }>"
193
+ else
194
+ mapping_str = mapping.map { |key, value|
195
+ "#{ key.inspect }=>#{ value.inspect }"
196
+ }.join(", ")
197
+ yield "Enum<#{ type } mapping={#{ mapping_str }}#{ opts }>"
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ def visit_default(default)
204
+ visit(default.type) do |type|
205
+ visit_options(default.options, default.meta) do |opts|
206
+ if default.is_a?(Default::Callable)
207
+ visit_callable(default.value) do |fn|
208
+ yield "Default<#{ type } value_fn=#{ fn }#{ opts }>"
209
+ end
210
+ else
211
+ yield "Default<#{ type } value=#{ default.value.inspect }#{ opts }>"
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ def visit_nominal(type)
218
+ visit_options(type.options, type.meta) do |opts|
219
+ yield "Nominal<#{ type.primitive }#{ opts }>"
220
+ end
221
+ end
222
+
223
+ def visit_safe(safe)
224
+ visit(safe.type) do |type|
225
+ yield "Safe<#{ type }>"
226
+ end
227
+ end
228
+
229
+ def visit_hash(hash)
230
+ options = hash.options.dup
231
+ type_fn_str = ""
232
+
233
+ if type_fn = options.delete(:type_transform_fn)
234
+ visit_callable(type_fn) do |fn|
235
+ type_fn_str = "type_fn=#{ fn }"
236
+ end
237
+ end
238
+
239
+ visit_options(options, hash.meta) do |opts|
240
+ if opts.empty? && type_fn_str.empty?
241
+ yield "Hash"
242
+ else
243
+ yield "Hash<#{ type_fn_str }#{ opts }>"
244
+ end
245
+ end
246
+ end
247
+
248
+ def visit_callable(callable)
249
+ fn = callable.is_a?(String) ? FnContainer[callable] : callable
250
+
251
+ case fn
252
+ when Method
253
+ yield "#{ fn.receiver }.#{ fn.name }"
254
+ when Proc
255
+ path, line = fn.source_location
256
+
257
+ if line && line.zero?
258
+ yield ".#{ path }"
259
+ elsif path
260
+ yield "#{ path.sub(Dir.pwd + "/", EMPTY_STRING) }:#{ line }"
261
+ elsif fn.lambda?
262
+ yield "(lambda)"
263
+ else
264
+ match = fn.to_s.match(/\A#<Proc:0x\h+\(&:(\w+)\)>\z/)
265
+
266
+ if match
267
+ yield ".#{ match[1] }"
268
+ else
269
+ yield "(proc)"
270
+ end
271
+ end
272
+ else
273
+ call = fn.method(:call)
274
+
275
+ if call.owner == fn.class
276
+ yield "#{ fn.class.to_s }#call"
277
+ else
278
+ yield "#{ fn.to_s }.call"
279
+ end
280
+ end
281
+ end
282
+
283
+ def visit_options(options, meta)
284
+ if options.empty? && meta.empty?
285
+ yield ""
286
+ else
287
+ opts = options.empty? ? "" : " options=#{ options.inspect }"
288
+
289
+ if meta.empty?
290
+ yield opts
291
+ else
292
+ values = meta.map do |key, value|
293
+ case key
294
+ when Symbol
295
+ "#{ key }: #{ value.inspect }"
296
+ else
297
+ "#{ key.inspect }=>#{ value.inspect }"
298
+ end
299
+ end
300
+
301
+ yield "#{ opts } meta={#{ values.join(", ") }}"
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ PRINTER = Printer.new.freeze
308
+ end
309
+ end
@@ -3,42 +3,52 @@ require 'dry/equalizer'
3
3
  module Dry
4
4
  module Types
5
5
  class Result
6
- include Dry::Equalizer(:input)
6
+ include Dry::Equalizer(:input, inspect: false)
7
7
 
8
+ # @return [Object]
8
9
  attr_reader :input
9
10
 
11
+ # @param [Object] input
10
12
  def initialize(input)
11
13
  @input = input
12
14
  end
13
15
 
14
16
  class Success < Result
17
+ # @return [true]
15
18
  def success?
16
19
  true
17
20
  end
18
21
 
22
+ # @return [false]
19
23
  def failure?
20
24
  false
21
25
  end
22
26
  end
23
27
 
24
28
  class Failure < Result
25
- include Dry::Equalizer(:input, :error)
29
+ include Dry::Equalizer(:input, :error, inspect: false)
26
30
 
31
+ # @return [#to_s]
27
32
  attr_reader :error
28
33
 
34
+ # @param [Object] input
35
+ # @param [#to_s] error
29
36
  def initialize(input, error)
30
37
  super(input)
31
38
  @error = error
32
39
  end
33
40
 
41
+ # @return [String]
34
42
  def to_s
35
43
  error.to_s
36
44
  end
37
45
 
46
+ # @return [false]
38
47
  def success?
39
48
  false
40
49
  end
41
50
 
51
+ # @return [true]
42
52
  def failure?
43
53
  true
44
54
  end