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
data/lib/rspec/support/differ.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
RSpec::Support.require_rspec_support 'encoded_string'
|
2
2
|
RSpec::Support.require_rspec_support 'hunk_generator'
|
3
|
+
RSpec::Support.require_rspec_support "object_formatter"
|
3
4
|
|
4
5
|
require 'pp'
|
5
6
|
|
6
7
|
module RSpec
|
7
8
|
module Support
|
9
|
+
# rubocop:disable Metrics/ClassLength
|
8
10
|
class Differ
|
9
11
|
def diff(actual, expected)
|
10
12
|
diff = ""
|
11
13
|
|
12
|
-
|
14
|
+
unless actual.nil? || expected.nil?
|
13
15
|
if all_strings?(actual, expected)
|
14
16
|
if any_multiline_strings?(actual, expected)
|
15
17
|
diff = diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
|
@@ -22,34 +24,35 @@ module RSpec
|
|
22
24
|
diff.to_s
|
23
25
|
end
|
24
26
|
|
27
|
+
# rubocop:disable Metrics/MethodLength
|
25
28
|
def diff_as_string(actual, expected)
|
26
|
-
|
29
|
+
encoding = EncodedString.pick_encoding(actual, expected)
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
actual = EncodedString.new(actual, encoding)
|
32
|
+
expected = EncodedString.new(expected, encoding)
|
30
33
|
|
31
|
-
output = EncodedString.new("\n",
|
34
|
+
output = EncodedString.new("\n", encoding)
|
35
|
+
hunks = build_hunks(actual, expected)
|
32
36
|
|
33
37
|
hunks.each_cons(2) do |prev_hunk, current_hunk|
|
34
38
|
begin
|
35
39
|
if current_hunk.overlaps?(prev_hunk)
|
36
40
|
add_old_hunk_to_hunk(current_hunk, prev_hunk)
|
37
41
|
else
|
38
|
-
add_to_output(output, prev_hunk.diff(
|
42
|
+
add_to_output(output, prev_hunk.diff(format_type).to_s)
|
39
43
|
end
|
40
44
|
ensure
|
41
45
|
add_to_output(output, "\n")
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
45
|
-
if hunks.last
|
46
|
-
finalize_output(output, hunks.last.diff(format).to_s)
|
47
|
-
end
|
49
|
+
finalize_output(output, hunks.last.diff(format_type).to_s) if hunks.last
|
48
50
|
|
49
51
|
color_diff output
|
50
52
|
rescue Encoding::CompatibilityError
|
51
|
-
handle_encoding_errors
|
53
|
+
handle_encoding_errors(actual, expected)
|
52
54
|
end
|
55
|
+
# rubocop:enable Metrics/MethodLength
|
53
56
|
|
54
57
|
def diff_as_object(actual, expected)
|
55
58
|
actual_as_string = object_to_string(actual)
|
@@ -57,8 +60,9 @@ module RSpec
|
|
57
60
|
diff_as_string(actual_as_string, expected_as_string)
|
58
61
|
end
|
59
62
|
|
60
|
-
|
61
|
-
|
63
|
+
def color?
|
64
|
+
@color
|
65
|
+
end
|
62
66
|
|
63
67
|
def initialize(opts={})
|
64
68
|
@color = opts.fetch(:color, false)
|
@@ -68,19 +72,19 @@ module RSpec
|
|
68
72
|
private
|
69
73
|
|
70
74
|
def no_procs?(*args)
|
71
|
-
args.
|
75
|
+
safely_flatten(args).none? { |a| Proc === a }
|
72
76
|
end
|
73
77
|
|
74
78
|
def all_strings?(*args)
|
75
|
-
args.
|
79
|
+
safely_flatten(args).all? { |a| String === a }
|
76
80
|
end
|
77
81
|
|
78
82
|
def any_multiline_strings?(*args)
|
79
|
-
all_strings?(*args) && args.
|
83
|
+
all_strings?(*args) && safely_flatten(args).any? { |a| multiline?(a) }
|
80
84
|
end
|
81
85
|
|
82
86
|
def no_numbers?(*args)
|
83
|
-
args.
|
87
|
+
safely_flatten(args).none? { |a| Numeric === a }
|
84
88
|
end
|
85
89
|
|
86
90
|
def coerce_to_string(string_or_array)
|
@@ -93,7 +97,7 @@ module RSpec
|
|
93
97
|
if Array === entry
|
94
98
|
entry.inspect
|
95
99
|
else
|
96
|
-
entry.to_s.gsub("\n", "\\n")
|
100
|
+
entry.to_s.gsub("\n", "\\n").gsub("\r", "\\r")
|
97
101
|
end
|
98
102
|
end
|
99
103
|
end
|
@@ -108,8 +112,8 @@ module RSpec
|
|
108
112
|
end
|
109
113
|
end
|
110
114
|
|
111
|
-
def
|
112
|
-
|
115
|
+
def build_hunks(actual, expected)
|
116
|
+
HunkGenerator.new(actual, expected).hunks
|
113
117
|
end
|
114
118
|
|
115
119
|
def finalize_output(output, final_line)
|
@@ -125,7 +129,12 @@ module RSpec
|
|
125
129
|
hunk.merge(oldhunk)
|
126
130
|
end
|
127
131
|
|
128
|
-
def
|
132
|
+
def safely_flatten(array)
|
133
|
+
array = array.flatten(1) until (array == array.flatten(1))
|
134
|
+
array
|
135
|
+
end
|
136
|
+
|
137
|
+
def format_type
|
129
138
|
:unified
|
130
139
|
end
|
131
140
|
|
@@ -152,7 +161,7 @@ module RSpec
|
|
152
161
|
def color_diff(diff)
|
153
162
|
return diff unless color?
|
154
163
|
|
155
|
-
diff.lines.map
|
164
|
+
diff.lines.map do |line|
|
156
165
|
case line[0].chr
|
157
166
|
when "+"
|
158
167
|
green line
|
@@ -163,43 +172,44 @@ module RSpec
|
|
163
172
|
else
|
164
173
|
normal(line)
|
165
174
|
end
|
166
|
-
|
175
|
+
end.join
|
167
176
|
end
|
168
177
|
|
169
178
|
def object_to_string(object)
|
170
179
|
object = @object_preparer.call(object)
|
171
180
|
case object
|
172
181
|
when Hash
|
173
|
-
object
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
"#{pp_key} => #{pp_value},"
|
178
|
-
end.join("\n")
|
182
|
+
hash_to_string(object)
|
183
|
+
when Array
|
184
|
+
PP.pp(ObjectFormatter.prepare_for_inspection(object), "".dup)
|
179
185
|
when String
|
180
186
|
object =~ /\n/ ? object : object.inspect
|
181
187
|
else
|
182
|
-
PP.pp(object,"")
|
188
|
+
PP.pp(object, "".dup)
|
183
189
|
end
|
184
190
|
end
|
185
191
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
192
|
+
def hash_to_string(hash)
|
193
|
+
formatted_hash = ObjectFormatter.prepare_for_inspection(hash)
|
194
|
+
formatted_hash.keys.sort_by { |k| k.to_s }.map do |key|
|
195
|
+
pp_key = PP.singleline_pp(key, "".dup)
|
196
|
+
pp_value = PP.singleline_pp(formatted_hash[key], "".dup)
|
197
|
+
|
198
|
+
"#{pp_key} => #{pp_value},"
|
199
|
+
end.join("\n")
|
193
200
|
end
|
194
201
|
|
195
|
-
def handle_encoding_errors
|
196
|
-
if
|
197
|
-
"Could not produce a diff because the encoding of the actual string
|
198
|
-
|
202
|
+
def handle_encoding_errors(actual, expected)
|
203
|
+
if actual.source_encoding != expected.source_encoding
|
204
|
+
"Could not produce a diff because the encoding of the actual string " \
|
205
|
+
"(#{actual.source_encoding}) differs from the encoding of the expected " \
|
206
|
+
"string (#{expected.source_encoding})"
|
199
207
|
else
|
200
|
-
"Could not produce a diff because of the encoding of the string
|
208
|
+
"Could not produce a diff because of the encoding of the string " \
|
209
|
+
"(#{expected.source_encoding})"
|
201
210
|
end
|
202
211
|
end
|
203
212
|
end
|
213
|
+
# rubocop:enable Metrics/ClassLength
|
204
214
|
end
|
205
215
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
RSpec::Support.require_rspec_support 'ruby_features'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Support
|
5
|
+
# @api private
|
6
|
+
#
|
7
|
+
# Replacement for fileutils#mkdir_p because we don't want to require parts
|
8
|
+
# of stdlib in RSpec.
|
9
|
+
class DirectoryMaker
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
# Implements nested directory construction
|
13
|
+
def self.mkdir_p(path)
|
14
|
+
stack = generate_stack(path)
|
15
|
+
path.split(File::SEPARATOR).each do |part|
|
16
|
+
stack = generate_path(stack, part)
|
17
|
+
begin
|
18
|
+
Dir.mkdir(stack) unless directory_exists?(stack)
|
19
|
+
rescue Errno::EEXIST => e
|
20
|
+
raise e unless directory_exists?(stack)
|
21
|
+
rescue Errno::ENOTDIR => e
|
22
|
+
raise Errno::EEXIST, e.message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if OS.windows_file_path?
|
28
|
+
def self.generate_stack(path)
|
29
|
+
if path.start_with?(File::SEPARATOR)
|
30
|
+
File::SEPARATOR
|
31
|
+
elsif path[1] == ':'
|
32
|
+
''
|
33
|
+
else
|
34
|
+
'.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
def self.generate_path(stack, part)
|
38
|
+
if stack == ''
|
39
|
+
part
|
40
|
+
elsif stack == File::SEPARATOR
|
41
|
+
File.join('', part)
|
42
|
+
else
|
43
|
+
File.join(stack, part)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
else
|
47
|
+
def self.generate_stack(path)
|
48
|
+
path.start_with?(File::SEPARATOR) ? File::SEPARATOR : "."
|
49
|
+
end
|
50
|
+
def self.generate_path(stack, part)
|
51
|
+
File.join(stack, part)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.directory_exists?(dirname)
|
56
|
+
File.exist?(dirname) && File.directory?(dirname)
|
57
|
+
end
|
58
|
+
private_class_method :directory_exists?
|
59
|
+
private_class_method :generate_stack
|
60
|
+
private_class_method :generate_path
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -2,10 +2,16 @@ module RSpec
|
|
2
2
|
module Support
|
3
3
|
# @private
|
4
4
|
class EncodedString
|
5
|
+
# Reduce allocations by storing constants.
|
6
|
+
UTF_8 = "UTF-8"
|
7
|
+
US_ASCII = "US-ASCII"
|
5
8
|
|
6
|
-
|
9
|
+
# Ruby's default replacement string is:
|
10
|
+
# U+FFFD ("\xEF\xBF\xBD"), for Unicode encoding forms, else
|
11
|
+
# ? ("\x3F")
|
12
|
+
REPLACE = "?"
|
7
13
|
|
8
|
-
def initialize(string, encoding
|
14
|
+
def initialize(string, encoding=nil)
|
9
15
|
@encoding = encoding
|
10
16
|
@source_encoding = detect_source_encoding(string)
|
11
17
|
@string = matching_encoding(string)
|
@@ -21,8 +27,18 @@ module RSpec
|
|
21
27
|
@string << matching_encoding(string)
|
22
28
|
end
|
23
29
|
|
24
|
-
|
25
|
-
|
30
|
+
if Ruby.jruby?
|
31
|
+
def split(regex_or_string)
|
32
|
+
@string.split(matching_encoding(regex_or_string))
|
33
|
+
rescue ArgumentError
|
34
|
+
# JRuby raises an ArgumentError when splitting a source string that
|
35
|
+
# contains invalid bytes.
|
36
|
+
remove_invalid_bytes(@string).split regex_or_string
|
37
|
+
end
|
38
|
+
else
|
39
|
+
def split(regex_or_string)
|
40
|
+
@string.split(matching_encoding(regex_or_string))
|
41
|
+
end
|
26
42
|
end
|
27
43
|
|
28
44
|
def to_s
|
@@ -30,35 +46,114 @@ module RSpec
|
|
30
46
|
end
|
31
47
|
alias :to_str :to_s
|
32
48
|
|
33
|
-
private
|
34
|
-
|
35
49
|
if String.method_defined?(:encoding)
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Encoding Exceptions:
|
54
|
+
#
|
55
|
+
# Raised by Encoding and String methods:
|
56
|
+
# Encoding::UndefinedConversionError:
|
57
|
+
# when a transcoding operation fails
|
58
|
+
# if the String contains characters invalid for the target encoding
|
59
|
+
# e.g. "\x80".encode('UTF-8','ASCII-8BIT')
|
60
|
+
# vs "\x80".encode('UTF-8','ASCII-8BIT', undef: :replace, replace: '<undef>')
|
61
|
+
# # => '<undef>'
|
62
|
+
# Encoding::CompatibilityError
|
63
|
+
# when Encoding.compatible?(str1, str2) is nil
|
64
|
+
# e.g. utf_16le_emoji_string.split("\n")
|
65
|
+
# e.g. valid_unicode_string.encode(utf8_encoding) << ascii_string
|
66
|
+
# Encoding::InvalidByteSequenceError:
|
67
|
+
# when the string being transcoded contains a byte invalid for
|
68
|
+
# either the source or target encoding
|
69
|
+
# e.g. "\x80".encode('UTF-8','US-ASCII')
|
70
|
+
# vs "\x80".encode('UTF-8','US-ASCII', invalid: :replace, replace: '<byte>')
|
71
|
+
# # => '<byte>'
|
72
|
+
# ArgumentError
|
73
|
+
# when operating on a string with invalid bytes
|
74
|
+
# e.g."\x80".split("\n")
|
75
|
+
# TypeError
|
76
|
+
# when a symbol is passed as an encoding
|
77
|
+
# Encoding.find(:"UTF-8")
|
78
|
+
# when calling force_encoding on an object
|
79
|
+
# that doesn't respond to #to_str
|
80
|
+
#
|
81
|
+
# Raised by transcoding methods:
|
82
|
+
# Encoding::ConverterNotFoundError:
|
83
|
+
# when a named encoding does not correspond with a known converter
|
84
|
+
# e.g. 'abc'.force_encoding('UTF-8').encode('foo')
|
85
|
+
# or a converter path cannot be found
|
86
|
+
# e.g. "\x80".force_encoding('ASCII-8BIT').encode('Emacs-Mule')
|
87
|
+
#
|
88
|
+
# Raised by byte <-> char conversions
|
89
|
+
# RangeError: out of char range
|
90
|
+
# e.g. the UTF-16LE emoji: 128169.chr
|
36
91
|
def matching_encoding(string)
|
92
|
+
string = remove_invalid_bytes(string)
|
37
93
|
string.encode(@encoding)
|
38
94
|
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
|
39
|
-
|
95
|
+
# Originally defined as a constant to avoid unneeded allocations, this hash must
|
96
|
+
# be defined inline (without {}) to avoid warnings on Ruby 2.7
|
97
|
+
#
|
98
|
+
# In MRI 2.1 'invalid: :replace' changed to also replace an invalid byte sequence
|
99
|
+
# see https://github.com/ruby/ruby/blob/v2_1_0/NEWS#L176
|
100
|
+
# https://www.ruby-forum.com/topic/6861247
|
101
|
+
# https://twitter.com/nalsh/status/553413844685438976
|
102
|
+
#
|
103
|
+
# For example, given:
|
104
|
+
# "\x80".force_encoding("Emacs-Mule").encode(:invalid => :replace).bytes.to_a
|
105
|
+
#
|
106
|
+
# On MRI 2.1 or above: 63 # '?'
|
107
|
+
# else : 128 # "\x80"
|
108
|
+
#
|
109
|
+
string.encode(@encoding, :invalid => :replace, :undef => :replace, :replace => REPLACE)
|
40
110
|
rescue Encoding::ConverterNotFoundError
|
41
|
-
|
111
|
+
# Originally defined as a constant to avoid unneeded allocations, this hash must
|
112
|
+
# be defined inline (without {}) to avoid warnings on Ruby 2.7
|
113
|
+
string.dup.force_encoding(@encoding).encode(:invalid => :replace, :replace => REPLACE)
|
42
114
|
end
|
43
115
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
116
|
+
# Prevents raising ArgumentError
|
117
|
+
if String.method_defined?(:scrub)
|
118
|
+
# https://github.com/ruby/ruby/blob/eeb05e8c11/doc/NEWS-2.1.0#L120-L123
|
119
|
+
# https://github.com/ruby/ruby/blob/v2_1_0/string.c#L8242
|
120
|
+
# https://github.com/hsbt/string-scrub
|
121
|
+
# https://github.com/rubinius/rubinius/blob/v2.5.2/kernel/common/string.rb#L1913-L1972
|
122
|
+
def remove_invalid_bytes(string)
|
123
|
+
string.scrub(REPLACE)
|
124
|
+
end
|
125
|
+
else
|
126
|
+
# http://stackoverflow.com/a/8711118/879854
|
127
|
+
# Loop over chars in a string replacing chars
|
128
|
+
# with invalid encoding, which is a pretty good proxy
|
129
|
+
# for the invalid byte sequence that causes an ArgumentError
|
130
|
+
def remove_invalid_bytes(string)
|
131
|
+
string.chars.map do |char|
|
132
|
+
char.valid_encoding? ? char : REPLACE
|
133
|
+
end.join
|
49
134
|
end
|
50
135
|
end
|
51
136
|
|
52
137
|
def detect_source_encoding(string)
|
53
138
|
string.encoding
|
54
139
|
end
|
140
|
+
|
141
|
+
def self.pick_encoding(source_a, source_b)
|
142
|
+
Encoding.compatible?(source_a, source_b) || Encoding.default_external
|
143
|
+
end
|
55
144
|
else
|
145
|
+
|
146
|
+
def self.pick_encoding(_source_a, _source_b)
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
56
151
|
def matching_encoding(string)
|
57
152
|
string
|
58
153
|
end
|
59
154
|
|
60
|
-
def detect_source_encoding(
|
61
|
-
|
155
|
+
def detect_source_encoding(_string)
|
156
|
+
US_ASCII
|
62
157
|
end
|
63
158
|
end
|
64
159
|
end
|
@@ -6,14 +6,14 @@ module RSpec
|
|
6
6
|
module FuzzyMatcher
|
7
7
|
# @api private
|
8
8
|
def self.values_match?(expected, actual)
|
9
|
-
if
|
9
|
+
if Hash === actual
|
10
|
+
return hashes_match?(expected, actual) if Hash === expected
|
11
|
+
elsif Array === expected && Enumerable === actual && !(Struct === actual)
|
10
12
|
return arrays_match?(expected, actual.to_a)
|
11
|
-
elsif Hash === expected && Hash === actual
|
12
|
-
return hashes_match?(expected, actual)
|
13
|
-
elsif actual == expected
|
14
|
-
return true
|
15
13
|
end
|
16
14
|
|
15
|
+
return true if expected == actual
|
16
|
+
|
17
17
|
begin
|
18
18
|
expected === actual
|
19
19
|
rescue ArgumentError
|
@@ -46,4 +46,3 @@ module RSpec
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Support
|
3
|
+
# @private
|
4
|
+
def self.matcher_definitions
|
5
|
+
@matcher_definitions ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
# Used internally to break cyclic dependency between mocks, expectations,
|
9
|
+
# and support. We don't currently have a consistent implementation of our
|
10
|
+
# matchers, though we are considering changing that:
|
11
|
+
# https://github.com/rspec/rspec-mocks/issues/513
|
12
|
+
#
|
13
|
+
# @private
|
14
|
+
def self.register_matcher_definition(&block)
|
15
|
+
matcher_definitions << block
|
16
|
+
end
|
17
|
+
|
18
|
+
# Remove a previously registered matcher. Useful for cleaning up after
|
19
|
+
# yourself in specs.
|
20
|
+
#
|
21
|
+
# @private
|
22
|
+
def self.deregister_matcher_definition(&block)
|
23
|
+
matcher_definitions.delete(block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @private
|
27
|
+
def self.is_a_matcher?(object)
|
28
|
+
matcher_definitions.any? { |md| md.call(object) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
#
|
33
|
+
# gives a string representation of an object for use in RSpec descriptions
|
34
|
+
def self.rspec_description_for_object(object)
|
35
|
+
if RSpec::Support.is_a_matcher?(object) && object.respond_to?(:description)
|
36
|
+
object.description
|
37
|
+
else
|
38
|
+
object
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|