henrebotha
henrebotha

Reputation: 1298

after_create file saving callback resulting in intermittent error

In my user model, I have an after_create callback that looks like this:

def set_default_profile_image
  file = Tempfile.new([self.initials, ".jpg"])
  file.binmode
  file.write(Avatarly.generate_avatar(self.full_name, format: "jpg", size: 300))
  begin
    self.profile_image = File.open(file.path)
  ensure
    file.close
    file.unlink
  end
  self.save
end

(self.initials is simply a utility method that returns the user's initials, so that e.g. my profile image would be "HB.jpg".)

If I call the method directly on an existing user, it works maybe 80% of the time. The other times, it gives me an error message so long I can't reproduce it here (I can't even scroll back far enough in tmux to see the start of it). The error message (or what I can see of it, anyway) comprises a list of MIME types, followed by this bit:

content type discovered from file command: application/x-empty. See documentation to allow this combination.

If I create a new user, the callback results in the same error message 100% of the time.

My method uses the Avatarly gem to generate placeholder avatars; the gem yields them in blob form, hence the creation of a Tempfile to write to.

I can't understand why the above error would occur.

Upvotes: 0

Views: 190

Answers (3)

henrebotha
henrebotha

Reputation: 1298

I found the solution in an issue on Paperclip's github. I don't really understand the causes very well, but it seems that this is a filesystem issue, where the Tempfile is not yet persisted to disk by the time it gets read into the model.

The solution is to do absolutely anything to the Tempfile before assigning it; file.read works just fine.

def set_default_profile_image
  file = Tempfile.new([self.initials, ".jpg"])
  file.binmode
  file.write(Avatarly.generate_avatar(self.full_name, format: "jpg", size: 300))
  file.read # <-- this fixes the issue
  begin
    self.profile_image = File.open(file.path)
  ensure
    file.close
    file.unlink
  end
  self.save
end

Upvotes: 0

Andy Gauge
Andy Gauge

Reputation: 1428

What do you expect happens when you do this?

self.profile_image = File.open(file.path)

Without a block, this is the same as:

self.profile_image = File.new(file.path)

They both return a file object. Is profile_image in the database? I'm pretty sure it is going to be mad that you sent a File object to be persisted. If you want the data from that file in the database, do something like:

self.profile_image = File.open(file.path).read

If you want to save the tempfile's path:

self.profile_image = File.path(file.path)

If you are using the path remember that you are saving a tempfile, and the file will not last very long!

Upvotes: 0

CWitty
CWitty

Reputation: 4536

Make sure that full_name has a valid return value and try moving your save call into the begin section. You may be racing against a save and the tempfile being removed/unlink.

Upvotes: 1

Related Questions