call_sheet 0.1.0 → 0.2.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 +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 Version](https://img.shields.io/gem/v/call_sheet.svg)][gem]
|
8
|
+
[![Code Climate](https://img.shields.io/codeclimate/github/icelab/call_sheet.svg)][code_climate]
|
9
|
+
[![API Documentation Coverage](http://inch-ci.org/github/icelab/call_sheet.svg)][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
|