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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/business_flow/cacheable.rb +6 -1
- data/lib/business_flow/dsl.rb +88 -45
- data/lib/business_flow/step.rb +64 -14
- data/lib/business_flow/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25f54a9a721877633ad76d93f973d3baf0e84d0d
|
4
|
+
data.tar.gz: 7c4e8e3b4f997cf91d1d663194930ccf3807e34a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 398c6d9402cb210f13a5bce50646d00c1cbbda22c18571526066483521f559a7002c87bdc0210f6f74a72cf6a5ff2f79d54cb4fe61155a3e3d54ded720e02ae3
|
7
|
+
data.tar.gz: dd18129f71f57d0f5a7032c45643a81a5ffe439a5bdaa500e5143a8c52630921bd049d5994ce0292d7f0f353c1e63821ab3472341f83543555d864517467a84e
|
data/Gemfile.lock
CHANGED
@@ -54,10 +54,15 @@ module BusinessFlow
|
|
54
54
|
if key
|
55
55
|
@cache_key = Callable.new(key)
|
56
56
|
else
|
57
|
-
@cache_key ||=
|
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)
|
data/lib/business_flow/dsl.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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)
|
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
|
-
|
42
|
-
|
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
|
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 = "
|
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
|
-
|
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
|
419
|
-
|
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
|
data/lib/business_flow/step.rb
CHANGED
@@ -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
|
-
|
39
|
-
|
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
|
-
|
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
|
-
|
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]
|
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
|
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
|
-
|
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
|
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.
|
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-
|
11
|
+
date: 2019-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|