-
-
Save carlosramireziii/6d0ca6b414d8a6af08371c30ba4dedcd to your computer and use it in GitHub Desktop.
require "rspec/expectations" | |
RSpec::Matchers.define :allow_content_type do |*content_types| | |
match do |record| | |
matcher.matches?(record, content_types) | |
end | |
chain :for do |attr_name| | |
matcher.for(attr_name) | |
end | |
chain :with_message do |message| | |
matcher.with_message(message) | |
end | |
private | |
def matcher | |
@matcher ||= AllowContentTypeMatcher.new | |
end | |
class AllowContentTypeMatcher | |
def for(attr_name) | |
@attr_name = attr_name | |
end | |
def with_message(message) | |
@message = message | |
end | |
def matches?(record, content_types) | |
Array.wrap(content_types).all? do |content_type| | |
record.send(attr_name).attach attachment_for(content_type) | |
record.valid? | |
!record.errors[attr_name].include? message | |
end | |
end | |
private | |
attr_reader :attr_name | |
def attachment_for(content_type) | |
suffix = content_type.to_s.split("/").last | |
{ io: StringIO.new("Hello world!"), filename: "test.#{suffix}", content_type: content_type } | |
end | |
def message | |
@message || I18n.translate("activerecord.errors.messages.content_type") | |
end | |
end | |
end | |
RSpec::Matchers.alias_matcher :allow_content_types, :allow_content_type |
class ContentTypeValidator < ActiveModel::EachValidator | |
def validate_each(record, attribute, value) | |
unless value.attached? && value.content_type.in?(content_types) | |
value.purge if record.new_record? # Only purge the offending blob if the record is new | |
record.errors.add(attribute, :content_type, options) | |
end | |
end | |
private | |
def content_types | |
options.fetch(:in) | |
end | |
end |
en: | |
activerecord: | |
errors: | |
messages: | |
content_type: is not a valid file format |
Hi! Thank you!
I don't understand much in Rails, so please excuse me if the question is lame.
I watch table — active_storage_blobs — and records are not deleted (active_storage_attachments is OK).
Am I doing something wrong?
This is great, thank you!
I modified this slightly for my use case such that it will allow the attachment to be nil.
class ContentTypeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return unless value.attached?
return if value.content_type.in?(content_types)
value.purge
record.errors.add(attribute, :content_type, options)
end
private
def content_types
options.fetch(:in)
end
end
Where (directory structure) in rails would I store this validator?
I get FrozenError: can't modify frozen Hash
. Not sure where it's coming from.
@fydelio I store my validators in app/validators
(i.e. app/validators/content_type_validator.rb
)
Nice! How come you only purge the photo if it's a new record?
Great question! If a persisted record has an existing attachment then we don't want to purge it on a validation error because then we'll lose the existing value. I believe this behavior will be different in Rails 6+ because attachments are no longer persisted immediately upon attachment.
I watch table — active_storage_blobs — and records are not deleted (active_storage_attachments is OK).
If you call destroy
on an attachment then it won't automatically delete the associated blob. You'll want to make sure you use the purge
method for deleting the attachment and blob.
Where (directory structure) in rails would I store this validator?
@fydelio I store my validators in
app/validators
(i.e.app/validators/content_type_validator.rb
)
This is exactly the directory structure I use as well!
I modified this slightly for my use case such that it will allow the attachment to be nil.
Awesome!
I get
FrozenError: can't modify frozen Hash
. Not sure where it's coming from.
@thelucid Yes, I got a similar error with the @meara approach. can't modify frozen attributes
- Were you able to fix it? I added the check back and it worked as well:
def validate_each(record, attribute, value)
return unless value.attached?
return if value.content_type.in?(content_types)
value.purge if record.new_record?
record.errors.add(attribute, :content_type, **options)
end
@carlosramireziii Thank you, it works nicely.
Nice! How come you only purge the photo if it's a new record?