nxt_pipeline 0.2.8 → 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/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
|