Fun with attachment_fu

As I mentioned in my previous post I am going to outline my experiences with attachment_fu. I will try to extend the brilliant tutorial that first introduced me to attachment_fu and to ImageScience.

The problem

The simple problem I had was that I wanted to be able to associate a image (and thumbnails) to a article. Although this sounds easy, I had some trouble along the way (hence the post).

Write the Migration models



class CreateImages < ActiveRecord::Migration

  def self.up
    create_table :images do |t|
      t.column :parent_id,  :integer
      t.column :content_type, :string
      t.column :filename, :string    
      t.column :thumbnail, :string 
      t.column :size, :integer
      t.column :width, :integer
      t.column :height, :integer
      t.column :content_id, :integer
    end
  end

  def self.down
    drop_table :images
  end
end

class CreateContents < ActiveRecord::Migration

  def self.up
    create_table :contents do |t|
      t.column :title , :string
      t.column :summary, :text
      t.column :body, :text
      t.column :status, :string
    end
  end

  def self.down
    drop_table :contents
  end
end

The only thing to note here is that I have placed a content_id foreign key into the image table, to allow them to be linked.

Write the models

The models are going to have two different associations, both of type has_one. The first association has an extra condition that allows it to only return the parent image, the second association returns the parent image and all its associated thumbnails.


class Content < ActiveRecord::Base
  has_one :image, :conditions => 'parent_id is null'
  has_one :all_images, :class_name => 'Image' # all photos + thumbnails
end

The Image model has the standard attachment_fu declaration along side the thumbnail declarations.


class Image < ActiveRecord::Base
  has_attachment :content_type => :image, 
                   :storage => :file_system,
                   :thumbnails => { :thumb => '107x76>'}    
end

Write the form view


<% form_for(:content, :url => { :action=>'create'}, 
                      :html => { :multipart => true }) do |f| -%>

<p>
  <label for="title">Title</label><br/>
  <%= f.text_field :title %>
</p>

<p>
  <label for="summary">Summary</label><br/>
  <%= f.text_area :summary %>
</p>

<p>
  <label for="title">Body</label><br/>
  <%= f.text_area :body %>
</p>

 <p>
    <label for="image">Associate Image:</label><br/>
    <%= file_field 'image','uploaded_data' %>
  </p>
  <p>
    <%= submit_tag 'Create' %>
  </p>
<% end -%>

As you can see I have placed the image in a different hash, as you can’t associate the file directly to the model. All of the other fields are pretty standard. To test out the update method you just need to change the URL to point to the update method and pass it the content id. I will leave it out here for simplicity.

Write the controller

This is where I was having most trouble. No matter what I did, it just did not save the dam thing. So here is my create and update actions in my contents controller


def create
    #Create the content
    @content = Content.new(params[:content])
    respond_to do |format|
      if @content.save
        #Here comes the important bit!
        if !params[:image][:uploaded_data].blank?
          @content.image = Image.create(params[:image])       
        end
        flash[:notice] = 'Content was successfully created.'
        format.html { redirect_to :action=>'show', :id=>@content }
      else
        format.html { render :action => "new" }
      end
    end
  end  

OK that was not that hard :). But I was having problems on the update action. The create action seemed to be working fine. Here is the update action.


  def update
      @content = Content.find(params[:id])
      respond_to do |format|
        if @content.update_attributes(params[:content])
          # Heres the important bit!
          if !params[:image][:uploaded_data].blank?
            #find current image
            @image = @content.image ||= Image.new
            @image = @content.image.build(params[:image])
            @image.save       
          end
          format.html { redirect_to :action=>'show', :id=>@content }
        else
          format.html { render :action => "edit" }
        end
      end
    end
 

As you can see I am using the build method to create the image. I am not sure why, but If I put the build method into the create method it just fails. Oh yeah and in a real application I would group the image methods into their own respective functions.

The content view

So for all the code we have written we can now show the images that are associated with a specific article.


  <h2><%=@content.title%> </h2>
  <p><%=@content.summary%></p>

<%if !@content.image.blank?%> 
  <p>
    Original Image : <%=image_tag @content.image.public_filename %>
  </p>
  <p>
    Thumbnail : <%=image_tag @content.image.public_filename(:thumb) %>
  </p>
  <p>
    <%=@content.body%>
  </p>
<%end%>

Outstanding issues

There a few bugs in attachment_fu that I came across, they are :

  • The width and the height are not being set for thumbnails when I use ImageScience and attachment_fu.
  • The size (in the database field) of the thumbnails are not correct. They just display the original file’s image size.

I will blog again regarding these issues in the near future.

Code

In writing this post, I made a test rails app so I could make sure that the code I have above is correct and compiles without any errors. If anyone wants this I can put a link to it here, just leave a comment.

As usual all comments welcome.

Hamza

1 Response to “Fun with attachment_fu”

  1. Henry Leparskas Says:
    Thanks for going over this important case. I have been able to use your example in my own 'has_one' model association. I'm not sure if it is necessary, but I also added the 'belongs_to' into the 'image' model. I had no trouble with the update from the edit. In fact, the association took care of it nicely, as in; if !params[:mugshot][:uploaded_data].blank? @person.mugshot.update_attributes(params[:mugshot]) end

Leave a Reply