Make Paperclip attachment file path backwards compatible

Published 05 April 2013

Somewhere between Paperclip version 2.7 and version 3.4 the fine people at Thoughtbot changed the default URL and path for attachments.

The old path structure was:

:rails_root/public/system/:attachment/:id/:style/:filename

where :attachment is the name of the attachment you specified in has_attached_file and :id is the ID of the model.

This has changed to be:

:rails_root/public/system/:class/:attachment/:id_partition/:style/:filename

where :class is the class name of the model and :id_partition is a unique set if subdirectories like /000/000/013.

As they say
NOTE: This is a change from previous versions of Paperclip, but is overall a safer choice for the default file store.
and they’re correct, because if you had 2 attachments with the same name then one can easily clobber another.

You have 3 choices if you’re upgrading Paperclip:

1. Move all your files from the old to the new directory structure. A complete PITA
2. Individually add the path and URL options to the has_attached_file directive in each model. Fine, but not ideal if you have several occurrences
3. Monkey patch Paperclip to use the same path and URL as version 2.×. This is what I decided to do.

To do this in Rails, create a file under config/initializers called paperclip.rb with these contents:

# Change the default Paperclip path and URL options to remain consistent with pre 3.0 version
module Paperclip
  class Attachment
    def self.default_options
      @default_options ||= {
        :convert_options       => {},
        :default_style         => :original,
        :default_url           => "/:attachment/:style/missing.png",
        :escape_url            => true,
        :restricted_characters => /[&$+,\/:;=?@<>\[\]\{\}\|\\\^~%# ]/,
        :hash_data             => ":class/:attachment/:id/:style/:updated_at",
        :hash_digest           => "SHA1",
        :interpolator          => Paperclip::Interpolations,
        :only_process          => [],
        :path                  => ":rails_root/public:url",
        :preserve_files        => false,
        :processors            => [:thumbnail],
        :source_file_options   => {},
        :storage               => :filesystem,
        :styles                => {},
        :url                   => "/system/:attachment/:id/:style/:filename", # this is the only line which is different to the original
        :url_generator         => Paperclip::UrlGenerator,
        :use_default_time_zone => true,
        :use_timestamp         => true,
        :whiny                 => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
      }
    end
  end
end

and restart Rails. Now all your paths and URLs will be as before.