nxt_pipeline 0.2.8 → 0.3.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: 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