Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

Commit

Permalink
Add AttachmentContentTypeValidator
Browse files Browse the repository at this point in the history
  • Loading branch information
sikachu committed Mar 23, 2012
1 parent ee42b19 commit da5d716
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 49 deletions.
34 changes: 0 additions & 34 deletions lib/paperclip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -195,40 +195,6 @@ def has_attached_file(name, options = {})
end
end

# Places ActiveRecord-style validations on the content type of the file
# assigned. The possible options are:
# * +content_type+: Allowed content types. Can be a single content type
# or an array. Each type can be a String or a Regexp. It should be
# noted that Internet Explorer uploads files with content_types that you
# may not expect. For example, JPEG images are given image/pjpeg and
# PNGs are image/x-png, so keep that in mind when determining how you
# match. Allows all by default.
# * +message+: The message to display when the uploaded file has an invalid
# content type.
# * +if+: A lambda or name of an instance method. Validation will only
# be run is this lambda or method returns true.
# * +unless+: Same as +if+ but validates if lambda or method returns false.
# NOTE: If you do not specify an [attachment]_content_type field on your
# model, content_type validation will work _ONLY upon assignment_ and
# re-validation after the instance has been reloaded will always succeed.
# You'll still need to have a virtual attribute (created by +attr_accessor+)
# name +[attachment]_content_type+ to be able to use this validator.
def validates_attachment_content_type name, options = {}
validation_options = options.dup
allowed_types = [validation_options[:content_type]].flatten
validates_each(:"#{name}_content_type", validation_options) do |record, attr, value|
if !allowed_types.any?{|t| t === value } && !(value.nil? || value.blank?)
if record.errors.method(:add).arity == -2
message = options[:message] || "is not one of #{allowed_types.join(", ")}"
message = message.call if message.respond_to?(:call)
record.errors.add(:"#{name}_content_type", message)
else
record.errors.add(:"#{name}_content_type", :inclusion, :default => options[:message], :value => value)
end
end
end
end

# Returns the attachment definitions defined by each call to
# has_attached_file.
def attachment_definitions
Expand Down
1 change: 1 addition & 0 deletions lib/paperclip/validators.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'active_support/concern'
require 'paperclip/validators/attachment_content_type_validator'
require 'paperclip/validators/attachment_presence_validator'
require 'paperclip/validators/attachment_size_validator'

Expand Down
45 changes: 45 additions & 0 deletions lib/paperclip/validators/attachment_content_type_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Paperclip
module Validators
class AttachmentContentTypeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
attribute = "#{attribute}_content_type".to_sym
value = record.send(:read_attribute_for_validation, attribute)
allowed_types = [options[:content_type]].flatten

unless value.blank?
allowed_types.any? do |type|
unless type === value
record.errors.add(attribute, :invalid, options.merge(
:types => allowed_types.join(', ')
))
end
end
end
end
end

module HelperMethods
# Places ActiveRecord-style validations on the content type of the file
# assigned. The possible options are:
# * +content_type+: Allowed content types. Can be a single content type
# or an array. Each type can be a String or a Regexp. It should be
# noted that Internet Explorer uploads files with content_types that you
# may not expect. For example, JPEG images are given image/pjpeg and
# PNGs are image/x-png, so keep that in mind when determining how you
# match. Allows all by default.
# * +message+: The message to display when the uploaded file has an invalid
# content type.
# * +if+: A lambda or name of an instance method. Validation will only
# be run is this lambda or method returns true.
# * +unless+: Same as +if+ but validates if lambda or method returns false.
# NOTE: If you do not specify an [attachment]_content_type field on your
# model, content_type validation will work _ONLY upon assignment_ and
# re-validation after the instance has been reloaded will always succeed.
# You'll still need to have a virtual attribute (created by +attr_accessor+)
# name +[attachment]_content_type+ to be able to use this validator.
def validates_attachment_content_type(*attr_names)
validates_with AttachmentContentTypeValidator, _merge_attributes(attr_names)
end
end
end
end
15 changes: 0 additions & 15 deletions test/paperclip_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -329,21 +329,6 @@ def self.should_validate validation, options, valid_file, invalid_file
should_validate validation, options, valid_file, invalid_file
end

context "with content_type validation and lambda message" do
context "and assigned an invalid file" do
setup do
Dummy.send(:"validates_attachment_content_type", :avatar, :content_type => %r{image/.*}, :message => lambda {'lambda content type message'})
@dummy = Dummy.new
@dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "text.txt"), "rb")
@dummy.valid?
end

should "have a content type error message" do
assert [@dummy.errors[:avatar_content_type]].flatten.any?{|error| error =~ %r/lambda content type message/ }
end
end
end

context "with size validation and less_than 10240 option" do
context "and assigned an invalid file" do
setup do
Expand Down
106 changes: 106 additions & 0 deletions test/validators/attachment_content_type_validator_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
require './test/helper'

class AttachmentContentTypeValidatorTest < Test::Unit::TestCase
def setup
rebuild_model
@dummy = Dummy.new
end

def build_validator(options)
@validator = Paperclip::Validators::AttachmentContentTypeValidator.new(options.merge(
:attributes => :avatar
))
end

context "with a nil content type" do
setup do
build_validator :content_type => "image/jpg"
@dummy.stubs(:avatar_content_type => nil)
@validator.validate(@dummy)
end

should "not set an error message" do
assert @dummy.errors[:avatar_content_type].blank?
end
end

context "with an allowed type" do
context "as a string" do
setup do
build_validator :content_type => "image/jpg"
@dummy.stubs(:avatar_content_type => "image/jpg")
@validator.validate(@dummy)
end

should "not set an error message" do
assert @dummy.errors[:avatar_content_type].blank?
end
end

context "as an regexp" do
setup do
build_validator :content_type => /^image\/.*/
@dummy.stubs(:avatar_content_type => "image/jpg")
@validator.validate(@dummy)
end

should "not set an error message" do
assert @dummy.errors[:avatar_content_type].blank?
end
end
end

context "with a disallowed type" do
context "as a string" do
setup do
build_validator :content_type => "image/png"
@dummy.stubs(:avatar_content_type => "image/jpg")
@validator.validate(@dummy)
end

should "set a correct default error message" do
assert @dummy.errors[:avatar_content_type].present?
assert_includes @dummy.errors[:avatar_content_type], "is invalid"
end
end

context "as a regexp" do
setup do
build_validator :content_type => /^text\/.*/
@dummy.stubs(:avatar_content_type => "image/jpg")
@validator.validate(@dummy)
end

should "set a correct default error message" do
assert @dummy.errors[:avatar_content_type].present?
assert_includes @dummy.errors[:avatar_content_type], "is invalid"
end
end

context "with :message option" do
context "without interpolation" do
setup do
build_validator :content_type => "image/png", :message => "should be a PNG image"
@dummy.stubs(:avatar_content_type => "image/jpg")
@validator.validate(@dummy)
end

should "set a correct error message" do
assert_includes @dummy.errors[:avatar_content_type], "should be a PNG image"
end
end

context "with interpolation" do
setup do
build_validator :content_type => "image/png", :message => "should have content type %{types}"
@dummy.stubs(:avatar_content_type => "image/jpg")
@validator.validate(@dummy)
end

should "set a correct error message" do
assert_includes @dummy.errors[:avatar_content_type], "should have content type image/png"
end
end
end
end
end

0 comments on commit da5d716

Please sign in to comment.