/
have_attributes.rb
114 lines (98 loc) · 3.11 KB
/
have_attributes.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
module RSpec
module Matchers
module BuiltIn
# @api private
# Provides the implementation for `have_attributes`.
# Not intended to be instantiated directly.
class HaveAttributes < BaseMatcher
# @private
attr_reader :respond_to_failed
def initialize(expected)
@expected = expected
@values = {}
@respond_to_failed = false
@negated = false
end
# @private
def actual
@values
end
# @api private
# @return [Boolean]
def matches?(actual)
@actual = actual
@negated = false
return false unless respond_to_attributes?
perform_match(:all?)
end
# @api private
# @return [Boolean]
def does_not_match?(actual)
@actual = actual
@negated = true
return false unless respond_to_attributes?
perform_match(:none?)
end
# @api private
# @return [String]
def description
described_items = surface_descriptions_in(expected)
improve_hash_formatting "have attributes #{RSpec::Support::ObjectFormatter.format(described_items)}"
end
# @api private
# @return [Boolean]
def diffable?
!@respond_to_failed && !@negated
end
# @api private
# @return [String]
def failure_message
respond_to_failure_message_or do
"expected #{actual_formatted} to #{description} but had attributes #{ formatted_values }"
end
end
# @api private
# @return [String]
def failure_message_when_negated
respond_to_failure_message_or { "expected #{actual_formatted} not to #{description}" }
end
private
def cache_all_values
@values = {}
expected.each do |attribute_key, _attribute_value|
actual_value = @actual.__send__(attribute_key)
@values[attribute_key] = actual_value
end
end
def perform_match(predicate)
cache_all_values
expected.__send__(predicate) do |attribute_key, attribute_value|
actual_has_attribute?(attribute_key, attribute_value)
end
end
def actual_has_attribute?(attribute_key, attribute_value)
values_match?(attribute_value, @values.fetch(attribute_key))
end
def respond_to_attributes?
matches = respond_to_matcher.matches?(@actual)
@respond_to_failed = !matches
matches
end
def respond_to_matcher
@respond_to_matcher ||= RespondTo.new(*expected.keys).with(0).arguments.tap { |m| m.ignoring_method_signature_failure! }
end
def respond_to_failure_message_or
if respond_to_failed
respond_to_matcher.failure_message
else
improve_hash_formatting(yield)
end
end
def formatted_values
values = RSpec::Support::ObjectFormatter.format(@values)
improve_hash_formatting(values)
end
end
end
end
end