Commit e83f88f3 by Prem Sichanugrist

Add AttachementSizeValidator

parent 03f777f8
...@@ -2,23 +2,24 @@ PATH ...@@ -2,23 +2,24 @@ PATH
remote: . remote: .
specs: specs:
paperclip (2.7.0) paperclip (2.7.0)
activerecord (>= 2.3.0) activemodel (>= 3.0.0)
activesupport (>= 2.3.2) activerecord (>= 3.0.0)
activesupport (>= 3.0.0)
cocaine (>= 0.0.2) cocaine (>= 0.0.2)
mime-types mime-types
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
activemodel (3.2.1) activemodel (3.2.2)
activesupport (= 3.2.1) activesupport (= 3.2.2)
builder (~> 3.0.0) builder (~> 3.0.0)
activerecord (3.2.1) activerecord (3.2.2)
activemodel (= 3.2.1) activemodel (= 3.2.2)
activesupport (= 3.2.1) activesupport (= 3.2.2)
arel (~> 3.0.0) arel (~> 3.0.2)
tzinfo (~> 0.3.29) tzinfo (~> 0.3.29)
activesupport (3.2.1) activesupport (3.2.2)
i18n (~> 0.6) i18n (~> 0.6)
multi_json (~> 1.0) multi_json (~> 1.0)
appraisal (0.4.1) appraisal (0.4.1)
...@@ -111,7 +112,7 @@ GEM ...@@ -111,7 +112,7 @@ GEM
shoulda (2.11.3) shoulda (2.11.3)
sqlite3 (1.3.4) sqlite3 (1.3.4)
term-ansicolor (1.0.7) term-ansicolor (1.0.7)
tzinfo (0.3.31) tzinfo (0.3.32)
uuidtools (2.1.2) uuidtools (2.1.2)
xpath (0.1.4) xpath (0.1.4)
nokogiri (~> 1.3) nokogiri (~> 1.3)
......
...@@ -195,31 +195,6 @@ module Paperclip ...@@ -195,31 +195,6 @@ module Paperclip
end end
end end
# Places ActiveRecord-style validations on the size of the file assigned. The
# possible options are:
# * +in+: a Range of bytes (i.e. +1..1.megabyte+),
# * +less_than+: equivalent to :in => 0..options[:less_than]
# * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
# * +message+: error message to display, use :min and :max as replacements
# * +if+: A lambda or name of an instance method. Validation will only
# be run if this lambda or method returns true.
# * +unless+: Same as +if+ but validates if lambda or method returns false.
def validates_attachment_size name, options = {}
min = options[:greater_than] || (options[:in] && options[:in].first) || 0
max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
range = (min..max)
message = options[:message] || "must be between :min and :max bytes"
message = message.call if message.respond_to?(:call)
message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
validates_inclusion_of :"#{name}_file_size",
:in => range,
:message => message,
:if => options[:if],
:unless => options[:unless],
:allow_nil => true
end
# Places ActiveRecord-style validations on the presence of a file. # Places ActiveRecord-style validations on the presence of a file.
# Options: # Options:
# * +if+: A lambda or name of an instance method. Validation will only # * +if+: A lambda or name of an instance method. Validation will only
......
...@@ -5,7 +5,11 @@ module Paperclip ...@@ -5,7 +5,11 @@ module Paperclip
def self.included base #:nodoc: def self.included base #:nodoc:
base.extend ClassMethods base.extend ClassMethods
base.send :include, Callbacks base.send :include, Callbacks
base.send :include, Validators
base.class_attribute :attachment_definitions base.class_attribute :attachment_definitions
locale_path = Dir.glob(File.dirname(__FILE__) + "/locales/*.{rb,yml}")
I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
end end
end end
end end
en:
errors:
messages:
in_between: "must be in between %{min} and %{max}"
number:
human:
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
require 'active_support/concern'
require 'paperclip/validators/attachment_size_validator'
module Paperclip
module Validators
extend ActiveSupport::Concern
included do
extend HelperMethods
include HelperMethods
end
end
end
require 'active_model/validations/numericality'
module Paperclip
module Validators
class AttachmentSizeValidator < ActiveModel::Validations::NumericalityValidator
AVAILABLE_CHECKS = [:less_than, :less_than_or_equal_to, :greater_than, :greater_than_or_equal_to]
def initialize(options)
extract_options(options)
super
end
def validate_each(record, attr_name, value)
attr_name = "#{attr_name}_file_size".to_sym
value = record.send(:read_attribute_for_validation, attr_name)
unless value.blank?
options.slice(*AVAILABLE_CHECKS).each do |option, option_value|
option_value = option_value.call(record) if option_value.is_a?(Proc)
option_value = extract_option_value(option, option_value)
unless value.send(CHECKS[option], option_value)
error_message_key = options[:in] ? :in_between : option
record.errors.add(attr_name, error_message_key, filtered_options(value).merge(
:min => min_value_in_human_size(record),
:max => max_value_in_human_size(record),
:count => human_size(option_value)
))
end
end
end
end
private
def extract_options(options)
if range = options[:in]
if !options[:in].respond_to?(:call)
options[:less_than_or_equal_to] = range.max
options[:greater_than_or_equal_to] = range.min
else
options[:less_than_or_equal_to] = range
options[:greater_than_or_equal_to] = range
end
end
end
def extract_option_value(option, option_value)
if option_value.is_a?(Range)
if [:less_than, :less_than_or_equal_to].include?(option)
option_value.max
else
option_value.min
end
else
option_value
end
end
def human_size(size)
storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => size.to_i, :raise => true)
storage_units_format.gsub(/%n/, size.to_i.to_s).gsub(/%u/, unit).html_safe
end
def min_value_in_human_size(record)
value = options[:greater_than_or_equal_to] || options[:greater_than]
value = value.call(record) if value.respond_to?(:call)
value = value.min if value.respond_to?(:min)
human_size(value)
end
def max_value_in_human_size(record)
value = options[:less_than_or_equal_to] || options[:less_than]
value = value.call(record) if value.respond_to?(:call)
value = value.max if value.respond_to?(:max)
human_size(value)
end
end
module HelperMethods
# Places ActiveRecord-style validations on the size of the file assigned. The
# possible options are:
# * +in+: a Range of bytes (i.e. +1..1.megabyte+),
# * +less_than+: equivalent to :in => 0..options[:less_than]
# * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
# * +message+: error message to display, use :min and :max as replacements
# * +if+: A lambda or name of an instance method. Validation will only
# be run if this lambda or method returns true.
# * +unless+: Same as +if+ but validates if lambda or method returns false.
def validates_attachment_size(*attr_names)
validates_with AttachmentSizeValidator, _merge_attributes(attr_names)
end
end
end
end
...@@ -20,8 +20,9 @@ Gem::Specification.new do |s| ...@@ -20,8 +20,9 @@ Gem::Specification.new do |s|
s.requirements << "ImageMagick" s.requirements << "ImageMagick"
s.add_dependency('activerecord', '>= 2.3.0') s.add_dependency('activerecord', '>= 3.0.0')
s.add_dependency('activesupport', '>= 2.3.2') s.add_dependency('activemodel', '>= 3.0.0')
s.add_dependency('activesupport', '>= 3.0.0')
s.add_dependency('cocaine', '>= 0.0.2') s.add_dependency('cocaine', '>= 0.0.2')
s.add_dependency('mime-types') s.add_dependency('mime-types')
......
...@@ -9,11 +9,10 @@ require 'mocha' ...@@ -9,11 +9,10 @@ require 'mocha'
require 'active_record' require 'active_record'
require 'active_record/version' require 'active_record/version'
require 'active_support' require 'active_support'
require 'active_support/core_ext'
require 'mime/types' require 'mime/types'
require 'pathname' require 'pathname'
require 'pathname'
puts "Testing against version #{ActiveRecord::VERSION::STRING}" puts "Testing against version #{ActiveRecord::VERSION::STRING}"
`ruby -e 'exit 0'` # Prime $? with a value. `ruby -e 'exit 0'` # Prime $? with a value.
......
...@@ -354,22 +354,7 @@ class PaperclipTest < Test::Unit::TestCase ...@@ -354,22 +354,7 @@ class PaperclipTest < Test::Unit::TestCase
end end
should "have a file size min/max error message" do should "have a file size min/max error message" do
assert [@dummy.errors[:avatar_file_size]].flatten.any?{|error| error =~ %r/between 0 and 10240 bytes/ } assert_includes @dummy.errors[:avatar_file_size], "must be less than 10240 Bytes"
end
end
end
context "with size validation and less_than 10240 option with lambda message" do
context "and assigned an invalid file" do
setup do
Dummy.send(:"validates_attachment_size", :avatar, :less_than => 10240, :message => lambda {'lambda between 0 and 10240 bytes'})
@dummy = Dummy.new
@dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "12k.png"), "rb")
@dummy.valid?
end
should "have a file size min/max error message" do
assert [@dummy.errors[:avatar_file_size]].flatten.any?{|error| error =~ %r/lambda between 0 and 10240 bytes/ }
end end
end end
end end
......
require './test/helper'
class AttachmentSizeValidatorTest < Test::Unit::TestCase
def setup
rebuild_model
@dummy = Dummy.new
end
def build_validator(options)
@validator = Paperclip::Validators::AttachmentSizeValidator.new(options.merge(
:attributes => :avatar
))
end
def self.should_allow_attachment_file_size(size)
context "when the attachment size is #{size}" do
should "add error to dummy object" do
@dummy.stubs(:avatar_file_size).returns(size)
@validator.validate(@dummy)
assert @dummy.errors[:avatar_file_size].blank?,
"Expect an error message on :avatar_file_size, got none."
end
end
end
def self.should_not_allow_attachment_file_size(size, options = {})
context "when the attachment size is #{size}" do
setup do
@dummy.stubs(:avatar_file_size).returns(size)
@validator.validate(@dummy)
end
should "add error to dummy object" do
assert @dummy.errors[:avatar_file_size].present?,
"Unexpected error message on :avatar_file_size"
end
if options[:message]
should "return a correct error message" do
assert_includes @dummy.errors[:avatar_file_size], options[:message]
end
end
end
end
context "with :in option" do
context "as a range" do
setup do
build_validator :in => (5.kilobytes..10.kilobytes)
end
should_allow_attachment_file_size(7.kilobytes)
should_not_allow_attachment_file_size(4.kilobytes)
should_not_allow_attachment_file_size(11.kilobytes)
end
context "as a proc" do
setup do
build_validator :in => lambda { |avatar| (5.kilobytes..10.kilobytes) }
end
should_allow_attachment_file_size(7.kilobytes)
should_not_allow_attachment_file_size(4.kilobytes)
should_not_allow_attachment_file_size(11.kilobytes)
end
end
context "with :greater_than option" do
context "as number" do
setup do
build_validator :greater_than => 10.kilobytes
end
should_allow_attachment_file_size 11.kilobytes
should_not_allow_attachment_file_size 10.kilobytes
end
context "as a proc" do
setup do
build_validator :greater_than => lambda { |avatar| 10.kilobytes }
end
should_allow_attachment_file_size 11.kilobytes
should_not_allow_attachment_file_size 10.kilobytes
end
end
context "with :less_than option" do
context "as number" do
setup do
build_validator :less_than => 10.kilobytes
end
should_allow_attachment_file_size 9.kilobytes
should_not_allow_attachment_file_size 10.kilobytes
end
context "as a proc" do
setup do
build_validator :less_than => lambda { |avatar| 10.kilobytes }
end
should_allow_attachment_file_size 9.kilobytes
should_not_allow_attachment_file_size 10.kilobytes
end
end
context "with :greater_than and :less_than option" do
context "as numbers" do
setup do
build_validator :greater_than => 5.kilobytes,
:less_than => 10.kilobytes
end
should_allow_attachment_file_size 7.kilobytes
should_not_allow_attachment_file_size 5.kilobytes
should_not_allow_attachment_file_size 10.kilobytes
end
context "as a proc" do
setup do
build_validator :greater_than => lambda { |avatar| 5.kilobytes },
:less_than => lambda { |avatar| 10.kilobytes }
end
should_allow_attachment_file_size 7.kilobytes
should_not_allow_attachment_file_size 5.kilobytes
should_not_allow_attachment_file_size 10.kilobytes
end
end
context "with :message option" do
context "given a range" do
setup do
build_validator :in => (5.kilobytes..10.kilobytes),
:message => "is invalid. (Between %{min} and %{max} please.)"
end
should_not_allow_attachment_file_size 11.kilobytes,
:message => "is invalid. (Between 5120 Bytes and 10240 Bytes please.)"
end
context "given :less_than and :greater_than" do
setup do
build_validator :less_than => 10.kilobytes,
:greater_than => 5.kilobytes,
:message => "is invalid. (Between %{min} and %{max} please.)"
end
should_not_allow_attachment_file_size 11.kilobytes,
:message => "is invalid. (Between 5120 Bytes and 10240 Bytes please.)"
end
end
context "default error messages" do
context "given :less_than and :greater_than" do
setup do
build_validator :greater_than => 5.kilobytes,
:less_than => 10.kilobytes
end
should_not_allow_attachment_file_size 11.kilobytes,
:message => "must be less than 10240 Bytes"
should_not_allow_attachment_file_size 4.kilobytes,
:message => "must be greater than 5120 Bytes"
end
context "given a size range" do
setup do
build_validator :in => (5.kilobytes..10.kilobytes)
end
should_not_allow_attachment_file_size 11.kilobytes,
:message => "must be in between 5120 Bytes and 10240 Bytes"
should_not_allow_attachment_file_size 4.kilobytes,
:message => "must be in between 5120 Bytes and 10240 Bytes"
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment