# config/initializers/paperclip.rb
CouchRest::Model::Base.send(:include, Paperclip::Glue)

module Paperclip
  class << self
    def logger #:nodoc:
      @logger ||= Logger.new("#{Rails.root}/log/paperclip.log")
    end
  end
end

# we need to hack couchrest model because we need to resave a doc after attaching attachments without going into a loop from
# paperclip callbacks
module CouchRest
  module Model
    module Persistence
      def save_without_callbacks(options={})
        return false unless perform_validations(options)
        result = database.save_doc(self)
        result['ok'] == true
      end
    end
  end
end

module Paperclip
  module Storage
    module Couch
      def self.extended base
        base.instance_eval do
          if @path == self.class.default_options[:path]
            @path = ":attachment/:style/:basename.:extension"
          end

          if @url == self.class.default_options[:url]
            @url = ":couch_url"
          end
        end

        Paperclip.interpolates(:couch_url) do |attachment, style|
          "#{attachment.instance.attachment_url(attachment.path(style))}"
        end

        Paperclip.interpolates(:couch_uri) do |attachment, style|
          "#{attachment.instance.attachment_uri(attachment.path(style))}"
        end
      end

      def exists?(style_name = default_style)
        instance.has_attachment? path(style_name)
      end

      # Returns representation of the data of the file assigned to the given
      # style, in the format most representative of the current storage.
      def to_file style = default_style
        return @queued_for_write[style] if @queued_for_write[style]
        if instance.has_attachment?(path(style))
          filename = path(style)
          extname  = File.extname(filename)
          basename = File.basename(filename, extname)
          file = Tempfile.new([basename, extname])
          file.binmode
          file.write instance.read_attachment(path(style))
          file.rewind
          return file
        else
          return nil
        end
      end
      
      def flush_writes #:nodoc:
        attachments_modified = false
        @queued_for_write.each do |style_name, file|
          log("saving #{path(style_name)} to CouchDB")

          if instance.has_attachment? path(style_name)
            instance.update_attachment(:name => path(style_name), :file => file)
          else
            instance.create_attachment(:name => path(style_name), :file => file)
          end          
          attachments_modified = true
        end

        if attachments_modified
          instance.save_without_callbacks 

          # because couchrest will re-encode attachment files every time it saves,
          # we set the stub to true so it skips them on subsequent saves with the same
          # instance
          @queued_for_write.each do |style_name, file|
            instance.attachments[path(style_name)]['stub'] = true            
          end
        end
          
        @queued_for_write = {}
      end

      def flush_deletes #:nodoc:
        attachments_modified = false
        @queued_for_delete.each do |path|
          begin
            if instance.has_attachment? path
              instance.delete_attachment(path)
              attachments_modified = true
            end
          rescue Exception => e
            log "Error trying to delete from CouchDB: #{e}"
          end
        end
        instance.save_without_callbacks if attachments_modified
        @queued_for_delete = []
      end
    end

  end
end

###
### Your model:
###
class User < CouchRest::Model::Base
  property :avatar_file_name, String
  property :avatar_content_type, String
  property :avatar_file_size, Integer
  property :avatar_updated_at, String
  has_attached_file :avatar, :styles => {:thumbnail => '100x100#'}, :storage => :couch, :url => ":couch_url"
end

I'm developing a Rails3 application that uses CouchRest::Model to interface with CouchDB. On its own, the excellent Paperclip plugin supports ActiveRecord and comes with two backends for filesystem and AWS S3.

CouchDB supports storing files as attachments directly on documents, so I thought this would be a good fit to be able to store all information in one place, which should help with devops (can replicate the entire database which will include all attachment files).

Anyway, out this code in an initializer, and it should work for you. This depends on rails3 and the couchrest, couchrest_model, and paperclip gems.

It would be possible to modify this code to use couch as a standalone backend without couchrest_model, but this way seems cleaner, to keep the attachments with the document itself.

I haven't tested all details of paperclip yet, so this is a work in progress and YMMV.