nxt_pipeline 0.3.0 → 0.4.3

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: 1a0f00d3ec3878cedc3f87bbd4a95d48ac4c16dcc998ad44a4b92aa083a91ac4
4
- data.tar.gz: '0698836b0f3f0cf567faf3cd4fded931617a4b733eb58514dab0b89bed62963c'
3
+ metadata.gz: 0f47be27dd03af5d8a11274b4667a40e2a0c14b4be0a373dd90e1fc7deb93c03
4
+ data.tar.gz: 6964e21671bb458e187dd3289538e49243e42b06c38c804ffc2b69ed94146612
5
5
  SHA512:
6
- metadata.gz: 23410c5a8ab7c9fd036ae4c6ad9b3cca91c849b121f4afc02f0658b87b261efb0542bdbd0d687067390e4e3c7ce2ae0ae3ccfbb36a42b1a07d94855aea1263f7
7
- data.tar.gz: cf7a641b429f16655ef6f3103995a19661e2b4c2dc7ad9029c0053a90f18ff785565646f7a6da121a70cc2d84ca7bb8b6721eb680bc354a628a951c725f1940c
6
+ metadata.gz: 5ba8339e42e2e485e9c0c00a35d6df56324b1145605d564039a4931d384d0297ef44b3613c8e45e83dfcfea83f0ed7b5a377f3a6e89f2a03e5b9dbfcc6e47b9a
7
+ data.tar.gz: f9c938b9302bf9f2b4c126ddaa1b978b99c2f02fe1cdbc6d17be307135a22a504e002c6b110fa0b2eb92a04b5623f4293c8f92575c82ad2240f3de0b4a49a8ad
@@ -7,7 +7,9 @@ jobs:
7
7
  build:
8
8
  docker:
9
9
  # specify the version you desire here
10
- - image: circleci/ruby:2.6.1-node
10
+ - image: circleci/ruby:2.7.0-node
11
+ environment:
12
+ BUNDLER_VERSION: 2.1.4
11
13
 
12
14
  working_directory: ~/repo
13
15
 
@@ -19,6 +21,8 @@ jobs:
19
21
  keys:
20
22
  - v1-dependencies-{{ checksum "Gemfile.lock" }}
21
23
 
24
+ - run: gem install bundler --version $BUNDLER_VERSION
25
+
22
26
  - run:
23
27
  name: install dependencies
24
28
  command: |
data/.pryrc ADDED
@@ -0,0 +1,6 @@
1
+ if defined?(PryByebug)
2
+ Pry.commands.alias_command 'c', 'continue'
3
+ Pry.commands.alias_command 's', 'step'
4
+ Pry.commands.alias_command 'n', 'next'
5
+ Pry.commands.alias_command 'f', 'finish'
6
+ end
@@ -1 +1 @@
1
- 2.6.3
1
+ 2.7.0
@@ -1,4 +1,17 @@
1
- ## Unreleased
1
+ ## nxt_pipeline 0.4.3 (October 20, 2020)
2
+
3
+ Add new attribute readers on step object.
4
+
5
+ After executing a step execution_finished_at execution_started_at and execution_duration
6
+ will be set and can be accessed via attribute readers.
7
+
8
+ ## nxt_pipeline 0.4.2 (October 12, 2020)
9
+
10
+ * Fix bug when registering an error without passing arguments in which case the callback didn't get executed. More info: https://github.com/nxt-insurance/nxt_pipeline/issues/39
11
+
12
+ ## nxt_pipeline 0.4.1 (March 13, 2020)
13
+
14
+ * Fix deprecation warnings for Ruby 2.7
2
15
 
3
16
  ## nxt_pipeline 0.2.0 (March 10, 2019)
4
17
 
@@ -14,7 +27,7 @@
14
27
  Renamed `NxtPipeline::Pipeline#burst?` to `NxtPipeline::Pipeline#failed?`.
15
28
  Renamed `NxtPipeline::Pipeline#burst_segment` to `NxtPipeline::Pipeline#failed_step`.
16
29
  Renamed `NxtPipeline::Pipeline::rescue_segment_burst` to `NxtPipeline::Pipeline::rescue_errors`.
17
-
30
+
18
31
  *Nils Sommer*
19
32
 
20
33
  * Setup [guard](https://github.com/guard/guard) to run specs upon file changes during development.
@@ -1,19 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_pipeline (0.3.0)
4
+ nxt_pipeline (0.4.3)
5
5
  activesupport
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (5.2.3)
10
+ activesupport (6.0.3.4)
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
+ zeitwerk (~> 2.2, >= 2.2.2)
16
+ byebug (11.1.1)
15
17
  coderay (1.1.2)
16
- concurrent-ruby (1.1.5)
18
+ concurrent-ruby (1.1.7)
17
19
  diff-lcs (1.3)
18
20
  ffi (1.11.1)
19
21
  formatador (0.2.5)
@@ -31,59 +33,63 @@ GEM
31
33
  guard (~> 2.1)
32
34
  guard-compat (~> 1.1)
33
35
  rspec (>= 2.99.0, < 4.0)
34
- i18n (1.6.0)
36
+ i18n (1.8.5)
35
37
  concurrent-ruby (~> 1.0)
36
38
  listen (3.1.5)
37
39
  rb-fsevent (~> 0.9, >= 0.9.4)
38
40
  rb-inotify (~> 0.9, >= 0.9.7)
39
41
  ruby_dep (~> 1.2)
40
42
  lumberjack (1.0.13)
41
- method_source (0.9.2)
42
- minitest (5.11.3)
43
+ method_source (1.0.0)
44
+ minitest (5.14.2)
43
45
  nenv (0.3.0)
44
46
  notiffany (0.1.1)
45
47
  nenv (~> 0.1)
46
48
  shellany (~> 0.0)
47
- pry (0.12.2)
48
- coderay (~> 1.1.0)
49
- method_source (~> 0.9.0)
50
- rake (12.3.3)
49
+ pry (0.13.0)
50
+ coderay (~> 1.1)
51
+ method_source (~> 1.0)
52
+ pry-byebug (3.9.0)
53
+ byebug (~> 11.0)
54
+ pry (~> 0.13.0)
55
+ rake (13.0.1)
51
56
  rb-fsevent (0.10.3)
52
57
  rb-inotify (0.10.0)
53
58
  ffi (~> 1.0)
54
- rspec (3.8.0)
55
- rspec-core (~> 3.8.0)
56
- rspec-expectations (~> 3.8.0)
57
- rspec-mocks (~> 3.8.0)
58
- rspec-core (3.8.1)
59
- rspec-support (~> 3.8.0)
60
- rspec-expectations (3.8.4)
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
66
  diff-lcs (>= 1.2.0, < 2.0)
62
- rspec-support (~> 3.8.0)
63
- rspec-mocks (3.8.1)
67
+ rspec-support (~> 3.9.0)
68
+ rspec-mocks (3.9.0)
64
69
  diff-lcs (>= 1.2.0, < 2.0)
65
- rspec-support (~> 3.8.0)
66
- rspec-support (3.8.2)
70
+ rspec-support (~> 3.9.0)
71
+ rspec-support (3.9.0)
67
72
  rspec_junit_formatter (0.4.1)
68
73
  rspec-core (>= 2, < 4, != 2.12.0)
69
74
  ruby_dep (1.5.0)
70
75
  shellany (0.0.1)
71
76
  thor (0.20.3)
72
77
  thread_safe (0.3.6)
73
- tzinfo (1.2.5)
78
+ tzinfo (1.2.7)
74
79
  thread_safe (~> 0.1)
80
+ zeitwerk (2.4.0)
75
81
 
76
82
  PLATFORMS
77
83
  ruby
78
84
 
79
85
  DEPENDENCIES
80
- bundler (~> 1.17)
86
+ bundler (~> 2.1)
81
87
  guard-rspec
82
88
  nxt_pipeline!
83
- pry
84
- rake (~> 12.3)
89
+ pry-byebug
90
+ rake (~> 13.0)
85
91
  rspec (~> 3.0)
86
92
  rspec_junit_formatter
87
93
 
88
94
  BUNDLED WITH
89
- 1.17.2
95
+ 2.1.4
data/README.md CHANGED
@@ -24,35 +24,35 @@ Or install it yourself as:
24
24
 
25
25
  ### Constructors
26
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
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
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.
30
+ by the step yielded to the constructor.
31
31
 
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
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.
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
37
  p.constructor(:service, default: true) do |step, arg:|
38
38
  step.to_s = step.service_class.to_s
39
39
  result = step.service_class.new(options: arg).call
40
40
  result && { arg: result }
41
41
  end
42
-
42
+
43
43
  p.constructor(:job) do |step, arg:|
44
44
  step.job_class.perform_later(*arg) && { arg: arg }
45
45
  end
46
46
  end
47
47
 
48
- # Once a pipeline was created you can still configure it
48
+ # Once a pipeline was created you can still configure it
49
49
  pipeline.constructor(:call) do |step, arg:|
50
50
  result = step.caller.new(arg).call
51
51
  result && { arg: result }
52
52
  end
53
53
 
54
- # same with block syntax
55
- # You can use this to split up execution from configuration
54
+ # same with block syntax
55
+ # You can use this to split up execution from configuration
56
56
  pipeline.configure do |p|
57
57
  p.constructor(:call) do |step, arg:|
58
58
  result = step.caller.new(arg).call
@@ -61,13 +61,13 @@ pipeline.configure do |p|
61
61
  end
62
62
  ```
63
63
 
64
- ### Defining steps
64
+ ### Defining steps
65
65
 
66
66
  Once your pipeline knows how to execute your steps you can add those.
67
67
 
68
68
  ```ruby
69
69
  pipeline.step :service, service_class: MyServiceClass, to_s: 'First step'
70
- pipeline.step service_class: MyOtherServiceClass, to_s: 'Second step'
70
+ pipeline.step service_class: MyOtherServiceClass, to_s: 'Second step'
71
71
  # ^ Since service is the default step you don't have to specify it the step type each time
72
72
  pipeline.step :job, job_class: MyJobClass # to_s is optional
73
73
  pipeline.step :job, job_class: MyOtherJobClass
@@ -82,19 +82,19 @@ end
82
82
  ```
83
83
 
84
84
  You can also define inline steps, meaning the block will be executed. When you do not provide a :to_s option, type
85
- will be used as :to_s option per default. When no type was given for an inline block the type of the inline block
86
- will be set to :inline.
85
+ will be used as :to_s option per default. When no type was given for an inline block the type of the inline block
86
+ will be set to :inline.
87
87
 
88
88
  ### Execution
89
89
 
90
- You can then execute the steps with:
90
+ You can then execute the steps with:
91
91
 
92
92
  ```ruby
93
- pipeline.execute('initial argument')
93
+ pipeline.execute(arg: 'initial argument')
94
94
 
95
95
  # Or run the steps directly using block syntax
96
96
 
97
- pipeline.execute('initial argument') do |p|
97
+ pipeline.execute(arg: 'initial argument') do |p|
98
98
  p.step :service, service_class: MyServiceClass, to_s: 'First step'
99
99
  p.step :service, service_class: MyOtherServiceClass, to_s: 'Second step'
100
100
  p.step :job, job_class: MyJobClass # to_s is optional
@@ -106,18 +106,18 @@ end
106
106
  You can also directly execute a pipeline with:
107
107
 
108
108
  ```ruby
109
- NxtPipeline::Pipeline.execute('initial argument') do |p|
109
+ NxtPipeline::Pipeline.execute(arg: 'initial argument') do |p|
110
110
  p.step do |_, arg:|
111
111
  { arg: arg.upcase }
112
112
  end
113
113
  end
114
- ```
114
+ ```
115
115
 
116
- You can query the steps of your pipeline simply by calling `pipeline.steps`. A NxtPipeline::Step will provide you with
117
- an interface to it's type, options, status (:success, :skipped, :failed), result, error and the index in the pipeline.
116
+ You can query the steps of your pipeline simply by calling `pipeline.steps`. A NxtPipeline::Step will provide you with
117
+ an interface to it's type, options, status (:success, :skipped, :failed), execution_finished_at execution_started_at, execution_duration, result, error and the index in the pipeline.
118
118
 
119
119
  ```
120
- pipeline.steps.first
120
+ pipeline.steps.first
121
121
  # will give you something like this:
122
122
 
123
123
  #<NxtPipeline::Step:0x00007f83eb399448
@@ -128,20 +128,23 @@ pipeline.steps.first
128
128
  @opts={:to_s=>:transformer, :method=>:upcase},
129
129
  @result=nil,
130
130
  @status=nil,
131
- @type=:transformer>
132
- ```
131
+ @type=:transformer
132
+ @execution_duration=1.0e-05,
133
+ @execution_finished_at=2020-10-22 15:52:55.806417 +0100,
134
+ @execution_started_at=2020-10-22 15:52:55.806407 +0100,>
135
+ ```
133
136
 
134
137
  ### Guard clauses
135
138
 
136
139
  You can also define guard clauses that take a proc to prevent the execution of a step.
137
- When the guard takes an argument the step argument is yielded.
140
+ When the guard takes an argument the step argument is yielded.
138
141
 
139
142
  ```ruby
140
- pipeline.execute('initial argument') do |p|
143
+ pipeline.execute(arg: 'initial argument') do |p|
141
144
  p.step :service, service_class: MyServiceClass, if: -> (arg:) { arg == 'initial argument' }
142
145
  p.step :service, service_class: MyOtherServiceClass, unless: -> { false }
143
146
  end
144
-
147
+
145
148
  ```
146
149
 
147
150
  ### Error callbacks
@@ -153,25 +156,25 @@ NxtPipeline::Pipeline.new do |p|
153
156
  p.step do |_, arg:|
154
157
  { arg: arg.upcase }
155
158
  end
156
-
159
+
157
160
  p.on_error MyCustomError do |step, opts, error|
158
161
  # First matching error callback will be executed!
159
162
  end
160
-
163
+
161
164
  p.on_errors ArgumentError, KeyError do |step, opts, error|
162
165
  # First matching error callback will be executed!
163
166
  end
164
-
167
+
165
168
  p.on_errors YetAnotherError, halt_on_error: false do |step, opts, error|
166
169
  # After executing the callback the pipeline will not halt but continue to
167
170
  # execute the next steps.
168
171
  end
169
-
172
+
170
173
  p.on_errors do |step, opts, error|
171
174
  # This will match all errors inheriting from StandardError
172
175
  end
173
176
  end
174
- ```
177
+ ```
175
178
 
176
179
  ### Before and After callbacks
177
180
 
@@ -181,70 +184,27 @@ You can also define callbacks that run before and after the `#execute` action. B
181
184
  NxtPipeline::Pipeline.new do |p|
182
185
  p.before_execute do |pipeline, arg:|
183
186
  # Will be called from within #execute before entering the first step
184
- # After any configure block though!
187
+ # After any configure block though!
185
188
  end
186
-
189
+
187
190
  p.after_execute do |pipeline, arg:|
188
191
  # Will be called from within #execute after executing last step
189
192
  end
190
193
  end
191
194
  ```
192
195
 
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.
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.
194
197
 
195
- ### DSL
198
+ ### Step resolvers
196
199
 
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:
200
+ NxtPipeline is using so called step_resolvers to find the constructor for a given step by the arguments passed in.
201
+ You can also use this if you are not fine with resolving the constructor from the step argument. Check out the
202
+ `nxt_pipeline/spec/step_resolver_spec.rb` for examples how you can implement your own step_resolvers.
199
203
 
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
204
 
245
205
  ## Topics
246
206
  - Step orchestration (chainable steps)
247
- - Constructors should take arg as first and step as second arg
207
+ - Constructors should take arg as first and step as second arg
248
208
 
249
209
  ## Development
250
210
 
@@ -5,7 +5,6 @@ require 'nxt_pipeline/constructor'
5
5
  require 'nxt_pipeline/pipeline'
6
6
  require 'nxt_pipeline/step'
7
7
  require 'nxt_pipeline/error_callback'
8
- require 'nxt_pipeline/dsl'
9
8
 
10
9
  module NxtPipeline
11
10
  end
@@ -1,13 +1,20 @@
1
1
  module NxtPipeline
2
2
  class Constructor
3
- def initialize(name, opts, block)
3
+ def initialize(name, **opts, &block)
4
4
  @name = name
5
5
  @block = block
6
6
  @opts = opts
7
7
  end
8
8
 
9
9
  attr_reader :opts, :block
10
-
11
- delegate :call, :arity, to: :block
10
+
11
+ delegate :arity, to: :block
12
+
13
+ def call(*args, **opts, &block)
14
+ # ActiveSupport's #delegate does not properly handle keyword arg passing
15
+ # in the latest released version. Thefore we bypass delegation by reimplementing
16
+ # the method ourselves. This is already fixed in Rails master though.
17
+ self.block.call(*args, **opts, &block)
18
+ end
12
19
  end
13
20
  end
@@ -1,7 +1,7 @@
1
1
  module NxtPipeline
2
2
  class ErrorCallback
3
- def initialize(errors, halt_on_error, callback)
4
- @errors = errors
3
+ def initialize(errors, halt_on_error, &callback)
4
+ @errors = errors.any? ? errors : [StandardError]
5
5
  @halt_on_error = halt_on_error
6
6
  @callback = callback
7
7
  end
@@ -4,14 +4,15 @@ module NxtPipeline
4
4
  new(&block).execute(**opts)
5
5
  end
6
6
 
7
- def initialize(&block)
7
+ def initialize(step_resolvers = default_step_resolvers, &block)
8
8
  @steps = []
9
9
  @error_callbacks = []
10
10
  @logger = Logger.new
11
11
  @current_step = nil
12
12
  @current_arg = nil
13
13
  @default_constructor_name = nil
14
- @registry = {}
14
+ @constructors = {}
15
+ @step_resolvers = step_resolvers
15
16
 
16
17
  configure(&block) if block_given?
17
18
  end
@@ -22,14 +23,18 @@ module NxtPipeline
22
23
 
23
24
  def constructor(name, **opts, &constructor)
24
25
  name = name.to_sym
25
- raise StandardError, "Already registered step :#{name}" if registry[name]
26
+ raise StandardError, "Already registered step :#{name}" if constructors[name]
26
27
 
27
- registry[name] = Constructor.new(name, opts, constructor)
28
+ constructors[name] = Constructor.new(name, **opts, &constructor)
28
29
 
29
30
  return unless opts.fetch(:default, false)
30
31
  set_default_constructor(name)
31
32
  end
32
33
 
34
+ def step_resolver(&block)
35
+ step_resolvers << block
36
+ end
37
+
33
38
  def set_default_constructor(name)
34
39
  raise_duplicate_default_constructor if default_constructor_name.present?
35
40
  self.default_constructor_name = name
@@ -39,43 +44,54 @@ module NxtPipeline
39
44
  raise ArgumentError, 'Default step already defined'
40
45
  end
41
46
 
42
- def step(type = nil, **opts, &block)
43
- type = type&.to_sym
44
-
47
+ def step(argument = nil, **opts, &block)
45
48
  constructor = if block_given?
46
49
  # make type the :to_s of inline steps
47
50
  # fall back to :inline if no type is given
48
- type ||= :inline
49
- opts.reverse_merge!(to_s: type)
50
- Constructor.new(type, opts, block)
51
+ argument ||= :inline
52
+ opts.reverse_merge!(to_s: argument)
53
+ Constructor.new(:inline, **opts, &block)
51
54
  else
52
- if type
53
- raise_reserved_type_inline_error if type == :inline
54
- registry.fetch(type) { raise KeyError, "No step :#{type} registered" }
55
+ constructor = step_resolvers.lazy.map do |resolver|
56
+ resolver.call(argument)
57
+ end.find(&:itself)
58
+
59
+ if constructor
60
+ constructor && constructors.fetch(constructor) { raise KeyError, "No step :#{argument} registered" }
61
+ elsif default_constructor
62
+ argument ||= default_constructor_name
63
+ default_constructor
55
64
  else
56
- # When no type was given we try to fallback to the type of the default constructor
57
- type = default_constructor_name
58
- # If none was given - raise
59
- default_constructor || (raise StandardError, 'No default step registered')
65
+ raise StandardError, "Could not resolve step from: #{argument}"
60
66
  end
61
67
  end
62
68
 
63
- steps << Step.new(type, constructor, steps.count, **opts)
69
+ steps << Step.new(argument, constructor, steps.count, **opts)
64
70
  end
65
71
 
66
- def execute(**opts, &block)
72
+ def execute(**changeset, &block)
67
73
  reset
68
74
 
69
75
  configure(&block) if block_given?
70
- before_execute_callback.call(self, opts) if before_execute_callback.respond_to?(:call)
76
+ before_execute_callback.call(self, changeset) if before_execute_callback.respond_to?(:call)
71
77
 
72
- result = steps.inject(opts) do |options, step|
73
- execute_step(step, **options)
78
+ result = steps.inject(changeset) do |changeset, step|
79
+ execute_step(step, **changeset)
74
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
+ )
89
+ end
90
+
75
91
  callback = find_error_callback(error)
76
92
  raise unless callback && callback.continue_after_error?
77
93
  handle_step_error(error)
78
- options
94
+ changeset
79
95
  end
80
96
 
81
97
  after_execute_callback.call(self, result) if after_execute_callback.respond_to?(:call)
@@ -94,7 +110,7 @@ module NxtPipeline
94
110
  end
95
111
 
96
112
  def on_errors(*errors, halt_on_error: true, &callback)
97
- error_callbacks << ErrorCallback.new(errors, halt_on_error, callback)
113
+ error_callbacks << ErrorCallback.new(errors, halt_on_error, &callback)
98
114
  end
99
115
 
100
116
  alias :on_error :on_errors
@@ -109,7 +125,7 @@ module NxtPipeline
109
125
 
110
126
  private
111
127
 
112
- attr_reader :error_callbacks, :registry
128
+ attr_reader :error_callbacks, :constructors, :step_resolvers
113
129
  attr_accessor :current_step,
114
130
  :current_arg,
115
131
  :default_constructor_name,
@@ -119,15 +135,15 @@ module NxtPipeline
119
135
  def default_constructor
120
136
  return unless default_constructor_name
121
137
 
122
- @default_constructor ||= registry[default_constructor_name.to_sym]
138
+ @default_constructor ||= constructors[default_constructor_name.to_sym]
123
139
  end
124
140
 
125
- def execute_step(step, **opts)
141
+ def execute_step(step, **changeset)
126
142
  self.current_step = step
127
- self.current_arg = opts
128
- result = step.execute(**opts)
143
+ self.current_arg = changeset
144
+ result = step.execute(**changeset)
129
145
  log_step(step)
130
- result || opts
146
+ result || changeset
131
147
  end
132
148
 
133
149
  def find_error_callback(error)
@@ -148,5 +164,9 @@ module NxtPipeline
148
164
  def raise_reserved_type_inline_error
149
165
  raise ArgumentError, 'Type :inline is reserved for inline steps!'
150
166
  end
167
+
168
+ def default_step_resolvers
169
+ [->(step_argument) { step_argument.is_a?(Symbol) && step_argument }]
170
+ end
151
171
  end
152
172
  end
@@ -1,49 +1,78 @@
1
1
  module NxtPipeline
2
2
  class Step
3
- def initialize(type, constructor, index, **opts)
3
+ def initialize(argument, constructor, index, **opts)
4
4
  define_attr_readers(opts)
5
5
 
6
- @type = type
6
+ @argument = argument
7
7
  @index = index
8
8
  @opts = opts
9
9
  @constructor = constructor
10
- @to_s = "#{opts.merge(type: type)}"
10
+ @to_s = "#{opts.merge(argument: argument)}"
11
+ @options_mapper = opts[:map_options]
11
12
 
12
13
  @status = nil
13
14
  @result = nil
14
15
  @error = nil
16
+ @mapped_options = nil
15
17
  end
16
18
 
17
- attr_reader :type, :result, :status, :error, :opts, :index
19
+ attr_reader :argument, :result, :status, :execution_started_at, :execution_finished_at, :execution_duration, :error, :opts, :index, :mapped_options
18
20
  attr_accessor :to_s
19
21
 
20
22
  alias_method :name=, :to_s=
23
+ alias_method :name, :to_s
21
24
 
22
- def execute(**opts)
23
- guard_args = [opts, self]
24
- if_guard_args = guard_args.take(if_guard.arity)
25
- unless_guard_guard_args = guard_args.take(unless_guard.arity)
25
+ def execute(**changeset)
26
+ track_execution_time do
27
+ set_mapped_options(changeset)
28
+ guard_args = [changeset, self]
26
29
 
27
- if !unless_guard.call(*unless_guard_guard_args) && if_guard.call(*if_guard_args)
28
- self.result = constructor.call(self, **opts)
29
- end
30
+ if evaluate_unless_guard(guard_args) && evaluate_if_guard(guard_args)
31
+ self.result = construct_result(changeset)
32
+ end
30
33
 
31
- set_status
32
- result
34
+ set_status
35
+ result
36
+ end
33
37
  rescue StandardError => e
34
38
  self.status = :failed
35
39
  self.error = e
36
40
  raise
37
41
  end
38
42
 
39
- def type?(potential_type)
40
- type.to_sym == potential_type.to_sym
43
+ def set_mapped_options(changeset)
44
+ mapper = options_mapper || default_options_mapper
45
+ mapper_args = [changeset, self].take(mapper.arity)
46
+ self.mapped_options = mapper.call(*mapper_args)
41
47
  end
42
48
 
43
49
  private
44
50
 
45
- attr_writer :result, :status, :error
46
- attr_reader :constructor
51
+ attr_writer :result, :status, :error, :mapped_options, :execution_started_at, :execution_finished_at, :execution_duration
52
+ attr_reader :constructor, :options_mapper
53
+
54
+ def evaluate_if_guard(args)
55
+ execute_callable(if_guard, args)
56
+ end
57
+
58
+ def evaluate_unless_guard(args)
59
+ !execute_callable(unless_guard, args)
60
+ end
61
+
62
+ def construct_result(changeset)
63
+ args = [self, changeset]
64
+ execute_callable(constructor, args)
65
+ end
66
+
67
+ def execute_callable(callable, args)
68
+ args = args.take(callable.arity)
69
+
70
+ if args.last.is_a?(Hash)
71
+ callable.call(*args.take(args.length - 1), **args.last)
72
+ else
73
+ callable.call(*args)
74
+ end
75
+ end
47
76
 
48
77
  def if_guard
49
78
  opts.fetch(:if) { guard(true) }
@@ -68,5 +97,30 @@ module NxtPipeline
68
97
  def set_status
69
98
  self.status = result.present? ? :success : :skipped
70
99
  end
100
+
101
+ def track_execution_time(&block)
102
+ set_execution_started_at
103
+ block.call
104
+ ensure
105
+ set_execution_finished_at
106
+ set_execution_duration
107
+ end
108
+
109
+ def set_execution_started_at
110
+ self.execution_started_at = Time.current
111
+ end
112
+
113
+ def set_execution_finished_at
114
+ self.execution_finished_at = Time.current
115
+ end
116
+
117
+ def set_execution_duration
118
+ self.execution_duration = execution_finished_at - execution_started_at
119
+ end
120
+
121
+ def default_options_mapper
122
+ # returns an empty hash
123
+ ->(_) { {} }
124
+ end
71
125
  end
72
- end
126
+ end
@@ -1,3 +1,4 @@
1
1
  module NxtPipeline
2
- VERSION = "0.3.0".freeze
2
+ VERSION = "0.4.3".freeze
3
3
  end
4
+
@@ -35,9 +35,9 @@ Gem::Specification.new do |spec|
35
35
  spec.require_paths = ["lib"]
36
36
 
37
37
  spec.add_dependency "activesupport"
38
- spec.add_development_dependency "bundler", "~> 1.17"
38
+ spec.add_development_dependency "bundler", "~> 2.1"
39
39
  spec.add_development_dependency "guard-rspec"
40
- spec.add_development_dependency "rake", "~> 12.3"
40
+ spec.add_development_dependency "rake", "~> 13.0"
41
41
  spec.add_development_dependency "rspec", "~> 3.0"
42
- spec.add_development_dependency "pry"
42
+ spec.add_development_dependency "pry-byebug"
43
43
  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.3.0
4
+ version: 0.4.3
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-12 00:00:00.000000000 Z
13
+ date: 2020-10-23 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -32,14 +32,14 @@ dependencies:
32
32
  requirements:
33
33
  - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '1.17'
35
+ version: '2.1'
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '1.17'
42
+ version: '2.1'
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: guard-rspec
45
45
  requirement: !ruby/object:Gem::Requirement
@@ -60,14 +60,14 @@ dependencies:
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '12.3'
63
+ version: '13.0'
64
64
  type: :development
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
- version: '12.3'
70
+ version: '13.0'
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: rspec
73
73
  requirement: !ruby/object:Gem::Requirement
@@ -83,7 +83,7 @@ dependencies:
83
83
  - !ruby/object:Gem::Version
84
84
  version: '3.0'
85
85
  - !ruby/object:Gem::Dependency
86
- name: pry
86
+ name: pry-byebug
87
87
  requirement: !ruby/object:Gem::Requirement
88
88
  requirements:
89
89
  - - ">="
@@ -105,6 +105,7 @@ extra_rdoc_files: []
105
105
  files:
106
106
  - ".circleci/config.yml"
107
107
  - ".gitignore"
108
+ - ".pryrc"
108
109
  - ".rspec"
109
110
  - ".ruby-version"
110
111
  - ".travis.yml"
@@ -121,7 +122,6 @@ files:
121
122
  - bin/setup
122
123
  - lib/nxt_pipeline.rb
123
124
  - lib/nxt_pipeline/constructor.rb
124
- - lib/nxt_pipeline/dsl.rb
125
125
  - lib/nxt_pipeline/error_callback.rb
126
126
  - lib/nxt_pipeline/logger.rb
127
127
  - lib/nxt_pipeline/pipeline.rb
@@ -150,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
150
  - !ruby/object:Gem::Version
151
151
  version: '0'
152
152
  requirements: []
153
- rubygems_version: 3.0.3
153
+ rubygems_version: 3.1.2
154
154
  signing_key:
155
155
  specification_version: 4
156
156
  summary: DSL to build Pipeline with mountable Segments to process things.
@@ -1,44 +0,0 @@
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