call_sheet 0.3.2 → 0.4.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 +1 -1
- data/README.md +38 -13
- data/lib/call_sheet.rb +13 -14
- data/lib/call_sheet/result_matcher.rb +7 -23
- data/lib/call_sheet/step_adapters/raw.rb +0 -1
- data/lib/call_sheet/transaction.rb +223 -3
- data/lib/call_sheet/version.rb +1 -1
- data/spec/examples.txt +38 -20
- data/spec/integration/call_sheet_spec.rb +11 -15
- data/spec/integration/publishing_step_events_spec.rb +1 -1
- data/spec/unit/transaction_spec.rb +173 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04addbea6823e48be8a177f6ed780599763e6e60
|
4
|
+
data.tar.gz: 0b8fc7f7227ee2b2deb80d5c480738d9a6c2968f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff2fae39de04d6840acfba7b5f802b0b018eb108e4e2eee2de3c536b53adad1220d382091cfc9059f1b08d1b1e1bd459f7b6cb971e6f56f0b817cf49c23a899f
|
7
|
+
data.tar.gz: e5b423a612353fcdc38ffe7b0a7904a2848da73ab78e9242092e124cc8841622d9408aa98d910f535509185cc464248d679510cd15671d5313cdf2498c2e39a5
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
[gitter]: https://gitter.im/icelab/call_sheet?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
1
2
|
[gem]: https://rubygems.org/gems/call_sheet
|
2
3
|
[code_climate]: https://codeclimate.com/github/icelab/call_sheet
|
3
4
|
[inch]: http://inch-ci.org/github/icelab/call_sheet
|
4
5
|
|
5
|
-
# Call Sheet
|
6
|
+
# Call Sheet [](gitter)
|
6
7
|
|
7
8
|
[][gem]
|
8
9
|
[][code_climate]
|
@@ -23,35 +24,53 @@ Call Sheet is based on the following ideas, drawn mostly from [Transflow](http:/
|
|
23
24
|
|
24
25
|
## Why?
|
25
26
|
|
26
|
-
Requiring a business transaction
|
27
|
+
Requiring a business transaction’s steps to exist as independent operations directly addressable via a container means that they can be tested in isolation and easily reused throughout your application. Following from this, keeping the business transaction to a series of high-level, declarative steps ensures that it’s easy to understand at a glance.
|
27
28
|
|
28
29
|
The output of each step is wrapped in a [Kleisli](https://github.com/txus/kleisli) `Either` object (`Right` for success or `Left` for failure). This allows the steps to be chained together and ensures that processing stops in the case of a failure. Returning an `Either` from the overall transaction also allows for error handling to remain a primary concern without it getting in the way of tidy, straightforward operation logic. Wrapping the step output also means that you can work with a wide variety of operations within your application – they don’t need to return an `Either` already.
|
29
30
|
|
30
31
|
## Usage
|
31
32
|
|
32
|
-
|
33
|
+
### Container
|
33
34
|
|
34
|
-
|
35
|
+
All you need to use Call Sheet is a container to hold your application’s operations. Each operation must respond to `#call(input)`.
|
35
36
|
|
36
|
-
|
37
|
-
* `try` – the operation may raise an exception in an error case. This is caught and returned as `Left(exception)`. The output is otherwise returned as `Right(output)`.
|
38
|
-
* `tee` – the operation interacts with some external system and has no meaningful output. The original input is passed through and returned as `Right(input)`.
|
39
|
-
* `raw` or `step` – the operation already returns its own `Either` object, and needs no special handling.
|
37
|
+
The operations will be resolved from the container via `#[]`. For our examples, we’ll use a plain hash:
|
40
38
|
|
41
39
|
```ruby
|
42
|
-
DB = []
|
43
|
-
|
44
40
|
container = {
|
45
41
|
process: -> input { {name: input["name"], email: input["email"]} },
|
46
42
|
validate: -> input { input[:email].nil? ? raise(ValidationFailure, "not valid") : input },
|
47
43
|
persist: -> input { DB << input and true }
|
48
44
|
}
|
45
|
+
```
|
46
|
+
|
47
|
+
For larger apps, you may like to consider something like [dry-container](https://github.com/dryrb/dry-container).
|
49
48
|
|
49
|
+
### Defining a transaction
|
50
|
+
|
51
|
+
Define a transaction to bring your opererations together:
|
52
|
+
|
53
|
+
```ruby
|
50
54
|
save_user = CallSheet(container: container) do
|
51
55
|
map :process
|
52
56
|
try :validate, catch: ValidationFailure
|
53
57
|
tee :persist
|
54
58
|
end
|
59
|
+
```
|
60
|
+
|
61
|
+
Operations are formed into steps using _step adapters._ Step adapters wrap the output of your operations to make them easy to integrate into a transaction. The following adapters are available:
|
62
|
+
|
63
|
+
* `step` – the operation already returns an `Either` object (`Right(output)` for success and `Left(output)` for failure), and needs no special handling.
|
64
|
+
* `map` – any output is considered successful and returned as `Right(output)`
|
65
|
+
* `try` – the operation may raise an exception in an error case. This is caught and returned as `Left(exception)`. The output is otherwise returned as `Right(output)`.
|
66
|
+
* `tee` – the operation interacts with some external system and has no meaningful output. The original input is passed through and returned as `Right(input)`.
|
67
|
+
|
68
|
+
### Calling a transaction
|
69
|
+
|
70
|
+
Calling a transaction will run its operations in their specified order, with the output of each operation becoming the input for the next.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
DB = []
|
55
74
|
|
56
75
|
save_user.call("name" => "Jane", "email" => "jane@doe.com")
|
57
76
|
# => Right({:name=>"Jane", :email=>"jane@doe.com"})
|
@@ -60,7 +79,7 @@ DB
|
|
60
79
|
# => [{:name=>"Jane", :email=>"jane@doe.com"}]
|
61
80
|
```
|
62
81
|
|
63
|
-
Each transaction returns a result value wrapped in a `Left` or `Right` object. You can handle these
|
82
|
+
Each transaction returns a result value wrapped in a `Left` or `Right` object (based on the output of its final step). You can handle these results (including errors arising from particular steps) with a match block:
|
64
83
|
|
65
84
|
```ruby
|
66
85
|
save_user.call(name: "Jane", email: "jane@doe.com") do |m|
|
@@ -139,6 +158,10 @@ NOTIFICATIONS
|
|
139
158
|
|
140
159
|
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)`.
|
141
160
|
|
161
|
+
### Extending transactions
|
162
|
+
|
163
|
+
You can extend existing transactions by inserting or removing steps. See the [API docs](http://www.rubydoc.info/github/icelab/call_sheet/CallSheet/Transaction) for more information.
|
164
|
+
|
142
165
|
### Working with a larger container
|
143
166
|
|
144
167
|
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:
|
@@ -151,8 +174,6 @@ save_user = CallSheet(container: large_whole_app_container) do
|
|
151
174
|
end
|
152
175
|
```
|
153
176
|
|
154
|
-
A `raw` step (also aliased as `step`) can be used if the operation in your container already returns an `Either` and therefore doesn’t need any special handling.
|
155
|
-
|
156
177
|
## Installation
|
157
178
|
|
158
179
|
Add this line to your application’s `Gemfile`:
|
@@ -163,6 +184,10 @@ gem "call_sheet"
|
|
163
184
|
|
164
185
|
Run `bundle` to install the gem.
|
165
186
|
|
187
|
+
## Documentation
|
188
|
+
|
189
|
+
View the [full API documentation](http://www.rubydoc.info/github/icelab/call_sheet) on RubyDoc.info.
|
190
|
+
|
166
191
|
## Contributing
|
167
192
|
|
168
193
|
Bug reports and pull requests are welcome on [GitHub](http://github.com/icelab/call_sheet).
|
data/lib/call_sheet.rb
CHANGED
@@ -12,25 +12,24 @@ require "call_sheet/dsl"
|
|
12
12
|
# `#call(*args, input)`.
|
13
13
|
#
|
14
14
|
# Each operation will be called in the order it was specified in your
|
15
|
-
# transaction, with its output is passed as the
|
15
|
+
# transaction, with its output is passed as the input to the next operation.
|
16
16
|
# Operations will only be called if the previous step was a success.
|
17
17
|
#
|
18
|
-
# A step is successful when it returns a `
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# plain `step` or `raw` steps.
|
18
|
+
# A step is successful when it returns a [Kleisli](kleisli) `Right` object
|
19
|
+
# wrapping its output value. A step is a failure when it returns a `Left`
|
20
|
+
# object. If your operations already return a `Right` or `Left`, they can be
|
21
|
+
# added to your operation as plain `step` steps.
|
23
22
|
#
|
24
|
-
# If your operations don't already return `
|
25
|
-
#
|
23
|
+
# If your operations don't already return `Right` or `Left`, then they can be
|
24
|
+
# added to the transaction with the following steps:
|
26
25
|
#
|
27
|
-
# * `map` --- wrap the output of the operation in a `
|
28
|
-
# * `try` --- wrap the output of the operation in a `
|
29
|
-
# exception is raised, which will be caught and returned as a `
|
26
|
+
# * `map` --- wrap the output of the operation in a `Right`
|
27
|
+
# * `try` --- wrap the output of the operation in a `Right`, unless a certain
|
28
|
+
# exception is raised, which will be caught and returned as a `Left`.
|
30
29
|
# * `tee` --- ignore the output of the operation and pass through its original
|
31
|
-
# input as a `
|
30
|
+
# input as a `Right`.
|
32
31
|
#
|
33
|
-
# [
|
32
|
+
# [kleisli]: https://rubygems.org/gems/kleisli
|
34
33
|
#
|
35
34
|
# @example
|
36
35
|
# container = {do_first: some_obj, do_second: some_obj}
|
@@ -42,7 +41,7 @@ require "call_sheet/dsl"
|
|
42
41
|
#
|
43
42
|
# my_transaction.call(some_input)
|
44
43
|
#
|
45
|
-
# @param [Hash]
|
44
|
+
# @param options [Hash] the options hash
|
46
45
|
# @option options [#[]] :container the operations container
|
47
46
|
#
|
48
47
|
# @return [CallSheet::Transaction] the transaction object
|
@@ -1,39 +1,23 @@
|
|
1
1
|
module CallSheet
|
2
2
|
class ResultMatcher
|
3
3
|
attr_reader :result
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :output
|
5
5
|
|
6
6
|
def initialize(result)
|
7
7
|
@result = result
|
8
|
-
@value = result.value
|
9
8
|
end
|
10
9
|
|
11
10
|
def success(&block)
|
12
|
-
|
13
|
-
end
|
11
|
+
return output unless result.is_a?(Kleisli::Either::Right)
|
14
12
|
|
15
|
-
|
16
|
-
block.call FailureMatcher.new(result) if result.is_a?(Kleisli::Either::Left)
|
13
|
+
@output = block.call(result.value)
|
17
14
|
end
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
attr_reader :value
|
22
|
-
|
23
|
-
def initialize(result)
|
24
|
-
@result = result
|
25
|
-
@value = result.value
|
26
|
-
end
|
27
|
-
|
28
|
-
def on(step_name, &block)
|
29
|
-
if value.__step_name == step_name
|
30
|
-
@matched = true
|
31
|
-
block.call value
|
32
|
-
end
|
33
|
-
end
|
16
|
+
def failure(step_name = nil, &block)
|
17
|
+
return output unless result.is_a?(Kleisli::Either::Left)
|
34
18
|
|
35
|
-
|
36
|
-
block.call
|
19
|
+
if step_name.nil? || step_name == result.value.__step_name
|
20
|
+
@output = block.call(result.value)
|
37
21
|
end
|
38
22
|
end
|
39
23
|
end
|
@@ -4,13 +4,39 @@ module CallSheet
|
|
4
4
|
class Transaction
|
5
5
|
# @api private
|
6
6
|
attr_reader :steps
|
7
|
-
private :steps
|
8
7
|
|
9
8
|
# @api private
|
10
9
|
def initialize(steps)
|
11
10
|
@steps = steps
|
12
11
|
end
|
13
12
|
|
13
|
+
# Run the transaction.
|
14
|
+
#
|
15
|
+
# Each operation will be called in the order it was specified, with its
|
16
|
+
# output passed as input to the next operation. Operations will only be
|
17
|
+
# called if the previous step was a success.
|
18
|
+
#
|
19
|
+
# If any of the operations require extra arguments beyond the main input
|
20
|
+
# e.g. with a signature like `#call(something_else, input)`, then you must
|
21
|
+
# pass the extra arguments as arrays for each step in the options hash.
|
22
|
+
#
|
23
|
+
# @example Running a transaction
|
24
|
+
# my_transaction.call(some_input)
|
25
|
+
#
|
26
|
+
# @example Running a transaction with extra step arguments
|
27
|
+
# my_transaction.call(some_input, step_name: [extra_argument])
|
28
|
+
#
|
29
|
+
# The return value will be the output from the last operation, wrapped in
|
30
|
+
# a [Kleisli](kleisli) `Either` object, a `Right` for a successful
|
31
|
+
# transaction or a `Left` for a failed transaction.
|
32
|
+
#
|
33
|
+
# [kleisli]: https://rubygems.org/gems/kleisli
|
34
|
+
#
|
35
|
+
# @param input
|
36
|
+
# @param options [Hash] extra step arguments
|
37
|
+
#
|
38
|
+
# @return [Right, Left] output from the final step
|
39
|
+
#
|
14
40
|
# @api public
|
15
41
|
def call(input, options = {}, &block)
|
16
42
|
assert_valid_options(options)
|
@@ -19,11 +45,40 @@ module CallSheet
|
|
19
45
|
steps = steps_with_options_applied(options)
|
20
46
|
result = steps.inject(Right(input), :>>)
|
21
47
|
|
22
|
-
|
23
|
-
|
48
|
+
if block
|
49
|
+
block.call(ResultMatcher.new(result))
|
50
|
+
else
|
51
|
+
result
|
52
|
+
end
|
24
53
|
end
|
25
54
|
alias_method :[], :call
|
26
55
|
|
56
|
+
# Subscribe to notifications from steps.
|
57
|
+
#
|
58
|
+
# When each step completes, it will send a `[step_name]_success` or
|
59
|
+
# `[step_name]_failure` message to any subscribers.
|
60
|
+
#
|
61
|
+
# For example, if you had a step called `persist`, then it would send
|
62
|
+
# either `persist_success` or `persist_failure` messages to subscribers
|
63
|
+
# after the operation completes.
|
64
|
+
#
|
65
|
+
# Pass a single object to subscribe to notifications from all steps, or
|
66
|
+
# pass a hash with step names as keys to subscribe to notifications from
|
67
|
+
# specific steps.
|
68
|
+
#
|
69
|
+
# @example Subscribing to notifications from all steps
|
70
|
+
# my_transaction.subscribe(my_listener)
|
71
|
+
#
|
72
|
+
# @example Subscribing to notifications from specific steps
|
73
|
+
# my_transaction.subscirbe(some_step: my_listener, another_step: another_listener)
|
74
|
+
#
|
75
|
+
# Notifications are implemented using the [Wisper](wisper) gem.
|
76
|
+
#
|
77
|
+
# [wisper]: https://rubygems.org/gems/wisper
|
78
|
+
#
|
79
|
+
# @param listeners [Object, Hash{Symbol => Object}] the listener object or
|
80
|
+
# hash of steps and listeners
|
81
|
+
#
|
27
82
|
# @api public
|
28
83
|
def subscribe(listeners)
|
29
84
|
if listeners.is_a?(Hash)
|
@@ -39,6 +94,163 @@ module CallSheet
|
|
39
94
|
self
|
40
95
|
end
|
41
96
|
|
97
|
+
# Return a transaction with the steps from the provided transaction
|
98
|
+
# prepended onto the beginning of the steps in `self`.
|
99
|
+
#
|
100
|
+
# @example Prepend an existing transaction
|
101
|
+
# my_transaction = CallSheet(container: container) do
|
102
|
+
# step :first
|
103
|
+
# step :second
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# other_transaction = CallSheet(container: container) do
|
107
|
+
# step :another
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# my_transaction.prepend(other_transaction)
|
111
|
+
#
|
112
|
+
# @example Prepend a transaction defined inline
|
113
|
+
# my_transaction = CallSheet(container: container) do
|
114
|
+
# step :first
|
115
|
+
# step :second
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# my_transaction.prepend(container: container) do
|
119
|
+
# step :another
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# @param other [CallSheet::Transaction] the transaction to prepend.
|
123
|
+
# Optional if you will define a transaction inline via a block.
|
124
|
+
# @param options [Hash] the options hash for defining a transaction inline
|
125
|
+
# via a block. Optional if the transaction is passed directly as
|
126
|
+
# `other`.
|
127
|
+
# @option options [#[]] :container the operations container
|
128
|
+
#
|
129
|
+
# @return [CallSheet::Transaction] the modified transaction object
|
130
|
+
#
|
131
|
+
# @api public
|
132
|
+
def prepend(other = nil, **options, &block)
|
133
|
+
other = accept_or_build_transaction(other, **options, &block)
|
134
|
+
|
135
|
+
self.class.new(other.steps + steps)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Return a transaction with the steps from the provided transaction
|
139
|
+
# appended onto the end of the steps in `self`.
|
140
|
+
#
|
141
|
+
# @example Append an existing transaction
|
142
|
+
# my_transaction = CallSheet(container: container) do
|
143
|
+
# step :first
|
144
|
+
# step :second
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# other_transaction = CallSheet(container: container) do
|
148
|
+
# step :another
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# my_transaction.append(other_transaction)
|
152
|
+
#
|
153
|
+
# @example Append a transaction defined inline
|
154
|
+
# my_transaction = CallSheet(container: container) do
|
155
|
+
# step :first
|
156
|
+
# step :second
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# my_transaction.append(container: container) do
|
160
|
+
# step :another
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# @param other [CallSheet::Transaction] the transaction to append.
|
164
|
+
# Optional if you will define a transaction inline via a block.
|
165
|
+
# @param options [Hash] the options hash for defining a transaction inline
|
166
|
+
# via a block. Optional if the transaction is passed directly as
|
167
|
+
# `other`.
|
168
|
+
# @option options [#[]] :container the operations container
|
169
|
+
#
|
170
|
+
# @return [CallSheet::Transaction] the modified transaction object
|
171
|
+
#
|
172
|
+
# @api public
|
173
|
+
def append(other = nil, **options, &block)
|
174
|
+
other = accept_or_build_transaction(other, **options, &block)
|
175
|
+
|
176
|
+
self.class.new(steps + other.steps)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Return a transaction with the steps from the provided transaction
|
180
|
+
# inserted into a specific place among the steps in `self`.
|
181
|
+
#
|
182
|
+
# Transactions can be inserted either before or after a named step.
|
183
|
+
#
|
184
|
+
# @example Insert an existing transaction (before a step)
|
185
|
+
# my_transaction = CallSheet(container: container) do
|
186
|
+
# step :first
|
187
|
+
# step :second
|
188
|
+
# end
|
189
|
+
#
|
190
|
+
# other_transaction = CallSheet(container: container) do
|
191
|
+
# step :another
|
192
|
+
# end
|
193
|
+
#
|
194
|
+
# my_transaction.insert(other_transaction, before: :second)
|
195
|
+
#
|
196
|
+
# @example Append a transaction defined inline (after a step)
|
197
|
+
# my_transaction = CallSheet(container: container) do
|
198
|
+
# step :first
|
199
|
+
# step :second
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# my_transaction.insert(after: :first, container: container) do
|
203
|
+
# step :another
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# @param other [CallSheet::Transaction] the transaction to append.
|
207
|
+
# Optional if you will define a transaction inline via a block.
|
208
|
+
# @param before [Symbol] the name of the step before which the transaction
|
209
|
+
# should be inserted (provide either this or `after`)
|
210
|
+
# @param after [Symbol] the name of the step after which the transaction
|
211
|
+
# should be inserted (provide either this or `before`)
|
212
|
+
# @param options [Hash] the options hash for defining a transaction inline
|
213
|
+
# via a block. Optional if the transaction is passed directly as
|
214
|
+
# `other`.
|
215
|
+
# @option options [#[]] :container the operations container
|
216
|
+
#
|
217
|
+
# @return [CallSheet::Transaction] the modified transaction object
|
218
|
+
#
|
219
|
+
# @api public
|
220
|
+
def insert(other = nil, before: nil, after: nil, **options, &block)
|
221
|
+
insertion_step = before || after
|
222
|
+
unless steps.map(&:step_name).include?(insertion_step)
|
223
|
+
raise ArgumentError, "+#{insertion_step}+ is not a valid step name"
|
224
|
+
end
|
225
|
+
|
226
|
+
other = accept_or_build_transaction(other, **options, &block)
|
227
|
+
index = steps.index { |step| step.step_name == insertion_step } + (!!after ? 1 : 0)
|
228
|
+
|
229
|
+
self.class.new(steps.dup.insert(index, *other.steps))
|
230
|
+
end
|
231
|
+
|
232
|
+
# @overload remove(step, ...)
|
233
|
+
# Return a transaction with steps removed.
|
234
|
+
#
|
235
|
+
# @example
|
236
|
+
# my_transaction = CallSheet(container: container) do
|
237
|
+
# step :first
|
238
|
+
# step :second
|
239
|
+
# step :third
|
240
|
+
# end
|
241
|
+
#
|
242
|
+
# my_transaction.remove(:first, :third)
|
243
|
+
#
|
244
|
+
# @param step [Symbol] the names of a step to remove
|
245
|
+
# @param ... [Symbol] more names of steps to remove
|
246
|
+
#
|
247
|
+
# @return [CallSheet::Transaction] the modified transaction object
|
248
|
+
#
|
249
|
+
# @api public
|
250
|
+
def remove(*steps_to_remove)
|
251
|
+
self.class.new(steps.reject { |step| steps_to_remove.include?(step.step_name) })
|
252
|
+
end
|
253
|
+
|
42
254
|
private
|
43
255
|
|
44
256
|
def assert_valid_options(options)
|
@@ -69,5 +281,13 @@ module CallSheet
|
|
69
281
|
end
|
70
282
|
}
|
71
283
|
end
|
284
|
+
|
285
|
+
def accept_or_build_transaction(other_transaction = nil, **options, &block)
|
286
|
+
unless other_transaction || block
|
287
|
+
raise ArgumentError, "a transaction must be provided or defined in a block"
|
288
|
+
end
|
289
|
+
|
290
|
+
other_transaction || DSL.new(**options, &block).call
|
291
|
+
end
|
72
292
|
end
|
73
293
|
end
|
data/lib/call_sheet/version.rb
CHANGED
data/spec/examples.txt
CHANGED
@@ -1,23 +1,41 @@
|
|
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.
|
3
|
+
./spec/integration/call_sheet_spec.rb[1:1:1] | passed | 0.00106 seconds |
|
4
|
+
./spec/integration/call_sheet_spec.rb[1:1:2] | passed | 0.00021 seconds |
|
5
5
|
./spec/integration/call_sheet_spec.rb[1:1:3] | passed | 0.00019 seconds |
|
6
|
-
./spec/integration/call_sheet_spec.rb[1:1:4] | passed | 0.
|
7
|
-
./spec/integration/call_sheet_spec.rb[1:1:5] | passed | 0.
|
8
|
-
./spec/integration/call_sheet_spec.rb[1:2:1] | passed | 0.
|
9
|
-
./spec/integration/call_sheet_spec.rb[1:2:2] | passed | 0.
|
10
|
-
./spec/integration/call_sheet_spec.rb[1:2:3] | passed | 0.
|
11
|
-
./spec/integration/call_sheet_spec.rb[1:2:4] | passed | 0.
|
12
|
-
./spec/integration/call_sheet_spec.rb[1:2:5] | passed | 0.
|
13
|
-
./spec/integration/call_sheet_spec.rb[1:2:6] | passed | 0.
|
14
|
-
./spec/integration/call_sheet_spec.rb[1:3:1] | passed | 0.
|
15
|
-
./spec/integration/call_sheet_spec.rb[1:3:2] | passed | 0.
|
16
|
-
./spec/integration/call_sheet_spec.rb[1:3:3] | passed | 0.
|
17
|
-
./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.
|
18
|
-
./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.
|
19
|
-
./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.
|
20
|
-
./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.
|
21
|
-
./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.
|
22
|
-
./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.
|
23
|
-
./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.
|
6
|
+
./spec/integration/call_sheet_spec.rb[1:1:4] | passed | 0.00033 seconds |
|
7
|
+
./spec/integration/call_sheet_spec.rb[1:1:5] | passed | 0.00021 seconds |
|
8
|
+
./spec/integration/call_sheet_spec.rb[1:2:1] | passed | 0.00153 seconds |
|
9
|
+
./spec/integration/call_sheet_spec.rb[1:2:2] | passed | 0.00052 seconds |
|
10
|
+
./spec/integration/call_sheet_spec.rb[1:2:3] | passed | 0.00021 seconds |
|
11
|
+
./spec/integration/call_sheet_spec.rb[1:2:4] | passed | 0.00024 seconds |
|
12
|
+
./spec/integration/call_sheet_spec.rb[1:2:5] | passed | 0.00023 seconds |
|
13
|
+
./spec/integration/call_sheet_spec.rb[1:2:6] | passed | 0.00023 seconds |
|
14
|
+
./spec/integration/call_sheet_spec.rb[1:3:1] | passed | 0.00022 seconds |
|
15
|
+
./spec/integration/call_sheet_spec.rb[1:3:2] | passed | 0.00017 seconds |
|
16
|
+
./spec/integration/call_sheet_spec.rb[1:3:3] | passed | 0.00018 seconds |
|
17
|
+
./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.00037 seconds |
|
18
|
+
./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.00026 seconds |
|
19
|
+
./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.00026 seconds |
|
20
|
+
./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.00134 seconds |
|
21
|
+
./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.00051 seconds |
|
22
|
+
./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.00039 seconds |
|
23
|
+
./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.00035 seconds |
|
24
|
+
./spec/unit/transaction_spec.rb[1:1:1] | passed | 0.00014 seconds |
|
25
|
+
./spec/unit/transaction_spec.rb[1:1:2] | passed | 0.00028 seconds |
|
26
|
+
./spec/unit/transaction_spec.rb[1:1:3] | passed | 0.00013 seconds |
|
27
|
+
./spec/unit/transaction_spec.rb[1:1:4] | passed | 0.00013 seconds |
|
28
|
+
./spec/unit/transaction_spec.rb[1:2:1] | passed | 0.00018 seconds |
|
29
|
+
./spec/unit/transaction_spec.rb[1:2:2] | passed | 0.00079 seconds |
|
30
|
+
./spec/unit/transaction_spec.rb[1:2:3] | passed | 0.00145 seconds |
|
31
|
+
./spec/unit/transaction_spec.rb[1:2:4] | passed | 0.00014 seconds |
|
32
|
+
./spec/unit/transaction_spec.rb[1:3:1] | passed | 0.00022 seconds |
|
33
|
+
./spec/unit/transaction_spec.rb[1:3:2] | passed | 0.00016 seconds |
|
34
|
+
./spec/unit/transaction_spec.rb[1:4:1] | passed | 0.00036 seconds |
|
35
|
+
./spec/unit/transaction_spec.rb[1:4:2] | passed | 0.00017 seconds |
|
36
|
+
./spec/unit/transaction_spec.rb[1:4:3] | passed | 0.00013 seconds |
|
37
|
+
./spec/unit/transaction_spec.rb[1:4:4] | passed | 0.00012 seconds |
|
38
|
+
./spec/unit/transaction_spec.rb[1:4:5:1] | passed | 0.00018 seconds |
|
39
|
+
./spec/unit/transaction_spec.rb[1:4:5:2] | passed | 0.00031 seconds |
|
40
|
+
./spec/unit/transaction_spec.rb[1:4:6:1] | passed | 0.00021 seconds |
|
41
|
+
./spec/unit/transaction_spec.rb[1:4:6:2] | passed | 0.00021 seconds |
|
@@ -2,7 +2,7 @@ RSpec.describe CallSheet do
|
|
2
2
|
let(:call_sheet) {
|
3
3
|
CallSheet(container: container) do
|
4
4
|
map :process
|
5
|
-
|
5
|
+
step :verify
|
6
6
|
try :validate, catch: Test::NotValidError
|
7
7
|
tee :persist
|
8
8
|
end
|
@@ -79,8 +79,8 @@ RSpec.describe CallSheet do
|
|
79
79
|
results = []
|
80
80
|
|
81
81
|
call_sheet.call(input) do |m|
|
82
|
-
m.failure do |
|
83
|
-
results << "Failed: #{
|
82
|
+
m.failure do |value|
|
83
|
+
results << "Failed: #{value}"
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -91,10 +91,8 @@ RSpec.describe CallSheet do
|
|
91
91
|
results = []
|
92
92
|
|
93
93
|
call_sheet.call(input) do |m|
|
94
|
-
m.failure do |
|
95
|
-
|
96
|
-
results << "Validation failure: #{v}"
|
97
|
-
end
|
94
|
+
m.failure :validate do |value|
|
95
|
+
results << "Validation failure: #{value}"
|
98
96
|
end
|
99
97
|
end
|
100
98
|
|
@@ -105,14 +103,12 @@ RSpec.describe CallSheet do
|
|
105
103
|
results = []
|
106
104
|
|
107
105
|
call_sheet.call(input) do |m|
|
108
|
-
m.failure do |
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
results << "Catch-all failure: #{v}"
|
115
|
-
end
|
106
|
+
m.failure :some_other_step do |value|
|
107
|
+
results << "Some other step failure"
|
108
|
+
end
|
109
|
+
|
110
|
+
m.failure do |value|
|
111
|
+
results << "Catch-all failure: #{value}"
|
116
112
|
end
|
117
113
|
end
|
118
114
|
|
@@ -0,0 +1,173 @@
|
|
1
|
+
RSpec.describe CallSheet::Transaction do
|
2
|
+
subject(:container) {
|
3
|
+
{
|
4
|
+
upcase: -> input { input.upcase },
|
5
|
+
reverse: -> input { input.reverse },
|
6
|
+
exclaim_all: -> input { input.split(" ").map { |str| str + "!" }.join(" ") },
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
describe "#prepend" do
|
11
|
+
let(:initial_transaction) {
|
12
|
+
CallSheet(container: container) do
|
13
|
+
map :exclaim_all
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
it "prepends the transaction" do
|
18
|
+
other_transaction = CallSheet(container: container) do
|
19
|
+
map :reverse
|
20
|
+
end
|
21
|
+
new_transaction = initial_transaction.prepend(other_transaction)
|
22
|
+
|
23
|
+
expect(new_transaction.call("hello world").right).to eq "dlrow! olleh!"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "accepts a transaction defined in a block" do
|
27
|
+
new_transaction = initial_transaction.prepend(container: container) do
|
28
|
+
map :reverse
|
29
|
+
end
|
30
|
+
|
31
|
+
expect(new_transaction.call("hello world").right).to eq "dlrow! olleh!"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises an argument error if a transaction is neither passed nor defined" do
|
35
|
+
expect { initial_transaction.prepend }.to raise_error(ArgumentError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "leaves the original transaction unmodified" do
|
39
|
+
new_transaction = initial_transaction.prepend(container: container) do
|
40
|
+
map :reverse
|
41
|
+
end
|
42
|
+
|
43
|
+
expect(initial_transaction.call("the quick brown fox").right).to eq "the! quick! brown! fox!"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#append" do
|
48
|
+
let(:initial_transaction) {
|
49
|
+
CallSheet(container: container) do
|
50
|
+
map :exclaim_all
|
51
|
+
end
|
52
|
+
}
|
53
|
+
|
54
|
+
it "appends the transaction" do
|
55
|
+
other_transaction = CallSheet(container: container) do
|
56
|
+
map :reverse
|
57
|
+
end
|
58
|
+
new_transaction = initial_transaction.append(other_transaction)
|
59
|
+
|
60
|
+
expect(new_transaction.call("hello world").right).to eq "!dlrow !olleh"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "accepts a transaction defined in a block" do
|
64
|
+
new_transaction = initial_transaction.append(container: container) do
|
65
|
+
map :reverse
|
66
|
+
end
|
67
|
+
|
68
|
+
expect(new_transaction.call("hello world").right).to eq "!dlrow !olleh"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "raises an argument error if a transaction is neither passed nor defined" do
|
72
|
+
expect { initial_transaction.insert(before: :reverse) }.to raise_error(ArgumentError)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "leaves the original transaction unmodified" do
|
76
|
+
new_transaction = initial_transaction.append(container: container) do
|
77
|
+
map :reverse
|
78
|
+
end
|
79
|
+
|
80
|
+
expect(initial_transaction.call("hello world").right).to eq "hello! world!"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#remove" do
|
85
|
+
let(:initial_transaction) {
|
86
|
+
CallSheet(container: container) do
|
87
|
+
map :upcase
|
88
|
+
map :exclaim_all
|
89
|
+
map :reverse
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
it "removes the specified steps" do
|
94
|
+
new_transaction = initial_transaction.remove(:exclaim_all, :reverse)
|
95
|
+
expect(new_transaction.call("hello world").right).to eq "HELLO WORLD"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "leaves the original transaction unmodified" do
|
99
|
+
new_transaction = initial_transaction.remove(:exclaim_all, :reverse)
|
100
|
+
expect(initial_transaction.call("hello world").right).to eq "!DLROW !OLLEH"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#insert" do
|
105
|
+
let(:initial_transaction) {
|
106
|
+
CallSheet(container: container) do
|
107
|
+
map :upcase
|
108
|
+
map :reverse
|
109
|
+
end
|
110
|
+
}
|
111
|
+
|
112
|
+
it "accepts a transaction passed as an argument" do
|
113
|
+
other_transaction = CallSheet(container: container) do
|
114
|
+
map :exclaim_all
|
115
|
+
end
|
116
|
+
new_transaction = initial_transaction.insert(other_transaction, before: :reverse)
|
117
|
+
|
118
|
+
expect(new_transaction.call("hello world").right).to eq "!DLROW !OLLEH"
|
119
|
+
end
|
120
|
+
|
121
|
+
it "accepts a transaction defined in a block" do
|
122
|
+
new_transaction = initial_transaction.insert(before: :reverse, container: container) do
|
123
|
+
map :exclaim_all
|
124
|
+
end
|
125
|
+
|
126
|
+
expect(new_transaction.call("hello world").right).to eq "!DLROW !OLLEH"
|
127
|
+
end
|
128
|
+
|
129
|
+
it "raises an argument error if a transaction is neither passed nor defined" do
|
130
|
+
expect { initial_transaction.insert(before: :reverse) }.to raise_error(ArgumentError)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "raises an argument error if an invalid step name is provided" do
|
134
|
+
expect {
|
135
|
+
initial_transaction.insert(before: :non_existent, container: container) do
|
136
|
+
map :exclaim_all
|
137
|
+
end
|
138
|
+
}.to raise_error(ArgumentError)
|
139
|
+
end
|
140
|
+
|
141
|
+
context "before" do
|
142
|
+
let!(:new_transaction) {
|
143
|
+
initial_transaction.insert(before: :reverse, container: container) do
|
144
|
+
map :exclaim_all
|
145
|
+
end
|
146
|
+
}
|
147
|
+
|
148
|
+
it "inserts the new steps before the specified one" do
|
149
|
+
expect(new_transaction.call("hello world").right).to eq "!DLROW !OLLEH"
|
150
|
+
end
|
151
|
+
|
152
|
+
it "leaves the original transaction unmodified" do
|
153
|
+
expect(initial_transaction.call("hello world").right).to eq "DLROW OLLEH"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "after" do
|
158
|
+
let!(:new_transaction) {
|
159
|
+
initial_transaction.insert(after: :reverse, container: container) do
|
160
|
+
map :exclaim_all
|
161
|
+
end
|
162
|
+
}
|
163
|
+
|
164
|
+
it "inserts the new steps after the specified one" do
|
165
|
+
expect(new_transaction.call("hello world").right).to eq "DLROW! OLLEH!"
|
166
|
+
end
|
167
|
+
|
168
|
+
it "leaves the original transaction unmodified" do
|
169
|
+
expect(initial_transaction.call("hello world").right).to eq "DLROW OLLEH"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
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.4.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-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kleisli
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- spec/integration/publishing_step_events_spec.rb
|
154
154
|
- spec/spec_helper.rb
|
155
155
|
- spec/support/test_module_constants.rb
|
156
|
+
- spec/unit/transaction_spec.rb
|
156
157
|
homepage: https://github.com/icelab/call_sheet
|
157
158
|
licenses:
|
158
159
|
- MIT
|
@@ -165,7 +166,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
166
|
requirements:
|
166
167
|
- - ">="
|
167
168
|
- !ruby/object:Gem::Version
|
168
|
-
version:
|
169
|
+
version: 2.0.0
|
169
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
171
|
requirements:
|
171
172
|
- - ">="
|