rspec-expectations 3.5.0 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +138 -2
  5. data/README.md +37 -20
  6. data/lib/rspec/expectations.rb +2 -1
  7. data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
  8. data/lib/rspec/expectations/configuration.rb +14 -0
  9. data/lib/rspec/expectations/expectation_target.rb +2 -2
  10. data/lib/rspec/expectations/fail_with.rb +9 -1
  11. data/lib/rspec/expectations/handler.rb +2 -2
  12. data/lib/rspec/expectations/minitest_integration.rb +1 -1
  13. data/lib/rspec/expectations/syntax.rb +2 -2
  14. data/lib/rspec/expectations/version.rb +1 -1
  15. data/lib/rspec/matchers.rb +97 -97
  16. data/lib/rspec/matchers/built_in/all.rb +1 -0
  17. data/lib/rspec/matchers/built_in/base_matcher.rb +14 -2
  18. data/lib/rspec/matchers/built_in/be.rb +2 -2
  19. data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
  20. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
  21. data/lib/rspec/matchers/built_in/change.rb +127 -53
  22. data/lib/rspec/matchers/built_in/compound.rb +6 -2
  23. data/lib/rspec/matchers/built_in/contain_exactly.rb +18 -2
  24. data/lib/rspec/matchers/built_in/exist.rb +5 -1
  25. data/lib/rspec/matchers/built_in/has.rb +1 -1
  26. data/lib/rspec/matchers/built_in/include.rb +6 -0
  27. data/lib/rspec/matchers/built_in/raise_error.rb +1 -1
  28. data/lib/rspec/matchers/built_in/respond_to.rb +13 -4
  29. data/lib/rspec/matchers/built_in/satisfy.rb +27 -4
  30. data/lib/rspec/matchers/built_in/yield.rb +43 -30
  31. data/lib/rspec/matchers/composable.rb +6 -20
  32. data/lib/rspec/matchers/dsl.rb +72 -4
  33. data/lib/rspec/matchers/english_phrasing.rb +3 -3
  34. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +16 -7
  35. data/lib/rspec/matchers/generated_descriptions.rb +1 -2
  36. metadata +22 -18
  37. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 564420270880a852a9f86af59b32445beff85990
4
- data.tar.gz: 68e50116cd97b1137f8ed688de17db436464b7b5
2
+ SHA256:
3
+ metadata.gz: d8cb915ab51f5b587fcbaa864e697cc291ff3c1f49afd8d829837c1a9c4c4f6b
4
+ data.tar.gz: 6aa770883cacbfcf2921074a98506a0a789b4557ee6a7ea79738f008107e582d
5
5
  SHA512:
6
- metadata.gz: 18a464c2ffba3b59d6c998419b36462177b5246fd9a66cb73c693358b9985bdffeb6fd4cafc97beff928ced458cb4e70fd78731b2e35a2e7f0aca89d4b1a0e0e
7
- data.tar.gz: b56c7c4cad9670fdf2925a700de34f1be6f14d2d197a12efba71f4f816e0a9a3f4f7c172c019b46ccab5344fe464362642ca53cbe522143fde63ad99d038978b
6
+ metadata.gz: fc2669ca822767874cc62c9e7cef32040f90da7cd0d4c13b1bd97fde68230f7f7019a8b717e2ebfcd223da5b9e25aaa60270c0797ba4d304def5e4ae3bb63f03
7
+ data.tar.gz: e9d7a9e1b186c1eee547c315f177181992f5be9e35c61a16a041d97901537d054943a779e0d6bd7f2502e51a1fcbf95f0f6a10eab46fcdd85bebd8be1170a369
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,7 +1,143 @@
1
+ ### 3.9.0 / 2019-10-02
2
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.5...v3.9.0)
3
+
4
+ Enhancements:
5
+
6
+ * The `respond_to` matcher now uses the signature from `initialize` to validate checks
7
+ for `new` (unless `new` is non standard). (Jon Rowe, #1072)
8
+ * Generated descriptions for matchers now use `is expected to` rather than `should` in
9
+ line with our preferred DSL. (Pete Johns, #1080, rspec/rspec-core#2572)
10
+ * Add the ability to re-raise expectation errors when matching
11
+ with `match_when_negated` blocks. (Jon Rowe, #1130)
12
+ * Add a warning when an empty diff is produce due to identical inspect output.
13
+ (Benoit Tigeot, #1126)
14
+
15
+ ### 3.8.6 / 2019-10-07
16
+
17
+ Bug Fixes:
18
+
19
+ * Revert #1125 due to the change being incompatible with our semantic versioning
20
+ policy.
21
+
22
+ ### 3.8.5 / 2019-10-02
23
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.4...v3.8.5)
24
+
25
+ Bug Fixes:
26
+
27
+ * Prevent unsupported implicit block expectation syntax from being used.
28
+ (Phil Pirozhkov, #1125)
29
+
30
+ ### 3.8.4 / 2019-06-10
31
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.3...v3.8.4)
32
+
33
+ Bug Fixes:
34
+
35
+ * Prevent false negatives when checking objects for the methods required to run the
36
+ the `be_an_instance_of` and `be_kind_of` matchers. (Nazar Matus, #1112)
37
+
38
+ ### 3.8.3 / 2019-04-20
39
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.2...v3.8.3)
40
+
41
+ Bug Fixes:
42
+
43
+ * Prevent composed `all` matchers from leaking into their siblings leading to duplicate
44
+ failures. (Jamie English, #1086)
45
+ * Prevent objects which change their hash on comparison from failing change checks.
46
+ (Phil Pirozhkov, #1110)
47
+ * Issue an `ArgumentError` rather than a `NoMethodError` when `be_an_instance_of` and
48
+ `be_kind_of` matchers encounter objects not supporting those methods.
49
+ (Taichi Ishitani, #1107)
50
+
51
+ ### 3.8.2 / 2018-10-09
52
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.1...v3.8.2)
53
+
54
+ Bug Fixes:
55
+
56
+ * Change `include` matcher to rely on a `respond_to?(:include?)` check rather than a direct
57
+ Hash comparison before calling `to_hash` to convert to a hash. (Jordan Owens, #1073)
58
+ * Prevent unexpected call stack jumps from causing an obscure error (`IndexError`), and
59
+ replace that error with a proper informative message. (Jon Rowe, #1076)
60
+
61
+ ### 3.8.1 / 2018-08-06
62
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.0...v3.8.1)
63
+
64
+ Bug Fixes:
65
+
66
+ * Fix regression in `include` matcher so stopped
67
+ `expect(hash.with_indifferent_access).to include(:symbol_key)`
68
+ from working. (Eito Katagiri, #1069)
69
+
70
+ ### 3.8.0 / 2018-08-04
71
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.7.0...v3.8.0)
72
+
73
+ Enhancements:
74
+
75
+ * Improve failure message of `change(receiver, :message)` by including the
76
+ receiver as `SomeClass#some_message`. (Tomohiro Hashidate, #1005)
77
+ * Improve `change` matcher so that it can correctly detect changes in
78
+ deeply nested mutable objects (such as arrays-of-hashes-of-arrays).
79
+ The improved logic uses the before/after `hash` value to see if the
80
+ object has been mutated, rather than shallow duping the object.
81
+ (Myron Marston, #1034)
82
+ * Improve `include` matcher so that pseudo-hash objects (e.g. objects
83
+ that decorate a hash using a `SimpleDelegator` or similar) are treated
84
+ as a hash, as long as they implement `to_hash`. (Pablo Brasero, #1012)
85
+ * Add `max_formatted_output_length=` to configuration, allowing changing
86
+ the length at which we truncate large output strings.
87
+ (Sam Phippen #951, Benoit Tigeot #1056)
88
+ * Improve error message when passing a matcher that doesn't support block
89
+ expectations to a block based `expect`. (@nicktime, #1066)
90
+
91
+ ### 3.7.0 / 2017-10-17
92
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.6.0...v3.7.0)
93
+
94
+ Enhancements:
95
+
96
+ * Improve compatibility with `--enable-frozen-string-literal` option
97
+ on Ruby 2.3+. (Pat Allan, #997)
98
+
99
+ ### 3.6.0 / 2017-05-04
100
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.6.0.beta2...v3.6.0)
101
+
102
+ Enhancements:
103
+
104
+ * Treat NoMethodError as a failure for comparison matchers. (Jon Rowe, #972)
105
+ * Allow for scoped aliased and negated matchers--just call
106
+ `alias_matcher` or `define_negated_matcher` from within an example
107
+ group. (Markus Reiter, #974)
108
+ * Improve failure message of `change` matcher with block and `satisfy` matcher
109
+ by including the block snippet instead of just describing it as `result` or
110
+ `block` when Ripper is available. (Yuji Nakayama, #987)
111
+
112
+ Bug Fixes:
113
+
114
+ * Fix `yield_with_args` and `yield_successive_args` matchers so that
115
+ they compare expected to actual args at the time the args are yielded
116
+ instead of at the end, in case the method that is yielding mutates the
117
+ arguments after yielding. (Alyssa Ross, #965)
118
+
119
+ ### 3.6.0.beta2 / 2016-12-12
120
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.6.0.beta1...v3.6.0.beta2)
121
+
122
+ Bug Fixes:
123
+
124
+ * Using the exist matcher on `File` no longer produces a deprecation warning.
125
+ (Jon Rowe, #954)
126
+
127
+ ### 3.6.0.beta1 / 2016-10-09
128
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.5.0...v3.6.0.beta1)
129
+
130
+ Bug Fixes:
131
+
132
+ * Fix `contain_exactly` to work correctly with ranges. (Myron Marston, #940)
133
+ * Fix `change` to work correctly with sets. (Marcin Gajewski, #939)
134
+
1
135
  ### 3.5.0 / 2016-07-01
2
136
  [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.5.0.beta4...v3.5.0)
3
137
 
4
- **No user facing changes since beta4**
138
+ Enhancements:
139
+
140
+ * Add support for keyword arguments to the `respond_to` matcher. (Rob Smith, #915).
5
141
 
6
142
  ### 3.5.0.beta4 / 2016-06-05
7
143
  [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.5.0.beta3...v3.5.0.beta4)
@@ -74,7 +210,7 @@ Bug Fixes:
74
210
 
75
211
  * Fix failure message from dynamic predicate matchers when the object
76
212
  does not respond to the predicate so that it is inspected rather
77
- than relying upon it's `to_s` -- that way for `nil`, `"nil"` is
213
+ than relying upon its `to_s` -- that way for `nil`, `"nil"` is
78
214
  printed rather than an empty string. (Myron Marston, #841)
79
215
  * Fix SystemStackError raised when diffing an Enumerable object
80
216
  whose `#each` includes the object itself. (Yuji Nakayama, #857)
data/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  RSpec::Expectations lets you express expected outcomes on an object in an
4
4
  example.
5
5
 
6
- expect(account.balance).to eq(Money.new(37.42, :USD))
6
+ ```ruby
7
+ expect(account.balance).to eq(Money.new(37.42, :USD))
8
+ ```
7
9
 
8
10
  ## Install
9
11
 
@@ -18,7 +20,7 @@ RSpec repos as well. Add the following to your `Gemfile`:
18
20
 
19
21
  ```ruby
20
22
  %w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
21
- gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => 'master'
23
+ gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'master'
22
24
  end
23
25
  ```
24
26
 
@@ -150,7 +152,7 @@ expect { |b| 5.tap(&b) }.to yield_control # passes regardless of yielded args
150
152
  expect { |b| yield_if_true(true, &b) }.to yield_with_no_args # passes only if no args are yielded
151
153
 
152
154
  expect { |b| 5.tap(&b) }.to yield_with_args(5)
153
- expect { |b| 5.tap(&b) }.to yield_with_args(Fixnum)
155
+ expect { |b| 5.tap(&b) }.to yield_with_args(Integer)
154
156
  expect { |b| "a string".tap(&b) }.to yield_with_args(/str/)
155
157
 
156
158
  expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
@@ -173,30 +175,45 @@ expect(1..10).to cover(3)
173
175
  ### Collection membership
174
176
 
175
177
  ```ruby
176
- expect(actual).to include(expected)
178
+ # exact order, entire collection
179
+ expect(actual).to eq(expected)
180
+
181
+ # exact order, partial collection (based on an exact position)
177
182
  expect(actual).to start_with(expected)
178
183
  expect(actual).to end_with(expected)
179
184
 
180
- expect(actual).to contain_exactly(individual, items)
181
- # ...which is the same as:
182
- expect(actual).to match_array(expected_array)
185
+ # any order, entire collection
186
+ expect(actual).to match_array(expected)
187
+
188
+ # You can also express this by passing the expected elements
189
+ # as individual arguments
190
+ expect(actual).to contain_exactly(expected_element1, expected_element2)
191
+
192
+ # any order, partial collection
193
+ expect(actual).to include(expected)
183
194
  ```
184
195
 
185
196
  #### Examples
186
197
 
187
198
  ```ruby
188
- expect([1, 2, 3]).to include(1)
189
- expect([1, 2, 3]).to include(1, 2)
190
- expect([1, 2, 3]).to start_with(1)
191
- expect([1, 2, 3]).to start_with(1, 2)
192
- expect([1, 2, 3]).to end_with(3)
193
- expect([1, 2, 3]).to end_with(2, 3)
194
- expect({:a => 'b'}).to include(:a => 'b')
195
- expect("this string").to include("is str")
196
- expect("this string").to start_with("this")
197
- expect("this string").to end_with("ring")
198
- expect([1, 2, 3]).to contain_exactly(2, 3, 1)
199
- expect([1, 2, 3]).to match_array([3, 2, 1])
199
+ expect([1, 2, 3]).to eq([1, 2, 3]) # Order dependent equality check
200
+ expect([1, 2, 3]).to include(1) # Exact ordering, partial collection matches
201
+ expect([1, 2, 3]).to include(2, 3) #
202
+ expect([1, 2, 3]).to start_with(1) # As above, but from the start of the collection
203
+ expect([1, 2, 3]).to start_with(1, 2) #
204
+ expect([1, 2, 3]).to end_with(3) # As above but from the end of the collection
205
+ expect([1, 2, 3]).to end_with(2, 3) #
206
+ expect({:a => 'b'}).to include(:a => 'b') # Matching within hashes
207
+ expect("this string").to include("is str") # Matching within strings
208
+ expect("this string").to start_with("this") #
209
+ expect("this string").to end_with("ring") #
210
+ expect([1, 2, 3]).to contain_exactly(2, 3, 1) # Order independent matches
211
+ expect([1, 2, 3]).to match_array([3, 2, 1]) #
212
+
213
+ # Order dependent compound matchers
214
+ expect(
215
+ [{:a => 'hash'},{:a => 'another'}]
216
+ ).to match([a_hash_including(:a => 'hash'), a_hash_including(:a => 'another')])
200
217
  ```
201
218
 
202
219
  ## `should` syntax
@@ -263,7 +280,7 @@ expect(hash).to match(
263
280
  :a => {
264
281
  :b => a_collection_containing_exactly(
265
282
  a_string_starting_with("f"),
266
- an_instance_of(Fixnum)
283
+ an_instance_of(Integer)
267
284
  ),
268
285
  :c => { :d => (a_value < 3) }
269
286
  }
@@ -76,6 +76,7 @@ module RSpec
76
76
  class MultipleExpectationsNotMetError < ExpectationNotMetError
77
77
  end
78
78
 
79
- autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
79
+ autoload :BlockSnippetExtractor, "rspec/expectations/block_snippet_extractor"
80
+ autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
80
81
  end
81
82
  end
@@ -0,0 +1,253 @@
1
+ module RSpec
2
+ module Expectations
3
+ # @private
4
+ class BlockSnippetExtractor # rubocop:disable Metrics/ClassLength
5
+ # rubocop should properly handle `Struct.new {}` as an inner class definition.
6
+
7
+ attr_reader :proc, :method_name
8
+
9
+ def self.try_extracting_single_line_body_of(proc, method_name)
10
+ lines = new(proc, method_name).body_content_lines
11
+ return nil unless lines.count == 1
12
+ lines.first
13
+ rescue Error
14
+ nil
15
+ end
16
+
17
+ def initialize(proc, method_name)
18
+ @proc = proc
19
+ @method_name = method_name.to_s.freeze
20
+ end
21
+
22
+ # Ideally we should properly handle indentations of multiline snippet,
23
+ # but it's not implemented yet since because we use result of this method only when it's a
24
+ # single line and implementing the logic introduces additional complexity.
25
+ def body_content_lines
26
+ raw_body_lines.map(&:strip).reject(&:empty?)
27
+ end
28
+
29
+ private
30
+
31
+ def raw_body_lines
32
+ raw_body_snippet.split("\n")
33
+ end
34
+
35
+ def raw_body_snippet
36
+ block_token_extractor.body_tokens.map(&:string).join
37
+ end
38
+
39
+ def block_token_extractor
40
+ @block_token_extractor ||= BlockTokenExtractor.new(method_name, source, beginning_line_number)
41
+ end
42
+
43
+ if RSpec.respond_to?(:world)
44
+ def source
45
+ raise TargetNotFoundError unless File.exist?(file_path)
46
+ RSpec.world.source_from_file(file_path)
47
+ end
48
+ else
49
+ RSpec::Support.require_rspec_support 'source'
50
+ def source
51
+ raise TargetNotFoundError unless File.exist?(file_path)
52
+ @source ||= RSpec::Support::Source.from_file(file_path)
53
+ end
54
+ end
55
+
56
+ def file_path
57
+ source_location.first
58
+ end
59
+
60
+ def beginning_line_number
61
+ source_location.last
62
+ end
63
+
64
+ def source_location
65
+ proc.source_location || raise(TargetNotFoundError)
66
+ end
67
+
68
+ Error = Class.new(StandardError)
69
+ TargetNotFoundError = Class.new(Error)
70
+ AmbiguousTargetError = Class.new(Error)
71
+
72
+ # @private
73
+ # Performs extraction of block body snippet using tokens,
74
+ # which cannot be done with node information.
75
+ BlockTokenExtractor = Struct.new(:method_name, :source, :beginning_line_number) do
76
+ attr_reader :state, :body_tokens
77
+
78
+ def initialize(*)
79
+ super
80
+ parse!
81
+ end
82
+
83
+ private
84
+
85
+ def parse!
86
+ @state = :initial
87
+
88
+ catch(:finish) do
89
+ source.tokens.each do |token|
90
+ invoke_state_handler(token)
91
+ end
92
+ end
93
+ end
94
+
95
+ def finish!
96
+ throw :finish
97
+ end
98
+
99
+ def invoke_state_handler(token)
100
+ __send__("#{state}_state", token)
101
+ end
102
+
103
+ def initial_state(token)
104
+ @state = :after_method_call if token.location == block_locator.method_call_location
105
+ end
106
+
107
+ def after_method_call_state(token)
108
+ @state = :after_opener if handle_opener_token(token)
109
+ end
110
+
111
+ def after_opener_state(token)
112
+ if handle_closer_token(token)
113
+ finish_or_find_next_block_if_incorrect!
114
+ elsif pipe_token?(token)
115
+ finalize_pending_tokens!
116
+ @state = :after_beginning_of_args
117
+ else
118
+ pending_tokens << token
119
+ handle_opener_token(token)
120
+ @state = :after_beginning_of_body unless token.type == :on_sp
121
+ end
122
+ end
123
+
124
+ def after_beginning_of_args_state(token)
125
+ @state = :after_beginning_of_body if pipe_token?(token)
126
+ end
127
+
128
+ def after_beginning_of_body_state(token)
129
+ if handle_closer_token(token)
130
+ finish_or_find_next_block_if_incorrect!
131
+ else
132
+ pending_tokens << token
133
+ handle_opener_token(token)
134
+ end
135
+ end
136
+
137
+ def pending_tokens
138
+ @pending_tokens ||= []
139
+ end
140
+
141
+ def finalize_pending_tokens!
142
+ pending_tokens.freeze.tap do
143
+ @pending_tokens = nil
144
+ end
145
+ end
146
+
147
+ def finish_or_find_next_block_if_incorrect!
148
+ body_tokens = finalize_pending_tokens!
149
+
150
+ if correct_block?(body_tokens)
151
+ @body_tokens = body_tokens
152
+ finish!
153
+ else
154
+ @state = :after_method_call
155
+ end
156
+ end
157
+
158
+ def handle_opener_token(token)
159
+ opener_token?(token).tap do |boolean|
160
+ opener_token_stack.push(token) if boolean
161
+ end
162
+ end
163
+
164
+ def opener_token?(token)
165
+ token.type == :on_lbrace || (token.type == :on_kw && token.string == 'do')
166
+ end
167
+
168
+ def handle_closer_token(token)
169
+ if opener_token_stack.last.closed_by?(token)
170
+ opener_token_stack.pop
171
+ opener_token_stack.empty?
172
+ else
173
+ false
174
+ end
175
+ end
176
+
177
+ def opener_token_stack
178
+ @opener_token_stack ||= []
179
+ end
180
+
181
+ def pipe_token?(token)
182
+ token.type == :on_op && token.string == '|'
183
+ end
184
+
185
+ def correct_block?(body_tokens)
186
+ return true if block_locator.body_content_locations.empty?
187
+ content_location = block_locator.body_content_locations.first
188
+ content_location.between?(body_tokens.first.location, body_tokens.last.location)
189
+ end
190
+
191
+ def block_locator
192
+ @block_locator ||= BlockLocator.new(method_name, source, beginning_line_number)
193
+ end
194
+ end
195
+
196
+ # @private
197
+ # Locates target block with node information (semantics), which tokens don't have.
198
+ BlockLocator = Struct.new(:method_name, :source, :beginning_line_number) do
199
+ def method_call_location
200
+ @method_call_location ||= method_ident_node.location
201
+ end
202
+
203
+ def body_content_locations
204
+ @body_content_locations ||= block_body_node.map(&:location).compact
205
+ end
206
+
207
+ private
208
+
209
+ def method_ident_node
210
+ method_call_node = block_wrapper_node.children.first
211
+ method_call_node.find do |node|
212
+ method_ident_node?(node)
213
+ end
214
+ end
215
+
216
+ def block_body_node
217
+ block_node = block_wrapper_node.children[1]
218
+ block_node.children.last
219
+ end
220
+
221
+ def block_wrapper_node
222
+ case candidate_block_wrapper_nodes.size
223
+ when 1
224
+ candidate_block_wrapper_nodes.first
225
+ when 0
226
+ raise TargetNotFoundError
227
+ else
228
+ raise AmbiguousTargetError
229
+ end
230
+ end
231
+
232
+ def candidate_block_wrapper_nodes
233
+ @candidate_block_wrapper_nodes ||= candidate_method_ident_nodes.map do |method_ident_node|
234
+ block_wrapper_node = method_ident_node.each_ancestor.find { |node| node.type == :method_add_block }
235
+ next nil unless block_wrapper_node
236
+ method_call_node = block_wrapper_node.children.first
237
+ method_call_node.include?(method_ident_node) ? block_wrapper_node : nil
238
+ end.compact
239
+ end
240
+
241
+ def candidate_method_ident_nodes
242
+ source.nodes_by_line_number[beginning_line_number].select do |node|
243
+ method_ident_node?(node)
244
+ end
245
+ end
246
+
247
+ def method_ident_node?(node)
248
+ node.type == :@ident && node.args.first == method_name
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end