nxt_pipeline 0.4.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f47be27dd03af5d8a11274b4667a40e2a0c14b4be0a373dd90e1fc7deb93c03
4
- data.tar.gz: 6964e21671bb458e187dd3289538e49243e42b06c38c804ffc2b69ed94146612
3
+ metadata.gz: 77672035013c0cfe29bb5c5057178a9822e25c5b02e83b87a8cbb8dadac1d687
4
+ data.tar.gz: bb097fa702a19a9756d86a2ff63792bed998d9f8c5425263864695606bfca70d
5
5
  SHA512:
6
- metadata.gz: 5ba8339e42e2e485e9c0c00a35d6df56324b1145605d564039a4931d384d0297ef44b3613c8e45e83dfcfea83f0ed7b5a377f3a6e89f2a03e5b9dbfcc6e47b9a
7
- data.tar.gz: f9c938b9302bf9f2b4c126ddaa1b978b99c2f02fe1cdbc6d17be307135a22a504e002c6b110fa0b2eb92a04b5623f4293c8f92575c82ad2240f3de0b4a49a8ad
6
+ metadata.gz: 2d355345b5aadcb3d02895cd85196cc60b34caef8c919d11480a5a2cd429c5e32baa37c83f8e169ca316311735343297ba0a3b763044e12ac02c8349cc849929
7
+ data.tar.gz: eeaa40df4479d0b4b39e5cfe5fd89756d6681dc3d9e9f96e65f6f1e4f9793c588298c33c0fa446c0f7ddf73a3a9e4a43c5da5f371cf25928f18c80bed52e3e58
@@ -1,3 +1,8 @@
1
+ ## nxt_pipeline 1.0.0 (24.11.2020)
2
+
3
+ Replace after and before execute hooks with proper callbacks.
4
+ Introduce before, after and around step callbacks
5
+
1
6
  ## nxt_pipeline 0.4.3 (October 20, 2020)
2
7
 
3
8
  Add new attribute readers on step object.
@@ -42,4 +47,4 @@ will be set and can be accessed via attribute readers.
42
47
 
43
48
  * Initial Release.
44
49
 
45
- *Nils Sommer*
50
+ *Nils Sommer*
@@ -1,25 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_pipeline (0.4.3)
4
+ nxt_pipeline (1.0.0)
5
5
  activesupport
6
+ nxt_registry
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
- activesupport (6.0.3.4)
11
+ activesupport (6.1.1)
11
12
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
- i18n (>= 0.7, < 2)
13
- minitest (~> 5.1)
14
- tzinfo (~> 1.1)
15
- zeitwerk (~> 2.2, >= 2.2.2)
16
- byebug (11.1.1)
17
- coderay (1.1.2)
18
- concurrent-ruby (1.1.7)
19
- diff-lcs (1.3)
20
- ffi (1.11.1)
13
+ i18n (>= 1.6, < 2)
14
+ minitest (>= 5.1)
15
+ tzinfo (~> 2.0)
16
+ zeitwerk (~> 2.3)
17
+ byebug (11.1.3)
18
+ coderay (1.1.3)
19
+ concurrent-ruby (1.1.8)
20
+ diff-lcs (1.4.4)
21
+ ffi (1.14.2)
21
22
  formatador (0.2.5)
22
- guard (2.15.0)
23
+ guard (2.16.2)
23
24
  formatador (>= 0.2.4)
24
25
  listen (>= 2.7, < 4.0)
25
26
  lumberjack (>= 1.0.12, < 2.0)
@@ -33,51 +34,50 @@ GEM
33
34
  guard (~> 2.1)
34
35
  guard-compat (~> 1.1)
35
36
  rspec (>= 2.99.0, < 4.0)
36
- i18n (1.8.5)
37
+ i18n (1.8.7)
37
38
  concurrent-ruby (~> 1.0)
38
- listen (3.1.5)
39
- rb-fsevent (~> 0.9, >= 0.9.4)
40
- rb-inotify (~> 0.9, >= 0.9.7)
41
- ruby_dep (~> 1.2)
42
- lumberjack (1.0.13)
39
+ listen (3.4.1)
40
+ rb-fsevent (~> 0.10, >= 0.10.3)
41
+ rb-inotify (~> 0.9, >= 0.9.10)
42
+ lumberjack (1.2.8)
43
43
  method_source (1.0.0)
44
- minitest (5.14.2)
44
+ minitest (5.14.3)
45
45
  nenv (0.3.0)
46
- notiffany (0.1.1)
46
+ notiffany (0.1.3)
47
47
  nenv (~> 0.1)
48
48
  shellany (~> 0.0)
49
- pry (0.13.0)
49
+ nxt_registry (0.3.6)
50
+ activesupport
51
+ pry (0.13.1)
50
52
  coderay (~> 1.1)
51
53
  method_source (~> 1.0)
52
54
  pry-byebug (3.9.0)
53
55
  byebug (~> 11.0)
54
56
  pry (~> 0.13.0)
55
- rake (13.0.1)
56
- rb-fsevent (0.10.3)
57
- rb-inotify (0.10.0)
57
+ rake (13.0.3)
58
+ rb-fsevent (0.10.4)
59
+ rb-inotify (0.10.1)
58
60
  ffi (~> 1.0)
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
+ rspec (3.10.0)
62
+ rspec-core (~> 3.10.0)
63
+ rspec-expectations (~> 3.10.0)
64
+ rspec-mocks (~> 3.10.0)
65
+ rspec-core (3.10.1)
66
+ rspec-support (~> 3.10.0)
67
+ rspec-expectations (3.10.1)
66
68
  diff-lcs (>= 1.2.0, < 2.0)
67
- rspec-support (~> 3.9.0)
68
- rspec-mocks (3.9.0)
69
+ rspec-support (~> 3.10.0)
70
+ rspec-mocks (3.10.1)
69
71
  diff-lcs (>= 1.2.0, < 2.0)
70
- rspec-support (~> 3.9.0)
71
- rspec-support (3.9.0)
72
+ rspec-support (~> 3.10.0)
73
+ rspec-support (3.10.1)
72
74
  rspec_junit_formatter (0.4.1)
73
75
  rspec-core (>= 2, < 4, != 2.12.0)
74
- ruby_dep (1.5.0)
75
76
  shellany (0.0.1)
76
- thor (0.20.3)
77
- thread_safe (0.3.6)
78
- tzinfo (1.2.7)
79
- thread_safe (~> 0.1)
80
- zeitwerk (2.4.0)
77
+ thor (1.1.0)
78
+ tzinfo (2.0.4)
79
+ concurrent-ruby (~> 1.0)
80
+ zeitwerk (2.4.2)
81
81
 
82
82
  PLATFORMS
83
83
  ruby
data/README.md CHANGED
@@ -176,24 +176,59 @@ NxtPipeline::Pipeline.new do |p|
176
176
  end
177
177
  ```
178
178
 
179
- ### Before and After callbacks
179
+ ### Before, around and after callbacks
180
180
 
181
- You can also define callbacks that run before and after the `#execute` action. Both callback blocks get the pipeline instance (to access stuff like the `log`) and the argument of the pipeline yielded.
181
+ You can also define callbacks :before, :around and :after each step and or the `#execute` method. You can also register
182
+ multiple callbacks, but probably you want to keep them to a minimum to not end up in hell.
183
+
184
+ #### Step callbacks
185
+
186
+ ```ruby
187
+ NxtPipeline::Pipeline.new do |p|
188
+ p.before_step do |_, change_set|
189
+ change_set[:acc] << 'before step 1'
190
+ change_set
191
+ end
192
+
193
+ p.around_step do |_, change_set, execution|
194
+ change_set[:acc] << 'around step 1'
195
+ execution.call # you have to specify where in your callback you want to call the inner block
196
+ change_set[:acc] << 'around step 1'
197
+ change_set
198
+ end
199
+
200
+ p.after_step do |_, change_set|
201
+ change_set[:acc] << 'after step 1'
202
+ change_set
203
+ end
204
+ end
205
+ ```
206
+
207
+ #### Execution callbacks
182
208
 
183
209
  ```ruby
184
210
  NxtPipeline::Pipeline.new do |p|
185
- p.before_execute do |pipeline, arg:|
186
- # Will be called from within #execute before entering the first step
187
- # After any configure block though!
211
+ p.before_execution do |_, change_set|
212
+ change_set[:acc] << 'before execution 1'
213
+ change_set
214
+ end
215
+
216
+ p.around_execution do |_, change_set, execution|
217
+ change_set[:acc] << 'around execution 1'
218
+ execution.call # you have to specify where in your callback you want to call the inner block
219
+ change_set[:acc] << 'around execution 1'
220
+ change_set
188
221
  end
189
222
 
190
- p.after_execute do |pipeline, arg:|
191
- # Will be called from within #execute after executing last step
223
+ p.after_execution do |_, change_set|
224
+ change_set[:acc] << 'after execution 1'
225
+ change_set
192
226
  end
193
227
  end
194
228
  ```
195
229
 
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.
230
+ Note that the `after_execute` callback will not be called in case a step raises an error.
231
+ See the previous section (_Error callbacks_) for how to define callbacks that run in case of errors.
197
232
 
198
233
  ### Step resolvers
199
234
 
@@ -203,7 +238,6 @@ You can also use this if you are not fine with resolving the constructor from th
203
238
 
204
239
 
205
240
  ## Topics
206
- - Step orchestration (chainable steps)
207
241
  - Constructors should take arg as first and step as second arg
208
242
 
209
243
  ## Development
@@ -1,9 +1,11 @@
1
1
  require 'active_support/all'
2
+ require 'nxt_registry'
2
3
  require 'nxt_pipeline/version'
3
4
  require 'nxt_pipeline/logger'
4
5
  require 'nxt_pipeline/constructor'
5
6
  require 'nxt_pipeline/pipeline'
6
7
  require 'nxt_pipeline/step'
8
+ require 'nxt_pipeline/callbacks'
7
9
  require 'nxt_pipeline/error_callback'
8
10
 
9
11
  module NxtPipeline
@@ -0,0 +1,58 @@
1
+ module NxtPipeline
2
+ class Callbacks
3
+ def initialize(pipeline:)
4
+ @registry = build_registry
5
+ @pipeline = pipeline
6
+ end
7
+
8
+ def register(path, callback)
9
+ registry.resolve!(*path) << callback
10
+ end
11
+
12
+ def run(kind_of_callback, type, change_set)
13
+ registry.resolve!(kind_of_callback, type).each do |callback|
14
+ run_callback(callback, change_set)
15
+ end
16
+ end
17
+
18
+ def around(type, change_set, &execution)
19
+ around_callbacks = registry.resolve!(:around, type)
20
+ return execution.call unless around_callbacks.any?
21
+
22
+ callback_chain = around_callbacks.reverse.inject(execution) do |previous, callback|
23
+ -> { callback.call(pipeline, change_set, previous) }
24
+ end
25
+
26
+ callback_chain.call
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :registry, :pipeline
32
+
33
+ def run_callback(callback, change_set)
34
+ args = [pipeline, change_set]
35
+ args = args.take(callback.arity)
36
+ callback.call(*args)
37
+ end
38
+
39
+ def build_registry
40
+ NxtRegistry::Registry.new(:callbacks) do
41
+ register(:before) do
42
+ register(:step, [])
43
+ register(:execution, [])
44
+ end
45
+
46
+ register(:around) do
47
+ register(:step, [])
48
+ register(:execution, [])
49
+ end
50
+
51
+ register(:after) do
52
+ register(:step, [])
53
+ register(:execution, [])
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -35,9 +35,9 @@ module NxtPipeline
35
35
  step_resolvers << block
36
36
  end
37
37
 
38
- def set_default_constructor(name)
38
+ def set_default_constructor(default_constructor)
39
39
  raise_duplicate_default_constructor if default_constructor_name.present?
40
- self.default_constructor_name = name
40
+ self.default_constructor_name = default_constructor
41
41
  end
42
42
 
43
43
  def raise_duplicate_default_constructor
@@ -46,8 +46,7 @@ module NxtPipeline
46
46
 
47
47
  def step(argument = nil, **opts, &block)
48
48
  constructor = if block_given?
49
- # make type the :to_s of inline steps
50
- # fall back to :inline if no type is given
49
+ # make type the :to_s of inline steps fall back to :inline if no type is given
51
50
  argument ||= :inline
52
51
  opts.reverse_merge!(to_s: argument)
53
52
  Constructor.new(:inline, **opts, &block)
@@ -66,40 +65,33 @@ module NxtPipeline
66
65
  end
67
66
  end
68
67
 
69
- steps << Step.new(argument, constructor, steps.count, **opts)
68
+ register_step(argument, constructor, callbacks, **opts)
70
69
  end
71
70
 
72
- def execute(**changeset, &block)
71
+ def execute(**change_set, &block)
73
72
  reset
74
73
 
75
74
  configure(&block) if block_given?
76
- before_execute_callback.call(self, changeset) if before_execute_callback.respond_to?(:call)
77
-
78
- result = steps.inject(changeset) do |changeset, step|
79
- execute_step(step, **changeset)
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
- )
75
+ callbacks.run(:before, :execution, change_set)
76
+
77
+ result = callbacks.around :execution, change_set do
78
+ steps.inject(change_set) do |set, step|
79
+ execute_step(step, **set)
80
+ rescue StandardError => error
81
+ decorate_error_with_details(error, set, step, logger)
82
+ handle_error_of_step(error)
83
+ set
89
84
  end
90
-
91
- callback = find_error_callback(error)
92
- raise unless callback && callback.continue_after_error?
93
- handle_step_error(error)
94
- changeset
95
85
  end
96
86
 
97
- after_execute_callback.call(self, result) if after_execute_callback.respond_to?(:call)
87
+ callbacks.run(:after, :execution, change_set)
98
88
  result
99
89
  rescue StandardError => error
100
90
  handle_step_error(error)
101
91
  end
102
92
 
93
+ alias_method :call, :execute
94
+
103
95
  def handle_step_error(error)
104
96
  log_step(current_step)
105
97
  callback = find_error_callback(error)
@@ -115,22 +107,40 @@ module NxtPipeline
115
107
 
116
108
  alias :on_error :on_errors
117
109
 
118
- def before_execute(&callback)
119
- self.before_execute_callback = callback
110
+ def before_step(&block)
111
+ callbacks.register([:before, :step], block)
112
+ end
113
+
114
+ def after_step(&block)
115
+ callbacks.register([:after, :step], block)
116
+ end
117
+
118
+ def around_step(&block)
119
+ callbacks.register([:around, :step], block)
120
120
  end
121
121
 
122
- def after_execute(&callback)
123
- self.after_execute_callback = callback
122
+ def before_execution(&block)
123
+ callbacks.register([:before, :execution], block)
124
+ end
125
+
126
+ def after_execution(&block)
127
+ callbacks.register([:after, :execution], block)
128
+ end
129
+
130
+ def around_execution(&block)
131
+ callbacks.register([:around, :execution], block)
124
132
  end
125
133
 
126
134
  private
127
135
 
136
+ def callbacks
137
+ @callbacks ||= NxtPipeline::Callbacks.new(pipeline: self)
138
+ end
139
+
128
140
  attr_reader :error_callbacks, :constructors, :step_resolvers
129
141
  attr_accessor :current_step,
130
142
  :current_arg,
131
- :default_constructor_name,
132
- :before_execute_callback,
133
- :after_execute_callback
143
+ :default_constructor_name
134
144
 
135
145
  def default_constructor
136
146
  return unless default_constructor_name
@@ -138,12 +148,12 @@ module NxtPipeline
138
148
  @default_constructor ||= constructors[default_constructor_name.to_sym]
139
149
  end
140
150
 
141
- def execute_step(step, **changeset)
151
+ def execute_step(step, **change_set)
142
152
  self.current_step = step
143
- self.current_arg = changeset
144
- result = step.execute(**changeset)
153
+ self.current_arg = change_set
154
+ result = step.execute(**change_set)
145
155
  log_step(step)
146
- result || changeset
156
+ result || change_set
147
157
  end
148
158
 
149
159
  def find_error_callback(error)
@@ -168,5 +178,30 @@ module NxtPipeline
168
178
  def default_step_resolvers
169
179
  [->(step_argument) { step_argument.is_a?(Symbol) && step_argument }]
170
180
  end
181
+
182
+ def decorate_error_with_details(error, change_set, step, logger)
183
+ error.define_singleton_method :details do
184
+ OpenStruct.new(
185
+ change_set: change_set,
186
+ logger: logger,
187
+ step: step
188
+ )
189
+ end
190
+ error
191
+ end
192
+
193
+ def register_step(argument, constructor, callbacks, **opts)
194
+ steps << Step.new(argument, constructor, steps.count, self, callbacks, **opts)
195
+ end
196
+
197
+ def handle_error_of_step(error)
198
+ error_callback = find_error_callback(error)
199
+ raise error unless error_callback.present? && error_callback.continue_after_error?
200
+
201
+ log_step(current_step)
202
+ raise error unless error_callback.present?
203
+
204
+ error_callback.call(current_step, current_arg, error)
205
+ end
171
206
  end
172
207
  end
@@ -1,8 +1,10 @@
1
1
  module NxtPipeline
2
2
  class Step
3
- def initialize(argument, constructor, index, **opts)
3
+ def initialize(argument, constructor, index, pipeline, callbacks, **opts)
4
4
  define_attr_readers(opts)
5
5
 
6
+ @pipeline = pipeline
7
+ @callbacks = callbacks
6
8
  @argument = argument
7
9
  @index = index
8
10
  @opts = opts
@@ -16,21 +18,37 @@ module NxtPipeline
16
18
  @mapped_options = nil
17
19
  end
18
20
 
19
- attr_reader :argument, :result, :status, :execution_started_at, :execution_finished_at, :execution_duration, :error, :opts, :index, :mapped_options
21
+ attr_reader :argument,
22
+ :result,
23
+ :status,
24
+ :execution_started_at,
25
+ :execution_finished_at,
26
+ :execution_duration,
27
+ :error,
28
+ :opts,
29
+ :index,
30
+ :mapped_options
31
+
20
32
  attr_accessor :to_s
21
33
 
22
34
  alias_method :name=, :to_s=
23
35
  alias_method :name, :to_s
24
36
 
25
- def execute(**changeset)
37
+ def execute(**change_set)
26
38
  track_execution_time do
27
- set_mapped_options(changeset)
28
- guard_args = [changeset, self]
39
+ set_mapped_options(change_set)
40
+ guard_args = [change_set, self]
41
+
42
+ callbacks.run(:before, :step, change_set)
29
43
 
30
44
  if evaluate_unless_guard(guard_args) && evaluate_if_guard(guard_args)
31
- self.result = construct_result(changeset)
45
+ callbacks.around(:step, change_set) do
46
+ set_result(change_set)
47
+ end
32
48
  end
33
49
 
50
+ callbacks.run(:after, :step, change_set)
51
+
34
52
  set_status
35
53
  result
36
54
  end
@@ -40,16 +58,16 @@ module NxtPipeline
40
58
  raise
41
59
  end
42
60
 
43
- def set_mapped_options(changeset)
61
+ def set_mapped_options(change_set)
44
62
  mapper = options_mapper || default_options_mapper
45
- mapper_args = [changeset, self].take(mapper.arity)
63
+ mapper_args = [change_set, self].take(mapper.arity)
46
64
  self.mapped_options = mapper.call(*mapper_args)
47
65
  end
48
66
 
49
67
  private
50
68
 
51
69
  attr_writer :result, :status, :error, :mapped_options, :execution_started_at, :execution_finished_at, :execution_duration
52
- attr_reader :constructor, :options_mapper
70
+ attr_reader :constructor, :options_mapper, :pipeline, :callbacks
53
71
 
54
72
  def evaluate_if_guard(args)
55
73
  execute_callable(if_guard, args)
@@ -59,9 +77,9 @@ module NxtPipeline
59
77
  !execute_callable(unless_guard, args)
60
78
  end
61
79
 
62
- def construct_result(changeset)
63
- args = [self, changeset]
64
- execute_callable(constructor, args)
80
+ def set_result(change_set)
81
+ args = [self, change_set]
82
+ self.result = execute_callable(constructor, args)
65
83
  end
66
84
 
67
85
  def execute_callable(callable, args)
@@ -123,4 +141,4 @@ module NxtPipeline
123
141
  ->(_) { {} }
124
142
  end
125
143
  end
126
- end
144
+ end
@@ -1,4 +1,4 @@
1
1
  module NxtPipeline
2
- VERSION = "0.4.3".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
4
4
 
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.require_paths = ["lib"]
36
36
 
37
37
  spec.add_dependency "activesupport"
38
+ spec.add_dependency "nxt_registry"
38
39
  spec.add_development_dependency "bundler", "~> 2.1"
39
40
  spec.add_development_dependency "guard-rspec"
40
41
  spec.add_development_dependency "rake", "~> 13.0"
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.3
4
+ version: 1.0.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: 2020-10-23 00:00:00.000000000 Z
13
+ date: 2021-01-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -26,6 +26,20 @@ dependencies:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
28
  version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: nxt_registry
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
29
43
  - !ruby/object:Gem::Dependency
30
44
  name: bundler
31
45
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +135,7 @@ files:
121
135
  - bin/rspec
122
136
  - bin/setup
123
137
  - lib/nxt_pipeline.rb
138
+ - lib/nxt_pipeline/callbacks.rb
124
139
  - lib/nxt_pipeline/constructor.rb
125
140
  - lib/nxt_pipeline/error_callback.rb
126
141
  - lib/nxt_pipeline/logger.rb