attachment_fu (with rmagick) on windows

Today, I've been working to get attachment_fu working with rmagick on Windows. At first, it wasn't uploading the file properly and after much Googling implemented these changes to get it working.

1. I added a pause to my #create in order to allow Windows time to catch up. In short, Ruby is moving faster than the Windows file system, and it was trying to create a record for a file that didn't exist yet. Here's what I did:

  def create
@upload = Upload.new(params[:upload])
sleep 5 ## <<-- pauses so Windows can catch up. Probably would be
## smart to make this something like sleep 5 if OS == Windows
## obviously, that's not the right syntax.
if @upload.save
flash[:notice] = 'Upload was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end

I am fully aware that this is a hack, but it gets the job done.

2. I continued to get the error message "Size is not included in the list". Long story short, this is the error message attachment_fu returns when the file size exceeds the limit set in the model. In addition, it provides this message if no file is provided at all which is a duplicate error because validates_as_attachment is already doing a validates_presence_of against the size.

So, in order to create some better error messaging, and following the notions of how to hack a plugin from this blog post, I created a new plugin called attachment_fu_hacks and in init.rb added this code:
klass = Numeric
klass.class_eval do
def to_human
units = %w{B KB MB GB TB}
e = (Math.log(self)/Math.log(1024)).floor
s = "%.1f" % (to_f / 1024**e)
s.sub(/\.?0*$/, ' ' + units[e])
end
end

klass = Technoweenie::AttachmentFu::InstanceMethods
klass.module_eval do

# overrides attachment_fu.rb:342 b/c to provide better error messages
def attachment_attributes_valid?

attr_name = :size
enum = attachment_options[attr_name]
val = send(attr_name)
if !enum.nil? && !val.nil? && !enum.include?(val)
err_msg = enum.end.to_human
errors.add attr_name, "of file is too big (must be less than #{err_msg})"
end

attr_name = :content_type
enum = attachment_options[attr_name]
val = send(attr_name)
if !enum.nil? && !val.nil? && !enum.include?(val)
err_msg = enum.join(', ').gsub(/image\//, '.') unless enum.nil?
errors.add attr_name, "is not supported (must be #{err_msg})"
end
end
end

klass = Technoweenie::AttachmentFu::ClassMethods
klass.module_eval do
# overrides attachment_fu.rb:106 b/c i don't think it's necessary
# to evaluate size or content_type if there is no file
def validates_as_attachment
validates_presence_of :filename
validate :attachment_attributes_valid?
end
end

I recognize that there might be a more idiomatic Ruby way to write attachment_attributes_valid?, but I prefer this because it's very clear as to what's going on.

2 comments:

Unknown said...

I have to tell you a big thank you - your "sleep 5" works like a charm on my windows dev box and enabled me to present to a client without embarrassment!

Jakub Arnold said...

The sleep 5 saved my life :)