Paperclip Logo

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.

Resources

Follow me on Twitter