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.

Issue with has_many and Array.empty?

I've posted a narrative of my issue on the Google Rails Group:


/app/controllers/document_groups_controller.rb
def show
@document_group = DocumentGroup.find(params[:id])
@journal = Journal.find(params[:journal_id])
@document_group_templates = @document_group.document_templates
@logger = RAILS_DEFAULT_LOGGER

#
# this is the money line: comment it out and @document_group_templates.empty?
# is true in the view. leave it in, and it's false. true is correct.
#
@logger.debug "###### --->" + @document_group_templates.inspect
#
# it turns out that this triggers the SQL to be run by Rails that acutally
# populates the @document_group_templates Array.
#

render :action => :show, :layout => 'ajax_div'
end


/app/views/document_groups/show.rhtml
#
#@document_group_templates.empty? evaluates to true without the inspection above
#
<% if !@document_group_templates.empty? %>
#
# now, what's interesting is that if i change this to
#
# if @document_group_templates.count > 0
#
# that also triggers the SQL to be run. but, interestingly, #size does not
# trigger the SQL.
#
<ul>
<%
@document_group_templates.each do |child|
%><li><%= link_to child.name, :controller => :template, :action => :show, :id => child.id %><%
end
%>
</ul>
<% end %>

/app/models/document_group.rb
class DocumentGroup < ActiveRecord::Base
has_many :document_templates, :order => :document_group_position
acts_as_tree :counter_cache => true
end

/app/models/document_template.rb
class DocumentTemplate < ActiveRecord::Base
belongs_to :document_group, :counter_cache => true
acts_as_list :scope => :document_group, :column => :document_group_position
end

How to call render :partial properly

At some point, you may be going bananas because you're getting this error:

NoMethodError in DocumentController#new
undefined method `include?' for :new:Symbol

and the problem is that you're doing this somewhere:
render :partial => :new

instead of this:
render :partial => 'new'


This error is spawned by the method partial_pieces in /ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/partials.rb:90 which is trying to figure out if the partial you're calling is in the directory of the current controller, or some other directory. Here's that code:
89      def partial_pieces(partial_path)
90 if partial_path.include?('/')
91 return File.dirname(partial_path), File.basename(partial_path)
92 else
93 return controller.class.controller_path, partial_path
94 end
95 end

The error comes from line 90. Seems like if this were inserted at line 89.5, then this problem could be handled more gracefully
partial_path = partial_path.to_s if partial_path.is_a? Symbol

given the fuzzyness of symbols and strings, it would seem to make sense to me.