verbalize 1.4.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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