rspec-sleeping_king_studios 2.1.1 → 2.2.0.rc.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 +4 -4
- data/CHANGELOG.md +52 -0
- data/DEVELOPMENT.md +26 -17
- data/LICENSE +1 -1
- data/README.md +198 -19
- data/lib/rspec/sleeping_king_studios/configuration.rb +63 -2
- data/lib/rspec/sleeping_king_studios/examples/property_examples.rb +115 -27
- data/lib/rspec/sleeping_king_studios/examples/rspec_matcher_examples.rb +66 -51
- data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors.rb +8 -269
- data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb +268 -0
- data/lib/rspec/sleeping_king_studios/matchers/base_matcher.rb +5 -15
- data/lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of.rb +3 -63
- data/lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of_matcher.rb +65 -0
- data/lib/rspec/sleeping_king_studios/matchers/built_in/include.rb +3 -108
- data/lib/rspec/sleeping_king_studios/matchers/built_in/include_matcher.rb +134 -0
- data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to.rb +3 -258
- data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to_matcher.rb +116 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/alias_method.rb +11 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/alias_method_matcher.rb +107 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/be_boolean.rb +9 -36
- data/lib/rspec/sleeping_king_studios/matchers/core/be_boolean_matcher.rb +37 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/construct.rb +3 -210
- data/lib/rspec/sleeping_king_studios/matchers/core/construct_matcher.rb +113 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/delegate_method.rb +11 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/delegate_method_matcher.rb +311 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_constant.rb +16 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_constant_matcher.rb +225 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_predicate.rb +11 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_predicate_matcher.rb +97 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_property.rb +3 -104
- data/lib/rspec/sleeping_king_studios/matchers/core/have_property_matcher.rb +108 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_reader.rb +3 -74
- data/lib/rspec/sleeping_king_studios/matchers/core/have_reader_matcher.rb +96 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_writer.rb +4 -59
- data/lib/rspec/sleeping_king_studios/matchers/core/have_writer_matcher.rb +55 -0
- data/lib/rspec/sleeping_king_studios/matchers/description.rb +62 -0
- data/lib/rspec/sleeping_king_studios/matchers/macros.rb +32 -0
- data/lib/rspec/sleeping_king_studios/matchers/shared/match_parameters.rb +168 -66
- data/lib/rspec/sleeping_king_studios/matchers/shared/match_property.rb +25 -0
- data/lib/rspec/sleeping_king_studios/matchers.rb +0 -4
- data/lib/rspec/sleeping_king_studios/support/method_signature.rb +51 -0
- data/lib/rspec/sleeping_king_studios/support/method_signature_expectation.rb +158 -0
- data/lib/rspec/sleeping_king_studios/support.rb +9 -0
- data/lib/rspec/sleeping_king_studios/version.rb +10 -4
- metadata +46 -30
@@ -0,0 +1,268 @@
|
|
1
|
+
# lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb
|
2
|
+
require 'rspec/sleeping_king_studios/matchers/base_matcher'
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/active_model'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/active_model/have_errors/error_expectation'
|
5
|
+
require 'sleeping_king_studios/tools/array_tools'
|
6
|
+
require 'sleeping_king_studios/tools/string_tools'
|
7
|
+
|
8
|
+
module RSpec::SleepingKingStudios::Matchers::ActiveModel
|
9
|
+
# Matcher for testing ActiveModel object validations.
|
10
|
+
#
|
11
|
+
# @since 1.0.0
|
12
|
+
class HaveErrorsMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
|
13
|
+
include RSpec::SleepingKingStudios::Matchers::ActiveModel::HaveErrors
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
|
18
|
+
# The error and message expectations are set up through #on and
|
19
|
+
# #with_message.
|
20
|
+
@error_expectations = []
|
21
|
+
end # constructor
|
22
|
+
|
23
|
+
# (see BaseMatcher#description)
|
24
|
+
def description
|
25
|
+
message = 'have errors'
|
26
|
+
|
27
|
+
attribute_messages = @error_expectations.select(&:expected).map do |expectation|
|
28
|
+
attribute_message = ":#{expectation.attribute}"
|
29
|
+
|
30
|
+
error_messages = expectation.messages.select(&:expected).map do |message_expectation|
|
31
|
+
%{"#{message_expectation.message}"}
|
32
|
+
end # map
|
33
|
+
|
34
|
+
unless error_messages.empty?
|
35
|
+
tools = ::SleepingKingStudios::Tools::ArrayTools
|
36
|
+
|
37
|
+
attribute_message <<
|
38
|
+
" with message#{error_messages.count == 1 ? '' : 's'} "\
|
39
|
+
"#{tools.humanize_list error_messages}"
|
40
|
+
end # unless
|
41
|
+
|
42
|
+
attribute_message
|
43
|
+
end # each
|
44
|
+
message << " on #{attribute_messages.join(", and on ")}" unless attribute_messages.empty?
|
45
|
+
|
46
|
+
message
|
47
|
+
end # method description
|
48
|
+
|
49
|
+
# Checks if the object can be validated, whether the object is valid, and
|
50
|
+
# checks the errors on the object against the expected errors and messages
|
51
|
+
# from #on and #with_message, if any.
|
52
|
+
#
|
53
|
+
# @param [Object] actual The object to test against the matcher.
|
54
|
+
#
|
55
|
+
# @return [Boolean] True if the object responds to :valid? and is valid
|
56
|
+
# or object.errors does not match the specified errors and messages (if
|
57
|
+
# any); otherwise false.
|
58
|
+
#
|
59
|
+
# @see #matches?
|
60
|
+
def does_not_match? actual
|
61
|
+
super
|
62
|
+
|
63
|
+
@negative_expectation = true
|
64
|
+
|
65
|
+
return false unless @validates = actual.respond_to?(:valid?)
|
66
|
+
|
67
|
+
!matches?(actual)
|
68
|
+
end # method does_not_match?
|
69
|
+
|
70
|
+
# Checks if the object can be validated, whether the object is valid, and
|
71
|
+
# checks the errors on the object against the expected errors and messages
|
72
|
+
# from #on and #with_message, if any.
|
73
|
+
#
|
74
|
+
# @param [Object] actual The object to test against the matcher.
|
75
|
+
#
|
76
|
+
# @return [Boolean] True if the object responds to :valid?, is not valid,
|
77
|
+
# and object.errors matches the specified errors and messages (if any);
|
78
|
+
# otherwise false.
|
79
|
+
#
|
80
|
+
# @see RSpec::SleepingKingStudios::Matchers::BaseMatcher#matches?
|
81
|
+
def matches? actual
|
82
|
+
super
|
83
|
+
|
84
|
+
return false unless @validates = actual.respond_to?(:valid?)
|
85
|
+
|
86
|
+
!@actual.valid? && attributes_have_errors?
|
87
|
+
end # method matches?
|
88
|
+
|
89
|
+
# Adds an error expectation. If the actual object does not have an error on
|
90
|
+
# the specified attribute, #matches? will return false.
|
91
|
+
#
|
92
|
+
# @param [String, Symbol] attribute
|
93
|
+
#
|
94
|
+
# @return [HaveErrorsMatcher] self
|
95
|
+
#
|
96
|
+
# @example Setting an error expectation
|
97
|
+
# expect(actual).to have_errors.on(:foo)
|
98
|
+
def on attribute
|
99
|
+
@error_expectations << ErrorExpectation.new(attribute)
|
100
|
+
|
101
|
+
self
|
102
|
+
end # method on
|
103
|
+
|
104
|
+
# Adds a message expectation for the most recently added error attribute.
|
105
|
+
# If the actual object does not have an error on the that attribute with
|
106
|
+
# the specified message, #matches? will return false.
|
107
|
+
#
|
108
|
+
# @param [String, Regexp] message The expected error message. If a string,
|
109
|
+
# matcher will check for an exact match; if a regular expression, matcher
|
110
|
+
# will check if the message matches the regexp.
|
111
|
+
#
|
112
|
+
# @raise [ArgumentError] If no error attribute has been added.
|
113
|
+
#
|
114
|
+
# @return [HaveErrorsMatcher] self
|
115
|
+
#
|
116
|
+
# @example Setting an error and a message expectation
|
117
|
+
# expect(actual).to have_errors.on(:foo).with("can't be blank")
|
118
|
+
#
|
119
|
+
# @see #on
|
120
|
+
def with_message message
|
121
|
+
raise ArgumentError.new "no attribute specified for error message" if
|
122
|
+
@error_expectations.empty?
|
123
|
+
|
124
|
+
@error_expectations.last.messages << MessageExpectation.new(message)
|
125
|
+
|
126
|
+
self
|
127
|
+
end # method with_message
|
128
|
+
|
129
|
+
# Adds a set of message expectations for the most recently added error
|
130
|
+
# attribute.
|
131
|
+
#
|
132
|
+
# @param [Array<String, Regexp>] messages
|
133
|
+
#
|
134
|
+
# @see #with_message
|
135
|
+
def with_messages *messages
|
136
|
+
messages.each do |message| self.with_message(message); end
|
137
|
+
|
138
|
+
self
|
139
|
+
end # method with_message
|
140
|
+
alias_method :with, :with_messages
|
141
|
+
|
142
|
+
# (see BaseMatcher#failure_message)
|
143
|
+
def failure_message
|
144
|
+
# Failure cases:
|
145
|
+
# * object is not a model ("to respond to valid")
|
146
|
+
# * expected one or more errors, but received none ("to have errors")
|
147
|
+
# * expected one or more messages on :attribute, but received none or a
|
148
|
+
# subset ("to have errors on")
|
149
|
+
|
150
|
+
if !@validates
|
151
|
+
"expected #{@actual.inspect} to respond to :valid?"
|
152
|
+
elsif expected_errors.empty?
|
153
|
+
"expected #{@actual.inspect} to have errors"
|
154
|
+
else
|
155
|
+
"expected #{@actual.inspect} to have errors#{expected_errors_message}#{received_errors_message}"
|
156
|
+
end # if-elsif-else
|
157
|
+
end # method failure_message
|
158
|
+
|
159
|
+
# (see BaseMatcher#failure_message_when_negated)
|
160
|
+
def failure_message_when_negated
|
161
|
+
# Failure cases:
|
162
|
+
# * object is not a model ("to respond to valid")
|
163
|
+
# * expected one or more errors, received one or more ("not to have
|
164
|
+
# errors")
|
165
|
+
# * expected one or more messages on attribute, received one or more
|
166
|
+
# ("not to have errors on")
|
167
|
+
# * expected specific messages on attribute, received all ("not to have
|
168
|
+
# errors on")
|
169
|
+
|
170
|
+
if !@validates
|
171
|
+
"expected #{@actual.inspect} to respond to :valid?"
|
172
|
+
elsif expected_errors.empty?
|
173
|
+
return "expected #{@actual.inspect} not to have errors#{received_errors_message}"
|
174
|
+
else
|
175
|
+
return "expected #{@actual.inspect} not to have errors#{expected_errors_message}#{received_errors_message}"
|
176
|
+
end # if-else
|
177
|
+
end # method failure_message_when_negated
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def attributes_have_errors?
|
182
|
+
# Iterate through the received errors and match them against the expected
|
183
|
+
# errors and messages.
|
184
|
+
@actual.errors.messages.each do |attribute, messages|
|
185
|
+
# Find the matching error expectation, if any.
|
186
|
+
error_expectation = @error_expectations.detect do |error_expectation|
|
187
|
+
error_expectation.attribute == attribute
|
188
|
+
end # detect
|
189
|
+
|
190
|
+
if error_expectation
|
191
|
+
error_expectation.received = true
|
192
|
+
|
193
|
+
# If the error includes message expectations, iterate through the
|
194
|
+
# received messages.
|
195
|
+
unless error_expectation.messages.empty?
|
196
|
+
messages.each do |message|
|
197
|
+
# Find the matching message expectation, if any.
|
198
|
+
message_expectation = error_expectation.messages.detect do |message_expectation|
|
199
|
+
if Regexp === message_expectation.message
|
200
|
+
message =~ message_expectation.message
|
201
|
+
else
|
202
|
+
message == message_expectation.message
|
203
|
+
end # if-else
|
204
|
+
end # detect
|
205
|
+
|
206
|
+
if message_expectation
|
207
|
+
message_expectation.received = true
|
208
|
+
else
|
209
|
+
error_expectation.messages << MessageExpectation.new(message, false, true)
|
210
|
+
end # if-else
|
211
|
+
end # each
|
212
|
+
end # unless
|
213
|
+
else
|
214
|
+
error_expectation = ErrorExpectation.new attribute, false, true
|
215
|
+
messages.each do |message|
|
216
|
+
error_expectation.messages << MessageExpectation.new(message, false, true)
|
217
|
+
end # each
|
218
|
+
|
219
|
+
@error_expectations << error_expectation
|
220
|
+
end # if-else
|
221
|
+
end # each
|
222
|
+
|
223
|
+
missing_errors.empty? && missing_messages.empty?
|
224
|
+
end # method attributes_have_errors
|
225
|
+
|
226
|
+
def expected_errors
|
227
|
+
@error_expectations.select do |error_expectation|
|
228
|
+
error_expectation.expected
|
229
|
+
end # select
|
230
|
+
end # method expected_errors
|
231
|
+
|
232
|
+
def missing_errors
|
233
|
+
@error_expectations.select do |error_expectation|
|
234
|
+
error_expectation.expected && !error_expectation.received
|
235
|
+
end # select
|
236
|
+
end # method missing_errors
|
237
|
+
|
238
|
+
def missing_messages
|
239
|
+
@error_expectations.select do |error_expectation|
|
240
|
+
!error_expectation.messages.missing.empty?
|
241
|
+
end # select
|
242
|
+
end # method missing_messages
|
243
|
+
|
244
|
+
def unexpected_errors
|
245
|
+
@error_expectations.select do |error_expectation|
|
246
|
+
!error_expectation.expected && error_expectation.received
|
247
|
+
end # select
|
248
|
+
end # method unexpected_errors
|
249
|
+
|
250
|
+
def expected_errors_message
|
251
|
+
"\n expected errors:" + expected_errors.map do |error_expectation|
|
252
|
+
"\n #{error_expectation.attribute}: " + (error_expectation.messages.empty? ?
|
253
|
+
"(#{@negative_expectation ? 'none' : 'any'})" :
|
254
|
+
error_expectation.messages.expected.map(&:message).map(&:inspect).join(", "))
|
255
|
+
end.join # map
|
256
|
+
end # method expected_errors_message
|
257
|
+
|
258
|
+
def received_errors_message
|
259
|
+
return "" unless @validates
|
260
|
+
|
261
|
+
return "\n received errors:\n (none)" if @actual.errors.messages.empty?
|
262
|
+
|
263
|
+
"\n received errors:" + @actual.errors.messages.map do |attr, ary|
|
264
|
+
"\n #{attr}: " + ary.map(&:inspect).join(", ")
|
265
|
+
end.join # map
|
266
|
+
end # method received_errors_message
|
267
|
+
end # class
|
268
|
+
end # module
|
@@ -1,21 +1,18 @@
|
|
1
1
|
# lib/rspec/sleeping_king_studios/matchers/base_matcher.rb
|
2
2
|
|
3
3
|
require 'rspec/sleeping_king_studios/matchers'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/description'
|
5
|
+
|
6
|
+
require 'sleeping_king_studios/tools/string_tools'
|
4
7
|
|
5
8
|
module RSpec::SleepingKingStudios::Matchers
|
6
9
|
# Minimal implementation of the RSpec matcher interface.
|
7
10
|
#
|
8
11
|
# @since 1.0.0
|
9
12
|
class BaseMatcher
|
10
|
-
|
13
|
+
include RSpec::SleepingKingStudios::Matchers::Description
|
11
14
|
|
12
|
-
|
13
|
-
#
|
14
|
-
# @return [String] the matcher description
|
15
|
-
def description
|
16
|
-
return name_to_sentence unless defined?(@expected)
|
17
|
-
"#{name_to_sentence}#{to_sentence @expected}"
|
18
|
-
end # method description
|
15
|
+
attr_reader :actual
|
19
16
|
|
20
17
|
# Inverse of #matches? method.
|
21
18
|
#
|
@@ -51,12 +48,5 @@ module RSpec::SleepingKingStudios::Matchers
|
|
51
48
|
def failure_message_when_negated
|
52
49
|
"expected #{@actual.inspect} not to #{description}"
|
53
50
|
end # method failure_message_when_negated
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
# @api private
|
58
|
-
def name_to_sentence
|
59
|
-
'match'
|
60
|
-
end # method name_to_sentence
|
61
51
|
end # class
|
62
52
|
end # module
|
@@ -1,69 +1,9 @@
|
|
1
1
|
# lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of.rb
|
2
2
|
|
3
|
-
require 'rspec/sleeping_king_studios/matchers/built_in'
|
4
|
-
require 'sleeping_king_studios/
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/built_in/be_kind_of_matcher'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/macros'
|
5
5
|
|
6
|
-
module RSpec::SleepingKingStudios::Matchers::
|
7
|
-
# Extensions to the built-in RSpec #be_kind_of matcher.
|
8
|
-
class BeAKindOfMatcher < RSpec::Matchers::BuiltIn::BeAKindOf
|
9
|
-
include SleepingKingStudios::Tools::EnumerableTools
|
10
|
-
|
11
|
-
# (see BaseMatcher#description)
|
12
|
-
def description
|
13
|
-
message = "be #{type_string}"
|
14
|
-
end # method description
|
15
|
-
|
16
|
-
# Checks if the object matches one of the specified types. Allows an
|
17
|
-
# expected value of nil as a shortcut for expecting an instance of
|
18
|
-
# NilClass.
|
19
|
-
#
|
20
|
-
# @param [Module, nil, Array<Module, nil>] expected The type or types to
|
21
|
-
# check the object against.
|
22
|
-
# @param [Object] actual The object to check.
|
23
|
-
#
|
24
|
-
# @return [Boolean] True if the object matches one of the specified types,
|
25
|
-
# otherwise false.
|
26
|
-
def match expected, actual
|
27
|
-
match_type? expected
|
28
|
-
end # method match
|
29
|
-
|
30
|
-
# (see BaseMatcher#failure_message)
|
31
|
-
def failure_message
|
32
|
-
"expected #{@actual.inspect} to be #{type_string}"
|
33
|
-
end # method failure_message
|
34
|
-
|
35
|
-
# (see BaseMatcher#failure_message_when_negated)
|
36
|
-
def failure_message_when_negated
|
37
|
-
"expected #{@actual.inspect} not to be #{type_string}"
|
38
|
-
end # method failure_message_when_negated
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def match_type? expected
|
43
|
-
case
|
44
|
-
when expected.nil?
|
45
|
-
@actual.nil?
|
46
|
-
when expected.is_a?(Enumerable)
|
47
|
-
expected.reduce(false) { |memo, obj| memo || match_type?(obj) }
|
48
|
-
else
|
49
|
-
@actual.kind_of? expected
|
50
|
-
end # case
|
51
|
-
end # method match_type?
|
52
|
-
|
53
|
-
def type_string
|
54
|
-
case
|
55
|
-
when @expected.nil?
|
56
|
-
@expected.inspect
|
57
|
-
when @expected.is_a?(Enumerable) && 1 < @expected.count
|
58
|
-
"a #{humanize_list @expected.map { |value| value.nil? ? 'nil' : value }, :last_separator => ' or '}"
|
59
|
-
else
|
60
|
-
"a #{expected}"
|
61
|
-
end # case
|
62
|
-
end # method type_string
|
63
|
-
end # class
|
64
|
-
end # module
|
65
|
-
|
66
|
-
module RSpec::SleepingKingStudios::Matchers
|
6
|
+
module RSpec::SleepingKingStudios::Matchers::Macros
|
67
7
|
# @see RSpec::SleepingKingStudios::Matchers::BuiltIn::BeAKindOfMatcher#match
|
68
8
|
def be_kind_of expected
|
69
9
|
RSpec::SleepingKingStudios::Matchers::BuiltIn::BeAKindOfMatcher.new expected
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of_matcher.rb
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/built_in'
|
4
|
+
require 'sleeping_king_studios/tools/array_tools'
|
5
|
+
|
6
|
+
module RSpec::SleepingKingStudios::Matchers::BuiltIn
|
7
|
+
# Extensions to the built-in RSpec #be_kind_of matcher.
|
8
|
+
class BeAKindOfMatcher < RSpec::Matchers::BuiltIn::BeAKindOf
|
9
|
+
# (see BaseMatcher#description)
|
10
|
+
def description
|
11
|
+
message = "be #{type_string}"
|
12
|
+
end # method description
|
13
|
+
|
14
|
+
# Checks if the object matches one of the specified types. Allows an
|
15
|
+
# expected value of nil as a shortcut for expecting an instance of
|
16
|
+
# NilClass.
|
17
|
+
#
|
18
|
+
# @param [Module, nil, Array<Module, nil>] expected The type or types to
|
19
|
+
# check the object against.
|
20
|
+
# @param [Object] actual The object to check.
|
21
|
+
#
|
22
|
+
# @return [Boolean] True if the object matches one of the specified types,
|
23
|
+
# otherwise false.
|
24
|
+
def match expected, actual
|
25
|
+
match_type? expected
|
26
|
+
end # method match
|
27
|
+
|
28
|
+
# (see BaseMatcher#failure_message)
|
29
|
+
def failure_message
|
30
|
+
"expected #{@actual.inspect} to be #{type_string}"
|
31
|
+
end # method failure_message
|
32
|
+
|
33
|
+
# (see BaseMatcher#failure_message_when_negated)
|
34
|
+
def failure_message_when_negated
|
35
|
+
"expected #{@actual.inspect} not to be #{type_string}"
|
36
|
+
end # method failure_message_when_negated
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def match_type? expected
|
41
|
+
case
|
42
|
+
when expected.nil?
|
43
|
+
@actual.nil?
|
44
|
+
when expected.is_a?(Enumerable)
|
45
|
+
expected.reduce(false) { |memo, obj| memo || match_type?(obj) }
|
46
|
+
else
|
47
|
+
@actual.kind_of? expected
|
48
|
+
end # case
|
49
|
+
end # method match_type?
|
50
|
+
|
51
|
+
def type_string
|
52
|
+
case
|
53
|
+
when @expected.nil?
|
54
|
+
@expected.inspect
|
55
|
+
when @expected.is_a?(Enumerable) && 1 < @expected.count
|
56
|
+
tools = ::SleepingKingStudios::Tools::ArrayTools
|
57
|
+
items = @expected.map { |value| value.nil? ? 'nil' : value }
|
58
|
+
|
59
|
+
"a #{tools.humanize_list items, :last_separator => ' or '}"
|
60
|
+
else
|
61
|
+
"a #{expected}"
|
62
|
+
end # case
|
63
|
+
end # method type_string
|
64
|
+
end # class
|
65
|
+
end # module
|
@@ -1,114 +1,9 @@
|
|
1
1
|
# lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of.rb
|
2
2
|
|
3
|
-
require 'rspec/sleeping_king_studios/matchers/built_in'
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/built_in/include_matcher'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/macros'
|
4
5
|
|
5
|
-
module RSpec::SleepingKingStudios::Matchers::
|
6
|
-
# Extensions to the built-in RSpec #include matcher.
|
7
|
-
class IncludeMatcher < RSpec::Matchers::BuiltIn::Include
|
8
|
-
# @param [Array<Hash, Proc, Object>] expected the items expected to be
|
9
|
-
# matched by the actual object
|
10
|
-
#
|
11
|
-
# @yield If a block is provided, the block is converted to a proc and
|
12
|
-
# appended to the item expectations.
|
13
|
-
# @yieldparam [Object] item An item from the actual object; yield(item)
|
14
|
-
# should return true if and only if the item matches the desired
|
15
|
-
# predicate.
|
16
|
-
def initialize *expected, &block
|
17
|
-
expected << block if block_given?
|
18
|
-
|
19
|
-
super *expected
|
20
|
-
end # constructor
|
21
|
-
|
22
|
-
# @api private
|
23
|
-
#
|
24
|
-
# @return [Boolean]
|
25
|
-
def matches?(actual)
|
26
|
-
perform_match(actual) { |v| v }
|
27
|
-
end # method matches?
|
28
|
-
|
29
|
-
# @api private
|
30
|
-
#
|
31
|
-
# @return [Boolean]
|
32
|
-
def does_not_match?(actual)
|
33
|
-
perform_match(actual) { |v| !v }
|
34
|
-
end # method does_not_match?
|
35
|
-
|
36
|
-
# @api private
|
37
|
-
#
|
38
|
-
# Converts the expected item to a human-readable string. Retained for
|
39
|
-
# pre-3.3 compatibility.
|
40
|
-
def to_word expected_item
|
41
|
-
case
|
42
|
-
when is_matcher_with_description?(expected_item)
|
43
|
-
expected_item.description
|
44
|
-
when Proc === expected_item
|
45
|
-
"an item matching the block"
|
46
|
-
else
|
47
|
-
expected_item.inspect
|
48
|
-
end # case
|
49
|
-
end # method to_word
|
50
|
-
|
51
|
-
# (see BaseMatcher#failure_message)
|
52
|
-
def failure_message
|
53
|
-
message = super.sub ':__block_comparison__', 'an item matching the block'
|
54
|
-
|
55
|
-
message << ", but it does not respond to `include?`" unless actual.respond_to?(:include?) || message =~ /does not respond to/
|
56
|
-
|
57
|
-
message
|
58
|
-
end # method failure_message_for_should
|
59
|
-
|
60
|
-
# (see BaseMatcher#failure_message_when_negated)
|
61
|
-
def failure_message_when_negated
|
62
|
-
message = super.sub ':__block_comparison__', 'an item matching the block'
|
63
|
-
|
64
|
-
message << ", but it does not respond to `include?`" unless actual.respond_to?(:include?) || message =~ /does not respond to/
|
65
|
-
|
66
|
-
message
|
67
|
-
end # method
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
# @api private
|
72
|
-
def perform_match(actual, &block)
|
73
|
-
@actual = actual
|
74
|
-
@divergent_items = excluded_from_actual(&block)
|
75
|
-
actual.respond_to?(:include?) && @divergent_items.empty?
|
76
|
-
end # method perform_match
|
77
|
-
|
78
|
-
# @api private
|
79
|
-
def excluded_from_actual
|
80
|
-
return [] unless @actual.respond_to?(:include?)
|
81
|
-
|
82
|
-
expected.inject([]) do |memo, expected_item|
|
83
|
-
if comparing_proc?(expected_item)
|
84
|
-
memo << :__block_comparison__ unless yield actual_matches_proc?(expected_item)
|
85
|
-
elsif comparing_hash_to_a_subset?(expected_item)
|
86
|
-
expected_item.each do |(key, value)|
|
87
|
-
memo << { key => value } unless yield actual_hash_includes?(key, value)
|
88
|
-
end # each
|
89
|
-
elsif comparing_hash_keys?(expected_item)
|
90
|
-
memo << expected_item unless yield actual_hash_has_key?(expected_item)
|
91
|
-
else
|
92
|
-
memo << expected_item unless yield actual_collection_includes?(expected_item)
|
93
|
-
end # if-elsif-else
|
94
|
-
|
95
|
-
memo
|
96
|
-
end # inject
|
97
|
-
end # method excluded_from_actual
|
98
|
-
|
99
|
-
# @api private
|
100
|
-
def actual_matches_proc? expected_item
|
101
|
-
!!actual.detect(&expected_item)
|
102
|
-
end # method actual_matches_proc?
|
103
|
-
|
104
|
-
# @api private
|
105
|
-
def comparing_proc? expected_item
|
106
|
-
expected_item.is_a?(Proc)
|
107
|
-
end # method comparing_proc?
|
108
|
-
end # class
|
109
|
-
end # module
|
110
|
-
|
111
|
-
module RSpec::SleepingKingStudios::Matchers
|
6
|
+
module RSpec::SleepingKingStudios::Matchers::Macros
|
112
7
|
# @see RSpec::SleepingKingStudios::Matchers::BuiltIn::IncludeMatcher#matches?
|
113
8
|
def include *expected, &block
|
114
9
|
RSpec::SleepingKingStudios::Matchers::BuiltIn::IncludeMatcher.new *expected, &block
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# lib/rspec/sleeping_king_studios/matchers/built_in/include_matcher.rb
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/built_in'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/description'
|
5
|
+
|
6
|
+
module RSpec::SleepingKingStudios::Matchers::BuiltIn
|
7
|
+
# Extensions to the built-in RSpec #include matcher.
|
8
|
+
class IncludeMatcher < RSpec::Matchers::BuiltIn::Include
|
9
|
+
include RSpec::SleepingKingStudios::Matchers::Description
|
10
|
+
|
11
|
+
# @param [Array<Hash, Proc, Object>] expected the items expected to be
|
12
|
+
# matched by the actual object
|
13
|
+
#
|
14
|
+
# @yield If a block is provided, the block is converted to a proc and
|
15
|
+
# appended to the item expectations.
|
16
|
+
# @yieldparam [Object] item An item from the actual object; yield(item)
|
17
|
+
# should return true if and only if the item matches the desired
|
18
|
+
# predicate.
|
19
|
+
def initialize *expected, &block
|
20
|
+
expected << block if block_given?
|
21
|
+
|
22
|
+
super *expected
|
23
|
+
end # constructor
|
24
|
+
|
25
|
+
# (see BaseMatcher#description)
|
26
|
+
def description
|
27
|
+
desc = super
|
28
|
+
|
29
|
+
# Format hash expectations.
|
30
|
+
desc = desc.gsub(/(\S)=>(\S)/, '\1 => \2')
|
31
|
+
|
32
|
+
# Replace processed block expectation stub with proper description.
|
33
|
+
desc = desc.gsub ':__block_comparison__', 'an item matching the block'
|
34
|
+
|
35
|
+
desc
|
36
|
+
end # method description
|
37
|
+
|
38
|
+
# @api private
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
def matches?(actual)
|
42
|
+
@actual = actual
|
43
|
+
|
44
|
+
perform_match(actual) { |v| v }
|
45
|
+
end # method matches?
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
def does_not_match?(actual)
|
51
|
+
@actual = actual
|
52
|
+
|
53
|
+
perform_match(actual) { |v| !v }
|
54
|
+
end # method does_not_match?
|
55
|
+
|
56
|
+
# (see BaseMatcher#failure_message)
|
57
|
+
def failure_message
|
58
|
+
message = super.sub ':__block_comparison__', 'an item matching the block'
|
59
|
+
|
60
|
+
message << ", but it does not respond to `include?`" unless actual.respond_to?(:include?) || message =~ /does not respond to/
|
61
|
+
|
62
|
+
message
|
63
|
+
end # method failure_message_for_should
|
64
|
+
|
65
|
+
# (see BaseMatcher#failure_message_when_negated)
|
66
|
+
def failure_message_when_negated
|
67
|
+
message = super.sub ':__block_comparison__', 'an item matching the block'
|
68
|
+
|
69
|
+
message << ", but it does not respond to `include?`" unless actual.respond_to?(:include?) || message =~ /does not respond to/
|
70
|
+
|
71
|
+
message
|
72
|
+
end # method
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def actual_matches_proc? expected_item
|
77
|
+
if actual.respond_to?(:detect)
|
78
|
+
!!actual.detect(&expected_item)
|
79
|
+
else
|
80
|
+
!!expected_item.call(actual)
|
81
|
+
end # if-else
|
82
|
+
end # method actual_matches_proc?
|
83
|
+
|
84
|
+
def comparing_proc? expected_item
|
85
|
+
expected_item.is_a?(Proc)
|
86
|
+
end # method comparing_proc?
|
87
|
+
|
88
|
+
def excluded_from_actual
|
89
|
+
return [] unless @actual.respond_to?(:include?)
|
90
|
+
|
91
|
+
expected.inject([]) do |memo, expected_item|
|
92
|
+
if comparing_proc?(expected_item)
|
93
|
+
memo << :__block_comparison__ unless yield actual_matches_proc?(expected_item)
|
94
|
+
elsif comparing_hash_to_a_subset?(expected_item)
|
95
|
+
expected_item.each do |(key, value)|
|
96
|
+
memo << { key => value } unless yield actual_hash_includes?(key, value)
|
97
|
+
end # each
|
98
|
+
elsif comparing_hash_keys?(expected_item)
|
99
|
+
memo << expected_item unless yield actual_hash_has_key?(expected_item)
|
100
|
+
else
|
101
|
+
memo << expected_item unless yield actual_collection_includes?(expected_item)
|
102
|
+
end # if-elsif-else
|
103
|
+
|
104
|
+
memo
|
105
|
+
end # inject
|
106
|
+
end # method excluded_from_actual
|
107
|
+
|
108
|
+
def expected_items_for_description
|
109
|
+
# Preprocess items to stub out block expectations.
|
110
|
+
@expected.map { |item| item.is_a?(Proc) ? :__block_comparison__ : item }
|
111
|
+
end # method expected_items_for_description
|
112
|
+
|
113
|
+
def perform_match(actual, &block)
|
114
|
+
@actual = actual
|
115
|
+
@divergent_items = excluded_from_actual(&block)
|
116
|
+
actual.respond_to?(:include?) && @divergent_items.empty?
|
117
|
+
end # method perform_match
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
# Converts the expected item to a human-readable string. Retained for
|
122
|
+
# pre-3.3 compatibility.
|
123
|
+
def to_word expected_item
|
124
|
+
case
|
125
|
+
when is_matcher_with_description?(expected_item)
|
126
|
+
expected_item.description
|
127
|
+
when Proc === expected_item
|
128
|
+
"an item matching the block"
|
129
|
+
else
|
130
|
+
expected_item.inspect
|
131
|
+
end # case
|
132
|
+
end # method to_word
|
133
|
+
end # class
|
134
|
+
end # module
|