Если вдруг захотелось изменить опцию :path в paperclip...

Posted by Dmitry Polushkin Sat, 19 Sep 2009 18:11:00 GMT

Позавчера столкнулся с проблемой, когда мне понадобилось изменить опцию :path в знаменитом и всеми используемом плагине для Rails paperclip от thoughtbot, когда проекту уже 2 месяца. Естественно, что файлов, за это время накопилось большое множество, и перемещать через консольные команды:

  1. не правильно вовсе, т.к. это почти что не возможно (структура, даже самая базовая настолько динамичная, что вряд ли это возможно без подключения, как минимум к базе данных… а bash + mysql – это hardcore);
  2. можно легко напутать, и переместить файлы в неправильное место;
  3. не люблю много писанины;

Лучше написать всеми любимый Raketask и радоваться, как он сделает всю работу за вас. И так, я начал с того, что полез в исходники paperclip. Так как я уже туда многократно заглядывал, то я знал, что там всё просто, логично, а не так как в attachment_fu. Решение нашлось через 2 часа.

Если вдруг у вас случилась такая же ситуация, вам понадобилось переместить файлы, и вы не знаете пока как, то предлагаю использовать ниже приведённый Raketask, который отработал на production и даже не разу не понадобилось восстанавливать файлы из backup’a.

Как пользоваться?

  1. если у вас нет Raketask’а для paperclip в lib/tasks/, то скопируйте его туда из gem’a
  2. дальше просто: подумайте какой path вы бы хотели использовать и напишите нечто вроде
    1. rake paperclip:change_path CLASS=Image PATH=:rails_root/public/system/:class/:attachment/:style/:id.:extension RAILS_ENV=production
  3. после этого сделайте commit и deploy на сервере с изменённым параметром :path для has_attachement

И это всё.

  desc "Moves files to a new :path NB! backup your files before use this task (set CLASS and PATH, ATTACHMENT is optional)"
  task :change_path => :environment do
    raise 'PATH must be specified, like PATH=:rails_root/public/system/:class/:attachment/:style/:id.:extension' if ENV['PATH'].blank?
    path = ENV['PATH']
    for_all_attachments do |instance, name|
      object = instance.send(name)
      old_path = object.instance_variable_get :'@path'
      
      styles = [:original] + object.styles.keys
      
      styles.each do |style|
        if object.exists?(style)
          object.queued_for_write[style] = object.to_file(style)
        end
      end

      object.instance_variable_set :'@path', path
      object.flush_writes

      object.instance_variable_set :'@path', old_path
      object.instance_variable_set :'@queued_for_delete', styles.map { |style| object.path(style) }
      object.flush_deletes

      # if stopped working then use:
      # path = Paperclip::Interpolations.interpolate(path, object, style)
      # and own write and delete functions
    end
  end