giant robots smashing into other giant robots

We are thoughtbot. We make web & mobile apps.

Tagged:

Comments (View)

What good is a flexible paperclip?

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:

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:

  • add rounded corners
  • invert
  • rotate
  • OCR
  • pick out every third word of your text docs
  • run spellcheck
  • automatically print them out

… 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.

Tagged:

Comments (View)

Paperclip Tips and Updates

So there’s been a bit of activity on the Paperclip front. I’ve added Amazon S3 support using the RightAWS gem. Some fellow githubbers have contributed some patches to fix up said S3 support, as well as to add more (and better) validations for content type, etc. We’ve brought it up to a nice, round v2.1.2 as of yesterday. Give yourselves a round of applause.

I highly encourage you to give it a whirl if you haven’t already. It’s very easy to both get and to use!

An interesting note!

Ken Robertson has gone to great lengths to port Paperclip to DataMapper, which should be great news for all you Merbers out there. He’s kept it quite up-to-date, and it’s right alongside the 2.1.2 current release. Hopefully we’ll be able to merge codebases in the future, though right now there’s enough of a difference between DM and AR to make that not terribly feasible.

Here’s a handy tip!

One of the most frequently asked questions is how to use data from your instances in the path and/or URL. The answer is the interpolations hash, which is quite user-extensible. Let’s say you had a song that played in the background of each User’s profile (making this exercise purely hypothetical, of course), and you wanted its name to be the same as the User’s username.

One really good way of allowing this functionality would be to add the following to your config/initializers/paperclip.rb file:

Paperclip::Attachment.interpolations[:username] = proc do |attachment, style|
  attachment.instance.login # or whatever you've named your User's login/username/etc. attribute
end

The #instance method is the instance of the model that this attachment is attached to. You can access any of the model’s attributes, methods, associations, and so on from that object just like normal. Also note that you don’t have to name your interpolation the same thing as the attribute you’re interpolating (though it helps clarity).

Now you can add this to your :path and :url parameters like so:

class User < ActiveRecord::Base
  has_attached_file :song,
                    :path => ":rails_root/public/system/:attachment/:username.:extension",
                    :url  => "/:attachment/:username.:extension"
end

When you call #url or #path on your attachment, Paperclip will run through the interpolations hash, find strings that match its keys, and replace them with the return values of the procs. In this case, you’d produce a lovely url of ”/songs/jyurek.mp3”

It’s a very easy way to add flexibility to your files’ names without having to modify any code yourself.

Another handy tip!

S3 support is now baked in! The updated has_attached_file call looks kinda like this one does:

class House < ActiveRecord::Base
  has_attached_file :blueprint,
                    :styles => { :thumbnail => "150x150" },
                    :path => ":attachment/:id/:style.:extension",
                    :storage => :s3,
                    :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
                    :bucket => "bucket-of-holding"
end

Please note the additional options, including:

  • :storage: Specify :s3 here to use S3. Strictly speaking you could specify :filesystem here if you’re using the filesystem, but it’s the default so don’t bother.
  • :s3_credentials: You should give this a Hash, a path to a file, or a File object. The File should contain a YAML-ized Hash, and the contents of the Hash should be the access_key_id and secret_access_key used to access your S3 account. You can also “environment-space” these inside the hash, just like your database.yml file.
  • :bucket: The name of the S3 bucket that will be holding your data.

Note that there’s no :url option. That’s because the S3 URLs are generated from the bucket and the path name, so you don’t have to worry about them. You can also specify permissions for your files, in case you don’t want the default “public-read”, by using :s3_permissions.

You can find more about the S3 Storage options and the Filesystem Storage options at their RDocs.

Keep up to date!

Remember, there’s always the Paperclip Google Group and the Paperclip Lighthouse Account in case you have problems, questions, or feature requests.

Tagged:

Comments (View)

For attaching files, use Paperclip

Last Update: For archival reasons, I’m removing direct links to releases, etc. from here. All the info you need is at the official paperclip project page.

For some reason, file attachment is annoying. I don’t know why, and I know a lot of people have attempted to solve the problem in the past, myself included. Yet it still is. Having gotten fed up with gotchas and design decisions that we didn’t agree with, I went and wrote Paperclip on the plane to RailsConf last year. We’ve been using it here in various forms since and IMHO it’s the way to handle uploads, and finally decided that it should be released.

class User < ActiveRecord::Base
  has_attached_file :avatar,
                    :styles => { :square => ["64x64#", :png],
                                 :small  => "150x150>" }
end

A file is treated like any other attribute. It’s assigned like any other attribute, and it’s not saved until you call #save. It doesn’t have its own model. You can say where it’s saved on the filesystem, and what URL it’s referred to by (which means you can let Apache/nginx handle it or you can route it through the app for permissions/security). You can say what thumbnails are made, what resolution and format they are, and you can actually save cropped square thumbnails without any hassle.

class AddAvatarToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :avatar_file_name, :string
    add_column :users, :avatar_content_type, :string
    add_column :users, :avatar_file_size, :integer
  end

  def self.down; ...; end
end

You don’t need mini_magick (which we’ve found may have been causing issues), you don’t need ImageScience (which, on the first image we tried to upload, failed to decode it, and it was a simple GIF), you don’t need RMagick (which has memory issues that were the impetus for mini_magick in the first place). You just need ImageMagick installed somewhere, which is as easy as yum, apt-get, or port on any system worth hosting on.

And this isn’t just for avatars and images. You can upload anything. No thumbnails are made by default, so it won’t automagically choke on your Excel docs.

Usage

In your model:

class User < ActiveRecord::Base
  has_attached_file :avatar, 
                    :styles => { :medium => "300x300>",
                                 :thumb  => "100x100>" }
end

In your edit and new views:

<% form_for :user, :html => { :multipart => true } do |form| %>
  <%= form.file_field :avatar %>
<% end %>

In your controller:

def create
  @user = User.create( params[:user] )
end

In your show view:

<%= image_tag @user.avatar.url %>
<%= image_tag @user.avatar.url(:medium) %>
<%= image_tag @user.avatar.url(:thumb) %>

Installation

It can’t be everything for everyone, but for the vast majority of cases we’ve come across, this is the right fit. Really, once you use this you’ll wonder why managing files was such a hassle.