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 +4 -4
- data/Gemfile.lock +6 -6
- data/lib/business_flow/cacheable.rb +7 -2
- data/lib/business_flow/dsl.rb +78 -22
- data/lib/business_flow/not_nil_validator.rb +1 -2
- data/lib/business_flow/step.rb +20 -4
- 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: aa83401ab6f440779738905d43a9b7114ed03e68
|
4
|
+
data.tar.gz: 5af8dbff7edf19b183a8201f418ef3555aadd54a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
12
|
-
activesupport (= 5.1.
|
13
|
-
activesupport (5.1.
|
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 (
|
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.
|
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
|
-
|
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 ||=
|
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
|
|
data/lib/business_flow/dsl.rb
CHANGED
@@ -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
|
-
@
|
21
|
-
@
|
22
|
-
|
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
|
-
|
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
|
-
|
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)
|
126
|
+
def self.create_parameter_field(klass, field, fallback = nil)
|
127
|
+
klass.send(:define_method, field, ¶meter_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
|
-
|
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
|
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
|
152
|
-
|
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
|
-
|
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
|
data/lib/business_flow/step.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
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.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-
|
11
|
+
date: 2018-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|