Commit 8911e40e by jyurek

New hotness. Holy crap.

git-svn-id: https://svn.thoughtbot.com/plugins/paperclip/trunk@229 7bbfaf0e-4d1d-0410-9690-a8bb5f8ef2aa
parent 72c4e738
......@@ -3,6 +3,7 @@ class <%= migration_name %> < ActiveRecord::Migration
<% attachments.each do |attachment| -%>
add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_size, :integer
<% end -%>
end
......@@ -10,6 +11,7 @@ class <%= migration_name %> < ActiveRecord::Migration
<% attachments.each do |attachment| -%>
remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_size
<% end -%>
end
end
module Thoughtbot
module Paperclip
module ClassMethods
def has_attached_file_with_filesystem *attachment_names
has_attached_file_without_filesystem *attachment_names
end
alias_method_chain :has_attached_file, :filesystem
end
class Storage #:nodoc:
class Filesystem < Storage #:nodoc:
def path_for attachment, style = nil
style ||= attachment[:default_style]
file = attachment[:instance]["#{attachment[:name]}_file_name"]
return nil unless file && attachment[:instance].id
prefix = interpolate attachment, "#{attachment[:path_prefix]}/#{attachment[:path]}", style
File.join( prefix.split("/") )
end
def url_for attachment, style = nil
style ||= attachment[:default_style]
file = attachment[:instance]["#{attachment[:name]}_file_name"]
return nil unless file && attachment[:instance].id
module Storage
# == Filesystem
# Typically, Paperclip stores your files in the filesystem, so that Apache (or whatever your
# main asset server is) can easily access your files without Rails having to be called all
# the time.
module Filesystem
interpolate attachment, "#{attachment[:url_prefix]}/#{attachment[:path]}", style
def file_name style = nil
style ||= @definition.default_style
pattern = if original_filename && instance.id
File.join(@definition.path_prefix, @definition.path)
else
@definition.missing_file_name
end
interpolate( style, pattern )
end
def ensure_directories_for attachment
attachment[:files].each do |style, file|
dirname = File.dirname(path_for(attachment, style))
FileUtils.mkdir_p dirname
def url style = nil
style ||= @definition.default_style
pattern = if original_filename && instance.id
[@definition.url_prefix, @definition.url || @definition.path].compact.join("/")
else
@definition.missing_url
end
interpolate( style, pattern )
end
def write_attachment attachment
return if attachment[:files].blank?
ensure_directories_for attachment
attachment[:files].each do |style, atch|
atch.rewind
data = atch.read
File.open( path_for(attachment, style), "w" ) do |file|
def write_attachment
return if @files.blank?
ensure_directories
@files.each do |style, data|
data.rewind
data = data.read
File.open( file_name(style), "w" ) do |file|
file.rewind
file.write(data)
end
end
attachment[:files] = nil
attachment[:dirty] = false
end
def delete_attachment attachment, complain = false
(attachment[:thumbnails].keys + [:original]).each do |style|
file_path = path_for(attachment, style)
def delete_attachment complain = false
@definition.styles.keys.each do |style|
file_path = file_name(style)
begin
FileUtils.rm file_path if file_path
rescue SystemCallError => e
raise PaperclipError.new(attachment), "Could not delete thumbnail." if ::Thoughtbot::Paperclip.options[:whiny_deletes] || complain
raise PaperclipError.new(self), "Could not delete thumbnail." if Thoughtbot::Paperclip.options[:whiny_deletes] || complain
end
end
end
def attachment_valid? attachment
attachment[:thumbnails].merge(:original => nil).all? do |style, geometry|
if attachment[:instance]["#{attachment[:name]}_file_name"]
if attachment[:dirty]
!attachment[:files][style].blank? && attachment[:errors].empty?
else
File.file?( path_for(attachment, style) )
end
def validate_existence *constraints
@definition.styles.keys.each do |style|
if @dirty
errors << "requires a valid #{style} file." unless @files && @files[style]
else
false
errors << "requires a valid #{style} file." unless File.exists?( file_name(style) )
end
end
end
def validate_size *constraints
errors << "file too large. Must be under #{constraints.last} bytes." if original_file_size > constraints.last
errors << "file too small. Must be over #{constraints.first} bytes." if original_file_size <= constraints.first
end
private
def ensure_directories
@files.each do |style, file|
dirname = File.dirname( file_name(style) )
FileUtils.mkdir_p dirname
end
end
end
end
end
......
module Thoughtbot
module Paperclip
module ClassMethods
module ClassMethods #:nodoc:
def has_attached_file_with_s3 *attachment_names
attachments, options = has_attached_file_without_s3 *attachment_names
has_attached_file_without_s3 *attachment_names
access_key = secret_key = ""
if file_name = s3_credentials_file
......@@ -16,7 +16,7 @@ module Thoughtbot
secret_key = Thoughtbot::Paperclip.options[:s3_secret_access_key]
end
if options[:storage].to_s.downcase == "s3"
if @definition.storage_module == Thoughtbot::Paperclip::Storage::S3
require 'aws/s3'
AWS::S3::Base.establish_connection!(
:access_key_id => access_key,
......@@ -35,28 +35,106 @@ module Thoughtbot
nil
end
end
class AttachmentDefinition
def s3_access
@options[:s3_access_privilege]
end
end
module Storage
# == Amazon S3 Storage
# Paperclip can store your files in Amazon's S3 Web Service. You can keep your keys in an s3.yml
# file inside the +config+ directory, similarly to your database.yml.
#
# access_key_id: 12345
# secret_access_key: 212434...4656456
#
# You can also environment-namespace the entries like you would in your database.yml:
#
# development:
# access_key_id: 12345
# secret_access_key: 212434...4656456
# production:
# access_key_id: abcde
# secret_access_key: gbkjhg...wgbrtjh
#
# The location of this file is configurable. You don't even have to use it if you don't want. Both
# the file's location or the keys themselves may be located in the Thoughtbot::Paperclip.options
# hash. The S3-related options in this hash are all prefixed with "s3".
#
# Thoughtbot::Paperclip.options = {
# :s3_persistent => true,
# :s3_credentials_file => "/home/me/.private/s3.yml"
# }
#
# This configuration is best placed in your +environment.rb+ or env-specific file.
# The complete list of options is as follows:
# * s3_access_key_id: The Amazon S3 ID you were given to access your account.
# * s3_secret_access_key: The secret key supplied by Amazon. This should be kept far away from prying
# eyes, which is why it's suggested that you keep these keys in a separate file that
# only you and the database can read.
# * s3_credentials_file: The path to the file where your credentials are kept in YAML format, as
# described above.
# * s3_persistent: Maintains an HTTP connection to the Amazon service if possible.
module S3
def file_name style = nil
style ||= @definition.default_style
pattern = if original_filename && instance.id
@definition.url
else
@definition.missing_url
end
interpolate( style, pattern )
end
def url style = nil
"http://s3.amazonaws.com/#{bucket}/#{file_name(style)}"
end
class Storage #:nodoc:
class S3 < Storage #:nodoc:
def path_for attachment, style = nil
style ||= attachment[:default_style]
file = attachment[:instance]["#{attachment[:name]}_file_name"]
return nil unless file && attachment[:instance].id
def write_attachment
return if @files.blank?
bucket = ensure_bucket
@files.each do |style, data|
data.rewind
AWS::S3::S3Object.store( file_name(style), data, bucket, :access => @definition.s3_access || :public_read )
end
end
interpolate attachment, attachment[:path], style
def delete_attachment complain = false
(attachment[:thumbnails].keys + [:original]).each do |style|
begin
AWS::S3::S3Object.delete( file_name(style), bucket )
rescue AWS::S3::ResponseError => error
raise
end
end
end
def validate_existence *constraints
@definition.styles.keys.each do |style|
if @dirty
errors << "requires a valid #{style} file." unless @files && @files[style]
else
errors << "requires a valid #{style} file." unless AWS::S3::S3Object.exists?( file_name(style), bucket )
end
end
end
def url_for attachment, style = nil
"http://s3.amazonaws.com/#{bucket_for(attachment)}/#{path_for(attachment, style)}"
def validate_size *constraints
errors << "file too large. Must be under #{constraints.last} bytes." if original_file_size > constraints.last
errors << "file too small. Must be over #{constraints.first} bytes." if original_file_size <= constraints.first
end
def bucket_for attachment
bucket_name = interpolate attachment, attachment[:url_prefix], nil
private
def bucket
interpolate(nil, @definition.url_prefix)
end
def ensure_bucket_for attachment, style = nil
def ensure_bucket
begin
bucket_name = bucket_for attachment
bucket_name = bucket
AWS::S3::Bucket.create(bucket_name)
bucket_name
rescue AWS::S3::S3Exception => e
......@@ -64,37 +142,6 @@ module Thoughtbot
end
end
def write_attachment attachment
return if attachment[:files].blank?
bucket = ensure_bucket_for attachment
attachment[:files].each do |style, atch|
atch.rewind
AWS::S3::S3Object.store( path_for(attachment, style), atch, bucket, :access => attachment[:access] || :public_read )
end
attachment[:files] = nil
attachment[:dirty] = false
end
def delete_attachment attachment, complain = false
(attachment[:thumbnails].keys + [:original]).each do |style|
file_path = path_for(attachment, style)
AWS::S3::S3Object.delete( file_path, bucket_for(attachment) )
end
end
def attachment_valid? attachment
attachment[:thumbnails].merge(:original => nil).all? do |style, geometry|
if attachment[:instance]["#{attachment[:name]}_file_name"]
if attachment[:dirty]
!attachment[:files][style].blank? && attachment[:errors].empty?
else
AWS::S3::S3Object.exists?( path_for(attachment, style), bucket_for(attachment) )
end
else
false
end
end
end
end
end
end
......
......@@ -2,22 +2,28 @@ begin
ActiveRecord::Base.connection.create_table :foos, :force => true do |table|
table.column :image_file_name, :string
table.column :image_content_type, :string
table.column :image_size, :integer
end
ActiveRecord::Base.connection.create_table :bars, :force => true do |table|
table.column :document_file_name, :string
table.column :document_content_type, :string
table.column :document_size, :integer
end
ActiveRecord::Base.connection.create_table :non_standards, :force => true do |table|
table.column :resume_file_name, :string
table.column :resume_content_type, :string
table.column :resume_size, :integer
table.column :avatar_file_name, :string
table.column :avatar_content_type, :string
table.column :avatar_size, :integer
end
ActiveRecord::Base.connection.create_table :ess_threes, :force => true do |table|
table.column :resume_file_name, :string
table.column :resume_content_type, :string
table.column :resume_size, :integer
table.column :avatar_file_name, :string
table.column :avatar_content_type, :string
table.column :avatar_size, :integer
end
ActiveRecord::Base.connection.create_table :negatives, :force => true do |table|
table.column :this_is_the_wrong_name_file_name, :string
......
......@@ -22,7 +22,7 @@ class PaperclipImagesTest < Test::Unit::TestCase
end
def test_should_save_the_file_and_its_thumbnails
assert @foo.save
assert @foo.save, @foo.errors.full_messages.inspect
assert File.exists?( @foo.image_file_name(:original) ), @foo.image_file_name(:original)
assert File.exists?( @foo.image_file_name(:medium) ), @foo.image_file_name(:medium)
assert File.exists?( @foo.image_file_name(:thumb) ), @foo.image_file_name(:thumb)
......@@ -43,7 +43,7 @@ class PaperclipImagesTest < Test::Unit::TestCase
def test_should_ensure_that_file_are_accessible_after_reload
assert @foo.save
assert @foo.image_valid?
assert @foo.valid?
assert @foo.valid?, @foo.errors.full_messages.inspect
@foo2 = Foo.find @foo.id
assert @foo.image_valid?
......@@ -72,7 +72,7 @@ class PaperclipImagesTest < Test::Unit::TestCase
end
assert @foo.destroy_image
assert @foo.save
assert @foo.save, @foo.errors.full_messages.inspect
mappings.each do |style, file, url|
assert_not_equal file, @foo.image_file_name(style)
assert_equal "", @foo.image_file_name(style)
......@@ -128,8 +128,8 @@ class PaperclipImagesTest < Test::Unit::TestCase
Thoughtbot::Paperclip.options[:image_magick_path] = "/does/not/exist"
assert_nothing_raised{ @foo.image = @file }
assert !@foo.save
assert !@foo.valid?
assert !@foo.save, @foo.errors.full_messages.inspect
assert @foo.errors.length > 0
assert @foo.errors.on(:image)
[@foo.errors.on(:image)].flatten.each do |err|
......
......@@ -15,8 +15,8 @@ class PaperclipTest < Test::Unit::TestCase
end
def test_should_validate_before_save
assert @bar.document_valid?
assert @bar.valid?
assert @bar.document_valid?, @bar.errors.full_messages.inspect
assert @bar.valid?, @bar.errors.full_messages.inspect
end
def test_should_save_the_created_file_to_the_final_asset_directory
......@@ -25,9 +25,9 @@ class PaperclipTest < Test::Unit::TestCase
end
def test_should_validate
assert @bar.save
assert @bar.document_valid?
assert @bar.valid?
assert @bar.save, @bar.errors.full_messages.inspect
assert @bar.document_valid?, @bar.errors.full_messages.inspect
assert @bar.valid?, @bar.errors.full_messages.inspect
end
def test_should_default_to_original_for_path_and_url
......@@ -47,7 +47,7 @@ class PaperclipTest < Test::Unit::TestCase
def test_should_put_on_errors_if_no_file_exists
assert @bar.save
@bar.document = nil
assert !@bar.document_valid?
assert !@bar.document_valid?, @bar.errors.full_messages.inspect
assert !@bar.save
assert @bar.errors.length > 0
assert @bar.errors.on(:document)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment