« dziesig

Hobo with Paperclip - storage to db column.

Posted by dziesig.

After installing Paperclip, getting it to work, and searching for the uploaded data I decided to RTM and found that I had two choices, storage on the file system (the default) or storage to s3. For business reasons (and the desire to eventually host on Heroku) I needed the data storage in the appropriate db tables.

Kevin found http://patshaughnessy.net/2009/2/19/database-storage-for-paperclip for me (I never thought to Google for it). Pat Shaughnessy did a really good implementation for Rails 3.0 which I used in place of the original paperclip. After a few issues, I got it working on Ralis 3.0, Hobo 1.3, as follows:

First install the plain-vanilla paperclip and get it working (use the default file system storage for simple installation) so you are starting from a known good condition.

Then

Edit Gemfile

gem 'paperclip',  :git => 'git://github.com/patshaughnessy/paperclip.git'

I put this just after the hobo line:

gem "hobo", ">= 1.3.0.pre25"

Then

sudo bundle install

In my case, this overwrote the existing plain-vanilla paperclip with Pat’s version.

Edit the model (in my case agent_documents.rb) adding/changing only two lines:

document_file	:binary # in the field definition block

has_attached_file :document, :storage => :database #THIS DID NOT WORK AT FIRST!

hobo g migration … THIS FAILED.

There is a chicken vs. egg problem here. The has_attached_file line is processed before the document_file :binary field declaration which leads to an undefined method error. The solution is to first comment out the , :storage => :database, then

hobo g migration

This time it works. Now un-comment “, :storage => :database”, restart your server and have fun uploading attachments.

Now for the download:

Edit the controller (in my case agent_documents_controller.rb), adding:

downloads_files_for :agent_document, :document

the first argument is the name of the model, the second is the attachment name as generated by paperclip.

Edit routes.rb, adding:

match 'agent_documents/documents/:id' => 'agent_documents#documents' 

at the appropriate priority level (mine was low so I put it at the end).

Lastly, insert the link in your appropriate dryml file. In my case, I added it to views/agent_documents/show.dryml:

<nav-item-external href="#{this.document.url}">View Document</nav-item-external>

or

<a-external href="#{this.document.url}">View Document</a-external>

My memory is going. I created (plagiarized and modified I think) the nav-item-external tag, but forgot about it, so anyone who implements this will get an error. The definition (and the similar a-external tag) is:

<def tag="nav-item-external" attrs="name">
  <% body = parameters.default
     body = h(this.to_s) if body.blank?
     name ||= body.gsub(/<.*?>/, '').strip
   -%>
  <li class="#{'current' if (c = scope.current_navigation) && c.downcase == name.downcase}"
    merge-attrs="&attributes - (attrs_for(:a)+['target'])">
  <a onclick="window.open(this.href);return false;" merge-attrs="&attributes & (attrs_for(:a)+['target'])"><%= body %></a>
  </li>
</def>


<def tag="a-external" attrs="name">
  <% body = parameters.default
     body = h(this.to_s) if body.blank?
     name ||= body.gsub(/<.*?>/, '').strip
  -%>
    <a onclick="window.open(this.href);return false;" merge-attrs="&attributes & (attrs_for(:a)+['target'])"><%= body %></a>
</def>

In my case, I put both of these in app/views/taglibs/internet_nav.dryml

User contributed notes

  • On January 05, 2011 dziesig said:

    Found a bug. If the file name field is filled in, and the record fails validation, the displayed page contains all of the uploaded data, in addition to the proper display of the validation failure messages (which is at the bottom of the very big page).
  • On January 11, 2011 dziesig said:

    January 05, 2011 bug was caused by order of fields in the validates_presence_of method. If the attachment method is last in the sequence, the upload operates properly.

    I have not tried using hobo's :required parameter so I can't speculate on it's behavior.
  • On April 27, 2011 dziesig said:

    Remember to make the form multipart or you will go crazy trying to figure out where the “Browse” button went.

        <extend tag="form" for="YourModelName">
          <old-form merge multipart>
            <field-list: fields="documents"/>
          </old-form>
        </extend>
  • On June 20, 2011 Bryan Larsen said:

    Don, did you set a default scope as recommended in the article? (http://patshaughnessy.net/2009/4/14/database-storage-for-paperclip-rewritten-to-use-a-single-table)