nxt_pipeline 0.2.0 → 0.2.1
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/.gitignore +1 -0
- data/Gemfile.lock +5 -4
- data/README.md +78 -57
- data/lib/nxt_pipeline.rb +2 -5
- data/lib/nxt_pipeline/error_callback.rb +18 -0
- data/lib/nxt_pipeline/pipeline.rb +74 -58
- data/lib/nxt_pipeline/step.rb +17 -32
- data/lib/nxt_pipeline/version.rb +1 -1
- data/nxt_pipeline.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b2eac461b1a77771494ee996ece40aece6ec068070fdb282f68192119976d93
|
4
|
+
data.tar.gz: 339d7b6b27ce3b85a95d5a80558d2d4e217e0ce8b1f8965eb573f534caf6bb18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe288cb402d9f354dea2f5d2cf47640f29757b74ed90e187beb74c7fe8fa721177718e6eb052a398c54e97561a2b17dbbe7d93132231f2b285b0b93e3e3fe160
|
7
|
+
data.tar.gz: c0837560b35952dc0744df9f75ae61993910f57c97c777ff3fd28921910b080fcf8cf68f2dc6308a790c9e2bfb9f63a530229a609b2711950e6b81e5dc6eda09
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nxt_pipeline (0.2.
|
4
|
+
nxt_pipeline (0.2.1)
|
5
5
|
activesupport
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activesupport (5.2.2)
|
10
|
+
activesupport (5.2.2.1)
|
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
15
|
coderay (1.1.2)
|
16
|
-
concurrent-ruby (1.1.
|
16
|
+
concurrent-ruby (1.1.5)
|
17
17
|
diff-lcs (1.3)
|
18
18
|
ffi (1.10.0)
|
19
19
|
formatador (0.2.5)
|
@@ -31,7 +31,7 @@ GEM
|
|
31
31
|
guard (~> 2.1)
|
32
32
|
guard-compat (~> 1.1)
|
33
33
|
rspec (>= 2.99.0, < 4.0)
|
34
|
-
i18n (1.
|
34
|
+
i18n (1.6.0)
|
35
35
|
concurrent-ruby (~> 1.0)
|
36
36
|
listen (3.1.5)
|
37
37
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
@@ -80,6 +80,7 @@ DEPENDENCIES
|
|
80
80
|
bundler (~> 1.17)
|
81
81
|
guard-rspec
|
82
82
|
nxt_pipeline!
|
83
|
+
pry
|
83
84
|
rake (~> 10.0)
|
84
85
|
rspec (~> 3.0)
|
85
86
|
rspec_junit_formatter
|
data/README.md
CHANGED
@@ -22,93 +22,114 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
25
|
+
### Constructors
|
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
|
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.
|
26
31
|
|
27
32
|
```ruby
|
28
|
-
|
29
|
-
|
33
|
+
pipeline = NxtPipeline::Pipeline.new do |p|
|
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
|
+
p.constructor(:service, default: true) do |step, arg|
|
37
|
+
step.service_class.new(options: arg).call
|
38
|
+
end
|
30
39
|
|
31
|
-
step
|
32
|
-
|
40
|
+
p.constructor(:job) do |step, arg|
|
41
|
+
step.job_class.perform_later(*arg) && arg
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Once a pipeline was created you can still configure it
|
46
|
+
pipeline.constructor(:call) do |step, arg|
|
47
|
+
step.caller.new(arg).call
|
48
|
+
end
|
49
|
+
|
50
|
+
# same with block syntax
|
51
|
+
# You can use this to split up execution from configuration
|
52
|
+
pipeline.configure do |p|
|
53
|
+
p.constructor(:call) do |step, arg|
|
54
|
+
step.caller.new(arg).call
|
55
|
+
end
|
33
56
|
end
|
34
57
|
```
|
35
58
|
|
36
|
-
|
59
|
+
### Defining steps
|
60
|
+
|
61
|
+
Once your pipeline knows how to execute your steps you can add those.
|
37
62
|
|
38
63
|
```ruby
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
64
|
+
pipeline.step :service, service_class: MyServiceClass, to_s: 'First step'
|
65
|
+
pipeline.step service_class: MyOtherServiceClass, to_s: 'Second step'
|
66
|
+
# ^ Since service is the default step you don't have to specify it the step type each time
|
67
|
+
pipeline.step :job, job_class: MyJobClass # to_s is optional
|
68
|
+
pipeline.step :job, job_class: MyOtherJobClass
|
69
|
+
|
70
|
+
pipeline.step :step_name_for_better_log do |_, arg|
|
71
|
+
# ...
|
43
72
|
end
|
44
73
|
|
45
|
-
|
46
|
-
|
47
|
-
words.sort
|
48
|
-
end
|
74
|
+
pipeline.step to_s: 'This is the same as above' do |step, arg|
|
75
|
+
# ... step.to_s => 'This is the same as above'
|
49
76
|
end
|
50
77
|
```
|
51
78
|
|
52
|
-
You can
|
79
|
+
You can also define inline steps, meaning the block will be executed
|
53
80
|
|
54
|
-
|
81
|
+
### Execution
|
55
82
|
|
56
|
-
|
57
|
-
MyPipeline.new(words: %w[Ruby is awesome]).run
|
58
|
-
# => ["AWESOME", "IS", "RUBY"]
|
59
|
-
```
|
83
|
+
You can then execute the steps with:
|
60
84
|
|
61
|
-
|
85
|
+
```ruby
|
86
|
+
pipeline.execute('initial argument')
|
62
87
|
|
63
|
-
|
88
|
+
# Or run the steps directly using block syntax
|
64
89
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
after_each_step do
|
72
|
-
# Code run after each step
|
73
|
-
end
|
74
|
-
|
75
|
-
around_each_step do |pipeline, segment|
|
76
|
-
# Code run before each step
|
77
|
-
segment.call
|
78
|
-
# Code run after each step
|
79
|
-
end
|
90
|
+
pipeline.execute do |p|
|
91
|
+
p.step :service, service_class: MyServiceClass, to_s: 'First step'
|
92
|
+
p.step :service, service_class: MyOtherServiceClass, to_s: 'Second step'
|
93
|
+
p.step :job, job_class: MyJobClass # to_s is optional
|
94
|
+
p.step :job, job_class: MyOtherJobClass
|
80
95
|
end
|
81
|
-
```
|
82
|
-
|
83
|
-
The callback methods are syntactic sugar for [ActiveSupport Callbacks](https://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html), so the same rules apply regarding order or execution.
|
84
96
|
|
85
|
-
|
97
|
+
```
|
86
98
|
|
87
|
-
|
99
|
+
You can also directly execute a pipeline with:
|
88
100
|
|
89
101
|
```ruby
|
90
|
-
|
91
|
-
|
92
|
-
|
102
|
+
NxtPipeline::Pipeline.execute('initial argument') do |p|
|
103
|
+
p.step do |_, arg|
|
104
|
+
arg.upcase
|
93
105
|
end
|
94
106
|
end
|
95
|
-
```
|
107
|
+
```
|
96
108
|
|
97
|
-
|
109
|
+
### Error callbacks
|
98
110
|
|
99
|
-
|
100
|
-
pipeline = MyPipeline.new(...)
|
111
|
+
Apart from defining constructors and steps you can also define error callbacks.
|
101
112
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
113
|
+
```ruby
|
114
|
+
NxtPipeline::Pipeline.new do |p|
|
115
|
+
p.step do |_, arg|
|
116
|
+
arg.upcase
|
117
|
+
end
|
118
|
+
|
119
|
+
p.on_error MyCustomError do |step, arg, error|
|
120
|
+
# First matching error callback will be executed!
|
121
|
+
end
|
122
|
+
|
123
|
+
p.on_errors ArgumentError, KeyError do |step, arg, error|
|
124
|
+
# First matching error callback will be executed!
|
125
|
+
end
|
107
126
|
|
108
|
-
|
109
|
-
|
127
|
+
p.on_errors do |step, arg, error|
|
128
|
+
# This will match all errors inheriting from StandardError
|
129
|
+
end
|
110
130
|
end
|
111
|
-
```
|
131
|
+
```
|
132
|
+
|
112
133
|
|
113
134
|
## Development
|
114
135
|
|
data/lib/nxt_pipeline.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
require 'active_support'
|
2
|
-
require 'active_support/core_ext'
|
3
|
-
|
4
1
|
require 'nxt_pipeline/version'
|
5
|
-
require 'nxt_pipeline/step'
|
6
2
|
require 'nxt_pipeline/pipeline'
|
3
|
+
require 'nxt_pipeline/step'
|
4
|
+
require 'nxt_pipeline/error_callback'
|
7
5
|
|
8
6
|
module NxtPipeline
|
9
|
-
class Error < StandardError; end
|
10
7
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module NxtPipeline
|
2
|
+
class ErrorCallback
|
3
|
+
def initialize(errors, callback)
|
4
|
+
@errors = errors
|
5
|
+
@callback = callback
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :errors, :callback
|
9
|
+
|
10
|
+
def applies_to_error?(error)
|
11
|
+
(error.class.ancestors & errors).any?
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(step, arg, error)
|
15
|
+
callback.call(step, arg, error)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,81 +1,97 @@
|
|
1
1
|
module NxtPipeline
|
2
2
|
class Pipeline
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
def self.execute(opts, &block)
|
4
|
+
new(&block).execute(opts)
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(&block)
|
8
|
+
@steps = []
|
9
|
+
@error_callbacks = []
|
10
|
+
@log = {}
|
11
|
+
@current_step = nil
|
12
|
+
@default_constructor = nil
|
13
|
+
@registry = {}
|
14
|
+
configure(&block) if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :log
|
18
|
+
|
19
|
+
# register steps with name and block
|
20
|
+
def constructor(name, **opts, &constructor)
|
21
|
+
name = name.to_sym
|
22
|
+
raise StandardError, "Already registered step :#{name}" if registry[name]
|
23
|
+
|
24
|
+
registry[name] = constructor
|
7
25
|
|
8
|
-
|
9
|
-
|
26
|
+
return unless opts.fetch(:default, false)
|
27
|
+
default_constructor ? (raise ArgumentError, 'Default step already defined') : self.default_constructor = constructor
|
10
28
|
end
|
11
29
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
30
|
+
def step(type = nil, **opts, &block)
|
31
|
+
constructor = if block_given?
|
32
|
+
# make first argument the to_s of step if given
|
33
|
+
opts.merge!(to_s: type) if type && !opts.key?(:to_s)
|
34
|
+
block
|
35
|
+
else
|
36
|
+
if type
|
37
|
+
registry.fetch(type) { raise KeyError, "No step :#{type} registered" }
|
38
|
+
else
|
39
|
+
default_constructor || (raise StandardError, 'No default step registered')
|
18
40
|
end
|
19
41
|
end
|
20
|
-
end
|
21
42
|
|
22
|
-
|
23
|
-
failed_step.present?
|
43
|
+
steps << Step.new(constructor, **opts)
|
24
44
|
end
|
25
45
|
|
26
|
-
|
27
|
-
|
28
|
-
|
46
|
+
def execute(arg, &block)
|
47
|
+
reset_log
|
48
|
+
configure(&block) if block_given?
|
49
|
+
steps.inject(arg) do |argument, step|
|
50
|
+
execute_step(step, argument)
|
29
51
|
end
|
52
|
+
end
|
30
53
|
|
31
|
-
|
32
|
-
|
33
|
-
|
54
|
+
def on_errors(*errors, &callback)
|
55
|
+
error_callbacks << ErrorCallback.new(errors, callback)
|
56
|
+
end
|
34
57
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def before_each_step(*filters, &block)
|
41
|
-
set_callback :each_step_pipe_through, :before, *filters, &block
|
42
|
-
end
|
43
|
-
|
44
|
-
def after_each_step(*filters, &block)
|
45
|
-
set_callback :each_step_pipe_through, :after, *filters, &block
|
46
|
-
end
|
47
|
-
|
48
|
-
def around_each_step(*filters, &block)
|
49
|
-
set_callback :each_step_pipe_through, :around, *filters, &block
|
50
|
-
end
|
51
|
-
|
52
|
-
attr_reader :pipe_attr_name
|
53
|
-
attr_accessor :rescueable_block
|
54
|
-
|
55
|
-
def steps
|
56
|
-
@steps ||= []
|
57
|
-
end
|
58
|
-
|
59
|
-
def rescueable_errors
|
60
|
-
@rescueable_errors ||= []
|
61
|
-
end
|
58
|
+
alias :on_error :on_errors
|
59
|
+
|
60
|
+
def configure(&block)
|
61
|
+
block.call(self)
|
62
62
|
end
|
63
63
|
|
64
64
|
private
|
65
65
|
|
66
|
-
attr_reader :
|
66
|
+
attr_reader :error_callbacks, :registry
|
67
|
+
attr_accessor :steps, :current_step, :default_constructor
|
68
|
+
attr_writer :log
|
67
69
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
end
|
70
|
+
def execute_step(step, arg)
|
71
|
+
self.current_step = step.to_s
|
72
|
+
result = step.execute(arg)
|
72
73
|
|
73
|
-
|
74
|
-
|
74
|
+
if result # step was successful
|
75
|
+
log[current_step] = { status: :success }
|
76
|
+
return result
|
77
|
+
else # step was not successful if nil or false
|
78
|
+
log[current_step] = { status: :skipped }
|
79
|
+
return arg
|
80
|
+
end
|
81
|
+
rescue StandardError => error
|
82
|
+
log[current_step] = { status: :failed, reason: "#{error.class}: #{error.message}" }
|
83
|
+
callback = find_error_callback(error)
|
75
84
|
|
76
|
-
|
85
|
+
raise unless callback
|
86
|
+
callback.call(step, arg, error)
|
87
|
+
end
|
88
|
+
|
89
|
+
def find_error_callback(error)
|
90
|
+
error_callbacks.find { |callback| callback.applies_to_error?(error) }
|
91
|
+
end
|
77
92
|
|
78
|
-
|
93
|
+
def reset_log
|
94
|
+
self.log = {}
|
79
95
|
end
|
80
96
|
end
|
81
|
-
end
|
97
|
+
end
|
data/lib/nxt_pipeline/step.rb
CHANGED
@@ -1,47 +1,32 @@
|
|
1
1
|
module NxtPipeline
|
2
2
|
class Step
|
3
|
-
def initialize(
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def pipe_through
|
10
|
-
# Public interface of Step, to be implemented by subclasses.
|
11
|
-
raise NotImplementedError
|
3
|
+
def initialize(constructor, **opts)
|
4
|
+
define_attr_readers(opts)
|
5
|
+
@opts = opts
|
6
|
+
@constructor = constructor
|
12
7
|
end
|
13
8
|
|
14
|
-
|
15
|
-
raise ArgumentError, 'Arguments missing' if args.empty?
|
9
|
+
attr_accessor :constructor
|
16
10
|
|
17
|
-
|
18
|
-
|
11
|
+
def execute(arg)
|
12
|
+
constructor.call(self, arg)
|
13
|
+
# instance_exec(arg, &constructor)
|
14
|
+
end
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
16
|
+
def to_s
|
17
|
+
"#{self.class} opts => #{opts}"
|
24
18
|
end
|
25
19
|
|
26
20
|
private
|
27
21
|
|
28
|
-
|
22
|
+
attr_reader :opts
|
29
23
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
keyword_args.include?(arg)
|
24
|
+
def define_attr_readers(opts)
|
25
|
+
opts.each do |key, value|
|
26
|
+
define_singleton_method key.to_s do
|
27
|
+
value
|
28
|
+
end
|
36
29
|
end
|
37
|
-
|
38
|
-
raise ArgumentError, arguments_missing_msg(missing_keyword_args) if missing_keyword_args.any?
|
39
|
-
|
40
|
-
keyword_args.slice(*self.step_args)
|
41
|
-
end
|
42
|
-
|
43
|
-
def arguments_missing_msg(missing_arg_keys)
|
44
|
-
"Arguments missing: #{missing_arg_keys.map { |a| "#{a}:" }.join(', ')}"
|
45
30
|
end
|
46
31
|
end
|
47
32
|
end
|
data/lib/nxt_pipeline/version.rb
CHANGED
data/nxt_pipeline.gemspec
CHANGED
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.2.
|
4
|
+
version: 0.2.1
|
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-
|
13
|
+
date: 2019-04-19 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -82,6 +82,20 @@ dependencies:
|
|
82
82
|
- - "~>"
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '3.0'
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: pry
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
85
99
|
description:
|
86
100
|
email:
|
87
101
|
- mail@nilssommer.de
|
@@ -106,6 +120,7 @@ files:
|
|
106
120
|
- bin/rspec
|
107
121
|
- bin/setup
|
108
122
|
- lib/nxt_pipeline.rb
|
123
|
+
- lib/nxt_pipeline/error_callback.rb
|
109
124
|
- lib/nxt_pipeline/pipeline.rb
|
110
125
|
- lib/nxt_pipeline/step.rb
|
111
126
|
- lib/nxt_pipeline/version.rb
|