Search code examples
ruby-on-railscustom-validators

Generic method for validation of paperclip files in different models


I have 2 models users , companies

User model:

has_attached_file :avatar,
             ...
                :whiny=>false
validates_with ImageSizeValidator
validates_with ImageTypeValidator
validates_with ImageConvertionValidator

Company model:

has_attached_file :logo,
#the rest is similar

I have done validation for users and put it in validation_helper

class ImageSizeValidator < ActiveModel::Validator
 def validate(record)
   if record.avatar_file_name.present?
     record.errors[:base] << (I18n.t :in_between, scope:  "activerecord.errors.models.user.attributes.avatar_file_size") unless record.avatar_file_size.to_i < 200000
   end
 end
end
class ImageTypeValidator < ActiveModel::Validator
 def validate(record)
  if record.avatar_file_name.present?
    record.errors[:base] << (I18n.t :file_type, scope: "activerecord.errors.models.user.attributes") unless ['image/jpeg', 'image/gif','image/png'].include?(record.avatar_content_type)
  end
 end
end

My problem is that the names will be different so avatar_file_name for users and logo for companies.

Do I have to do a specific method for each? How can I work this around?


Solution

  • you just need to add options. If you take a look at documentation, you can pass arguments in block:

    #model
    validates_with ImageSizeValidator, paperclip_field_name: :avatar
    
    #validator
     def validate(record)
       if record.send(options[:paperclip_field_name].to_s+"_file_name").present?
         record.errors[:base] << (I18n.t :in_between, scope:  "activerecord.errors.models.user.attributes.#{options[:paperclip_field_name]}_file_size") unless record.send(options[:paperclip_field_name].to_s+"_file_name").to_i < 200000
       end
     end
    

    but much easier to use validate_each method

    #model
    validates :avatar, image_size: true, image_type: true, image_conversion: true
    
    #validator
    def validate_each(record, attribute, value)
      if record.send(attribute.to_s+"_file_name").present?
        record.errors[:base] << (I18n.t :in_between, scope:  "activerecord.errors.models.user.attributes.#{attribute}_file_name)") unless record.send(attribute.to_s+"_file_name").to_i < 200000
      end
    end