teckel 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/teckel/chain.rb +76 -36
- data/lib/teckel/config.rb +13 -0
- data/lib/teckel/operation.rb +16 -8
- data/lib/teckel/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 520b717d1e115614b1c93cfac56deac0e567bfc5d39e2ca6e93094f3c958fd9b
|
4
|
+
data.tar.gz: d5f8f5a5dd2a2dabebe1add49ab139e9c5d14fd5c1fd3b1ef92f78c99c604401
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb4021c0102a0d18e45df23a40669984462b5d469608f16cefcff68fefa38a088ae4851f51052ba5b3c3f5305dc6d0abfbf2a50a81ca240748cf226ea5b66366
|
7
|
+
data.tar.gz: 4298d3bff376cace7dd78a863fc60da2f7e4600f8032da19a3c7ecaee6168146fe41658af16e1811ad7de157cf15a37aa0bad42cdc19602de8eac1b2a2f49a83
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 0.3.0
|
4
|
+
|
5
|
+
- `finalize!`'ing a Chain will also finalize all it's Operations
|
6
|
+
- Changed attribute naming of `StepFailure`:
|
7
|
+
+ `.operation` will now give the operation class of the step - was `.step` before
|
8
|
+
+ `.step` will now give the name of the step (which Operation failed) - was `.step_name` before
|
9
|
+
|
3
10
|
## 0.2.0
|
4
11
|
|
5
12
|
- Around Hooks for Chains
|
data/lib/teckel/chain.rb
CHANGED
@@ -77,20 +77,20 @@ module Teckel
|
|
77
77
|
#
|
78
78
|
# result = MyChain.call(name: "Bob", age: 23)
|
79
79
|
# result.is_a?(Teckel::Result) #=> true
|
80
|
-
# result.success[:user].is_a?(User)
|
81
|
-
# result.success[:friend].is_a?(User)
|
80
|
+
# result.success[:user].is_a?(User) #=> true
|
81
|
+
# result.success[:friend].is_a?(User) #=> true
|
82
82
|
#
|
83
83
|
# AddFriend.fail_befriend = true
|
84
84
|
# failure_result = MyChain.call(name: "Bob", age: 23)
|
85
85
|
# failure_result.is_a?(Teckel::Chain::StepFailure) #=> true
|
86
86
|
#
|
87
87
|
# # additional step information
|
88
|
-
# failure_result.
|
89
|
-
# failure_result.
|
88
|
+
# failure_result.step #=> :befriend
|
89
|
+
# failure_result.operation #=> AddFriend
|
90
90
|
#
|
91
91
|
# # otherwise behaves just like a normal +Result+
|
92
|
-
# failure_result.failure?
|
93
|
-
# failure_result.failure
|
92
|
+
# failure_result.failure? #=> true
|
93
|
+
# failure_result.failure #=> {message: "Did not find a friend."}
|
94
94
|
#
|
95
95
|
# @example DB transaction around hook
|
96
96
|
# class CreateUser
|
@@ -163,33 +163,44 @@ module Teckel
|
|
163
163
|
# failure_result.is_a?(Teckel::Chain::StepFailure) #=> true
|
164
164
|
#
|
165
165
|
# # triggered DB rollback
|
166
|
-
# LOG
|
166
|
+
# LOG #=> [:before, :rollback]
|
167
167
|
#
|
168
168
|
# # additional step information
|
169
|
-
# failure_result.
|
170
|
-
# failure_result.
|
169
|
+
# failure_result.step #=> :befriend
|
170
|
+
# failure_result.operation #=> AddFriend
|
171
171
|
#
|
172
172
|
# # otherwise behaves just like a normal +Result+
|
173
|
-
# failure_result.failure?
|
174
|
-
# failure_result.failure
|
173
|
+
# failure_result.failure? #=> true
|
174
|
+
# failure_result.failure #=> {message: "Did not find a friend."}
|
175
175
|
module Chain
|
176
|
+
# Internal wrapper of a step definition
|
177
|
+
Step = Struct.new(:name, :operation) do
|
178
|
+
def finalize!
|
179
|
+
name.freeze
|
180
|
+
operation.finalize!
|
181
|
+
freeze
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
176
185
|
# Like {Teckel::Result Teckel::Result} but for failing Chains
|
177
186
|
#
|
178
187
|
# When a Chain fails, it stores the failed +Operation+ and it's name.
|
179
188
|
class StepFailure
|
180
189
|
extend Forwardable
|
181
190
|
|
182
|
-
def initialize(step,
|
183
|
-
@step, @
|
191
|
+
def initialize(step, result)
|
192
|
+
@step, @result = step, result
|
184
193
|
end
|
185
194
|
|
186
|
-
# @!
|
187
|
-
#
|
188
|
-
|
195
|
+
# @!method step
|
196
|
+
# Delegates to +step.name+
|
197
|
+
# @return [String,Symbol] The name of the failed operation.
|
198
|
+
def_delegator :@step, :name, :step
|
189
199
|
|
190
|
-
# @!
|
191
|
-
#
|
192
|
-
|
200
|
+
# @!method operation
|
201
|
+
# Delegates to +step.operation+
|
202
|
+
# @return [Teckel::Operation] The failed Operation class.
|
203
|
+
def_delegator :@step, :operation
|
193
204
|
|
194
205
|
# @!attribute result [R]
|
195
206
|
# @return [Teckel::Result] the failure Result
|
@@ -232,10 +243,10 @@ module Teckel
|
|
232
243
|
def call(input)
|
233
244
|
last_result = input
|
234
245
|
failed = nil
|
235
|
-
steps.each do |
|
236
|
-
last_result = step.call(last_result)
|
246
|
+
steps.each do |step|
|
247
|
+
last_result = step.operation.call(last_result)
|
237
248
|
if last_result.failure?
|
238
|
-
failed = StepFailure.new(step,
|
249
|
+
failed = StepFailure.new(step, last_result)
|
239
250
|
break
|
240
251
|
end
|
241
252
|
end
|
@@ -248,20 +259,20 @@ module Teckel
|
|
248
259
|
# The expected input for this chain
|
249
260
|
# @return [Class] The {Teckel::Operation.input} of the first step
|
250
261
|
def input
|
251
|
-
|
262
|
+
steps.first&.operation&.input
|
252
263
|
end
|
253
264
|
|
254
265
|
# The expected output for this chain
|
255
266
|
# @return [Class] The {Teckel::Operation.output} of the last step
|
256
267
|
def output
|
257
|
-
|
268
|
+
steps.last&.operation&.output
|
258
269
|
end
|
259
270
|
|
260
271
|
# List of all possible errors
|
261
272
|
# @return [<Class>] List of all steps {Teckel::Operation.error}s
|
262
273
|
def errors
|
263
|
-
|
264
|
-
err =
|
274
|
+
steps.each_with_object([]) do |step, m|
|
275
|
+
err = step.operation.error
|
265
276
|
m << err if err
|
266
277
|
end
|
267
278
|
end
|
@@ -273,7 +284,14 @@ module Teckel
|
|
273
284
|
# @param operation [Operation::Results] The operation to call.
|
274
285
|
# Must return a {Teckel::Result} object.
|
275
286
|
def step(name, operation)
|
276
|
-
|
287
|
+
steps << Step.new(name, operation)
|
288
|
+
end
|
289
|
+
|
290
|
+
# Get the list of defined steps
|
291
|
+
#
|
292
|
+
# @return [<Step>]
|
293
|
+
def steps
|
294
|
+
@config.for(:steps) { [] }
|
277
295
|
end
|
278
296
|
|
279
297
|
# Set or get the optional around hook.
|
@@ -339,7 +357,7 @@ module Teckel
|
|
339
357
|
# either the success or failure value. Note that the {StepFailure} behaves
|
340
358
|
# just like a {Teckel::Result} with added information about which step failed.
|
341
359
|
def call(input)
|
342
|
-
runner = self.runner.new(
|
360
|
+
runner = self.runner.new(steps)
|
343
361
|
if around
|
344
362
|
around.call(runner, input)
|
345
363
|
else
|
@@ -347,33 +365,56 @@ module Teckel
|
|
347
365
|
end
|
348
366
|
end
|
349
367
|
|
368
|
+
# @!visibility private
|
369
|
+
# @return [void]
|
370
|
+
def define!
|
371
|
+
raise MissingConfigError, "Cannot define Chain with no steps" if steps.empty?
|
372
|
+
|
373
|
+
%i[around runner].each { |e| public_send(e) }
|
374
|
+
steps.each(&:finalize!)
|
375
|
+
nil
|
376
|
+
end
|
377
|
+
|
350
378
|
# Disallow any further changes to this Chain.
|
379
|
+
# @note This also calls +finalize!+ on all Operations defined as steps.
|
351
380
|
#
|
352
381
|
# @return [self] Frozen self
|
353
382
|
# @!visibility public
|
354
383
|
def finalize!
|
355
|
-
|
356
|
-
|
384
|
+
define!
|
385
|
+
steps.freeze
|
357
386
|
@config.freeze
|
358
|
-
|
387
|
+
freeze
|
359
388
|
end
|
360
389
|
|
390
|
+
# Produces a shallow copy of this chain.
|
391
|
+
# It's {around}, {runner} and {steps} will get +dup+'ed
|
392
|
+
#
|
393
|
+
# @return [self]
|
361
394
|
# @!visibility public
|
362
395
|
def dup
|
363
396
|
super.tap do |copy|
|
364
|
-
|
365
|
-
|
397
|
+
new_config = @config.dup
|
398
|
+
new_config.replace(:steps) { steps.dup }
|
399
|
+
|
400
|
+
copy.instance_variable_set(:@config, new_config)
|
366
401
|
end
|
367
402
|
end
|
368
403
|
|
404
|
+
# Produces a clone of this chain.
|
405
|
+
# It's {around}, {runner} and {steps} will get +dup+'ed
|
406
|
+
#
|
407
|
+
# @return [self]
|
369
408
|
# @!visibility public
|
370
409
|
def clone
|
371
410
|
if frozen?
|
372
411
|
super
|
373
412
|
else
|
374
413
|
super.tap do |copy|
|
375
|
-
|
376
|
-
|
414
|
+
new_config = @config.dup
|
415
|
+
new_config.replace(:steps) { steps.dup }
|
416
|
+
|
417
|
+
copy.instance_variable_set(:@config, new_config)
|
377
418
|
end
|
378
419
|
end
|
379
420
|
end
|
@@ -383,7 +424,6 @@ module Teckel
|
|
383
424
|
receiver.extend ClassMethods
|
384
425
|
|
385
426
|
receiver.class_eval do
|
386
|
-
@steps = []
|
387
427
|
@config = Config.new
|
388
428
|
end
|
389
429
|
end
|
data/lib/teckel/config.rb
CHANGED
@@ -30,6 +30,14 @@ module Teckel
|
|
30
30
|
@config = {}
|
31
31
|
end
|
32
32
|
|
33
|
+
# Allow getting or setting a value, with some weird rules:
|
34
|
+
# - The +value+ might not be +nil+
|
35
|
+
# - Setting via +value+ is allowed only once. Successive calls will raise a {FrozenConfigError}
|
36
|
+
# - Setting via +block+ works almost like {Hash#fetch}:
|
37
|
+
# - returns the existing value if key is present
|
38
|
+
# - sets (and returns) the blocks return value otherwise
|
39
|
+
# - calling without +value+ and +block+ works like {Hash#[]}
|
40
|
+
#
|
33
41
|
# @!visibility private
|
34
42
|
def for(key, value = nil, &block)
|
35
43
|
if value.nil?
|
@@ -45,6 +53,11 @@ module Teckel
|
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
56
|
+
# @!visibility private
|
57
|
+
def replace(key)
|
58
|
+
@config[key] = yield if @config.key?(key)
|
59
|
+
end
|
60
|
+
|
48
61
|
# @!visibility private
|
49
62
|
def freeze
|
50
63
|
@config.freeze
|
data/lib/teckel/operation.rb
CHANGED
@@ -283,7 +283,8 @@ module Teckel
|
|
283
283
|
end
|
284
284
|
|
285
285
|
# Convenience method for setting {#input}, {#output} or {#error} to the {None} value.
|
286
|
-
# @return [None
|
286
|
+
# @return [Object] The {Teckel::None} class.
|
287
|
+
#
|
287
288
|
# @example Enforcing nil input, output or error
|
288
289
|
# class MyOperation
|
289
290
|
# include Teckel::Operation
|
@@ -333,7 +334,7 @@ module Teckel
|
|
333
334
|
end
|
334
335
|
|
335
336
|
# @!visibility private
|
336
|
-
# @return [
|
337
|
+
# @return [void]
|
337
338
|
def define!
|
338
339
|
%i[input input_constructor output output_constructor error error_constructor runner].each { |e|
|
339
340
|
public_send(e)
|
@@ -349,11 +350,13 @@ module Teckel
|
|
349
350
|
# @!visibility public
|
350
351
|
def finalize!
|
351
352
|
define!
|
352
|
-
freeze
|
353
353
|
@config.freeze
|
354
|
-
|
354
|
+
freeze
|
355
355
|
end
|
356
356
|
|
357
|
+
# Produces a shallow copy of this operation and all it's configuration.
|
358
|
+
#
|
359
|
+
# @return [self]
|
357
360
|
# @!visibility public
|
358
361
|
def dup
|
359
362
|
super.tap do |copy|
|
@@ -361,6 +364,9 @@ module Teckel
|
|
361
364
|
end
|
362
365
|
end
|
363
366
|
|
367
|
+
# Produces a clone of this operation and all it's configuration
|
368
|
+
#
|
369
|
+
# @return [self]
|
364
370
|
# @!visibility public
|
365
371
|
def clone
|
366
372
|
if frozen?
|
@@ -384,13 +390,17 @@ module Teckel
|
|
384
390
|
end
|
385
391
|
|
386
392
|
module InstanceMethods
|
387
|
-
# Halt any further execution with a
|
393
|
+
# Halt any further execution with a output value
|
394
|
+
#
|
395
|
+
# @return a thing matching your {Operation::ClassMethods#output Operation#output} definition
|
388
396
|
# @!visibility protected
|
389
397
|
def success!(*args)
|
390
398
|
throw :success, args
|
391
399
|
end
|
392
400
|
|
393
|
-
# Halt any further execution with an
|
401
|
+
# Halt any further execution with an error value
|
402
|
+
#
|
403
|
+
# @return a thing matching your {Operation::ClassMethods#error Operation#error} definition
|
394
404
|
# @!visibility protected
|
395
405
|
def fail!(*args)
|
396
406
|
throw :failure, args
|
@@ -404,8 +414,6 @@ module Teckel
|
|
404
414
|
receiver.class_eval do
|
405
415
|
@config = Config.new
|
406
416
|
|
407
|
-
@runner = Runner
|
408
|
-
|
409
417
|
protected :success!, :fail!
|
410
418
|
end
|
411
419
|
end
|
data/lib/teckel/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teckel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Schulze
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -135,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
135
|
- !ruby/object:Gem::Version
|
136
136
|
version: '0'
|
137
137
|
requirements: []
|
138
|
-
rubygems_version: 3.
|
138
|
+
rubygems_version: 3.0.3
|
139
139
|
signing_key:
|
140
140
|
specification_version: 4
|
141
141
|
summary: Operations with enforced in/out/err data structures
|