sanjayginde
11/19/2012 - 7:52 PM

Support setting a Paperclip attachment from a remote URL with a generic mixin for Rails

Support setting a Paperclip attachment from a remote URL with a generic mixin for Rails

# app/controllers/photos_controller.rb

class PhotosController < ApplicationController
  
  def create
    @photo = Photo.new(params[:photo])
    if @photo.save
      redirect_to photos_path
    else
      render :action => 'new'
    end
  end
  
end
# app/models/photo.rb

class Photo < ActiveRecord::Base

  has_attached_file :image # etc...
  can_attach_from_remote_url :image

end
# app/views/photos/new.html.erb

<%= error_messages_for :photo %>
<% form_for :photo, :html => { :multipart => true } do |f| %>
  Upload a photo: <%= f.file_field :image %><br>
  ...or provide a URL: <%= f.text_field :image_remote_url %><br>
  <%= f.submit 'Submit' %>
<% end %>
# lib/paperclip/can_attach_from_remote_url.rb

require 'open-uri'

module Paperclip
  module CanAttachFromRemoteUrl
    module ClassMethods

      def can_attach_from_remote_url(name)
        attr_accessor "#{name}_remote_url"

        before_validation "download_remote_#{name}", :if => "#{name}_remote_url_provided?"

        define_method "#{name}_remote_url_provided?" do 
          self.send("#{name}_remote_url").present? && !self.send("#{name}").dirty?
        end

        define_method "download_remote_#{name}" do
          begin
            io = open(URI.parse(self.send("#{name}_remote_url")))
            if io
              def io.original_filename; base_uri.path.split('/').last; end
              io.original_filename.blank? ? nil : io

              send("#{name}=", io)
            end
          rescue 
            #TODO: should do some error handling (make invalid?)
            # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
          end
        end
      end

    end

    module Glue
      def self.included(base)
        base.extend ClassMethods
      end
    end

  end
end


# config/initializers/paperclip.rb

#... append to configuration to wire in the code
ActiveRecord::Base.send :include, Paperclip::CanAttachFromRemoteUrl::Glue

See also: http://almosteffortless.com/2008/12/11/easy-upload-via-url-with-paperclip/

Forked from: https://gist.github.com/1502777

The goal was a more generic solution to the one in gist this is forked from. It does not require the addition of any more db columns, as I choose not to store the remote URL.

Currently missing, is validation of the remote URL and error handling. Probably makes sense to just work with ActiveRecord::Errors

Currently, the file upload takes precedence over the the remote url, if a user supplies both. I'm thinking that should probably be an option flag on can_attach_remote_url.

More references: