call_sheet 0.1.0 → 0.2.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 +4 -0
- data/README.md +46 -20
- data/lib/call_sheet.rb +46 -1
- data/lib/call_sheet/dsl.rb +10 -3
- data/lib/call_sheet/step.rb +13 -2
- data/lib/call_sheet/step_adapters.rb +6 -0
- data/lib/call_sheet/step_adapters/base.rb +1 -0
- data/lib/call_sheet/step_adapters/map.rb +1 -0
- data/lib/call_sheet/step_adapters/raw.rb +2 -0
- data/lib/call_sheet/step_adapters/tee.rb +1 -0
- data/lib/call_sheet/step_adapters/try.rb +9 -1
- data/lib/call_sheet/transaction.rb +18 -0
- data/lib/call_sheet/version.rb +2 -1
- data/spec/examples.txt +19 -15
- data/spec/integration/call_sheet_spec.rb +27 -1
- data/spec/integration/passing_step_arguments_spec.rb +1 -1
- data/spec/integration/publishing_step_events_spec.rb +67 -0
- metadata +32 -3
- data/spec/integration/inline_procs_spec.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96d48219b6b93de88c46f9bdd0e146a1d7874fc7
|
4
|
+
data.tar.gz: d8f8982248cab8b53578356484b0a933aaab5a92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 460c3efef5e4b95ab44bb4a6b105c022e60fd57b0830f8c6ecbbdbaca20b29698a51e2389104823381785dba3893af85f57f29b0621d622690cab0d3d694ca93
|
7
|
+
data.tar.gz: ab53c4c441ab6c0b58baaff91b8a8ba188bf190d5b12355922f51719d86ec9c565660df927f5b34f2011da3972dfd4c12a1bcb37ed43ee193e8d965415fdba52
|
data/Gemfile.lock
CHANGED
@@ -3,6 +3,7 @@ PATH
|
|
3
3
|
specs:
|
4
4
|
call_sheet (0.1.0)
|
5
5
|
deterministic (>= 0.15.3)
|
6
|
+
wisper (>= 1.6.0)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
@@ -44,6 +45,8 @@ GEM
|
|
44
45
|
json (~> 1.8)
|
45
46
|
simplecov-html (~> 0.10.0)
|
46
47
|
simplecov-html (0.10.0)
|
48
|
+
wisper (1.6.1)
|
49
|
+
yard (0.8.7.6)
|
47
50
|
|
48
51
|
PLATFORMS
|
49
52
|
ruby
|
@@ -55,6 +58,7 @@ DEPENDENCIES
|
|
55
58
|
rspec (~> 3.3.0)
|
56
59
|
rubocop (~> 0.34.2)
|
57
60
|
simplecov (~> 0.10.0)
|
61
|
+
yard
|
58
62
|
|
59
63
|
BUNDLED WITH
|
60
64
|
1.10.6
|
data/README.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
|
+
[gem]: https://rubygems.org/gems/call_sheet
|
2
|
+
[code_climate]: https://codeclimate.com/github/icelab/call_sheet
|
3
|
+
[inch]: http://inch-ci.org/github/icelab/call_sheet
|
4
|
+
|
1
5
|
# Call Sheet
|
2
6
|
|
7
|
+
[][gem]
|
8
|
+
[][code_climate]
|
9
|
+
[][inch]
|
10
|
+
|
3
11
|
Call Sheet is a business transaction DSL. It provides a simple way to define a complex business transaction that includes processing by many different objects. It makes error handling a primary concern by using a “[Railway Oriented Programming](http://fsharpforfunandprofit.com/rop/)” approach for capturing and returning errors from any step in the transaction.
|
4
12
|
|
5
13
|
Call Sheet is based on the following ideas, drawn mostly from [Transflow](http://github.com/solnic/transflow):
|
@@ -28,20 +36,20 @@ Each operation is integrated into your business transaction through one of the f
|
|
28
36
|
* `map` – any output is considered successful and returned as `Success(output)`
|
29
37
|
* `try` – the operation may raise an exception in an error case. This is caught and returned as `Failure(exception)`. The output is otherwise returned as `Success(output)`.
|
30
38
|
* `tee` – the operation interacts with some external system and has no meaningful output. The original input is passed through and returned as `Success(input)`.
|
31
|
-
* `raw` – the operation already returns its own `Result` object, and needs no special handling.
|
39
|
+
* `raw` or `step` – the operation already returns its own `Result` object, and needs no special handling.
|
32
40
|
|
33
41
|
```ruby
|
34
42
|
DB = []
|
35
43
|
|
36
44
|
container = {
|
37
45
|
process: -> input { {name: input["name"], email: input["email"]} },
|
38
|
-
validate: -> input { input[:email].nil? ? raise("not valid") : input },
|
46
|
+
validate: -> input { input[:email].nil? ? raise(ValidationFailure, "not valid") : input },
|
39
47
|
persist: -> input { DB << input and true }
|
40
48
|
}
|
41
49
|
|
42
50
|
save_user = CallSheet(container: container) do
|
43
51
|
map :process
|
44
|
-
try :validate
|
52
|
+
try :validate, catch: ValidationFailure
|
45
53
|
tee :persist
|
46
54
|
end
|
47
55
|
|
@@ -73,24 +81,24 @@ You can use guard expressions like `where { f == :step_name }` in the failure ma
|
|
73
81
|
|
74
82
|
### Passing additional step arguments
|
75
83
|
|
76
|
-
Additional arguments for step operations can be passed at the time of calling your transaction. Provide these arguments as an array, and they’ll be [splatted](https://endofline.wordpress.com/2011/01/21/the-strange-ruby-splat/) into the front of the operation’s arguments. This
|
84
|
+
Additional arguments for step operations can be passed at the time of calling your transaction. Provide these arguments as an array, and they’ll be [splatted](https://endofline.wordpress.com/2011/01/21/the-strange-ruby-splat/) into the front of the operation’s arguments. This means that transactions can effectively support operations with any sort of `#call(*args, input)` interface.
|
77
85
|
|
78
86
|
```ruby
|
79
87
|
DB = []
|
80
88
|
|
81
89
|
container = {
|
82
90
|
process: -> input { {name: input["name"], email: input["email"]} },
|
83
|
-
validate: -> allowed, input { input[:email].include?(allowed) ? raise("not allowed") : input },
|
91
|
+
validate: -> allowed, input { input[:email].include?(allowed) ? raise(ValidationFailure, "not allowed") : input },
|
84
92
|
persist: -> input { DB << input and true }
|
85
93
|
}
|
86
94
|
|
87
95
|
save_user = CallSheet(container: container) do
|
88
96
|
map :process
|
89
|
-
try :validate
|
97
|
+
try :validate, catch: ValidationFailure
|
90
98
|
tee :persist
|
91
99
|
end
|
92
100
|
|
93
|
-
input = {name
|
101
|
+
input = {"name" => "Jane", "email" => "jane@doe.com"}
|
94
102
|
save_user.call(input, validate: ["doe.com"])
|
95
103
|
# => Success({:name=>"Jane", :email=>"jane@doe.com"})
|
96
104
|
|
@@ -98,28 +106,46 @@ save_user.call(input, validate: ["smith.com"])
|
|
98
106
|
# => Failure("not allowed")
|
99
107
|
```
|
100
108
|
|
101
|
-
###
|
109
|
+
### Subscribing to step notifications
|
102
110
|
|
103
|
-
|
111
|
+
As well as pattern matching on the final transaction result, you can subscribe to individual steps and trigger specific behaviour based on their success or failure:
|
104
112
|
|
105
113
|
```ruby
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
114
|
+
NOTIFICATIONS = []
|
115
|
+
|
116
|
+
module UserPersistListener
|
117
|
+
extend self
|
118
|
+
|
119
|
+
def persist_success(user)
|
120
|
+
NOTIFICATIONS << "#{user[:email]} persisted"
|
121
|
+
end
|
122
|
+
|
123
|
+
def persist_failure(user)
|
124
|
+
NOTIFICATIONS << "#{user[:email]} failed to persist"
|
125
|
+
end
|
110
126
|
end
|
127
|
+
|
128
|
+
|
129
|
+
input = {"name" => "Jane", "email" => "jane@doe.com"}
|
130
|
+
|
131
|
+
save_user.subscribe(persist: UserPersistListener)
|
132
|
+
save_user.call(input, validate: ["doe.com"])
|
133
|
+
|
134
|
+
NOTIFICATIONS
|
135
|
+
# => ["jane@doe.com persisted"]
|
111
136
|
```
|
112
137
|
|
113
|
-
|
138
|
+
This pub/sub mechanism is provided by the [Wisper](https://github.com/krisleech/wisper) gem. You can subscribe to specific steps using the `#subscribe(step_name: listener)` API, or subscribe to all steps via `#subscribe(listener)`.
|
139
|
+
|
140
|
+
### Working with a larger container
|
114
141
|
|
115
|
-
|
142
|
+
In practice, your container won’t be a trivial collection of generically named operations. You can keep your transaction step names simple by using the `with:` option to provide the identifiers for the operations within your container:
|
116
143
|
|
117
144
|
```ruby
|
118
|
-
|
119
|
-
map :
|
120
|
-
|
121
|
-
|
122
|
-
tee :persist
|
145
|
+
save_user = CallSheet(container: large_whole_app_container) do
|
146
|
+
map :process, with: "attributes.user"
|
147
|
+
try :validate, with: "validations.user", catch: ValidationFailure
|
148
|
+
tee :persist, with: "persistance.commands.update_user"
|
123
149
|
end
|
124
150
|
```
|
125
151
|
|
data/lib/call_sheet.rb
CHANGED
@@ -2,7 +2,52 @@ require "deterministic"
|
|
2
2
|
require "call_sheet/version"
|
3
3
|
require "call_sheet/dsl"
|
4
4
|
|
5
|
-
#
|
5
|
+
# Define a business transaction.
|
6
|
+
#
|
7
|
+
# A business transaction is a series of callable operation objects that
|
8
|
+
# receive input and produce an output.
|
9
|
+
#
|
10
|
+
# The operations should be addressable via `#[]` in a container object that
|
11
|
+
# you pass when creating the transaction. The operations must respond to
|
12
|
+
# `#call(*args, input)`.
|
13
|
+
#
|
14
|
+
# Each operation will be called in the order it was specified in your
|
15
|
+
# transaction, with its output is passed as the intput to the next operation.
|
16
|
+
# Operations will only be called if the previous step was a success.
|
17
|
+
#
|
18
|
+
# A step is successful when it returns a `Success` object (from the
|
19
|
+
# [Deterministic](deterministic) gem) wrapping its output value. A step is a
|
20
|
+
# failure when it returns a `Failure` object. If your operations already
|
21
|
+
# return a `Success` or `Failure`, they can be added to your operation as
|
22
|
+
# plain `step` or `raw` steps.
|
23
|
+
#
|
24
|
+
# If your operations don't already return `Success` or `Failure`, then they
|
25
|
+
# can be added to the transaction with the following steps:
|
26
|
+
#
|
27
|
+
# * `map` --- wrap the output of the operation in a `Success`
|
28
|
+
# * `try` --- wrap the output of the operation in a `Success`, unless a certain
|
29
|
+
# exception is raised, which will be caught and returned as a `Failure`.
|
30
|
+
# * `tee` --- ignore the output of the operation and pass through its original
|
31
|
+
# input as a `Sucess`.
|
32
|
+
#
|
33
|
+
# [deterministic]: https://github.com/pzol/deterministic
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# container = {do_first: some_obj, do_second: some_obj}
|
37
|
+
#
|
38
|
+
# my_transaction = CallSheet(container: container) do
|
39
|
+
# step :do_first
|
40
|
+
# step :do_second
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# my_transaction.call(some_input)
|
44
|
+
#
|
45
|
+
# @param [Hash] options the options hash
|
46
|
+
# @option options [#[]] :container the operations container
|
47
|
+
#
|
48
|
+
# @return [CallSheet::Transaction] the transaction object
|
49
|
+
#
|
50
|
+
# @api public
|
6
51
|
def CallSheet(options = {}, &block)
|
7
52
|
CallSheet::DSL.new(options, &block).call
|
8
53
|
end
|
data/lib/call_sheet/dsl.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require "call_sheet/step"
|
2
2
|
require "call_sheet/step_adapters"
|
3
3
|
require "call_sheet/step_adapters/base"
|
4
|
-
require "call_sheet/step_adapters/raw"
|
5
4
|
require "call_sheet/step_adapters/map"
|
5
|
+
require "call_sheet/step_adapters/raw"
|
6
6
|
require "call_sheet/step_adapters/tee"
|
7
7
|
require "call_sheet/step_adapters/try"
|
8
8
|
require "call_sheet/transaction"
|
@@ -11,10 +11,16 @@ module CallSheet
|
|
11
11
|
class DSL
|
12
12
|
include Deterministic::Prelude::Result
|
13
13
|
|
14
|
-
|
14
|
+
# @api private
|
15
|
+
attr_reader :options
|
16
|
+
|
17
|
+
# @api private
|
15
18
|
attr_reader :container
|
19
|
+
|
20
|
+
# @api private
|
16
21
|
attr_reader :steps
|
17
22
|
|
23
|
+
# @api private
|
18
24
|
def initialize(options, &block)
|
19
25
|
@options = options
|
20
26
|
@container = options.fetch(:container)
|
@@ -25,11 +31,12 @@ module CallSheet
|
|
25
31
|
|
26
32
|
StepAdapters.each do |adapter_name, adapter_class|
|
27
33
|
define_method adapter_name do |step_name, options = {}|
|
28
|
-
operation =
|
34
|
+
operation = container[options.fetch(:with, step_name)]
|
29
35
|
steps << Step.new(step_name, adapter_class.new(operation, options))
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
39
|
+
# @api private
|
33
40
|
def call
|
34
41
|
Transaction.new(steps)
|
35
42
|
end
|
data/lib/call_sheet/step.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
require "wisper"
|
1
2
|
require "call_sheet/step_failure"
|
2
3
|
|
3
4
|
module CallSheet
|
5
|
+
# @api private
|
4
6
|
class Step
|
5
7
|
include Deterministic::Prelude::Result
|
8
|
+
include Wisper::Publisher
|
6
9
|
|
7
10
|
attr_reader :step_name
|
8
11
|
attr_reader :operation
|
@@ -19,8 +22,16 @@ module CallSheet
|
|
19
22
|
end
|
20
23
|
|
21
24
|
def call(input)
|
22
|
-
|
23
|
-
result
|
25
|
+
args = (call_args << input)
|
26
|
+
result = operation.call(*args)
|
27
|
+
|
28
|
+
result.map { |value|
|
29
|
+
broadcast :"#{step_name}_success", value
|
30
|
+
Success(value)
|
31
|
+
}.map_err { |value|
|
32
|
+
broadcast :"#{step_name}_failure", *args, value
|
33
|
+
Failure(StepFailure.new(step_name, value))
|
34
|
+
}
|
24
35
|
end
|
25
36
|
|
26
37
|
def arity
|
@@ -11,6 +11,12 @@ module CallSheet
|
|
11
11
|
extend Forwardable
|
12
12
|
def_delegators :registry, :[], :each
|
13
13
|
|
14
|
+
# Register a step adapter.
|
15
|
+
#
|
16
|
+
# @param [Symbol] name the name to expose for adding steps to a transaction
|
17
|
+
# @param klass the step adapter class
|
18
|
+
#
|
19
|
+
# @api public
|
14
20
|
def register(name, klass)
|
15
21
|
registry[name.to_sym] = klass
|
16
22
|
end
|
@@ -1,8 +1,16 @@
|
|
1
1
|
module CallSheet
|
2
2
|
module StepAdapters
|
3
|
+
# @api private
|
3
4
|
class Try < Base
|
5
|
+
def initialize(*)
|
6
|
+
super
|
7
|
+
raise ArgumentError, "+try+ steps require one or more exception classes provided via +catch:+" unless options[:catch]
|
8
|
+
end
|
9
|
+
|
4
10
|
def call(*args, input)
|
5
|
-
|
11
|
+
Success(operation.call(*args, input))
|
12
|
+
rescue *Array(options[:catch]) => e
|
13
|
+
Failure(e)
|
6
14
|
end
|
7
15
|
end
|
8
16
|
|
@@ -2,13 +2,16 @@ module CallSheet
|
|
2
2
|
class Transaction
|
3
3
|
include Deterministic::Prelude::Result
|
4
4
|
|
5
|
+
# @api private
|
5
6
|
attr_reader :steps
|
6
7
|
private :steps
|
7
8
|
|
9
|
+
# @api private
|
8
10
|
def initialize(steps)
|
9
11
|
@steps = steps
|
10
12
|
end
|
11
13
|
|
14
|
+
# @api public
|
12
15
|
def call(input, options = {})
|
13
16
|
assert_valid_options(options)
|
14
17
|
assert_options_satisfy_step_arity(options)
|
@@ -18,6 +21,21 @@ module CallSheet
|
|
18
21
|
end
|
19
22
|
alias_method :[], :call
|
20
23
|
|
24
|
+
# @api public
|
25
|
+
def subscribe(listeners)
|
26
|
+
if listeners.is_a?(Hash)
|
27
|
+
listeners.each do |step_name, listener|
|
28
|
+
steps.detect { |step| step.step_name == step_name }.subscribe(listener)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
steps.each do |step|
|
32
|
+
step.subscribe(listeners)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
21
39
|
private
|
22
40
|
|
23
41
|
def assert_valid_options(options)
|
data/lib/call_sheet/version.rb
CHANGED
data/spec/examples.txt
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
example_id | status | run_time |
|
2
2
|
-------------------------------------------------------- | ------ | --------------- |
|
3
|
-
./spec/integration/call_sheet_spec.rb[1:1:1] | passed | 0.
|
4
|
-
./spec/integration/call_sheet_spec.rb[1:1:2] | passed | 0.
|
5
|
-
./spec/integration/call_sheet_spec.rb[1:1:3] | passed | 0.
|
6
|
-
./spec/integration/call_sheet_spec.rb[1:1:4] | passed | 0.
|
7
|
-
./spec/integration/call_sheet_spec.rb[1:2:1] | passed | 0.
|
8
|
-
./spec/integration/call_sheet_spec.rb[1:2:2] | passed | 0.
|
9
|
-
./spec/integration/call_sheet_spec.rb[1:2:3] | passed | 0.
|
10
|
-
./spec/integration/call_sheet_spec.rb[1:2:4] | passed | 0.
|
11
|
-
./spec/integration/call_sheet_spec.rb[1:2:5] | passed | 0.
|
12
|
-
./spec/integration/
|
13
|
-
./spec/integration/
|
14
|
-
./spec/integration/
|
15
|
-
./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.
|
16
|
-
./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.
|
17
|
-
./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.
|
3
|
+
./spec/integration/call_sheet_spec.rb[1:1:1] | passed | 0.00244 seconds |
|
4
|
+
./spec/integration/call_sheet_spec.rb[1:1:2] | passed | 0.00114 seconds |
|
5
|
+
./spec/integration/call_sheet_spec.rb[1:1:3] | passed | 0.00101 seconds |
|
6
|
+
./spec/integration/call_sheet_spec.rb[1:1:4] | passed | 0.00237 seconds |
|
7
|
+
./spec/integration/call_sheet_spec.rb[1:2:1] | passed | 0.0008 seconds |
|
8
|
+
./spec/integration/call_sheet_spec.rb[1:2:2] | passed | 0.00086 seconds |
|
9
|
+
./spec/integration/call_sheet_spec.rb[1:2:3] | passed | 0.00154 seconds |
|
10
|
+
./spec/integration/call_sheet_spec.rb[1:2:4] | passed | 0.00087 seconds |
|
11
|
+
./spec/integration/call_sheet_spec.rb[1:2:5] | passed | 0.00085 seconds |
|
12
|
+
./spec/integration/call_sheet_spec.rb[1:3:1] | passed | 0.00068 seconds |
|
13
|
+
./spec/integration/call_sheet_spec.rb[1:3:2] | passed | 0.00072 seconds |
|
14
|
+
./spec/integration/call_sheet_spec.rb[1:3:3] | passed | 0.00065 seconds |
|
15
|
+
./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.00312 seconds |
|
16
|
+
./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.00023 seconds |
|
17
|
+
./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.00202 seconds |
|
18
|
+
./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.00096 seconds |
|
19
|
+
./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.00077 seconds |
|
20
|
+
./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.00254 seconds |
|
21
|
+
./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.00083 seconds |
|
@@ -1,8 +1,11 @@
|
|
1
1
|
RSpec.describe CallSheet do
|
2
|
+
include Deterministic::Prelude::Result
|
3
|
+
|
2
4
|
let(:call_sheet) {
|
3
5
|
CallSheet(container: container) do
|
4
6
|
map :process
|
5
|
-
|
7
|
+
raw :verify
|
8
|
+
try :validate, catch: Test::NotValidError
|
6
9
|
tee :persist
|
7
10
|
end
|
8
11
|
}
|
@@ -10,6 +13,7 @@ RSpec.describe CallSheet do
|
|
10
13
|
let(:container) {
|
11
14
|
{
|
12
15
|
process: -> input { {name: input["name"], email: input["email"]} },
|
16
|
+
verify: -> input { Success(input) },
|
13
17
|
validate: -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input },
|
14
18
|
persist: -> input { Test::DB << input and true }
|
15
19
|
}
|
@@ -83,4 +87,26 @@ RSpec.describe CallSheet do
|
|
83
87
|
expect(match).to eq "Matched validate failure"
|
84
88
|
end
|
85
89
|
end
|
90
|
+
|
91
|
+
context "failed in a raw step" do
|
92
|
+
let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
|
93
|
+
let(:run_call_sheet) { call_sheet.call(input) }
|
94
|
+
|
95
|
+
before do
|
96
|
+
container[:verify] = -> input { Failure("raw failure") }
|
97
|
+
end
|
98
|
+
|
99
|
+
it "does not run subsequent operations" do
|
100
|
+
run_call_sheet
|
101
|
+
expect(Test::DB).to be_empty
|
102
|
+
end
|
103
|
+
|
104
|
+
it "returns a failure" do
|
105
|
+
expect(run_call_sheet).to be_failure
|
106
|
+
end
|
107
|
+
|
108
|
+
it "returns the failing value from the operation" do
|
109
|
+
expect(run_call_sheet.value).to eq "raw failure"
|
110
|
+
end
|
111
|
+
end
|
86
112
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
RSpec.describe "publishing step events" do
|
2
|
+
include Deterministic::Prelude::Result
|
3
|
+
|
4
|
+
let(:call_sheet) {
|
5
|
+
CallSheet(container: container) do
|
6
|
+
map :process
|
7
|
+
raw :verify
|
8
|
+
tee :persist
|
9
|
+
end
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:container) {
|
13
|
+
{
|
14
|
+
process: -> input { {name: input["name"]} },
|
15
|
+
verify: -> input { input[:name].to_s != "" ? Success(input) : Failure("no name") },
|
16
|
+
persist: -> input { Test::DB << input and true }
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
let(:subscriber) { spy(:subscriber) }
|
21
|
+
|
22
|
+
before do
|
23
|
+
Test::DB = []
|
24
|
+
end
|
25
|
+
|
26
|
+
context "subscribing to all step events" do
|
27
|
+
before do
|
28
|
+
call_sheet.subscribe(subscriber)
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "subscriber receives success events" do
|
32
|
+
call_sheet.call("name" => "Jane")
|
33
|
+
|
34
|
+
expect(subscriber).to have_received(:process_success).with(name: "Jane")
|
35
|
+
expect(subscriber).to have_received(:verify_success).with(name: "Jane")
|
36
|
+
expect(subscriber).to have_received(:persist_success).with(name: "Jane")
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "subsriber receives success events for passing steps, a failure event for the failing step, and no subsequent events" do
|
40
|
+
call_sheet.call("name" => "")
|
41
|
+
|
42
|
+
expect(subscriber).to have_received(:process_success).with(name: "")
|
43
|
+
expect(subscriber).to have_received(:verify_failure).with({name: ""}, "no name")
|
44
|
+
expect(subscriber).not_to have_received(:persist_success)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "subscribing to particular step events" do
|
49
|
+
before do
|
50
|
+
call_sheet.subscribe(verify: subscriber)
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "subscriber receives success event for the specified step" do
|
54
|
+
call_sheet.call("name" => "Jane")
|
55
|
+
|
56
|
+
expect(subscriber).to have_received(:verify_success).with(name: "Jane")
|
57
|
+
expect(subscriber).not_to have_received(:process_success)
|
58
|
+
expect(subscriber).not_to have_received(:persist_success)
|
59
|
+
end
|
60
|
+
|
61
|
+
specify "subscriber receives failure event for the specified step" do
|
62
|
+
call_sheet.call("name" => "")
|
63
|
+
|
64
|
+
expect(subscriber).to have_received(:verify_failure).with({name: ""}, "no name")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: call_sheet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Riley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deterministic
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.15.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: wisper
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.6.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.6.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +108,20 @@ dependencies:
|
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: 0.10.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
97
125
|
description:
|
98
126
|
email:
|
99
127
|
- tim@icelab.com.au
|
@@ -120,8 +148,8 @@ files:
|
|
120
148
|
- lib/call_sheet/version.rb
|
121
149
|
- spec/examples.txt
|
122
150
|
- spec/integration/call_sheet_spec.rb
|
123
|
-
- spec/integration/inline_procs_spec.rb
|
124
151
|
- spec/integration/passing_step_arguments_spec.rb
|
152
|
+
- spec/integration/publishing_step_events_spec.rb
|
125
153
|
- spec/spec_helper.rb
|
126
154
|
- spec/support/test_module_constants.rb
|
127
155
|
homepage: https://github.com/icelab/call_sheet
|
@@ -149,3 +177,4 @@ signing_key:
|
|
149
177
|
specification_version: 4
|
150
178
|
summary: Business Transaction Flow DSL
|
151
179
|
test_files: []
|
180
|
+
has_rdoc:
|
@@ -1,40 +0,0 @@
|
|
1
|
-
RSpec.describe "using inline procs with raw steps" do
|
2
|
-
let(:call_sheet) {
|
3
|
-
CallSheet(container: container) do
|
4
|
-
map :process
|
5
|
-
raw :validate, with: -> input { input[:email].nil? ? Failure("email required") : Success(input) }
|
6
|
-
end
|
7
|
-
}
|
8
|
-
|
9
|
-
let(:container) { {process: -> input { {name: input["name"], email: input["email"]} }} }
|
10
|
-
|
11
|
-
before do
|
12
|
-
Test::NotValidError = Class.new(StandardError)
|
13
|
-
Test::DB = []
|
14
|
-
end
|
15
|
-
|
16
|
-
context "inline step returns Success" do
|
17
|
-
it "calls all the operations" do
|
18
|
-
input = {"name" => "Jane", "email" => "jane@doe.com"}
|
19
|
-
expect(call_sheet.call(input)).to be_success
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context "inline step returns Failure" do
|
24
|
-
let(:input) { {"name" => "Jane"} }
|
25
|
-
|
26
|
-
it "stops running the step operations and returns the failure" do
|
27
|
-
expect(call_sheet.call(input)).to be_failure
|
28
|
-
end
|
29
|
-
|
30
|
-
it "supports pattern matching on the failed step name" do
|
31
|
-
match = call_sheet.call(input).match do
|
32
|
-
Success(_) {}
|
33
|
-
Failure(f, where { f == :validate }) { "Matched validate failure" }
|
34
|
-
Failure(_) {}
|
35
|
-
end
|
36
|
-
|
37
|
-
expect(match).to eq "Matched validate failure"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|