Skip to content

Instantly share code, notes, and snippets.

@glongman
Created April 3, 2012 20:27
Show Gist options
  • Save glongman/2295280 to your computer and use it in GitHub Desktop.
Save glongman/2295280 to your computer and use it in GitHub Desktop.
My unsubmitted carrierwave-mongoid issue

Environment:

MacOSX 10.6.8 Ruby 1.9.3-p125 MongoDB

db version v2.0.3, pdfile version 4.5
Tue Apr  3 14:47:32 git version: 05bb8aa793660af8fce7e36b510ad48c27439697

Rails 3.2.1 Mongoid 2.4.5 carrierwave-mongoid 0.1.3

Have run into this problem when the Mongoid::IdentityMap is on and I upload a file with a different name. We are storing zip files in GridFS.

What we are seeing is that this is not a bug per se in carrierwave but rather, when the IdenitityMap is on the work that carrierwave does to determine if the old file should be removed is farked up by the map. Long boring details of what I observed are at the end of this submission.

Looking at carrierwave code (and the carrierwave-mongoid code) we think that the

before_filter :store_previous_model_for<column>

fetches a pristine copy of the record by calling the carrierwave-mongoid version of

find_previous_model_for<column>

so the after_save filter can compare the two and remove the old file if need be.

But with the IdentityMap on find_previous(...) will pull the record from the IdentityMap and will not fetch a pristine copy of the record from the database. Thus carrierwave's checks fail and the old file is not remove from the store.

Alas, I see no facility in Mongoid to bypass the IdentityMap in this case. Perhaps I will bug the Mongoid folks or make a patch for them myself. Something like this could be used by carrierwave-mongoid to fix this issue:

(carrierwave-mongoid) /lib/carrierwave/mongoid.rb

        def find_previous_model_for_#{column}

           self.ignore_identity_map do # this does not exist in Mongoid today

             if self.embedded?
               ancestors       = [[ self.metadata.key, self._parent ]].tap { |x| x.unshift([ x.first.last.metadata.key, x.first.last._parent ]) while x.first.last.embedded? }
               first_parent = ancestors.first.last
               reloaded_parent = first_parent.class.find(first_parent.to_key.first)
               association = ancestors.inject(reloaded_parent) { |parent,(key,ancestor)| (parent.is_a?(Array) ? parent.find(ancestor.to_key.first) : parent).send(key) }
               association.is_a?(Array) ? association.find(to_key.first) : association
             else
               self.class.find(to_key.first)
             end
           
          end

        end

So that's my issue. Right now I think it's not fixable without a change in Mongoid itself.

What I observed

First, with no file attached and the IdentityMap turned off (TL;DR works as expected)

rails console

>> s = Site.first
=> #<Site _id: 4f7b3c189cf77ec43f000002, _type: nil, created_at: 2012-04-03 18:06:16 UTC, name: "NOSM", file_map: {}, site_zip: nil>
>> s.site_zip = File.open('/Users/glongman/Downloads/smacss.zip')
=> #<File:/Users/glongman/Downloads/smacss.zip>
>> s.save
=> true

mongo query showing the first zip in the db:

> db.fs.files.find({filename: /.*.zip/})
{ "_id" : ObjectId("4f7b3b829cf77ec305000001"), 
"filename" : "/sites/4f79b80e9cf77e1b86000002/smacss.zip", "contentType" : "binary/octet-stream", "length" : 1278914, "chunkSize" : 262144, "uploadDate" : ISODate("2012-04-03T18:03:46.470Z"), "md5" : "e95487ea11a61b6e504018975aaaf4cd" }

Now we return to the console and replace the attached zip with another one

>> s.site_zip = File.open('/Users/glongman/Downloads/nosm.zip')
=> #<File:/Users/glongman/Downloads/nosm.zip>
>> s.save
=> true

And the db shows that carrierwave replaced smacss.zip with nosm.zip

> db.fs.files.find({filename: /.*.zip/})
{ "_id" : ObjectId("4f7b3bf29cf77ec436000007"), 
"filename" : "/sites/4f7b3bb89cf77ec431000002/nosm.zip", "contentType" : "binary/octet-stream", "length" : 336729, "chunkSize" : 262144, "uploadDate" : ISODate("2012-04-03T18:05:38.664Z"), "md5" : "54aacc97b24fa23d5fd322b21c3b0d38" }

Now with the IdentityMap turned on (TL;DR old attachment not removed from GridFS by carrierwave)

Attachments from previous were removed before starting

>> s = Site.first
=> #<Site _id: 4f7b3c189cf77ec43f000002, _type: nil, created_at: 2012-04-03 18:06:16 UTC, name: "NOSM", file_map: {}, site_zip: nil>
>> s.site_zip = File.open('/Users/glongman/Downloads/smacss.zip')
=> #<File:/Users/glongman/Downloads/smacss.zip>
>> s.save
=> true
> db.fs.files.find({filename: /.*.zip/})
{ "_id" : ObjectId("4f7b3c2f9cf77ec444000001"), 
"filename" : "/sites/4f7b3c189cf77ec43f000002/smacss.zip", "contentType" : "binary/octet-stream", "length" : 1278914, "chunkSize" : 262144, "uploadDate" : ISODate("2012-04-03T18:06:39.440Z"), "md5" : "e95487ea11a61b6e504018975aaaf4cd" }
>> s.site_zip = File.open('/Users/glongman/Downloads/nosm.zip')
=> #<File:/Users/glongman/Downloads/nosm.zip>
>> s.save
=> true
> db.fs.files.find({filename: /.*.zip/})
{ "_id" : ObjectId("4f7b3c499cf77ec444000007"), 
"filename" : "/sites/4f7b3c189cf77ec43f000002/nosm.zip", "contentType" : "binary/octet-stream", "length" : 336729, "chunkSize" : 262144, "uploadDate" : ISODate("2012-04-03T18:07:05.288Z"), "md5" : "54aacc97b24fa23d5fd322b21c3b0d38" }
{ "_id" : ObjectId("4f7b3c2f9cf77ec444000001"), 
"filename" : "/sites/4f7b3c189cf77ec43f000002/smacss.zip", "contentType" : "binary/octet-stream", "length" : 1278914, "chunkSize" : 262144, "uploadDate" : ISODate("2012-04-03T18:06:39.440Z"), "md5" : "e95487ea11a61b6e504018975aaaf4cd" }

:(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment