nxt_pipeline 0.3.1 → 0.4.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: be4eec17fa6c2abc134a5e02ef10681e60397e1b358a54c593e38faabcaa28f0
4
- data.tar.gz: a109440aa292f3f33be8cc43b4ceadfef9b7e7869d8e701dd2a0afebf37f62d2
3
+ metadata.gz: e8762564262fbcc4274226dd10acd423e960f31624ae20ca539a4951a626e211
4
+ data.tar.gz: 17f1adb27f76aaddf71e2dd00ae303e8837560e4fdfae911a98812f30408e541
5
5
  SHA512:
6
- metadata.gz: 3fc83095718bc6e4e2a8d1827e3e59ab1e03abe3e5b14994a8aa2699f4a996bc72cb85439a52720df378310ec93fb8de4009f483ddec31fa2b06b6b8e7a6dd8d
7
- data.tar.gz: 6244dcbcea68037108f6b91f0d15489ebc61cbe03b950d3f4306b66f24f5fd849401124a7816a9b3a940eebb11dad73f13ce36718e25047bed1951d7b20a8efc
6
+ metadata.gz: efb282aef978706713a0f76332a66298c99d5278794e6935742d08a5d54d271d9c01a633e0c8fcf7d5b67a4c0f945d3fe4de64b871558e7e4a80ac5e87088264
7
+ data.tar.gz: 48b4ba6cbb45a9a7f2848e70bc3bd4e6bde884badf820e74cda8d1fea69903f5119204bc073f5f8b6e6386ef6c805f5b0e009938dd881a024d8842947a3697e5
data/Gemfile.lock CHANGED
@@ -1,17 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_pipeline (0.3.1)
4
+ nxt_pipeline (0.4.0)
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.0)
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.1, >= 2.1.8)
15
16
  coderay (1.1.2)
16
17
  concurrent-ruby (1.1.5)
17
18
  diff-lcs (1.3)
@@ -72,6 +73,7 @@ GEM
72
73
  thread_safe (0.3.6)
73
74
  tzinfo (1.2.5)
74
75
  thread_safe (~> 0.1)
76
+ zeitwerk (2.1.9)
75
77
 
76
78
  PLATFORMS
77
79
  ruby
data/README.md CHANGED
@@ -90,11 +90,11 @@ will be set to :inline.
90
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,7 +106,7 @@ 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
@@ -137,7 +137,7 @@ You can also define guard clauses that take a proc to prevent the execution of a
137
137
  When the guard takes an argument the step argument is yielded.
138
138
 
139
139
  ```ruby
140
- pipeline.execute('initial argument') do |p|
140
+ pipeline.execute(arg: 'initial argument') do |p|
141
141
  p.step :service, service_class: MyServiceClass, if: -> (arg:) { arg == 'initial argument' }
142
142
  p.step :service, service_class: MyOtherServiceClass, unless: -> { false }
143
143
  end
@@ -1,27 +1,22 @@
1
1
  module NxtPipeline
2
2
  module Dsl
3
3
  module ClassMethods
4
- def pipeline(name = :default, &block)
4
+ def pipeline(name = :default, parent = NxtPipeline::Pipeline, &block)
5
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
6
+ raise ArgumentError, "No block given!" unless block_given?
7
+ raise_already_registered_error(name) if pipeline_registry.key?(name)
8
+ register_pipeline(name, block, parent)
14
9
  end
15
10
 
16
- def pipeline!(name, &block)
11
+ def pipeline!(name, parent = NxtPipeline::Pipeline, &block)
17
12
  raise ArgumentError, "No block given!" unless block_given?
18
- pipeline_registry[name] = block
13
+ register_pipeline(name, block, parent)
19
14
  end
20
15
 
21
16
  private
22
17
 
23
18
  def inherited(child)
24
- child.instance_variable_set(:@pipeline_registry, pipeline_registry.clone)
19
+ child.instance_variable_set(:@pipeline_registry, pipeline_registry.deep_dup)
25
20
  end
26
21
 
27
22
  def raise_already_registered_error(name)
@@ -31,13 +26,22 @@ module NxtPipeline
31
26
  def pipeline_registry
32
27
  @pipeline_registry ||= ActiveSupport::HashWithIndifferentAccess.new
33
28
  end
29
+
30
+ def register_pipeline(name, block, parent)
31
+ pipeline_registry[name] = { config: block, parent: parent }
32
+ end
34
33
  end
35
34
 
36
35
  def self.included(base)
37
36
  base.extend(ClassMethods)
38
37
 
39
38
  def pipeline(name = :default)
40
- self.class.pipeline(name)
39
+ registry = self.class.send(:pipeline_registry)
40
+ entry = registry.fetch(name) { raise KeyError, "No pipeline #{name} registered"}
41
+ config = entry.fetch(:config)
42
+ pipeline = entry.fetch(:parent).new
43
+ instance_exec(pipeline, &config)
44
+ pipeline
41
45
  end
42
46
  end
43
47
  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,44 @@ 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
75
81
  callback = find_error_callback(error)
76
82
  raise unless callback && callback.continue_after_error?
77
83
  handle_step_error(error)
78
- options
84
+ changeset
79
85
  end
80
86
 
81
87
  after_execute_callback.call(self, result) if after_execute_callback.respond_to?(:call)
@@ -109,7 +115,7 @@ module NxtPipeline
109
115
 
110
116
  private
111
117
 
112
- attr_reader :error_callbacks, :registry
118
+ attr_reader :error_callbacks, :constructors, :step_resolvers
113
119
  attr_accessor :current_step,
114
120
  :current_arg,
115
121
  :default_constructor_name,
@@ -119,15 +125,15 @@ module NxtPipeline
119
125
  def default_constructor
120
126
  return unless default_constructor_name
121
127
 
122
- @default_constructor ||= registry[default_constructor_name.to_sym]
128
+ @default_constructor ||= constructors[default_constructor_name.to_sym]
123
129
  end
124
130
 
125
- def execute_step(step, **opts)
131
+ def execute_step(step, **changeset)
126
132
  self.current_step = step
127
- self.current_arg = opts
128
- result = step.execute(**opts)
133
+ self.current_arg = changeset
134
+ result = step.execute(**changeset)
129
135
  log_step(step)
130
- result || opts
136
+ result || changeset
131
137
  end
132
138
 
133
139
  def find_error_callback(error)
@@ -148,5 +154,9 @@ module NxtPipeline
148
154
  def raise_reserved_type_inline_error
149
155
  raise ArgumentError, 'Type :inline is reserved for inline steps!'
150
156
  end
157
+
158
+ def default_step_resolvers
159
+ [->(step_argument) { step_argument.is_a?(Symbol) && step_argument }]
160
+ end
151
161
  end
152
162
  end
@@ -1,32 +1,41 @@
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, :error, :opts, :index, :mapped_options
18
20
  attr_accessor :to_s
19
21
 
20
22
  alias_method :name=, :to_s=
21
23
  alias_method :name, :to_s
22
24
 
23
- def execute(**opts)
24
- guard_args = [opts, self]
25
+ def execute(**changeset)
26
+ mapper = options_mapper || default_options_mapper
27
+ mapper_args = [changeset, self].take(mapper.arity)
28
+ self.mapped_options = mapper.call(*mapper_args)
29
+
30
+ guard_args = [changeset, self]
31
+
25
32
  if_guard_args = guard_args.take(if_guard.arity)
26
33
  unless_guard_guard_args = guard_args.take(unless_guard.arity)
27
34
 
28
35
  if !unless_guard.call(*unless_guard_guard_args) && if_guard.call(*if_guard_args)
29
- self.result = constructor.call(self, **opts)
36
+ constructor_args = [self, changeset]
37
+ constructor_args = constructor_args.take(constructor.arity)
38
+ self.result = constructor.call(*constructor_args)
30
39
  end
31
40
 
32
41
  set_status
@@ -37,14 +46,14 @@ module NxtPipeline
37
46
  raise
38
47
  end
39
48
 
40
- def type?(potential_type)
41
- type.to_sym == potential_type.to_sym
42
- end
49
+ # def type?(potential_type)
50
+ # constructor.resolve_type(potential_type)
51
+ # end
43
52
 
44
53
  private
45
54
 
46
- attr_writer :result, :status, :error
47
- attr_reader :constructor
55
+ attr_writer :result, :status, :error, :mapped_options
56
+ attr_reader :constructor, :options_mapper
48
57
 
49
58
  def if_guard
50
59
  opts.fetch(:if) { guard(true) }
@@ -69,5 +78,10 @@ module NxtPipeline
69
78
  def set_status
70
79
  self.status = result.present? ? :success : :skipped
71
80
  end
81
+
82
+ def default_options_mapper
83
+ # returns an empty hash
84
+ ->(changeset) { {} }
85
+ end
72
86
  end
73
87
  end
@@ -1,3 +1,4 @@
1
1
  module NxtPipeline
2
- VERSION = "0.3.1".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
4
+
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.1
4
+ version: 0.4.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-13 00:00:00.000000000 Z
13
+ date: 2019-08-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport