0

I am using the Carrierwave gem with a Sinatra app to upload and store images.

I am following How to: Use file's MD5 as filename and everything works as expected. However I do not understand how the following piece of code provided on the how-to page works:

class ImageUploader < CarrierWave::Uploader::Base
  storage :file
  def md5
    chunk = model.send(mounted_as)
    @md5 ||= Digest::MD5.hexdigest(chunk.read)
  end
  def filename
    @name ||= "#{md5}#{File.extname(super)}" if super
  end
end

I in particular do not understand what model.send(mounted_as)does, what the ||= operator means, and why the if super conditional is used (and what it does).

Could somebody please explain this to me?

tobias.henn
  • 225
  • 2
  • 13
  • Try Google/StackOverflow search: [Object#send](http://ruby-doc.org/core-2.1.0/Object.html#method-i-send), [What does ||= (or equals) mean in Ruby?](http://stackoverflow.com/questions/995593/what-does-or-equals-mean-in-ruby), [Super keyword in Ruby](http://stackoverflow.com/questions/4632224/super-keyword-in-ruby) – Patrick Oscity Jan 02 '14 at 12:28
  • I think I understand what the `send` method and `super` in general mean, but I do not get why these methods/concepts are used in the above context. I understand that constructs like `@current_user ||= User.find_by_id(session[:user_id])` are used to avoid unnecessary database calls. But isn't it clear in our context that `@md5` needs to be calculated since it for sure is not known yet? – tobias.henn Jan 02 '14 at 12:42
  • Ok, I think the `||=` pattern is used to create the `@md5`instance variable when it is first read, while making sure that we do not recalculate it for every single subsequent call. Thanks. – tobias.henn Jan 02 '14 at 12:50
  • But why the `if super`? Is this some sort of error handling? – tobias.henn Jan 02 '14 at 13:29
  • the `if super` means "only return something if the superclass method returns something other than `nil` or `false`". It is there to prevent `File.extname` from throwing an error if `filename` is `nil`. – Patrick Oscity Jan 02 '14 at 14:05

1 Answers1

1

Say, for example the model is Person and the ImageUploader is mounted as avatar.

class Person < ActiveRecord::Base
  mount_uploader :avatar, ImageUploader
end

Then, the md5 method would be calling something to the affect of chunk = person.avatar and using this to calculate the hash of the file contents, which you want for the name.

The filename method checking to see if there is a @filename instance variable, as defined in the CarrierWave::Uploader::Store class.

class CarrierWave::Uploader::Store
  def filename
    @filename
  end
end

Then calling this again to get the filename extension to use in the constructed filename. The @name variable is then just a temporary cache of the name, so that future calls to the method do not require the whole thing to be calculated again.

Edit:

The carrierwave uploader has methods/instance variables for the model (eg Person instance) and declared mount point in the model (eg avatar). These are from the mount_uploader declaration in your active record model.

module CarrierWave::Uploader::Mountable
  attr_reader :model, :mounted_as

  def initialize(model=nil, mounted_as=nil)
    @model = model
    @mounted_as = mounted_as
  end
end

These are used for various things, as well as being made available for us to do things just such as you are trying. It is just an abstract way to call person.avatar, which returns the file (File instance not string path). This is then read into the MD5 lib, which gives the hexdigest.

Rewriting this in more plain terms

class ImageUploader < CarrierWave::Uploader::Base
  def md5
    uploaded_file = model.send(mounted_as) # person.avatar (File instance)
    @md5 ||= Digest::MD5.hexdigest(uploaded_file.read) # hexdigest of file content
  end
end
S.Spencer
  • 581
  • 3
  • 8
  • Thanks for explaining this! But can you clarify a little bit more why exactly `chunk = model.send(mounted_as)` does return the temporary file path? What exactly does `model` refer to? – tobias.henn Jan 02 '14 at 16:41