business_flow 0.6.0 → 0.7.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
  SHA1:
3
- metadata.gz: b485a68eacf74616f5f9e0fe635e210e017c9249
4
- data.tar.gz: a3f786a40733abc5299058190545226953178450
3
+ metadata.gz: aa83401ab6f440779738905d43a9b7114ed03e68
4
+ data.tar.gz: 5af8dbff7edf19b183a8201f418ef3555aadd54a
5
5
  SHA512:
6
- metadata.gz: f74896456557f2042328b355973aabbe77b311466f76470fff9483a6d13a732c4847da248848bc61d4609115be75ff3eed27927e0c717ddaee58de7784fdaf25
7
- data.tar.gz: 327b5cda58ad5add151556a4ad164ec360f0a0421f66827f4ba431f8bc21b075131f92aabc113b4ff491c80029357c9188b01e9c194801a7c543beadfdfb9990
6
+ metadata.gz: 9d44e6d0dd4d71e140fe4f4fff24b1243295355cd71c4e128585a08c0abab3cb93f5da9ea28db39cdd25a7088fa8e03c53ca4fe35623fe63b2c7dbd45df71cd2
7
+ data.tar.gz: f16efd204e218ffd86a0850cefa452b338171164d7fb1ded0bb157745637459e9c49a843acfb4ee5366a8bddc7f370dab3252df8fe7d7abd7762702d8c35da7f
data/Gemfile.lock CHANGED
@@ -1,18 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- business_flow (0.6.0)
4
+ business_flow (0.7.0)
5
5
  activemodel (>= 3.0)
6
6
  activesupport (>= 3.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (5.1.5)
12
- activesupport (= 5.1.5)
13
- activesupport (5.1.5)
11
+ activemodel (5.1.6)
12
+ activesupport (= 5.1.6)
13
+ activesupport (5.1.6)
14
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
- i18n (~> 0.7)
15
+ i18n (>= 0.7, < 2)
16
16
  minitest (~> 5.1)
17
17
  tzinfo (~> 1.1)
18
18
  ast (2.4.0)
@@ -30,7 +30,7 @@ GEM
30
30
  diff-lcs (1.3)
31
31
  docile (1.1.5)
32
32
  equalizer (0.0.11)
33
- i18n (0.9.5)
33
+ i18n (1.0.0)
34
34
  concurrent-ruby (~> 1.0)
35
35
  ice_nine (0.11.2)
36
36
  json (2.1.0)
@@ -7,7 +7,8 @@ module BusinessFlow
7
7
 
8
8
  def cache_key
9
9
  klass = self.class
10
- "#{klass.name}/#{klass.cache_key.call(self, nil)}"
10
+ key = Digest::SHA256.hexdigest(klass.cache_key.call(self, nil).to_s)
11
+ "#{klass.name.underscore}/#{key}"
11
12
  end
12
13
 
13
14
  # DSL Methods
@@ -16,7 +17,11 @@ module BusinessFlow
16
17
  if store
17
18
  @cache_store = store
18
19
  else
19
- @cache_store ||= ActiveSupport::Cache::MemoryStore.new
20
+ @cache_store ||= if defined?(Rails)
21
+ Rails.cache
22
+ else
23
+ ActiveSupport::Cache::MemoryStore.new
24
+ end
20
25
  end
21
26
  end
22
27
 
@@ -8,8 +8,6 @@ module BusinessFlow
8
8
  # was initialized with. The provided .call will instantiate the including
9
9
  # class with a parameter_object as the only argument.
10
10
  module ClassMethods
11
- attr_reader :requirements
12
-
13
11
  # Requires that a field be retrievable from the initialization parameters
14
12
  #
15
13
  # This will only require that the field is not nil. The field may still
@@ -17,25 +15,26 @@ module BusinessFlow
17
15
  #
18
16
  # @param fields The fields required from the initialization parameters
19
17
  def needs(*fields)
20
- @requirements ||= []
21
- @requirements.push(*fields)
22
- wants(*fields)
23
- end
24
-
25
- # Allows a field to be retrieved from the initialiaztoin parameters
26
- #
27
- # Unlike needs, this applies no validation to the field. It may
28
- # be nil.
29
- #
30
- # @param (see #needs)
31
- def wants(*fields)
18
+ @needs ||= []
19
+ return @needs if fields.blank?
20
+ @needs.push(*fields)
32
21
  fields.each do |field|
33
22
  PrivateHelpers.create_parameter_field(self, field)
34
23
  end
35
24
  end
36
25
 
26
+ # Allows a field to be retrieved from the initialiaztion parameters
27
+ def wants(field, default = proc { nil }, opts = {})
28
+ internal_name = "wants_#{field}".to_sym
29
+ uses(internal_name, default, opts)
30
+ PrivateHelpers.create_parameter_field(self, field, internal_name)
31
+ end
32
+
37
33
  # Declares that you will expose a field to the outside world.
38
34
  def provides(*fields)
35
+ @provides ||= []
36
+ return @provides if fields.blank?
37
+ @provides.push(*fields)
39
38
  fields.each { |field| PrivateHelpers.create_field(self, field) }
40
39
  end
41
40
 
@@ -44,7 +43,14 @@ module BusinessFlow
44
43
  # validations.
45
44
  def expects(field, options = {})
46
45
  validates field, options.merge(on: field)
47
- provides field
46
+ PrivateHelpers.create_field(self, field)
47
+ end
48
+
49
+ def uses(field, klass, opts = {})
50
+ callable = Callable.new(klass, self)
51
+ step = Step.new(callable, opts)
52
+ PrivateHelpers.create_memoized_field(self, field, step)
53
+ private field
48
54
  end
49
55
 
50
56
  def step(klass, opts = {})
@@ -53,7 +59,9 @@ module BusinessFlow
53
59
  condition: PrivateHelpers.create_conditional_callable(self, opts)
54
60
  )
55
61
  step_queue << step = Step.new(callable, opts)
56
- provides(*step.outputs.values)
62
+ step.outputs.values.each do |field|
63
+ PrivateHelpers.create_field(self, field)
64
+ end
57
65
  end
58
66
 
59
67
  def call(parameter_object)
@@ -115,15 +123,30 @@ module BusinessFlow
115
123
  end
116
124
  end
117
125
 
118
- def self.create_parameter_field(klass, field)
119
- klass.send(:define_method, field) do
126
+ def self.create_parameter_field(klass, field, fallback = nil)
127
+ klass.send(:define_method, field, &parameter_proc(field, fallback))
128
+ klass.send(:private, field)
129
+ end
130
+
131
+ def self.read_from_parameter_object(field)
132
+ proc do
120
133
  if parameter_object.is_a?(Hash) && parameter_object.key?(field)
121
134
  parameter_object[field]
122
135
  else
123
136
  parameter_object.public_send(field)
124
137
  end
125
138
  end
126
- klass.send(:private, field)
139
+ end
140
+
141
+ def self.parameter_proc(field, fallback)
142
+ read = read_from_parameter_object(field)
143
+ proc do
144
+ begin
145
+ instance_exec(&read)
146
+ rescue NoMethodError
147
+ nil
148
+ end || (fallback && send(fallback))
149
+ end
127
150
  end
128
151
 
129
152
  def self.create_conditional_callable(klass, opts)
@@ -139,21 +162,54 @@ module BusinessFlow
139
162
 
140
163
  def self.create_field(klass, field)
141
164
  return unless field.is_a?(Symbol)
142
- klass.send(:attr_reader, field)
165
+ define_getter(klass, field)
143
166
  setter_name = "#{field}=".to_sym
144
167
  define_setter(klass, setter_name, field)
145
168
  klass.send(:private, setter_name)
146
169
  end
147
170
 
171
+ def self.define_getter(klass, field)
172
+ return if klass.method_defined?(field) ||
173
+ klass.private_method_defined?(field)
174
+ klass.send(:attr_reader, field)
175
+ end
176
+
148
177
  def self.define_setter(klass, setter_name, field)
149
178
  return if klass.method_defined?(setter_name) ||
150
179
  klass.private_method_defined?(setter_name)
151
- klass.send(:define_method, setter_name) do |new_value|
152
- instance_variable_set("@#{field}".to_sym, new_value)
180
+ klass.send(:define_method, setter_name,
181
+ &setter_proc("@#{field}", field))
182
+ end
183
+
184
+ def self.safe_ivar_name(field)
185
+ ivar_name = "@business_flow_dsl_#{field}"
186
+ if ivar_name.end_with?('?')
187
+ ivar_name.sub!(/\?$/, '_query')
188
+ elsif ivar_name.end_with?('!')
189
+ ivar_name.sub!(/\!$/, '_bang')
190
+ end
191
+ ivar_name.to_sym
192
+ end
193
+
194
+ def self.setter_proc(ivar_name, field)
195
+ proc do |new_value|
196
+ instance_variable_set(ivar_name, new_value)
153
197
  throw :halt_step unless valid?(field)
154
198
  new_value
155
199
  end
156
200
  end
201
+
202
+ def self.create_memoized_field(klass, field, step)
203
+ ivar_name = safe_ivar_name(field)
204
+ setter_proc = self.setter_proc(ivar_name, field)
205
+ klass.send(:define_method, field) do
206
+ if instance_variable_defined?(ivar_name)
207
+ instance_variable_get(ivar_name)
208
+ else
209
+ instance_exec(step.call(self).output, &setter_proc)
210
+ end
211
+ end
212
+ end
157
213
  end
158
214
  end
159
215
  end
@@ -5,8 +5,7 @@ module BusinessFlow
5
5
  # Dear reek -- I didn't decided the ActiveModel Validator API, I just
6
6
  # have to live with it.
7
7
  def validate(record)
8
- requirements = record.class.requirements || []
9
- requirements.each do |attribute|
8
+ record.class.needs.each do |attribute|
10
9
  value = record.read_attribute_for_validation(attribute)
11
10
  record.errors[attribute] << 'must not be nil' if value.nil?
12
11
  end
@@ -35,9 +35,10 @@ module BusinessFlow
35
35
  # Represents the result of a step, and allows setting response values on
36
36
  # an object, and merging error data into the same object.
37
37
  class Result
38
- def initialize(result, output_map)
38
+ def initialize(result, output_map, output_callable)
39
39
  @result = result
40
40
  @output_map = output_map
41
+ @output_callable = output_callable
41
42
  @result_errors = begin
42
43
  result.errors
43
44
  rescue NoMethodError
@@ -58,6 +59,10 @@ module BusinessFlow
58
59
  @result_errors.present?
59
60
  end
60
61
 
62
+ def output
63
+ @output_callable.call(@result)
64
+ end
65
+
61
66
  def self.process_output(object, output, output_setter)
62
67
  case output_setter
63
68
  when Symbol
@@ -99,19 +104,26 @@ module BusinessFlow
99
104
  def merge_into(_object); end
100
105
  end
101
106
 
102
- attr_reader :outputs
107
+ # Manage creating results for our step
108
+ ResultFactory = Struct.new(:outputs, :output_callable) do
109
+ def result(step_result)
110
+ Result.new(step_result, outputs, output_callable)
111
+ end
112
+ end
103
113
 
104
114
  def initialize(callable, opts)
105
115
  @callable = callable
106
116
  @input_object = Inputs.new(opts[:inputs] || {})
107
- @outputs = opts[:outputs] || {}
117
+ outputs = opts[:outputs] || {}
118
+ output = opts[:output] || ->(result) { result }
119
+ @result_factory = ResultFactory.new(outputs, output)
108
120
  @condition = opts[:condition] || proc { true }
109
121
  end
110
122
 
111
123
  def call(parameter_source)
112
124
  parameters = @input_object.parameters_from_source(parameter_source)
113
125
  if @condition.call(parameter_source, parameters)
114
- Result.new(@callable.call(parameter_source, parameters), outputs)
126
+ @result_factory.result(@callable.call(parameter_source, parameters))
115
127
  else
116
128
  ConditionFailedResult.new
117
129
  end
@@ -121,6 +133,10 @@ module BusinessFlow
121
133
  @input_object.inputs
122
134
  end
123
135
 
136
+ def outputs
137
+ @result_factory.outputs
138
+ end
139
+
124
140
  def to_s
125
141
  @callable.to_s
126
142
  end
@@ -1,3 +1,3 @@
1
1
  module BusinessFlow
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  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.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Scarborough
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-29 00:00:00.000000000 Z
11
+ date: 2018-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel