148 lines
3.6 KiB
Ruby
148 lines
3.6 KiB
Ruby
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)
|
|
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
|