business_flow 0.14.3 → 0.15.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: 8d092e9b5f888ba0d28ec1c9cbdfce342b0ba04f
4
- data.tar.gz: 8a646af9cb30e6508bbec4071b0cd68a2a4658a8
3
+ metadata.gz: 25f54a9a721877633ad76d93f973d3baf0e84d0d
4
+ data.tar.gz: 7c4e8e3b4f997cf91d1d663194930ccf3807e34a
5
5
  SHA512:
6
- metadata.gz: cd0382fa43d0a787c73619acf21c9dfe3053f97647a2604a3206150eff8a6fa02f4552ac17df9327e49c3c2fcc03bf14f2e4a8cef3488f10f54720c7384152e2
7
- data.tar.gz: 1a4701642f270245dff8f06241b9973dd775819810ce152cc3f8f0f777384cb6283d4db5b8f74c9e1d8cacad9c607ed74026ff7dc62fb45a09d2dff1d775a37a
6
+ metadata.gz: 398c6d9402cb210f13a5bce50646d00c1cbbda22c18571526066483521f559a7002c87bdc0210f6f74a72cf6a5ff2f79d54cb4fe61155a3e3d54ded720e02ae3
7
+ data.tar.gz: dd18129f71f57d0f5a7032c45643a81a5ffe439a5bdaa500e5143a8c52630921bd049d5994ce0292d7f0f353c1e63821ab3472341f83543555d864517467a84e
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- business_flow (0.14.3)
4
+ business_flow (0.15.0)
5
5
  activemodel (>= 3.0)
6
6
  activesupport (>= 3.0)
7
7
 
@@ -54,10 +54,15 @@ module BusinessFlow
54
54
  if key
55
55
  @cache_key = Callable.new(key)
56
56
  else
57
- @cache_key ||= Callable.new(:parameter_object)
57
+ @cache_key ||= default_cache_key
58
58
  end
59
59
  end
60
60
 
61
+ # :reek:UtilityFunction
62
+ def default_cache_key
63
+ Callable.new(:_business_flow_dsl_parameters)
64
+ end
65
+
61
66
  def execute(flow)
62
67
  with_cache(flow) do
63
68
  super(flow)._business_flow_cacheable_finalize(flow.cache_key)
@@ -4,6 +4,32 @@ module BusinessFlow
4
4
  module DSL
5
5
  # Contains the DSL for BusinessFlow
6
6
  module ClassMethods
7
+ # Holds metadata about inputs to a flow
8
+ class Inputs
9
+ attr_reader :all, :needs, :optionals
10
+
11
+ def initialize(klass)
12
+ @needs = FieldList.new([], ParameterField, klass)
13
+ @optionals = FieldList.new([], ParameterField, klass)
14
+ @all = []
15
+ end
16
+
17
+ def add_needs(fields)
18
+ @all += fields
19
+ @needs.add_fields(fields)
20
+ end
21
+
22
+ def add_optional(fields)
23
+ @all += fields
24
+ @optionals.add_fields(fields)
25
+ end
26
+
27
+ def add_wants(field)
28
+ @all << field.name
29
+ @optionals.add_field(field)
30
+ end
31
+ end
32
+
7
33
  # Requires that a field be retrievable from the initialization parameters
8
34
  #
9
35
  # This will only require that the field is not nil. The field may still
@@ -11,22 +37,24 @@ module BusinessFlow
11
37
  #
12
38
  # @param fields The fields required from the initialization parameters
13
39
  def needs(*fields)
14
- @needs ||= FieldList.new([], ParameterField, self)
15
- @needs.add_fields(fields)
40
+ inputs.add_needs(fields)
16
41
  end
17
42
 
18
43
  # Allows a field to be retieved form the initialization paramters,
19
44
  # but does not require it to be non-nil
20
45
  def optional(*fields)
21
- @optionals ||= FieldList.new([], ParameterField, self)
22
- @optionals.add_fields(fields)
46
+ inputs.add_optional(fields)
23
47
  end
24
48
 
25
49
  # Allows a field to be retrieved from the initialiaztion parameters
26
50
  def wants(field, default = proc { nil }, opts = {})
27
51
  internal_name = "wants_#{field}".to_sym
28
52
  uses(internal_name, default, opts)
29
- ParameterField.new(field, internal_name).add_to(self)
53
+ inputs.add_wants(ParameterField.new(field, internal_name))
54
+ end
55
+
56
+ def inputs
57
+ @inputs ||= Inputs.new(self)
30
58
  end
31
59
 
32
60
  # Declares that you will expose a field to the outside world.
@@ -38,8 +66,11 @@ module BusinessFlow
38
66
  @provides.add_fields(fields)
39
67
  end
40
68
 
41
- def uses(field, klass, opts = {})
42
- step = Step.new(Callable.new(klass), opts)
69
+ # :reek:ControlParameter It's just nicer to have this fall back to a
70
+ # block than have a different DSL method when using/not using blocks
71
+ def uses(field, klass = nil, opts = {}, &blk)
72
+ step = Step.new(Callable.new(klass || blk),
73
+ { default_output: field }.merge(opts))
43
74
  retriever = proc { step.call(self).output }
44
75
  UsesField.new(field, retriever).add_to(self)
45
76
  end
@@ -68,8 +99,7 @@ module BusinessFlow
68
99
  finalize_initializer
69
100
  allocate.tap do |flow|
70
101
  catch(:halt_step) do
71
- flow.send(:_business_flow_dsl_initialize,
72
- ParameterObject.new(parameter_object))
102
+ flow.send(:_business_flow_dsl_initialize, parameter_object)
73
103
  end
74
104
  end
75
105
  end
@@ -203,6 +233,33 @@ module BusinessFlow
203
233
  initialize
204
234
  end
205
235
 
236
+ # :reek:NilCheck
237
+ private def _business_flow_parameter_fetch(key)
238
+ value = _business_flow_parameter_inner_fetch(key)
239
+ # We only want to fall back to a default if we were
240
+ # given nil. Other falsy vlues should be directly used.
241
+ return yield if value.nil? && block_given?
242
+ value
243
+ end
244
+
245
+ private def _business_flow_parameter_inner_fetch(key)
246
+ if @parameter_object.is_a?(Hash) && @parameter_object.key?(key)
247
+ @parameter_object[key]
248
+ else
249
+ @parameter_object.public_send(key)
250
+ end
251
+ rescue NoMethodError
252
+ nil
253
+ end
254
+
255
+ private def _business_flow_dsl_parameters
256
+ @_business_flow_dsl_parameters ||= Hash[
257
+ self.class.inputs.all.map do |input|
258
+ [input, _business_flow_parameter_inner_fetch(input)]
259
+ end
260
+ ]
261
+ end
262
+
206
263
  def errors
207
264
  @errors ||= ActiveModel::Errors.new(self)
208
265
  end
@@ -239,13 +296,12 @@ module BusinessFlow
239
296
  fields.each do |field|
240
297
  add_field(@field_klass.new(field))
241
298
  end
242
- @field_list.concat(fields)
299
+ @field_list
243
300
  end
244
301
 
245
- private
246
-
247
302
  def add_field(field)
248
303
  @klasses.each { |klass| field.add_to(klass) }
304
+ @field_list.push(field.name)
249
305
  end
250
306
  end
251
307
 
@@ -264,6 +320,10 @@ module BusinessFlow
264
320
  Field.eval_method(klass, setter_name, setter)
265
321
  end
266
322
 
323
+ def name
324
+ field
325
+ end
326
+
267
327
  def self.eval_method(klass, name, str)
268
328
  return if klass.method_defined?(name) ||
269
329
  klass.private_method_defined?(name)
@@ -307,6 +367,10 @@ module BusinessFlow
307
367
  @field.add_to(klass)
308
368
  klass.send(:public, @name)
309
369
  end
370
+
371
+ def name
372
+ @field.name
373
+ end
310
374
  end
311
375
 
312
376
  # Helper class around memoized fields
@@ -326,6 +390,10 @@ module BusinessFlow
326
390
  )
327
391
  end
328
392
 
393
+ def name
394
+ field
395
+ end
396
+
329
397
  private
330
398
 
331
399
  attr_reader :retriever, :setter_factory
@@ -353,6 +421,8 @@ module BusinessFlow
353
421
  # Responsible for declaring fields which will be memoized and validated
354
422
  # when first set
355
423
  class UsesField
424
+ attr_reader :name
425
+
356
426
  def initialize(field, retriever)
357
427
  @name = field
358
428
  @retriever = retriever
@@ -364,6 +434,7 @@ module BusinessFlow
364
434
  klass.send(:define_method, retriever_method_name, &@retriever)
365
435
  klass.send(:private, retriever_method_name)
366
436
  @field.add_to(klass)
437
+ klass.send(:public, @name)
367
438
  end
368
439
 
369
440
  private
@@ -377,7 +448,7 @@ module BusinessFlow
377
448
  # Helper class around input parameter fields
378
449
  class ParameterField
379
450
  def initialize(field, fallback = nil)
380
- retriever = "@parameter_object.fetch(:#{field})"
451
+ retriever = "_business_flow_parameter_fetch(:#{field})"
381
452
  retriever += " { send(:#{fallback}) }" if fallback
382
453
  @field = MemoizedField.new(field, retriever, method(:setter_factory))
383
454
  end
@@ -387,42 +458,14 @@ module BusinessFlow
387
458
  klass.send(:public, @field.field)
388
459
  end
389
460
 
390
- private
391
-
392
- def setter_factory(_field, ivar_name)
393
- "#{ivar_name} = new_value"
394
- end
395
- end
396
-
397
- # Manage logic around input parameters
398
- class ParameterObject
399
- def initialize(parameters)
400
- @parameters = parameters
401
- end
402
-
403
- # :reek:NilCheck
404
- def fetch(key)
405
- value = inner_fetch(key)
406
- # We only want to fall back to a default if we were
407
- # given nil. Other falsy vlues should be directly used.
408
- return yield if value.nil? && block_given?
409
- value
410
- end
411
-
412
- def to_s
413
- @parameters.to_s
461
+ def name
462
+ @field.name
414
463
  end
415
464
 
416
465
  private
417
466
 
418
- def inner_fetch(key)
419
- if @parameters.is_a?(Hash) && @parameters.key?(key)
420
- @parameters[key]
421
- else
422
- @parameters.public_send(key)
423
- end
424
- rescue NoMethodError
425
- nil
467
+ def setter_factory(_field, ivar_name)
468
+ "#{ivar_name} = new_value"
426
469
  end
427
470
  end
428
471
  end
@@ -35,11 +35,12 @@ 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(parameter_source, result, output_map, output_callable)
39
- @parameter_source = parameter_source
38
+ attr_reader :output
39
+
40
+ def initialize(result, output_map, output)
40
41
  @result = result
42
+ @output = output
41
43
  @output_map = output_map
42
- @output_callable = output_callable
43
44
  end
44
45
 
45
46
  def merge_into(object)
@@ -69,10 +70,6 @@ module BusinessFlow
69
70
  end
70
71
  end
71
72
 
72
- def output
73
- @parameter_source.instance_exec(@result, &@output_callable)
74
- end
75
-
76
73
  def self.process_output(object, output, output_setter)
77
74
  case output_setter
78
75
  when Symbol
@@ -120,10 +117,31 @@ module BusinessFlow
120
117
  def merge_into(_object); end
121
118
  end
122
119
 
120
+ CONDITION_FAILED = ConditionFailedResult.new.freeze
121
+
123
122
  # Manage creating results for our step
124
- ResultFactory = Struct.new(:outputs, :output_callable) do
123
+ ResultFactory = Struct.new(:outputs, :output_callable, :default_output) do
125
124
  def result(step_result, parameter_source)
126
- Result.new(parameter_source, step_result, outputs, output_callable)
125
+ callable = callable_for(step_result)
126
+
127
+ output = if callable
128
+ parameter_source.instance_exec(step_result, &callable)
129
+ else
130
+ step_result
131
+ end
132
+
133
+ Result.new(step_result, outputs, output)
134
+ end
135
+
136
+ private
137
+
138
+ # :reek:ManualDispatch This is faster.
139
+ def callable_for(step_result)
140
+ if output_callable
141
+ output_callable
142
+ elsif default_output && step_result.respond_to?(default_output)
143
+ default_output
144
+ end
127
145
  end
128
146
  end
129
147
 
@@ -152,12 +170,14 @@ module BusinessFlow
152
170
  # Responsible for creating objects based on our input options
153
171
  Options = Struct.new(:opts) do
154
172
  def input_object
155
- Inputs.new(opts[:inputs] || {})
173
+ inputs = opts[:inputs]
174
+ inputs.present? ? Inputs.new(inputs) : nil
156
175
  end
157
176
 
158
177
  def result_factory
159
178
  ResultFactory.new(opts[:outputs] || {},
160
- opts[:output] || ->(result) { result })
179
+ opts[:output],
180
+ opts[:default_output])
161
181
  end
162
182
 
163
183
  def condition
@@ -167,7 +187,7 @@ module BusinessFlow
167
187
  if if_stmts.present? || unless_stmts.present?
168
188
  ConditionList.new(if_stmts, unless_stmts)
169
189
  end
170
- end || proc { true }
190
+ end
171
191
  end
172
192
  end
173
193
 
@@ -177,15 +197,17 @@ module BusinessFlow
177
197
  @input_object = opts.input_object
178
198
  @result_factory = opts.result_factory
179
199
  @condition = opts.condition
200
+
201
+ update_call_method
180
202
  end
181
203
 
182
204
  def call(parameter_source)
183
205
  parameters = @input_object.parameters_from_source(parameter_source)
184
- if @condition.call(parameter_source, parameters)
206
+ if !@condition || @condition.call(parameter_source, parameters)
185
207
  @result_factory.result(@callable.call(parameter_source, parameters),
186
208
  parameter_source)
187
209
  else
188
- ConditionFailedResult.new
210
+ CONDITION_FAILED
189
211
  end
190
212
  end
191
213
 
@@ -200,5 +222,33 @@ module BusinessFlow
200
222
  def to_s
201
223
  @callable.to_s
202
224
  end
225
+
226
+ private
227
+
228
+ PARAMETERS_NO_INPUT = 'parameter_source'.freeze
229
+ PARAMETERS_WITH_INPUT =
230
+ '@input_object.parameters_from_source(parameter_source)'.freeze
231
+ WITHOUT_CONDITION = %(
232
+ @result_factory.result(@callable.call(parameter_source, parameters),
233
+ parameter_source)
234
+ ).freeze
235
+ WITH_CONDITION = %(
236
+ if @condition.call(parameter_source, parameters)
237
+ #{WITHOUT_CONDITION}
238
+ else
239
+ CONDITION_FAILED
240
+ end
241
+ ).freeze
242
+
243
+ def update_call_method
244
+ params = @input_object ? PARAMETERS_WITH_INPUT : PARAMETERS_NO_INPUT
245
+ code = %(
246
+ def call(parameter_source)
247
+ parameters = #{params}
248
+ #{@condition ? WITH_CONDITION : WITHOUT_CONDITION}
249
+ end
250
+ )
251
+ instance_eval code
252
+ end
203
253
  end
204
254
  end
@@ -1,3 +1,3 @@
1
1
  module BusinessFlow
2
- VERSION = '0.14.3'.freeze
2
+ VERSION = '0.15.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.14.3
4
+ version: 0.15.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: 2019-03-17 00:00:00.000000000 Z
11
+ date: 2019-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel