nxt_pipeline 0.4.3 → 1.0.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 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