Roll your own attachment_fu validations
Lots of folks use attachment_fu to handle image uploads. It handles all the dirty work of uploading files, integrates with Amazon S3, and even supports all the different image libraries.
Which is awesome... but it kinda sucks at validating files. Take a pretty typical Photo class:
class Photo < ActiveRecord::Base
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 5.megabytes,
:resize_to => '320x200>',
:thumbnails => { :thumb => '100x100>', :widget => "262x262>" },
:processor => MiniMagick
validates_as_attachment
end
What happens when you forget to select a file before uploading?
- Content type can't be blank
- Content type is not included in the list
- Size can't be blank
- Size is not included in the list
- Filename can't be blank
So, let's try uploading a Word document:
- Content type is not included in the list
Or a file that's too big:
- Size is not included in the list
Let's try it again, but in English:
class Photo < ActiveRecord::Base
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 5.megabytes,
:resize_to => '320x200>',
:thumbnails => { :thumb => '100x100>', :widget => "262x262>" },
:processor => MiniMagick
def validate
errors.add_to_base("You must choose a file to upload") unless self.filename
unless self.filename == nil
# Images should only be GIF, JPEG, or PNG
[:content_type].each do |attr_name|
enum = attachment_options[attr_name]
unless enum.nil? || enum.include?(send(attr_name))
errors.add_to_base("You can only upload images (GIF, JPEG, or PNG)")
end
end
# Images should be less than 5 MB
[:size].each do |attr_name|
enum = attachment_options[attr_name]
unless enum.nil? || enum.include?(send(attr_name))
errors.add_to_base("Images should be smaller than 5 MB in size")
end
end
end
end
end
To get started, let's drop the validates_as_attachment method. Or, more specifically, try extracting its useful parts into something more useful.
Since we're dropping attachment_fu's validations, we need to roll our own validations for the Photo class. You can do this by declaring a validate method in your class, and adding appropriate logic.
We can also use errors.add_to_base to get total control over your validation messages. By default, Rails pair the model attribute name with an error message ("person" + "is missing an ear"), but sometimes this can lead to awkward phrases ("person is empty").
You'll need to custom this for your application, of course, but this post should give you some examples.
(I'm planning to follow this blog post up with a series of posts about improving validation messages.)

Very interesting. Looking forward to those follow-ups.
Thanks Pat! I got off my ass and improved my validations. I just posted my stab at a plugin for just this, initially based off your code. I've also posted it to the attachment_fu google group for comment.
It was done that way on purpose so you could do cool stuff like this. I figure it's easier to let you define this stuff in Rails, rather than coming up with my own slick DSL that's constantly being updated for everyone's edge cases.
Cool, Rick. It's always nice to have options. :)
Sweet! This is a nice tutorial for my beginner skills in Ruby. Got my custom errors messages for attachment_fu looking hot!
Hi Patrick,
Useful article here, as usual. I'm just moving first steps about attachment_fu, looking for a way to upload ":other" files type ... say opml files, do you have some exaples to manage something like :
has_attachment :content_type => ['text/x-opml', 'application/xml', 'text/xml', 'text/html', 'text/plain'], .... and eventually the ":storage => :db_file" way ?
Thanks in advance.
lgs
New comments are disabled right now. Once we finish migrating this blog, we'll restore them.