Displaying articles with tag

attachment_fu and restful_authentication

Posted by Jason, Sat Jun 09 04:04:00 UTC 2007

Howto override methods in attachment_fu to get the file system storage design that you want...



The days of file_column are long over. Its hard to stay current with everything going on in the world of rails. I am constantly keeping an eye on my IRC scroll trying to keep as up to date on what is the up to date way of doing things. Today when logging into Trac I noticed that I had a ticket which required a user to upload images. A few things crossed my mind, the first of which was file_column, a Ruby on Rails plugin that I have used many times in the past for doing just that. A few things to consider. I have no idea what kind of image processor will be used on the server. I need a way to resize images, filter content types, and since this application is expected to get just short of a million hits a day, Amazon s3 integration is a needed scaling option. I soon realized that file_column was going to need some personalization if this was going to work. Sitting back for a minute and thinking about how was the best method to approach this, I did what many Rails developers do, I checked out what Rick Olsen's been up to. Wow, look at that.. Acts_as_attachment. Very nice, I had never heard of that one before. Amazingly enough, before I had even heard about this plugin (its been a while since I had to deal with uploading images, I'll admit), he had rewrote it. Sometimes it truly amazes me how fast that guy moves. Welcome to my world attachment_fu.

I found a really nice post which explains a basic overview of how to get things set up. The purpose of my post is not to document how to get attachment_fu working, as the above post and Rick Olsen's documentation (source code) do a very good job of that. Instead, I intend just to point out a few things that I noticed that may not be so obvious if you are just getting intimate with attachment_fu. I wanted to have attachment_fu and restful_authentication share the same model and form, so that a user can sign up, upload a picture, and submit all in one clean action. The example configuration in the post above assumes that you will be using a separate model, with a belongs_to association. Photo belongs_to Person, or something of the like. When I followed along, without much thought I included the parent_id field in my database migration not realizing the importance of that field. As it turns out, attachment_fu is smart enough to see if this field exists, and if it does it sets the directory to that name to store the images under. Note the following code in filesystem_backend.rb:


def attachment_path_id
  ((respond_to?(:parent_id) && parent_id) || id).to_i
end


So, if you are trying to get restful_authentication and attachment_fu to play nicely together, do not include parent_id as this example suggests. Reading on.. In the event that your application is small enough where you really don't need the level of separation that attachment_fu gives, you can override the full_filename method in the model. Rick made a reference to this, but didn't really get into much detail. He's probably busy writing plugins to assist me in keeping my boss happy, while allowing me a social life. Anyhow, to give an example of how one would do this to get a really simple file system storage, drop something like the following into your model (a separate model, or your restful_authentication model, either is fine):


class User < ActiveRecord::Base
   
  has_attachment :storage => :file_system, :path_prefix => 'public/pictures'
  validates_as_attachment
  validates_uniqueness_of :filename
 
  def full_filename(thumbnail = nil)
    file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s
    File.join(RAILS_ROOT, file_system_path, thumbnail_name_for(thumbnail))
  end
  
end


This will override the full_filename method found in lib/attachment_fu/technoweenie/attachment_fu/backends/file_system_backend.rb. You can override any other methods in that file, or any plugin for that matter the same way. While at it, a requirement was that if the user did not wish to upload an image, they could choose from a selection of stock images present below the upload form. This is nice, but proved to be a little tricky as if you selected a radio button for one of the stock images, all of the validation for attachment_fu failed. The requirements stated that since the company paying for this application may wish to change the default pictures from time to time, and if they did, they didn't want it to change the image that visitors had previously selected, it was necessary to have the attachment_fu upload these stock images from their home in public, to their place on the filesystem. I handled the logic for figuring out if the user was uploading a picture or using one of the supplied default images in the update method of the user_controller, provided by restful authentication, which looks something like:


  if params[:default_image] # controlled by the radio button
    @user.uploaded_data = default_image_filehandle(params[:default_image])  #radio button image
  else
    @user.uploaded_data = params[:user][:uploaded_data]  # use the upload form
  end


You may have noticed the default_image_filehandle method above, that is my slightly hackish way to inject some necessary information into the user object before its sent off to attachment_fu's validation. I'm sure that their are better, more elegant ways of doing so, but this worked for me. That method in detail is:


  def default_image_filehandle(name)
    faked_upload = File.open(File.join(RAILS_ROOT,'public','images/default_images',name))
    (class << faked_upload; self; end).class_eval do
    alias local_path path
      define_method(:original_filename) {name}

      # Below I just feed it values that will allow attachment_fu validation to pass.
      # Since this is being uploaded from public, no serious validation is really needed.
      define_method(:content_type) {"image/jpeg"}
      define_method(:size) { 68335 }
    end
    faked_upload
  end


And there you have it. Restful authentication and attachment_fu working with uploaded pictures, or some default site supplied photos. Plus, its kind of a neat way to use class_eval :) Aaah yes... Attachment_fu. It's nice to be intimate.

1 comment | Filed Under: Plugins | Tags: