Skip to content

Instantly share code, notes, and snippets.

@denyago
Created October 19, 2011 14:18
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save denyago/1298417 to your computer and use it in GitHub Desktop.
Save denyago/1298417 to your computer and use it in GitHub Desktop.
Very quickly implemented MIME Type validator for CarrierWave. See also: https://gist.github.com/1009861 and https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Validate-attachment-file-size
en:
errors:
messages:
wrong_content_type: "is the wrong content type"
# Based on: https://gist.github.com/1009861
class FileMimeTypeValidator < ActiveModel::EachValidator
MESSAGES = { :content_type => :wrong_content_type }.freeze
CHECKS = [ :content_type ].freeze
DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
RESERVED_OPTIONS = [:content_type, :tokenizer]
def initialize(options)
super
end
def check_validity!
keys = CHECKS & options.keys
if keys.empty?
raise ArgumentError, 'Patterns unspecified. Specify the :content_type option.'
end
keys.each do |key|
value = options[key]
unless valid_content_type_option?(value)
raise ArgumentError, ":#{key} must be a String or a Regexp or an Array"
end
if key.is_a?(Array) && key == :content_type
options[key].each do |val|
raise ArgumentError, "#{val} must be a String or a Regexp" unless val.is_a?(String) || val.is_a?(Regexp)
end
end
end
end
def validate_each(record, attribute, value)
raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base
value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
return if value.length == 0
CHECKS.each do |key|
next unless check_value = options[key]
if key == :content_type
if check_value.is_a?(String) || check_value.is_a?(Regexp)
do_validation(value, check_value, key, record, attribute)
else
check_value.each do |pattern|
do_validation(value, pattern, key, record, attribute)
end
end
end
end
end
def help
Helper.instance
end
class Helper
include Singleton
include ActionView::Helpers::NumberHelper
end
private
def valid_content_type_option?(content_type)
return true if %w{Array String Regexp}.include?(content_type.class.to_s)
false
end
def do_validation(value, pattern, key, record, attribute)
return if value.file.content_type.send((pattern.is_a?(String) ? "==" : "=~" ), pattern)
errors_options = options.except(*RESERVED_OPTIONS)
default_message = options[MESSAGES[key]]
errors_options[:message] ||= default_message if default_message
record.errors.add(attribute, MESSAGES[key], errors_options)
end
end
class Post < ActiveRecord::Base
mount_uploader :image, ImagePostUploader
validates :image,
:file_mime_type => {
:content_type => /image/
}
end
@DSushil20
Copy link

Validation logic working fine with me, but the problem is that; active record every time run this validator even when no file is uploaded. So this obviously fails. Is there any way to tackle this situation?

@danielfriis
Copy link

Thanks for this! It works almost as I hoped. However, whenever I upload an unvalid file it raises ActiveRecord::RecordInvalid exception, instead of the usual flash notice.

Validation failed: Image Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: Command ("identify -ping /var/folders/90/bt4jhsys70d71dfhr06q3crh0000gn/T/mini_magick20130722-17393-19zaaq0.html") failed: {:status_code=>1, :output=>"identify: delegate failed \"html2ps\" -U -o \"%o\" \"%i\"' @ error/delegate.c/InvokeDelegate/1065.\nidentify: unable to open image /var/tmp/magick-17517Qh--EFN7KBBh': No such file or directory @ error/blob.c/OpenBlob/2638.\nidentify: unable to open file /var/tmp/magick-17517Qh--EFN7KBBh': No such file or directory @ error/constitute.c/ReadImage/589.\n"}, Image is the wrong content type

Is it possible to correct this behavior? (I'm a rails newbie and need some points on how to do it myself)

@evgenii
Copy link

evgenii commented Aug 30, 2014

Hi Denis, cool validator thanks for that.

But I use it like this:

  validates :icon,
    :file_mime_type => {
      :content_type => ['image/jpeg', 'image/png', 'image/gif'],
      :if => :has_icon?
    }

and in this case I've got 2 errors for each of invalid content_type.

that is why I've got update for you:

  • def validate_each(record, attribute, value) looks like this for me:
  def validate_each(record, attribute, value)
    raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base
    value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
    return if value.length == 0

    CHECKS.each do |key|
      next unless check_value = options[key]
      do_validation(value, check_value, key, record, attribute) if key == :content_type
    end
  end
  • and def do_validation(value, pattern, key, record, attribute)
  def do_validation(value, pattern, key, record, attribute)
    if pattern.is_a?(String) || pattern.is_a?(Regexp)
      return if value.file.content_type.send((pattern.is_a?(String) ? "==" : "=~" ), pattern)
    else
      valid_list = pattern.map do |p|
        value.file.content_type.send((p.is_a?(String) ? "==" : "=~" ), p)
      end
      return if valid_list.include?(true)
    end

    errors_options = options.except(*RESERVED_OPTIONS)

    default_message = options[MESSAGES[key]]
    errors_options[:message] ||= default_message if default_message

    record.errors.add(attribute, MESSAGES[key], errors_options)
  end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment