I have similar models as described in RailsCasts:
app/models/resident.rb:
class Resident < ActiveRecord::Base
  include PhotoConcern
end
app/models/employee.rb:
class Employee < ActiveRecord::Base
  include PhotoConcern
end
app/models/concerns/photo_concern.rb:
module PhotoConcern
  extend ActiveSupport::Concern
  included do
    mount_uploader :photo, PhotoUploader
    attr_accessor :photo_crop_x, :photo_crop_y, :photo_crop_w, :photo_crop_h
    after_save :crop_photo
    def crop_photo
      photo.recreate_versions! if photo_crop_x.present?
    end
  end
end
app/uploaders/photo_uploader.rb:
class PhotoUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  storage :file
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
  version :cropped do
    process :crop
  end
  version :thumb, from_version: :cropped do
    process resize_to_fill: [100, 100]
  end
  version :avatar, from_version: :cropped do
    process resize_to_fill: [200, 200]
  end
  def crop
    return if model.photo_crop_x.blank?
    resize_to_limit(500, nil)
    resize_to_fit(500, nil)
    manipulate! do |img|
      size = model.photo_crop_w << 'x' << model.photo_crop_h
      offset = '+' << model.photo_crop_x << '+' << model.photo_crop_y
      img.crop("#{size}#{offset}")
      img
    end
  end
end
app/views/employees/show.slim
= image_tag (@employee.photo.present? ? @employee.photo.url(:avatar) : "client_#{@employee.sex}.png"), class: 'img-circle img-responsive'
I want to rename version files after cropping so my users don't struggle with a cache. It's described in CarrierWave wiki how to rename files but also it's written "In order to save the newly generated filename you have to call save! on the model after recreate_versions!".
How can I rename version files? I can't call save! in my Employee's after_save again because there are more hooks that shouldn't be called twice. Also, PhotoConcern is included into another class.
Related wiki articles:
In order to save the newly generated filename you have to call save! on the model after recreate_versions!.
So I believe the answer to your doubts is included in the Carrierwave rubydocumentation
recreate_versions!(*versions) ⇒ ObjectRecreate versions and reprocess them. This can be used to recreate versions if their parameters somehow have changed.
This method will store the *versions if they are not ommitted, otherwise the cached file will be stored.
# File 'lib/carrierwave/uploader/versions.rb', line 216
def recreate_versions!(*versions)
  # Some files could possibly not be stored on the local disk. This
  # doesn't play nicely with processing. Make sure that we're only
  # processing a cached file
  #
  # The call to store! will trigger the necessary callbacks to both
  # process this version and all sub-versions
  if versions.any?
    file = sanitized_file if !cached?
    # the file will be stored
    store_versions!(file, versions)
  else
    cache! if !cached?
    # If new_file is omitted, a previously cached file will be stored.
    store!
  end
What does store! do?
This is the rubydoc page about store!
store!(new_file = nil) ⇒ ObjectStores the file by passing it to this Uploader's storage engine. If new_file is omitted, a previously cached file will be stored
This method is included in your class PhotoUploader < CarrierWave::Uploader::Base and it uses with_callbacks to store your file using the callback :store. The callback triggers this method.
# File 'lib/carrierwave/uploader/store.rb', line 53
def store!(new_file=nil)
  cache!(new_file) if new_file && ((@cache_id != parent_cache_id) || @cache_id.nil?)
  if !cache_only and @file and @cache_id
    with_callbacks(:store, new_file) do
      new_file = storage.store!(@file)
      if delete_tmp_file_after_storage
        @file.delete unless move_to_store
        cache_storage.delete_dir!(cache_path(nil))
      end
      @file = new_file
      @cache_id = nil
    end
  end
end
What does store_versions! method do?
def store_versions!(new_file, versions=nil)
  if versions
    active = Hash[active_versions]
    versions.each { |v| active[v].try(:store!, new_file) } unless active.empty?
  else
    active_versions.each { |name, v| v.store!(new_file) }
  end
end
What are the Carrierwave callbacks and how to use them?
 after :store, :store_versions!
This question on SO explains and the wiki explain how callbacks work, by doing after :store, :my_method inside your version :low do block, you will execute my_method only on after :store callback (only for that version).
The :store callback corresponds to the execution of store!.
And what is the @filename attribute? How does recreate_versions! encode the filename?
@filename is defined with method filename in lib/carrierwave/uploader/store.rb
##
# Override this in your Uploader to change the filename.
#
# Be careful using record ids as filenames. If the filename is stored in the database
# the record id will be nil when the filename is set. Don't use record ids unless you
# understand this limitation.
#
# Do not use the version_name in the filename, as it will prevent versions from being
# loaded correctly.
#
# === Returns
#
# [String] a filename
#
def filename
  @filename
end
The guide from carrierwave suggest to use def filename to recreate unique filenames when recreating versions with recreate_version!.
This method does not save to the Database, to save to the database you need to calle save! on the appropriata Carrierwave callbacks, without breaking you Carrierwave GEM
I don't have the solution to this issue, but there is no documentation on this and we should start building it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With