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 +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
|