Paperclip Processors: Doing so much more with your attachments

It seems like I was doing the same thing over the weekend as the Fellas over at The Web Fellas have been up to lately. Making a few Paperclip::Processor classes. I though I would quickly share my hack to get at the ActiveRecord instance variable inside of my processors. And quickly throw out there a few of my processors.
First my patch/hack to get at the ActiveRecord instance the attachment belongs to.
# config/initializers/paperclip_patch.rb
module Paperclip
class Attachment
# I need access to the AR instance in order to scope things by the site.
def post_process_styles
log("Post-processing #{name}")
@styles.each do |name, args|
begin
raise RuntimeError.new("Style #{name} has no processors defined.") if args[:processors].blank?
@queued_for_write[name] = args[:processors].inject(@queued_for_write[:original]) do |file, processor|
log("Processing #{name} #{file} in the #{processor} processor.")
Paperclip.processor(processor).make(file, args.merge(:instance => @instance))
end
rescue PaperclipError => e
log("An error was received while processing: #{e.inspect}")
(@errors[:processing] ||= []) << e.message if @whiny
end
end
end
end
end
This gives me access inside of the processors the ActiveRecord instance through option[:instance].
I’m cranking out a homegrown muilt-domain CMS to consolidate this and all my other sites all under one roof (not done yet but soon…). Part of that CMS allows me to edit the different sites CSS and JavaScript files directly in the browser as well as use liquid inside those files.
Besides running the files through the liquid template engine, who would dare send out any CSS or JS that has not been compressed! :) And as an added bonus Paperclip keeps the original around with no overhead of managing the different variations of your attachments. This means I get to work in the browser with a nicely formatted version of the files.
Here is how it works with with my stylesheets.
# app/models/stylesheet.rb
class Stylesheet < Asset
has_attached_file :data,
:path => ':rails_root/public/assets/:site_id/:class/:style_:basename.:extension',
:processors => [:liquidize, :compress],
:styles => {
:compressed => {} }
},
:url => '/assets/:site_id/:class/:style_:basename.:extension'
end
And here are the two processors to liquidize and compress my assets.
# lib/paperclip_procssors/liquidize.rb
module Paperclip
class Liquidize < Processor
class InstanceNotGiven < ArgumentError; end
def initialize(file, options = {})
super
@file = file
@instance = options[:instance]
@current_format = File.extname(@file.path)
@basename = File.basename(@file.path, @current_format)
end
def make
@file.pos = 0 # Reset the file position incase it is coming out of a another processor
dst = Tempfile.new([@basename, @format].compact.join("."))
dst << Liquid::Template.parse(@file.read).render(Liquid::Context.new(nil, { :site => @instance.site }))
dst
end
end
end
module Paperclip
class Compress < Processor
class InstanceNotGiven < ArgumentError; end
def compress_css(source)
# Poached from asset_packager ...
end
def compress_file
case @instance
when Javascript then compress_js(@file.read)
when Stylesheet then compress_css(@file.read)
end
end
def compress_js(source)
# Poached from asset_packager ...
end
def initialize(file, options = {})
super
@file = file
@instance = options[:instance]
@current_format = File.extname(@file.path)
@basename = File.basename(@file.path, @current_format)
end
def make
@file.pos = 0 # Reset the file position incase it is coming out of a another processor
dst = Tempfile.new([@basename, @format].compact.join("."))
dst << compress_file
dst
end
end
end
I’m adding processors to allow me to work with my CSS, pages and layouts in SASS and HAML inside of the browser next.
Paperclip is so much more then a replacement for Attachment Fu. It is time to check it out if you have not already. There is so much more under the covers then just easy image thumbnails.
