nxt_pipeline 0.2.8 → 0.3.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: 3905e37feab9967d3cf749542c69adaa64280b788a7b862348e6002eff0e863f
4
- data.tar.gz: d899cfc62cda30aaef508168130884ed1150f49120aae8b2ffe6552a03a61929
3
+ metadata.gz: 1a0f00d3ec3878cedc3f87bbd4a95d48ac4c16dcc998ad44a4b92aa083a91ac4
4
+ data.tar.gz: '0698836b0f3f0cf567faf3cd4fded931617a4b733eb58514dab0b89bed62963c'
5
5
  SHA512:
6
- metadata.gz: 3de1e8ee02aa0778e673f97a2a58701ec548a6546f84d0bf2d63db5bf339fa267d61a6ff5193353e83f2ce3cfeb259b2ce2f08cab52150ca675364d018c83611
7
- data.tar.gz: 65eec50438f474ae1d54c70e4baa6648e91cd8a28a662ece2efcec1a30eaf66350252541049995c66ef221fe77ba4f8bcd1f5a56aa97d9b59d96d731639ba801
6
+ metadata.gz: 23410c5a8ab7c9fd036ae4c6ad9b3cca91c849b121f4afc02f0658b87b261efb0542bdbd0d687067390e4e3c7ce2ae0ae3ccfbb36a42b1a07d94855aea1263f7
7
+ data.tar.gz: cf7a641b429f16655ef6f3103995a19661e2b4c2dc7ad9029c0053a90f18ff785565646f7a6da121a70cc2d84ca7bb8b6721eb680bc354a628a951c725f1940c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_pipeline (0.2.8)
4
+ nxt_pipeline (0.3.0)
5
5
  activesupport
6
6
 
7
7
  GEM
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
- p.constructor(:service, default: true) do |step, arg|
37
- step.service_class.new(options: arg).call
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, arg, error|
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, arg, error|
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, arg, error|
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, arg, error|
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,13 @@
1
+ module NxtPipeline
2
+ class Constructor
3
+ def initialize(name, opts, block)
4
+ @name = name
5
+ @block = block
6
+ @opts = opts
7
+ end
8
+
9
+ attr_reader :opts, :block
10
+
11
+ delegate :call, :arity, to: :block
12
+ end
13
+ end
@@ -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(arg, &block)
66
+ def execute(**opts, &block)
68
67
  reset
69
68
 
70
69
  configure(&block) if block_given?
71
- before_execute_callback.call(self, arg) if before_execute_callback.respond_to?(:call)
70
+ before_execute_callback.call(self, opts) if before_execute_callback.respond_to?(:call)
72
71
 
73
- result = steps.inject(arg) do |argument, step|
74
- execute_step(step, argument)
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
- argument
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, arg)
125
+ def execute_step(step, **opts)
127
126
  self.current_step = step
128
- self.current_arg = arg
129
- result = step.execute(arg)
127
+ self.current_arg = opts
128
+ result = step.execute(**opts)
130
129
  log_step(step)
131
- result || arg
130
+ result || opts
132
131
  end
133
132
 
134
133
  def find_error_callback(error)
@@ -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(arg)
18
- guard_args = [arg, self]
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, arg)
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) { no_guard }
49
+ opts.fetch(:if) { guard(true) }
49
50
  end
50
51
 
51
52
  def unless_guard
52
- opts.fetch(:unless) { no_guard }
53
+ opts.fetch(:unless) { guard(false) }
53
54
  end
54
55
 
55
- def no_guard
56
- -> { true }
56
+ def guard(result)
57
+ -> { result }
57
58
  end
58
59
 
59
60
  def define_attr_readers(opts)
@@ -1,3 +1,3 @@
1
1
  module NxtPipeline
2
- VERSION = "0.2.8".freeze
2
+ VERSION = "0.3.0".freeze
3
3
  end
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.2.8
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-09 00:00:00.000000000 Z
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