rspec-support 3.0.4 → 3.12.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +322 -0
- data/{LICENSE.txt → LICENSE.md} +3 -2
- data/README.md +29 -6
- data/lib/rspec/support/caller_filter.rb +35 -16
- data/lib/rspec/support/comparable_version.rb +46 -0
- data/lib/rspec/support/differ.rb +51 -41
- data/lib/rspec/support/directory_maker.rb +63 -0
- data/lib/rspec/support/encoded_string.rb +110 -15
- data/lib/rspec/support/fuzzy_matcher.rb +5 -6
- data/lib/rspec/support/hunk_generator.rb +0 -1
- data/lib/rspec/support/matcher_definition.rb +42 -0
- data/lib/rspec/support/method_signature_verifier.rb +287 -54
- data/lib/rspec/support/mutex.rb +73 -0
- data/lib/rspec/support/object_formatter.rb +275 -0
- data/lib/rspec/support/recursive_const_methods.rb +76 -0
- data/lib/rspec/support/reentrant_mutex.rb +78 -0
- data/lib/rspec/support/ruby_features.rb +177 -14
- data/lib/rspec/support/source/location.rb +21 -0
- data/lib/rspec/support/source/node.rb +110 -0
- data/lib/rspec/support/source/token.rb +94 -0
- data/lib/rspec/support/source.rb +85 -0
- data/lib/rspec/support/spec/deprecation_helpers.rb +19 -32
- data/lib/rspec/support/spec/diff_helpers.rb +31 -0
- data/lib/rspec/support/spec/in_sub_process.rb +43 -16
- data/lib/rspec/support/spec/library_wide_checks.rb +150 -0
- data/lib/rspec/support/spec/shell_out.rb +108 -0
- data/lib/rspec/support/spec/stderr_splitter.rb +31 -9
- data/lib/rspec/support/spec/string_matcher.rb +45 -0
- data/lib/rspec/support/spec/with_isolated_directory.rb +13 -0
- data/lib/rspec/support/spec/with_isolated_stderr.rb +0 -2
- data/lib/rspec/support/spec.rb +46 -26
- data/lib/rspec/support/version.rb +1 -1
- data/lib/rspec/support/warnings.rb +6 -6
- data/lib/rspec/support/with_keywords_when_needed.rb +33 -0
- data/lib/rspec/support.rb +87 -3
- data.tar.gz.sig +0 -0
- metadata +70 -52
- metadata.gz.sig +0 -0
- data/lib/rspec/support/version_checker.rb +0 -53
@@ -0,0 +1,275 @@
|
|
1
|
+
RSpec::Support.require_rspec_support 'matcher_definition'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Support
|
5
|
+
# Provide additional output details beyond what `inspect` provides when
|
6
|
+
# printing Time, DateTime, or BigDecimal
|
7
|
+
# @api private
|
8
|
+
class ObjectFormatter # rubocop:disable Metrics/ClassLength
|
9
|
+
ELLIPSIS = "..."
|
10
|
+
|
11
|
+
attr_accessor :max_formatted_output_length
|
12
|
+
|
13
|
+
# Methods are deferred to a default instance of the class to maintain the interface
|
14
|
+
# For example, calling ObjectFormatter.format is still possible
|
15
|
+
def self.default_instance
|
16
|
+
@default_instance ||= new
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.format(object)
|
20
|
+
default_instance.format(object)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.prepare_for_inspection(object)
|
24
|
+
default_instance.prepare_for_inspection(object)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(max_formatted_output_length=200)
|
28
|
+
@max_formatted_output_length = max_formatted_output_length
|
29
|
+
@current_structure_stack = []
|
30
|
+
end
|
31
|
+
|
32
|
+
def format(object)
|
33
|
+
if max_formatted_output_length.nil?
|
34
|
+
prepare_for_inspection(object).inspect
|
35
|
+
else
|
36
|
+
formatted_object = prepare_for_inspection(object).inspect
|
37
|
+
if formatted_object.length < max_formatted_output_length
|
38
|
+
formatted_object
|
39
|
+
else
|
40
|
+
beginning = truncate_string formatted_object, 0, max_formatted_output_length / 2
|
41
|
+
ending = truncate_string formatted_object, -max_formatted_output_length / 2, -1
|
42
|
+
beginning + ELLIPSIS + ending
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Prepares the provided object to be formatted by wrapping it as needed
|
48
|
+
# in something that, when `inspect` is called on it, will produce the
|
49
|
+
# desired output.
|
50
|
+
#
|
51
|
+
# This allows us to apply the desired formatting to hash/array data structures
|
52
|
+
# at any level of nesting, simply by walking that structure and replacing items
|
53
|
+
# with custom items that have `inspect` defined to return the desired output
|
54
|
+
# for that item. Then we can just use `Array#inspect` or `Hash#inspect` to
|
55
|
+
# format the entire thing.
|
56
|
+
def prepare_for_inspection(object)
|
57
|
+
case object
|
58
|
+
when Array
|
59
|
+
prepare_array(object)
|
60
|
+
when Hash
|
61
|
+
prepare_hash(object)
|
62
|
+
else
|
63
|
+
inspector_class = INSPECTOR_CLASSES.find { |inspector| inspector.can_inspect?(object) }
|
64
|
+
inspector_class.new(object, self)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def prepare_array(array)
|
69
|
+
with_entering_structure(array) do
|
70
|
+
array.map { |element| prepare_element(element) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def prepare_hash(input_hash)
|
75
|
+
with_entering_structure(input_hash) do
|
76
|
+
sort_hash_keys(input_hash).inject({}) do |output_hash, key_and_value|
|
77
|
+
key, value = key_and_value.map { |element| prepare_element(element) }
|
78
|
+
output_hash[key] = value
|
79
|
+
output_hash
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def sort_hash_keys(input_hash)
|
85
|
+
if input_hash.keys.all? { |k| k.is_a?(String) || k.is_a?(Symbol) }
|
86
|
+
Hash[input_hash.sort_by { |k, _v| k.to_s }]
|
87
|
+
else
|
88
|
+
input_hash
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def prepare_element(element)
|
93
|
+
if recursive_structure?(element)
|
94
|
+
case element
|
95
|
+
when Array then InspectableItem.new('[...]')
|
96
|
+
when Hash then InspectableItem.new('{...}')
|
97
|
+
else raise # This won't happen
|
98
|
+
end
|
99
|
+
else
|
100
|
+
prepare_for_inspection(element)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def with_entering_structure(structure)
|
105
|
+
@current_structure_stack.push(structure)
|
106
|
+
return_value = yield
|
107
|
+
@current_structure_stack.pop
|
108
|
+
return_value
|
109
|
+
end
|
110
|
+
|
111
|
+
def recursive_structure?(object)
|
112
|
+
@current_structure_stack.any? { |seen_structure| seen_structure.equal?(object) }
|
113
|
+
end
|
114
|
+
|
115
|
+
InspectableItem = Struct.new(:text) do
|
116
|
+
def inspect
|
117
|
+
text
|
118
|
+
end
|
119
|
+
|
120
|
+
def pretty_print(pp)
|
121
|
+
pp.text(text)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
BaseInspector = Struct.new(:object, :formatter) do
|
126
|
+
def self.can_inspect?(_object)
|
127
|
+
raise NotImplementedError
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
raise NotImplementedError
|
132
|
+
end
|
133
|
+
|
134
|
+
def pretty_print(pp)
|
135
|
+
pp.text(inspect)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class TimeInspector < BaseInspector
|
140
|
+
FORMAT = "%Y-%m-%d %H:%M:%S"
|
141
|
+
|
142
|
+
def self.can_inspect?(object)
|
143
|
+
Time === object
|
144
|
+
end
|
145
|
+
|
146
|
+
if Time.method_defined?(:nsec)
|
147
|
+
def inspect
|
148
|
+
object.strftime("#{FORMAT}.#{"%09d" % object.nsec} %z")
|
149
|
+
end
|
150
|
+
else # for 1.8.7
|
151
|
+
def inspect
|
152
|
+
object.strftime("#{FORMAT}.#{"%06d" % object.usec} %z")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class DateTimeInspector < BaseInspector
|
158
|
+
FORMAT = "%a, %d %b %Y %H:%M:%S.%N %z"
|
159
|
+
|
160
|
+
def self.can_inspect?(object)
|
161
|
+
defined?(DateTime) && DateTime === object
|
162
|
+
end
|
163
|
+
|
164
|
+
# ActiveSupport sometimes overrides inspect. If `ActiveSupport` is
|
165
|
+
# defined use a custom format string that includes more time precision.
|
166
|
+
def inspect
|
167
|
+
if defined?(ActiveSupport)
|
168
|
+
object.strftime(FORMAT)
|
169
|
+
else
|
170
|
+
object.inspect
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class BigDecimalInspector < BaseInspector
|
176
|
+
def self.can_inspect?(object)
|
177
|
+
defined?(BigDecimal) && BigDecimal === object
|
178
|
+
end
|
179
|
+
|
180
|
+
def inspect
|
181
|
+
"#{object.to_s('F')} (#{object.inspect})"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class DescribableMatcherInspector < BaseInspector
|
186
|
+
def self.can_inspect?(object)
|
187
|
+
Support.is_a_matcher?(object) && object.respond_to?(:description)
|
188
|
+
end
|
189
|
+
|
190
|
+
def inspect
|
191
|
+
object.description
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class UninspectableObjectInspector < BaseInspector
|
196
|
+
OBJECT_ID_FORMAT = '%#016x'
|
197
|
+
|
198
|
+
def self.can_inspect?(object)
|
199
|
+
object.inspect
|
200
|
+
false
|
201
|
+
rescue NoMethodError
|
202
|
+
true
|
203
|
+
end
|
204
|
+
|
205
|
+
def inspect
|
206
|
+
"#<#{klass}:#{native_object_id}>"
|
207
|
+
end
|
208
|
+
|
209
|
+
def klass
|
210
|
+
Support.class_of(object)
|
211
|
+
end
|
212
|
+
|
213
|
+
# http://stackoverflow.com/a/2818916
|
214
|
+
def native_object_id
|
215
|
+
OBJECT_ID_FORMAT % (object.__id__ << 1)
|
216
|
+
rescue NoMethodError
|
217
|
+
# In Ruby 1.9.2, BasicObject responds to none of #__id__, #object_id, #id...
|
218
|
+
'-'
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class DelegatorInspector < BaseInspector
|
223
|
+
def self.can_inspect?(object)
|
224
|
+
defined?(Delegator) && Delegator === object
|
225
|
+
end
|
226
|
+
|
227
|
+
def inspect
|
228
|
+
"#<#{object.class}(#{formatter.format(object.send(:__getobj__))})>"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class InspectableObjectInspector < BaseInspector
|
233
|
+
def self.can_inspect?(object)
|
234
|
+
object.inspect
|
235
|
+
true
|
236
|
+
rescue NoMethodError
|
237
|
+
false
|
238
|
+
end
|
239
|
+
|
240
|
+
def inspect
|
241
|
+
object.inspect
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
INSPECTOR_CLASSES = [
|
246
|
+
TimeInspector,
|
247
|
+
DateTimeInspector,
|
248
|
+
BigDecimalInspector,
|
249
|
+
UninspectableObjectInspector,
|
250
|
+
DescribableMatcherInspector,
|
251
|
+
DelegatorInspector,
|
252
|
+
InspectableObjectInspector
|
253
|
+
].tap do |classes|
|
254
|
+
# 2.4 has improved BigDecimal formatting so we do not need
|
255
|
+
# to provide our own.
|
256
|
+
# https://github.com/ruby/bigdecimal/pull/42
|
257
|
+
classes.delete(BigDecimalInspector) if RUBY_VERSION >= '2.4'
|
258
|
+
end
|
259
|
+
|
260
|
+
private
|
261
|
+
|
262
|
+
# Returns the substring defined by the start_index and end_index
|
263
|
+
# If the string ends with a partial ANSI code code then that
|
264
|
+
# will be removed as printing partial ANSI
|
265
|
+
# codes to the terminal can lead to corruption
|
266
|
+
def truncate_string(str, start_index, end_index)
|
267
|
+
cut_str = str[start_index..end_index]
|
268
|
+
|
269
|
+
# ANSI color codes are like: \e[33m so anything with \e[ and a
|
270
|
+
# number without a 'm' is an incomplete color code
|
271
|
+
cut_str.sub(/\e\[\d+$/, '')
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Support
|
3
|
+
# Provides recursive constant lookup methods useful for
|
4
|
+
# constant stubbing.
|
5
|
+
module RecursiveConstMethods
|
6
|
+
# We only want to consider constants that are defined directly on a
|
7
|
+
# particular module, and not include top-level/inherited constants.
|
8
|
+
# Unfortunately, the constant API changed between 1.8 and 1.9, so
|
9
|
+
# we need to conditionally define methods to ignore the top-level/inherited
|
10
|
+
# constants.
|
11
|
+
#
|
12
|
+
# Given:
|
13
|
+
# class A; B = 1; end
|
14
|
+
# class C < A; end
|
15
|
+
#
|
16
|
+
# On 1.8:
|
17
|
+
# - C.const_get("Hash") # => ::Hash
|
18
|
+
# - C.const_defined?("Hash") # => false
|
19
|
+
# - C.constants # => ["B"]
|
20
|
+
# - None of these methods accept the extra `inherit` argument
|
21
|
+
# On 1.9:
|
22
|
+
# - C.const_get("Hash") # => ::Hash
|
23
|
+
# - C.const_defined?("Hash") # => true
|
24
|
+
# - C.const_get("Hash", false) # => raises NameError
|
25
|
+
# - C.const_defined?("Hash", false) # => false
|
26
|
+
# - C.constants # => [:B]
|
27
|
+
# - C.constants(false) #=> []
|
28
|
+
if Module.method(:const_defined?).arity == 1
|
29
|
+
def const_defined_on?(mod, const_name)
|
30
|
+
mod.const_defined?(const_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_const_defined_on(mod, const_name)
|
34
|
+
return mod.const_get(const_name) if const_defined_on?(mod, const_name)
|
35
|
+
|
36
|
+
raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def constants_defined_on(mod)
|
40
|
+
mod.constants.select { |c| const_defined_on?(mod, c) }
|
41
|
+
end
|
42
|
+
else
|
43
|
+
def const_defined_on?(mod, const_name)
|
44
|
+
mod.const_defined?(const_name, false)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_const_defined_on(mod, const_name)
|
48
|
+
mod.const_get(const_name, false)
|
49
|
+
end
|
50
|
+
|
51
|
+
def constants_defined_on(mod)
|
52
|
+
mod.constants(false)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def recursive_const_get(const_name)
|
57
|
+
normalize_const_name(const_name).split('::').inject(Object) do |mod, name|
|
58
|
+
get_const_defined_on(mod, name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def recursive_const_defined?(const_name)
|
63
|
+
parts = normalize_const_name(const_name).split('::')
|
64
|
+
parts.inject([Object, '']) do |(mod, full_name), name|
|
65
|
+
yield(full_name, name) if block_given? && !(Module === mod)
|
66
|
+
return false unless const_defined_on?(mod, name)
|
67
|
+
[get_const_defined_on(mod, name), [mod.name, name].join('::')]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def normalize_const_name(const_name)
|
72
|
+
const_name.sub(/\A::/, '')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Support
|
3
|
+
# Allows a thread to lock out other threads from a critical section of code,
|
4
|
+
# while allowing the thread with the lock to reenter that section.
|
5
|
+
#
|
6
|
+
# Based on Monitor as of 2.2 -
|
7
|
+
# https://github.com/ruby/ruby/blob/eb7ddaa3a47bf48045d26c72eb0f263a53524ebc/lib/monitor.rb#L9
|
8
|
+
#
|
9
|
+
# Depends on Mutex, but Mutex is only available as part of core since 1.9.1:
|
10
|
+
# exists - http://ruby-doc.org/core-1.9.1/Mutex.html
|
11
|
+
# dne - http://ruby-doc.org/core-1.9.0/Mutex.html
|
12
|
+
#
|
13
|
+
# @private
|
14
|
+
class ReentrantMutex
|
15
|
+
def initialize
|
16
|
+
@owner = nil
|
17
|
+
@count = 0
|
18
|
+
@mutex = Mutex.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def synchronize
|
22
|
+
enter
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# This is fixing a bug #501 that is specific to Ruby 3.0. The new implementation
|
31
|
+
# depends on `owned?` that was introduced in Ruby 2.0, so both should work for Ruby 2.x.
|
32
|
+
if RUBY_VERSION.to_f >= 3.0
|
33
|
+
def enter
|
34
|
+
@mutex.lock unless @mutex.owned?
|
35
|
+
@count += 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def exit
|
39
|
+
unless @mutex.owned?
|
40
|
+
raise ThreadError, "Attempt to unlock a mutex which is locked by another thread/fiber"
|
41
|
+
end
|
42
|
+
@count -= 1
|
43
|
+
@mutex.unlock if @count == 0
|
44
|
+
end
|
45
|
+
else
|
46
|
+
def enter
|
47
|
+
@mutex.lock if @owner != Thread.current
|
48
|
+
@owner = Thread.current
|
49
|
+
@count += 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def exit
|
53
|
+
@count -= 1
|
54
|
+
return unless @count == 0
|
55
|
+
@owner = nil
|
56
|
+
@mutex.unlock
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if defined? ::Mutex
|
62
|
+
# On 1.9 and up, this is in core, so we just use the real one
|
63
|
+
class Mutex < ::Mutex
|
64
|
+
# If you mock Mutex.new you break our usage of Mutex, so
|
65
|
+
# instead we capture the original method to return Mutexs.
|
66
|
+
NEW_MUTEX_METHOD = Mutex.method(:new)
|
67
|
+
|
68
|
+
def self.new
|
69
|
+
NEW_MUTEX_METHOD.call
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else # For 1.8.7
|
73
|
+
# :nocov:
|
74
|
+
RSpec::Support.require_rspec_support "mutex"
|
75
|
+
# :nocov:
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -1,35 +1,198 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
RSpec::Support.require_rspec_support "comparable_version"
|
3
|
+
|
1
4
|
module RSpec
|
2
5
|
module Support
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# Provides query methods for different OS or OS features.
|
9
|
+
module OS
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def windows?
|
13
|
+
!!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
|
14
|
+
end
|
15
|
+
|
16
|
+
def windows_file_path?
|
17
|
+
::File::ALT_SEPARATOR == '\\'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
# Provides query methods for different rubies
|
24
|
+
module Ruby
|
25
|
+
module_function
|
26
|
+
|
27
|
+
def jruby?
|
28
|
+
RUBY_PLATFORM == 'java'
|
29
|
+
end
|
30
|
+
|
31
|
+
def jruby_version
|
32
|
+
@jruby_version ||= ComparableVersion.new(JRUBY_VERSION)
|
33
|
+
end
|
34
|
+
|
35
|
+
def jruby_9000?
|
36
|
+
jruby? && JRUBY_VERSION >= '9.0.0.0'
|
37
|
+
end
|
38
|
+
|
39
|
+
def rbx?
|
40
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
41
|
+
end
|
42
|
+
|
43
|
+
def non_mri?
|
44
|
+
!mri?
|
45
|
+
end
|
46
|
+
|
47
|
+
def mri?
|
48
|
+
!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
|
49
|
+
end
|
50
|
+
|
51
|
+
def truffleruby?
|
52
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'truffleruby'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
3
56
|
# @api private
|
4
57
|
#
|
5
58
|
# Provides query methods for ruby features that differ among
|
6
59
|
# implementations.
|
7
60
|
module RubyFeatures
|
61
|
+
module_function
|
62
|
+
|
63
|
+
if Ruby.jruby? && RUBY_VERSION.to_f < 1.9
|
64
|
+
# On JRuby 1.7 `--1.8` mode, `Process.respond_to?(:fork)` returns true,
|
65
|
+
# but when you try to fork, it raises an error:
|
66
|
+
# NotImplementedError: fork is not available on this platform
|
67
|
+
#
|
68
|
+
# When we drop support for JRuby 1.7 and/or Ruby 1.8, we can drop
|
69
|
+
# this special case.
|
70
|
+
def fork_supported?
|
71
|
+
false
|
72
|
+
end
|
73
|
+
else
|
74
|
+
def fork_supported?
|
75
|
+
Process.respond_to?(:fork)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
8
79
|
def optional_and_splat_args_supported?
|
9
80
|
Method.method_defined?(:parameters)
|
10
81
|
end
|
11
|
-
module_function :optional_and_splat_args_supported?
|
12
82
|
|
13
|
-
def
|
14
|
-
|
83
|
+
def caller_locations_supported?
|
84
|
+
respond_to?(:caller_locations, true)
|
15
85
|
end
|
16
|
-
module_function :kw_args_supported?
|
17
86
|
|
18
|
-
|
19
|
-
|
87
|
+
if Exception.method_defined?(:cause)
|
88
|
+
def supports_exception_cause?
|
89
|
+
true
|
90
|
+
end
|
91
|
+
else
|
92
|
+
def supports_exception_cause?
|
93
|
+
false
|
94
|
+
end
|
20
95
|
end
|
21
|
-
module_function :required_kw_args_supported?
|
22
96
|
|
23
|
-
|
24
|
-
|
97
|
+
if RUBY_VERSION.to_f >= 2.7
|
98
|
+
def supports_taint?
|
99
|
+
false
|
100
|
+
end
|
101
|
+
else
|
102
|
+
def supports_taint?
|
103
|
+
true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
ripper_requirements = [ComparableVersion.new(RUBY_VERSION) >= '1.9.2']
|
107
|
+
|
108
|
+
ripper_requirements.push(false) if Ruby.rbx?
|
109
|
+
|
110
|
+
if Ruby.jruby?
|
111
|
+
ripper_requirements.push(Ruby.jruby_version >= '1.7.5')
|
112
|
+
# Ripper on JRuby 9.0.0.0.rc1 - 9.1.8.0 reports wrong line number
|
113
|
+
# or cannot parse source including `:if`.
|
114
|
+
# Ripper on JRuby 9.x.x.x < 9.1.17.0 can't handle keyword arguments
|
115
|
+
# Neither can JRuby 9.2, e.g. < 9.2.1.0
|
116
|
+
ripper_requirements.push(!Ruby.jruby_version.between?('9.0.0.0.rc1', '9.2.0.0'))
|
117
|
+
end
|
118
|
+
|
119
|
+
# TruffleRuby disables ripper due to low performance
|
120
|
+
ripper_requirements.push(false) if Ruby.truffleruby?
|
121
|
+
|
122
|
+
if ripper_requirements.all?
|
123
|
+
def ripper_supported?
|
124
|
+
true
|
125
|
+
end
|
126
|
+
else
|
127
|
+
def ripper_supported?
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def distincts_kw_args_from_positional_hash?
|
133
|
+
RUBY_VERSION >= '3.0.0'
|
25
134
|
end
|
26
|
-
module_function :module_prepends_supported?
|
27
135
|
|
28
|
-
|
29
|
-
|
30
|
-
|
136
|
+
if Ruby.mri?
|
137
|
+
def kw_args_supported?
|
138
|
+
RUBY_VERSION >= '2.0.0'
|
139
|
+
end
|
140
|
+
|
141
|
+
def required_kw_args_supported?
|
142
|
+
RUBY_VERSION >= '2.1.0'
|
143
|
+
end
|
144
|
+
|
145
|
+
def supports_rebinding_module_methods?
|
146
|
+
RUBY_VERSION.to_i >= 2
|
147
|
+
end
|
148
|
+
else
|
149
|
+
# RBX / JRuby et al support is unknown for keyword arguments
|
150
|
+
begin
|
151
|
+
eval("o = Object.new; def o.m(a: 1); end;"\
|
152
|
+
" raise SyntaxError unless o.method(:m).parameters.include?([:key, :a])")
|
153
|
+
|
154
|
+
def kw_args_supported?
|
155
|
+
true
|
156
|
+
end
|
157
|
+
rescue SyntaxError
|
158
|
+
def kw_args_supported?
|
159
|
+
false
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
begin
|
164
|
+
eval("o = Object.new; def o.m(a: ); end;"\
|
165
|
+
"raise SyntaxError unless o.method(:m).parameters.include?([:keyreq, :a])")
|
166
|
+
|
167
|
+
def required_kw_args_supported?
|
168
|
+
true
|
169
|
+
end
|
170
|
+
rescue SyntaxError
|
171
|
+
def required_kw_args_supported?
|
172
|
+
false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
begin
|
177
|
+
Module.new { def foo; end }.instance_method(:foo).bind(Object.new)
|
178
|
+
|
179
|
+
def supports_rebinding_module_methods?
|
180
|
+
true
|
181
|
+
end
|
182
|
+
rescue TypeError
|
183
|
+
def supports_rebinding_module_methods?
|
184
|
+
false
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def module_refinement_supported?
|
190
|
+
Module.method_defined?(:refine) || Module.private_method_defined?(:refine)
|
191
|
+
end
|
192
|
+
|
193
|
+
def module_prepends_supported?
|
194
|
+
Module.method_defined?(:prepend) || Module.private_method_defined?(:prepend)
|
31
195
|
end
|
32
|
-
module_function :supports_rebinding_module_methods?
|
33
196
|
end
|
34
197
|
end
|
35
198
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Support
|
3
|
+
class Source
|
4
|
+
# @private
|
5
|
+
# Represents a source location of node or token.
|
6
|
+
Location = Struct.new(:line, :column) do
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
def self.location?(array)
|
10
|
+
array.is_a?(Array) && array.size == 2 && array.all? { |e| e.is_a?(Integer) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def <=>(other)
|
14
|
+
line_comparison = (line <=> other.line)
|
15
|
+
return line_comparison unless line_comparison == 0
|
16
|
+
column <=> other.column
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|