teckel 0.4.0 → 0.8.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 +71 -0
- data/README.md +3 -3
- data/lib/teckel/chain/config.rb +286 -0
- data/lib/teckel/chain/result.rb +3 -3
- data/lib/teckel/chain/runner.rb +28 -17
- data/lib/teckel/chain.rb +14 -190
- data/lib/teckel/config.rb +13 -7
- data/lib/teckel/operation/config.rb +400 -0
- data/lib/teckel/operation/result.rb +8 -8
- data/lib/teckel/operation/runner.rb +29 -25
- data/lib/teckel/operation.rb +87 -388
- data/lib/teckel/result.rb +8 -6
- data/lib/teckel/version.rb +1 -1
- data/lib/teckel.rb +0 -1
- data/spec/chain/around_hook_spec.rb +100 -0
- data/spec/chain/default_settings_spec.rb +39 -0
- data/spec/chain/inheritance_spec.rb +44 -44
- data/spec/chain/none_input_spec.rb +36 -0
- data/spec/chain/results_spec.rb +28 -28
- data/spec/chain_spec.rb +132 -57
- data/spec/doctest_helper.rb +1 -0
- data/spec/operation/config_spec.rb +227 -0
- data/spec/operation/contract_trace_spec.rb +118 -0
- data/spec/operation/default_settings_spec.rb +120 -0
- data/spec/operation/fail_on_input_spec.rb +103 -0
- data/spec/operation/inheritance_spec.rb +38 -38
- data/spec/operation/result_spec.rb +27 -12
- data/spec/operation/results_spec.rb +67 -67
- data/spec/operation_spec.rb +276 -230
- data/spec/rb27/pattern_matching_spec.rb +2 -2
- data/spec/result_spec.rb +12 -10
- data/spec/spec_helper.rb +10 -0
- metadata +41 -12
- data/spec/chain_around_hook_spec.rb +0 -100
@@ -38,16 +38,16 @@ module Teckel
|
|
38
38
|
include Teckel::Result
|
39
39
|
|
40
40
|
# @param value [Object] The result value
|
41
|
-
# @param
|
42
|
-
def initialize(value,
|
41
|
+
# @param successful [Boolean] whether this is a successful result
|
42
|
+
def initialize(value, successful)
|
43
43
|
@value = value
|
44
|
-
@
|
44
|
+
@successful = successful
|
45
45
|
end
|
46
46
|
|
47
47
|
# Whether this is a success result
|
48
48
|
# @return [Boolean]
|
49
49
|
def successful?
|
50
|
-
@
|
50
|
+
@successful
|
51
51
|
end
|
52
52
|
|
53
53
|
# @!attribute [r] value
|
@@ -59,8 +59,8 @@ module Teckel
|
|
59
59
|
# @param default [Mixed] return this default value if it's not a failure result
|
60
60
|
# @return [Mixed] the value/payload
|
61
61
|
def failure(default = nil, &block)
|
62
|
-
return
|
63
|
-
return yield(
|
62
|
+
return value unless @successful
|
63
|
+
return yield(value) if block
|
64
64
|
|
65
65
|
default
|
66
66
|
end
|
@@ -70,8 +70,8 @@ module Teckel
|
|
70
70
|
# @param default [Mixed] return this default value if it's not a success result
|
71
71
|
# @return [Mixed] the value/payload
|
72
72
|
def success(default = nil, &block)
|
73
|
-
return
|
74
|
-
return yield(
|
73
|
+
return value if @successful
|
74
|
+
return yield(value) if block
|
75
75
|
|
76
76
|
default
|
77
77
|
end
|
@@ -16,54 +16,58 @@ module Teckel
|
|
16
16
|
attr_reader :operation, :settings
|
17
17
|
|
18
18
|
def call(input = nil)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
return simple_return == UNDEFINED ? build_output(*out) : build_output(simple_return)
|
19
|
+
catch(:halt) do
|
20
|
+
op = instance
|
21
|
+
op_input = op.instance_exec(input, &operation.input_constructor)
|
22
|
+
op.call(op_input)
|
23
|
+
nil # return values need to go through +success!+ or +fail!+
|
25
24
|
end
|
26
|
-
build_error(*err)
|
27
25
|
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
def with(*)
|
32
|
-
raise Teckel::Error, "Operation already has settings assigned."
|
33
|
-
end
|
27
|
+
def instance
|
28
|
+
return @instance if instance_variable_defined?(:@instance)
|
34
29
|
|
35
|
-
|
30
|
+
op = operation.new
|
31
|
+
op.runner = self
|
32
|
+
op.settings = settings unless settings.eql?(UNDEFINED)
|
36
33
|
|
37
|
-
|
38
|
-
op = @operation.new
|
39
|
-
op.settings = settings if settings != UNDEFINED
|
40
|
-
op.call(input)
|
34
|
+
@instance = op
|
41
35
|
end
|
42
36
|
|
43
|
-
|
44
|
-
|
37
|
+
# This is just here to raise a meaningful error.
|
38
|
+
# @!visibility private
|
39
|
+
def with(*)
|
40
|
+
raise Error, "Operation already has settings assigned."
|
45
41
|
end
|
46
42
|
|
47
|
-
|
43
|
+
# Halt any further execution with a output value
|
44
|
+
#
|
45
|
+
# @return a thing matching your {Teckel::Operation::Config#output output} definition
|
46
|
+
# @!visibility protected
|
47
|
+
def success!(*args)
|
48
48
|
value =
|
49
|
-
if args.size
|
49
|
+
if args.size.equal?(1) && operation.output === args.first # rubocop:disable Style/CaseEquality
|
50
50
|
args.first
|
51
51
|
else
|
52
52
|
operation.output_constructor.call(*args)
|
53
53
|
end
|
54
54
|
|
55
|
-
|
55
|
+
throw :halt, instance.instance_exec(value, true, &operation.result_constructor)
|
56
56
|
end
|
57
57
|
|
58
|
-
|
58
|
+
# Halt any further execution with an error value
|
59
|
+
#
|
60
|
+
# @return a thing matching your {Teckel::Operation::Config#error error} definition
|
61
|
+
# @!visibility protected
|
62
|
+
def fail!(*args)
|
59
63
|
value =
|
60
|
-
if args.size
|
64
|
+
if args.size.equal?(1) && operation.error === args.first # rubocop:disable Style/CaseEquality
|
61
65
|
args.first
|
62
66
|
else
|
63
67
|
operation.error_constructor.call(*args)
|
64
68
|
end
|
65
69
|
|
66
|
-
|
70
|
+
throw :halt, instance.instance_exec(value, false, &operation.result_constructor)
|
67
71
|
end
|
68
72
|
end
|
69
73
|
end
|
data/lib/teckel/operation.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "operation/config"
|
3
4
|
require_relative "operation/result"
|
4
5
|
require_relative "operation/runner"
|
5
6
|
|
@@ -13,13 +14,14 @@ module Teckel
|
|
13
14
|
# +input+. +output+ and +error+ methods to point them to anonymous classes.
|
14
15
|
#
|
15
16
|
# If you like "traditional" result objects to ask +successful?+ or +failure?+ on,
|
16
|
-
# use {
|
17
|
+
# use {Teckel::Operation::Config#result! result!} and get {Teckel::Operation::Result}
|
17
18
|
#
|
18
19
|
# By default, +input+. +output+ and +error+ classes are build using +:[]+
|
19
20
|
# (eg: +Input[some: :param]+).
|
20
|
-
#
|
21
|
-
# {
|
22
|
-
# {
|
21
|
+
#
|
22
|
+
# Use {Teckel::Operation::Config#input_constructor input_constructor},
|
23
|
+
# {Teckel::Operation::Config#output_constructor output_constructor} and
|
24
|
+
# {Teckel::Operation::Config#error_constructor error_constructor} to change them.
|
23
25
|
#
|
24
26
|
# @example class definitions via methods
|
25
27
|
# class CreateUserViaMethods
|
@@ -56,318 +58,28 @@ module Teckel
|
|
56
58
|
# @!visibility public
|
57
59
|
module Operation
|
58
60
|
module ClassMethods
|
59
|
-
# @!
|
60
|
-
|
61
|
-
# @overload input()
|
62
|
-
# Get the configured class wrapping the input data structure.
|
63
|
-
# @return [Class] The +input+ class
|
64
|
-
# @overload input(klass)
|
65
|
-
# Set the class wrapping the input data structure.
|
66
|
-
# @param klass [Class] The +input+ class
|
67
|
-
# @return [Class] The +input+ class
|
68
|
-
def input(klass = nil)
|
69
|
-
@config.for(:input, klass) { self::Input if const_defined?(:Input) } ||
|
70
|
-
raise(Teckel::MissingConfigError, "Missing input config for #{self}")
|
71
|
-
end
|
72
|
-
|
73
|
-
# @overload input_constructor()
|
74
|
-
# The callable constructor to build an instance of the +input+ class.
|
75
|
-
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
76
|
-
# @return [Proc] A callable that will return an instance of the +input+ class.
|
77
|
-
#
|
78
|
-
# @overload input_constructor(sym_or_proc)
|
79
|
-
# Define how to build the +input+.
|
80
|
-
# @param sym_or_proc [Symbol, #call]
|
81
|
-
# - Either a +Symbol+ representing the _public_ method to call on the +input+ class.
|
82
|
-
# - Or anything that response to +#call+ (like a +Proc+).
|
83
|
-
# @return [#call] The callable constructor
|
84
|
-
#
|
85
|
-
# @example simple symbol to method constructor
|
86
|
-
# class MyOperation
|
87
|
-
# include Teckel::Operation
|
88
|
-
#
|
89
|
-
# class Input
|
90
|
-
# def initialize(name:, age:); end
|
91
|
-
# end
|
92
|
-
#
|
93
|
-
# # If you need more control over how to build a new +Input+ instance
|
94
|
-
# # MyOperation.call(name: "Bob", age: 23) # -> Input.new(name: "Bob", age: 23)
|
95
|
-
# input_constructor :new
|
96
|
-
# end
|
97
|
-
#
|
98
|
-
# MyOperation.input_constructor.is_a?(Method) #=> true
|
99
|
-
#
|
100
|
-
# @example Custom Proc constructor
|
101
|
-
# class MyOperation
|
102
|
-
# include Teckel::Operation
|
103
|
-
#
|
104
|
-
# class Input
|
105
|
-
# def initialize(*args, **opts); end
|
106
|
-
# end
|
107
|
-
#
|
108
|
-
# # If you need more control over how to build a new +Input+ instance
|
109
|
-
# # MyOperation.call("foo", opt: "bar") # -> Input.new(name: "foo", opt: "bar")
|
110
|
-
# input_constructor ->(name, options) { Input.new(name: name, **options) }
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# MyOperation.input_constructor.is_a?(Proc) #=> true
|
114
|
-
def input_constructor(sym_or_proc = nil)
|
115
|
-
get_set_counstructor(:input_constructor, input, sym_or_proc) ||
|
116
|
-
raise(MissingConfigError, "Missing input_constructor config for #{self}")
|
117
|
-
end
|
118
|
-
|
119
|
-
# @overload output()
|
120
|
-
# Get the configured class wrapping the output data structure.
|
121
|
-
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
122
|
-
# @return [Class] The +output+ class
|
123
|
-
#
|
124
|
-
# @overload output(klass)
|
125
|
-
# Set the class wrapping the output data structure.
|
126
|
-
# @param klass [Class] The +output+ class
|
127
|
-
# @return [Class] The +output+ class
|
128
|
-
def output(klass = nil)
|
129
|
-
@config.for(:output, klass) { self::Output if const_defined?(:Output) } ||
|
130
|
-
raise(Teckel::MissingConfigError, "Missing output config for #{self}")
|
131
|
-
end
|
132
|
-
|
133
|
-
# @overload output_constructor()
|
134
|
-
# The callable constructor to build an instance of the +output+ class.
|
135
|
-
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
136
|
-
# @return [Proc] A callable that will return an instance of +output+ class.
|
137
|
-
#
|
138
|
-
# @overload output_constructor(sym_or_proc)
|
139
|
-
# Define how to build the +output+.
|
140
|
-
# @param sym_or_proc [Symbol, #call]
|
141
|
-
# - Either a +Symbol+ representing the _public_ method to call on the +output+ class.
|
142
|
-
# - Or anything that response to +#call+ (like a +Proc+).
|
143
|
-
# @return [#call] The callable constructor
|
144
|
-
#
|
145
|
-
# @example
|
146
|
-
# class MyOperation
|
147
|
-
# include Teckel::Operation
|
148
|
-
#
|
149
|
-
# class Output
|
150
|
-
# def initialize(*args, **opts); end
|
151
|
-
# end
|
152
|
-
#
|
153
|
-
# # MyOperation.call("foo", "bar") # -> Output.new("foo", "bar")
|
154
|
-
# output_constructor :new
|
155
|
-
#
|
156
|
-
# # If you need more control over how to build a new +Output+ instance
|
157
|
-
# # MyOperation.call("foo", opt: "bar") # -> Output.new(name: "foo", opt: "bar")
|
158
|
-
# output_constructor ->(name, options) { Output.new(name: name, **options) }
|
159
|
-
# end
|
160
|
-
def output_constructor(sym_or_proc = nil)
|
161
|
-
get_set_counstructor(:output_constructor, output, sym_or_proc) ||
|
162
|
-
raise(MissingConfigError, "Missing output_constructor config for #{self}")
|
163
|
-
end
|
164
|
-
|
165
|
-
# @overload error()
|
166
|
-
# Get the configured class wrapping the error data structure.
|
167
|
-
# @return [Class] The +error+ class
|
168
|
-
#
|
169
|
-
# @overload error(klass)
|
170
|
-
# Set the class wrapping the error data structure.
|
171
|
-
# @param klass [Class] The +error+ class
|
172
|
-
# @return [Class,nil] The +error+ class or +nil+ if it does not error
|
173
|
-
def error(klass = nil)
|
174
|
-
@config.for(:error, klass) { self::Error if const_defined?(:Error) } ||
|
175
|
-
raise(Teckel::MissingConfigError, "Missing error config for #{self}")
|
176
|
-
end
|
177
|
-
|
178
|
-
# @overload error_constructor()
|
179
|
-
# The callable constructor to build an instance of the +error+ class.
|
180
|
-
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
181
|
-
# @return [Proc] A callable that will return an instance of +error+ class.
|
182
|
-
#
|
183
|
-
# @overload error_constructor(sym_or_proc)
|
184
|
-
# Define how to build the +error+.
|
185
|
-
# @param sym_or_proc [Symbol, #call]
|
186
|
-
# - Either a +Symbol+ representing the _public_ method to call on the +error+ class.
|
187
|
-
# - Or anything that response to +#call+ (like a +Proc+).
|
188
|
-
# @return [#call] The callable constructor
|
189
|
-
#
|
190
|
-
# @example
|
191
|
-
# class MyOperation
|
192
|
-
# include Teckel::Operation
|
193
|
-
#
|
194
|
-
# class Error
|
195
|
-
# def initialize(*args, **opts); end
|
196
|
-
# end
|
197
|
-
#
|
198
|
-
# # MyOperation.call("foo", "bar") # -> Error.new("foo", "bar")
|
199
|
-
# error_constructor :new
|
200
|
-
#
|
201
|
-
# # If you need more control over how to build a new +Error+ instance
|
202
|
-
# # MyOperation.call("foo", opt: "bar") # -> Error.new(name: "foo", opt: "bar")
|
203
|
-
# error_constructor ->(name, options) { Error.new(name: name, **options) }
|
204
|
-
# end
|
205
|
-
def error_constructor(sym_or_proc = nil)
|
206
|
-
get_set_counstructor(:error_constructor, error, sym_or_proc) ||
|
207
|
-
raise(MissingConfigError, "Missing error_constructor config for #{self}")
|
208
|
-
end
|
209
|
-
|
210
|
-
# @!endgroup
|
211
|
-
|
212
|
-
# @overload settings()
|
213
|
-
# Get the configured class wrapping the settings data structure.
|
214
|
-
# @return [Class] The +settings+ class, or {Teckel::Contracts::None} as default
|
215
|
-
#
|
216
|
-
# @overload settings(klass)
|
217
|
-
# Set the class wrapping the settings data structure.
|
218
|
-
# @param klass [Class] The +settings+ class
|
219
|
-
# @return [Class] The +settings+ class configured
|
220
|
-
def settings(klass = nil)
|
221
|
-
@config.for(:settings, klass) { const_defined?(:Settings) ? self::Settings : none }
|
222
|
-
end
|
223
|
-
|
224
|
-
# @overload settings_constructor()
|
225
|
-
# The callable constructor to build an instance of the +settings+ class.
|
226
|
-
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
227
|
-
# @return [Proc] A callable that will return an instance of +settings+ class.
|
228
|
-
#
|
229
|
-
# @overload settings_constructor(sym_or_proc)
|
230
|
-
# Define how to build the +settings+.
|
231
|
-
# @param sym_or_proc [Symbol, #call]
|
232
|
-
# - Either a +Symbol+ representing the _public_ method to call on the +settings+ class.
|
233
|
-
# - Or anything that response to +#call+ (like a +Proc+).
|
234
|
-
# @return [#call] The callable constructor
|
235
|
-
#
|
236
|
-
# @example
|
237
|
-
# class MyOperation
|
238
|
-
# include Teckel::Operation
|
239
|
-
#
|
240
|
-
# class Settings
|
241
|
-
# def initialize(*args); end
|
242
|
-
# end
|
243
|
-
#
|
244
|
-
# # MyOperation.with("foo", "bar") # -> Settings.new("foo", "bar")
|
245
|
-
# settings_constructor :new
|
246
|
-
# end
|
247
|
-
def settings_constructor(sym_or_proc = nil)
|
248
|
-
get_set_counstructor(:settings_constructor, settings, sym_or_proc) ||
|
249
|
-
raise(MissingConfigError, "Missing settings_constructor config for #{self}")
|
250
|
-
end
|
251
|
-
|
252
|
-
# @overload runner()
|
253
|
-
# @return [Class] The Runner class
|
254
|
-
# @!visibility protected
|
255
|
-
#
|
256
|
-
# @overload runner(klass)
|
257
|
-
# Overwrite the default runner
|
258
|
-
# @param klass [Class] A class like the {Runner}
|
259
|
-
# @!visibility protected
|
260
|
-
def runner(klass = nil)
|
261
|
-
@config.for(:runner, klass) { Teckel::Operation::Runner }
|
262
|
-
end
|
263
|
-
|
264
|
-
# @overload result()
|
265
|
-
# Get the configured result object class wrapping {error} or {output}.
|
266
|
-
# The {ValueResult} default will act as a pass-through and does. Any error
|
267
|
-
# or output will just returned as-is.
|
268
|
-
# @return [Class] The +result+ class, or {ValueResult} as default
|
269
|
-
#
|
270
|
-
# @overload result(klass)
|
271
|
-
# Set the result object class wrapping {error} or {output}.
|
272
|
-
# @param klass [Class] The +result+ class
|
273
|
-
# @return [Class] The +result+ class configured
|
274
|
-
def result(klass = nil)
|
275
|
-
@config.for(:result, klass) { const_defined?(:Result, false) ? self::Result : ValueResult }
|
276
|
-
end
|
277
|
-
|
278
|
-
# @overload result_constructor()
|
279
|
-
# The callable constructor to build an instance of the +result+ class.
|
280
|
-
# Defaults to {Teckel::DEFAULT_CONSTRUCTOR}
|
281
|
-
# @return [Proc] A callable that will return an instance of +result+ class.
|
282
|
-
#
|
283
|
-
# @overload result_constructor(sym_or_proc)
|
284
|
-
# Define how to build the +result+.
|
285
|
-
# @param sym_or_proc [Symbol, #call]
|
286
|
-
# - Either a +Symbol+ representing the _public_ method to call on the +result+ class.
|
287
|
-
# - Or anything that response to +#call+ (like a +Proc+).
|
288
|
-
# @return [#call] The callable constructor
|
289
|
-
#
|
290
|
-
# @example
|
291
|
-
# class MyOperation
|
292
|
-
# include Teckel::Operation
|
293
|
-
#
|
294
|
-
# class Result
|
295
|
-
# include Teckel::Result
|
296
|
-
# def initialize(value, success, opts = {}); end
|
297
|
-
# end
|
298
|
-
#
|
299
|
-
# # If you need more control over how to build a new +Settings+ instance
|
300
|
-
# result_constructor ->(value, success) { result.new(value, success, {foo: :bar}) }
|
301
|
-
# end
|
302
|
-
def result_constructor(sym_or_proc = nil)
|
303
|
-
get_set_counstructor(:result_constructor, result, sym_or_proc) ||
|
304
|
-
raise(MissingConfigError, "Missing result_constructor config for #{self}")
|
305
|
-
end
|
306
|
-
|
307
|
-
# @!group Shortcuts
|
308
|
-
|
309
|
-
# Shortcut to use {Teckel::Operation::Result} as a result object,
|
310
|
-
# wrapping any {error} or {output}.
|
311
|
-
#
|
312
|
-
# @!visibility protected
|
313
|
-
# @note Don't use in conjunction with {result} or {result_constructor}
|
314
|
-
# @return [nil]
|
315
|
-
def result!
|
316
|
-
@config.for(:result, Teckel::Operation::Result)
|
317
|
-
@config.for(:result_constructor, Teckel::Operation::Result.method(:new))
|
318
|
-
nil
|
319
|
-
end
|
320
|
-
|
321
|
-
# Convenience method for setting {#input}, {#output} or {#error} to the
|
322
|
-
# {Teckel::Contracts::None} value.
|
323
|
-
# @return [Object] The {Teckel::Contracts::None} class.
|
324
|
-
#
|
325
|
-
# @example Enforcing nil input, output or error
|
326
|
-
# class MyOperation
|
327
|
-
# include Teckel::Operation
|
328
|
-
#
|
329
|
-
# input none
|
330
|
-
#
|
331
|
-
# # same as
|
332
|
-
# output Teckel::Contracts::None
|
333
|
-
#
|
334
|
-
# error none
|
335
|
-
#
|
336
|
-
# def call(_) # you still need to take than +nil+ input when using `input none`
|
337
|
-
# # when using `error none`:
|
338
|
-
# # `fail!` works, but `fail!("data")` raises an error
|
339
|
-
#
|
340
|
-
# # when using `output none`:
|
341
|
-
# # `success!` works, but `success!("data")` raises an error
|
342
|
-
# # same thing when using simple return values as success:
|
343
|
-
# # take care to not return anything
|
344
|
-
# nil
|
345
|
-
# end
|
346
|
-
# end
|
347
|
-
#
|
348
|
-
# MyOperation.call #=> nil
|
349
|
-
def none
|
350
|
-
Teckel::Contracts::None
|
351
|
-
end
|
352
|
-
|
353
|
-
# @endgroup
|
61
|
+
# @!visibility private
|
62
|
+
UNDEFINED = Object.new
|
354
63
|
|
355
64
|
# Invoke the Operation
|
356
65
|
#
|
357
|
-
# @param input Any form of input your {#input} class can handle via the given
|
358
|
-
#
|
66
|
+
# @param input Any form of input your {Teckel::Operation::Config#input input} class can handle via the given
|
67
|
+
# {Teckel::Operation::Config#input_constructor input_constructor}
|
68
|
+
# @return Either An instance of your defined {Teckel::Operation::Config#error error} class or
|
69
|
+
# {Teckel::Operation::Config#output output} class
|
359
70
|
# @!visibility public
|
360
71
|
def call(input = nil)
|
361
|
-
|
72
|
+
runable.call(input)
|
362
73
|
end
|
363
74
|
|
364
|
-
# Provide {InstanceMethods#settings() settings} to the
|
75
|
+
# Provide {InstanceMethods#settings() settings} to the operation.
|
365
76
|
#
|
366
77
|
# This method is intended to be called on the operation class outside of
|
367
|
-
# it's definition, prior to
|
78
|
+
# it's definition, prior to invoking {#call}.
|
368
79
|
#
|
369
|
-
# @param
|
370
|
-
#
|
80
|
+
# @param settings Any form of settings your {Teckel::Operation::Config#settings settings} class can handle via the given
|
81
|
+
# {Teckel::Operation::Config#settings_constructor settings_constructor}
|
82
|
+
# @return [Class] The configured {Teckel::Operation::Config#runner runner}
|
371
83
|
# @!visibility public
|
372
84
|
#
|
373
85
|
# @example Inject settings for an operation call
|
@@ -396,120 +108,107 @@ module Teckel
|
|
396
108
|
# MyOperation.with(false).call
|
397
109
|
# MyOperation.call
|
398
110
|
# LOG #=> []
|
399
|
-
def with(
|
400
|
-
|
111
|
+
def with(settings)
|
112
|
+
runable(settings_constructor.call(settings))
|
401
113
|
end
|
402
114
|
alias :set :with
|
403
115
|
|
404
|
-
#
|
405
|
-
# @return [void]
|
406
|
-
def define!
|
407
|
-
%i[
|
408
|
-
input input_constructor
|
409
|
-
output output_constructor
|
410
|
-
error error_constructor
|
411
|
-
settings settings_constructor
|
412
|
-
result result_constructor
|
413
|
-
runner
|
414
|
-
].each { |e| public_send(e) }
|
415
|
-
nil
|
416
|
-
end
|
417
|
-
|
418
|
-
# Disallow any further changes to this Operation.
|
419
|
-
# Make sure all configurations are set.
|
420
|
-
#
|
421
|
-
# @raise [MissingConfigError]
|
422
|
-
# @return [self] Frozen self
|
423
|
-
# @!visibility public
|
424
|
-
def finalize!
|
425
|
-
define!
|
426
|
-
@config.freeze
|
427
|
-
self
|
428
|
-
end
|
429
|
-
|
430
|
-
# Produces a shallow copy of this operation and all it's configuration.
|
116
|
+
# Constructs a Runner instance for {call} and {with}.
|
431
117
|
#
|
432
|
-
# @
|
433
|
-
#
|
434
|
-
def dup
|
435
|
-
super.tap do |copy|
|
436
|
-
copy.instance_variable_set(:@config, @config.dup)
|
437
|
-
end
|
438
|
-
end
|
439
|
-
|
440
|
-
# Produces a clone of this operation and all it's configuration
|
118
|
+
# @note This method is public to make testing, stubbing and mocking easier.
|
119
|
+
# Your normal application code should use {with} and/or {call}
|
441
120
|
#
|
442
|
-
# @
|
121
|
+
# @param settings Optional. Any form of settings your
|
122
|
+
# {Teckel::Operation::Config#settings settings} class can handle via the
|
123
|
+
# given {Teckel::Operation::Config#settings_constructor settings_constructor}
|
124
|
+
# @return [Class] The configured {Teckel::Operation::Config#runner runner}
|
443
125
|
# @!visibility public
|
444
|
-
def
|
445
|
-
if
|
446
|
-
|
126
|
+
def runable(settings = UNDEFINED)
|
127
|
+
if settings != UNDEFINED
|
128
|
+
runner.new(self, settings)
|
129
|
+
elsif default_settings
|
130
|
+
runner.new(self, default_settings.call)
|
447
131
|
else
|
448
|
-
|
449
|
-
copy.instance_variable_set(:@config, @config.dup)
|
450
|
-
end
|
132
|
+
runner.new(self)
|
451
133
|
end
|
452
134
|
end
|
453
135
|
|
454
|
-
#
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
#
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
136
|
+
# Convenience method for setting {Teckel::Operation::Config#input input},
|
137
|
+
# {Teckel::Operation::Config#output output} or
|
138
|
+
# {Teckel::Operation::Config#error error} to the
|
139
|
+
# {Teckel::Contracts::None} value.
|
140
|
+
#
|
141
|
+
# @return [Object] The {Teckel::Contracts::None} class.
|
142
|
+
#
|
143
|
+
# @example Enforcing nil input, output or error
|
144
|
+
# class MyOperation
|
145
|
+
# include Teckel::Operation
|
146
|
+
#
|
147
|
+
# input none
|
148
|
+
#
|
149
|
+
# # same as
|
150
|
+
# output Teckel::Contracts::None
|
151
|
+
#
|
152
|
+
# error none
|
153
|
+
#
|
154
|
+
# def call(_) # you still need to take than +nil+ input when using `input none`
|
155
|
+
# # when using `error none`:
|
156
|
+
# # `fail!` works, but `fail!("data")` raises an error
|
157
|
+
#
|
158
|
+
# # when using `output none`:
|
159
|
+
# # `success!` works, but `success!("data")` raises an error
|
160
|
+
# end
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# MyOperation.call #=> nil
|
164
|
+
def none
|
165
|
+
Contracts::None
|
483
166
|
end
|
484
167
|
end
|
485
168
|
|
486
169
|
module InstanceMethods
|
170
|
+
# @!method call(input)
|
171
|
+
# @abstract
|
172
|
+
# @see Operation
|
173
|
+
# @see ClassMethods#call
|
174
|
+
#
|
175
|
+
# The entry point for your operation. It needs to always accept an input value, even when
|
176
|
+
# using +input none+.
|
177
|
+
# If your Operation expects to generate success or failure outputs, you need to use either
|
178
|
+
# {.success!} or {.fail!} respectively. Simple return values will get ignored by default. See
|
179
|
+
# {Teckel::Operation::Config#runner} and {Teckel::Operation::Runner} on how to overwrite.
|
180
|
+
|
487
181
|
# @!attribute [r] settings()
|
488
182
|
# @return [Class,nil] When executed with settings, an instance of the
|
489
183
|
# configured {.settings} class. Otherwise +nil+
|
490
184
|
# @see ClassMethods#settings
|
491
185
|
# @!visibility public
|
492
186
|
|
493
|
-
#
|
187
|
+
# Delegates to the configured Runner.
|
188
|
+
# The default behavior is to halt any further execution with a output value.
|
494
189
|
#
|
495
|
-
# @
|
190
|
+
# @see Teckel::Operation::Runner#success!
|
496
191
|
# @!visibility protected
|
497
192
|
def success!(*args)
|
498
|
-
|
193
|
+
runner.success!(*args)
|
499
194
|
end
|
500
195
|
|
501
|
-
#
|
196
|
+
# Delegates to the configured Runner.
|
197
|
+
# The default behavior is to halt any further execution with an error value.
|
502
198
|
#
|
503
|
-
# @
|
199
|
+
# @see Teckel::Operation::Runner#fail!
|
504
200
|
# @!visibility protected
|
505
201
|
def fail!(*args)
|
506
|
-
|
202
|
+
runner.fail!(*args)
|
507
203
|
end
|
508
204
|
end
|
509
205
|
|
510
206
|
def self.included(receiver)
|
511
|
-
receiver.
|
512
|
-
|
207
|
+
receiver.class_eval do
|
208
|
+
extend Config
|
209
|
+
extend ClassMethods
|
210
|
+
include InstanceMethods
|
211
|
+
end
|
513
212
|
end
|
514
213
|
end
|
515
214
|
end
|