What good is a flexible paperclip?

Posted by Jon Yurek

Dec 30

Originally found at http://flickr.com/photos/toofarnorth/9984261

Since it’s the Holidays, I’ve been spending a bit more time than normal on Paperclip. And since that time has been particularly fruitful and there’s been a release or two, I figured I should probably tell someone about it before I friggin’ explode. It’s all about making Paperclip more flexible, more adaptable, and more friendly to use. Can you believe there’s more to file uploads than avatars?

Newer, more sensible defaults.

Overall, this is actually a bit small on the change meter, but it may affect some of you, so it’s up front. The :path and :url defaults have changed. By default now, files will be saved to :rails_root/public/system/:attachments/:id/:style/:basename/:extension. It’s the “system” part of that that’s important, because now it means that if you’re deploying with Capistrano, you don’t have to do anything and your attachments will survive deployments. This was not previously the case, regrettably, but it is now!

Callbacks and such.

Thanks to the callback methods pioneered by ActiveRecord itself with the fantastic before_save and family, Paperclip now defines a before_post_process and after_post_process callback, which can be used exactly like all the AR callbacks. Not only that, if you’re the kind of person who likes to have more than one attachment on a model, there are per-attachment callbacks as well, called before_<attachment>_post_process and after_<attachment>_post_process. The before_ callbacks are fully capable of stopping processing if they need to, simply by returning false (not nil, but false, which is a distinction ActiveRecord makes, as well). Thus, if you are uploading images that have “E” in the name, you can write a before_post_process that looks like this:

1
2
3
4
5
6
7
8
class User < ActiveRecord::Base
  has_attached_file :avatar, :styles => {:tiny => "32x32#" }
  before_post_process :check_avatar_name_for_capital_e

  def check_avatar_name_for_capital_e
    not self.avatar.original_filename.match(/E/)
  end
end

This will prevent the image from being thumbnailed. It will not prevent the attachment from being saved, though. Just from being processed. As a bonus, something else that will prevent attachments from being processed is failing validation. If you have a size, content_type, or presence validation that fails, the attachment will not go through processing (which means ImageMagick won’t try to convert that Word Doc into a PNG if you don’t want it to).

Expanded Post-Processing.

I realize that there’s more to image uploads than thumbnailing. And there’s more to file uploads than images. But until now, all you could do was thumbnail your images. What gives! Well, starting now you can define your own processors that can do whatever you want to your uploads. You can:

... well, you can if you can write code, since none of those exist yet. The only one that’s written is still the thumbnailer/format converter.

But the point is that now you can have Paperclip do whatever you want. Check out the Paperclip::Processor class documentation for more info on exactly what you need to do to make a Processor, but the gist is that you’ll take in a file and some options, and you spit back out a file. That’s pretty much it, and Paperclip places no limits on what you can do, say, or call during that time (so if you spend 30 seconds rendering a POVRay scene, that’s your fault for making your users wait).

Paperclip will automatically detect files in your Rails app’s lib/paperclip_processors directory, so just drop them there and you’ll be running in no time.

The Code!

As always, the code is available for forking and cloning on GitHub, and the documentation is available on our site.

If you’d like to contribute to paperclip with a patch, bug report, or feature request, don’t hesitate to get on over to the Paperclip Google group, or our Paperclip Lighthouse. As much as we like GitHub, we don’t really work well with pull requests from there. Creating a LH ticket with a link to the branch you want us to pull works much better.


Comments on this post

Jason Morrison

Dec 30

Jason Morrison said,

Sweet changes! Performing a virus scan is another processing task that comes to mind.

josh

Dec 30

josh said,

cool. bookmarked for future use.

Rick M.

Dec 30

Rick M. said,

Sweeeeeeet.

Igor Petrushenko

Dec 31

Igor Petrushenko said,

Hi, guys

Thanks for the update!

Only one thing which doesn’t allow me to use paperclip, is that I need multiple attachments for my models. Am I missed somethings, and currently it’s possible?

Anyway, thanks for the plugin.

Happy New Year!

Jon Yurek

Dec 31

Jon Yurek said,

@Igor: Multiple attachments are possible. If you want an arbitrary number of attachments (i.e. a has_many), then you can just place the attachment on a separate model, but if you need, say, an avatar and a resume attachment, then you can simply “has_attached_file :avatar” and then “has_attached_file :resume”. As long as you have both a avatar_file_name column and a resume_file_name column, both will work just like you want.

Will Merrell

Jan 01

Will Merrell said,

First, thanks a lot for Paperclip. I switched over to it a little while ago, and love it. It just works.

I also needed multiple attachments for one aspect of my projects, and found a plugin called PaperclipPolymorph. You can find it at:

http://github.com/heavysixer/paperclippolymorph/tree/master

Google it for several tutorials.

What I would love to see is this plugin, or a better one if there is one, “officially” sanctioned by Paperclip, or brought in and included, or some form of co-operative development, so that there will always be a viable technique for doing multiple attachments. What I really don’t want to see is one of these plugins rot from lack of attention and fall into disuse.

Anyway, I think the changes described in this article are great, and I am glad to see them. But please give some thought to multiple attachments, because I think it is really important for some applications.

Thanks, —Will Merrell

Jon Yurek

Jan 03

Jon Yurek said,

@Will, Thanks for the kind words.

Frankly, I don’t see why that project exists. Paperclip doesn’t do anything fancy on its own and making multiple attachments (or even polymorphic attachments) isn’t any more complicated than making them for any other model that needs to be extracted out. That was one of my intentions behind Paperclip: to make a file work just like other attributes, which it does. There doesn’t need to be any more magic than what ActiveRecord already gives you, IMHO. Perhaps this would be cleared up if I wrote a tutorial about has_many (and belongs_to :polymorphic).

Patrick Berkeley

Jan 04

Patrick Berkeley said,

Thank you for Paperclip. I appreciate its simplicity. When I first started using it I thought polymorphic attachments weren’t possible, I think because most of the examples I found were for single model attachments. And probably more because I’m fairly new to Rails. For people looking for an example of polymorphic attachments with Paperclip check out: http://github.com/schof/spree/tree/master

Will Merrell

Jan 04

Will Merrell said,

@Jon,

I think you are probably right. As I was trying to figure out how to attach photos to my models, I came across an article that described PaperclipPolymorph before I had dug in deep enough to understand that basic Paperclip could be used that way, so I simply latched onto it and used it. It does work, and it handles creating the polymorphic tables easily, but it is really a very thin layer that just does exactly what you describe. In retrospect, I am not sure that another plugin to manage is worth it.

I think that Patrick is right that the early examples in the documentation seemed to imply that you could only attach one image per model. Perhaps an example or two would help.

One other point, it seems like other kinds of files should be able to be attached, but this is also not clear. If you are in the mood to make examples, one showing how to handle other kinds of files would be useful. (It may be easy, I just haven’t needed it yet, so I haven’t looked yet.)

Thanks, —Will

Harry

Jan 04

Harry said,

I’m attaching multiple photos (for a slideshow) and multiple attached documents to a model via has_many. It’s really, really easy. Here’s my writeup of doing protected file downloads on the attached docs using Paperclip.

http://harrylove.org/2008/12/22/protected-file-downloads-with-ruby-on-rails-and-paperclip

Here’s a simple example for a blog post with multiple photos:
class Post < ActiveRecord::Base
  has_many :photos
end

class Photo < ActiveRecord::Base
  belongs_to :post
  has_attached_file :photograph ...
end

Then do @post.photos to access your photos for the post.

DAZ

Jan 04

DAZ said,

Just wanted to say thanks for paperclip and add a +1 for Will’s suggestion of a tutorial using other file types and multiple attachments using their own model.

cheers,

DAZ

jeroen houben

Jan 06

jeroen houben said,

cool!

I’ve actually rolled my own solution for a particular project because this functionality was missing (and I didn’t even need image resizing etc) – nice to see this is now possible in a clean manner.

FYI: The generated RDocs have a small problem, I think the last paragraph gets chopped on the index page.


Sorry, comments are closed for this article.

© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots