Permalink
Browse files

Added automatic renaming of attachments on model update. Not supporte…

…d for S3 yet.
  • Loading branch information...
1 parent 75d10b9 commit d01bdc192f5a15722db088b29326323eac1ff0dc thedarkone committed Jan 17, 2009
Showing with 89 additions and 13 deletions.
  1. +28 −7 lib/paperclip.rb
  2. +21 −2 lib/paperclip/attachment.rb
  3. +18 −4 lib/paperclip/storage.rb
  4. +22 −0 test/attachment_test.rb
View
@@ -188,6 +188,7 @@ def has_attached_file name, options = {}
attachment_definitions[name] = {:validations => {}}.merge(options)
after_save :save_attached_files
+ before_update :rename_attached_files
before_destroy :destroy_attached_files
define_callbacks :before_post_process, :after_post_process
@@ -281,22 +282,42 @@ def attachment_for name
@_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
end
- def each_attachment
- self.class.attachment_definitions.each do |name, definition|
- yield(name, attachment_for(name))
+ def all_attachments
+ self.class.attachment_definitions.map do |name, definition|
+ attachment_for(name)
end
end
-
+
def save_attached_files
logger.info("[paperclip] Saving attachments.")
- each_attachment do |name, attachment|
- attachment.send(:save)
+ all_attachments.each(&:save)
+ end
+
+ def rename_attached_files
+ logger.info("[paperclip] Renaming attachments.")
+ # only trying to handle the case when some attributes of the model were changed (e.g. avatar_file_name),
+ # but nothing has been done to the attachment itself (i.e. it is 'clean') and we only need to rename the files
+ unless (my_changes = changes).empty? || (clean_attachments = unchaned_attachments).empty?
+ # how the instance looked before
+ old_instance = self.class.new(attributes.merge!(_old_attributes_hash(my_changes)))
+ clean_attachments.each do |attachment|
+ attachment.send(:queue_existing_for_rename, old_instance)
+ attachment.send(:flush_renames)
+ end
end
end
+ def unchaned_attachments
+ all_attachments.reject {|attachment| attachment.dirty? || !attachment.file?}
+ end
+
+ def _old_attributes_hash(changes)
+ changes.inject({}) {|h, (attr, (old_value, new_value))| h.update(attr => old_value) }
+ end
+
def destroy_attached_files
logger.info("[paperclip] Deleting attachments.")
- each_attachment do |name, attachment|
+ all_attachments.each do |attachment|
attachment.send(:queue_existing_for_delete)
attachment.send(:flush_deletes)
end
@@ -16,7 +16,7 @@ def self.default_options
}
end
- attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write
+ attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write, :queued_for_rename
# Creates an Attachment object. +name+ is the name of the attachment,
# +instance+ is the ActiveRecord object instance it's attached to, and
@@ -39,6 +39,7 @@ def initialize name, instance, options = {}
@background = options[:background].nil? ? instance.respond_to?(:spawn) : options[:background]
@processors = options[:processors] || [:thumbnail]
@options = options
+ @queued_for_rename = []
@queued_for_delete = []
@queued_for_write = {}
@errors = {}
@@ -235,6 +236,10 @@ def reprocess!
def file?
!original_filename.blank?
end
+
+ def exists?(style = default_style)
+ path_exists?(path(style))
+ end
# Writes the attachment-specific attribute on the instance. For example,
# instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
@@ -374,10 +379,20 @@ def interpolate pattern, style = default_style #:nodoc:
end
end
+ def queue_existing_for_rename(old_instance)
+ old_attachment = old_instance.attachment_for(name)
+ @queued_for_rename += all_styles.map do |style|
+ old_path, new_path = old_attachment.path(style), path(style)
+ if old_path != new_path && path_exists?(old_path)
+ [old_path, new_path]
+ end
+ end.compact
+ end
+
def queue_existing_for_delete #:nodoc:
return unless file?
log("Queueing the existing files for #{name} for deletion.")
- @queued_for_delete += [:original, *@styles.keys].uniq.map do |style|
+ @queued_for_delete += all_styles.map do |style|
path(style) if exists?(style)
end.compact
instance_write(:file_name, nil)
@@ -386,6 +401,10 @@ def queue_existing_for_delete #:nodoc:
instance_write(:updated_at, nil)
end
+ def all_styles
+ [:original, *@styles.keys].uniq
+ end
+
def flush_errors #:nodoc:
@errors.each do |error, message|
instance.errors.add(name, message) if message
View
@@ -20,9 +20,9 @@ module Filesystem
def self.extended base
end
- def exists?(style = default_style)
+ def path_exists?(path)
if original_filename
- File.exist?(path(style))
+ File.exist?(path)
else
false
end
@@ -46,6 +46,15 @@ def flush_writes #:nodoc:
end
@queued_for_write = {}
end
+
+ def flush_renames
+ logger.info("[paperclip] Renaming files for #{name}")
+ @queued_for_rename.each do |old_path, new_path|
+ logger.info("[paperclip] #{old_path} -> #{new_path}")
+ File.rename(old_path, new_path)
+ end
+ @queued_for_rename = []
+ end
def flush_deletes #:nodoc:
logger.info("[paperclip] Deleting files for #{name}")
@@ -153,8 +162,8 @@ def parse_credentials creds
(creds[ENV['RAILS_ENV']] || creds).symbolize_keys
end
- def exists?(style = default_style)
- s3_bucket.key(path(style)) ? true : false
+ def path_exists?(path)
+ s3_bucket.key(path) ? true : false
end
def s3_protocol
@@ -182,6 +191,11 @@ def flush_writes #:nodoc:
end
@queued_for_write = {}
end
+
+ def flush_renames
+ logger.warn("[paperclip] Renaming files not implemented for S3")
+ @queued_for_rename = []
+ end
def flush_deletes #:nodoc:
logger.info("[paperclip] Writing files for #{name}")
View
@@ -369,6 +369,28 @@ def do_after_all; end
end
+ context "Attachment with changed file_name attribute on its instance" do
+ setup do
+ rebuild_model :path => ":basename.:extension"
+ @dummy = Dummy.new
+ @file = File.new(File.join(File.dirname(__FILE__),
+ "fixtures",
+ "5k.png"), 'rb')
+ @dummy.avatar = @file
+ @dummy.avatar_file_name = "renamed.png"
+ @dummy.stubs(:changes).returns({"avatar_file_name" => ["5k.png", "renamed.png"]})
+ @dummy.avatar.stubs(:path_exists?).with('5k.png').returns(true)
+ @dummy.avatar.stubs(:dirty?).returns(false)
+ end
+
+ should "rename the avatar" do
+ File.expects(:rename).with("5k.png", "renamed.png")
+ @dummy.rename_attached_files
+ end
+
+ teardown { @file.close }
+ end
+
context "An attachment" do
setup do
Paperclip::Attachment.default_options.merge!({

0 comments on commit d01bdc1

Please sign in to comment.