Commit f79dadce by Jon Yurek

Merge branch 'master' of git://github.com/mvanholstyn/paperclip into mvanholstyn/master

parents 22b4c0c2 1fcf1d7e
......@@ -131,7 +131,7 @@ module Paperclip
end
define_method "#{name}?" do
! attachment_for(name).file.nil?
! attachment_for(name).original_filename.blank?
end
validates_each(name) do |record, attr, value|
......@@ -161,7 +161,7 @@ module Paperclip
# Places ActiveRecord-style validations on the presence of a file.
def validates_attachment_presence name
attachment_definitions[name][:validations] << lambda do |attachment, instance|
if attachment.file.nil? || !File.exist?(attachment.file.path)
if attachment.original_filename.blank?
"must be set."
end
end
......
......@@ -15,7 +15,7 @@ module Paperclip
}
end
attr_reader :name, :instance, :file, :styles, :default_style
attr_reader :name, :instance, :styles, :default_style
# Creates an Attachment object. +name+ is the name of the attachment, +instance+ is the
# ActiveRecord object instance it's attached to, and +options+ is the same as the hash
......@@ -35,19 +35,13 @@ module Paperclip
@storage = options[:storage]
@options = options
@queued_for_delete = []
@processed_files = {}
@queued_for_write = {}
@errors = []
@file = nil
@validation_errors = nil
@dirty = false
normalize_style_definition
initialize_storage
if original_filename
@processed_files = locate_files
@file = @processed_files[@default_style]
end
end
# What gets called when you call instance.attachment = File. It clears errors,
......@@ -58,11 +52,11 @@ module Paperclip
queue_existing_for_delete
@errors = []
@validation_errors = nil
@validation_errors = nil
return nil if uploaded_file.nil?
@file = uploaded_file.to_tempfile
@queued_for_write[:original] = uploaded_file.to_tempfile
@instance[:"#{@name}_file_name"] = uploaded_file.original_filename
@instance[:"#{@name}_content_type"] = uploaded_file.content_type
@instance[:"#{@name}_file_size"] = uploaded_file.size
......@@ -79,8 +73,8 @@ module Paperclip
# and can point to an action in your app, if you need fine grained security.
# This is not recommended if you don't need the security, however, for
# performance reasons.
def url style = nil
@file ? interpolate(@url, style) : interpolate(@default_url, style)
def url style = default_style
exists?(style) ? interpolate(@url, style) : interpolate(@default_url, style)
end
# Returns the path of the attachment as defined by the :path optionn. If the
......@@ -118,7 +112,6 @@ module Paperclip
flush_deletes
flush_writes
@dirty = false
@file = @processed_files[default_style]
true
else
flush_errors
......@@ -126,14 +119,6 @@ module Paperclip
end
end
# Returns representation of the data of the file assigned to the given
# style, in the format most representative of the current storage.
def to_file style = nil
@processed_files[style || default_style]
end
alias_method :to_io, :to_file
# Returns the name of the file as originally assigned, and as lives in the
# <attachment>_file_name attribute of the model.
def original_filename
......@@ -149,7 +134,7 @@ module Paperclip
@interpolations ||= {
:rails_root => lambda{|attachment,style| RAILS_ROOT },
:class => lambda do |attachment,style|
attachment.instance.class.to_s.downcase.pluralize
attachment.instance.class.name.underscore.pluralize
end,
:basename => lambda do |attachment,style|
attachment.original_filename.gsub(/\.(.*?)$/, "")
......@@ -196,11 +181,11 @@ module Paperclip
end
def post_process #:nodoc:
return nil if @file.nil?
return if @queued_for_write[:original].nil?
@styles.each do |name, args|
begin
dimensions, format = args
@processed_files[name] = Thumbnail.make(self.file,
@queued_for_write[name] = Thumbnail.make(@queued_for_write[:original],
dimensions,
format,
@whiny_thumnails)
......@@ -210,11 +195,9 @@ module Paperclip
@errors << e.message
end
end
@processed_files[:original] = @file
end
def interpolate pattern, style = nil #:nodoc:
style ||= default_style
def interpolate pattern, style = default_style #:nodoc:
pattern = pattern.dup
self.class.interpolations.each do |tag, l|
pattern.gsub!(/:\b#{tag}\b/) do |match|
......@@ -225,9 +208,10 @@ module Paperclip
end
def queue_existing_for_delete #:nodoc:
@queued_for_delete += @processed_files.values
@file = nil
@processed_files = {}
return if original_filename.blank?
@queued_for_delete += [:original, *@styles.keys].uniq.map do |style|
path(style) if exists?(style)
end.compact
@instance[:"#{@name}_file_name"] = nil
@instance[:"#{@name}_content_type"] = nil
@instance[:"#{@name}_file_size"] = nil
......
......@@ -25,7 +25,7 @@ module IOStream
while self.read(in_blocks_of, buffer) do
dstio.write(buffer)
end
dstio.rewind
dstio.rewind
dstio
end
end
......
module Paperclip
module Storage
# With the Filesystem module installed, @file and @processed_files are just File instances.
module Filesystem
def self.extended base
end
def locate_files
[:original, *@styles.keys].uniq.inject({}) do |files, style|
files[style] = File.new(path(style), "rb") if File.exist?(path(style))
files
def exists?(style = default_style)
if original_filename
File.exist?(path(style))
else
false
end
end
# Returns representation of the data of the file assigned to the given
# style, in the format most representative of the current storage.
def to_file style = default_style
File.new(path(style)) if exists?(style)
end
alias_method :to_io, :to_file
def flush_writes #:nodoc:
@processed_files.each do |style, file|
FileUtils.mkdir_p( File.dirname(path(style)) )
@processed_files[style] = file.stream_to(path(style)) unless file.path == path(style)
@queued_for_write.each do |style, file|
FileUtils.mkdir_p(File.dirname(path(style)))
result = file.stream_to(path(style))
file.close
result.close
end
@queued_for_write = {}
end
def flush_deletes #:nodoc:
@queued_for_delete.compact.each do |file|
@queued_for_delete.each do |path|
begin
FileUtils.rm(file.path)
FileUtils.rm(path) if File.exist?(path)
rescue Errno::ENOENT => e
# ignore them
end
......@@ -32,8 +42,6 @@ module Paperclip
end
end
# With the S3 module included, @file and the @processed_files will be
# RightAws::S3::Key instances.
module S3
def self.extended base
require 'right_aws'
......@@ -58,35 +66,37 @@ module Paperclip
creds = find_credentials(creds).stringify_keys
(creds[ENV['RAILS_ENV']] || creds).symbolize_keys
end
def exists?(style = default_style)
@s3_bucket.key(path(style)) ? true : false
end
def locate_files
[:original, *@styles.keys].uniq.inject({}) do |files, style|
files[style] = @s3_bucket.key(path(style))
files
end
# Returns representation of the data of the file assigned to the given
# style, in the format most representative of the current storage.
def to_file style = default_style
@s3_bucket.key(path(style))
end
alias_method :to_io, :to_file
def flush_writes #:nodoc:
return if not dirty?
@processed_files.each do |style, key|
@queued_for_write.each do |style, file|
begin
unless key.is_a? RightAws::S3::Key
saved_data = key
key = @processed_files[style] = @s3_bucket.key(path(style))
key.data = saved_data
end
key = @s3_bucket.key(path(style))
key.data = file
key.put(nil, @s3_permissions)
rescue RightAws::AwsError => e
@processed_files[style] = nil
raise
end
end
@queued_for_write = {}
end
def flush_deletes #:nodoc:
@queued_for_delete.compact.each do |file|
@queued_for_delete.each do |path|
begin
file.delete
if file = @s3_bucket.key(path)
file.delete
end
rescue RightAws::AwsError
# Ignore this.
end
......
......@@ -28,6 +28,16 @@ class AttachmentTest < Test::Unit::TestCase
end
end
context "without an Attachment" do
setup do
@dummy = Dummy.new
end
should "return false when asked exists?" do
assert !@dummy.avatar.exists?
end
end
context "on an Attachment" do
setup do
@dummy = Dummy.new
......@@ -96,7 +106,7 @@ class AttachmentTest < Test::Unit::TestCase
end
should "return its default_url when no file assigned" do
assert @attachment.file.nil?
assert @attachment.to_file.nil?
assert_equal "/tests/original/missing.png", @attachment.url
assert_equal "/tests/blah/missing.png", @attachment.url(:blah)
end
......@@ -123,29 +133,28 @@ class AttachmentTest < Test::Unit::TestCase
@attachment.assign(@file)
end
should "return the real url" do
assert @attachment.file
assert_equal "/tests/41/original/5k.png", @attachment.url
assert_equal "/tests/41/blah/5k.png", @attachment.url(:blah)
end
should "be dirty" do
assert @attachment.dirty?
end
should "have its image and attachments as tempfiles" do
[nil, :large, :medium, :small].each do |style|
assert File.exists?(@attachment.to_io(style))
end
end
context "and saved" do
setup do
@attachment.save
end
should "return the real url" do
assert @attachment.to_file
assert_equal "/tests/41/original/5k.png", @attachment.url
assert_equal "/tests/41/small/5k.jpg", @attachment.url(:small)
end
should "return its default_url when no file assigned" do
assert @attachment.to_file
assert_equal "/tests/blah/missing.png", @attachment.url(:blah)
end
should "commit the files to disk" do
[nil, :large, :medium, :small].each do |style|
[:large, :medium, :small].each do |style|
io = @attachment.to_io(style)
assert File.exists?(io)
assert ! io.is_a?(::Tempfile)
......@@ -166,12 +175,25 @@ class AttachmentTest < Test::Unit::TestCase
end
end
should "have #file be equal #to_io(:original)" do
assert_equal @attachment.file, @attachment.to_io(:original)
should "still have its #file attribute not be nil" do
assert ! @attachment.to_file.nil?
end
should "still have its #file attribute not be nil" do
assert ! @attachment.file.nil?
context "and deleted" do
setup do
@existing_names = @attachment.styles.keys.collect do |style|
@attachment.path(style)
end
@instance.expects(:[]=).with(:test_file_name, nil)
@instance.expects(:[]=).with(:test_content_type, nil)
@instance.expects(:[]=).with(:test_file_size, nil)
@attachment.assign nil
@attachment.save
end
should "delete the files" do
@existing_names.each{|f| assert ! File.exists?(f) }
end
end
context "and deleted" do
......
require 'test/helper.rb'
class IntegrationTest < Test::Unit::TestCase
context "Many models at once" do
setup do
rebuild_model
@file = File.new(File.join(FIXTURES_DIR, "5k.png"))
300.times do |i|
Dummy.create! :avatar => @file
end
end
should "not exceed the open file limit" do
assert_nothing_raised do
dummies = Dummy.find(:all)
dummies.each { |dummy| dummy.avatar }
end
end
end
context "A model with a filesystem attachment" do
setup do
rebuild_model :styles => { :large => "300x300>",
......@@ -19,8 +36,7 @@ class IntegrationTest < Test::Unit::TestCase
end
should "write and delete its files" do
[["100x15", nil],
["434x66", :original],
[["434x66", :original],
["300x46", :large],
["100x15", :medium],
["32x32", :thumb]].each do |geo, style|
......@@ -78,10 +94,10 @@ class IntegrationTest < Test::Unit::TestCase
end
should "know the difference between good files, bad files, not files, and nil" do
expected = @dummy.avatar.file
expected = @dummy.avatar.to_file
@dummy.avatar = "not a file"
assert @dummy.valid?
assert_equal expected.path, @dummy.avatar.file.path
assert_equal expected.path, @dummy.avatar.to_file.path
@dummy.avatar = @bad_file
assert ! @dummy.valid?
......@@ -204,10 +220,10 @@ class IntegrationTest < Test::Unit::TestCase
end
should "know the difference between good files, bad files, not files, and nil" do
expected = @dummy.avatar.file
expected = @dummy.avatar.to_file
@dummy.avatar = "not a file"
assert @dummy.valid?
assert_equal expected, @dummy.avatar.file
assert_equal expected, @dummy.avatar.to_file
@dummy.avatar = @bad_file
assert ! @dummy.valid?
......
......@@ -78,10 +78,6 @@ class S3Test < Test::Unit::TestCase
@dummy.avatar = @file
end
should "still return a Tempfile when sent #to_io" do
assert_equal Tempfile, @dummy.avatar.to_io.class
end
context "and saved" do
setup do
@key_mock = stub
......
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