7

This is my Image model, in which I've implemented a method for validating the attachment's dimensions:

class Image < ActiveRecord::Base
  attr_accessible :file

  belongs_to :imageable, polymorphic: true

  has_attached_file :file,
                     styles: { thumb: '220x175#', thumb_big: '460x311#' }

  validates_attachment :file,
                        presence: true,
                        size: { in: 0..600.kilobytes },
                        content_type: { content_type: 'image/jpeg' }

  validate :file_dimensions

  private

  def file_dimensions(width = 680, height = 540)
    dimensions = Paperclip::Geometry.from_file(file.queued_for_write[:original].path)
    unless dimensions.width == width && dimensions.height == height
      errors.add :file, "Width must be #{width}px and height must be #{height}px"
    end
  end
end

This works fine, but it's not reusable since the method takes fixed values for width & height. I want to transform this to a Custom Validator, so I can use it in other models too. I've read the guides about this, I know it'll be something like this in app/models/dimensions_validator.rb:

class DimensionsValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    dimensions = Paperclip::Geometry.from_file(record.queued_for_write[:original].path)

    unless dimensions.width == 680 && dimensions.height == 540
      record.errors[attribute] << "Width must be #{width}px and height must be #{height}px"
    end
  end
end

but I know I'm missing something cause this code doesn't work. The thing is that I want to call the validation like this in my model:

validates :attachment, dimensions: { width: 300, height: 200}.

Any idea on how this validator should be implemented?

Agis
  • 32,639
  • 3
  • 73
  • 81
  • 1
    I'm not sure but I thought you can acces your width and height through the options attribute.. like: `options[:width]` and `options[:height]` – Tim Baas Sep 12 '12 at 13:13

2 Answers2

19

Put this in app/validators/dimensions_validator.rb:

class DimensionsValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    # I'm not sure about this:
    dimensions = Paperclip::Geometry.from_file(value.queued_for_write[:original].path)
    # But this is what you need to know:
    width = options[:width]
    height = options[:height] 

    record.errors[attribute] << "Width must be #{width}px" unless dimensions.width == width
    record.errors[attribute] << "Height must be #{height}px" unless dimensions.height == height
  end
end

Then, in the model:

validates :file, :dimensions => { :width => 300, :height => 300 }
Tim Baas
  • 6,035
  • 5
  • 45
  • 72
  • We don't have to add the validator to load_path as long as you place the file in models/ and name it following the Rails conventions (dimensions_validator.rb). I've edited the answer. Thanks! – Agis Sep 12 '12 at 13:51
  • Great. How could we perform the check only if an image is uploaded? I've tried something `unless record.nil?' and 'unless value.nil?' but it didn't work. – Agis Sep 12 '12 at 14:06
  • You could try: `if value.present?`, but I thought `value.nil?` should work.. What is the value of `value` when it's not uploaded and you debug it? – Tim Baas Sep 12 '12 at 14:19
  • 3
    I'd recommend not putting this into `/app/models`. Placing the file in `/app/validators` will still result in Rails autoloading the file and will keep your models directory clean, following SRP. – Nathan May 06 '14 at 20:55
  • Why not add this as a gem? I will create one – svelandiag Jan 23 '16 at 06:30
0

There is a gem that does this called paperclip-dimension-validator.

Mario Zigliotto
  • 8,315
  • 7
  • 52
  • 71