nxt_pipeline 0.3.0 → 0.4.3

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: 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