laminar 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/CHANGELOG.md +24 -2
- data/README.md +42 -7
- data/lib/laminar/callbacks.rb +1 -1
- data/lib/laminar/context.rb +6 -2
- data/lib/laminar/flow.rb +41 -10
- data/lib/laminar/flow/branch.rb +2 -4
- data/lib/laminar/flow/specification.rb +8 -0
- data/lib/laminar/flow/step.rb +1 -3
- data/lib/laminar/particle.rb +2 -1
- data/lib/laminar/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 697f7db8e9717c6795e2e95abfcc3601c50994e90fba9cc5275b27b0a8995b92
|
4
|
+
data.tar.gz: fc4d199a808988c03b9ce8b8b12fa6cd9b6c4886a0f605b0dc53072970813c3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fbe3e9b76547ef3e9cbb62ff3a05c552aba735fa280bcbc24c5d3bcffd2bb7f9c9d799e84489e7ffb035ca84697f78216f9e67924e6536d1d8bd463290537a1
|
7
|
+
data.tar.gz: 26523b261d6927e59f629abe85f11a788435b5c304d60eacbedd148882e52f9432d2fb0255d3a52dbcffc544e7e2fab4483e70228c97f5d0c8b1019a4ff96255
|
data/.rubocop.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,30 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [v0.5.0](https://github.com/rmlockerd/laminar/tree/v0.5.0) (2018-11-12)
|
4
|
+
[Full Changelog](https://github.com/rmlockerd/laminar/compare/v0.4.0...v0.5.0)
|
4
5
|
|
5
|
-
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- Add 'soft' halt capability for flows [\#11](https://github.com/rmlockerd/laminar/issues/11)
|
9
|
+
- Check starting context for a flow [\#9](https://github.com/rmlockerd/laminar/issues/9)
|
10
|
+
|
11
|
+
**Fixed bugs:**
|
12
|
+
|
13
|
+
- Branch conditionals \(if:/unless:\) do not get context passed [\#10](https://github.com/rmlockerd/laminar/issues/10)
|
14
|
+
|
15
|
+
## [v0.4.0](https://github.com/rmlockerd/laminar/tree/v0.4.0) (2018-10-12)
|
16
|
+
[Full Changelog](https://github.com/rmlockerd/laminar/compare/v0.3.0...v0.4.0)
|
17
|
+
|
18
|
+
**Implemented enhancements:**
|
19
|
+
|
20
|
+
- Shorthand for branching to end [\#8](https://github.com/rmlockerd/laminar/issues/8)
|
21
|
+
|
22
|
+
**Fixed bugs:**
|
23
|
+
|
24
|
+
- branch if/unless don't accept blocks/Procs [\#7](https://github.com/rmlockerd/laminar/issues/7)
|
25
|
+
|
26
|
+
## [v0.3.0](https://github.com/rmlockerd/laminar/tree/v0.3.0) (2018-10-01)
|
27
|
+
[Full Changelog](https://github.com/rmlockerd/laminar/compare/v0.2.0...v0.3.0)
|
6
28
|
|
7
29
|
**Implemented enhancements:**
|
8
30
|
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# Laminar
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/laminar.svg)](https://badge.fury.io/rb/laminar)
|
3
4
|
[![Build Status](https://travis-ci.org/rmlockerd/laminar.svg?branch=master)](https://travis-ci.org/rmlockerd/laminar)
|
4
|
-
[![Maintainability](https://
|
5
|
-
[![Test Coverage](https://
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/6b2d761ca042af6461e3/maintainability)](https://codeclimate.com/github/rmlockerd/laminar/maintainability)
|
6
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/6b2d761ca042af6461e3/test_coverage)](https://codeclimate.com/github/rmlockerd/laminar/test_coverage)
|
6
7
|
|
7
8
|
A simple Chain-of-Responsibility/Interactor gem that helps MVC applications organise their business logic, keeping their models and controllers skinny and their logic easily testable. Individual chunks of business logic (called particles) can be easily composed into more complex chains called flows.
|
8
9
|
|
@@ -237,6 +238,31 @@ class CheckEquipment
|
|
237
238
|
end
|
238
239
|
```
|
239
240
|
|
241
|
+
#### Flow Parameters
|
242
|
+
Flows do not get the benefit of keyword argument checking like ordinary
|
243
|
+
Particles, since their #call method is implemented by the Flow mixin.
|
244
|
+
You can, however, specify a list of required context keys in the flow
|
245
|
+
definition itself:
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
flow do
|
249
|
+
context_must_have :product_sku, :unit_price
|
250
|
+
...
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
The context is simply checked for the presence of the specified
|
255
|
+
list of keys. If you need to do more complicated validation of
|
256
|
+
context, use a ``#before`` callback that halts the flow if
|
257
|
+
validation fails.
|
258
|
+
|
259
|
+
Context validation happens just prior to execution of the
|
260
|
+
first step in the flow (and any ``#before_each``) callbacks, but after
|
261
|
+
execution of the Flow's own ``#before`` callbacks. Since you can
|
262
|
+
manipulate context in a callback, this is useful to set up
|
263
|
+
context required by a flow's particles that you don't necessarily
|
264
|
+
expect your caller to provide.
|
265
|
+
|
240
266
|
#### Flow Branching
|
241
267
|
Ordinarily particle execution is sequential in the order specified.
|
242
268
|
However, you can optionally branch to a different label with `branch`.
|
@@ -250,7 +276,7 @@ However, you can optionally branch to a different label with `branch`.
|
|
250
276
|
end
|
251
277
|
```
|
252
278
|
|
253
|
-
You can use the
|
279
|
+
You can use the endflow directive to terminate the flow gracefully
|
254
280
|
(skipping all remaining steps).
|
255
281
|
|
256
282
|
```ruby
|
@@ -309,6 +335,11 @@ no condition is satisfied, execution drops to the next step.
|
|
309
335
|
step :last_step
|
310
336
|
end
|
311
337
|
```
|
338
|
+
#### Halting a Flow
|
339
|
+
If a particle calls ``#halt!`` or ``#fail!`` on its context, execution
|
340
|
+
of any surrounding Flow (or nested flows) stops immediately via a
|
341
|
+
``ParticleStopped`` error. To gracefully signal that an enclosing
|
342
|
+
flow should stop without raising an error, use ``#halt`` instead.
|
312
343
|
|
313
344
|
#### Flow Callbacks
|
314
345
|
|
@@ -318,11 +349,15 @@ A flow can specify callback(s) to run before/after every step:
|
|
318
349
|
class MyFlow
|
319
350
|
include Laminar::Flow
|
320
351
|
|
321
|
-
|
322
|
-
|
352
|
+
flow do
|
353
|
+
before_each :thing, :thing2 # method
|
354
|
+
before_each { ... } # block
|
323
355
|
|
324
|
-
|
325
|
-
|
356
|
+
after_each :thing, :thing2 # method
|
357
|
+
after_each { ... } # block
|
358
|
+
|
359
|
+
# steps ...
|
360
|
+
end
|
326
361
|
```
|
327
362
|
|
328
363
|
The order of execution for callbacks in a flow looks like:
|
data/lib/laminar/callbacks.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Laminar
|
4
|
+
# Callback hooks for particles
|
4
5
|
module Callbacks
|
5
6
|
def self.included(klass)
|
6
7
|
klass.class_eval do
|
@@ -34,7 +35,6 @@ module Laminar
|
|
34
35
|
|
35
36
|
# Additional instance methods
|
36
37
|
module InstanceMethods
|
37
|
-
|
38
38
|
private
|
39
39
|
|
40
40
|
def run_before_callbacks
|
data/lib/laminar/context.rb
CHANGED
@@ -31,10 +31,14 @@ module Laminar
|
|
31
31
|
@halted
|
32
32
|
end
|
33
33
|
|
34
|
-
def halt
|
34
|
+
def halt(context = {})
|
35
35
|
@halted = true
|
36
36
|
merge!(context)
|
37
|
-
|
37
|
+
end
|
38
|
+
|
39
|
+
def halt!(context = {})
|
40
|
+
halt(context)
|
41
|
+
raise ParticleStopped, self
|
38
42
|
end
|
39
43
|
|
40
44
|
def fail!(context = {})
|
data/lib/laminar/flow.rb
CHANGED
@@ -103,14 +103,11 @@ module Laminar
|
|
103
103
|
end
|
104
104
|
|
105
105
|
# Initiates evaluation of the flow.
|
106
|
-
# @param object the context/input on which the flow will operate.
|
107
|
-
# is usually a Hash but in simple cases can be a single object. The
|
108
|
-
# implementing flow class should provide a #context_valid? method
|
109
|
-
# that returns true is the given context contains the minimum required
|
110
|
-
# information.
|
106
|
+
# @param object the context/input on which the flow will operate.
|
111
107
|
def call(*)
|
112
108
|
return context if flowspec.nil?
|
113
109
|
|
110
|
+
validate_required_context
|
114
111
|
step = flowspec.steps[flowspec.first_step]
|
115
112
|
loop do
|
116
113
|
break unless invoke_step(step)
|
@@ -124,14 +121,35 @@ module Laminar
|
|
124
121
|
|
125
122
|
def invoke_step(step)
|
126
123
|
return if step.nil?
|
127
|
-
|
128
|
-
|
129
|
-
step
|
130
|
-
|
131
|
-
run_callbacks(flowspec.after_each_callbacks)
|
124
|
+
|
125
|
+
pre_step_callbacks(step)
|
126
|
+
run_particle(step)
|
127
|
+
post_step_callbacks(step)
|
132
128
|
!context.halted?
|
133
129
|
end
|
134
130
|
|
131
|
+
def post_step_callbacks(step)
|
132
|
+
guarded_callback(step.after_callbacks)
|
133
|
+
guarded_callback(flowspec.after_each_callbacks)
|
134
|
+
end
|
135
|
+
|
136
|
+
def pre_step_callbacks(step)
|
137
|
+
guarded_callback(flowspec.before_each_callbacks)
|
138
|
+
guarded_callback(step.before_callbacks)
|
139
|
+
end
|
140
|
+
|
141
|
+
def run_particle(step)
|
142
|
+
return if context.halted?
|
143
|
+
|
144
|
+
step.particle.call!(context)
|
145
|
+
end
|
146
|
+
|
147
|
+
def guarded_callback(list)
|
148
|
+
return if context.halted?
|
149
|
+
|
150
|
+
run_callbacks(list)
|
151
|
+
end
|
152
|
+
|
135
153
|
# Given a step, returns the next step that satisfies the
|
136
154
|
# execution/branch conditions.
|
137
155
|
def next_step(current)
|
@@ -143,6 +161,19 @@ module Laminar
|
|
143
161
|
|
144
162
|
flowspec.steps[next_name]
|
145
163
|
end
|
164
|
+
|
165
|
+
def validate_required_context
|
166
|
+
missing = []
|
167
|
+
flowspec.flow_params.each do |param|
|
168
|
+
next if context.key?(param)
|
169
|
+
|
170
|
+
missing << param
|
171
|
+
end
|
172
|
+
|
173
|
+
return if missing.empty?
|
174
|
+
|
175
|
+
raise ArgumentError, "missing context: #{missing.join(', ')}"
|
176
|
+
end
|
146
177
|
end
|
147
178
|
end
|
148
179
|
end
|
data/lib/laminar/flow/branch.rb
CHANGED
@@ -18,9 +18,7 @@ module Laminar
|
|
18
18
|
attr_accessor :name, :condition, :condition_type
|
19
19
|
|
20
20
|
def initialize(name, options = {})
|
21
|
-
unless name.class.method_defined?(:to_sym)
|
22
|
-
raise ArgumentError, 'invalid name'
|
23
|
-
end
|
21
|
+
raise ArgumentError, 'invalid name' unless name.class.method_defined?(:to_sym)
|
24
22
|
|
25
23
|
validate_options(options)
|
26
24
|
@name = name.to_sym
|
@@ -41,7 +39,7 @@ module Laminar
|
|
41
39
|
|
42
40
|
def run_condition(target)
|
43
41
|
if @condition.is_a?(Proc)
|
44
|
-
@condition.call()
|
42
|
+
@condition.call(target.context)
|
45
43
|
else
|
46
44
|
target.send(@condition)
|
47
45
|
end
|
@@ -23,6 +23,14 @@ module Laminar
|
|
23
23
|
@prev_step = step
|
24
24
|
end
|
25
25
|
|
26
|
+
def context_must_have(*params)
|
27
|
+
flow_params.concat(params.flatten)
|
28
|
+
end
|
29
|
+
|
30
|
+
def flow_params
|
31
|
+
@flow_params ||= []
|
32
|
+
end
|
33
|
+
|
26
34
|
def before_each(*args, &block)
|
27
35
|
before_each_callbacks.concat(args)
|
28
36
|
before_each_callbacks << block if block
|
data/lib/laminar/flow/step.rb
CHANGED
@@ -14,9 +14,7 @@ module Laminar
|
|
14
14
|
valid_options %i[class].freeze
|
15
15
|
|
16
16
|
def initialize(name, options = {}, &block)
|
17
|
-
unless name.class.method_defined?(:to_sym)
|
18
|
-
raise ArgumentError, 'invalid name'
|
19
|
-
end
|
17
|
+
raise ArgumentError, 'invalid name' unless name.class.method_defined?(:to_sym)
|
20
18
|
|
21
19
|
validate_options(options)
|
22
20
|
@class_name = (options[:class] || name).to_s.camelize
|
data/lib/laminar/particle.rb
CHANGED
@@ -26,6 +26,7 @@ module Laminar
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
# Laminar::Particle instance methods.
|
29
30
|
module InstanceMethods
|
30
31
|
def initialize(context = {})
|
31
32
|
@context = Context.build(context)
|
@@ -43,7 +44,7 @@ module Laminar
|
|
43
44
|
|
44
45
|
param_list = context_slice
|
45
46
|
param_list.empty? ? call : call(context_slice)
|
46
|
-
run_after_callbacks
|
47
|
+
run_after_callbacks unless context.halted?
|
47
48
|
context
|
48
49
|
end
|
49
50
|
|
data/lib/laminar/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: laminar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Lockerd
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
92
92
|
- ".gitignore"
|
93
93
|
- ".rspec"
|
94
|
+
- ".rubocop.yml"
|
94
95
|
- ".travis.yml"
|
95
96
|
- CHANGELOG.md
|
96
97
|
- CODE_OF_CONDUCT.md
|