rspec-support 3.5.0 → 3.9.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: bd9a51bb43733eb4eddd9869ad64d04870dcea8d
4
- data.tar.gz: ce2037102c6fa6d0fba4bce9e5060c8843b98d2a
2
+ SHA256:
3
+ metadata.gz: 27ddd84c490aacfced87a3d772c93cf8391484030d28f78789ff5e9263811d42
4
+ data.tar.gz: 515eecb4064c04ac636b6171ad1308532596a13d9b132ac4689893ab76f43bfe
5
5
  SHA512:
6
- metadata.gz: 133de5b6bf306bcc7aad2eaab37e189bd41c74ae110a26f638f05baa5060f57f4e3df7a870f8db00c02c4b8d51f5d3ffccd9192479b6076338d8e767d1de26a8
7
- data.tar.gz: 674f9df382ca7c4a9433197f9cfdf0b845a031b0f1a5c2e7bb6e993d289a1d69d85dcefda2aec4b62c59c6515a6048b80ed5a5c63d8ad04b240720762ed06037
6
+ metadata.gz: 3755a83a18dcd1326ac122e8250b0abbe0c50af2e630748478c8dfbf0c0f64f65d59725db3ae5370e382f49409b8f7d1639aa09a75c143d4186a8bfeda36a9e3
7
+ data.tar.gz: 6a41a8e6f905e1c2a37a776a2438d461859aab8434b1cd20c0aa4ca50fb17d898465535710018c8c01c199fde6e179ffef35feb1cd118dc4b71894a0a12d960b
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,7 +1,90 @@
1
+ ### 3.9.0 / 2019-10-07
2
+
3
+ *NO CHANGES*
4
+
5
+ Version 3.9.0 was released to allow other RSpec gems to release 3.9.0.
6
+
7
+ ### 3.8.3 / 2019-10-02
8
+
9
+ Bug Fixes:
10
+
11
+ * Escape \r when outputting strings inside arrays.
12
+ (Tomita Masahiro, Jon Rowe, #378)
13
+ * Ensure that optional hash arguments are recognised correctly vs keyword
14
+ arguments. (Evgeni Dzhelyov, #366)
15
+
16
+ ### 3.8.2 / 2019-06-10
17
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.8.1...v3.8.2)
18
+
19
+ Bug Fixes:
20
+
21
+ * Ensure that an empty hash is recognised as empty keyword arguments when
22
+ applicable. (Thomas Walpole, #375)
23
+ * Ensure that diffing truthy values produce diffs consistently.
24
+ (Lucas Nestor, #377)
25
+
26
+ ### 3.8.1 / 2019-03-03
27
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.8.0...v3.8.1)
28
+
29
+ Bug Fixes:
30
+
31
+ * Ensure that inspecting a `SimpleDelegator` based object works regardless of
32
+ visibilty of the `__getobj__` method. (Jon Rowe, #369)
33
+
34
+ ### 3.8.0 / 2018-08-04
35
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.7.1...v3.8.0)
36
+
37
+ Bug Fixes:
38
+
39
+ * Order hash keys before diffing to improve diff accuracy when using mocked calls.
40
+ (James Crisp, #334)
41
+
42
+ ### 3.7.1 / 2018-01-29
43
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.7.0...v3.7.1)
44
+
45
+ Bug Fixes:
46
+
47
+ * Fix source extraction logic so that it does not trigger a `SystemStackError`
48
+ when processing deeply nested example groups. (Craig Bass, #343)
49
+
50
+ ### 3.7.0 / 2017-10-17
51
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.6.0...v3.7.0)
52
+
53
+ Enhancements:
54
+
55
+ * Improve compatibility with `--enable-frozen-string-literal` option
56
+ on Ruby 2.3+. (Pat Allan, #320)
57
+ * Add `Support.class_of` for extracting class of any object.
58
+ (Yuji Nakayama, #325)
59
+
60
+ Bug Fixes:
61
+
62
+ * Fix recursive const support to not blow up when given buggy classes
63
+ that raise odd errors from `#to_str`. (Myron Marston, #317)
64
+
65
+ ### 3.6.0 / 2017-05-04
66
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.6.0.beta2...3.6.0)
67
+
68
+ Enhancements:
69
+
70
+ * Import `Source` classes from rspec-core. (Yuji Nakayama, #315)
71
+
72
+ ### 3.6.0.beta2 / 2016-12-12
73
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.6.0.beta1...v3.6.0.beta2)
74
+
75
+ No user-facing changes.
76
+
77
+ ### 3.6.0.beta1 / 2016-10-09
78
+ [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.5.0...v3.6.0.beta1)
79
+
80
+ Bug Fixes:
81
+
82
+ * Prevent truncated formatted object output from mangling console codes. (#294, Anson Kelly)
83
+
1
84
  ### 3.5.0 / 2016-07-01
2
85
  [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.5.0.beta4...v3.5.0)
3
86
 
4
- **No user facing changes since beat4**
87
+ **No user facing changes since beta4**
5
88
 
6
89
  ### 3.5.0.beta4 / 2016-06-05
7
90
  [Full Changelog](http://github.com/rspec/rspec-support/compare/v3.5.0.beta3...v3.5.0.beta4)
data/README.md CHANGED
@@ -13,7 +13,7 @@ RSpec repos as well. Add the following to your `Gemfile`:
13
13
 
14
14
  ```ruby
15
15
  %w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
16
- gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => 'master'
16
+ gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'master'
17
17
  end
18
18
  ```
19
19
 
@@ -73,6 +73,16 @@ module RSpec
73
73
  end
74
74
  end
75
75
 
76
+ # @api private
77
+ #
78
+ # Used internally to get a class of a given object, even if it does not respond to #class.
79
+ def self.class_of(object)
80
+ object.class
81
+ rescue NoMethodError
82
+ singleton_class = class << object; self; end
83
+ singleton_class.ancestors.find { |ancestor| !ancestor.equal?(singleton_class) }
84
+ end
85
+
76
86
  # A single thread local variable so we don't excessively pollute that namespace.
77
87
  def self.thread_local_data
78
88
  Thread.current[:__rspec] ||= {}
@@ -129,7 +139,7 @@ module RSpec
129
139
  end
130
140
  end
131
141
 
132
- # The Differ is only needed when a a spec fails with a diffable failure.
142
+ # The Differ is only needed when a spec fails with a diffable failure.
133
143
  # In the more common case of all specs passing or the only failures being
134
144
  # non-diffable, we can avoid the extra cost of loading the differ, diff-lcs,
135
145
  # pp, etc by avoiding an unnecessary require. Instead, autoload will take
@@ -11,7 +11,7 @@ module RSpec
11
11
  def diff(actual, expected)
12
12
  diff = ""
13
13
 
14
- if actual && expected
14
+ unless actual.nil? || expected.nil?
15
15
  if all_strings?(actual, expected)
16
16
  if any_multiline_strings?(actual, expected)
17
17
  diff = diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
@@ -97,7 +97,7 @@ module RSpec
97
97
  if Array === entry
98
98
  entry.inspect
99
99
  else
100
- entry.to_s.gsub("\n", "\\n")
100
+ entry.to_s.gsub("\n", "\\n").gsub("\r", "\\r")
101
101
  end
102
102
  end
103
103
  end
@@ -181,19 +181,19 @@ module RSpec
181
181
  when Hash
182
182
  hash_to_string(object)
183
183
  when Array
184
- PP.pp(ObjectFormatter.prepare_for_inspection(object), "")
184
+ PP.pp(ObjectFormatter.prepare_for_inspection(object), "".dup)
185
185
  when String
186
186
  object =~ /\n/ ? object : object.inspect
187
187
  else
188
- PP.pp(object, "")
188
+ PP.pp(object, "".dup)
189
189
  end
190
190
  end
191
191
 
192
192
  def hash_to_string(hash)
193
193
  formatted_hash = ObjectFormatter.prepare_for_inspection(hash)
194
194
  formatted_hash.keys.sort_by { |k| k.to_s }.map do |key|
195
- pp_key = PP.singleline_pp(key, "")
196
- pp_value = PP.singleline_pp(formatted_hash[key], "")
195
+ pp_key = PP.singleline_pp(key, "".dup)
196
+ pp_value = PP.singleline_pp(formatted_hash[key], "".dup)
197
197
 
198
198
  "#{pp_key} => #{pp_value},"
199
199
  end.join("\n")
@@ -33,6 +33,18 @@ module RSpec
33
33
  optional_max_arg_count <= max_non_kw_args
34
34
  end
35
35
 
36
+ def classify_arity(arity=@method.arity)
37
+ if arity < 0
38
+ # `~` inverts the one's complement and gives us the
39
+ # number of required args
40
+ @min_non_kw_args = ~arity
41
+ @max_non_kw_args = INFINITY
42
+ else
43
+ @min_non_kw_args = arity
44
+ @max_non_kw_args = arity
45
+ end
46
+ end
47
+
36
48
  if RubyFeatures.optional_and_splat_args_supported?
37
49
  def description
38
50
  @description ||= begin
@@ -65,14 +77,19 @@ module RSpec
65
77
  given_kw_args - @allowed_kw_args
66
78
  end
67
79
 
80
+ # If the last argument is Hash, Ruby will treat only symbol keys as keyword arguments
81
+ # the rest will be grouped in another Hash and passed as positional argument.
68
82
  def has_kw_args_in?(args)
69
- Hash === args.last && could_contain_kw_args?(args)
83
+ Hash === args.last &&
84
+ could_contain_kw_args?(args) &&
85
+ (args.last.empty? || args.last.keys.any? { |x| x.is_a?(Symbol) })
70
86
  end
71
87
 
72
88
  # Without considering what the last arg is, could it
73
89
  # contain keyword arguments?
74
90
  def could_contain_kw_args?(args)
75
91
  return false if args.count <= min_non_kw_args
92
+
76
93
  @allows_any_kw_args || @allowed_kw_args.any?
77
94
  end
78
95
 
@@ -137,36 +154,58 @@ module RSpec
137
154
  false
138
155
  end
139
156
 
140
- def classify_parameters
141
- arity = @method.arity
142
- if arity < 0
143
- # `~` inverts the one's complement and gives us the
144
- # number of required args
145
- @min_non_kw_args = ~arity
146
- @max_non_kw_args = INFINITY
147
- else
148
- @min_non_kw_args = arity
149
- @max_non_kw_args = arity
150
- end
151
- end
157
+ alias_method :classify_parameters, :classify_arity
152
158
  end
153
159
 
154
160
  INFINITY = 1 / 0.0
155
161
  end
156
162
 
157
- # Some versions of JRuby have a nasty bug we have to work around :(.
158
- # https://github.com/jruby/jruby/issues/2816
159
- if RSpec::Support::Ruby.jruby? &&
160
- RubyFeatures.optional_and_splat_args_supported? &&
161
- Class.new { attr_writer :foo }.instance_method(:foo=).parameters == []
163
+ if RSpec::Support::Ruby.jruby?
164
+ # JRuby has only partial support for UnboundMethod#parameters, so we fall back on using #arity
165
+ # https://github.com/jruby/jruby/issues/2816 and https://github.com/jruby/jruby/issues/2817
166
+ if RubyFeatures.optional_and_splat_args_supported? &&
167
+ Java::JavaLang::String.instance_method(:char_at).parameters == []
162
168
 
163
- class MethodSignature < remove_const(:MethodSignature)
164
- private
169
+ class MethodSignature < remove_const(:MethodSignature)
170
+ private
165
171
 
166
- def classify_parameters
167
- super
168
- return unless @method.parameters == [] && @method.arity == 1
169
- @max_non_kw_args = @min_non_kw_args = 1
172
+ def classify_parameters
173
+ super
174
+ if (arity = @method.arity) != 0 && @method.parameters.empty?
175
+ classify_arity(arity)
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ # JRuby used to always report -1 arity for Java proxy methods.
182
+ # The workaround essentially makes use of Java's introspection to figure
183
+ # out matching methods (which could be more than one partly because Java
184
+ # supports multiple overloads, and partly because JRuby introduces
185
+ # aliases to make method names look more Rubyesque). If there is only a
186
+ # single match, we can use that methods arity directly instead of the
187
+ # default -1 arity.
188
+ #
189
+ # This workaround only works for Java proxy methods, and in order to
190
+ # support regular methods and blocks, we need to be careful about calling
191
+ # owner and java_class as they might not be available
192
+ if Java::JavaLang::String.instance_method(:char_at).arity == -1
193
+ class MethodSignature < remove_const(:MethodSignature)
194
+ private
195
+
196
+ def classify_parameters
197
+ super
198
+ return unless @method.arity == -1
199
+ return unless @method.respond_to?(:owner)
200
+ return unless @method.owner.respond_to?(:java_class)
201
+ java_instance_methods = @method.owner.java_class.java_instance_methods
202
+ compatible_overloads = java_instance_methods.select do |java_method|
203
+ @method == @method.owner.instance_method(java_method.name)
204
+ end
205
+ if compatible_overloads.size == 1
206
+ classify_arity(compatible_overloads.first.arity)
207
+ end
208
+ end
170
209
  end
171
210
  end
172
211
  end
@@ -323,7 +362,14 @@ module RSpec
323
362
 
324
363
  def split_args(*args)
325
364
  kw_args = if @signature.has_kw_args_in?(args)
326
- args.pop.keys
365
+ last = args.pop
366
+ non_kw_args = last.reject { |k, _| k.is_a?(Symbol) }
367
+ if non_kw_args.empty?
368
+ last.keys
369
+ else
370
+ args << non_kw_args
371
+ last.select { |k, _| k.is_a?(Symbol) }.keys
372
+ end
327
373
  else
328
374
  []
329
375
  end
@@ -5,7 +5,7 @@ module RSpec
5
5
  # Provide additional output details beyond what `inspect` provides when
6
6
  # printing Time, DateTime, or BigDecimal
7
7
  # @api private
8
- class ObjectFormatter # rubocop:disable Style/ClassLength
8
+ class ObjectFormatter # rubocop:disable Metrics/ClassLength
9
9
  ELLIPSIS = "..."
10
10
 
11
11
  attr_accessor :max_formatted_output_length
@@ -31,15 +31,15 @@ module RSpec
31
31
 
32
32
  def format(object)
33
33
  if max_formatted_output_length.nil?
34
- return prepare_for_inspection(object).inspect
34
+ prepare_for_inspection(object).inspect
35
35
  else
36
36
  formatted_object = prepare_for_inspection(object).inspect
37
37
  if formatted_object.length < max_formatted_output_length
38
- return formatted_object
38
+ formatted_object
39
39
  else
40
- beginning = formatted_object[0 .. max_formatted_output_length / 2]
41
- ending = formatted_object[-max_formatted_output_length / 2 ..-1]
42
- return beginning + ELLIPSIS + ending
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
43
  end
44
44
  end
45
45
  end
@@ -73,7 +73,7 @@ module RSpec
73
73
 
74
74
  def prepare_hash(input_hash)
75
75
  with_entering_structure(input_hash) do
76
- input_hash.inject({}) do |output_hash, key_and_value|
76
+ sort_hash_keys(input_hash).inject({}) do |output_hash, key_and_value|
77
77
  key, value = key_and_value.map { |element| prepare_element(element) }
78
78
  output_hash[key] = value
79
79
  output_hash
@@ -81,6 +81,14 @@ module RSpec
81
81
  end
82
82
  end
83
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
+
84
92
  def prepare_element(element)
85
93
  if recursive_structure?(element)
86
94
  case element
@@ -199,8 +207,7 @@ module RSpec
199
207
  end
200
208
 
201
209
  def klass
202
- singleton_class = class << object; self; end
203
- singleton_class.ancestors.find { |ancestor| !ancestor.equal?(singleton_class) }
210
+ Support.class_of(object)
204
211
  end
205
212
 
206
213
  # http://stackoverflow.com/a/2818916
@@ -218,7 +225,7 @@ module RSpec
218
225
  end
219
226
 
220
227
  def inspect
221
- "#<#{object.class}(#{formatter.format(object.__getobj__)})>"
228
+ "#<#{object.class}(#{formatter.format(object.send(:__getobj__))})>"
222
229
  end
223
230
  end
224
231
 
@@ -243,7 +250,26 @@ module RSpec
243
250
  DescribableMatcherInspector,
244
251
  DelegatorInspector,
245
252
  InspectableObjectInspector
246
- ]
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
247
273
  end
248
274
  end
249
275
  end
@@ -64,7 +64,7 @@ module RSpec
64
64
  parts.inject([Object, '']) do |(mod, full_name), name|
65
65
  yield(full_name, name) if block_given? && !(Module === mod)
66
66
  return false unless const_defined_on?(mod, name)
67
- [get_const_defined_on(mod, name), [mod, name].join('::')]
67
+ [get_const_defined_on(mod, name), [mod.name, name].join('::')]
68
68
  end
69
69
  end
70
70
 
@@ -28,6 +28,14 @@ module RSpec
28
28
  RUBY_PLATFORM == 'java'
29
29
  end
30
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
+
31
39
  def rbx?
32
40
  defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
33
41
  end
@@ -48,6 +56,22 @@ module RSpec
48
56
  module RubyFeatures
49
57
  module_function
50
58
 
59
+ if Ruby.jruby?
60
+ # On JRuby 1.7 `--1.8` mode, `Process.respond_to?(:fork)` returns true,
61
+ # but when you try to fork, it raises an error:
62
+ # NotImplementedError: fork is not available on this platform
63
+ #
64
+ # When we drop support for JRuby 1.7 and/or Ruby 1.8, we can drop
65
+ # this special case.
66
+ def fork_supported?
67
+ false
68
+ end
69
+ else
70
+ def fork_supported?
71
+ Process.respond_to?(:fork)
72
+ end
73
+ end
74
+
51
75
  def optional_and_splat_args_supported?
52
76
  Method.method_defined?(:parameters)
53
77
  end
@@ -71,9 +95,10 @@ module RSpec
71
95
  ripper_requirements.push(false) if Ruby.rbx?
72
96
 
73
97
  if Ruby.jruby?
74
- ripper_requirements.push(ComparableVersion.new(JRUBY_VERSION) >= '1.7.5')
75
- # Ripper on JRuby 9.0.0.0.rc1 or later reports wrong line number.
76
- ripper_requirements.push(ComparableVersion.new(JRUBY_VERSION) < '9.0.0.0.rc1')
98
+ ripper_requirements.push(Ruby.jruby_version >= '1.7.5')
99
+ # Ripper on JRuby 9.0.0.0.rc1 - 9.1.8.0 reports wrong line number
100
+ # or cannot parse source including `:if`.
101
+ ripper_requirements.push(!Ruby.jruby_version.between?('9.0.0.0.rc1', '9.1.8.0'))
77
102
  end
78
103
 
79
104
  if ripper_requirements.all?
@@ -100,7 +125,6 @@ module RSpec
100
125
  end
101
126
  else
102
127
  # RBX / JRuby et al support is unknown for keyword arguments
103
- # rubocop:disable Lint/Eval
104
128
  begin
105
129
  eval("o = Object.new; def o.m(a: 1); end;"\
106
130
  " raise SyntaxError unless o.method(:m).parameters.include?([:key, :a])")
@@ -138,7 +162,10 @@ module RSpec
138
162
  false
139
163
  end
140
164
  end
141
- # rubocop:enable Lint/Eval
165
+ end
166
+
167
+ def module_refinement_supported?
168
+ Module.method_defined?(:refine) || Module.private_method_defined?(:refine)
142
169
  end
143
170
 
144
171
  def module_prepends_supported?
@@ -0,0 +1,75 @@
1
+ RSpec::Support.require_rspec_support 'encoded_string'
2
+ RSpec::Support.require_rspec_support 'ruby_features'
3
+
4
+ module RSpec
5
+ module Support
6
+ # @private
7
+ # Represents a Ruby source file and provides access to AST and tokens.
8
+ class Source
9
+ attr_reader :source, :path
10
+
11
+ def self.from_file(path)
12
+ source = File.read(path)
13
+ new(source, path)
14
+ end
15
+
16
+ if String.method_defined?(:encoding)
17
+ def initialize(source_string, path=nil)
18
+ @source = RSpec::Support::EncodedString.new(source_string, Encoding.default_external)
19
+ @path = path ? File.expand_path(path) : '(string)'
20
+ end
21
+ else # for 1.8.7
22
+ # :nocov:
23
+ def initialize(source_string, path=nil)
24
+ @source = RSpec::Support::EncodedString.new(source_string)
25
+ @path = path ? File.expand_path(path) : '(string)'
26
+ end
27
+ # :nocov:
28
+ end
29
+
30
+ def lines
31
+ @lines ||= source.split("\n")
32
+ end
33
+
34
+ def inspect
35
+ "#<#{self.class} #{path}>"
36
+ end
37
+
38
+ if RSpec::Support::RubyFeatures.ripper_supported?
39
+ RSpec::Support.require_rspec_support 'source/node'
40
+ RSpec::Support.require_rspec_support 'source/token'
41
+
42
+ def ast
43
+ @ast ||= begin
44
+ require 'ripper'
45
+ sexp = Ripper.sexp(source)
46
+ raise SyntaxError unless sexp
47
+ Node.new(sexp)
48
+ end
49
+ end
50
+
51
+ def tokens
52
+ @tokens ||= begin
53
+ require 'ripper'
54
+ tokens = Ripper.lex(source)
55
+ Token.tokens_from_ripper_tokens(tokens)
56
+ end
57
+ end
58
+
59
+ def nodes_by_line_number
60
+ @nodes_by_line_number ||= begin
61
+ nodes_by_line_number = ast.select(&:location).group_by { |node| node.location.line }
62
+ Hash.new { |hash, key| hash[key] = [] }.merge(nodes_by_line_number)
63
+ end
64
+ end
65
+
66
+ def tokens_by_line_number
67
+ @tokens_by_line_number ||= begin
68
+ nodes_by_line_number = tokens.group_by { |token| token.location.line }
69
+ Hash.new { |hash, key| hash[key] = [] }.merge(nodes_by_line_number)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ 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
@@ -0,0 +1,110 @@
1
+ RSpec::Support.require_rspec_support 'source/location'
2
+
3
+ module RSpec
4
+ module Support
5
+ class Source
6
+ # @private
7
+ # A wrapper for Ripper AST node which is generated with `Ripper.sexp`.
8
+ class Node
9
+ include Enumerable
10
+
11
+ attr_reader :sexp, :parent
12
+
13
+ def self.sexp?(array)
14
+ array.is_a?(Array) && array.first.is_a?(Symbol)
15
+ end
16
+
17
+ def initialize(ripper_sexp, parent=nil)
18
+ @sexp = ripper_sexp.freeze
19
+ @parent = parent
20
+ end
21
+
22
+ def type
23
+ sexp[0]
24
+ end
25
+
26
+ def args
27
+ @args ||= raw_args.map do |raw_arg|
28
+ if Node.sexp?(raw_arg)
29
+ Node.new(raw_arg, self)
30
+ elsif Location.location?(raw_arg)
31
+ Location.new(*raw_arg)
32
+ elsif raw_arg.is_a?(Array)
33
+ ExpressionSequenceNode.new(raw_arg, self)
34
+ else
35
+ raw_arg
36
+ end
37
+ end.freeze
38
+ end
39
+
40
+ def children
41
+ @children ||= args.select { |arg| arg.is_a?(Node) }.freeze
42
+ end
43
+
44
+ def location
45
+ @location ||= args.find { |arg| arg.is_a?(Location) }
46
+ end
47
+
48
+ # We use a loop here (instead of recursion) to prevent SystemStackError
49
+ def each
50
+ return to_enum(__method__) unless block_given?
51
+
52
+ node_queue = []
53
+ node_queue << self
54
+
55
+ while (current_node = node_queue.shift)
56
+ yield current_node
57
+ node_queue.concat(current_node.children)
58
+ end
59
+ end
60
+
61
+ def each_ancestor
62
+ return to_enum(__method__) unless block_given?
63
+
64
+ current_node = self
65
+
66
+ while (current_node = current_node.parent)
67
+ yield current_node
68
+ end
69
+ end
70
+
71
+ def inspect
72
+ "#<#{self.class} #{type}>"
73
+ end
74
+
75
+ private
76
+
77
+ def raw_args
78
+ sexp[1..-1] || []
79
+ end
80
+ end
81
+
82
+ # @private
83
+ # Basically `Ripper.sexp` generates arrays whose first element is a symbol (type of sexp),
84
+ # but it exceptionally generates typeless arrays for expression sequence:
85
+ #
86
+ # Ripper.sexp('foo; bar')
87
+ # => [
88
+ # :program,
89
+ # [ # Typeless array
90
+ # [:vcall, [:@ident, "foo", [1, 0]]],
91
+ # [:vcall, [:@ident, "bar", [1, 5]]]
92
+ # ]
93
+ # ]
94
+ #
95
+ # We wrap typeless arrays in this pseudo type node
96
+ # so that it can be handled in the same way as other type node.
97
+ class ExpressionSequenceNode < Node
98
+ def type
99
+ :_expression_sequence
100
+ end
101
+
102
+ private
103
+
104
+ def raw_args
105
+ sexp
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,87 @@
1
+ RSpec::Support.require_rspec_support 'source/location'
2
+
3
+ module RSpec
4
+ module Support
5
+ class Source
6
+ # @private
7
+ # A wrapper for Ripper token which is generated with `Ripper.lex`.
8
+ class Token
9
+ CLOSING_TYPES_BY_OPENING_TYPE = {
10
+ :on_lbracket => :on_rbracket,
11
+ :on_lparen => :on_rparen,
12
+ :on_lbrace => :on_rbrace,
13
+ :on_heredoc_beg => :on_heredoc_end
14
+ }.freeze
15
+
16
+ CLOSING_KEYWORDS_BY_OPENING_KEYWORD = {
17
+ 'def' => 'end',
18
+ 'do' => 'end',
19
+ }.freeze
20
+
21
+ attr_reader :token
22
+
23
+ def self.tokens_from_ripper_tokens(ripper_tokens)
24
+ ripper_tokens.map { |ripper_token| new(ripper_token) }.freeze
25
+ end
26
+
27
+ def initialize(ripper_token)
28
+ @token = ripper_token.freeze
29
+ end
30
+
31
+ def location
32
+ @location ||= Location.new(*token[0])
33
+ end
34
+
35
+ def type
36
+ token[1]
37
+ end
38
+
39
+ def string
40
+ token[2]
41
+ end
42
+
43
+ def ==(other)
44
+ token == other.token
45
+ end
46
+
47
+ alias_method :eql?, :==
48
+
49
+ def inspect
50
+ "#<#{self.class} #{type} #{string.inspect}>"
51
+ end
52
+
53
+ def keyword?
54
+ type == :on_kw
55
+ end
56
+
57
+ def opening?
58
+ opening_delimiter? || opening_keyword?
59
+ end
60
+
61
+ def closed_by?(other)
62
+ closed_by_delimiter?(other) || closed_by_keyword?(other)
63
+ end
64
+
65
+ private
66
+
67
+ def opening_delimiter?
68
+ CLOSING_TYPES_BY_OPENING_TYPE.key?(type)
69
+ end
70
+
71
+ def opening_keyword?
72
+ return false unless keyword?
73
+ CLOSING_KEYWORDS_BY_OPENING_KEYWORD.key?(string)
74
+ end
75
+
76
+ def closed_by_delimiter?(other)
77
+ other.type == CLOSING_TYPES_BY_OPENING_TYPE[type]
78
+ end
79
+
80
+ def closed_by_keyword?(other)
81
+ return false unless other.keyword?
82
+ other.string == CLOSING_KEYWORDS_BY_OPENING_KEYWORD[string]
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -3,40 +3,57 @@ module RSpec
3
3
  module InSubProcess
4
4
  if Process.respond_to?(:fork) && !(Ruby.jruby? && RUBY_VERSION == '1.8.7')
5
5
 
6
+ UnmarshableObject = Struct.new(:error)
7
+
6
8
  # Useful as a way to isolate a global change to a subprocess.
7
9
 
8
10
  # rubocop:disable MethodLength
9
11
  def in_sub_process(prevent_warnings=true)
10
- readme, writeme = IO.pipe
12
+ exception_reader, exception_writer = IO.pipe
13
+ result_reader, result_writer = IO.pipe
11
14
 
12
15
  pid = Process.fork do
13
- exception = nil
14
16
  warning_preventer = $stderr = RSpec::Support::StdErrSplitter.new($stderr)
15
17
 
16
18
  begin
17
- yield
19
+ result = yield
18
20
  warning_preventer.verify_no_warnings! if prevent_warnings
19
- rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
20
- exception = e
21
+ # rubocop:disable Lint/HandleExceptions
22
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => exception
23
+ # rubocop:enable Lint/HandleExceptions
21
24
  end
22
25
 
23
- writeme.write Marshal.dump(exception)
26
+ exception_writer.write marshal_dump_with_unmarshable_object_handling(exception)
27
+ exception_reader.close
28
+ exception_writer.close
29
+
30
+ result_writer.write marshal_dump_with_unmarshable_object_handling(result)
31
+ result_reader.close
32
+ result_writer.close
24
33
 
25
- readme.close
26
- writeme.close
27
34
  exit! # prevent at_exit hooks from running (e.g. minitest)
28
35
  end
29
36
 
30
- writeme.close
37
+ exception_writer.close
38
+ result_writer.close
31
39
  Process.waitpid(pid)
32
40
 
33
- exception = Marshal.load(readme.read)
34
- readme.close
35
-
41
+ exception = Marshal.load(exception_reader.read)
42
+ exception_reader.close
36
43
  raise exception if exception
44
+
45
+ result = Marshal.load(result_reader.read)
46
+ result_reader.close
47
+ result
37
48
  end
38
49
  # rubocop:enable MethodLength
39
50
  alias :in_sub_process_if_possible :in_sub_process
51
+
52
+ def marshal_dump_with_unmarshable_object_handling(object)
53
+ Marshal.dump(object)
54
+ rescue TypeError => error
55
+ Marshal.dump(UnmarshableObject.new(error))
56
+ end
40
57
  else
41
58
  def in_sub_process(*)
42
59
  skip "This spec requires forking to work properly, " \
@@ -59,8 +59,15 @@ module RSpec
59
59
  l =~ %r{bundler/source/rubygems} ||
60
60
  # Ignore bundler + rubygems warning.
61
61
  l =~ %r{site_ruby/\d\.\d\.\d/rubygems} ||
62
+ l =~ %r{jruby-\d\.\d\.\d\.\d/lib/ruby/stdlib/rubygems} ||
62
63
  # This is required for windows for some reason
63
- l =~ %r{lib/bundler/rubygems}
64
+ l =~ %r{lib/bundler/rubygems} ||
65
+ # This is a JRuby file that generates warnings on 9.0.3.0
66
+ l =~ %r{lib/ruby/stdlib/jar} ||
67
+ # This is a JRuby file that generates warnings on 9.1.7.0
68
+ l =~ %r{org/jruby/RubyKernel\.java} ||
69
+ # Remove blank lines
70
+ l == "" || l.nil?
64
71
  end.join("\n")
65
72
  end
66
73
 
@@ -1,7 +1,7 @@
1
1
  module RSpec
2
2
  module Support
3
3
  module Version
4
- STRING = '3.5.0'
4
+ STRING = '3.9.0'
5
5
  end
6
6
  end
7
7
  end
@@ -28,8 +28,8 @@ module RSpec
28
28
  # Used internally to print longer warnings
29
29
  def warn_with(message, options={})
30
30
  call_site = options.fetch(:call_site) { CallerFilter.first_non_rspec_line }
31
- message << " Use #{options[:replacement]} instead." if options[:replacement]
32
- message << " Called from #{call_site}." if call_site
31
+ message += " Use #{options[:replacement]} instead." if options[:replacement]
32
+ message += " Called from #{call_site}." if call_site
33
33
  Support.warning_notifier.call message
34
34
  end
35
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-support
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.0
4
+ version: 3.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Chelimsky
@@ -48,22 +48,8 @@ cert_chain:
48
48
  ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
49
49
  F3MdtaDehhjC
50
50
  -----END CERTIFICATE-----
51
- date: 2016-07-01 00:00:00.000000000 Z
51
+ date: 2019-10-07 00:00:00.000000000 Z
52
52
  dependencies:
53
- - !ruby/object:Gem::Dependency
54
- name: bundler
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '1.3'
60
- type: :development
61
- prerelease: false
62
- version_requirements: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - "~>"
65
- - !ruby/object:Gem::Version
66
- version: '1.3'
67
53
  - !ruby/object:Gem::Dependency
68
54
  name: rake
69
55
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +102,10 @@ files:
116
102
  - lib/rspec/support/recursive_const_methods.rb
117
103
  - lib/rspec/support/reentrant_mutex.rb
118
104
  - lib/rspec/support/ruby_features.rb
105
+ - lib/rspec/support/source.rb
106
+ - lib/rspec/support/source/location.rb
107
+ - lib/rspec/support/source/node.rb
108
+ - lib/rspec/support/source/token.rb
119
109
  - lib/rspec/support/spec.rb
120
110
  - lib/rspec/support/spec/deprecation_helpers.rb
121
111
  - lib/rspec/support/spec/formatting_support.rb
@@ -131,7 +121,12 @@ files:
131
121
  homepage: https://github.com/rspec/rspec-support
132
122
  licenses:
133
123
  - MIT
134
- metadata: {}
124
+ metadata:
125
+ bug_tracker_uri: https://github.com/rspec/rspec-support/issues
126
+ changelog_uri: https://github.com/rspec/rspec-support/blob/v3.9.0/Changelog.md
127
+ documentation_uri: https://rspec.info/documentation/
128
+ mailing_list_uri: https://groups.google.com/forum/#!forum/rspec
129
+ source_code_uri: https://github.com/rspec/rspec-support
135
130
  post_install_message:
136
131
  rdoc_options:
137
132
  - "--charset=UTF-8"
@@ -148,9 +143,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
143
  - !ruby/object:Gem::Version
149
144
  version: '0'
150
145
  requirements: []
151
- rubyforge_project:
152
- rubygems_version: 2.5.1
146
+ rubygems_version: 3.0.6
153
147
  signing_key:
154
148
  specification_version: 4
155
- summary: rspec-support-3.5.0
149
+ summary: rspec-support-3.9.0
156
150
  test_files: []
metadata.gz.sig CHANGED
Binary file