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