business_flow 0.14.3 → 0.15.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: 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