verbalize 1.4.1 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d3d8f53fd6b37b6d5729c0c73d9fabb5cfe3c0b
4
- data.tar.gz: 22afc7cd81eb619e8dd393f4b606824e479b2780
3
+ metadata.gz: ca403601a9b7decbc85e77a20103926c8d92765a
4
+ data.tar.gz: ad015b2d9a8bfa2afa3e9524be0bc8dcfe4260d8
5
5
  SHA512:
6
- metadata.gz: 771d473b91c42ff8f3c58a12c847eff35969b25aae89141e2dbb9dab4f063112a3552b991ac3174e913c2aab2b4362c8b3db4c95962c666cf446390339c2cc63
7
- data.tar.gz: 01b65780f3f72a326d8c9b185dfba2ef257ec50bbe29abb16778cd9b9fde468fdd5cb67cd708d86a43618bf3982fdb7b5a8d5b81187905156e317018411b65f1
6
+ metadata.gz: 10d7fcf1fda2c00aa8b12d571858595222bc329d8714234ffc4d52a247e31c66fa183e5c0da9d52475a88ca76de7c407a9d6ced73c30aa9dbe2ba88b8a103d38
7
+ data.tar.gz: c0699fbe4b2c5da4e8f40f143058c552e90c2ffb421ebbc42946d138269c6b7f16d5167cb783885239e76d8ac4e2ba86fa22a2cad2ff01657690b9c4afb06ac2
data/README.md CHANGED
@@ -179,8 +179,10 @@ end
179
179
  ### Sad Path
180
180
 
181
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:
182
+ class method which will return a `Verbalize::Failure` on failure.
183
+
184
+ Use of `call!` here is not advised as it will result in an exception being thrown. Set assertions on both
185
+ the failure outcome and value:
184
186
 
185
187
  ```ruby
186
188
  class MyAction
@@ -199,95 +201,75 @@ it 'fails when the input is out of bounds' do
199
201
  result = MyAction.call(a: 1000)
200
202
 
201
203
  expect(result).to be_failed
202
- expect(result.value).to eq '1000 is greater than 100!'
204
+ expect(result.failure).to eq '1000 is greater than 100!'
203
205
  end
204
206
  ```
205
207
 
206
208
  ### Stubbing Responses
207
209
 
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
+ When unit testing, it may be necessary to stub the responses of Verbalize actions. To correctly stub responses,
211
+ you should __always__ stub the `MyAction.perform` class method on the action class being stubbed per the
212
+ instructions below. __Never__ stub the `call` or `call!` methods directly.
213
+
214
+ Stubbing `.perform` will enable `Verbalize` to wrap results correctly for references to either `call` or `call!`.
210
215
 
211
- #### Stubbing `call`
216
+ #### Stubbing Successful Responses
212
217
 
213
- When an object calls a `Verbalize::Action` via `call`, you can stub the response with a `Verbalize::Success` or
214
- `Verbalize::Failure` instance.
218
+ To simulate a successful response of the `Verbalize::Action` being stubbed, you should stub the `MyAction.perform`
219
+ class method to return the __value__ you expect the `MyAction#call` instance method to return.
215
220
 
216
- Example:
221
+ For example, if you expect the action to return the value `123` on success:
217
222
 
218
223
  ```ruby
219
224
  class Foo
220
- def do_something
225
+ def self.multiply_by(multiple)
221
226
  result = MyAction.call(a: 1)
222
- raise 'I couldn\'t do the thing!' if result.failure?
227
+ raise "I couldn't do the thing!" if result.failure?
223
228
 
224
- 'baz'
229
+ result.value * multiple
225
230
  end
226
231
  end
227
232
 
228
233
  # rspec:
229
234
  describe Foo do
230
235
  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
236
+ it 'does the thing when MyAction succeeds' do
237
+ # Simulate the successful result
238
+ allow(MyAction).to receive(:perform)
239
+ .with(a: 1)
240
+ .and_return(123)
240
241
 
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)
242
+ result = described_class.multiply_by(100)
249
243
 
250
- expect {
251
- described_class.do_something
252
- }.to raise_error(StandardError, 'I couldnt do the thing!')
244
+ expect(result).to eq 12300
253
245
  end
254
246
  end
255
247
  end
256
248
  ```
257
249
 
258
- #### Stubbing `call!`
250
+ #### Stubbing Failure Responses
259
251
 
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):
252
+ To simulate a __failure__ response of the `Verbalize::Action` being stubbed, you should stub the `MyAction.perform`
253
+ class method to __throw__ `::Verbalize::THROWN_SYMBOL` with the __message__ you expect `MyAction#call` to throw
254
+ when the simulated failure occurs.
263
255
 
264
- Example:
256
+ For example, when you expect the outer class to raise an exception when MyAction fails:
265
257
 
266
258
  ```ruby
259
+ # See also: Foo class definition in Stubbing Successful Responses above
260
+
267
261
  # rspec:
268
262
  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')
263
+ describe '#multiply_by' do
264
+ it 'raises an error when MyAction fails' do
265
+ # Simulate the failure
266
+ allow(MyAction).to receive(:perform)
267
+ .with(a: 1)
268
+ .and_throw(::Verbalize::THROWN_SYMBOL, 'Y U NO!')
287
269
 
288
270
  expect {
289
- described_class.something
290
- }.to raise_error UncaughtThrowError
271
+ described_class.multiply_by(100)
272
+ }.to raise_error "I couldn't do the thing!"
291
273
  end
292
274
  end
293
275
  end
@@ -19,20 +19,11 @@ module Verbalize
19
19
  end
20
20
 
21
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
22
  def input( # rubocop:disable Metrics/MethodLength
28
23
  *required_keywords,
29
- optional: [],
30
- method_name: :call,
24
+ optional: [],
31
25
  **other_keyword_arguments
32
26
  )
33
-
34
- deprecate_custom_methods(method_name)
35
-
36
27
  unless other_keyword_arguments.empty?
37
28
  raise(
38
29
  ArgumentError,
@@ -44,14 +35,12 @@ module Verbalize
44
35
 
45
36
  class_eval BuildSafeActionMethod.call(
46
37
  required_keywords: required_keywords,
47
- optional_keywords: optional_keywords,
48
- method_name: method_name
38
+ optional_keywords: optional_keywords
49
39
  )
50
40
 
51
41
  class_eval BuildDangerousActionMethod.call(
52
42
  required_keywords: required_keywords,
53
- optional_keywords: optional_keywords,
54
- method_name: method_name
43
+ optional_keywords: optional_keywords
55
44
  )
56
45
 
57
46
  class_eval BuildInitializeMethod.call(
@@ -65,36 +54,33 @@ module Verbalize
65
54
  end
66
55
 
67
56
  def call
68
- __verbalized_send(:call)
57
+ __verbalized_send
69
58
  end
70
59
 
71
60
  def call!
72
- __verbalized_send!(:call)
61
+ __verbalized_send!
73
62
  end
74
63
 
75
64
  private
76
65
 
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.'
66
+ def perform(*args)
67
+ new(*args).send(:call)
82
68
  end
83
69
 
84
- def __verbalized_send(method_name, *args)
70
+ def __verbalized_send(*args)
85
71
  error = catch(:verbalize_error) do
86
- value = new(*args).send(method_name)
72
+ value = perform(*args)
87
73
  return Success.new(value)
88
74
  end
89
75
 
90
76
  Failure.new(error)
91
77
  end
92
78
 
93
- def __verbalized_send!(method_name, *args)
94
- new(*args).send(method_name)
79
+ def __verbalized_send!(*args)
80
+ perform(*args)
95
81
  rescue UncaughtThrowError => uncaught_throw_error
96
82
  fail_value = uncaught_throw_error.value
97
- error = VerbalizeError.new("Unhandled fail! called with: #{fail_value.inspect}.")
83
+ error = Verbalize::Error.new("Unhandled fail! called with: #{fail_value.inspect}.")
98
84
  error.set_backtrace(uncaught_throw_error.backtrace[2..-1])
99
85
  raise error
100
86
  end
@@ -5,15 +5,11 @@ module Verbalize
5
5
  private
6
6
 
7
7
  def declaration
8
- "def self.#{method_name}!(#{declaration_keyword_arguments})"
8
+ declare('self.call!')
9
9
  end
10
10
 
11
11
  def body
12
- if all_keywords.empty?
13
- " __verbalized_send!(:#{method_name})"
14
- else
15
- " __verbalized_send!(:#{method_name}, #{initialize_keywords_and_values})"
16
- end
12
+ verbalized_send_string(bang: true)
17
13
  end
18
14
  end
19
15
  end
@@ -5,7 +5,7 @@ module Verbalize
5
5
  private
6
6
 
7
7
  def declaration
8
- "def initialize(#{declaration_keyword_arguments})"
8
+ declare('initialize')
9
9
  end
10
10
 
11
11
  def body
@@ -1,56 +1,68 @@
1
- class BuildMethodBase
2
- def self.call(required_keywords: [], optional_keywords: [], method_name: :call)
3
- new(
4
- required_keywords: required_keywords,
5
- optional_keywords: optional_keywords,
6
- method_name: method_name
7
- ).call
8
- end
1
+ module Verbalize
2
+ class BuildMethodBase
3
+ def self.call(required_keywords: [], optional_keywords: [])
4
+ new(
5
+ required_keywords: required_keywords,
6
+ optional_keywords: optional_keywords
7
+ ).call
8
+ end
9
9
 
10
- def initialize(required_keywords: [], optional_keywords: [], method_name: :call)
11
- @required_keywords = required_keywords
12
- @optional_keywords = optional_keywords
13
- @method_name = method_name
14
- end
10
+ def initialize(required_keywords: [], optional_keywords: [])
11
+ @required_keywords = required_keywords
12
+ @optional_keywords = optional_keywords
13
+ end
15
14
 
16
- def call
17
- parts.compact.join "\n"
18
- end
15
+ def call
16
+ parts.compact.join "\n"
17
+ end
19
18
 
20
- private
19
+ private
21
20
 
22
- attr_reader :required_keywords, :optional_keywords, :method_name
21
+ attr_reader :required_keywords, :optional_keywords
23
22
 
24
- def parts
25
- [declaration, body, "end\n"]
26
- end
23
+ def parts
24
+ [declaration, body, "end\n"]
25
+ end
27
26
 
28
- def declaration
29
- raise NotImplementedError
30
- end
27
+ def declaration
28
+ raise NotImplementedError
29
+ end
31
30
 
32
- def body
33
- raise NotImplementedError
34
- end
31
+ def body
32
+ raise NotImplementedError
33
+ end
35
34
 
36
- def all_keywords
37
- required_keywords + optional_keywords
38
- end
35
+ def declare(method_name)
36
+ "def #{method_name}(#{declaration_keyword_arguments})"
37
+ end
39
38
 
40
- def required_keyword_segments
41
- required_keywords.map { |keyword| "#{keyword}:" }
42
- end
39
+ def all_keywords
40
+ required_keywords + optional_keywords
41
+ end
43
42
 
44
- def optional_keyword_segments
45
- optional_keywords.map { |keyword| "#{keyword}: nil" }
46
- end
43
+ def required_keyword_segments
44
+ required_keywords.map { |keyword| "#{keyword}:" }
45
+ end
47
46
 
48
- def declaration_keyword_arguments
49
- return if all_keywords.empty?
50
- (required_keyword_segments + optional_keyword_segments).join(', ')
51
- end
47
+ def optional_keyword_segments
48
+ optional_keywords.map { |keyword| "#{keyword}: nil" }
49
+ end
50
+
51
+ def declaration_keyword_arguments
52
+ return if all_keywords.empty?
53
+ (required_keyword_segments + optional_keyword_segments).join(', ')
54
+ end
55
+
56
+ def initialize_keywords_and_values
57
+ all_keywords.map { |variable| "#{variable}: #{variable}" }.join(', ')
58
+ end
59
+
60
+ def verbalized_send_string(bang: false)
61
+ send_string = ' __verbalized_send'
62
+ send_string += '!' if bang
52
63
 
53
- def initialize_keywords_and_values
54
- all_keywords.map { |variable| "#{variable}: #{variable}" }.join(', ')
64
+ return send_string if all_keywords.empty?
65
+ send_string + "(#{initialize_keywords_and_values})"
66
+ end
55
67
  end
56
68
  end
@@ -5,15 +5,11 @@ module Verbalize
5
5
  private
6
6
 
7
7
  def declaration
8
- "def self.#{method_name}(#{declaration_keyword_arguments})"
8
+ declare('self.call')
9
9
  end
10
10
 
11
11
  def body
12
- if all_keywords.empty?
13
- " __verbalized_send(:#{method_name})"
14
- else
15
- " __verbalized_send(:#{method_name}, #{initialize_keywords_and_values})"
16
- end
12
+ verbalized_send_string(bang: false)
17
13
  end
18
14
  end
19
15
  end
@@ -1,3 +1,3 @@
1
1
  module Verbalize
2
- VerbalizeError = Class.new(StandardError)
2
+ Error = Class.new(StandardError)
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'result'
2
+ require_relative 'error'
2
3
 
3
4
  module Verbalize
4
5
  class Failure < Result
@@ -13,10 +14,9 @@ module Verbalize
13
14
  end
14
15
 
15
16
  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
17
+ raise Verbalize::Error, 'You called #value on a Failure result. You should never use `Verbalize::Action#call` ' \
18
+ 'without also explicitly handling potential errors. Please use `Verbalize::Action#call!` to return a value ' \
19
+ 'directly on successful execution of an action, or handle the error case explicitly if using `#call`.'
20
20
  end
21
21
  end
22
22
  end
@@ -22,9 +22,7 @@ module Verbalize
22
22
  end
23
23
 
24
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
25
+ raise NotImplementedError, 'Subclasses must override Verbalize::Result#value'
28
26
  end
29
27
  end
30
28
  end
@@ -1,3 +1,3 @@
1
1
  module Verbalize
2
- VERSION = '1.4.1'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: verbalize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Taylor