rspec-support 3.13.6 → 4.0.0.beta1

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.
@@ -10,7 +10,7 @@ module RSpec
10
10
  # keyword args of a given method.
11
11
  #
12
12
  # @private
13
- class MethodSignature # rubocop:disable Metrics/ClassLength
13
+ class MethodSignature
14
14
  attr_reader :min_non_kw_args, :max_non_kw_args, :optional_kw_args, :required_kw_args
15
15
 
16
16
  def initialize(method)
@@ -47,141 +47,98 @@ module RSpec
47
47
  end
48
48
  end
49
49
 
50
- if RubyFeatures.optional_and_splat_args_supported?
51
- def description
52
- @description ||= begin
53
- parts = []
50
+ def description
51
+ @description ||= begin
52
+ parts = []
54
53
 
55
- unless non_kw_args_arity_description == "0"
56
- parts << "arity of #{non_kw_args_arity_description}"
57
- end
58
-
59
- if @optional_kw_args.any?
60
- parts << "optional keyword args (#{@optional_kw_args.map(&:inspect).join(", ")})"
61
- end
62
-
63
- if @required_kw_args.any?
64
- parts << "required keyword args (#{@required_kw_args.map(&:inspect).join(", ")})"
65
- end
66
-
67
- parts << "any additional keyword args" if @allows_any_kw_args
68
-
69
- parts.join(" and ")
54
+ unless non_kw_args_arity_description == "0"
55
+ parts << "arity of #{non_kw_args_arity_description}"
70
56
  end
71
- end
72
-
73
- def missing_kw_args_from(given_kw_args)
74
- @required_kw_args - given_kw_args
75
- end
76
-
77
- def invalid_kw_args_from(given_kw_args)
78
- return [] if @allows_any_kw_args
79
- given_kw_args - @allowed_kw_args
80
- end
81
57
 
82
- # Considering the arg types, are there kw_args?
83
- if RubyFeatures.kw_arg_separation?
84
- def has_kw_args_in?(args)
85
- # If the last arg is a hash, depending on the signature it could be kw_args or a positional parameter.
86
- return false unless Hash === args.last && could_contain_kw_args?(args)
87
-
88
- # If the position of the hash is beyond the count of required and optional positional
89
- # args then it is the kwargs hash
90
- return true if args.count > @max_non_kw_args
91
-
92
- # This is the proper way to disambiguate between positional args and keywords hash
93
- # but relies on beginning of the call chain annotating the method with
94
- # ruby2_keywords, so only use it for positive feedback as without the annotation
95
- # this is always false
96
- return true if Hash.ruby2_keywords_hash?(args[-1])
97
-
98
- # Otherwise, the hash could be defined kw_args or an optional positional parameter
99
- # inspect the keys against known kwargs to determine what it is
100
- # Note: the problem with this is that if a user passes only invalid keyword args,
101
- # rspec no longer detects is and will assign this to a positional argument
102
- return arbitrary_kw_args? || args.last.keys.all? { |x| @allowed_kw_args.include?(x) }
103
- end
104
- else
105
- def has_kw_args_in?(args)
106
- # Version <= Ruby 2.7
107
- # If the last argument is Hash, Ruby will treat only symbol keys as keyword arguments
108
- # the rest will be grouped in another Hash and passed as positional argument.
109
- Hash === args.last &&
110
- could_contain_kw_args?(args) &&
111
- (args.last.empty? || args.last.keys.any? { |x| x.is_a?(Symbol) })
58
+ if @optional_kw_args.any?
59
+ parts << "optional keyword args (#{@optional_kw_args.map(&:inspect).join(", ")})"
112
60
  end
113
- end
114
-
115
- # Without considering what the last arg is, could it
116
- # contain keyword arguments?
117
- def could_contain_kw_args?(args)
118
- return false if args.count <= min_non_kw_args
119
61
 
120
- @allows_any_kw_args || @allowed_kw_args.any?
121
- end
62
+ if @required_kw_args.any?
63
+ parts << "required keyword args (#{@required_kw_args.map(&:inspect).join(", ")})"
64
+ end
122
65
 
123
- def arbitrary_kw_args?
124
- @allows_any_kw_args
125
- end
66
+ parts << "any additional keyword args" if @allows_any_kw_args
126
67
 
127
- def unlimited_args?
128
- @max_non_kw_args == INFINITY
68
+ parts.join(" and ")
129
69
  end
70
+ end
130
71
 
131
- def classify_parameters
132
- optional_non_kw_args = @min_non_kw_args = 0
133
- @allows_any_kw_args = false
134
-
135
- @method.parameters.each do |(type, name)|
136
- case type
137
- # def foo(a:)
138
- when :keyreq then @required_kw_args << name
139
- # def foo(a: 1)
140
- when :key then @optional_kw_args << name
141
- # def foo(**kw_args)
142
- when :keyrest then @allows_any_kw_args = true
143
- # def foo(a)
144
- when :req then @min_non_kw_args += 1
145
- # def foo(a = 1)
146
- when :opt then optional_non_kw_args += 1
147
- # def foo(*a)
148
- when :rest then optional_non_kw_args = INFINITY
149
- end
150
- end
72
+ def missing_kw_args_from(given_kw_args)
73
+ @required_kw_args - given_kw_args
74
+ end
151
75
 
152
- @max_non_kw_args = @min_non_kw_args + optional_non_kw_args
153
- @allowed_kw_args = @required_kw_args + @optional_kw_args
154
- end
155
- else
156
- def description
157
- "arity of #{non_kw_args_arity_description}"
158
- end
76
+ def invalid_kw_args_from(given_kw_args)
77
+ return [] if @allows_any_kw_args
78
+ given_kw_args - @allowed_kw_args
79
+ end
159
80
 
160
- def missing_kw_args_from(_given_kw_args)
161
- []
162
- end
81
+ # Considering the arg types, are there kw_args?
82
+ def has_kw_args_in?(args)
83
+ # If the last arg is a hash, depending on the signature it could be kw_args or a positional parameter.
84
+ return false unless Hash === args.last && could_contain_kw_args?(args)
85
+
86
+ # If the position of the hash is beyond the count of required and optional positional
87
+ # args then it is the kwargs hash
88
+ return true if args.count > @max_non_kw_args
89
+
90
+ # This is the proper way to disambiguate between positional args and keywords hash
91
+ # but relies on beginning of the call chain annotating the method with
92
+ # ruby2_keywords, so only use it for positive feedback as without the annotation
93
+ # this is always false
94
+ return true if Hash.ruby2_keywords_hash?(args[-1])
95
+
96
+ # Otherwise, the hash could be defined kw_args or an optional positional parameter
97
+ # inspect the keys against known kwargs to determine what it is
98
+ # Note: the problem with this is that if a user passes only invalid keyword args,
99
+ # rspec no longer detects is and will assign this to a positional argument
100
+ return arbitrary_kw_args? || args.last.keys.all? { |x| @allowed_kw_args.include?(x) }
101
+ end
163
102
 
164
- def invalid_kw_args_from(_given_kw_args)
165
- []
166
- end
103
+ # Without considering what the last arg is, could it
104
+ # contain keyword arguments?
105
+ def could_contain_kw_args?(args)
106
+ return false if args.count <= min_non_kw_args
167
107
 
168
- def has_kw_args_in?(_args)
169
- false
170
- end
108
+ @allows_any_kw_args || @allowed_kw_args.any?
109
+ end
171
110
 
172
- def could_contain_kw_args?(*)
173
- false
174
- end
111
+ def arbitrary_kw_args?
112
+ @allows_any_kw_args
113
+ end
175
114
 
176
- def arbitrary_kw_args?
177
- false
178
- end
115
+ def unlimited_args?
116
+ @max_non_kw_args == INFINITY
117
+ end
179
118
 
180
- def unlimited_args?
181
- false
119
+ def classify_parameters
120
+ optional_non_kw_args = @min_non_kw_args = 0
121
+ @allows_any_kw_args = false
122
+
123
+ @method.parameters.each do |(type, name)|
124
+ case type
125
+ # def foo(a:)
126
+ when :keyreq then @required_kw_args << name
127
+ # def foo(a: 1)
128
+ when :key then @optional_kw_args << name
129
+ # def foo(**kw_args)
130
+ when :keyrest then @allows_any_kw_args = true
131
+ # def foo(a)
132
+ when :req then @min_non_kw_args += 1
133
+ # def foo(a = 1)
134
+ when :opt then optional_non_kw_args += 1
135
+ # def foo(*a)
136
+ when :rest then optional_non_kw_args = INFINITY
137
+ end
182
138
  end
183
139
 
184
- alias_method :classify_parameters, :classify_arity
140
+ @max_non_kw_args = @min_non_kw_args + optional_non_kw_args
141
+ @allowed_kw_args = @required_kw_args + @optional_kw_args
185
142
  end
186
143
 
187
144
  INFINITY = 1 / 0.0
@@ -190,8 +147,7 @@ module RSpec
190
147
  if RSpec::Support::Ruby.jruby?
191
148
  # JRuby has only partial support for UnboundMethod#parameters, so we fall back on using #arity
192
149
  # https://github.com/jruby/jruby/issues/2816 and https://github.com/jruby/jruby/issues/2817
193
- if RubyFeatures.optional_and_splat_args_supported? &&
194
- Java::JavaLang::String.instance_method(:char_at).parameters == []
150
+ if Java::JavaLang::String.instance_method(:char_at).parameters == []
195
151
 
196
152
  class MethodSignature < remove_const(:MethodSignature)
197
153
  private
@@ -277,7 +233,10 @@ module RSpec
277
233
  end
278
234
 
279
235
  def keywords=(values)
236
+ # until RSpec 4
237
+ # rubocop:disable Lint/UselessOr
280
238
  @keywords = values.to_a || []
239
+ # rubocop:enable Lint/UselessOr
281
240
  end
282
241
  end
283
242
 
@@ -291,11 +250,9 @@ module RSpec
291
250
  #
292
251
  # @api private
293
252
  class BlockSignature < MethodSignature
294
- if RubyFeatures.optional_and_splat_args_supported?
295
- def classify_parameters
296
- super
297
- @min_non_kw_args = @max_non_kw_args unless @max_non_kw_args == INFINITY
298
- end
253
+ def classify_parameters
254
+ super
255
+ @min_non_kw_args = @max_non_kw_args unless @max_non_kw_args == INFINITY
299
256
  end
300
257
  end
301
258
 
@@ -312,7 +269,7 @@ module RSpec
312
269
  @arbitrary_kw_args = @unlimited_args = false
313
270
  end
314
271
 
315
- def with_expectation(expectation) # rubocop:disable Metrics/MethodLength
272
+ def with_expectation(expectation)
316
273
  return self unless MethodSignatureExpectation === expectation
317
274
 
318
275
  if expectation.empty?
@@ -322,19 +279,9 @@ module RSpec
322
279
  @min_non_kw_args = @non_kw_args = expectation.min_count || 0
323
280
  @max_non_kw_args = expectation.max_count || @min_non_kw_args
324
281
 
325
- if RubyFeatures.optional_and_splat_args_supported?
326
- @unlimited_args = expectation.expect_unlimited_arguments
327
- else
328
- @unlimited_args = false
329
- end
330
-
331
- if RubyFeatures.kw_args_supported?
332
- @kw_args = expectation.keywords
333
- @arbitrary_kw_args = expectation.expect_arbitrary_keywords
334
- else
335
- @kw_args = []
336
- @arbitrary_kw_args = false
337
- end
282
+ @unlimited_args = expectation.expect_unlimited_arguments
283
+ @kw_args = expectation.keywords
284
+ @arbitrary_kw_args = expectation.expect_arbitrary_keywords
338
285
  end
339
286
 
340
287
  self
@@ -388,16 +335,7 @@ module RSpec
388
335
  end
389
336
 
390
337
  def split_args(args)
391
- kw_args = if @signature.has_kw_args_in?(args) && !RubyFeatures.kw_arg_separation?
392
- last = args.pop
393
- non_kw_args = last.reject { |k, _| k.is_a?(Symbol) }
394
- if non_kw_args.empty?
395
- last.keys
396
- else
397
- args << non_kw_args
398
- last.select { |k, _| k.is_a?(Symbol) }.keys
399
- end
400
- elsif @signature.has_kw_args_in?(args) && RubyFeatures.kw_arg_separation?
338
+ kw_args = if @signature.has_kw_args_in?(args)
401
339
  args.pop.keys
402
340
  else
403
341
  []
@@ -147,14 +147,8 @@ module RSpec
147
147
  Time === object
148
148
  end
149
149
 
150
- if Time.method_defined?(:nsec)
151
- def inspect
152
- object.strftime("#{FORMAT}.#{"%09d" % object.nsec} %z")
153
- end
154
- else # for 1.8.7
155
- def inspect
156
- object.strftime("#{FORMAT}.#{"%06d" % object.usec} %z")
157
- end
150
+ def inspect
151
+ object.strftime("#{FORMAT}.#{"%09d" % object.nsec} %z")
158
152
  end
159
153
  end
160
154
 
@@ -176,16 +170,6 @@ module RSpec
176
170
  end
177
171
  end
178
172
 
179
- class BigDecimalInspector < BaseInspector
180
- def self.can_inspect?(object)
181
- defined?(BigDecimal) && BigDecimal === object
182
- end
183
-
184
- def inspect
185
- "#{object.to_s('F')} (#{object.inspect})"
186
- end
187
- end
188
-
189
173
  class DescribableMatcherInspector < BaseInspector
190
174
  def self.can_inspect?(object)
191
175
  Support.is_a_matcher?(object) && object.respond_to?(:description)
@@ -217,9 +201,6 @@ module RSpec
217
201
  # http://stackoverflow.com/a/2818916
218
202
  def native_object_id
219
203
  OBJECT_ID_FORMAT % (object.__id__ << 1)
220
- rescue NoMethodError
221
- # In Ruby 1.9.2, BasicObject responds to none of #__id__, #object_id, #id...
222
- '-'
223
204
  end
224
205
  end
225
206
 
@@ -249,17 +230,11 @@ module RSpec
249
230
  INSPECTOR_CLASSES = [
250
231
  TimeInspector,
251
232
  DateTimeInspector,
252
- BigDecimalInspector,
253
233
  UninspectableObjectInspector,
254
234
  DescribableMatcherInspector,
255
235
  DelegatorInspector,
256
236
  InspectableObjectInspector
257
- ].tap do |classes|
258
- # 2.4 has improved BigDecimal formatting so we do not need
259
- # to provide our own.
260
- # https://github.com/ruby/bigdecimal/pull/42
261
- classes.delete(BigDecimalInspector) if RUBY_VERSION >= '2.4'
262
- end
237
+ ]
263
238
 
264
239
  private
265
240
 
@@ -7,52 +7,29 @@ module RSpec
7
7
  module RecursiveConstMethods
8
8
  # We only want to consider constants that are defined directly on a
9
9
  # particular module, and not include top-level/inherited constants.
10
- # Unfortunately, the constant API changed between 1.8 and 1.9, so
11
- # we need to conditionally define methods to ignore the top-level/inherited
12
- # constants.
13
10
  #
14
11
  # Given:
15
12
  # class A; B = 1; end
16
13
  # class C < A; end
17
14
  #
18
- # On 1.8:
19
- # - C.const_get("Hash") # => ::Hash
20
- # - C.const_defined?("Hash") # => false
21
- # - C.constants # => ["B"]
22
- # - None of these methods accept the extra `inherit` argument
23
- # On 1.9:
24
- # - C.const_get("Hash") # => ::Hash
25
- # - C.const_defined?("Hash") # => true
26
- # - C.const_get("Hash", false) # => raises NameError
27
- # - C.const_defined?("Hash", false) # => false
28
- # - C.constants # => [:B]
29
- # - C.constants(false) #=> []
30
- if Module.method(:const_defined?).arity == 1
31
- def const_defined_on?(mod, const_name)
32
- mod.const_defined?(const_name)
33
- end
34
-
35
- def get_const_defined_on(mod, const_name)
36
- return mod.const_get(const_name) if const_defined_on?(mod, const_name)
37
-
38
- raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
39
- end
40
-
41
- def constants_defined_on(mod)
42
- mod.constants.select { |c| const_defined_on?(mod, c) }
43
- end
44
- else
45
- def const_defined_on?(mod, const_name)
46
- mod.const_defined?(const_name, false)
47
- end
15
+ # Then:
16
+ # C.const_get("Hash") # => ::Hash
17
+ # C.const_defined?("Hash") # => true
18
+ # C.const_get("Hash", false) # => raises NameError
19
+ # C.const_defined?("Hash", false) # => false
20
+ # C.constants # => [:B]
21
+ # C.constants(false) #=> []
22
+
23
+ def const_defined_on?(mod, const_name)
24
+ mod.const_defined?(const_name, false)
25
+ end
48
26
 
49
- def get_const_defined_on(mod, const_name)
50
- mod.const_get(const_name, false)
51
- end
27
+ def get_const_defined_on(mod, const_name)
28
+ mod.const_get(const_name, false)
29
+ end
52
30
 
53
- def constants_defined_on(mod)
54
- mod.constants(false)
55
- end
31
+ def constants_defined_on(mod)
32
+ mod.constants(false)
56
33
  end
57
34
 
58
35
  def recursive_const_get(const_name)
@@ -2,16 +2,20 @@
2
2
 
3
3
  module RSpec
4
4
  module Support
5
+ # This class protects us against Mutex.new stubbed out within tests.
6
+ # @private
7
+ class Mutex < ::Mutex
8
+ class << self
9
+ define_method(:new, &::Mutex.method(:new))
10
+ end
11
+ end
12
+
5
13
  # Allows a thread to lock out other threads from a critical section of code,
6
14
  # while allowing the thread with the lock to reenter that section.
7
15
  #
8
16
  # Based on Monitor as of 2.2 -
9
17
  # https://github.com/ruby/ruby/blob/eb7ddaa3a47bf48045d26c72eb0f263a53524ebc/lib/monitor.rb#L9
10
18
  #
11
- # Depends on Mutex, but Mutex is only available as part of core since 1.9.1:
12
- # exists - http://ruby-doc.org/core-1.9.1/Mutex.html
13
- # dne - http://ruby-doc.org/core-1.9.0/Mutex.html
14
- #
15
19
  # @private
16
20
  class ReentrantMutex
17
21
  def initialize
@@ -29,52 +33,18 @@ module RSpec
29
33
 
30
34
  private
31
35
 
32
- # This is fixing a bug #501 that is specific to Ruby 3.0. The new implementation
33
- # depends on `owned?` that was introduced in Ruby 2.0, so both should work for Ruby 2.x.
34
- if RUBY_VERSION.to_f >= 3.0
35
- def enter
36
- @mutex.lock unless @mutex.owned?
37
- @count += 1
38
- end
39
-
40
- def exit
41
- unless @mutex.owned?
42
- raise ThreadError, "Attempt to unlock a mutex which is locked by another thread/fiber"
43
- end
44
- @count -= 1
45
- @mutex.unlock if @count == 0
46
- end
47
- else
48
- def enter
49
- @mutex.lock if @owner != Thread.current
50
- @owner = Thread.current
51
- @count += 1
52
- end
53
-
54
- def exit
55
- @count -= 1
56
- return unless @count == 0
57
- @owner = nil
58
- @mutex.unlock
59
- end
36
+ def enter
37
+ @mutex.lock unless @mutex.owned?
38
+ @count += 1
60
39
  end
61
- end
62
-
63
- if defined? ::Mutex
64
- # On 1.9 and up, this is in core, so we just use the real one
65
- class Mutex < ::Mutex
66
- # If you mock Mutex.new you break our usage of Mutex, so
67
- # instead we capture the original method to return Mutexes.
68
- NEW_MUTEX_METHOD = Mutex.method(:new)
69
40
 
70
- def self.new
71
- NEW_MUTEX_METHOD.call
41
+ def exit
42
+ unless @mutex.owned?
43
+ raise ThreadError, "Attempt to unlock a mutex which is locked by another thread/fiber"
72
44
  end
45
+ @count -= 1
46
+ @mutex.unlock if @count == 0
73
47
  end
74
- else # For 1.8.7
75
- # :nocov:
76
- RSpec::Support.require_rspec_support "mutex"
77
- # :nocov:
78
48
  end
79
49
  end
80
50
  end