Help us understand the problem. What is going on with this article?

ActiveRecordのコピー時にPaperclipのAttachmentも正しくコピーする

More than 5 years have passed since last update.

paperclip付きのActiveRecorddupした時にattachmentは同じファイルが別パスにコピーされるようにしようとして、はまりました。

何が起きたか

paperclipのattachmentをコピーする場合、以下のような方法が取れることがネット上でよく書かれている。

imageがpaperclip attachmentだとして、fooimagebarにコピーすることを考えます。

bar.image = foo.image

これ自体は正しいです。
でも、これをインスタンスのコピーdupに適用しようとすると、うまくいかないことがあります。

def dup
  copy = super
  copy.image = self.image
  copy
end

paperclipは内部でアクセッサを動的に定義しており、dupcloneメソッドを使うと、この動的に作られたアクセッサもコピーされてしまいます。
そのため、上のコードで言うと、copy.image === self.imageが成り立つことになります。

解決策

汚いやり方ですが、dup内でアクセッサをクリアします。
paperclipの内部ロジックがpaperclip 4から変わっているので両方に対応させたものが以下です。

  def dup
    copy = super
    if self.respond_to?(:each_attachment)
      copy.instance_variable_set("@_paperclip_attachments", {})
      self.each_attachment do |name, attachment|
        copy.__send__("#{name}=", self.__send__(name)) if self__send__(name).exists?
      end
    # Hack for paperclip 4
    elsif self.class.respond_to?(:attachment_definitions)
      self.class.attachment_definitions.keys.each do |name|
        copy.instance_variable_set("@attachment_#{name}", nil)
        copy.__send__("#{name}=", self.image) if self.__send__(name).exists?
      end
    end
    copy
  end

これで、正しくattachmentをコピーすることができました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away