business_flow 0.19.4 → 0.19.6

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: 307a7eda1932f6d130f00e092df7a21cb2a33b011e83673558fdcf0fcbddb197
4
- data.tar.gz: 6397d9e3d64e63892323e1d17489aab5d8c65a81dd38723cb7160278c2cbb41b
3
+ metadata.gz: e3d7a4e0a69860a84583948ad6d3b6189619629e2b6b8a3dac4bdc10c5467c8d
4
+ data.tar.gz: b3ae0a3456b06ad06826cd55f1c0a7b0c1f5d2789d5d4514b8a155603fb28388
5
5
  SHA512:
6
- metadata.gz: 73d2958f6aabc69cb04f442a317c1b4cf561f305cbc2641e88a623d003d46dd496d69d62b240fba1d9e0567f4297ed6fa80ef935ea832629563ce31205b419c3
7
- data.tar.gz: 15db0becd4c584ef9dd926eb02d4ffe34b30b184ffdbd484fc44b8264e25c1a856b8ea41049ba3f005a342d438dd16e1a75a32842c5e9e1c4d00a8c668eeec88
6
+ metadata.gz: '08d17ede87236b175510b8e08d9f09815e77f24dd922d3abd5bbd22f447a6a6abbde9f7a3ab36158b70a9dbde879c0ab7949524c39da2162dd66cf16149c87cd'
7
+ data.tar.gz: e738a183b1fc48a0632eb4db0f6c3212b1e270d1e36f3fcdee7bbe1b1c2e0c6655de48036f66f25e7aa289f7b46cbe561e7f9b524534a05e1b9c836719a7e937
data/.reek.yml ADDED
@@ -0,0 +1,13 @@
1
+ # Auto generated by Reeks --todo flag
2
+ ---
3
+ detectors:
4
+ FeatureEnvy:
5
+ exclude:
6
+ - BusinessFlow::ClusterLock::ClassMethods::LockFailure#add_to
7
+ LongParameterList:
8
+ exclude:
9
+ - BusinessFlow#self.add_error
10
+ UncommunicativeVariableName:
11
+ # N.B. If we want to enable this, we need to tweak the RuboCop config. In particular, RuboCop
12
+ # prefers `e` for exceptions.
13
+ enabled: false
data/.rubocop.yml ADDED
@@ -0,0 +1,84 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 2.7
6
+ NewCops: enable
7
+ Exclude:
8
+ - coverage/**/*
9
+
10
+ # Excluding specs here because of RSpec's tendency to look at the line-number info on a backtrace to
11
+ # decide what to show you when a matcher fails. I.E. a very long line may be needed to ensure you
12
+ # get good diagnostic info on a test failure.
13
+ Layout/LineLength:
14
+ Max: 100
15
+ Exclude:
16
+ - spec/**/*
17
+
18
+ RSpec/MultipleMemoizedHelpers:
19
+ Enabled: false
20
+
21
+ RSpec/IndexedLet:
22
+ # We have specs that are describing process steps, so... numbers are kinda the best way to describe that.
23
+ Enabled: false
24
+
25
+ RSpec/NestedGroups:
26
+ Max: 4
27
+
28
+ RSpec/LeakyConstantDeclaration:
29
+ # This effectively duplicates Lint/ConstantDefinitionInBlock.
30
+ Enabled: false
31
+
32
+ Lint/ConstantDefinitionInBlock:
33
+ Exclude:
34
+ - spec/**/*
35
+
36
+ Naming/MemoizedInstanceVariableName:
37
+ # This conflicts with Reek.
38
+ Enabled: false
39
+
40
+ # Explicit is bettr than implicit.
41
+ Style/EmptyElse:
42
+ Enabled: false
43
+
44
+ Style/RescueModifier:
45
+ Exclude:
46
+ - spec/**/*
47
+
48
+ Style/DocumentDynamicEvalDefinition:
49
+ Enabled: false
50
+
51
+ # Explicit is bettr than implicit.
52
+ FactoryBot/SyntaxMethods:
53
+ Enabled: false
54
+
55
+ # For `match(hash_including(...))`, in specs, this rule would make things pretty unreadable.
56
+ Layout/ClosingParenthesisIndentation:
57
+ Exclude:
58
+ - spec/**/*
59
+
60
+ # This makes things pretty unreadable in a lot of cases, including but not limited to
61
+ # `match(hash_including(...))` in specs.
62
+ Layout/FirstArgumentIndentation:
63
+ Enabled: false
64
+
65
+ Bundler/OrderedGems:
66
+ Exclude:
67
+ - 'Gemfile'
68
+
69
+ Lint/EmptyBlock:
70
+ Enabled: false
71
+
72
+ # OpenStruct is unwise for actual application code (esp. in light of potentially using yjit in the
73
+ # future), but the flexibility is handy for specs.
74
+ Style/OpenStructUse:
75
+ Exclude:
76
+ - spec/**/*
77
+
78
+ # Disabling this because the "preferred" way requires allocating a Range which is stupid and wrong.
79
+ Style/RandomWithOffset:
80
+ Enabled: false
81
+
82
+ # What, exactly, does the Ruby community have against the Truthiness Operator?!
83
+ Style/DoubleNegation:
84
+ Enabled: false
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ business_flow
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.7.4
data/Gemfile CHANGED
@@ -1,6 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in business_flow.gemspec
6
8
  gemspec
9
+
10
+ gem 'pry', '~> 0.14.2'
11
+ gem 'rake', '~> 13.0'
12
+ gem 'reek', '~> 6.1'
13
+ gem 'retryable', '~> 3.0.4'
14
+ gem 'rspec', '~> 3.0'
15
+ gem 'rubocop', '~> 1.59.0'
16
+ gem 'rubocop-rspec', '~> 2.29.1'
17
+ gem 'simplecov', '~> 0.22.0'
18
+ gem 'timecop', '~> 0.9.1'
data/Gemfile.lock CHANGED
@@ -1,16 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- business_flow (0.19.4)
4
+ business_flow (0.19.6)
5
5
  activemodel (>= 4.2, < 8)
6
6
  activesupport (>= 4.2, < 8)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (7.1.3)
12
- activesupport (= 7.1.3)
13
- activesupport (7.1.3)
11
+ activemodel (7.1.4)
12
+ activesupport (= 7.1.4)
13
+ activesupport (7.1.4)
14
14
  base64
15
15
  bigdecimal
16
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -22,75 +22,99 @@ GEM
22
22
  tzinfo (~> 2.0)
23
23
  ast (2.4.2)
24
24
  base64 (0.2.0)
25
- bigdecimal (3.1.6)
26
- concurrent-ruby (1.2.3)
25
+ bigdecimal (3.1.8)
26
+ coderay (1.1.3)
27
+ concurrent-ruby (1.3.4)
27
28
  connection_pool (2.4.1)
28
- diff-lcs (1.5.0)
29
- docile (1.4.0)
30
- drb (2.2.0)
31
- ruby2_keywords
32
- i18n (1.14.1)
29
+ diff-lcs (1.5.1)
30
+ docile (1.4.1)
31
+ drb (2.2.1)
32
+ i18n (1.14.6)
33
33
  concurrent-ruby (~> 1.0)
34
- jaro_winkler (1.5.4)
34
+ json (2.7.2)
35
35
  kwalify (0.7.2)
36
- minitest (5.21.2)
36
+ language_server-protocol (3.17.0.3)
37
+ method_source (1.1.0)
38
+ minitest (5.25.1)
37
39
  mutex_m (0.2.0)
38
- parallel (1.22.1)
39
- parser (3.2.2.1)
40
+ parallel (1.26.3)
41
+ parser (3.2.2.4)
40
42
  ast (~> 2.4.1)
43
+ racc
44
+ pry (0.14.2)
45
+ coderay (~> 1.1)
46
+ method_source (~> 1.0)
47
+ racc (1.8.1)
41
48
  rainbow (3.1.1)
42
- rake (10.5.0)
49
+ rake (13.2.1)
43
50
  reek (6.1.4)
44
51
  kwalify (~> 0.7.0)
45
52
  parser (~> 3.2.0)
46
53
  rainbow (>= 2.0, < 4.0)
54
+ regexp_parser (2.9.2)
47
55
  retryable (3.0.5)
48
- rspec (3.12.0)
49
- rspec-core (~> 3.12.0)
50
- rspec-expectations (~> 3.12.0)
51
- rspec-mocks (~> 3.12.0)
52
- rspec-core (3.12.0)
53
- rspec-support (~> 3.12.0)
54
- rspec-expectations (3.12.0)
56
+ rexml (3.3.7)
57
+ rspec (3.13.0)
58
+ rspec-core (~> 3.13.0)
59
+ rspec-expectations (~> 3.13.0)
60
+ rspec-mocks (~> 3.13.0)
61
+ rspec-core (3.13.1)
62
+ rspec-support (~> 3.13.0)
63
+ rspec-expectations (3.13.3)
55
64
  diff-lcs (>= 1.2.0, < 2.0)
56
- rspec-support (~> 3.12.0)
57
- rspec-mocks (3.12.0)
65
+ rspec-support (~> 3.13.0)
66
+ rspec-mocks (3.13.1)
58
67
  diff-lcs (>= 1.2.0, < 2.0)
59
- rspec-support (~> 3.12.0)
60
- rspec-support (3.12.0)
61
- rubocop (0.68.1)
62
- jaro_winkler (~> 1.5.1)
68
+ rspec-support (~> 3.13.0)
69
+ rspec-support (3.13.1)
70
+ rubocop (1.59.0)
71
+ json (~> 2.3)
72
+ language_server-protocol (>= 3.17.0)
63
73
  parallel (~> 1.10)
64
- parser (>= 2.5, != 2.5.1.1)
74
+ parser (>= 3.2.2.4)
65
75
  rainbow (>= 2.2.2, < 4.0)
76
+ regexp_parser (>= 1.8, < 3.0)
77
+ rexml (>= 3.2.5, < 4.0)
78
+ rubocop-ast (>= 1.30.0, < 2.0)
66
79
  ruby-progressbar (~> 1.7)
67
- unicode-display_width (>= 1.4.0, < 1.6)
68
- rubocop-rspec (1.24.0)
69
- rubocop (>= 0.53.0)
70
- ruby-progressbar (1.11.0)
71
- ruby2_keywords (0.0.5)
80
+ unicode-display_width (>= 2.4.0, < 3.0)
81
+ rubocop-ast (1.30.0)
82
+ parser (>= 3.2.1.0)
83
+ rubocop-capybara (2.21.0)
84
+ rubocop (~> 1.41)
85
+ rubocop-factory_bot (2.26.0)
86
+ rubocop (~> 1.41)
87
+ rubocop-rspec (2.29.2)
88
+ rubocop (~> 1.40)
89
+ rubocop-capybara (~> 2.17)
90
+ rubocop-factory_bot (~> 2.22)
91
+ rubocop-rspec_rails (~> 2.28)
92
+ rubocop-rspec_rails (2.29.0)
93
+ rubocop (~> 1.40)
94
+ ruby-progressbar (1.13.0)
72
95
  simplecov (0.22.0)
73
96
  docile (~> 1.1)
74
97
  simplecov-html (~> 0.11)
75
98
  simplecov_json_formatter (~> 0.1)
76
- simplecov-html (0.12.3)
99
+ simplecov-html (0.13.1)
77
100
  simplecov_json_formatter (0.1.4)
78
- timecop (0.9.6)
101
+ timecop (0.9.10)
79
102
  tzinfo (2.0.6)
80
103
  concurrent-ruby (~> 1.0)
81
- unicode-display_width (1.5.0)
104
+ unicode-display_width (2.6.0)
82
105
 
83
106
  PLATFORMS
84
107
  ruby
85
108
 
86
109
  DEPENDENCIES
87
110
  business_flow!
88
- rake (~> 10.0)
111
+ pry (~> 0.14.2)
112
+ rake (~> 13.0)
89
113
  reek (~> 6.1)
90
114
  retryable (~> 3.0.4)
91
115
  rspec (~> 3.0)
92
- rubocop (~> 0.53)
93
- rubocop-rspec (~> 1.24.0)
116
+ rubocop (~> 1.59.0)
117
+ rubocop-rspec (~> 2.29.1)
94
118
  simplecov (~> 0.22.0)
95
119
  timecop (~> 0.9.1)
96
120
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
data/bin/console CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # frozen_string_literal: true
4
+
3
5
  require 'bundler/setup'
4
6
  require 'business_flow'
5
7
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
@@ -23,12 +24,7 @@ Gem::Specification.new do |spec|
23
24
  spec.add_dependency 'activemodel', '>= 4.2', '< 8'
24
25
  spec.add_dependency 'activesupport', '>= 4.2', '< 8'
25
26
 
26
- spec.add_development_dependency 'rake', '~> 10.0'
27
- spec.add_development_dependency 'reek', '~> 6.1'
28
- spec.add_development_dependency 'retryable', '~> 3.0.4'
29
- spec.add_development_dependency 'rspec', '~> 3.0'
30
- spec.add_development_dependency 'rubocop', '~> 0.53'
31
- spec.add_development_dependency 'rubocop-rspec', '~> 1.24.0'
32
- spec.add_development_dependency 'simplecov', '~> 0.22.0'
33
- spec.add_development_dependency 'timecop', '~> 0.9.1'
27
+ spec.metadata['rubygems_mfa_required'] = 'true'
28
+
29
+ spec.required_ruby_version = '>= 2.7.0'
34
30
  end
@@ -68,13 +68,14 @@ module BusinessFlow
68
68
 
69
69
  def clear_cache(parameter_object = {})
70
70
  clear_cache!(parameter_object)
71
- rescue FlowFailedException => exc
72
- exc.flow
71
+ rescue FlowFailedException => e
72
+ e.flow
73
73
  end
74
74
 
75
75
  def clear_cache!(parameter_object = {})
76
76
  flow = build(parameter_object)
77
77
  raise FlowFailedException, result_from(flow) if flow.errors?
78
+
78
79
  ClassMethods.clear_cache(flow)
79
80
  end
80
81
 
@@ -91,8 +92,8 @@ module BusinessFlow
91
92
  with_cache(flow) do
92
93
  super(flow)._business_flow_cacheable_finalize(flow.cache_key)
93
94
  end
94
- rescue FlowFailedException => exc
95
- exc.flow
95
+ rescue FlowFailedException => e
96
+ e.flow
96
97
  end
97
98
 
98
99
  def with_cache(flow, &blk)
@@ -117,11 +118,13 @@ module BusinessFlow
117
118
  RESULT_FINALIZE = proc do |cache_key|
118
119
  @cache_key = cache_key
119
120
  raise FlowFailedException, self if errors?
121
+
120
122
  self
121
123
  end
122
124
 
123
125
  def add_cache_key_to_result_class
124
126
  return if @cache_key_added
127
+
125
128
  result_class = const_get(:Result)
126
129
  DSL::PublicField.new(:cache_key).add_to(result_class)
127
130
  result_class.send(:define_method, :_business_flow_cacheable_finalize,
@@ -98,11 +98,9 @@ module BusinessFlow
98
98
  def lookup_callable(first_instance)
99
99
  constant_name = @callable.to_s.camelcase
100
100
  first_instance.class.module_parents.each do |parent|
101
- begin
102
- return parent.const_get(constant_name)
103
- rescue NameError
104
- next
105
- end
101
+ return parent.const_get(constant_name)
102
+ rescue NameError
103
+ next
106
104
  end
107
105
  nil
108
106
  end
@@ -20,23 +20,24 @@ module BusinessFlow
20
20
  end
21
21
 
22
22
  def self.default_servers=(servers)
23
- if servers.is_a?(String)
24
- @default_servers = proc { servers }
25
- elsif servers
26
- @default_servers = Callable.new(servers)
27
- else
28
- @default_servers = nil
29
- end
23
+ @default_servers = if servers.is_a?(String)
24
+ proc { servers }
25
+ elsif servers
26
+ Callable.new(servers)
27
+ else
28
+ nil
29
+ end
30
30
  end
31
31
 
32
32
  def self.default_servers
33
- @default_servers ||= proc { nil }
33
+ @default_servers ||= proc {}
34
34
  end
35
35
 
36
36
  def assert_cluster_lock!
37
- @_business_flow_cluster_lock.assert! if !BusinessFlow::ClusterLock.disabled?
38
- rescue ZK::Exceptions::ZKError => exc
39
- errors.add(:cluster_lock, :assert_failed, message: exc.message)
37
+ @_business_flow_cluster_lock.assert! unless BusinessFlow::ClusterLock.disabled?
38
+ rescue ZK::Exceptions::ZKError => e
39
+ BusinessFlow.add_error(errors, :cluster_lock, :assert_failed, e.message)
40
+
40
41
  raise
41
42
  end
42
43
 
@@ -49,10 +50,11 @@ module BusinessFlow
49
50
  end
50
51
 
51
52
  # DSL Methods
52
- module ClassMethods
53
+ module ClassMethods # rubocop:disable Metrics/ModuleLength
53
54
  # Error raised when there is an internal issue with acquiring a lock.
54
55
  class LockFailure < StandardError
55
56
  attr_reader :error_type
57
+
56
58
  def initialize(error_type, message)
57
59
  @error_type = error_type
58
60
  super(message)
@@ -60,7 +62,9 @@ module BusinessFlow
60
62
 
61
63
  def add_to(flow)
62
64
  errors = flow.errors
63
- errors.add(:cluster_lock, error_type, message: message) if !errors.key?(:cluster_lock)
65
+ unless errors.key?(:cluster_lock)
66
+ BusinessFlow.add_error(errors, :cluster_lock, error_type, message)
67
+ end
64
68
  flow
65
69
  end
66
70
  end
@@ -114,8 +118,8 @@ module BusinessFlow
114
118
  ClassMethods.with_lock(flow, lock_info) do
115
119
  super(flow)._business_flow_cluster_lock_finalize(lock_info)
116
120
  end
117
- rescue LockFailure => exc
118
- return result_from(exc.add_to(flow))._business_flow_cluster_lock_finalize(lock_info)
121
+ rescue LockFailure => e
122
+ result_from(e.add_to(flow))._business_flow_cluster_lock_finalize(lock_info)
119
123
  end
120
124
 
121
125
  RESULT_FINALIZE = proc do |cluster_lock_info|
@@ -125,6 +129,7 @@ module BusinessFlow
125
129
 
126
130
  def add_cluster_luck_info_to_result_class
127
131
  return if @cluster_lock_info_added
132
+
128
133
  result_class = const_get(:Result)
129
134
  DSL::PublicField.new(:cluster_lock_info).add_to(result_class)
130
135
  result_class.send(:define_method, :_business_flow_cluster_lock_finalize,
@@ -134,19 +139,23 @@ module BusinessFlow
134
139
 
135
140
  # :reek:NilCheck
136
141
  def self.zookeeper_server_list(flow)
137
- servers = catch(:halt_step) { flow.class.with_zookeeper_servers.call(flow)&.merge_into(flow)&.to_s }
138
- if servers.nil? || servers.length == 0
142
+ servers =
143
+ catch(:halt_step) { flow.class.with_zookeeper_servers.call(flow)&.merge_into(flow)&.to_s }
144
+ if servers.nil? || servers.empty?
139
145
  raise LockFailure.new(:no_servers, 'no zookeeper servers provided')
140
146
  end
147
+
141
148
  servers
142
149
  end
143
150
 
144
151
  # :reek:NilCheck
145
152
  def self.lock_name(flow)
146
- lock_name = catch(:halt_step) { flow.class.with_cluster_lock.call(flow)&.merge_into(flow)&.to_s }
147
- if lock_name.nil? || lock_name.length == 0
153
+ lock_name =
154
+ catch(:halt_step) { flow.class.with_cluster_lock.call(flow)&.merge_into(flow)&.to_s }
155
+ if lock_name.nil? || lock_name.empty?
148
156
  raise LockFailure.new(:no_lock_name, 'no lock name provided')
149
157
  end
158
+
150
159
  lock_name
151
160
  end
152
161
 
@@ -169,7 +178,7 @@ module BusinessFlow
169
178
  def self.inner_acquire_lock(zk_connection, lock, payload)
170
179
  lock_held = lock.lock(wait: false)
171
180
  payload[:lock_acquired] = lock_held if payload
172
- if !lock_held
181
+ unless lock_held
173
182
  zk_connection.close!
174
183
  raise LockFailure.new(:lock_unavailable, 'the lock was not available')
175
184
  end
@@ -178,11 +187,11 @@ module BusinessFlow
178
187
 
179
188
  def self.cleanup(lock, zk_connection)
180
189
  begin
181
- lock.unlock if lock
190
+ lock&.unlock
182
191
  rescue ZK::Exceptions::OperationTimeOut
183
192
  # Just let the connection close handle this.
184
193
  end
185
- zk_connection.close! if zk_connection
194
+ zk_connection&.close!
186
195
  end
187
196
 
188
197
  # :reek:ControlParameter
@@ -199,16 +208,16 @@ module BusinessFlow
199
208
  end
200
209
  end
201
210
 
202
- def self.with_lock(flow, lock_info, &blk)
203
- zk_connection, lock =
204
- if !BusinessFlow::ClusterLock.disabled?
211
+ def self.with_lock(flow, lock_info, &_blk)
212
+ unless BusinessFlow::ClusterLock.disabled?
213
+ zk_connection, lock =
205
214
  instrumented_acquire_lock(flow, lock_info)
206
- end
215
+ end
207
216
  yield lock_info
208
- rescue ZK::Exceptions::LockAssertionFailedError, ZK::Exceptions::OperationTimeOut => exc
217
+ rescue ZK::Exceptions::LockAssertionFailedError, ZK::Exceptions::OperationTimeOut => e
209
218
  # This would occur if we asserted a cluster lock while executing the flow.
210
219
  # This will have set an error on the flow, so we can carry on.
211
- raise LockFailure.new(exception_to_error_type(exc), exc.message)
220
+ raise LockFailure.new(exception_to_error_type(e), e.message)
212
221
  ensure
213
222
  cleanup(lock, zk_connection)
214
223
  end
@@ -3,7 +3,7 @@
3
3
  module BusinessFlow
4
4
  # Provides compatibility for ActiveSupport/Model 4.x through 7.x
5
5
  module Compat
6
- if !Module.instance_methods.include?(:module_parents)
6
+ unless Module.instance_methods.include?(:module_parents)
7
7
  # ActiveSupport 5 removed #parents in favor of #module_parents.
8
8
  class ::Module
9
9
  def module_parents
@@ -12,13 +12,16 @@ module BusinessFlow
12
12
  end
13
13
  end
14
14
 
15
- if !ActiveModel::Errors.instance_methods.include?(:merge!)
15
+ unless ActiveModel::Errors.instance_methods.include?(:merge!)
16
16
  # ActiveModel 5 added details (which we do not use here) and #merge!
17
17
  # :reek:MissingSafeMethod Look it's the API.
18
- class ::ActiveModel::Errors
19
- def merge!(other)
20
- other.each do |attribute, message|
21
- self[attribute] << message
18
+ module ::ActiveModel
19
+ # Amend the errors class with a convenience method.
20
+ class Errors
21
+ def merge!(other)
22
+ other.each do |attribute, message|
23
+ self[attribute] << message
24
+ end
22
25
  end
23
26
  end
24
27
  end
@@ -13,6 +13,7 @@ module BusinessFlow
13
13
  def call
14
14
  @step_queue.each do |step|
15
15
  break if @flow.errors?
16
+
16
17
  execute_step(step)
17
18
  end
18
19
  end
@@ -5,7 +5,7 @@ module BusinessFlow
5
5
  # ClassMethods.
6
6
  module DSL
7
7
  # Contains the DSL for BusinessFlow
8
- module ClassMethods
8
+ module ClassMethods # rubocop:disable Metrics/ModuleLength
9
9
  # Holds metadata about inputs to a flow
10
10
  class Inputs
11
11
  attr_reader :all, :needs, :optionals
@@ -66,8 +66,8 @@ module BusinessFlow
66
66
 
67
67
  # Allows a field to be retrieved from the initialiaztion parameters
68
68
  def wants(field, default = nil, opts = {}, &blk)
69
- internal_name = "wants_#{field}".to_sym
70
- default = proc { nil } unless default || block_given?
69
+ internal_name = :"wants_#{field}"
70
+ default = proc {} unless default || block_given?
71
71
  uses(internal_name, default, opts, &blk)
72
72
  inputs.add_wants(ParameterField.new(field, internal_name))
73
73
  end
@@ -106,6 +106,7 @@ module BusinessFlow
106
106
  def call(parameter_object = {})
107
107
  flow = build(parameter_object)
108
108
  return result_from(flow) if flow.errors?
109
+
109
110
  execute(flow)
110
111
  end
111
112
 
@@ -128,6 +129,7 @@ module BusinessFlow
128
129
  def call!(*args)
129
130
  flow = call(*args)
130
131
  raise FlowFailedException, flow if flow.errors?
132
+
131
133
  flow
132
134
  end
133
135
 
@@ -137,9 +139,9 @@ module BusinessFlow
137
139
 
138
140
  def step_executor(executor_class = nil)
139
141
  if executor_class
140
- @executor_class = executor_class
142
+ @step_executor = executor_class
141
143
  else
142
- @executor_class ||= ::BusinessFlow::DefaultStepExecutor
144
+ @step_executor ||= ::BusinessFlow::DefaultStepExecutor
143
145
  end
144
146
  end
145
147
 
@@ -159,22 +161,32 @@ module BusinessFlow
159
161
 
160
162
  def finalize_result_provider
161
163
  return if @finalized_result_provider || !@result_copy
164
+
162
165
  const_get(:Result).class_eval "#{@result_copy}\nend", __FILE__, __LINE__
163
166
  @finalized_result_provider = true
164
167
  end
165
168
 
166
- def needs_code
169
+ def needs_code # rubocop:disable Metrics/MethodLength
167
170
  needs.map do |need|
168
- %(if #{need}.nil?
169
- errors.add(:#{need}, :invalid, message: 'must not be nil')
170
- throw :halt_step
171
- end
172
- )
171
+ if BusinessFlow.active_model5?
172
+ %(if #{need}.nil?
173
+ errors.add(:#{need}, :invalid, message: 'must not be nil')
174
+ throw :halt_step
175
+ end
176
+ )
177
+ else
178
+ %(if #{need}.nil?
179
+ errors.add(:#{need}, 'must not be nil')
180
+ throw :halt_step
181
+ end
182
+ )
183
+ end
173
184
  end.join("\n")
174
185
  end
175
186
 
176
187
  def finalize_initializer
177
188
  return if @finalized_initializer
189
+
178
190
  class_eval %{
179
191
  private def _business_flow_dsl_initialize(parameter_object)
180
192
  @parameter_object = parameter_object
@@ -189,7 +201,7 @@ module BusinessFlow
189
201
  FROM_FLOW_PREAMBLE = %(
190
202
  def from_flow(flow)
191
203
  return if errors?
192
- ).freeze
204
+ )
193
205
 
194
206
  RESULT_DEF = %(
195
207
  class Result
@@ -217,7 +229,7 @@ module BusinessFlow
217
229
  !valid?(context)
218
230
  end
219
231
  end
220
- ).freeze
232
+ )
221
233
 
222
234
  # Provides the minimum necessary methods to support the use of
223
235
  # ActiveModel::Errors
@@ -236,13 +248,16 @@ module BusinessFlow
236
248
  end
237
249
 
238
250
  # :reek:ManualDispatch I have no need to actually call human_attribute_name,
251
+ # :reek:TooManyStatements Breaking this up would add complexity.
239
252
  # I just need to know if I have to provide my own.
240
253
  def self.included(klass)
241
254
  klass.extend(ClassMethods)
242
255
  klass.class_eval RESULT_DEF, __FILE__, __LINE__
243
256
  klass.extend(ErrorSupport) unless klass.respond_to?(:human_attribute_name)
244
257
  klass.extend(ActiveModel::Naming)
245
- klass.include(ErrorSupport::InstanceMethods) unless klass.respond_to?(:read_attribute_for_validation)
258
+ return if klass.respond_to?(:read_attribute_for_validation)
259
+
260
+ klass.include(ErrorSupport::InstanceMethods)
246
261
  end
247
262
 
248
263
  attr_reader :parameter_object
@@ -250,6 +265,7 @@ module BusinessFlow
250
265
 
251
266
  def call
252
267
  return if invalid?
268
+
253
269
  klass = self.class
254
270
  klass.step_executor.new(klass.step_queue, self).call
255
271
  end
@@ -262,7 +278,7 @@ module BusinessFlow
262
278
  @parameter_object = parameter_object
263
279
  needs.each do |need|
264
280
  if send(need).nil?
265
- errors.add(need, :invalid, message: 'must not be nil')
281
+ BusinessFlow.add_error(need, :invalid, error_type, 'must not be nil')
266
282
  throw :halt_step
267
283
  end
268
284
  end
@@ -275,6 +291,7 @@ module BusinessFlow
275
291
  # We only want to fall back to a default if we were
276
292
  # given nil. Other falsy vlues should be directly used.
277
293
  return yield if value.nil? && block_given?
294
+
278
295
  value
279
296
  end
280
297
 
@@ -289,11 +306,8 @@ module BusinessFlow
289
306
  end
290
307
 
291
308
  private def _business_flow_dsl_parameters
292
- @_business_flow_dsl_parameters ||= Hash[
293
- self.class.inputs.all.map do |input|
294
- [input, _business_flow_parameter_inner_fetch(input)]
295
- end
296
- ]
309
+ @_business_flow_dsl_parameters ||=
310
+ self.class.inputs.all.to_h { |input| [input, _business_flow_parameter_inner_fetch(input)] }
297
311
  end
298
312
 
299
313
  def errors
@@ -303,7 +317,7 @@ module BusinessFlow
303
317
  def errors?
304
318
  # We're explicitly using the instance variable here so that if no
305
319
  # errors have been created, we don't initialize the error object.
306
- @errors && @errors.present?
320
+ @errors&.present?
307
321
  end
308
322
 
309
323
  def valid?(_context = nil)
@@ -364,6 +378,7 @@ module BusinessFlow
364
378
  def self.eval_method(klass, name, str)
365
379
  return if klass.method_defined?(name) ||
366
380
  klass.private_method_defined?(name)
381
+
367
382
  unsafe_eval_method(klass, name, str)
368
383
  end
369
384
 
@@ -444,12 +459,10 @@ module BusinessFlow
444
459
  end
445
460
 
446
461
  def safe_ivar_name
447
- @safe_ivar_name ||= begin
448
- "@business_flow_dsl_#{field}"
449
- .sub(/\?$/, '_query')
450
- .sub(/\!$/, '_bang')
451
- .to_sym
452
- end
462
+ @safe_ivar_name ||= "@business_flow_dsl_#{field}"
463
+ .sub(/\?$/, '_query')
464
+ .sub(/!$/, '_bang')
465
+ .to_sym
453
466
  end
454
467
  end
455
468
 
@@ -476,7 +489,7 @@ module BusinessFlow
476
489
 
477
490
  def retriever_method_name
478
491
  @retriever_method_name ||=
479
- "_business_flow_dsl_execute_step_for_#{@name}".to_sym
492
+ :"_business_flow_dsl_execute_step_for_#{@name}"
480
493
  end
481
494
  end
482
495
 
@@ -11,10 +11,24 @@ module BusinessFlow
11
11
 
12
12
  # Contains methods that we add to the DSL
13
13
  module ClassMethods
14
- INSTRUMENTATION_PREFIX = 'business_flow'.freeze
14
+ INSTRUMENTATION_PREFIX = 'business_flow'
15
+
16
+ def with_instrument_payload(payload = nil, opts = {}, &blk)
17
+ if payload.is_a?(Hash)
18
+ @payload = Step.new(Callable.new(proc { payload }), {})
19
+ elsif payload || blk
20
+ @payload = Step.new(Callable.new(payload || blk), { default_output: :instrument_payload}.merge(opts))
21
+ else
22
+ @payload ||= Step.new(Callable.new(proc { {} }), opts)
23
+ end
24
+ end
25
+
26
+ def self.instrument_payload(flow)
27
+ catch(:halt_step) { flow.class.with_instrument_payload.call(flow)&.merge_into(flow) }
28
+ end
15
29
 
16
30
  def instrument(name, flow)
17
- payload = { flow: flow }
31
+ payload = { flow: flow }.merge(ClassMethods.instrument_payload(flow))
18
32
  ActiveSupport::Notifications.instrument(
19
33
  "#{INSTRUMENTATION_PREFIX}.#{name}.#{instrumentation_name}", payload
20
34
  ) do
@@ -33,7 +47,7 @@ module BusinessFlow
33
47
 
34
48
  def event_name
35
49
  @event_name ||=
36
- "#{INSTRUMENTATION_PREFIX}.flow.#{instrumentation_name}".freeze
50
+ "#{INSTRUMENTATION_PREFIX}.flow.#{instrumentation_name}"
37
51
  end
38
52
  end
39
53
  end
@@ -4,27 +4,19 @@ module BusinessFlow
4
4
  # Fire ActiveSupport events for every step that's run and on errors
5
5
  class InstrumentedExecutor < DefaultStepExecutor
6
6
  def call
7
- name = flow_event_name
8
- payload = { flow: flow }
9
- ActiveSupport::Notifications.instrument(name, payload) do
7
+ f_class = flow.class
8
+ f_class.instrument('flow', flow) do |payload|
10
9
  super
10
+ notify_errors(f_class.event_name, payload)
11
11
  end
12
- notify_errors(name, payload)
13
12
  end
14
13
 
15
14
  protected
16
15
 
17
16
  def notify_errors(name, payload)
18
17
  return unless flow.errors?
19
- ActiveSupport::Notifications.publish(name + '.error', payload)
20
- end
21
-
22
- def flow_name
23
- @flow_name ||= flow.class.instrumentation_name
24
- end
25
18
 
26
- def flow_event_name
27
- @flow_event_name ||= flow.class.event_name
19
+ ActiveSupport::Notifications.publish("#{name}.error", payload)
28
20
  end
29
21
  end
30
22
  end
@@ -7,7 +7,7 @@ module BusinessFlow
7
7
 
8
8
  def execute_step(step)
9
9
  i_name = event_name(step)
10
- i_payload = { flow: flow, step: step }
10
+ i_payload = { flow: flow, step: step }.merge(::BusinessFlow::Instrument::ClassMethods.instrument_payload(flow))
11
11
  ActiveSupport::Notifications.instrument(i_name, i_payload) do |payload|
12
12
  payload[:step_result] = super
13
13
  end
@@ -21,5 +21,11 @@ module BusinessFlow
21
21
  def event_name(step)
22
22
  "business_flow.step.#{step_event_name(step)}"
23
23
  end
24
+
25
+ protected
26
+
27
+ def flow_name
28
+ @flow_name ||= flow.class.instrumentation_name
29
+ end
24
30
  end
25
31
  end
@@ -3,7 +3,7 @@
3
3
  begin
4
4
  require 'retryable'
5
5
  rescue LoadError
6
- STDERR.puts 'BusinessFlow::Retryable requires the retryable gem'
6
+ warn 'BusinessFlow::Retryable requires the retryable gem'
7
7
  raise
8
8
  end
9
9
 
@@ -14,12 +14,13 @@ module BusinessFlow
14
14
 
15
15
  def parameters_from_source(source)
16
16
  return source if inputs.blank?
17
- Hash[inputs.map do |input_name, input_value|
18
- [
19
- input_name,
20
- Inputs.process_input(source, input_value)
21
- ]
22
- end]
17
+
18
+ inputs.to_h do |input_name, input_value| # rubocop:disable Style/HashTransformValues
19
+ [
20
+ input_name,
21
+ Inputs.process_input(source, input_value)
22
+ ]
23
+ end
23
24
  end
24
25
 
25
26
  def self.process_input(source, input_value)
@@ -133,7 +134,7 @@ module BusinessFlow
133
134
  Result.new(step_result, outputs, output)
134
135
  end
135
136
 
136
- private
137
+ private
137
138
 
138
139
  # :reek:ManualDispatch This is faster.
139
140
  def callable_for(step_result)
@@ -183,9 +184,7 @@ module BusinessFlow
183
184
  opts.fetch(:condition) do
184
185
  if_stmts = opts[:if]
185
186
  unless_stmts = opts[:unless]
186
- if if_stmts.present? || unless_stmts.present?
187
- ConditionList.new(if_stmts, unless_stmts)
188
- end
187
+ ConditionList.new(if_stmts, unless_stmts) if if_stmts.present? || unless_stmts.present?
189
188
  end
190
189
  end
191
190
  end
@@ -222,6 +221,7 @@ module BusinessFlow
222
221
 
223
222
  def output_fields
224
223
  return [] unless outputs
224
+
225
225
  outputs.values.select { |field| field.is_a?(Symbol) }
226
226
  end
227
227
 
@@ -231,20 +231,20 @@ module BusinessFlow
231
231
 
232
232
  private
233
233
 
234
- PARAMETERS_NO_INPUT = 'parameter_source'.freeze
234
+ PARAMETERS_NO_INPUT = 'parameter_source'
235
235
  PARAMETERS_WITH_INPUT =
236
- '@input_object.parameters_from_source(parameter_source)'.freeze
236
+ '@input_object.parameters_from_source(parameter_source)'
237
237
  WITHOUT_CONDITION = %(
238
238
  @result_factory.result(@callable.call(parameter_source, parameters),
239
239
  parameter_source)
240
- ).freeze
240
+ )
241
241
  WITH_CONDITION = %(
242
242
  if @condition.call(parameter_source, parameters)
243
243
  #{WITHOUT_CONDITION}
244
244
  else
245
245
  CONDITION_FAILED
246
246
  end
247
- ).freeze
247
+ )
248
248
 
249
249
  def update_call_method
250
250
  params = @input_object ? PARAMETERS_WITH_INPUT : PARAMETERS_NO_INPUT
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BusinessFlow
4
- VERSION = '0.19.4'.freeze
4
+ VERSION = '0.19.6'
5
5
  end
data/lib/business_flow.rb CHANGED
@@ -18,4 +18,15 @@ require 'business_flow/instrument'
18
18
 
19
19
  # Makes the magic happen.
20
20
  module BusinessFlow
21
+ def self.active_model5?
22
+ @active_model5_in_use ||= ActiveModel.version >= Gem::Version.new('5.0.0')
23
+ end
24
+
25
+ def self.add_error(errors, field, error_type, message)
26
+ if active_model5?
27
+ errors.add(field, error_type, message: message)
28
+ else
29
+ errors.add(field, message)
30
+ end
31
+ end
21
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: business_flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.4
4
+ version: 0.19.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Scarborough
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-24 00:00:00.000000000 Z
11
+ date: 2024-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -50,118 +50,6 @@ dependencies:
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '8'
53
- - !ruby/object:Gem::Dependency
54
- name: rake
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '10.0'
60
- type: :development
61
- prerelease: false
62
- version_requirements: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - "~>"
65
- - !ruby/object:Gem::Version
66
- version: '10.0'
67
- - !ruby/object:Gem::Dependency
68
- name: reek
69
- requirement: !ruby/object:Gem::Requirement
70
- requirements:
71
- - - "~>"
72
- - !ruby/object:Gem::Version
73
- version: '6.1'
74
- type: :development
75
- prerelease: false
76
- version_requirements: !ruby/object:Gem::Requirement
77
- requirements:
78
- - - "~>"
79
- - !ruby/object:Gem::Version
80
- version: '6.1'
81
- - !ruby/object:Gem::Dependency
82
- name: retryable
83
- requirement: !ruby/object:Gem::Requirement
84
- requirements:
85
- - - "~>"
86
- - !ruby/object:Gem::Version
87
- version: 3.0.4
88
- type: :development
89
- prerelease: false
90
- version_requirements: !ruby/object:Gem::Requirement
91
- requirements:
92
- - - "~>"
93
- - !ruby/object:Gem::Version
94
- version: 3.0.4
95
- - !ruby/object:Gem::Dependency
96
- name: rspec
97
- requirement: !ruby/object:Gem::Requirement
98
- requirements:
99
- - - "~>"
100
- - !ruby/object:Gem::Version
101
- version: '3.0'
102
- type: :development
103
- prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- requirements:
106
- - - "~>"
107
- - !ruby/object:Gem::Version
108
- version: '3.0'
109
- - !ruby/object:Gem::Dependency
110
- name: rubocop
111
- requirement: !ruby/object:Gem::Requirement
112
- requirements:
113
- - - "~>"
114
- - !ruby/object:Gem::Version
115
- version: '0.53'
116
- type: :development
117
- prerelease: false
118
- version_requirements: !ruby/object:Gem::Requirement
119
- requirements:
120
- - - "~>"
121
- - !ruby/object:Gem::Version
122
- version: '0.53'
123
- - !ruby/object:Gem::Dependency
124
- name: rubocop-rspec
125
- requirement: !ruby/object:Gem::Requirement
126
- requirements:
127
- - - "~>"
128
- - !ruby/object:Gem::Version
129
- version: 1.24.0
130
- type: :development
131
- prerelease: false
132
- version_requirements: !ruby/object:Gem::Requirement
133
- requirements:
134
- - - "~>"
135
- - !ruby/object:Gem::Version
136
- version: 1.24.0
137
- - !ruby/object:Gem::Dependency
138
- name: simplecov
139
- requirement: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - "~>"
142
- - !ruby/object:Gem::Version
143
- version: 0.22.0
144
- type: :development
145
- prerelease: false
146
- version_requirements: !ruby/object:Gem::Requirement
147
- requirements:
148
- - - "~>"
149
- - !ruby/object:Gem::Version
150
- version: 0.22.0
151
- - !ruby/object:Gem::Dependency
152
- name: timecop
153
- requirement: !ruby/object:Gem::Requirement
154
- requirements:
155
- - - "~>"
156
- - !ruby/object:Gem::Version
157
- version: 0.9.1
158
- type: :development
159
- prerelease: false
160
- version_requirements: !ruby/object:Gem::Requirement
161
- requirements:
162
- - - "~>"
163
- - !ruby/object:Gem::Version
164
- version: 0.9.1
165
53
  description:
166
54
  email:
167
55
  - alex@teak.io
@@ -170,7 +58,11 @@ extensions: []
170
58
  extra_rdoc_files: []
171
59
  files:
172
60
  - ".gitignore"
61
+ - ".reek.yml"
173
62
  - ".rspec"
63
+ - ".rubocop.yml"
64
+ - ".ruby-gemset"
65
+ - ".ruby-version"
174
66
  - ".travis.yml"
175
67
  - Gemfile
176
68
  - Gemfile.lock
@@ -199,7 +91,8 @@ files:
199
91
  homepage: https://teak.io
200
92
  licenses:
201
93
  - MIT
202
- metadata: {}
94
+ metadata:
95
+ rubygems_mfa_required: 'true'
203
96
  post_install_message:
204
97
  rdoc_options: []
205
98
  require_paths:
@@ -208,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
101
  requirements:
209
102
  - - ">="
210
103
  - !ruby/object:Gem::Version
211
- version: '0'
104
+ version: 2.7.0
212
105
  required_rubygems_version: !ruby/object:Gem::Requirement
213
106
  requirements:
214
107
  - - ">="