paperclip で保存した attachment の path を変更する

最近の仕事でプライベートなファイルがpublicなディレクトリにおいていることがわかったので、ファイルの置き場所を変更しました。(ちなみにプライベートなWEBサイトでbasic認証はかかっている)
paperclip で下記のようにモデルの定義をすると、デフォルトのパスに従ったディレクトリに保存します。

class Invoice < ActiveRecord::Base
  has_attached_file :pdf
end

しかし、デフォルトの値だとnginx が配信できるディレクトリに保存してしまいますので、下記のようにpathとurl を上書きしなければなりません。

class Invoice < ActiveRecord::Base
  has_attached_file :pdf,
    url: '/:class/:id/:attachment',
    path: ':rails_root/non-public/system/:class/:attachment/:id_partition/:style/:filename'
end

すでに保存されているファイルを新しいパスに移動するには、attachmentを再代入してsaveします。
paperclip のwikiにあったコードを参考にすると下記のように書くと移行できます。

def  old_path
  ':rails_root/public/system/:class/:attachment/:id_partition/:style/:filename'
end

def migrate_old_pdf
  logger = Logger.new('./migrate.log')
  Invoice.find_each do |invoice|
    unless invoice.pdf?
      logger.warn "#{invoice.id} にPDFを持っていないためskipします"
      next
    end
    pdf_path = Paperclip::Interpolations.interpolate(old_path, invoice.pdf, 'original')
    unless File.exists?(pdf_path)
      logger.warn "#{invoice.id} にPDFを持っていますがFSにファイルが存在していないのでskipします"
      next
    end
    File.open(pdf_path) do |file|
      invoice.pdf = file
      invoice.save!
    end
    logger.info "#{invoice.id} のPDFを移行しました"
  end
end

def delete_old_pdf
  require 'FileUtils'
  Invoice.find_each do |invoice|
    pdf_path = Paperclip::Interpolations.interpolate(old_path, invoice.pdf, 'original')
    FileUtils.rm(pdf_path)
  end
end

上位のディレクトリだけが変わるだけなので mv コマンドでも十分っぽい...。

参考