verbalize 1.3.0 → 1.4.1
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/README.md +147 -4
- data/lib/verbalize.rb +6 -87
- data/lib/verbalize/action.rb +103 -0
- data/lib/verbalize/error.rb +3 -0
- data/lib/verbalize/failure.rb +22 -0
- data/lib/verbalize/result.rb +8 -2
- data/lib/verbalize/success.rb +11 -0
- data/lib/verbalize/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d3d8f53fd6b37b6d5729c0c73d9fabb5cfe3c0b
|
4
|
+
data.tar.gz: 22afc7cd81eb619e8dd393f4b606824e479b2780
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 771d473b91c42ff8f3c58a12c847eff35969b25aae89141e2dbb9dab4f063112a3552b991ac3174e913c2aab2b4362c8b3db4c95962c666cf446390339c2cc63
|
7
|
+
data.tar.gz: 01b65780f3f72a326d8c9b185dfba2ef257ec50bbe29abb16778cd9b9fde468fdd5cb67cd708d86a43618bf3982fdb7b5a8d5b81187905156e317018411b65f1
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
```ruby
|
8
8
|
class Add
|
9
|
-
include Verbalize
|
9
|
+
include Verbalize::Action
|
10
10
|
|
11
11
|
input :a, :b
|
12
12
|
|
@@ -30,7 +30,7 @@ value # => 42
|
|
30
30
|
|
31
31
|
```ruby
|
32
32
|
class Add
|
33
|
-
include Verbalize
|
33
|
+
include Verbalize::Action
|
34
34
|
|
35
35
|
input optional: [:a, :b]
|
36
36
|
|
@@ -55,7 +55,7 @@ Add.call(a: 660, b: 6) # => [:ok, 666]
|
|
55
55
|
|
56
56
|
```ruby
|
57
57
|
class Divide
|
58
|
-
include Verbalize
|
58
|
+
include Verbalize::Action
|
59
59
|
|
60
60
|
input :a, :b
|
61
61
|
|
@@ -96,7 +96,7 @@ class RubyAdd
|
|
96
96
|
end
|
97
97
|
|
98
98
|
class VerbalizeAdd
|
99
|
-
include Verbalize
|
99
|
+
include Verbalize::Action
|
100
100
|
|
101
101
|
input :a, :b
|
102
102
|
|
@@ -150,6 +150,149 @@ Comparison:
|
|
150
150
|
|
151
151
|
```
|
152
152
|
|
153
|
+
## Testing
|
154
|
+
|
155
|
+
### Happy Path
|
156
|
+
|
157
|
+
When testing positive cases of a `Verbalize::Action`, it is recommended to test using the `call!` class method and
|
158
|
+
assert on the result. This implicitly ensures a successful result, and your tests will fail with a bang! if
|
159
|
+
something goes wrong:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
class MyAction
|
163
|
+
include Verbalize::Action
|
164
|
+
|
165
|
+
input :a
|
166
|
+
|
167
|
+
def call
|
168
|
+
fail!('#{a} is greater than than 100!') if a >= 100
|
169
|
+
a + 1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'returns the expected result' do
|
174
|
+
result = MyAction.call!(a: 50)
|
175
|
+
expect(result).to eq 51
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
### Sad Path
|
180
|
+
|
181
|
+
When testing negative cases of a `Verbalize::Action`, it is recommended to test using the `call` non-bang
|
182
|
+
class method. Use of `call!` here is not advised as it will result in an exception being thrown. Set assertions
|
183
|
+
on both the outcome and error value of the result:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
class MyAction
|
187
|
+
include Verbalize::Action
|
188
|
+
|
189
|
+
input :a
|
190
|
+
|
191
|
+
def call
|
192
|
+
fail!('#{a} is greater than 100!') if a >= 100
|
193
|
+
a + 1
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# rspec:
|
198
|
+
it 'fails when the input is out of bounds' do
|
199
|
+
result = MyAction.call(a: 1000)
|
200
|
+
|
201
|
+
expect(result).to be_failed
|
202
|
+
expect(result.value).to eq '1000 is greater than 100!'
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
### Stubbing Responses
|
207
|
+
|
208
|
+
When unit testing, it may be necessary to stub responses of Actions. The approach required depends
|
209
|
+
on how the nested actions are called by the object under test.
|
210
|
+
|
211
|
+
#### Stubbing `call`
|
212
|
+
|
213
|
+
When an object calls a `Verbalize::Action` via `call`, you can stub the response with a `Verbalize::Success` or
|
214
|
+
`Verbalize::Failure` instance.
|
215
|
+
|
216
|
+
Example:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
class Foo
|
220
|
+
def do_something
|
221
|
+
result = MyAction.call(a: 1)
|
222
|
+
raise 'I couldn\'t do the thing!' if result.failure?
|
223
|
+
|
224
|
+
'baz'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# rspec:
|
229
|
+
describe Foo do
|
230
|
+
describe '#something' do
|
231
|
+
subject { described_class.new(foo: 'bar') }
|
232
|
+
|
233
|
+
it 'does the thing when my action succeeds' do
|
234
|
+
successful_result = Verbalize::Success.new(123)
|
235
|
+
allow(MyAction).to receive(:call)
|
236
|
+
.with(a: 1)
|
237
|
+
.and_return(successful_result)
|
238
|
+
|
239
|
+
result = described_class.do_something
|
240
|
+
|
241
|
+
expect(result).to eq 'baz'
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'raises an error when MyAction fails' do
|
245
|
+
failed_result = Verbalize::Failure.new('Y U NO!')
|
246
|
+
allow(MyAction).to receive(:call)
|
247
|
+
.with(a: 3)
|
248
|
+
.and_return(failed_result)
|
249
|
+
|
250
|
+
expect {
|
251
|
+
described_class.do_something
|
252
|
+
}.to raise_error(StandardError, 'I couldnt do the thing!')
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
```
|
257
|
+
|
258
|
+
#### Stubbing `call!`
|
259
|
+
|
260
|
+
When an object calls a `Verbalize::Action` via `call!`, you can stub the response for the positive case with the value
|
261
|
+
you expect to be returned. When stubbing the negative case, you should instead throw `Verbalize::THROWN_SYMBOL`
|
262
|
+
(and the error message, if desired):
|
263
|
+
|
264
|
+
Example:
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
# rspec:
|
268
|
+
describe Foo do
|
269
|
+
describe '#something' do
|
270
|
+
subject { described_class.new(foo: 'bar') }
|
271
|
+
|
272
|
+
it 'does the thing' do
|
273
|
+
allow(MyAction).to receive(:call!)
|
274
|
+
.with(a: 1)
|
275
|
+
.and_return(123)
|
276
|
+
|
277
|
+
result = described_class.something
|
278
|
+
|
279
|
+
expect(result).to eq 'baz'
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'returns an error when MyAction fails' do
|
283
|
+
failed_result = Verbalize::Failure.new('Y U NO!')
|
284
|
+
allow(MyAction).to receive(:call)
|
285
|
+
.with(a: 3)
|
286
|
+
.and_throw(::Verbalize::THROWN_SYMBOL, 'the failure message, if any')
|
287
|
+
|
288
|
+
expect {
|
289
|
+
described_class.something
|
290
|
+
}.to raise_error UncaughtThrowError
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
```
|
295
|
+
|
153
296
|
## Installation
|
154
297
|
|
155
298
|
Add this line to your application's Gemfile:
|
data/lib/verbalize.rb
CHANGED
@@ -1,94 +1,13 @@
|
|
1
1
|
require 'verbalize/version'
|
2
|
-
require 'verbalize/
|
3
|
-
require 'verbalize/build_safe_action_method'
|
4
|
-
require 'verbalize/build_dangerous_action_method'
|
5
|
-
require 'verbalize/build_attribute_readers'
|
6
|
-
require 'verbalize/result'
|
2
|
+
require 'verbalize/action'
|
7
3
|
|
8
4
|
module Verbalize
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def fail!(failure_value = nil)
|
13
|
-
throw(THROWN_SYMBOL, failure_value)
|
14
|
-
end
|
5
|
+
# Remove this in a later version of Verbalize if desired. Aliased here for backwards compatibility.
|
6
|
+
THROWN_SYMBOL = Action::THROWN_SYMBOL
|
15
7
|
|
16
8
|
def self.included(target)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
module ClassMethods
|
21
|
-
def verbalize(*arguments, **keyword_arguments)
|
22
|
-
method_name, *arguments = arguments
|
23
|
-
input(*arguments, method_name: method_name, **keyword_arguments)
|
24
|
-
end
|
25
|
-
|
26
|
-
def input( # rubocop:disable Metrics/MethodLength
|
27
|
-
*required_keywords,
|
28
|
-
optional: [],
|
29
|
-
method_name: :call,
|
30
|
-
**other_keyword_arguments
|
31
|
-
)
|
32
|
-
|
33
|
-
unless other_keyword_arguments.empty?
|
34
|
-
raise(
|
35
|
-
ArgumentError,
|
36
|
-
"Unsupported keyword arguments received: #{other_keyword_arguments.inspect}"
|
37
|
-
)
|
38
|
-
end
|
39
|
-
|
40
|
-
optional_keywords = Array(optional)
|
41
|
-
|
42
|
-
class_eval BuildSafeActionMethod.call(
|
43
|
-
required_keywords: required_keywords,
|
44
|
-
optional_keywords: optional_keywords,
|
45
|
-
method_name: method_name
|
46
|
-
)
|
47
|
-
|
48
|
-
class_eval BuildDangerousActionMethod.call(
|
49
|
-
required_keywords: required_keywords,
|
50
|
-
optional_keywords: optional_keywords,
|
51
|
-
method_name: method_name
|
52
|
-
)
|
53
|
-
|
54
|
-
class_eval BuildInitializeMethod.call(
|
55
|
-
required_keywords: required_keywords,
|
56
|
-
optional_keywords: optional_keywords
|
57
|
-
)
|
58
|
-
|
59
|
-
class_eval BuildAttributeReaders.call(
|
60
|
-
attributes: required_keywords + optional_keywords
|
61
|
-
)
|
62
|
-
end
|
63
|
-
|
64
|
-
def call
|
65
|
-
__verbalized_send(:call)
|
66
|
-
end
|
67
|
-
|
68
|
-
def call!
|
69
|
-
__verbalized_send!(:call)
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def __verbalized_send(method_name, *args)
|
75
|
-
outcome = :error
|
76
|
-
value = catch(:verbalize_error) do
|
77
|
-
value = new(*args).send(method_name)
|
78
|
-
# The outcome is :ok if the call didn't throw.
|
79
|
-
outcome = :ok
|
80
|
-
value
|
81
|
-
end
|
82
|
-
Result.new(outcome: outcome, value: value)
|
83
|
-
end
|
84
|
-
|
85
|
-
def __verbalized_send!(method_name, *args)
|
86
|
-
new(*args).send(method_name)
|
87
|
-
rescue UncaughtThrowError => uncaught_throw_error
|
88
|
-
fail_value = uncaught_throw_error.value
|
89
|
-
error = VerbalizeError.new("Unhandled fail! called with: #{fail_value.inspect}.")
|
90
|
-
error.set_backtrace(uncaught_throw_error.backtrace[2..-1])
|
91
|
-
raise error
|
92
|
-
end
|
9
|
+
warn Kernel.caller.first + ': `include Verbalize` is deprecated and will be removed in Verbalize v3.0; ' \
|
10
|
+
'include `Verbalize::Action` instead'
|
11
|
+
target.include Verbalize::Action
|
93
12
|
end
|
94
13
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require_relative 'error'
|
2
|
+
require_relative 'build_initialize_method'
|
3
|
+
require_relative 'build_safe_action_method'
|
4
|
+
require_relative 'build_dangerous_action_method'
|
5
|
+
require_relative 'build_attribute_readers'
|
6
|
+
require_relative 'success'
|
7
|
+
require_relative 'failure'
|
8
|
+
|
9
|
+
module Verbalize
|
10
|
+
module Action
|
11
|
+
THROWN_SYMBOL = :verbalize_error
|
12
|
+
|
13
|
+
def fail!(failure_value = nil)
|
14
|
+
throw(THROWN_SYMBOL, failure_value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.included(target)
|
18
|
+
target.extend ClassMethods
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def verbalize(*arguments, **keyword_arguments)
|
23
|
+
method_name, *arguments = arguments
|
24
|
+
input(*arguments, method_name: method_name, **keyword_arguments)
|
25
|
+
end
|
26
|
+
|
27
|
+
def input( # rubocop:disable Metrics/MethodLength
|
28
|
+
*required_keywords,
|
29
|
+
optional: [],
|
30
|
+
method_name: :call,
|
31
|
+
**other_keyword_arguments
|
32
|
+
)
|
33
|
+
|
34
|
+
deprecate_custom_methods(method_name)
|
35
|
+
|
36
|
+
unless other_keyword_arguments.empty?
|
37
|
+
raise(
|
38
|
+
ArgumentError,
|
39
|
+
"Unsupported keyword arguments received: #{other_keyword_arguments.inspect}"
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
optional_keywords = Array(optional)
|
44
|
+
|
45
|
+
class_eval BuildSafeActionMethod.call(
|
46
|
+
required_keywords: required_keywords,
|
47
|
+
optional_keywords: optional_keywords,
|
48
|
+
method_name: method_name
|
49
|
+
)
|
50
|
+
|
51
|
+
class_eval BuildDangerousActionMethod.call(
|
52
|
+
required_keywords: required_keywords,
|
53
|
+
optional_keywords: optional_keywords,
|
54
|
+
method_name: method_name
|
55
|
+
)
|
56
|
+
|
57
|
+
class_eval BuildInitializeMethod.call(
|
58
|
+
required_keywords: required_keywords,
|
59
|
+
optional_keywords: optional_keywords
|
60
|
+
)
|
61
|
+
|
62
|
+
class_eval BuildAttributeReaders.call(
|
63
|
+
attributes: required_keywords + optional_keywords
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def call
|
68
|
+
__verbalized_send(:call)
|
69
|
+
end
|
70
|
+
|
71
|
+
def call!
|
72
|
+
__verbalized_send!(:call)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def deprecate_custom_methods(method_name)
|
78
|
+
return if method_name == :call
|
79
|
+
warn Kernel.caller[2] + ': use of custom method names for Actions is deprecated and support ' \
|
80
|
+
'for it will be dropped in Verbalize v2.0. The `verbalize` method will also be removed.' \
|
81
|
+
'Use `input` and define `#call` on your Action class instead.'
|
82
|
+
end
|
83
|
+
|
84
|
+
def __verbalized_send(method_name, *args)
|
85
|
+
error = catch(:verbalize_error) do
|
86
|
+
value = new(*args).send(method_name)
|
87
|
+
return Success.new(value)
|
88
|
+
end
|
89
|
+
|
90
|
+
Failure.new(error)
|
91
|
+
end
|
92
|
+
|
93
|
+
def __verbalized_send!(method_name, *args)
|
94
|
+
new(*args).send(method_name)
|
95
|
+
rescue UncaughtThrowError => uncaught_throw_error
|
96
|
+
fail_value = uncaught_throw_error.value
|
97
|
+
error = VerbalizeError.new("Unhandled fail! called with: #{fail_value.inspect}.")
|
98
|
+
error.set_backtrace(uncaught_throw_error.backtrace[2..-1])
|
99
|
+
raise error
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'result'
|
2
|
+
|
3
|
+
module Verbalize
|
4
|
+
class Failure < Result
|
5
|
+
extend Gem::Deprecate
|
6
|
+
|
7
|
+
def initialize(failure)
|
8
|
+
super(outcome: :error, value: failure)
|
9
|
+
end
|
10
|
+
|
11
|
+
def failure
|
12
|
+
@value
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
warn Kernel.caller.first + ': `Verbalize::Failure#value` is deprecated; use `Verbalize::Failure#failure` '\
|
17
|
+
'instead when explicitly handling failures. `Verbalize::Failure#value` will raise an exception in Verbalize '\
|
18
|
+
'2.0 to prevent accidental use of `#value` on failure results without explicit error handling. '
|
19
|
+
@value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/verbalize/result.rb
CHANGED
@@ -5,7 +5,7 @@ module Verbalize
|
|
5
5
|
@value = value
|
6
6
|
end
|
7
7
|
|
8
|
-
attr_reader :outcome
|
8
|
+
attr_reader :outcome
|
9
9
|
|
10
10
|
def succeeded?
|
11
11
|
!failed?
|
@@ -18,7 +18,13 @@ module Verbalize
|
|
18
18
|
alias_method :failure?, :failed?
|
19
19
|
|
20
20
|
def to_ary
|
21
|
-
[outcome, value]
|
21
|
+
[outcome, @value]
|
22
|
+
end
|
23
|
+
|
24
|
+
def value
|
25
|
+
warn Kernel.caller.first + ': `Verbalize::Result#value` is deprecated and will be removed in Verbalize 2.0. '\
|
26
|
+
'Use `Verbalize::Failure#error` or `Verbalize::Success#value` instead.'
|
27
|
+
@value
|
22
28
|
end
|
23
29
|
end
|
24
30
|
end
|
data/lib/verbalize/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verbalize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Taylor
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -113,12 +113,16 @@ files:
|
|
113
113
|
- bin/setup
|
114
114
|
- circle.yml
|
115
115
|
- lib/verbalize.rb
|
116
|
+
- lib/verbalize/action.rb
|
116
117
|
- lib/verbalize/build_attribute_readers.rb
|
117
118
|
- lib/verbalize/build_dangerous_action_method.rb
|
118
119
|
- lib/verbalize/build_initialize_method.rb
|
119
120
|
- lib/verbalize/build_method_base.rb
|
120
121
|
- lib/verbalize/build_safe_action_method.rb
|
122
|
+
- lib/verbalize/error.rb
|
123
|
+
- lib/verbalize/failure.rb
|
121
124
|
- lib/verbalize/result.rb
|
125
|
+
- lib/verbalize/success.rb
|
122
126
|
- lib/verbalize/version.rb
|
123
127
|
- verbalize.gemspec
|
124
128
|
homepage: https://github.com/taylorzr/verbalize
|