delegate_matcher 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0ec0997475eaaee1d410562d34021316d878821a
4
+ data.tar.gz: ec92634564aabfac3448516ba0cdfd16051df08b
5
+ SHA512:
6
+ metadata.gz: 51ffc33cf2e980067e1647af487efc778ba776e5224e651d02a4d46edb6abdb7b071da44465847e8222eff8f77dd1aea1428837cd324c8c30e40821cc6cb2856
7
+ data.tar.gz: a5e1996cf241039408c03f2e3ffd69e132aeb563ecc9e684b5df2d889d30c586d4814be6f0a69711fc1bca6b736a8683d390d3611a100437c3217d45c2dc6f44
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /.idea/
2
+ /coverage/
3
+ /pkg/
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ Metrics/LineLength:
2
+ Max: 160
3
+
4
+ Metrics/CyclomaticComplexity:
5
+ Max: 10
6
+
7
+ Metrics/MethodLength:
8
+ Max: 15
9
+
10
+ Style/SingleSpaceBeforeFirstArg:
11
+ Enabled: false
12
+
13
+ Style/ExtraSpacing:
14
+ Enabled: false
15
+
16
+ Style/Documentation:
17
+ Enabled: false
18
+
19
+ Style/Semicolon:
20
+ Enabled: false
21
+
22
+ Lint/Eval:
23
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,87 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ delegate_matcher (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (4.2.3)
10
+ i18n (~> 0.7)
11
+ json (~> 1.7, >= 1.7.7)
12
+ minitest (~> 5.1)
13
+ thread_safe (~> 0.3, >= 0.3.4)
14
+ tzinfo (~> 1.1)
15
+ ast (2.1.0)
16
+ astrolabe (1.3.1)
17
+ parser (~> 2.2)
18
+ coveralls (0.7.2)
19
+ multi_json (~> 1.3)
20
+ rest-client (= 1.6.7)
21
+ simplecov (>= 0.7)
22
+ term-ansicolor (= 1.2.2)
23
+ thor (= 0.18.1)
24
+ diff-lcs (1.2.5)
25
+ docile (1.1.5)
26
+ i18n (0.7.0)
27
+ json (1.8.3)
28
+ mime-types (2.4.3)
29
+ minitest (5.7.0)
30
+ multi_json (1.10.1)
31
+ parser (2.2.2.6)
32
+ ast (>= 1.1, < 3.0)
33
+ powerpack (0.1.1)
34
+ rainbow (2.0.0)
35
+ rest-client (1.6.7)
36
+ mime-types (>= 1.16)
37
+ rspec (3.3.0)
38
+ rspec-core (~> 3.3.0)
39
+ rspec-expectations (~> 3.3.0)
40
+ rspec-mocks (~> 3.3.0)
41
+ rspec-core (3.3.2)
42
+ rspec-support (~> 3.3.0)
43
+ rspec-expectations (3.3.1)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.3.0)
46
+ rspec-its (1.1.0)
47
+ rspec-core (>= 3.0.0)
48
+ rspec-expectations (>= 3.0.0)
49
+ rspec-mocks (3.3.2)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.3.0)
52
+ rspec-support (3.3.0)
53
+ rubocop (0.33.0)
54
+ astrolabe (~> 1.3)
55
+ parser (>= 2.2.2.5, < 3.0)
56
+ powerpack (~> 0.1)
57
+ rainbow (>= 1.99.1, < 3.0)
58
+ ruby-progressbar (~> 1.4)
59
+ ruby-progressbar (1.7.5)
60
+ simplecov (0.9.1)
61
+ docile (~> 1.1.0)
62
+ multi_json (~> 1.0)
63
+ simplecov-html (~> 0.8.0)
64
+ simplecov-html (0.8.0)
65
+ term-ansicolor (1.2.2)
66
+ tins (~> 0.8)
67
+ thor (0.18.1)
68
+ thread_safe (0.3.5)
69
+ tins (0.13.2)
70
+ tzinfo (1.2.2)
71
+ thread_safe (~> 0.1)
72
+
73
+ PLATFORMS
74
+ ruby
75
+
76
+ DEPENDENCIES
77
+ activesupport (~> 4.2)
78
+ bundler (~> 1.7)
79
+ coveralls (~> 0.7)
80
+ delegate_matcher!
81
+ rspec (~> 3.0)
82
+ rspec-its (~> 1.1)
83
+ rubocop (~> 0.30)
84
+ simplecov (~> 0.9)
85
+
86
+ BUNDLED WITH
87
+ 1.10.6
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Declan Whelan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,237 @@
1
+ # Delegate Matcher
2
+
3
+ An RSpec matcher for validating delegation. This matcher works with delegation based on the [Forwardable](http://ruby-doc.org/stdlib-2.0.0/libdoc/forwardable/rdoc/Forwardable.html) module,
4
+ the [delegate](http://api.rubyonrails.org/classes/Module.html#method-i-delegate) method in the Active Support gem or with
5
+ simple custom delegation.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'delegate_matcher'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install delegate_matcher
25
+ ```
26
+
27
+ Then add the following to your ```spec_helper.rb``` file:
28
+
29
+
30
+ ```ruby
31
+ require 'delegate_matcher'
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ This matcher allows you to validate delegation to:
37
+ * instance methods
38
+ * class methods
39
+ * instance variables
40
+ * class variables
41
+ * constants
42
+ * arbitrary objects
43
+
44
+ ```ruby
45
+ describe Post do
46
+ it { should delegate(:name).to(:author) } # name => author().name instance method
47
+ it { should delegate(:name).to(:class)) } # name => self.class.name class method
48
+ it { should delegate(:name).to(:@author) } # name => @author.name instance variable
49
+ it { should delegate(:name).to(:@@author) } # name => @@author.name class variable
50
+ it { should delegate(:first).to(:GENRES) } # first => GENRES.first constant
51
+ it { should delegate(:name).to(author) } # name => author.name object
52
+ end
53
+ ```
54
+
55
+ ### Delegate Method Name
56
+
57
+ If the name of the method being invoked on the delegate is different from the method being called you
58
+ can check this using the ```with_prefix``` method (based on Active Support ```delegate``` method) or the
59
+ ```as``` method.
60
+
61
+ ```ruby
62
+ describe Post do
63
+ it { should delegate(:name).to(author).with_prefix } # author_name => author.name
64
+ it { should delegate(:name).to(author).with_prefix(:writer) } # writer_name => author.name
65
+ it { should delegate(:writer).to(author).as(:name) } # writer => author.name
66
+ end
67
+ ```
68
+
69
+ ### Handling Nil Delegates
70
+
71
+ If you expect the delegate to return ```nil``` when the delegate is ```nil``` rather than raising an error
72
+ then you can check this using the ```allow_nil``` method.
73
+
74
+ ```ruby
75
+ describe Post do
76
+ it { should delegate(:name).to(:author).allow_nil } # name => author && author.name
77
+ it { should delegate(:name).to(:author).allow_nil(true) } # name => author && author.name
78
+ it { should delegate(:name).to(:author).allow_nil(false) } # name => author.name
79
+ end
80
+ ```
81
+
82
+ Nil handling is only checked if ```allow_nil``` is specified.
83
+
84
+ Note that matcher will raise an error if you use this when checking delegation to a constant or an
85
+ object since the matcher cannot validate ```nil``` handling in these cases.
86
+
87
+ ### Arguments
88
+
89
+ If the method being delegated takes arguments then you can supply them with the ```with``` method. The matcher
90
+ will ensure that the provided arguments are in turn passed to the delegate.
91
+
92
+ ```ruby
93
+ describe Post do
94
+ it { should delegate(:name).with('Ms.')to(:author) } # name('Ms.') => author.name('Ms.')
95
+ end
96
+ ```
97
+
98
+ Also, in some cases the delegator might make minor changes to the arguments. While this is arguably no
99
+ longer true delegation you can still check that arguments are correctly passed by using a second ```with```
100
+ method to specify the arguments expected by the delegate.
101
+
102
+ ```ruby
103
+ describe Post do
104
+ it { should delegate(:name).with('Ms.')to(author).with('Miss') } # name('Ms.') => author.name('Miss')
105
+ end
106
+ ```
107
+
108
+ ### Blocks
109
+
110
+ You can check that a block passed is in turn passed to the delegate via the ```with_block``` method.
111
+
112
+ ```ruby
113
+ describe Post do
114
+ it { should delegate(:name).to(author).with_a_block } # name(&block) => author.name(&block)
115
+ it { should delegate(:name).to(author).with_block } # name(&block) => author.name(&block) alias for with_a_block
116
+
117
+ it { should delegate(:name).to(author).without_a_block } # name(&block) => author.name
118
+ it { should delegate(:name).to(author).without_block } # name(&block) => author.name alias for without_a_block
119
+ end
120
+ ```
121
+
122
+ By default, block delegated is only check if ```with_a_block``` or ```without_a_block``` is specified.
123
+
124
+ ### Active Support
125
+
126
+ You can test delegation based on the [delegate](http://api.rubyonrails.org/classes/Module.html#method-i-delegate) method in the Active Support gem.
127
+
128
+ ```ruby
129
+ class Post
130
+ attr_accessor :author
131
+
132
+ class_variable_set(:@@authors, ['Ann Rand', 'Catherine Asaro'])
133
+ GENRES ||= ['Fiction', 'Science Fiction']
134
+
135
+ delegate :name, to: :author
136
+ delegate :name, to: :author, prefix: true
137
+ delegate :name, to: :author, prefix: :writer
138
+ delegate :name_with_nil_check, to: :author, allow_nil: true
139
+ delegate :name_with_arg, to: :author
140
+ delegate :name_with_block, to: :author
141
+ delegate :count, to: :@@authors
142
+ delegate :first, to: :GENRES
143
+ delegate :name, to: :class, prefix: true
144
+ end
145
+
146
+ class Author
147
+ def name
148
+ 'Catherine Asaro'
149
+ end
150
+
151
+ def name_with_nil_check
152
+ name
153
+ end
154
+
155
+ def name_with_arg(arg)
156
+ "#{arg} #{name}"
157
+ end
158
+
159
+ def name_with_block(&block)
160
+ "#{block.call} #{name}"
161
+ end
162
+ end
163
+
164
+ describe Post do
165
+ it { should delegate(:name).to(:author) }
166
+ it { should delegate(:name).to(:@author) }
167
+ it { should delegate(:name_with_nil_check).to(:author).allow_nil }
168
+ it { should delegate(:name).to(:author).with_prefix }
169
+ it { should delegate(:name).to(:author).with_prefix(:writer) }
170
+
171
+ it { should delegate(:name_with_arg).to(:author).with('Ms.') }
172
+ it { should delegate(:name_with_block).to(:author).with_block }
173
+ it { should delegate(:count).to(:@@authors) }
174
+ it { should delegate(:first).to(:GENRES) }
175
+ it { should delegate(:name).to(:class).with_prefix }
176
+ end
177
+ ```
178
+ However, don't use the following features as they are not supported by the delegate method:
179
+ * delegation to objects
180
+ * different arguments passed to delegate
181
+
182
+ ### Forwardable Module
183
+
184
+ You can test delegation based on the [Forwardable](http://ruby-doc.org/stdlib-2.0.0/libdoc/forwardable/rdoc/Forwardable.html) module.
185
+
186
+ ```ruby
187
+ class Post
188
+ extend Forwardable
189
+
190
+ attr_accessor :author
191
+
192
+ def_delegator :author, :name
193
+ def_delegator :author, :name, :writer
194
+ def_delegator :author, :name_with_arg
195
+ def_delegator :author, :name_with_block
196
+ end
197
+
198
+ class Author
199
+ def name
200
+ 'Catherine Asaro'
201
+ end
202
+
203
+ def name_with_arg(arg)
204
+ "#{arg} #{name}"
205
+ end
206
+
207
+ def name_with_block(&block)
208
+ "#{block.call} #{name}"
209
+ end
210
+ end
211
+
212
+ describe Post do
213
+ it { should delegate(:name).to(:author) }
214
+ it { should delegate(:name).to(:@author) }
215
+ it { should delegate(:writer).to(:author).as(:name) }
216
+ it { should delegate(:name_with_arg).to(:author).with('Ms.') }
217
+ it { should delegate(:name_with_block).to(:author).with_block }
218
+ end
219
+ ```
220
+ However, don't use the following features as they are not supported by the Forwardable module:
221
+ * allow_nil
222
+ * delegation to class variables
223
+ * delegation to constants
224
+ * delegation to objects
225
+ * different arguments passed to delegate
226
+
227
+ ## Contributing
228
+
229
+ 1. Fork it ( https://github.com/dwhelan/delegate_matcher/fork )
230
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
231
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
232
+ 4. Push to the branch (`git push origin my-new-feature`)
233
+ 5. Create a new Pull Request
234
+
235
+ ## Notes
236
+
237
+ This matcher was inspired by [Alan Winograd](https://github.com/awinograd) via the gist https://gist.github.com/awinograd/6158961
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: [:spec, :rubocop, :build]
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'delegate_matcher/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = 'delegate_matcher'
9
+ gem.version = DelegateMatcher::VERSION
10
+ gem.authors = ['Declan Whelan']
11
+ gem.email = ['declan@pleanintuit.com']
12
+ gem.summary = 'A matcher for testing ruby delegation.'
13
+ gem.description = 'A matcher for testing ruby delegation.'
14
+ gem.homepage = 'https://github.com/dwhelan/delegate_matcher'
15
+ gem.license = 'MIT'
16
+
17
+ gem.files = `git ls-files -z`.split("\x0")
18
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^spec/})
20
+ gem.require_paths = ['lib']
21
+
22
+ gem.add_development_dependency 'activesupport', '~> 4.2'
23
+ gem.add_development_dependency 'bundler', '~> 1.7'
24
+ gem.add_development_dependency 'coveralls', '~> 0.7'
25
+ gem.add_development_dependency 'rspec', '~> 3.0'
26
+ gem.add_development_dependency 'rspec-its', '~> 1.1'
27
+ gem.add_development_dependency 'rubocop', '~> 0.30'
28
+ gem.add_development_dependency 'simplecov', '~> 0.9'
29
+ end
@@ -0,0 +1,281 @@
1
+ RSpec::Matchers.define(:delegate) do |method|
2
+ match do |delegator|
3
+ fail 'need to provide a "to"' unless delegate
4
+
5
+ @method = method
6
+ @delegator = delegator
7
+
8
+ allow_nil_ok? && delegate? && arguments_ok? && block_ok?
9
+ end
10
+
11
+ description do
12
+ "delegate #{delegator_description} to #{delegate_description}#{nil_description}#{block_description}"
13
+ end
14
+
15
+ def failure_message
16
+ message = failure_message_details(false)
17
+ message.empty? ? super : message
18
+ end
19
+
20
+ def failure_message_when_negated
21
+ message = failure_message_details(true)
22
+ message.empty? ? super : message
23
+ end
24
+
25
+ chain(:to) { |delegate| @delegate = delegate }
26
+ chain(:as) { |delegate_method| @delegate_method = delegate_method }
27
+ chain(:allow_nil) { |allow_nil = true| @expected_nil_check = allow_nil }
28
+ chain(:with_prefix) { |prefix = nil| @prefix = prefix || delegate.to_s.sub(/@/, '') }
29
+ chain(:with) { |*args| @expected_args = args; @args ||= args }
30
+ chain(:with_a_block) { @expected_block = true }
31
+ chain(:without_a_block) { @expected_block = false }
32
+
33
+ alias_method :with_block, :with_a_block
34
+ alias_method :without_block, :without_a_block
35
+
36
+ private
37
+
38
+ attr_reader :method, :delegator, :delegate, :prefix, :args
39
+ attr_reader :expected_nil_check, :actual_nil_check
40
+ attr_reader :expected_args, :actual_args
41
+ attr_reader :expected_block, :actual_block
42
+ attr_reader :actual_return_value
43
+
44
+ def delegate?(test_delegate = delegate_double)
45
+ case
46
+ when delegate_is_a_class_variable?
47
+ delegate_to_class_variable(test_delegate)
48
+ when delegate_is_an_instance_variable?
49
+ delegate_to_instance_variable(test_delegate)
50
+ when delegate_is_a_constant?
51
+ delegate_to_constant
52
+ when delegate_is_a_method?
53
+ delegate_to_method(test_delegate)
54
+ else
55
+ delegate_to_object
56
+ end
57
+
58
+ return_value_ok?
59
+ end
60
+
61
+ def delegate_is_a_class_variable?
62
+ delegate.to_s.start_with?('@@')
63
+ end
64
+
65
+ def delegate_is_an_instance_variable?
66
+ delegate.to_s[0] == '@'
67
+ end
68
+
69
+ def delegate_is_a_constant?
70
+ (delegate.is_a?(String) || delegate.is_a?(Symbol)) && (delegate.to_s =~ /^[A-Z]/)
71
+ end
72
+
73
+ def delegate_is_a_method?
74
+ delegate.is_a?(String) || delegate.is_a?(Symbol)
75
+ end
76
+
77
+ def delegate_to_class_variable(test_delegate)
78
+ actual_delegate = delegator.class.class_variable_get(delegate)
79
+ delegator.class.class_variable_set(delegate, test_delegate)
80
+ call
81
+ ensure
82
+ delegator.class.class_variable_set(delegate, actual_delegate)
83
+ end
84
+
85
+ def delegate_to_instance_variable(test_delegate)
86
+ actual_delegate = delegator.instance_variable_get(delegate)
87
+ delegator.instance_variable_set(delegate, test_delegate)
88
+ call
89
+ ensure
90
+ delegator.instance_variable_set(delegate, actual_delegate)
91
+ end
92
+
93
+ def delegate_to_constant
94
+ ensure_allow_nil_is_not_specified_for('a constant')
95
+ stub_delegation(delegator.class.const_get(delegate))
96
+ call
97
+ end
98
+
99
+ def delegate_to_method(test_delegate)
100
+ ensure_delegate_method_is_valid
101
+ allow(delegator).to receive(delegate) { test_delegate }
102
+ call
103
+ end
104
+
105
+ def delegate_to_object
106
+ ensure_allow_nil_is_not_specified_for('an object')
107
+ stub_delegation(delegate)
108
+ call
109
+ end
110
+
111
+ def ensure_allow_nil_is_not_specified_for(target)
112
+ fail %(cannot verify "allow_nil" expectations when delegating to #{target}) unless expected_nil_check.nil?
113
+ end
114
+
115
+ def ensure_delegate_method_is_valid
116
+ fail "#{delegator} does not respond to #{delegate}" unless delegator.respond_to?(delegate, true)
117
+ fail "#{delegator}'s' #{delegate} method expects parameters" unless [0, -1].include?(delegator.method(delegate).arity)
118
+ end
119
+
120
+ def delegator_method
121
+ @delegator_method || (prefix ? :"#{prefix}_#{method}" : method)
122
+ end
123
+
124
+ def delegate_method
125
+ @delegate_method || method
126
+ end
127
+
128
+ def call
129
+ @actual_return_value = delegator.send(delegator_method, *args, &block)
130
+ end
131
+
132
+ def block
133
+ @block ||= proc {}
134
+ end
135
+
136
+ def delegate_double
137
+ double('delegate').tap { |delegate| stub_delegation(delegate) }
138
+ end
139
+
140
+ def stub_delegation(delegate)
141
+ @delegated = false
142
+ allow(delegate).to(receive(delegate_method)) do |*args, &block|
143
+ @actual_args = args
144
+ @actual_block = block
145
+ @delegated = true
146
+ expected_return_value
147
+ end
148
+ end
149
+
150
+ def expected_return_value
151
+ self
152
+ end
153
+
154
+ def allow_nil_ok?
155
+ return true if expected_nil_check.nil?
156
+ return true unless delegate.is_a?(String) || delegate.is_a?(Symbol)
157
+
158
+ begin
159
+ actual_nil_check = true
160
+ delegate?(nil)
161
+ @return_value_when_delegate_nil = actual_return_value
162
+ rescue NoMethodError
163
+ actual_nil_check = false
164
+ end
165
+
166
+ expected_nil_check == actual_nil_check && @return_value_when_delegate_nil.nil?
167
+ end
168
+
169
+ def arguments_ok?
170
+ expected_args.nil? || actual_args.eql?(expected_args)
171
+ end
172
+
173
+ def block_ok?
174
+ case
175
+ when expected_block.nil?
176
+ true
177
+ when expected_block
178
+ actual_block == block
179
+ else
180
+ actual_block.nil?
181
+ end
182
+ end
183
+
184
+ def return_value_ok?
185
+ actual_return_value == expected_return_value
186
+ end
187
+
188
+ def delegator_description
189
+ "#{delegator_method}#{argument_description(args)}"
190
+ end
191
+
192
+ def delegate_description
193
+ case
194
+ when !args.eql?(expected_args)
195
+ "#{delegate}.#{delegate_method}#{argument_description(expected_args)}"
196
+ when delegate_method.eql?(delegator_method)
197
+ "#{delegate}"
198
+ else
199
+ "#{delegate}.#{delegate_method}"
200
+ end
201
+ end
202
+
203
+ def argument_description(args)
204
+ args ? "(#{args.map { |a| format('%p', a) }.join(', ')})" : ''
205
+ end
206
+
207
+ def nil_description
208
+ case
209
+ when expected_nil_check.nil?
210
+ ''
211
+ when expected_nil_check
212
+ ' with nil allowed'
213
+ else
214
+ ' with nil not allowed'
215
+ end
216
+ end
217
+
218
+ def block_description
219
+ case
220
+ when expected_block.nil?
221
+ ''
222
+ when expected_block
223
+ ' with a block'
224
+ else
225
+ ' without a block'
226
+ end
227
+ end
228
+
229
+ def failure_message_details(negated)
230
+ [
231
+ argument_failure_message(negated),
232
+ block_failure_message(negated),
233
+ return_value_failure_message(negated),
234
+ allow_nil_failure_message(negated)
235
+ ].reject(&:empty?).join(' and ')
236
+ end
237
+
238
+ def argument_failure_message(negated)
239
+ case
240
+ when expected_args.nil? || negated ^ arguments_ok?
241
+ ''
242
+ else
243
+ "was called with #{argument_description(actual_args)}"
244
+ end
245
+ end
246
+
247
+ def block_failure_message(negated)
248
+ case
249
+ when expected_block.nil? || (negated ^ block_ok?)
250
+ ''
251
+ when negated
252
+ "a block was #{expected_block ? '' : 'not '}passed"
253
+ when expected_block
254
+ actual_block.nil? ? 'a block was not passed' : "a different block #{actual_block} was passed"
255
+ else
256
+ 'a block was passed'
257
+ end
258
+ end
259
+
260
+ def return_value_failure_message(_negated)
261
+ case
262
+ when !@delegated || return_value_ok?
263
+ ''
264
+ else
265
+ format('a return value of %p was returned instead of the delegate return value', actual_return_value)
266
+ end
267
+ end
268
+
269
+ def allow_nil_failure_message(negated)
270
+ case
271
+ when expected_nil_check.nil? || negated ^ allow_nil_ok?
272
+ ''
273
+ when !@return_value_when_delegate_nil.nil?
274
+ 'did not return nil'
275
+ when negated
276
+ "#{delegate} was #{expected_nil_check ? '' : 'not '}allowed to be nil"
277
+ else
278
+ "#{delegate} was #{expected_nil_check ? 'not ' : ''}allowed to be nil"
279
+ end
280
+ end
281
+ end