nxt_pipeline 0.3.1 → 0.4.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: 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