paperclip
付きのActiveRecord
をdup
した時にattachment
は同じファイルが別パスにコピーされるようにしようとして、はまりました。
何が起きたか
paperclipのattachmentをコピーする場合、以下のような方法が取れることがネット上でよく書かれている。
imageがpaperclip attachmentだとして、foo
のimage
をbar
にコピーすることを考えます。
bar.image = foo.image
これ自体は正しいです。
でも、これをインスタンスのコピーdup
に適用しようとすると、うまくいかないことがあります。
def dup
copy = super
copy.image = self.image
copy
end
paperclipは内部でアクセッサを動的に定義しており、dup
やclone
メソッドを使うと、この動的に作られたアクセッサもコピーされてしまいます。
そのため、上のコードで言うと、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
をコピーすることができました。