nxt_pipeline 0.2.8 → 0.3.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/Gemfile.lock +1 -1
- data/README.md +80 -22
- data/lib/nxt_pipeline/constructor.rb +13 -0
- data/lib/nxt_pipeline/dsl.rb +44 -0
- data/lib/nxt_pipeline/pipeline.rb +13 -14
- data/lib/nxt_pipeline/step.rb +15 -14
- data/lib/nxt_pipeline/version.rb +1 -1
- data/lib/nxt_pipeline.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a0f00d3ec3878cedc3f87bbd4a95d48ac4c16dcc998ad44a4b92aa083a91ac4
|
4
|
+
data.tar.gz: '0698836b0f3f0cf567faf3cd4fded931617a4b733eb58514dab0b89bed62963c'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23410c5a8ab7c9fd036ae4c6ad9b3cca91c849b121f4afc02f0658b87b261efb0542bdbd0d687067390e4e3c7ce2ae0ae3ccfbb36a42b1a07d94855aea1263f7
|
7
|
+
data.tar.gz: cf7a641b429f16655ef6f3103995a19661e2b4c2dc7ad9029c0053a90f18ff785565646f7a6da121a70cc2d84ca7bb8b6721eb680bc354a628a951c725f1940c
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -32,26 +32,31 @@ by the step yielded to the constructor.
|
|
32
32
|
```ruby
|
33
33
|
pipeline = NxtPipeline::Pipeline.new do |p|
|
34
34
|
# Add a named constructor that will be used to execute your steps later
|
35
|
-
# All options that you pass in your step will be available through accessors in your constructor
|
36
|
-
|
37
|
-
|
35
|
+
# All options that you pass in your step will be available through accessors in your constructor
|
36
|
+
# You can call :to_s on a step to set it by default. You can later overwrite at execution for each step if needed.
|
37
|
+
p.constructor(:service, default: true) do |step, arg:|
|
38
|
+
step.to_s = step.service_class.to_s
|
39
|
+
result = step.service_class.new(options: arg).call
|
40
|
+
result && { arg: result }
|
38
41
|
end
|
39
42
|
|
40
|
-
p.constructor(:job) do |step, arg
|
41
|
-
step.job_class.perform_later(*arg) && arg
|
43
|
+
p.constructor(:job) do |step, arg:|
|
44
|
+
step.job_class.perform_later(*arg) && { arg: arg }
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
48
|
# Once a pipeline was created you can still configure it
|
46
|
-
pipeline.constructor(:call) do |step, arg
|
47
|
-
step.caller.new(arg).call
|
49
|
+
pipeline.constructor(:call) do |step, arg:|
|
50
|
+
result = step.caller.new(arg).call
|
51
|
+
result && { arg: result }
|
48
52
|
end
|
49
53
|
|
50
54
|
# same with block syntax
|
51
55
|
# You can use this to split up execution from configuration
|
52
56
|
pipeline.configure do |p|
|
53
|
-
p.constructor(:call) do |step, arg
|
54
|
-
step.caller.new(arg).call
|
57
|
+
p.constructor(:call) do |step, arg:|
|
58
|
+
result = step.caller.new(arg).call
|
59
|
+
result && { arg: result }
|
55
60
|
end
|
56
61
|
end
|
57
62
|
```
|
@@ -67,11 +72,11 @@ pipeline.step service_class: MyOtherServiceClass, to_s: 'Second step'
|
|
67
72
|
pipeline.step :job, job_class: MyJobClass # to_s is optional
|
68
73
|
pipeline.step :job, job_class: MyOtherJobClass
|
69
74
|
|
70
|
-
pipeline.step :step_name_for_better_log do |_, arg
|
75
|
+
pipeline.step :step_name_for_better_log do |_, arg:|
|
71
76
|
# ...
|
72
77
|
end
|
73
78
|
|
74
|
-
pipeline.step to_s: 'This is the same as above' do |step, arg
|
79
|
+
pipeline.step to_s: 'This is the same as above' do |step, arg:|
|
75
80
|
# ... step.to_s => 'This is the same as above'
|
76
81
|
end
|
77
82
|
```
|
@@ -102,8 +107,8 @@ You can also directly execute a pipeline with:
|
|
102
107
|
|
103
108
|
```ruby
|
104
109
|
NxtPipeline::Pipeline.execute('initial argument') do |p|
|
105
|
-
p.step do |_, arg
|
106
|
-
arg.upcase
|
110
|
+
p.step do |_, arg:|
|
111
|
+
{ arg: arg.upcase }
|
107
112
|
end
|
108
113
|
end
|
109
114
|
```
|
@@ -133,7 +138,7 @@ When the guard takes an argument the step argument is yielded.
|
|
133
138
|
|
134
139
|
```ruby
|
135
140
|
pipeline.execute('initial argument') do |p|
|
136
|
-
p.step :service, service_class: MyServiceClass, if: -> (arg) { arg == 'initial argument' }
|
141
|
+
p.step :service, service_class: MyServiceClass, if: -> (arg:) { arg == 'initial argument' }
|
137
142
|
p.step :service, service_class: MyOtherServiceClass, unless: -> { false }
|
138
143
|
end
|
139
144
|
|
@@ -145,24 +150,24 @@ Apart from defining constructors and steps you can also define error callbacks.
|
|
145
150
|
|
146
151
|
```ruby
|
147
152
|
NxtPipeline::Pipeline.new do |p|
|
148
|
-
p.step do |_, arg
|
149
|
-
arg.upcase
|
153
|
+
p.step do |_, arg:|
|
154
|
+
{ arg: arg.upcase }
|
150
155
|
end
|
151
156
|
|
152
|
-
p.on_error MyCustomError do |step,
|
157
|
+
p.on_error MyCustomError do |step, opts, error|
|
153
158
|
# First matching error callback will be executed!
|
154
159
|
end
|
155
160
|
|
156
|
-
p.on_errors ArgumentError, KeyError do |step,
|
161
|
+
p.on_errors ArgumentError, KeyError do |step, opts, error|
|
157
162
|
# First matching error callback will be executed!
|
158
163
|
end
|
159
164
|
|
160
|
-
p.on_errors YetAnotherError, halt_on_error: false do |step,
|
165
|
+
p.on_errors YetAnotherError, halt_on_error: false do |step, opts, error|
|
161
166
|
# After executing the callback the pipeline will not halt but continue to
|
162
167
|
# execute the next steps.
|
163
168
|
end
|
164
169
|
|
165
|
-
p.on_errors do |step,
|
170
|
+
p.on_errors do |step, opts, error|
|
166
171
|
# This will match all errors inheriting from StandardError
|
167
172
|
end
|
168
173
|
end
|
@@ -174,12 +179,12 @@ You can also define callbacks that run before and after the `#execute` action. B
|
|
174
179
|
|
175
180
|
```ruby
|
176
181
|
NxtPipeline::Pipeline.new do |p|
|
177
|
-
p.before_execute do |pipeline, arg
|
182
|
+
p.before_execute do |pipeline, arg:|
|
178
183
|
# Will be called from within #execute before entering the first step
|
179
184
|
# After any configure block though!
|
180
185
|
end
|
181
186
|
|
182
|
-
p.after_execute do |pipeline, arg
|
187
|
+
p.after_execute do |pipeline, arg:|
|
183
188
|
# Will be called from within #execute after executing last step
|
184
189
|
end
|
185
190
|
end
|
@@ -187,6 +192,59 @@ end
|
|
187
192
|
|
188
193
|
Note that the `after_execute` callback will not be called, when an error is raised in one of the steps. See the previous section (_Error callbacks_) for how to define callbacks that run in case of errors.
|
189
194
|
|
195
|
+
### DSL
|
196
|
+
|
197
|
+
The gem also comes with an easy DSL to make pipeline handling in your code more convenient.
|
198
|
+
Simply include NxtPipeline::Dsl in your class:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
class MyAwesomeClass
|
202
|
+
include NxtPipeline::Dsl
|
203
|
+
|
204
|
+
# register a pipeline with a name and a block
|
205
|
+
pipeline :validation do |p|
|
206
|
+
pipeline.constructor(:validate) do |step, arg:|
|
207
|
+
result = step.validator.call(arg: arg)
|
208
|
+
result && { arg: result }
|
209
|
+
end
|
210
|
+
|
211
|
+
pipeline.step :validate, validator: NameValidator
|
212
|
+
pipeline.step :validate, validator: AdressValidator
|
213
|
+
pipeline.step :validate, validator: BankAccountValidator
|
214
|
+
pipeline.step :validate, validator: PhoneNumberValidator
|
215
|
+
|
216
|
+
p.on_error ValidationError do |step, opts, error|
|
217
|
+
# ...
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
pipeline :execution do |p|
|
222
|
+
p.step do |_, arg:|
|
223
|
+
{ arg: arg.upcase }
|
224
|
+
end
|
225
|
+
|
226
|
+
p.on_error MyCustomError do |step, opts, error|
|
227
|
+
# nesting pipelines also works
|
228
|
+
pipeline(:error).execute(error)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
pipeline :error do |p|
|
233
|
+
p.step do |_, error|
|
234
|
+
error # do something here
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def call(arg)
|
239
|
+
# execute a pipeline simply by fetching it and calling execute on it as you would normally
|
240
|
+
pipeline(:execution).execute(arg: arg)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
## Topics
|
246
|
+
- Step orchestration (chainable steps)
|
247
|
+
- Constructors should take arg as first and step as second arg
|
190
248
|
|
191
249
|
## Development
|
192
250
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module NxtPipeline
|
2
|
+
module Dsl
|
3
|
+
module ClassMethods
|
4
|
+
def pipeline(name = :default, &block)
|
5
|
+
name = name.to_sym
|
6
|
+
|
7
|
+
if block_given?
|
8
|
+
raise_already_registered_error(name) if pipeline_registry.key?(name)
|
9
|
+
pipeline_registry[name] = block
|
10
|
+
else
|
11
|
+
config = pipeline_registry.fetch(name) { raise KeyError, "No pipeline #{name} registered"}
|
12
|
+
NxtPipeline::Pipeline.new(&config)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def pipeline!(name, &block)
|
17
|
+
raise ArgumentError, "No block given!" unless block_given?
|
18
|
+
pipeline_registry[name] = block
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def inherited(child)
|
24
|
+
child.instance_variable_set(:@pipeline_registry, pipeline_registry.clone)
|
25
|
+
end
|
26
|
+
|
27
|
+
def raise_already_registered_error(name)
|
28
|
+
raise KeyError, "Already registered a pipeline #{name}. Call pipeline! to overwrite already registered pipelines"
|
29
|
+
end
|
30
|
+
|
31
|
+
def pipeline_registry
|
32
|
+
@pipeline_registry ||= ActiveSupport::HashWithIndifferentAccess.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.included(base)
|
37
|
+
base.extend(ClassMethods)
|
38
|
+
|
39
|
+
def pipeline(name = :default)
|
40
|
+
self.class.pipeline(name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module NxtPipeline
|
2
2
|
class Pipeline
|
3
|
-
def self.execute(opts, &block)
|
4
|
-
new(&block).execute(opts)
|
3
|
+
def self.execute(**opts, &block)
|
4
|
+
new(&block).execute(**opts)
|
5
5
|
end
|
6
6
|
|
7
7
|
def initialize(&block)
|
@@ -20,12 +20,11 @@ module NxtPipeline
|
|
20
20
|
|
21
21
|
attr_accessor :logger, :steps
|
22
22
|
|
23
|
-
# register steps with name and block
|
24
23
|
def constructor(name, **opts, &constructor)
|
25
24
|
name = name.to_sym
|
26
25
|
raise StandardError, "Already registered step :#{name}" if registry[name]
|
27
26
|
|
28
|
-
registry[name] = constructor
|
27
|
+
registry[name] = Constructor.new(name, opts, constructor)
|
29
28
|
|
30
29
|
return unless opts.fetch(:default, false)
|
31
30
|
set_default_constructor(name)
|
@@ -48,7 +47,7 @@ module NxtPipeline
|
|
48
47
|
# fall back to :inline if no type is given
|
49
48
|
type ||= :inline
|
50
49
|
opts.reverse_merge!(to_s: type)
|
51
|
-
block
|
50
|
+
Constructor.new(type, opts, block)
|
52
51
|
else
|
53
52
|
if type
|
54
53
|
raise_reserved_type_inline_error if type == :inline
|
@@ -64,19 +63,19 @@ module NxtPipeline
|
|
64
63
|
steps << Step.new(type, constructor, steps.count, **opts)
|
65
64
|
end
|
66
65
|
|
67
|
-
def execute(
|
66
|
+
def execute(**opts, &block)
|
68
67
|
reset
|
69
68
|
|
70
69
|
configure(&block) if block_given?
|
71
|
-
before_execute_callback.call(self,
|
70
|
+
before_execute_callback.call(self, opts) if before_execute_callback.respond_to?(:call)
|
72
71
|
|
73
|
-
result = steps.inject(
|
74
|
-
execute_step(step,
|
72
|
+
result = steps.inject(opts) do |options, step|
|
73
|
+
execute_step(step, **options)
|
75
74
|
rescue StandardError => error
|
76
75
|
callback = find_error_callback(error)
|
77
76
|
raise unless callback && callback.continue_after_error?
|
78
77
|
handle_step_error(error)
|
79
|
-
|
78
|
+
options
|
80
79
|
end
|
81
80
|
|
82
81
|
after_execute_callback.call(self, result) if after_execute_callback.respond_to?(:call)
|
@@ -123,12 +122,12 @@ module NxtPipeline
|
|
123
122
|
@default_constructor ||= registry[default_constructor_name.to_sym]
|
124
123
|
end
|
125
124
|
|
126
|
-
def execute_step(step,
|
125
|
+
def execute_step(step, **opts)
|
127
126
|
self.current_step = step
|
128
|
-
self.current_arg =
|
129
|
-
result = step.execute(
|
127
|
+
self.current_arg = opts
|
128
|
+
result = step.execute(**opts)
|
130
129
|
log_step(step)
|
131
|
-
result ||
|
130
|
+
result || opts
|
132
131
|
end
|
133
132
|
|
134
133
|
def find_error_callback(error)
|
data/lib/nxt_pipeline/step.rb
CHANGED
@@ -5,22 +5,27 @@ module NxtPipeline
|
|
5
5
|
|
6
6
|
@type = type
|
7
7
|
@index = index
|
8
|
-
@result = nil
|
9
8
|
@opts = opts
|
10
|
-
@status = nil
|
11
9
|
@constructor = constructor
|
10
|
+
@to_s = "#{opts.merge(type: type)}"
|
11
|
+
|
12
|
+
@status = nil
|
13
|
+
@result = nil
|
12
14
|
@error = nil
|
13
15
|
end
|
14
16
|
|
15
17
|
attr_reader :type, :result, :status, :error, :opts, :index
|
18
|
+
attr_accessor :to_s
|
19
|
+
|
20
|
+
alias_method :name=, :to_s=
|
16
21
|
|
17
|
-
def execute(
|
18
|
-
guard_args = [
|
22
|
+
def execute(**opts)
|
23
|
+
guard_args = [opts, self]
|
19
24
|
if_guard_args = guard_args.take(if_guard.arity)
|
20
25
|
unless_guard_guard_args = guard_args.take(unless_guard.arity)
|
21
26
|
|
22
|
-
if unless_guard.call(*unless_guard_guard_args) && if_guard.call(*if_guard_args)
|
23
|
-
self.result = constructor.call(self,
|
27
|
+
if !unless_guard.call(*unless_guard_guard_args) && if_guard.call(*if_guard_args)
|
28
|
+
self.result = constructor.call(self, **opts)
|
24
29
|
end
|
25
30
|
|
26
31
|
set_status
|
@@ -31,10 +36,6 @@ module NxtPipeline
|
|
31
36
|
raise
|
32
37
|
end
|
33
38
|
|
34
|
-
def to_s
|
35
|
-
"#{opts.merge(type: type)}"
|
36
|
-
end
|
37
|
-
|
38
39
|
def type?(potential_type)
|
39
40
|
type.to_sym == potential_type.to_sym
|
40
41
|
end
|
@@ -45,15 +46,15 @@ module NxtPipeline
|
|
45
46
|
attr_reader :constructor
|
46
47
|
|
47
48
|
def if_guard
|
48
|
-
opts.fetch(:if) {
|
49
|
+
opts.fetch(:if) { guard(true) }
|
49
50
|
end
|
50
51
|
|
51
52
|
def unless_guard
|
52
|
-
opts.fetch(:unless) {
|
53
|
+
opts.fetch(:unless) { guard(false) }
|
53
54
|
end
|
54
55
|
|
55
|
-
def
|
56
|
-
-> {
|
56
|
+
def guard(result)
|
57
|
+
-> { result }
|
57
58
|
end
|
58
59
|
|
59
60
|
def define_attr_readers(opts)
|
data/lib/nxt_pipeline/version.rb
CHANGED
data/lib/nxt_pipeline.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'active_support/all'
|
2
2
|
require 'nxt_pipeline/version'
|
3
3
|
require 'nxt_pipeline/logger'
|
4
|
+
require 'nxt_pipeline/constructor'
|
4
5
|
require 'nxt_pipeline/pipeline'
|
5
6
|
require 'nxt_pipeline/step'
|
6
7
|
require 'nxt_pipeline/error_callback'
|
8
|
+
require 'nxt_pipeline/dsl'
|
7
9
|
|
8
10
|
module NxtPipeline
|
9
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nxt_pipeline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nils Sommer
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2019-08-
|
13
|
+
date: 2019-08-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -120,6 +120,8 @@ files:
|
|
120
120
|
- bin/rspec
|
121
121
|
- bin/setup
|
122
122
|
- lib/nxt_pipeline.rb
|
123
|
+
- lib/nxt_pipeline/constructor.rb
|
124
|
+
- lib/nxt_pipeline/dsl.rb
|
123
125
|
- lib/nxt_pipeline/error_callback.rb
|
124
126
|
- lib/nxt_pipeline/logger.rb
|
125
127
|
- lib/nxt_pipeline/pipeline.rb
|