Skip to content

Instantly share code, notes, and snippets.

@MarceloCajueiro
Last active July 18, 2024 07:07
Show Gist options
  • Save MarceloCajueiro/7fada6462beb066af72d6aefde9ae3e8 to your computer and use it in GitHub Desktop.
Save MarceloCajueiro/7fada6462beb066af72d6aefde9ae3e8 to your computer and use it in GitHub Desktop.
Migrate Paperclip to Carrierwave in a Rails application.

This tutorial allow you to migrate the Paperclip that uses Hash in the file name to Carrierwave. I will not give all the details but I will give the before and after at the end of this tutorial.

In my case the config of the upload with Paperclip was:

class SomeAttachment < ActiveRecord::Base
  has_attached_file :attachment,
                    validate_media_type: false,
                    url: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
                    path: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
                    hash_secret: Rails.application.secrets.secret_key_base.to_s,
                    s3_permissions: :private
end

It was saving the raw file name at attachment_file_name but to migrate to Carrierwave and to access the file properly we need to save the file name with the generated hash (Paperclip does that).

To do that, I created a new column called attachment_file_name_with_hash and ruuned this script:

SomeAttachment.find_each do |attachment|
  attachment.update_column(:attachment_file_name_with_hash, attachment.attachment.path.split('/').last)
end

I created the new attachment column:

add_column :some_attachments, :attachment, :string

And migrated the values in a migration:

execute(<<-SQL)
  update some_attachments
  set attachment = attachment_file_name_with_hash;
SQL

Finaly when creating the Carrierwave uploader, don't forget to set the store_dir equals to the Paperclip config.

In my case:

def store_dir
  "#{Rails.env}/#{model.class.to_s.underscore.pluralize}/#{model.id}"
end

Before

class AbsenceJustificationAttachment < ActiveRecord::Base
  belongs_to :absence_justification

  has_attached_file :attachment,
                    validate_media_type: false,
                    url: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
                    path: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
                    hash_secret: Rails.application.secrets.secret_key_base.to_s,
                    s3_permissions: :private

  validates_attachment_file_name :attachment, matches: [/png\z/, /jpeg\z/, /jpg\z/, /gif\z/, /pdf\z/, /odt\z/,
                                                        /doc\z/, /docx\z/, /ods\z/, /xls\z/, /xlsx\z/, /odp\z/,
                                                        /ppt\z/, /pptx\z/, /odg\z/, /xml\z/, /csv\z/]
  validates_with AttachmentSizeValidator, attributes: :attachment, less_than: 3.megabytes

  validates :attachment, presence: true
end

After

class AbsenceJustificationAttachment < ActiveRecord::Base
  belongs_to :absence_justification

  mount_uploader :attachment, DocUploader

  validates :attachment, presence: true

  def filename
    attachment&.path&.split('/')&.last
  end
end
class DocUploader < CarrierWave::Uploader::Base
  def store_dir
    "#{Rails.env}/#{model.class.to_s.underscore.pluralize}/#{model.id}"
  end

  def extension_white_list
    %w[png jpeg jpg gif pdf odt doc docx ods xls xlsx odp ppt pptx odg xml csv]
  end

  def filename
    original = original_filename.split(".")[0..-2].join(".")
    "#{original} - #{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected

  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) || model.instance_variable_set(var, SecureRandom.uuid)
  end
end
@TB-Development
Copy link

You can specify you're coming from paperclip with something like this:

 include CarrierWave::Compatibility::Paperclip
  storage :fog

  def paperclip_path
    "shared/system/:class/:attachment/:id_partition/:style/:basename"
  end

this would support paperclips way of defining upload and download locations, hence not breaking previous uploads.

See more here: https://www.rubydoc.info/github/jnicklas/carrierwave/CarrierWave/Compatibility/Paperclip

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