Paperclip Processors: Doing so much more with your attachments

Michael Deering February 22nd, 2009

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

Tags

Digg Style Google Search Promotion - Social Search

Michael Deering January 28th, 2009

This is the first time I noticed this Digg style promote/demote feature in my search results.

I’m not a big Digger but I have heard of their gaming algorithms, I can only imagine the amount of data Google is logging against this system to avoid gaming.

This is going to make SEO/SEM pretty interesting over the next while.

Digg

Tags

Follow me on Twitter

Michael Deering January 14th, 2009

I have been on Twitter for quite a while but I have just recently ramped up my use of it and in turn its usefulness.

I have not had any time to blog as my contracting is keeping me really busy right now. Any ‘free time’ that I do have is going into my side projects and incorporating a company to keep those under (more news on that as it unfolds).

So if you are interested in updates there are lots over here.

Cheers

Tags

Missing out on RubyConf 2008

Michael Deering November 6th, 2008

RubyConf 2008

I only hit a single Ruby conference last year and it was RubyConf 2007. I would say I had a decent time. I got some inspiration and ideas from the talks, did some business networking, and got to meet up and have dinner and drinks with many other kickass Ruby developers. The talks were really the main event and with Confreaks doing a bang up job I really would not have missed much just catching the talks when they got posted.

I really was okay with not being able to attend RubyConf 2008 this year until I checked out the IRC channel tonight. What is this? Hackfests, drunk hackers, jam sessions… Why did I have to go while the werewolf craze was at its prime ? I would be like a high school kid at a bush party right now, nail’n (weak American) beers and talking in code (there is a pun there).

Time to start getting my ducks in a row to hit acts_as_conference I guess.

Cheers to those who made it to RubyConf this year. Enjoy

Tags

Freelance Hours Avalible

Michael Deering October 23rd, 2008

A contract I was about to start working on just fell through last minute so I have 25-30 hours a week immediately available.

I’m looking for a challenge where I can apply my experience and grow my ever expanding toolkit of technologies.

Contact information is listed in the footer of the blog. In the footer you will find a link to my Linkedin profile if you are after a more traditional resume. Check out Lypp for a good representation of my recent work.

Sort summary

5+ years of software experience reaching across many industries. I am a professional who practices, refines and keeps up with best practices. I hold a university degree in Computer Engineering but my experience after obtaining that degree speaks volumes above actually obtaining it. I have all the token 2.0 skills such as AJAX, Rails, XHTML, JSON, APIs, ect… but I really shine with serious software challenges and being placed in mentoring roles.

Reach out and lets talk. At the very least I can give you some inspiration, advice, and ideas for your current situation and/or project.

No tire kickers please! Lets not waste each others time.

Tags

« Previous 1 3 4