rspec-support 3.13.7 → 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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +23 -2
- data/LICENSE.md +9 -6
- data/README.md +6 -38
- data/lib/rspec/support/caller_filter.rb +36 -47
- data/lib/rspec/support/differ.rb +33 -14
- data/lib/rspec/support/directory_maker.rb +0 -2
- data/lib/rspec/support/encoded_string.rb +63 -80
- data/lib/rspec/support/fuzzy_matcher.rb +1 -2
- data/lib/rspec/support/method_signature_verifier.rb +87 -149
- data/lib/rspec/support/object_formatter.rb +3 -28
- data/lib/rspec/support/recursive_const_methods.rb +16 -39
- data/lib/rspec/support/reentrant_mutex.rb +16 -46
- data/lib/rspec/support/ruby_features.rb +18 -138
- data/lib/rspec/support/source.rb +3 -12
- data/lib/rspec/support/spec/coverage.rb +50 -0
- data/lib/rspec/support/spec/in_sub_process.rb +1 -1
- data/lib/rspec/support/spec/library_wide_checks.rb +9 -12
- data/lib/rspec/support/spec/shell_out.rb +3 -25
- data/lib/rspec/support/spec/stderr_splitter.rb +7 -20
- data/lib/rspec/support/spec/string_matcher.rb +18 -28
- data/lib/rspec/support/spec.rb +3 -41
- data/lib/rspec/support/version.rb +1 -1
- data/lib/rspec/support/with_keywords_when_needed.rb +8 -17
- data/lib/rspec/support.rb +21 -51
- data.tar.gz.sig +0 -0
- metadata +21 -50
- metadata.gz.sig +0 -0
- data/lib/rspec/support/mutex.rb +0 -75
- data/lib/rspec/support/spec/diff_helpers.rb +0 -45
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc1363792fc66b3235686119b09af82568e6ab339f0cdd2835e92988f8693024
|
|
4
|
+
data.tar.gz: a375231249ed4d4b3b8b0d2a96e57f25497361aa048c5d850bcf80c12422ad21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e234899c929288a0a1908133573bb90225430716a58ec1fc4d63ac11a1603dde06db862dd536a490d0a35f833feaf6afc0afe7f6e7533fa18f8cdfc6f42647b3
|
|
7
|
+
data.tar.gz: 7d20632c52abfac745d28100ef5d41449c05bfdf97a78ee2723a73d2c3d13129cf24794cfc581e7bc4c5b4f1085556ad0d0613f1847d27ef39295552e6bf683e
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/Changelog.md
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
### Development
|
|
2
|
-
[Full Changelog](https://github.com/rspec/rspec/compare/rspec-support-v3.13.6...
|
|
2
|
+
[Full Changelog](https://github.com/rspec/rspec/compare/rspec-support-v3.13.6...main)
|
|
3
|
+
|
|
4
|
+
# 4.0.0.beta1 / 2026-02-18
|
|
5
|
+
|
|
6
|
+
Breaking Changes:
|
|
7
|
+
|
|
8
|
+
* Ruby < 3.0 is no longer supported. (Phil Pirozhkov, Jon Rowe, rspec/rspec-support#436, rspec/rspec#258)
|
|
9
|
+
* Removed unused `RSpec::Support::ObjectFormatter::BigDecimalInspector`. (Jon Rowe, rspec/rspec#254)
|
|
10
|
+
* Remove unsupported `RSpec::Support::RubyFeatures.supports_taint?` check. (Jon Rowe, rspec/rspec#255)
|
|
11
|
+
* Remove unsupported `RSpec::Support::RubyFeatures.kw_arg_separation?` check. (Jon Rowe, rspec/rspec#256)
|
|
12
|
+
* Remove unsupported `RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash?` check. (Jon Rowe, rspec/rspec#257)
|
|
13
|
+
|
|
14
|
+
Enhancements
|
|
15
|
+
|
|
16
|
+
* Improve diff for `anything` matchers by hiding the value "anything" is matched
|
|
17
|
+
against. (Karl Heitmann, rspec/rspec-mocks#599)
|
|
18
|
+
|
|
19
|
+
Bug Fixes:
|
|
20
|
+
|
|
21
|
+
* Switch current thread data to alias/accessors to avoid issues with mocked systems.
|
|
22
|
+
(Jon Rowe, #610)
|
|
23
|
+
* Always return a boolean from `RSpec::Support::Ruby.rbx?` and
|
|
24
|
+
`RSpec::Support::Ruby.truffleruby?` (Phil Pirozhkov, rspec/rspec-support#486)
|
|
3
25
|
|
|
4
26
|
### 3.13.7 / 2026-01-28
|
|
5
27
|
[Full Changelog](http://github.com/rspec/rspec/compare/rspec-support-v3.13.6...rspec-support-v3.13.7)
|
|
6
28
|
|
|
7
29
|
Bug Fixes:
|
|
8
|
-
|
|
9
30
|
* Stop fuzzy matching causing false positives from Range objects. (Eric Mueller, rspec/rspec#298)
|
|
10
31
|
|
|
11
32
|
### 3.13.6 / 2025-08-18
|
data/LICENSE.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
|
-
|
|
2
|
+
=====================
|
|
3
3
|
|
|
4
4
|
* Copyright © 2013 David Chelimsky, Myron Marston, Jon Rowe, Sam Phippen, Xavier Shay, Bradley Schaefer
|
|
5
|
+
* Copyright © 2012 David Chelimsky, Myron Marston
|
|
6
|
+
* Copyright © 2006 David Chelimsky, The RSpec Development Team
|
|
7
|
+
* Copyright © 2005 Steven Baker
|
|
5
8
|
|
|
6
9
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
7
10
|
a copy of this software and associated documentation files (the
|
|
@@ -16,8 +19,8 @@ included in all copies or substantial portions of the Software.
|
|
|
16
19
|
|
|
17
20
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
21
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
23
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
24
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
25
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
26
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,40 +1,8 @@
|
|
|
1
|
-
# RSpec::Support
|
|
1
|
+
# RSpec::Support
|
|
2
2
|
|
|
3
|
-
`
|
|
4
|
-
`RSpec::Expectations` and `RSpec::Mocks`. It is considered
|
|
5
|
-
suitable for internal use only at this time.
|
|
3
|
+
This is the readme for `rspec-support`, there isn't anything to see here but see also:
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Want to run against the `main` branch? You'll need to include the dependent
|
|
12
|
-
RSpec repos as well. Add the following to your `Gemfile`:
|
|
13
|
-
|
|
14
|
-
```ruby
|
|
15
|
-
%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
|
|
16
|
-
gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
|
|
17
|
-
end
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Contributing
|
|
21
|
-
|
|
22
|
-
Once you've set up the environment, you'll need to cd into the working
|
|
23
|
-
directory of whichever repo you want to work in. From there you can run the
|
|
24
|
-
specs and cucumber features, and make patches.
|
|
25
|
-
|
|
26
|
-
NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You
|
|
27
|
-
can treat each RSpec repo as an independent project.
|
|
28
|
-
|
|
29
|
-
- [Build details](BUILD_DETAIL.md)
|
|
30
|
-
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
31
|
-
- [Detailed contributing guide](CONTRIBUTING.md)
|
|
32
|
-
- [Development setup guide](DEVELOPMENT.md)
|
|
33
|
-
|
|
34
|
-
## Patches
|
|
35
|
-
|
|
36
|
-
Please submit a pull request or a github issue. If you submit an issue, please
|
|
37
|
-
include a link to either of:
|
|
38
|
-
|
|
39
|
-
* a gist (or equivalent) of the patch
|
|
40
|
-
* a branch or commit in your github fork of the repo
|
|
5
|
+
* [The combined readme](../README.md)
|
|
6
|
+
* [rspec-core](../rspec-core/README.md)
|
|
7
|
+
* [rspec-expectations](../rspec-expectations/README.md)
|
|
8
|
+
* [rspec-mocks](../rspec-mocks/README.md)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
RSpec::Support.require_rspec_support "ruby_features"
|
|
4
|
-
|
|
5
3
|
module RSpec
|
|
6
4
|
# Consistent implementation for "cleaning" the caller method to strip out
|
|
7
5
|
# non-rspec lines. This enables errors to be reported at the call site in
|
|
@@ -29,56 +27,47 @@ module RSpec
|
|
|
29
27
|
# with this complexity in our `RSpec.deprecate` calls, so we ignore it here.
|
|
30
28
|
IGNORE_REGEX = Regexp.union(LIB_REGEX, "rubygems/core_ext/kernel_require.rb", "<internal:", %r{/lib/ruby/[^/]+/bundled_gems\.rb})
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# lib/rspec/support/caller_filter.rb:62:in `first_non_rspec_line'
|
|
30
|
+
# This supports args because it's more efficient when the caller specifies
|
|
31
|
+
# these. It allows us to skip frames the caller knows are part of RSpec,
|
|
32
|
+
# and to decrease the increment size if the caller is confident the line will
|
|
33
|
+
# be found in a small number of stack frames from `skip_frames`.
|
|
34
|
+
#
|
|
35
|
+
# Note that there is a risk to passing a `skip_frames` value that is too high:
|
|
36
|
+
# If it skipped the first non-rspec line, then this method would return the
|
|
37
|
+
# 2nd or 3rd (or whatever) non-rspec line. Thus, you generally shouldn't pass
|
|
38
|
+
# values for these parameters, particularly since most places that use this are
|
|
39
|
+
# not hot spots (generally it gets used for deprecation warnings). However,
|
|
40
|
+
# if you do have a hot spot that calls this, passing `skip_frames` can make
|
|
41
|
+
# a significant difference. Just make sure that that particular use is tested
|
|
42
|
+
# so that if the provided `skip_frames` changes to no longer be accurate in
|
|
43
|
+
# such a way that would return the wrong stack frame, a test will fail to tell you.
|
|
44
|
+
#
|
|
45
|
+
# See benchmarks/skip_frames_for_caller_filter.rb for measurements.
|
|
46
|
+
def self.first_non_rspec_line(skip_frames=3, increment=5)
|
|
47
|
+
# Why a default `skip_frames` of 3?
|
|
48
|
+
# By the time `caller_locations` is called below, the first 3 frames are:
|
|
49
|
+
# lib/rspec/support/caller_filter.rb:63:in `block in first_non_rspec_line'
|
|
50
|
+
# lib/rspec/support/caller_filter.rb:62:in `loop'
|
|
51
|
+
# lib/rspec/support/caller_filter.rb:62:in `first_non_rspec_line'
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# See benchmarks/caller.rb for measurements.
|
|
53
|
+
# `caller` is an expensive method that scales linearly with the size of
|
|
54
|
+
# the stack. The performance hit for fetching it in chunks is small,
|
|
55
|
+
# and since the target line is probably near the top of the stack, the
|
|
56
|
+
# overall improvement of a chunked search like this is significant.
|
|
57
|
+
#
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
# The default increment of 5 for this method are mostly arbitrary, but
|
|
60
|
+
# is chosen to give good performance on the common case of creating a double.
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
loop do
|
|
63
|
+
stack = caller_locations(skip_frames, increment)
|
|
64
|
+
raise "No non-lib lines in stack" unless stack
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
line = stack.find { |l| l.path !~ IGNORE_REGEX }
|
|
67
|
+
return line.to_s if line
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
else
|
|
78
|
-
# Earlier rubies do not support the two argument form of `caller`. This
|
|
79
|
-
# fallback is logically the same, but slower.
|
|
80
|
-
def self.first_non_rspec_line(*)
|
|
81
|
-
caller.find { |line| line !~ IGNORE_REGEX }
|
|
69
|
+
skip_frames += increment
|
|
70
|
+
increment *= 2 # The choice of two here is arbitrary.
|
|
82
71
|
end
|
|
83
72
|
end
|
|
84
73
|
end
|
data/lib/rspec/support/differ.rb
CHANGED
|
@@ -17,6 +17,8 @@ module RSpec
|
|
|
17
17
|
if any_multiline_strings?(actual, expected)
|
|
18
18
|
diff = diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
|
|
19
19
|
end
|
|
20
|
+
elsif all_hashes?(actual, expected)
|
|
21
|
+
diff = diff_hashes_as_object(actual, expected)
|
|
20
22
|
elsif no_procs?(actual, expected) && no_numbers?(actual, expected)
|
|
21
23
|
diff = diff_as_object(actual, expected)
|
|
22
24
|
end
|
|
@@ -55,6 +57,25 @@ module RSpec
|
|
|
55
57
|
end
|
|
56
58
|
# rubocop:enable Metrics/MethodLength
|
|
57
59
|
|
|
60
|
+
if defined?(RSpec::Mocks::ArgumentMatchers::AnyArgMatcher)
|
|
61
|
+
def diff_hashes_as_object(actual, expected)
|
|
62
|
+
actual_to_diff =
|
|
63
|
+
actual.keys.reduce({}) do |hash, key|
|
|
64
|
+
if RSpec::Mocks::ArgumentMatchers::AnyArgMatcher === expected[key]
|
|
65
|
+
hash[key] = expected[key]
|
|
66
|
+
else
|
|
67
|
+
hash[key] = actual[key]
|
|
68
|
+
end
|
|
69
|
+
hash
|
|
70
|
+
end
|
|
71
|
+
diff_as_object(actual_to_diff, expected)
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
def diff_hashes_as_object(actual, expected)
|
|
75
|
+
diff_as_object(actual, expected)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
58
79
|
def diff_as_object(actual, expected)
|
|
59
80
|
actual_as_string = object_to_string(actual)
|
|
60
81
|
expected_as_string = object_to_string(expected)
|
|
@@ -76,6 +97,10 @@ module RSpec
|
|
|
76
97
|
safely_flatten(args).none? { |a| Proc === a }
|
|
77
98
|
end
|
|
78
99
|
|
|
100
|
+
def all_hashes?(actual, expected)
|
|
101
|
+
(Hash === actual) && (Hash === expected)
|
|
102
|
+
end
|
|
103
|
+
|
|
79
104
|
def all_strings?(*args)
|
|
80
105
|
safely_flatten(args).all? { |a| String === a }
|
|
81
106
|
end
|
|
@@ -103,14 +128,8 @@ module RSpec
|
|
|
103
128
|
end
|
|
104
129
|
end
|
|
105
130
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
string.include?("\n".encode(string.encoding))
|
|
109
|
-
end
|
|
110
|
-
else
|
|
111
|
-
def multiline?(string)
|
|
112
|
-
string.include?("\n")
|
|
113
|
-
end
|
|
131
|
+
def multiline?(string)
|
|
132
|
+
string.include?("\n".encode(string.encoding))
|
|
114
133
|
end
|
|
115
134
|
|
|
116
135
|
def build_hunks(actual, expected)
|
|
@@ -201,13 +220,13 @@ module RSpec
|
|
|
201
220
|
end
|
|
202
221
|
|
|
203
222
|
def handle_encoding_errors(actual, expected)
|
|
204
|
-
if actual.source_encoding
|
|
205
|
-
"Could not produce a diff because the encoding of the actual string " \
|
|
206
|
-
"(#{actual.source_encoding}) differs from the encoding of the expected " \
|
|
207
|
-
"string (#{expected.source_encoding})"
|
|
208
|
-
else
|
|
223
|
+
if actual.source_encoding == expected.source_encoding
|
|
209
224
|
"Could not produce a diff because of the encoding of the string " \
|
|
210
|
-
|
|
225
|
+
"(#{expected.source_encoding})"
|
|
226
|
+
else
|
|
227
|
+
"Could not produce a diff because the encoding of the actual string " \
|
|
228
|
+
"(#{actual.source_encoding}) differs from the encoding of the expected " \
|
|
229
|
+
"string (#{expected.source_encoding})"
|
|
211
230
|
end
|
|
212
231
|
end
|
|
213
232
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
RSpec::Support.require_rspec_support "ruby_features"
|
|
3
4
|
module RSpec
|
|
4
5
|
module Support
|
|
5
6
|
# @private
|
|
@@ -13,6 +14,10 @@ module RSpec
|
|
|
13
14
|
# ? ("\x3F")
|
|
14
15
|
REPLACE = "?"
|
|
15
16
|
|
|
17
|
+
def self.pick_encoding(source_a, source_b)
|
|
18
|
+
Encoding.compatible?(source_a, source_b) || Encoding.default_external
|
|
19
|
+
end
|
|
20
|
+
|
|
16
21
|
def initialize(string, encoding=nil)
|
|
17
22
|
@encoding = encoding
|
|
18
23
|
@source_encoding = detect_source_encoding(string)
|
|
@@ -48,12 +53,50 @@ module RSpec
|
|
|
48
53
|
end
|
|
49
54
|
alias :to_str :to_s
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Encoding Exceptions:
|
|
59
|
+
#
|
|
60
|
+
# Raised by Encoding and String methods:
|
|
61
|
+
# Encoding::UndefinedConversionError:
|
|
62
|
+
# when a transcoding operation fails
|
|
63
|
+
# if the String contains characters invalid for the target encoding
|
|
64
|
+
# e.g. "\x80".encode('UTF-8','ASCII-8BIT')
|
|
65
|
+
# vs "\x80".encode('UTF-8','ASCII-8BIT', undef: :replace, replace: '<undef>')
|
|
66
|
+
# # => '<undef>'
|
|
67
|
+
# Encoding::CompatibilityError
|
|
68
|
+
# when Encoding.compatible?(str1, str2) is nil
|
|
69
|
+
# e.g. utf_16le_emoji_string.split("\n")
|
|
70
|
+
# e.g. valid_unicode_string.encode(utf8_encoding) << ascii_string
|
|
71
|
+
# Encoding::InvalidByteSequenceError:
|
|
72
|
+
# when the string being transcoded contains a byte invalid for
|
|
73
|
+
# either the source or target encoding
|
|
74
|
+
# e.g. "\x80".encode('UTF-8','US-ASCII')
|
|
75
|
+
# vs "\x80".encode('UTF-8','US-ASCII', invalid: :replace, replace: '<byte>')
|
|
76
|
+
# # => '<byte>'
|
|
77
|
+
# ArgumentError
|
|
78
|
+
# when operating on a string with invalid bytes
|
|
79
|
+
# e.g."\x80".split("\n")
|
|
80
|
+
# TypeError
|
|
81
|
+
# when a symbol is passed as an encoding
|
|
82
|
+
# Encoding.find(:"UTF-8")
|
|
83
|
+
# when calling force_encoding on an object
|
|
84
|
+
# that doesn't respond to #to_str
|
|
85
|
+
#
|
|
86
|
+
# Raised by transcoding methods:
|
|
87
|
+
# Encoding::ConverterNotFoundError:
|
|
88
|
+
# when a named encoding does not correspond with a known converter
|
|
89
|
+
# e.g. 'abc'.force_encoding('UTF-8').encode('foo')
|
|
90
|
+
# or a converter path cannot be found
|
|
91
|
+
# e.g. "\x80".force_encoding('ASCII-8BIT').encode('Emacs-Mule')
|
|
92
|
+
#
|
|
93
|
+
# Raised by byte <-> char conversions
|
|
94
|
+
# RangeError: out of char range
|
|
95
|
+
# e.g. the UTF-16LE emoji: 128169.chr
|
|
96
|
+
def matching_encoding(string)
|
|
97
|
+
string = remove_invalid_bytes(string)
|
|
98
|
+
string.encode(@encoding)
|
|
99
|
+
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
|
|
57
100
|
# Raised by Encoding and String methods:
|
|
58
101
|
# Encoding::UndefinedConversionError:
|
|
59
102
|
# when a transcoding operation fails
|
|
@@ -80,83 +123,23 @@ module RSpec
|
|
|
80
123
|
# when calling force_encoding on an object
|
|
81
124
|
# that doesn't respond to #to_str
|
|
82
125
|
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
# when a named encoding does not correspond with a known converter
|
|
86
|
-
# e.g. 'abc'.force_encoding('UTF-8').encode('foo')
|
|
87
|
-
# or a converter path cannot be found
|
|
88
|
-
# e.g. "\x80".force_encoding('ASCII-8BIT').encode('Emacs-Mule')
|
|
126
|
+
# For example, given:
|
|
127
|
+
# "\x80".force_encoding("Emacs-Mule").encode(:invalid => :replace).bytes.to_a
|
|
89
128
|
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# Originally defined as a constant to avoid unneeded allocations, this hash must
|
|
98
|
-
# be defined inline (without {}) to avoid warnings on Ruby 2.7
|
|
99
|
-
#
|
|
100
|
-
# In MRI 2.1 'invalid: :replace' changed to also replace an invalid byte sequence
|
|
101
|
-
# see https://github.com/ruby/ruby/blob/v2_1_0/NEWS#L176
|
|
102
|
-
# https://www.ruby-forum.com/topic/6861247
|
|
103
|
-
# https://twitter.com/nalsh/status/553413844685438976
|
|
104
|
-
#
|
|
105
|
-
# For example, given:
|
|
106
|
-
# "\x80".force_encoding("Emacs-Mule").encode(:invalid => :replace).bytes.to_a
|
|
107
|
-
#
|
|
108
|
-
# On MRI 2.1 or above: 63 # '?'
|
|
109
|
-
# else : 128 # "\x80"
|
|
110
|
-
#
|
|
111
|
-
string.encode(@encoding, :invalid => :replace, :undef => :replace, :replace => REPLACE)
|
|
112
|
-
rescue Encoding::ConverterNotFoundError
|
|
113
|
-
# Originally defined as a constant to avoid unneeded allocations, this hash must
|
|
114
|
-
# be defined inline (without {}) to avoid warnings on Ruby 2.7
|
|
115
|
-
string.dup.force_encoding(@encoding).encode(:invalid => :replace, :replace => REPLACE)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Prevents raising ArgumentError
|
|
119
|
-
if String.method_defined?(:scrub)
|
|
120
|
-
# https://github.com/ruby/ruby/blob/eeb05e8c11/doc/NEWS-2.1.0#L120-L123
|
|
121
|
-
# https://github.com/ruby/ruby/blob/v2_1_0/string.c#L8242
|
|
122
|
-
# https://github.com/hsbt/string-scrub
|
|
123
|
-
# https://github.com/rubinius/rubinius/blob/v2.5.2/kernel/common/string.rb#L1913-L1972
|
|
124
|
-
def remove_invalid_bytes(string)
|
|
125
|
-
string.scrub(REPLACE)
|
|
126
|
-
end
|
|
127
|
-
else
|
|
128
|
-
# http://stackoverflow.com/a/8711118/879854
|
|
129
|
-
# Loop over chars in a string replacing chars
|
|
130
|
-
# with invalid encoding, which is a pretty good proxy
|
|
131
|
-
# for the invalid byte sequence that causes an ArgumentError
|
|
132
|
-
def remove_invalid_bytes(string)
|
|
133
|
-
string.chars.map do |char|
|
|
134
|
-
char.valid_encoding? ? char : REPLACE
|
|
135
|
-
end.join
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def detect_source_encoding(string)
|
|
140
|
-
string.encoding
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def self.pick_encoding(source_a, source_b)
|
|
144
|
-
Encoding.compatible?(source_a, source_b) || Encoding.default_external
|
|
145
|
-
end
|
|
146
|
-
else
|
|
147
|
-
|
|
148
|
-
def self.pick_encoding(_source_a, _source_b)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
private
|
|
129
|
+
# On MRI 2.1 or above: 63 # '?'
|
|
130
|
+
# else : 128 # "\x80"
|
|
131
|
+
#
|
|
132
|
+
string.encode(@encoding, :invalid => :replace, :undef => :replace, :replace => REPLACE)
|
|
133
|
+
rescue Encoding::ConverterNotFoundError
|
|
134
|
+
string.dup.force_encoding(@encoding).encode(:invalid => :replace, :replace => REPLACE)
|
|
135
|
+
end
|
|
152
136
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
137
|
+
def remove_invalid_bytes(string)
|
|
138
|
+
string.scrub(REPLACE)
|
|
139
|
+
end
|
|
156
140
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
end
|
|
141
|
+
def detect_source_encoding(string)
|
|
142
|
+
string.encoding
|
|
160
143
|
end
|
|
161
144
|
end
|
|
162
145
|
end
|
|
@@ -22,8 +22,7 @@ module RSpec
|
|
|
22
22
|
begin
|
|
23
23
|
expected === actual
|
|
24
24
|
rescue ArgumentError
|
|
25
|
-
# Some objects, like 0-arg lambdas
|
|
26
|
-
# ArgumentError for `expected === actual`.
|
|
25
|
+
# Some objects, like 0-arg lambdas, raise ArgumentError when compared with ===.
|
|
27
26
|
false
|
|
28
27
|
end
|
|
29
28
|
end
|