nxt_pipeline 0.3.0 → 0.4.3
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/.circleci/config.yml +5 -1
- data/.pryrc +6 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +15 -2
- data/Gemfile.lock +32 -26
- data/README.md +41 -81
- data/lib/nxt_pipeline.rb +0 -1
- data/lib/nxt_pipeline/constructor.rb +10 -3
- data/lib/nxt_pipeline/error_callback.rb +2 -2
- data/lib/nxt_pipeline/pipeline.rb +50 -30
- data/lib/nxt_pipeline/step.rb +72 -18
- data/lib/nxt_pipeline/version.rb +2 -1
- data/nxt_pipeline.gemspec +3 -3
- metadata +9 -9
- data/lib/nxt_pipeline/dsl.rb +0 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f47be27dd03af5d8a11274b4667a40e2a0c14b4be0a373dd90e1fc7deb93c03
|
|
4
|
+
data.tar.gz: 6964e21671bb458e187dd3289538e49243e42b06c38c804ffc2b69ed94146612
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5ba8339e42e2e485e9c0c00a35d6df56324b1145605d564039a4931d384d0297ef44b3613c8e45e83dfcfea83f0ed7b5a377f3a6e89f2a03e5b9dbfcc6e47b9a
|
|
7
|
+
data.tar.gz: f9c938b9302bf9f2b4c126ddaa1b978b99c2f02fe1cdbc6d17be307135a22a504e002c6b110fa0b2eb92a04b5623f4293c8f92575c82ad2240f3de0b4a49a8ad
|
data/.circleci/config.yml
CHANGED
|
@@ -7,7 +7,9 @@ jobs:
|
|
|
7
7
|
build:
|
|
8
8
|
docker:
|
|
9
9
|
# specify the version you desire here
|
|
10
|
-
- image: circleci/ruby:2.
|
|
10
|
+
- image: circleci/ruby:2.7.0-node
|
|
11
|
+
environment:
|
|
12
|
+
BUNDLER_VERSION: 2.1.4
|
|
11
13
|
|
|
12
14
|
working_directory: ~/repo
|
|
13
15
|
|
|
@@ -19,6 +21,8 @@ jobs:
|
|
|
19
21
|
keys:
|
|
20
22
|
- v1-dependencies-{{ checksum "Gemfile.lock" }}
|
|
21
23
|
|
|
24
|
+
- run: gem install bundler --version $BUNDLER_VERSION
|
|
25
|
+
|
|
22
26
|
- run:
|
|
23
27
|
name: install dependencies
|
|
24
28
|
command: |
|
data/.pryrc
ADDED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.7.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
##
|
|
1
|
+
## nxt_pipeline 0.4.3 (October 20, 2020)
|
|
2
|
+
|
|
3
|
+
Add new attribute readers on step object.
|
|
4
|
+
|
|
5
|
+
After executing a step execution_finished_at execution_started_at and execution_duration
|
|
6
|
+
will be set and can be accessed via attribute readers.
|
|
7
|
+
|
|
8
|
+
## nxt_pipeline 0.4.2 (October 12, 2020)
|
|
9
|
+
|
|
10
|
+
* Fix bug when registering an error without passing arguments in which case the callback didn't get executed. More info: https://github.com/nxt-insurance/nxt_pipeline/issues/39
|
|
11
|
+
|
|
12
|
+
## nxt_pipeline 0.4.1 (March 13, 2020)
|
|
13
|
+
|
|
14
|
+
* Fix deprecation warnings for Ruby 2.7
|
|
2
15
|
|
|
3
16
|
## nxt_pipeline 0.2.0 (March 10, 2019)
|
|
4
17
|
|
|
@@ -14,7 +27,7 @@
|
|
|
14
27
|
Renamed `NxtPipeline::Pipeline#burst?` to `NxtPipeline::Pipeline#failed?`.
|
|
15
28
|
Renamed `NxtPipeline::Pipeline#burst_segment` to `NxtPipeline::Pipeline#failed_step`.
|
|
16
29
|
Renamed `NxtPipeline::Pipeline::rescue_segment_burst` to `NxtPipeline::Pipeline::rescue_errors`.
|
|
17
|
-
|
|
30
|
+
|
|
18
31
|
*Nils Sommer*
|
|
19
32
|
|
|
20
33
|
* Setup [guard](https://github.com/guard/guard) to run specs upon file changes during development.
|
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
nxt_pipeline (0.3
|
|
4
|
+
nxt_pipeline (0.4.3)
|
|
5
5
|
activesupport
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
activesupport (
|
|
10
|
+
activesupport (6.0.3.4)
|
|
11
11
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
12
12
|
i18n (>= 0.7, < 2)
|
|
13
13
|
minitest (~> 5.1)
|
|
14
14
|
tzinfo (~> 1.1)
|
|
15
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
|
16
|
+
byebug (11.1.1)
|
|
15
17
|
coderay (1.1.2)
|
|
16
|
-
concurrent-ruby (1.1.
|
|
18
|
+
concurrent-ruby (1.1.7)
|
|
17
19
|
diff-lcs (1.3)
|
|
18
20
|
ffi (1.11.1)
|
|
19
21
|
formatador (0.2.5)
|
|
@@ -31,59 +33,63 @@ GEM
|
|
|
31
33
|
guard (~> 2.1)
|
|
32
34
|
guard-compat (~> 1.1)
|
|
33
35
|
rspec (>= 2.99.0, < 4.0)
|
|
34
|
-
i18n (1.
|
|
36
|
+
i18n (1.8.5)
|
|
35
37
|
concurrent-ruby (~> 1.0)
|
|
36
38
|
listen (3.1.5)
|
|
37
39
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
|
38
40
|
rb-inotify (~> 0.9, >= 0.9.7)
|
|
39
41
|
ruby_dep (~> 1.2)
|
|
40
42
|
lumberjack (1.0.13)
|
|
41
|
-
method_source (0.
|
|
42
|
-
minitest (5.
|
|
43
|
+
method_source (1.0.0)
|
|
44
|
+
minitest (5.14.2)
|
|
43
45
|
nenv (0.3.0)
|
|
44
46
|
notiffany (0.1.1)
|
|
45
47
|
nenv (~> 0.1)
|
|
46
48
|
shellany (~> 0.0)
|
|
47
|
-
pry (0.
|
|
48
|
-
coderay (~> 1.1
|
|
49
|
-
method_source (~>
|
|
50
|
-
|
|
49
|
+
pry (0.13.0)
|
|
50
|
+
coderay (~> 1.1)
|
|
51
|
+
method_source (~> 1.0)
|
|
52
|
+
pry-byebug (3.9.0)
|
|
53
|
+
byebug (~> 11.0)
|
|
54
|
+
pry (~> 0.13.0)
|
|
55
|
+
rake (13.0.1)
|
|
51
56
|
rb-fsevent (0.10.3)
|
|
52
57
|
rb-inotify (0.10.0)
|
|
53
58
|
ffi (~> 1.0)
|
|
54
|
-
rspec (3.
|
|
55
|
-
rspec-core (~> 3.
|
|
56
|
-
rspec-expectations (~> 3.
|
|
57
|
-
rspec-mocks (~> 3.
|
|
58
|
-
rspec-core (3.
|
|
59
|
-
rspec-support (~> 3.
|
|
60
|
-
rspec-expectations (3.
|
|
59
|
+
rspec (3.9.0)
|
|
60
|
+
rspec-core (~> 3.9.0)
|
|
61
|
+
rspec-expectations (~> 3.9.0)
|
|
62
|
+
rspec-mocks (~> 3.9.0)
|
|
63
|
+
rspec-core (3.9.0)
|
|
64
|
+
rspec-support (~> 3.9.0)
|
|
65
|
+
rspec-expectations (3.9.0)
|
|
61
66
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
62
|
-
rspec-support (~> 3.
|
|
63
|
-
rspec-mocks (3.
|
|
67
|
+
rspec-support (~> 3.9.0)
|
|
68
|
+
rspec-mocks (3.9.0)
|
|
64
69
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
65
|
-
rspec-support (~> 3.
|
|
66
|
-
rspec-support (3.
|
|
70
|
+
rspec-support (~> 3.9.0)
|
|
71
|
+
rspec-support (3.9.0)
|
|
67
72
|
rspec_junit_formatter (0.4.1)
|
|
68
73
|
rspec-core (>= 2, < 4, != 2.12.0)
|
|
69
74
|
ruby_dep (1.5.0)
|
|
70
75
|
shellany (0.0.1)
|
|
71
76
|
thor (0.20.3)
|
|
72
77
|
thread_safe (0.3.6)
|
|
73
|
-
tzinfo (1.2.
|
|
78
|
+
tzinfo (1.2.7)
|
|
74
79
|
thread_safe (~> 0.1)
|
|
80
|
+
zeitwerk (2.4.0)
|
|
75
81
|
|
|
76
82
|
PLATFORMS
|
|
77
83
|
ruby
|
|
78
84
|
|
|
79
85
|
DEPENDENCIES
|
|
80
|
-
bundler (~> 1
|
|
86
|
+
bundler (~> 2.1)
|
|
81
87
|
guard-rspec
|
|
82
88
|
nxt_pipeline!
|
|
83
|
-
pry
|
|
84
|
-
rake (~>
|
|
89
|
+
pry-byebug
|
|
90
|
+
rake (~> 13.0)
|
|
85
91
|
rspec (~> 3.0)
|
|
86
92
|
rspec_junit_formatter
|
|
87
93
|
|
|
88
94
|
BUNDLED WITH
|
|
89
|
-
1.
|
|
95
|
+
2.1.4
|
data/README.md
CHANGED
|
@@ -24,35 +24,35 @@ Or install it yourself as:
|
|
|
24
24
|
|
|
25
25
|
### Constructors
|
|
26
26
|
|
|
27
|
-
First you probably want to configure a pipeline so that it can execute your steps.
|
|
28
|
-
Therefore you want to define constructors for your steps. Constructors take a name
|
|
27
|
+
First you probably want to configure a pipeline so that it can execute your steps.
|
|
28
|
+
Therefore you want to define constructors for your steps. Constructors take a name
|
|
29
29
|
as the first argument and step options as the second. All step options are being exposed
|
|
30
|
-
by the step yielded to the constructor.
|
|
30
|
+
by the step yielded to the constructor.
|
|
31
31
|
|
|
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
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.
|
|
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
37
|
p.constructor(:service, default: true) do |step, arg:|
|
|
38
38
|
step.to_s = step.service_class.to_s
|
|
39
39
|
result = step.service_class.new(options: arg).call
|
|
40
40
|
result && { arg: result }
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
p.constructor(:job) do |step, arg:|
|
|
44
44
|
step.job_class.perform_later(*arg) && { arg: arg }
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# Once a pipeline was created you can still configure it
|
|
48
|
+
# Once a pipeline was created you can still configure it
|
|
49
49
|
pipeline.constructor(:call) do |step, arg:|
|
|
50
50
|
result = step.caller.new(arg).call
|
|
51
51
|
result && { arg: result }
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# same with block syntax
|
|
55
|
-
# You can use this to split up execution from configuration
|
|
54
|
+
# same with block syntax
|
|
55
|
+
# You can use this to split up execution from configuration
|
|
56
56
|
pipeline.configure do |p|
|
|
57
57
|
p.constructor(:call) do |step, arg:|
|
|
58
58
|
result = step.caller.new(arg).call
|
|
@@ -61,13 +61,13 @@ pipeline.configure do |p|
|
|
|
61
61
|
end
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
### Defining steps
|
|
64
|
+
### Defining steps
|
|
65
65
|
|
|
66
66
|
Once your pipeline knows how to execute your steps you can add those.
|
|
67
67
|
|
|
68
68
|
```ruby
|
|
69
69
|
pipeline.step :service, service_class: MyServiceClass, to_s: 'First step'
|
|
70
|
-
pipeline.step service_class: MyOtherServiceClass, to_s: 'Second step'
|
|
70
|
+
pipeline.step service_class: MyOtherServiceClass, to_s: 'Second step'
|
|
71
71
|
# ^ Since service is the default step you don't have to specify it the step type each time
|
|
72
72
|
pipeline.step :job, job_class: MyJobClass # to_s is optional
|
|
73
73
|
pipeline.step :job, job_class: MyOtherJobClass
|
|
@@ -82,19 +82,19 @@ end
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
You can also define inline steps, meaning the block will be executed. When you do not provide a :to_s option, type
|
|
85
|
-
will be used as :to_s option per default. When no type was given for an inline block the type of the inline block
|
|
86
|
-
will be set to :inline.
|
|
85
|
+
will be used as :to_s option per default. When no type was given for an inline block the type of the inline block
|
|
86
|
+
will be set to :inline.
|
|
87
87
|
|
|
88
88
|
### Execution
|
|
89
89
|
|
|
90
|
-
You can then execute the steps with:
|
|
90
|
+
You can then execute the steps with:
|
|
91
91
|
|
|
92
92
|
```ruby
|
|
93
|
-
pipeline.execute('initial argument')
|
|
93
|
+
pipeline.execute(arg: 'initial argument')
|
|
94
94
|
|
|
95
95
|
# Or run the steps directly using block syntax
|
|
96
96
|
|
|
97
|
-
pipeline.execute('initial argument') do |p|
|
|
97
|
+
pipeline.execute(arg: 'initial argument') do |p|
|
|
98
98
|
p.step :service, service_class: MyServiceClass, to_s: 'First step'
|
|
99
99
|
p.step :service, service_class: MyOtherServiceClass, to_s: 'Second step'
|
|
100
100
|
p.step :job, job_class: MyJobClass # to_s is optional
|
|
@@ -106,18 +106,18 @@ end
|
|
|
106
106
|
You can also directly execute a pipeline with:
|
|
107
107
|
|
|
108
108
|
```ruby
|
|
109
|
-
NxtPipeline::Pipeline.execute('initial argument') do |p|
|
|
109
|
+
NxtPipeline::Pipeline.execute(arg: 'initial argument') do |p|
|
|
110
110
|
p.step do |_, arg:|
|
|
111
111
|
{ arg: arg.upcase }
|
|
112
112
|
end
|
|
113
113
|
end
|
|
114
|
-
```
|
|
114
|
+
```
|
|
115
115
|
|
|
116
|
-
You can query the steps of your pipeline simply by calling `pipeline.steps`. A NxtPipeline::Step will provide you with
|
|
117
|
-
an interface to it's type, options, status (:success, :skipped, :failed), result, error and the index in the pipeline.
|
|
116
|
+
You can query the steps of your pipeline simply by calling `pipeline.steps`. A NxtPipeline::Step will provide you with
|
|
117
|
+
an interface to it's type, options, status (:success, :skipped, :failed), execution_finished_at execution_started_at, execution_duration, result, error and the index in the pipeline.
|
|
118
118
|
|
|
119
119
|
```
|
|
120
|
-
pipeline.steps.first
|
|
120
|
+
pipeline.steps.first
|
|
121
121
|
# will give you something like this:
|
|
122
122
|
|
|
123
123
|
#<NxtPipeline::Step:0x00007f83eb399448
|
|
@@ -128,20 +128,23 @@ pipeline.steps.first
|
|
|
128
128
|
@opts={:to_s=>:transformer, :method=>:upcase},
|
|
129
129
|
@result=nil,
|
|
130
130
|
@status=nil,
|
|
131
|
-
@type=:transformer
|
|
132
|
-
|
|
131
|
+
@type=:transformer
|
|
132
|
+
@execution_duration=1.0e-05,
|
|
133
|
+
@execution_finished_at=2020-10-22 15:52:55.806417 +0100,
|
|
134
|
+
@execution_started_at=2020-10-22 15:52:55.806407 +0100,>
|
|
135
|
+
```
|
|
133
136
|
|
|
134
137
|
### Guard clauses
|
|
135
138
|
|
|
136
139
|
You can also define guard clauses that take a proc to prevent the execution of a step.
|
|
137
|
-
When the guard takes an argument the step argument is yielded.
|
|
140
|
+
When the guard takes an argument the step argument is yielded.
|
|
138
141
|
|
|
139
142
|
```ruby
|
|
140
|
-
pipeline.execute('initial argument') do |p|
|
|
143
|
+
pipeline.execute(arg: 'initial argument') do |p|
|
|
141
144
|
p.step :service, service_class: MyServiceClass, if: -> (arg:) { arg == 'initial argument' }
|
|
142
145
|
p.step :service, service_class: MyOtherServiceClass, unless: -> { false }
|
|
143
146
|
end
|
|
144
|
-
|
|
147
|
+
|
|
145
148
|
```
|
|
146
149
|
|
|
147
150
|
### Error callbacks
|
|
@@ -153,25 +156,25 @@ NxtPipeline::Pipeline.new do |p|
|
|
|
153
156
|
p.step do |_, arg:|
|
|
154
157
|
{ arg: arg.upcase }
|
|
155
158
|
end
|
|
156
|
-
|
|
159
|
+
|
|
157
160
|
p.on_error MyCustomError do |step, opts, error|
|
|
158
161
|
# First matching error callback will be executed!
|
|
159
162
|
end
|
|
160
|
-
|
|
163
|
+
|
|
161
164
|
p.on_errors ArgumentError, KeyError do |step, opts, error|
|
|
162
165
|
# First matching error callback will be executed!
|
|
163
166
|
end
|
|
164
|
-
|
|
167
|
+
|
|
165
168
|
p.on_errors YetAnotherError, halt_on_error: false do |step, opts, error|
|
|
166
169
|
# After executing the callback the pipeline will not halt but continue to
|
|
167
170
|
# execute the next steps.
|
|
168
171
|
end
|
|
169
|
-
|
|
172
|
+
|
|
170
173
|
p.on_errors do |step, opts, error|
|
|
171
174
|
# This will match all errors inheriting from StandardError
|
|
172
175
|
end
|
|
173
176
|
end
|
|
174
|
-
```
|
|
177
|
+
```
|
|
175
178
|
|
|
176
179
|
### Before and After callbacks
|
|
177
180
|
|
|
@@ -181,70 +184,27 @@ You can also define callbacks that run before and after the `#execute` action. B
|
|
|
181
184
|
NxtPipeline::Pipeline.new do |p|
|
|
182
185
|
p.before_execute do |pipeline, arg:|
|
|
183
186
|
# Will be called from within #execute before entering the first step
|
|
184
|
-
# After any configure block though!
|
|
187
|
+
# After any configure block though!
|
|
185
188
|
end
|
|
186
|
-
|
|
189
|
+
|
|
187
190
|
p.after_execute do |pipeline, arg:|
|
|
188
191
|
# Will be called from within #execute after executing last step
|
|
189
192
|
end
|
|
190
193
|
end
|
|
191
194
|
```
|
|
192
195
|
|
|
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.
|
|
196
|
+
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.
|
|
194
197
|
|
|
195
|
-
###
|
|
198
|
+
### Step resolvers
|
|
196
199
|
|
|
197
|
-
|
|
198
|
-
|
|
200
|
+
NxtPipeline is using so called step_resolvers to find the constructor for a given step by the arguments passed in.
|
|
201
|
+
You can also use this if you are not fine with resolving the constructor from the step argument. Check out the
|
|
202
|
+
`nxt_pipeline/spec/step_resolver_spec.rb` for examples how you can implement your own step_resolvers.
|
|
199
203
|
|
|
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
204
|
|
|
245
205
|
## Topics
|
|
246
206
|
- Step orchestration (chainable steps)
|
|
247
|
-
- Constructors should take arg as first and step as second arg
|
|
207
|
+
- Constructors should take arg as first and step as second arg
|
|
248
208
|
|
|
249
209
|
## Development
|
|
250
210
|
|
data/lib/nxt_pipeline.rb
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
module NxtPipeline
|
|
2
2
|
class Constructor
|
|
3
|
-
def initialize(name, opts, block)
|
|
3
|
+
def initialize(name, **opts, &block)
|
|
4
4
|
@name = name
|
|
5
5
|
@block = block
|
|
6
6
|
@opts = opts
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
attr_reader :opts, :block
|
|
10
|
-
|
|
11
|
-
delegate :
|
|
10
|
+
|
|
11
|
+
delegate :arity, to: :block
|
|
12
|
+
|
|
13
|
+
def call(*args, **opts, &block)
|
|
14
|
+
# ActiveSupport's #delegate does not properly handle keyword arg passing
|
|
15
|
+
# in the latest released version. Thefore we bypass delegation by reimplementing
|
|
16
|
+
# the method ourselves. This is already fixed in Rails master though.
|
|
17
|
+
self.block.call(*args, **opts, &block)
|
|
18
|
+
end
|
|
12
19
|
end
|
|
13
20
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module NxtPipeline
|
|
2
2
|
class ErrorCallback
|
|
3
|
-
def initialize(errors, halt_on_error, callback)
|
|
4
|
-
@errors = errors
|
|
3
|
+
def initialize(errors, halt_on_error, &callback)
|
|
4
|
+
@errors = errors.any? ? errors : [StandardError]
|
|
5
5
|
@halt_on_error = halt_on_error
|
|
6
6
|
@callback = callback
|
|
7
7
|
end
|
|
@@ -4,14 +4,15 @@ module NxtPipeline
|
|
|
4
4
|
new(&block).execute(**opts)
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
def initialize(&block)
|
|
7
|
+
def initialize(step_resolvers = default_step_resolvers, &block)
|
|
8
8
|
@steps = []
|
|
9
9
|
@error_callbacks = []
|
|
10
10
|
@logger = Logger.new
|
|
11
11
|
@current_step = nil
|
|
12
12
|
@current_arg = nil
|
|
13
13
|
@default_constructor_name = nil
|
|
14
|
-
@
|
|
14
|
+
@constructors = {}
|
|
15
|
+
@step_resolvers = step_resolvers
|
|
15
16
|
|
|
16
17
|
configure(&block) if block_given?
|
|
17
18
|
end
|
|
@@ -22,14 +23,18 @@ module NxtPipeline
|
|
|
22
23
|
|
|
23
24
|
def constructor(name, **opts, &constructor)
|
|
24
25
|
name = name.to_sym
|
|
25
|
-
raise StandardError, "Already registered step :#{name}" if
|
|
26
|
+
raise StandardError, "Already registered step :#{name}" if constructors[name]
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
constructors[name] = Constructor.new(name, **opts, &constructor)
|
|
28
29
|
|
|
29
30
|
return unless opts.fetch(:default, false)
|
|
30
31
|
set_default_constructor(name)
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
def step_resolver(&block)
|
|
35
|
+
step_resolvers << block
|
|
36
|
+
end
|
|
37
|
+
|
|
33
38
|
def set_default_constructor(name)
|
|
34
39
|
raise_duplicate_default_constructor if default_constructor_name.present?
|
|
35
40
|
self.default_constructor_name = name
|
|
@@ -39,43 +44,54 @@ module NxtPipeline
|
|
|
39
44
|
raise ArgumentError, 'Default step already defined'
|
|
40
45
|
end
|
|
41
46
|
|
|
42
|
-
def step(
|
|
43
|
-
type = type&.to_sym
|
|
44
|
-
|
|
47
|
+
def step(argument = nil, **opts, &block)
|
|
45
48
|
constructor = if block_given?
|
|
46
49
|
# make type the :to_s of inline steps
|
|
47
50
|
# fall back to :inline if no type is given
|
|
48
|
-
|
|
49
|
-
opts.reverse_merge!(to_s:
|
|
50
|
-
Constructor.new(
|
|
51
|
+
argument ||= :inline
|
|
52
|
+
opts.reverse_merge!(to_s: argument)
|
|
53
|
+
Constructor.new(:inline, **opts, &block)
|
|
51
54
|
else
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
constructor = step_resolvers.lazy.map do |resolver|
|
|
56
|
+
resolver.call(argument)
|
|
57
|
+
end.find(&:itself)
|
|
58
|
+
|
|
59
|
+
if constructor
|
|
60
|
+
constructor && constructors.fetch(constructor) { raise KeyError, "No step :#{argument} registered" }
|
|
61
|
+
elsif default_constructor
|
|
62
|
+
argument ||= default_constructor_name
|
|
63
|
+
default_constructor
|
|
55
64
|
else
|
|
56
|
-
|
|
57
|
-
type = default_constructor_name
|
|
58
|
-
# If none was given - raise
|
|
59
|
-
default_constructor || (raise StandardError, 'No default step registered')
|
|
65
|
+
raise StandardError, "Could not resolve step from: #{argument}"
|
|
60
66
|
end
|
|
61
67
|
end
|
|
62
68
|
|
|
63
|
-
steps << Step.new(
|
|
69
|
+
steps << Step.new(argument, constructor, steps.count, **opts)
|
|
64
70
|
end
|
|
65
71
|
|
|
66
|
-
def execute(**
|
|
72
|
+
def execute(**changeset, &block)
|
|
67
73
|
reset
|
|
68
74
|
|
|
69
75
|
configure(&block) if block_given?
|
|
70
|
-
before_execute_callback.call(self,
|
|
76
|
+
before_execute_callback.call(self, changeset) if before_execute_callback.respond_to?(:call)
|
|
71
77
|
|
|
72
|
-
result = steps.inject(
|
|
73
|
-
execute_step(step, **
|
|
78
|
+
result = steps.inject(changeset) do |changeset, step|
|
|
79
|
+
execute_step(step, **changeset)
|
|
74
80
|
rescue StandardError => error
|
|
81
|
+
logger_for_error = logger
|
|
82
|
+
|
|
83
|
+
error.define_singleton_method :details do
|
|
84
|
+
OpenStruct.new(
|
|
85
|
+
changeset: changeset,
|
|
86
|
+
logger: logger_for_error,
|
|
87
|
+
step: step
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
75
91
|
callback = find_error_callback(error)
|
|
76
92
|
raise unless callback && callback.continue_after_error?
|
|
77
93
|
handle_step_error(error)
|
|
78
|
-
|
|
94
|
+
changeset
|
|
79
95
|
end
|
|
80
96
|
|
|
81
97
|
after_execute_callback.call(self, result) if after_execute_callback.respond_to?(:call)
|
|
@@ -94,7 +110,7 @@ module NxtPipeline
|
|
|
94
110
|
end
|
|
95
111
|
|
|
96
112
|
def on_errors(*errors, halt_on_error: true, &callback)
|
|
97
|
-
error_callbacks << ErrorCallback.new(errors, halt_on_error, callback)
|
|
113
|
+
error_callbacks << ErrorCallback.new(errors, halt_on_error, &callback)
|
|
98
114
|
end
|
|
99
115
|
|
|
100
116
|
alias :on_error :on_errors
|
|
@@ -109,7 +125,7 @@ module NxtPipeline
|
|
|
109
125
|
|
|
110
126
|
private
|
|
111
127
|
|
|
112
|
-
attr_reader :error_callbacks, :
|
|
128
|
+
attr_reader :error_callbacks, :constructors, :step_resolvers
|
|
113
129
|
attr_accessor :current_step,
|
|
114
130
|
:current_arg,
|
|
115
131
|
:default_constructor_name,
|
|
@@ -119,15 +135,15 @@ module NxtPipeline
|
|
|
119
135
|
def default_constructor
|
|
120
136
|
return unless default_constructor_name
|
|
121
137
|
|
|
122
|
-
@default_constructor ||=
|
|
138
|
+
@default_constructor ||= constructors[default_constructor_name.to_sym]
|
|
123
139
|
end
|
|
124
140
|
|
|
125
|
-
def execute_step(step, **
|
|
141
|
+
def execute_step(step, **changeset)
|
|
126
142
|
self.current_step = step
|
|
127
|
-
self.current_arg =
|
|
128
|
-
result = step.execute(**
|
|
143
|
+
self.current_arg = changeset
|
|
144
|
+
result = step.execute(**changeset)
|
|
129
145
|
log_step(step)
|
|
130
|
-
result ||
|
|
146
|
+
result || changeset
|
|
131
147
|
end
|
|
132
148
|
|
|
133
149
|
def find_error_callback(error)
|
|
@@ -148,5 +164,9 @@ module NxtPipeline
|
|
|
148
164
|
def raise_reserved_type_inline_error
|
|
149
165
|
raise ArgumentError, 'Type :inline is reserved for inline steps!'
|
|
150
166
|
end
|
|
167
|
+
|
|
168
|
+
def default_step_resolvers
|
|
169
|
+
[->(step_argument) { step_argument.is_a?(Symbol) && step_argument }]
|
|
170
|
+
end
|
|
151
171
|
end
|
|
152
172
|
end
|
data/lib/nxt_pipeline/step.rb
CHANGED
|
@@ -1,49 +1,78 @@
|
|
|
1
1
|
module NxtPipeline
|
|
2
2
|
class Step
|
|
3
|
-
def initialize(
|
|
3
|
+
def initialize(argument, constructor, index, **opts)
|
|
4
4
|
define_attr_readers(opts)
|
|
5
5
|
|
|
6
|
-
@
|
|
6
|
+
@argument = argument
|
|
7
7
|
@index = index
|
|
8
8
|
@opts = opts
|
|
9
9
|
@constructor = constructor
|
|
10
|
-
@to_s = "#{opts.merge(
|
|
10
|
+
@to_s = "#{opts.merge(argument: argument)}"
|
|
11
|
+
@options_mapper = opts[:map_options]
|
|
11
12
|
|
|
12
13
|
@status = nil
|
|
13
14
|
@result = nil
|
|
14
15
|
@error = nil
|
|
16
|
+
@mapped_options = nil
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
attr_reader :
|
|
19
|
+
attr_reader :argument, :result, :status, :execution_started_at, :execution_finished_at, :execution_duration, :error, :opts, :index, :mapped_options
|
|
18
20
|
attr_accessor :to_s
|
|
19
21
|
|
|
20
22
|
alias_method :name=, :to_s=
|
|
23
|
+
alias_method :name, :to_s
|
|
21
24
|
|
|
22
|
-
def execute(**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
def execute(**changeset)
|
|
26
|
+
track_execution_time do
|
|
27
|
+
set_mapped_options(changeset)
|
|
28
|
+
guard_args = [changeset, self]
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
if evaluate_unless_guard(guard_args) && evaluate_if_guard(guard_args)
|
|
31
|
+
self.result = construct_result(changeset)
|
|
32
|
+
end
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
set_status
|
|
35
|
+
result
|
|
36
|
+
end
|
|
33
37
|
rescue StandardError => e
|
|
34
38
|
self.status = :failed
|
|
35
39
|
self.error = e
|
|
36
40
|
raise
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
def
|
|
40
|
-
|
|
43
|
+
def set_mapped_options(changeset)
|
|
44
|
+
mapper = options_mapper || default_options_mapper
|
|
45
|
+
mapper_args = [changeset, self].take(mapper.arity)
|
|
46
|
+
self.mapped_options = mapper.call(*mapper_args)
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
private
|
|
44
50
|
|
|
45
|
-
attr_writer :result, :status, :error
|
|
46
|
-
attr_reader :constructor
|
|
51
|
+
attr_writer :result, :status, :error, :mapped_options, :execution_started_at, :execution_finished_at, :execution_duration
|
|
52
|
+
attr_reader :constructor, :options_mapper
|
|
53
|
+
|
|
54
|
+
def evaluate_if_guard(args)
|
|
55
|
+
execute_callable(if_guard, args)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def evaluate_unless_guard(args)
|
|
59
|
+
!execute_callable(unless_guard, args)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def construct_result(changeset)
|
|
63
|
+
args = [self, changeset]
|
|
64
|
+
execute_callable(constructor, args)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def execute_callable(callable, args)
|
|
68
|
+
args = args.take(callable.arity)
|
|
69
|
+
|
|
70
|
+
if args.last.is_a?(Hash)
|
|
71
|
+
callable.call(*args.take(args.length - 1), **args.last)
|
|
72
|
+
else
|
|
73
|
+
callable.call(*args)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
47
76
|
|
|
48
77
|
def if_guard
|
|
49
78
|
opts.fetch(:if) { guard(true) }
|
|
@@ -68,5 +97,30 @@ module NxtPipeline
|
|
|
68
97
|
def set_status
|
|
69
98
|
self.status = result.present? ? :success : :skipped
|
|
70
99
|
end
|
|
100
|
+
|
|
101
|
+
def track_execution_time(&block)
|
|
102
|
+
set_execution_started_at
|
|
103
|
+
block.call
|
|
104
|
+
ensure
|
|
105
|
+
set_execution_finished_at
|
|
106
|
+
set_execution_duration
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def set_execution_started_at
|
|
110
|
+
self.execution_started_at = Time.current
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def set_execution_finished_at
|
|
114
|
+
self.execution_finished_at = Time.current
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def set_execution_duration
|
|
118
|
+
self.execution_duration = execution_finished_at - execution_started_at
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def default_options_mapper
|
|
122
|
+
# returns an empty hash
|
|
123
|
+
->(_) { {} }
|
|
124
|
+
end
|
|
71
125
|
end
|
|
72
|
-
end
|
|
126
|
+
end
|
data/lib/nxt_pipeline/version.rb
CHANGED
data/nxt_pipeline.gemspec
CHANGED
|
@@ -35,9 +35,9 @@ Gem::Specification.new do |spec|
|
|
|
35
35
|
spec.require_paths = ["lib"]
|
|
36
36
|
|
|
37
37
|
spec.add_dependency "activesupport"
|
|
38
|
-
spec.add_development_dependency "bundler", "~> 1
|
|
38
|
+
spec.add_development_dependency "bundler", "~> 2.1"
|
|
39
39
|
spec.add_development_dependency "guard-rspec"
|
|
40
|
-
spec.add_development_dependency "rake", "~>
|
|
40
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
41
41
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
42
|
-
spec.add_development_dependency "pry"
|
|
42
|
+
spec.add_development_dependency "pry-byebug"
|
|
43
43
|
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.3
|
|
4
|
+
version: 0.4.3
|
|
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:
|
|
13
|
+
date: 2020-10-23 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activesupport
|
|
@@ -32,14 +32,14 @@ dependencies:
|
|
|
32
32
|
requirements:
|
|
33
33
|
- - "~>"
|
|
34
34
|
- !ruby/object:Gem::Version
|
|
35
|
-
version: '1
|
|
35
|
+
version: '2.1'
|
|
36
36
|
type: :development
|
|
37
37
|
prerelease: false
|
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
39
39
|
requirements:
|
|
40
40
|
- - "~>"
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
|
-
version: '1
|
|
42
|
+
version: '2.1'
|
|
43
43
|
- !ruby/object:Gem::Dependency
|
|
44
44
|
name: guard-rspec
|
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -60,14 +60,14 @@ dependencies:
|
|
|
60
60
|
requirements:
|
|
61
61
|
- - "~>"
|
|
62
62
|
- !ruby/object:Gem::Version
|
|
63
|
-
version: '
|
|
63
|
+
version: '13.0'
|
|
64
64
|
type: :development
|
|
65
65
|
prerelease: false
|
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
|
67
67
|
requirements:
|
|
68
68
|
- - "~>"
|
|
69
69
|
- !ruby/object:Gem::Version
|
|
70
|
-
version: '
|
|
70
|
+
version: '13.0'
|
|
71
71
|
- !ruby/object:Gem::Dependency
|
|
72
72
|
name: rspec
|
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -83,7 +83,7 @@ dependencies:
|
|
|
83
83
|
- !ruby/object:Gem::Version
|
|
84
84
|
version: '3.0'
|
|
85
85
|
- !ruby/object:Gem::Dependency
|
|
86
|
-
name: pry
|
|
86
|
+
name: pry-byebug
|
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
|
88
88
|
requirements:
|
|
89
89
|
- - ">="
|
|
@@ -105,6 +105,7 @@ extra_rdoc_files: []
|
|
|
105
105
|
files:
|
|
106
106
|
- ".circleci/config.yml"
|
|
107
107
|
- ".gitignore"
|
|
108
|
+
- ".pryrc"
|
|
108
109
|
- ".rspec"
|
|
109
110
|
- ".ruby-version"
|
|
110
111
|
- ".travis.yml"
|
|
@@ -121,7 +122,6 @@ files:
|
|
|
121
122
|
- bin/setup
|
|
122
123
|
- lib/nxt_pipeline.rb
|
|
123
124
|
- lib/nxt_pipeline/constructor.rb
|
|
124
|
-
- lib/nxt_pipeline/dsl.rb
|
|
125
125
|
- lib/nxt_pipeline/error_callback.rb
|
|
126
126
|
- lib/nxt_pipeline/logger.rb
|
|
127
127
|
- lib/nxt_pipeline/pipeline.rb
|
|
@@ -150,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
150
150
|
- !ruby/object:Gem::Version
|
|
151
151
|
version: '0'
|
|
152
152
|
requirements: []
|
|
153
|
-
rubygems_version: 3.
|
|
153
|
+
rubygems_version: 3.1.2
|
|
154
154
|
signing_key:
|
|
155
155
|
specification_version: 4
|
|
156
156
|
summary: DSL to build Pipeline with mountable Segments to process things.
|
data/lib/nxt_pipeline/dsl.rb
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
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
|