forked from thoughtbot/shoulda-matchers
/
have_attached_matcher.rb
148 lines (128 loc) · 3.6 KB
/
have_attached_matcher.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
module Shoulda
module Matchers
module ActiveRecord
def have_one_attached(name)
HaveAttachedMatcher.new(:one, name)
end
def have_many_attached(name)
HaveAttachedMatcher.new(:many, name)
end
# @private
class HaveAttachedMatcher
attr_reader :name
def initialize(macro, name)
@macro = macro
@name = name
end
def description
"have a has_#{macro}_attached called #{name}"
end
def failure_message
<<-MESSAGE
Expected #{expectation}, but this could not be proved.
#{@failure}
MESSAGE
end
def failure_message_when_negated
<<-MESSAGE
Did not expect #{expectation}, but it does.
MESSAGE
end
def expectation
"#{model_class.name} to #{description}"
end
def matches?(subject)
@subject = subject
reader_attribute_exists? &&
writer_attribute_exists? &&
attachments_association_exists? &&
blobs_association_exists? &&
eager_loading_scope_exists?
end
private
attr_reader :subject, :macro
def reader_attribute_exists?
if subject.respond_to?(name)
true
else
@failure = "#{model_class.name} does not have a :#{name} method."
false
end
end
def writer_attribute_exists?
if subject.respond_to?("#{name}=")
true
else
@failure = "#{model_class.name} does not have a :#{name}= method."
false
end
end
def attachments_association_exists?
if attachments_association_matcher.matches?(subject)
true
else
@failure = attachments_association_matcher.failure_message
false
end
end
def attachments_association_matcher
@_attachments_association_matcher ||=
AssociationMatcher.new(
:"has_#{macro}",
attachments_association_name,
).
conditions(name: name).
class_name('ActiveStorage::Attachment').
inverse_of(:record).
dependent(false)
end
def attachments_association_name
case macro
when :one then
"#{name}_attachment"
when :many then
"#{name}_attachments"
end
end
def blobs_association_exists?
if blobs_association_matcher.matches?(subject)
true
else
@failure = blobs_association_matcher.failure_message
false
end
end
def blobs_association_matcher
@_blobs_association_matcher ||=
AssociationMatcher.new(
:"has_#{macro}",
blobs_association_name,
).
through(attachments_association_name).
class_name('ActiveStorage::Blob').
source(:blob)
end
def blobs_association_name
case macro
when :one then
"#{name}_blob"
when :many then
"#{name}_blobs"
end
end
def eager_loading_scope_exists?
if model_class.respond_to?("with_attached_#{name}")
true
else
@failure = "#{model_class.name} does not have a " \
":with_attached_#{name} scope."
false
end
end
def model_class
subject.class
end
end
end
end
end