teckel 0.1.0 → 0.6.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/CHANGELOG.md +114 -0
- data/LICENSE_LOGO +4 -0
- data/README.md +22 -14
- data/lib/teckel.rb +11 -2
- data/lib/teckel/chain.rb +47 -152
- data/lib/teckel/chain/config.rb +246 -0
- data/lib/teckel/chain/result.rb +38 -0
- data/lib/teckel/chain/runner.rb +62 -0
- data/lib/teckel/chain/step.rb +18 -0
- data/lib/teckel/config.rb +41 -52
- data/lib/teckel/contracts.rb +19 -0
- data/lib/teckel/operation.rb +108 -253
- data/lib/teckel/operation/config.rb +396 -0
- data/lib/teckel/operation/result.rb +92 -0
- data/lib/teckel/operation/runner.rb +75 -0
- data/lib/teckel/result.rb +52 -53
- data/lib/teckel/version.rb +1 -1
- data/spec/chain/default_settings_spec.rb +39 -0
- data/spec/chain/inheritance_spec.rb +116 -0
- data/spec/chain/none_input_spec.rb +36 -0
- data/spec/chain/results_spec.rb +53 -0
- data/spec/chain_around_hook_spec.rb +100 -0
- data/spec/chain_spec.rb +180 -0
- data/spec/config_spec.rb +26 -0
- data/spec/doctest_helper.rb +7 -0
- data/spec/operation/contract_trace_spec.rb +116 -0
- data/spec/operation/default_settings_spec.rb +94 -0
- data/spec/operation/inheritance_spec.rb +94 -0
- data/spec/operation/result_spec.rb +34 -0
- data/spec/operation/results_spec.rb +117 -0
- data/spec/operation_spec.rb +483 -0
- data/spec/rb27/pattern_matching_spec.rb +193 -0
- data/spec/result_spec.rb +22 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/dry_base.rb +8 -0
- data/spec/support/fake_db.rb +12 -0
- data/spec/support/fake_models.rb +20 -0
- data/spec/teckel_spec.rb +7 -0
- metadata +64 -46
- data/.github/workflows/ci.yml +0 -67
- data/.github/workflows/pages.yml +0 -50
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -12
- data/.ruby-version +0 -1
- data/DEVELOPMENT.md +0 -28
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -71
- data/Rakefile +0 -30
- data/bin/console +0 -15
- data/bin/rake +0 -29
- data/bin/rspec +0 -29
- data/bin/rubocop +0 -18
- data/bin/setup +0 -8
- data/lib/teckel/operation/results.rb +0 -71
- data/teckel.gemspec +0 -33
@@ -0,0 +1,396 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Teckel
|
4
|
+
module Operation
|
5
|
+
module Config
|
6
|
+
# @overload input()
|
7
|
+
# Get the configured class wrapping the input data structure.
|
8
|
+
# @return [Class] The +input+ class
|
9
|
+
# @overload input(klass)
|
10
|
+
# Set the class wrapping the input data structure.
|
11
|
+
# @param klass [Class] The +input+ class
|
12
|
+
# @return [Class] The +input+ class
|
13
|
+
def input(klass = nil)
|
14
|
+
@config.for(:input, klass) { self::Input if const_defined?(:Input) } ||
|
15
|
+
raise(Teckel::MissingConfigError, "Missing input config for #{self}")
|
16
|
+
end
|
17
|
+
|
18
|
+
# @overload input_constructor()
|
19
|
+
# The callable constructor to build an instance of the +input+ class.
|
20
|
+
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
21
|
+
# @return [Proc] A callable that will return an instance of the +input+ class.
|
22
|
+
#
|
23
|
+
# @overload input_constructor(sym_or_proc)
|
24
|
+
# Define how to build the +input+.
|
25
|
+
# @param sym_or_proc [Symbol, #call]
|
26
|
+
# - Either a +Symbol+ representing the _public_ method to call on the +input+ class.
|
27
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
28
|
+
# @return [#call] The callable constructor
|
29
|
+
#
|
30
|
+
# @example simple symbol to method constructor
|
31
|
+
# class MyOperation
|
32
|
+
# include Teckel::Operation
|
33
|
+
#
|
34
|
+
# class Input
|
35
|
+
# def initialize(name:, age:); end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # If you need more control over how to build a new +Input+ instance
|
39
|
+
# # MyOperation.call(name: "Bob", age: 23) # -> Input.new(name: "Bob", age: 23)
|
40
|
+
# input_constructor :new
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# MyOperation.input_constructor.is_a?(Method) #=> true
|
44
|
+
#
|
45
|
+
# @example Custom Proc constructor
|
46
|
+
# class MyOperation
|
47
|
+
# include Teckel::Operation
|
48
|
+
#
|
49
|
+
# class Input
|
50
|
+
# def initialize(*args, **opts); end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # If you need more control over how to build a new +Input+ instance
|
54
|
+
# # MyOperation.call("foo", opt: "bar") # -> Input.new(name: "foo", opt: "bar")
|
55
|
+
# input_constructor ->(name, options) { Input.new(name: name, **options) }
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# MyOperation.input_constructor.is_a?(Proc) #=> true
|
59
|
+
def input_constructor(sym_or_proc = nil)
|
60
|
+
get_set_constructor(:input_constructor, input, sym_or_proc) ||
|
61
|
+
raise(MissingConfigError, "Missing input_constructor config for #{self}")
|
62
|
+
end
|
63
|
+
|
64
|
+
# @overload output()
|
65
|
+
# Get the configured class wrapping the output data structure.
|
66
|
+
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
67
|
+
# @return [Class] The +output+ class
|
68
|
+
#
|
69
|
+
# @overload output(klass)
|
70
|
+
# Set the class wrapping the output data structure.
|
71
|
+
# @param klass [Class] The +output+ class
|
72
|
+
# @return [Class] The +output+ class
|
73
|
+
def output(klass = nil)
|
74
|
+
@config.for(:output, klass) { self::Output if const_defined?(:Output) } ||
|
75
|
+
raise(Teckel::MissingConfigError, "Missing output config for #{self}")
|
76
|
+
end
|
77
|
+
|
78
|
+
# @overload output_constructor()
|
79
|
+
# The callable constructor to build an instance of the +output+ class.
|
80
|
+
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
81
|
+
# @return [Proc] A callable that will return an instance of +output+ class.
|
82
|
+
#
|
83
|
+
# @overload output_constructor(sym_or_proc)
|
84
|
+
# Define how to build the +output+.
|
85
|
+
# @param sym_or_proc [Symbol, #call]
|
86
|
+
# - Either a +Symbol+ representing the _public_ method to call on the +output+ class.
|
87
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
88
|
+
# @return [#call] The callable constructor
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# class MyOperation
|
92
|
+
# include Teckel::Operation
|
93
|
+
#
|
94
|
+
# class Output
|
95
|
+
# def initialize(*args, **opts); end
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# # MyOperation.call("foo", "bar") # -> Output.new("foo", "bar")
|
99
|
+
# output_constructor :new
|
100
|
+
#
|
101
|
+
# # If you need more control over how to build a new +Output+ instance
|
102
|
+
# # MyOperation.call("foo", opt: "bar") # -> Output.new(name: "foo", opt: "bar")
|
103
|
+
# output_constructor ->(name, options) { Output.new(name: name, **options) }
|
104
|
+
# end
|
105
|
+
def output_constructor(sym_or_proc = nil)
|
106
|
+
get_set_constructor(:output_constructor, output, sym_or_proc) ||
|
107
|
+
raise(MissingConfigError, "Missing output_constructor config for #{self}")
|
108
|
+
end
|
109
|
+
|
110
|
+
# @overload error()
|
111
|
+
# Get the configured class wrapping the error data structure.
|
112
|
+
# @return [Class] The +error+ class
|
113
|
+
#
|
114
|
+
# @overload error(klass)
|
115
|
+
# Set the class wrapping the error data structure.
|
116
|
+
# @param klass [Class] The +error+ class
|
117
|
+
# @return [Class,nil] The +error+ class or +nil+ if it does not error
|
118
|
+
def error(klass = nil)
|
119
|
+
@config.for(:error, klass) { self::Error if const_defined?(:Error) } ||
|
120
|
+
raise(Teckel::MissingConfigError, "Missing error config for #{self}")
|
121
|
+
end
|
122
|
+
|
123
|
+
# @overload error_constructor()
|
124
|
+
# The callable constructor to build an instance of the +error+ class.
|
125
|
+
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
126
|
+
# @return [Proc] A callable that will return an instance of +error+ class.
|
127
|
+
#
|
128
|
+
# @overload error_constructor(sym_or_proc)
|
129
|
+
# Define how to build the +error+.
|
130
|
+
# @param sym_or_proc [Symbol, #call]
|
131
|
+
# - Either a +Symbol+ representing the _public_ method to call on the +error+ class.
|
132
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
133
|
+
# @return [#call] The callable constructor
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
# class MyOperation
|
137
|
+
# include Teckel::Operation
|
138
|
+
#
|
139
|
+
# class Error
|
140
|
+
# def initialize(*args, **opts); end
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# # MyOperation.call("foo", "bar") # -> Error.new("foo", "bar")
|
144
|
+
# error_constructor :new
|
145
|
+
#
|
146
|
+
# # If you need more control over how to build a new +Error+ instance
|
147
|
+
# # MyOperation.call("foo", opt: "bar") # -> Error.new(name: "foo", opt: "bar")
|
148
|
+
# error_constructor ->(name, options) { Error.new(name: name, **options) }
|
149
|
+
# end
|
150
|
+
def error_constructor(sym_or_proc = nil)
|
151
|
+
get_set_constructor(:error_constructor, error, sym_or_proc) ||
|
152
|
+
raise(MissingConfigError, "Missing error_constructor config for #{self}")
|
153
|
+
end
|
154
|
+
|
155
|
+
# @!endgroup
|
156
|
+
|
157
|
+
# @overload settings()
|
158
|
+
# Get the configured class wrapping the settings data structure.
|
159
|
+
# @return [Class] The +settings+ class, or {Teckel::Contracts::None} as default
|
160
|
+
#
|
161
|
+
# @overload settings(klass)
|
162
|
+
# Set the class wrapping the settings data structure.
|
163
|
+
# @param klass [Class] The +settings+ class
|
164
|
+
# @return [Class] The +settings+ class configured
|
165
|
+
def settings(klass = nil)
|
166
|
+
@config.for(:settings, klass) { const_defined?(:Settings) ? self::Settings : none }
|
167
|
+
end
|
168
|
+
|
169
|
+
# @overload settings_constructor()
|
170
|
+
# The callable constructor to build an instance of the +settings+ class.
|
171
|
+
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
172
|
+
# @return [Proc] A callable that will return an instance of +settings+ class.
|
173
|
+
#
|
174
|
+
# @overload settings_constructor(sym_or_proc)
|
175
|
+
# Define how to build the +settings+.
|
176
|
+
# @param sym_or_proc [Symbol, #call]
|
177
|
+
# - Either a +Symbol+ representing the _public_ method to call on the +settings+ class.
|
178
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
179
|
+
# @return [#call] The callable constructor
|
180
|
+
#
|
181
|
+
# @example
|
182
|
+
# class MyOperation
|
183
|
+
# include Teckel::Operation
|
184
|
+
#
|
185
|
+
# class Settings
|
186
|
+
# def initialize(*args); end
|
187
|
+
# end
|
188
|
+
#
|
189
|
+
# # MyOperation.with("foo", "bar") # -> Settings.new("foo", "bar")
|
190
|
+
# settings_constructor :new
|
191
|
+
# end
|
192
|
+
def settings_constructor(sym_or_proc = nil)
|
193
|
+
get_set_constructor(:settings_constructor, settings, sym_or_proc) ||
|
194
|
+
raise(MissingConfigError, "Missing settings_constructor config for #{self}")
|
195
|
+
end
|
196
|
+
|
197
|
+
# Declare default settings this operation should use when called without
|
198
|
+
# {Teckel::Operation::ClassMethods#with #with}.
|
199
|
+
# When executing a Operation, +settings+ will no longer be +nil+, but
|
200
|
+
# whatever you define here.
|
201
|
+
#
|
202
|
+
# Explicit call-time settings will *not* get merged with declared default setting.
|
203
|
+
#
|
204
|
+
# @overload default_settings!()
|
205
|
+
# When this operation is called without {Teckel::Operation::ClassMethods#with #with},
|
206
|
+
# +settings+ will be an instance of the +settings+ class, initialized with no arguments.
|
207
|
+
#
|
208
|
+
# @overload default_settings!(sym_or_proc)
|
209
|
+
# When this operation is called without {Teckel::Operation::ClassMethods#with #with},
|
210
|
+
# +settings+ will be an instance of this callable constructor.
|
211
|
+
#
|
212
|
+
# @param sym_or_proc [Symbol, #call]
|
213
|
+
# - Either a +Symbol+ representing the _public_ method to call on the +settings+ class.
|
214
|
+
# - Or anything that responds to +#call+ (like a +Proc+).
|
215
|
+
#
|
216
|
+
# @overload default_settings!(arg1, arg2, ...)
|
217
|
+
# When this operation is called without {Teckel::Operation::ClassMethods#with #with},
|
218
|
+
# +settings+ will be an instance of the +settings+ class, initialized with those arguments.
|
219
|
+
#
|
220
|
+
# (Like calling +MyOperation.with(arg1, arg2, ...)+)
|
221
|
+
def default_settings!(*args)
|
222
|
+
callable =
|
223
|
+
if args.empty?
|
224
|
+
-> { settings_constructor.call }
|
225
|
+
elsif args.length == 1
|
226
|
+
build_constructor(settings, args.first)
|
227
|
+
end
|
228
|
+
|
229
|
+
callable ||= -> { settings_constructor.call(*args) }
|
230
|
+
|
231
|
+
@config.for(:default_settings, callable)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Getter for configured default settings
|
235
|
+
# @return [nil|#call] The callable constructor
|
236
|
+
def default_settings
|
237
|
+
@config.for(:default_settings)
|
238
|
+
end
|
239
|
+
|
240
|
+
# @overload runner()
|
241
|
+
# @return [Class] The Runner class
|
242
|
+
# @!visibility protected
|
243
|
+
#
|
244
|
+
# @overload runner(klass)
|
245
|
+
# Overwrite the default runner
|
246
|
+
# @param klass [Class] A class like the {Runner}
|
247
|
+
# @!visibility protected
|
248
|
+
def runner(klass = nil)
|
249
|
+
@config.for(:runner, klass) { Teckel::Operation::Runner }
|
250
|
+
end
|
251
|
+
|
252
|
+
# @overload result()
|
253
|
+
# Get the configured result object class wrapping {error} or {output}.
|
254
|
+
# The {ValueResult} default will act as a pass-through and does. Any error
|
255
|
+
# or output will just returned as-is.
|
256
|
+
# @return [Class] The +result+ class, or {ValueResult} as default
|
257
|
+
#
|
258
|
+
# @overload result(klass)
|
259
|
+
# Set the result object class wrapping {error} or {output}.
|
260
|
+
# @param klass [Class] The +result+ class
|
261
|
+
# @return [Class] The +result+ class configured
|
262
|
+
def result(klass = nil)
|
263
|
+
@config.for(:result, klass) { const_defined?(:Result, false) ? self::Result : ValueResult }
|
264
|
+
end
|
265
|
+
|
266
|
+
# @overload result_constructor()
|
267
|
+
# The callable constructor to build an instance of the +result+ class.
|
268
|
+
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
269
|
+
# @return [Proc] A callable that will return an instance of +result+ class.
|
270
|
+
#
|
271
|
+
# @overload result_constructor(sym_or_proc)
|
272
|
+
# Define how to build the +result+.
|
273
|
+
# @param sym_or_proc [Symbol, #call]
|
274
|
+
# - Either a +Symbol+ representing the _public_ method to call on the +result+ class.
|
275
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
276
|
+
# @return [#call] The callable constructor
|
277
|
+
#
|
278
|
+
# @example
|
279
|
+
# class MyOperation
|
280
|
+
# include Teckel::Operation
|
281
|
+
#
|
282
|
+
# class Result
|
283
|
+
# include Teckel::Result
|
284
|
+
# def initialize(value, success, opts = {}); end
|
285
|
+
# end
|
286
|
+
#
|
287
|
+
# # If you need more control over how to build a new +Result+ instance
|
288
|
+
# result_constructor ->(value, success) { result.new(value, success, {foo: :bar}) }
|
289
|
+
# end
|
290
|
+
def result_constructor(sym_or_proc = nil)
|
291
|
+
get_set_constructor(:result_constructor, result, sym_or_proc) ||
|
292
|
+
raise(MissingConfigError, "Missing result_constructor config for #{self}")
|
293
|
+
end
|
294
|
+
|
295
|
+
# @!group Shortcuts
|
296
|
+
|
297
|
+
# Shortcut to use {Teckel::Operation::Result} as a result object,
|
298
|
+
# wrapping any {error} or {output}.
|
299
|
+
#
|
300
|
+
# @!visibility protected
|
301
|
+
# @note Don't use in conjunction with {result} or {result_constructor}
|
302
|
+
# @return [nil]
|
303
|
+
def result!
|
304
|
+
@config.for(:result, Teckel::Operation::Result)
|
305
|
+
@config.for(:result_constructor, Teckel::Operation::Result.method(:new))
|
306
|
+
nil
|
307
|
+
end
|
308
|
+
|
309
|
+
# @!visibility private
|
310
|
+
REQUIRED_CONFIGS = %i[
|
311
|
+
input input_constructor
|
312
|
+
output output_constructor
|
313
|
+
error error_constructor
|
314
|
+
settings settings_constructor
|
315
|
+
result result_constructor
|
316
|
+
runner
|
317
|
+
].freeze
|
318
|
+
|
319
|
+
# @!visibility private
|
320
|
+
# @return [void]
|
321
|
+
def define!
|
322
|
+
REQUIRED_CONFIGS.each { |e| public_send(e) }
|
323
|
+
nil
|
324
|
+
end
|
325
|
+
|
326
|
+
# Disallow any further changes to this Operation.
|
327
|
+
# Make sure all configurations are set.
|
328
|
+
#
|
329
|
+
# @raise [MissingConfigError]
|
330
|
+
# @return [self] Frozen self
|
331
|
+
# @!visibility public
|
332
|
+
def finalize!
|
333
|
+
define!
|
334
|
+
@config.freeze
|
335
|
+
self
|
336
|
+
end
|
337
|
+
|
338
|
+
# Produces a shallow copy of this operation and all it's configuration.
|
339
|
+
#
|
340
|
+
# @return [self]
|
341
|
+
# @!visibility public
|
342
|
+
def dup
|
343
|
+
super.tap do |copy|
|
344
|
+
copy.instance_variable_set(:@config, @config.dup)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
# Produces a clone of this operation and all it's configuration
|
349
|
+
#
|
350
|
+
# @return [self]
|
351
|
+
# @!visibility public
|
352
|
+
def clone
|
353
|
+
if frozen?
|
354
|
+
super
|
355
|
+
else
|
356
|
+
super.tap do |copy|
|
357
|
+
copy.instance_variable_set(:@config, @config.dup)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# @!visibility private
|
363
|
+
def inherited(subclass)
|
364
|
+
subclass.instance_variable_set(:@config, @config.dup)
|
365
|
+
super subclass
|
366
|
+
end
|
367
|
+
|
368
|
+
# @!visibility private
|
369
|
+
def self.extended(base)
|
370
|
+
base.instance_exec do
|
371
|
+
@config = Teckel::Config.new
|
372
|
+
attr_accessor :runner
|
373
|
+
attr_accessor :settings
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
private
|
378
|
+
|
379
|
+
def get_set_constructor(name, on, sym_or_proc)
|
380
|
+
constructor = build_constructor(on, sym_or_proc) unless sym_or_proc.nil?
|
381
|
+
|
382
|
+
@config.for(name, constructor) {
|
383
|
+
build_constructor(on, Teckel::DEFAULT_CONSTRUCTOR)
|
384
|
+
}
|
385
|
+
end
|
386
|
+
|
387
|
+
def build_constructor(on, sym_or_proc)
|
388
|
+
if sym_or_proc.is_a?(Symbol) && on.respond_to?(sym_or_proc)
|
389
|
+
on.public_method(sym_or_proc)
|
390
|
+
elsif sym_or_proc.respond_to?(:call)
|
391
|
+
sym_or_proc
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Teckel
|
4
|
+
module Operation
|
5
|
+
# The optional, default result object for {Teckel::Operation}s.
|
6
|
+
# Wraps +output+ and +error+ into a {Teckel::Operation::Result}.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class CreateUser
|
10
|
+
# include Teckel::Operation
|
11
|
+
#
|
12
|
+
# result! # Shortcut to use this Result object
|
13
|
+
#
|
14
|
+
# input Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer)
|
15
|
+
# output Types.Instance(User)
|
16
|
+
# error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
|
17
|
+
#
|
18
|
+
# def call(input)
|
19
|
+
# user = User.new(name: input[:name], age: input[:age])
|
20
|
+
# if user.save
|
21
|
+
# success!(user) # exits early with success, prevents any further execution
|
22
|
+
# else
|
23
|
+
# fail!(message: "Could not save User", errors: user.errors)
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # A success call:
|
29
|
+
# CreateUser.call(name: "Bob", age: 23).is_a?(Teckel::Operation::Result) #=> true
|
30
|
+
# CreateUser.call(name: "Bob", age: 23).success.is_a?(User) #=> true
|
31
|
+
#
|
32
|
+
# # A failure call:
|
33
|
+
# CreateUser.call(name: "Bob", age: 10).is_a?(Teckel::Operation::Result) #=> true
|
34
|
+
# CreateUser.call(name: "Bob", age: 10).failure.is_a?(Hash) #=> true
|
35
|
+
#
|
36
|
+
# @!visibility public
|
37
|
+
class Result
|
38
|
+
include Teckel::Result
|
39
|
+
|
40
|
+
# @param value [Object] The result value
|
41
|
+
# @param success [Boolean] whether this is a successful result
|
42
|
+
def initialize(value, success)
|
43
|
+
@value = value
|
44
|
+
@success = (!!success).freeze
|
45
|
+
end
|
46
|
+
|
47
|
+
# Whether this is a success result
|
48
|
+
# @return [Boolean]
|
49
|
+
def successful?
|
50
|
+
@success
|
51
|
+
end
|
52
|
+
|
53
|
+
# @!attribute [r] value
|
54
|
+
# @return [Mixed] the value/payload
|
55
|
+
attr_reader :value
|
56
|
+
|
57
|
+
# Get the error/failure value
|
58
|
+
# @yield [Mixed] If a block is given and this is not a failure result, the value is yielded to the block
|
59
|
+
# @param default [Mixed] return this default value if it's not a failure result
|
60
|
+
# @return [Mixed] the value/payload
|
61
|
+
def failure(default = nil, &block)
|
62
|
+
return @value unless @success
|
63
|
+
return yield(@value) if block
|
64
|
+
|
65
|
+
default
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get the success value
|
69
|
+
# @yield [Mixed] If a block is given and this is not a success result, the value is yielded to the block
|
70
|
+
# @param default [Mixed] return this default value if it's not a success result
|
71
|
+
# @return [Mixed] the value/payload
|
72
|
+
def success(default = nil, &block)
|
73
|
+
return @value if @success
|
74
|
+
return yield(@value) if block
|
75
|
+
|
76
|
+
default
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# The default "no-op" Result handler. Just returns the value, ignoring the
|
81
|
+
# success state.
|
82
|
+
module ValueResult
|
83
|
+
class << self
|
84
|
+
def [](value, *_)
|
85
|
+
value
|
86
|
+
end
|
87
|
+
|
88
|
+
alias :new :[]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|