Skip to content

Commit

Permalink
Add support for models that have a string as primary key.
Browse files Browse the repository at this point in the history
Signed-off-by: rick <technoweenie@gmail.com>
  • Loading branch information
FooBarWidget authored and technoweenie committed Feb 13, 2009
1 parent 806fb74 commit 2c164b6
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 7 deletions.
1 change: 1 addition & 0 deletions README
Expand Up @@ -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:
Expand Down
35 changes: 30 additions & 5 deletions 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
Expand Down Expand Up @@ -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

Expand Down
65 changes: 64 additions & 1 deletion 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
Expand Down Expand Up @@ -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
32 changes: 32 additions & 0 deletions test/fixtures/attachment.rb
Expand Up @@ -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]
Expand Down
15 changes: 14 additions & 1 deletion test/schema.rb
Expand Up @@ -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
Expand Down Expand Up @@ -105,4 +118,4 @@
t.column :type, :string
t.column :aspect_ratio, :float
end
end
end

0 comments on commit 2c164b6

Please sign in to comment.