business_flow 0.6.0 → 0.7.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
  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