1 | module FileColumn |
---|
2 | module Validations #:nodoc: |
---|
3 | |
---|
4 | def self.append_features(base) |
---|
5 | super |
---|
6 | base.extend(ClassMethods) |
---|
7 | end |
---|
8 | |
---|
9 | # This module contains methods to create validations of uploaded files. All methods |
---|
10 | # in this module will be included as class methods into <tt>ActiveRecord::Base</tt> |
---|
11 | # so that you can use them in your models like this: |
---|
12 | # |
---|
13 | # class Entry < ActiveRecord::Base |
---|
14 | # file_column :image |
---|
15 | # validates_filesize_of :image, :in => 0..1.megabyte |
---|
16 | # end |
---|
17 | module ClassMethods |
---|
18 | EXT_REGEXP = /\.([A-z0-9]+)$/ |
---|
19 | |
---|
20 | # This validates the file type of one or more file_columns. A list of file columns |
---|
21 | # should be given followed by an options hash. |
---|
22 | # |
---|
23 | # Required options: |
---|
24 | # * <tt>:in</tt> => list of extensions or mime types. If mime types are used they |
---|
25 | # will be mapped into an extension via FileColumn::ClassMethods::MIME_EXTENSIONS. |
---|
26 | # |
---|
27 | # Examples: |
---|
28 | # validates_file_format_of :field, :in => ["gif", "png", "jpg"] |
---|
29 | # validates_file_format_of :field, :in => ["image/jpeg"] |
---|
30 | def validates_file_format_of(*attrs) |
---|
31 | |
---|
32 | options = attrs.pop if attrs.last.is_a?Hash |
---|
33 | raise ArgumentError, "Please include the :in option." if !options || !options[:in] |
---|
34 | options[:in] = [options[:in]] if options[:in].is_a?String |
---|
35 | raise ArgumentError, "Invalid value for option :in" unless options[:in].is_a?Array |
---|
36 | |
---|
37 | validates_each(attrs, options) do |record, attr, value| |
---|
38 | unless value.blank? |
---|
39 | mime_extensions = record.send("#{attr}_options")[:mime_extensions] |
---|
40 | extensions = options[:in].map{|o| mime_extensions[o] || o } |
---|
41 | record.errors.add attr, "is not a valid format." unless extensions.include?(value.scan(EXT_REGEXP).flatten.first) |
---|
42 | end |
---|
43 | end |
---|
44 | |
---|
45 | end |
---|
46 | |
---|
47 | # This validates the file size of one or more file_columns. A list of file columns |
---|
48 | # should be given followed by an options hash. |
---|
49 | # |
---|
50 | # Required options: |
---|
51 | # * <tt>:in</tt> => A size range. Note that you can use ActiveSupport's |
---|
52 | # numeric extensions for kilobytes, etc. |
---|
53 | # |
---|
54 | # Examples: |
---|
55 | # validates_filesize_of :field, :in => 0..100.megabytes |
---|
56 | # validates_filesize_of :field, :in => 15.kilobytes..1.megabyte |
---|
57 | def validates_filesize_of(*attrs) |
---|
58 | |
---|
59 | options = attrs.pop if attrs.last.is_a?Hash |
---|
60 | raise ArgumentError, "Please include the :in option." if !options || !options[:in] |
---|
61 | raise ArgumentError, "Invalid value for option :in" unless options[:in].is_a?Range |
---|
62 | |
---|
63 | validates_each(attrs, options) do |record, attr, value| |
---|
64 | unless value.blank? |
---|
65 | size = File.size(value) |
---|
66 | record.errors.add attr, "is smaller than the allowed size range." if size < options[:in].first |
---|
67 | record.errors.add attr, "is larger than the allowed size range." if size > options[:in].last |
---|
68 | end |
---|
69 | end |
---|
70 | |
---|
71 | end |
---|
72 | |
---|
73 | IMAGE_SIZE_REGEXP = /^(\d+)x(\d+)$/ |
---|
74 | |
---|
75 | # Validates the image size of one or more file_columns. A list of file columns |
---|
76 | # should be given followed by an options hash. The validation will pass |
---|
77 | # if both image dimensions (rows and columns) are at least as big as |
---|
78 | # given in the <tt>:min</tt> option. |
---|
79 | # |
---|
80 | # Required options: |
---|
81 | # * <tt>:min</tt> => minimum image dimension string, in the format NNxNN |
---|
82 | # (columns x rows). |
---|
83 | # |
---|
84 | # Example: |
---|
85 | # validates_image_size :field, :min => "1200x1800" |
---|
86 | # |
---|
87 | # This validation requires RMagick to be installed on your system |
---|
88 | # to check the image's size. |
---|
89 | def validates_image_size(*attrs) |
---|
90 | options = attrs.pop if attrs.last.is_a?Hash |
---|
91 | raise ArgumentError, "Please include a :min option." if !options || !options[:min] |
---|
92 | minimums = options[:min].scan(IMAGE_SIZE_REGEXP).first.collect{|n| n.to_i} rescue [] |
---|
93 | raise ArgumentError, "Invalid value for option :min (should be 'XXxYY')" unless minimums.size == 2 |
---|
94 | |
---|
95 | require 'RMagick' |
---|
96 | |
---|
97 | validates_each(attrs, options) do |record, attr, value| |
---|
98 | unless value.blank? |
---|
99 | begin |
---|
100 | img = ::Magick::Image::read(value).first |
---|
101 | record.errors.add('image', "is too small, must be at least #{minimums[0]}x#{minimums[1]}") if ( img.rows < minimums[1] || img.columns < minimums[0] ) |
---|
102 | rescue ::Magick::ImageMagickError |
---|
103 | record.errors.add('image', "invalid image") |
---|
104 | end |
---|
105 | img = nil |
---|
106 | GC.start |
---|
107 | end |
---|
108 | end |
---|
109 | end |
---|
110 | end |
---|
111 | end |
---|
112 | end |
---|