Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add support for models that have a string as primary key.

Signed-off-by: rick <technoweenie@gmail.com>
  • Loading branch information...
commit 2c164b6854b606665934422f5c27e19546eb03cf 1 parent 806fb74
@FooBarWidget FooBarWidget authored technoweenie committed
View
1  README
@@ -49,6 +49,7 @@ has_attachment(options = {})
# Defaults to :db_file. Options are :file_system, :db_file, and :s3.
:processor # Sets the image processor to use for resizing of the attached image.
# Options include ImageScience, Rmagick, and MiniMagick. Default is whatever is installed.
+ :uuid_primary_key # If your model's primary key is a 128-bit UUID in hexadecimal format, then set this to true.
Examples:
View
35 lib/technoweenie/attachment_fu/backends/file_system_backend.rb
@@ -1,4 +1,6 @@
require 'ftools'
+require 'digest/sha2'
+
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
module Backends
@@ -28,16 +30,39 @@ def base_path
# The attachment ID used in the full path of a file
def attachment_path_id
- ((respond_to?(:parent_id) && parent_id) || id).to_i
+ ((respond_to?(:parent_id) && parent_id) || id) || 0
end
- # by default paritions files into directories e.g. 0000/0001/image.jpg
- # to turn this off set :partition => false
+ # Partitions the given path into an array of path components.
+ #
+ # For example, given an <tt>*args</tt> of ["foo", "bar"], it will return
+ # <tt>["0000", "0001", "foo", "bar"]</tt> (assuming that that id returns 1).
+ #
+ # If the id is not an integer, then path partitioning will be performed by
+ # hashing the string value of the id with SHA-512, and splitting the result
+ # into 4 components. If the id a 128-bit UUID (as set by :uuid_primary_key => true)
+ # then it will be split into 2 components.
+ #
+ # To turn this off entirely, set :partition => false.
def partitioned_path(*args)
if respond_to?(:attachment_options) && attachment_options[:partition] == false
args
- else
- ("%08d" % attachment_path_id).scan(/..../) + args
+ elsif attachment_options[:uuid_primary_key]
+ # Primary key is a 128-bit UUID in hex format. Split it into 2 components.
+ path_id = attachment_path_id.to_s
+ component1 = path_id[0..15] || "-"
+ component2 = path_id[16..-1] || "-"
+ [component1, component2] + args
+ else
+ path_id = attachment_path_id
+ if path_id.is_a?(Integer)
+ # Primary key is an integer. Split it after padding it with 0.
+ ("%08d" % path_id).scan(/..../) + args
+ else
+ # Primary key is a String. Hash it, then split it into 4 components.
+ hash = Digest::SHA512.hexdigest(path_id.to_s)
+ [hash[0..31], hash[32..63], hash[64..95], hash[96..127]] + args
+ end
end
end
View
65 test/backends/file_system_test.rb
@@ -1,4 +1,5 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper'))
+require 'digest/sha2'
class FileSystemTest < Test::Unit::TestCase
include BaseAttachmentTests
@@ -77,4 +78,66 @@ def test_should_delete_old_file_when_renaming(klass = FileAttachment)
end
test_against_subclass :test_should_delete_old_file_when_renaming, FileAttachment
-end
+
+ def test_path_partitioning_works_on_integer_id(klass = FileAttachment)
+ attachment_model klass
+
+ # Create a random attachment object, doesn't matter what.
+ attachment = upload_file :filename => '/files/rails.png'
+ old_id = attachment.id
+ attachment.id = 1
+
+ begin
+ assert_equal ["0000", "0001", "bar.txt"], attachment.send(:partitioned_path, "bar.txt")
+ ensure
+ attachment.id = old_id
+ end
+ end
+
+ test_against_subclass :test_path_partitioning_works_on_integer_id, FileAttachment
+
+ def test_path_partitioning_with_string_id_works_by_generating_hash(klass = FileAttachmentWithStringId)
+ attachment_model klass
+
+ # Create a random attachment object, doesn't matter what.
+ attachment = upload_file :filename => '/files/rails.png'
+ old_id = attachment.id
+ attachment.id = "hello world some long string"
+ hash = Digest::SHA512.hexdigest("hello world some long string")
+
+ begin
+ assert_equal [
+ hash[0..31],
+ hash[32..63],
+ hash[64..95],
+ hash[96..127],
+ "bar.txt"
+ ], attachment.send(:partitioned_path, "bar.txt")
+ ensure
+ attachment.id = old_id
+ end
+ end
+
+ test_against_subclass :test_path_partitioning_with_string_id_works_by_generating_hash, FileAttachmentWithStringId
+
+ def test_path_partition_string_id_hashing_is_turned_off_if_id_is_uuid(klass = FileAttachmentWithUuid)
+ attachment_model klass
+
+ # Create a random attachment object, doesn't matter what.
+ attachment = upload_file :filename => '/files/rails.png'
+ old_id = attachment.id
+ attachment.id = "0c0743b698483569dc65909a8cdb3bf9"
+
+ begin
+ assert_equal [
+ "0c0743b698483569",
+ "dc65909a8cdb3bf9",
+ "bar.txt"
+ ], attachment.send(:partitioned_path, "bar.txt")
+ ensure
+ attachment.id = old_id
+ end
+ end
+
+ test_against_subclass :test_path_partition_string_id_hashing_is_turned_off_if_id_is_uuid, FileAttachmentWithUuid
+end
View
32 test/fixtures/attachment.rb
@@ -44,6 +44,38 @@ class FileAttachment < ActiveRecord::Base
validates_as_attachment
end
+class FileAttachmentWithStringId < ActiveRecord::Base
+ set_table_name 'file_attachments_with_string_id'
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files', :processor => :rmagick
+ validates_as_attachment
+
+ before_validation :auto_generate_id
+ before_save :auto_generate_id
+ @@last_id = 0
+
+ private
+ def auto_generate_id
+ @@last_id += 1
+ self.id = "id_#{@@last_id}"
+ end
+end
+
+class FileAttachmentWithUuid < ActiveRecord::Base
+ set_table_name 'file_attachments_with_string_id'
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files', :processor => :rmagick, :uuid_primary_key => true
+ validates_as_attachment
+
+ before_validation :auto_generate_id
+ before_save :auto_generate_id
+ @@last_id = 0
+
+ private
+ def auto_generate_id
+ @@last_id += 1
+ self.id = "%0127dx" % @@last_id
+ end
+end
+
class ImageFileAttachment < FileAttachment
has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
:content_type => :image, :resize_to => [50,50]
View
15 test/schema.rb
@@ -22,6 +22,19 @@
t.column :type, :string
t.column :aspect_ratio, :float
end
+
+ create_table :file_attachments_with_string_id, :id => false, :force => true do |t|
+ t.column :id, :string
+ t.column :parent_id, :string
+ t.column :thumbnail, :string
+ t.column :filename, :string, :limit => 255
+ t.column :content_type, :string, :limit => 255
+ t.column :size, :integer
+ t.column :width, :integer
+ t.column :height, :integer
+ t.column :type, :string
+ t.column :aspect_ratio, :float
+ end
create_table :gd2_attachments, :force => true do |t|
t.column :parent_id, :integer
@@ -105,4 +118,4 @@
t.column :type, :string
t.column :aspect_ratio, :float
end
-end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.