Use before_destroy on a model with paperclip

Posted on Jan 4, 2011

I have an application that I’m working on, this application has a demand that I will show the total files and the file total size inside a project. Here is my model:

Of course I have the option of using joins and so on and then calculating the file size, but because every project has many folders and every folder has many attachments this seems a bit off and not straight forward.

This is absolutely not the Rails way of doing things, so what I added a couple of fields inside the project model and here is what my model looked like after the change:

Now, I have straight forward fields inside my model, and the database work is very light, fast and straight forward. I added an observer for the attachment model, just to update the count and the total file size in the project model.

Here’s my observer’s code:

[ruby]

class AttachmentObserver < ActiveRecord::Observer

observe :attachment

def after_create(record)

project = record.project

if project

project.increment!(:file_count, 1)

project.increment!(:file_size, record.sketch_file_size) unless record.sketch_file_size.nil?

end

return true

end

def before_destroy(record)

project = record.project

if project

project.decrement!(:file_count, 1)

project.decrement!(:file_size, record.sketch_file_size) unless record.sketch_file_size.nil?

end

return true

end

end

[/ruby]

I added the observer to the application configuration.

Now, everything seemed to be working fine on create, when I deleted a model (deleted an attachment) I got a nil exception on the skecth_file_size property. The problem is that the paperclip plugin deleted the sketch file before my callback and so prevented me from using it.

This required a patch to the paperclip plugin.

Inside the paperclip plugin there’s a file called paperclip.rb

Ffor me, it was line 242, I changed it from before_destroy to after_destory like so:

[ruby]

after_destroy :destroy_attached_files

[/ruby]

Now, also in attachment.rb (also in paperclip plugin)

For me it was line 335:

[ruby]

unless instance.frozen?

instance_write(:file_name, nil)

instance_write(:content_type, nil)

instance_write(:file_size, nil)

instance_write(:updated_at, nil)

end

[/ruby]

This worked like a charm and now my callback works, updating the model of the project as the files are adding and deleting, I don’t have to do complicated joins to get simple data.

Here’s how it looks in my application now:

This is the code I use to make it happen:

[ruby]

<%= @project.name %>

[/ruby]