Commit 89c8d117 by Jon Yurek

Adds IO adapters to abstract the things that can be assigned.

Needs work for S3 Attachments.
parent 63886520
File added
......@@ -19,13 +19,10 @@ GEM
activesupport (= 3.2.2)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activerecord-jdbc-adapter (1.2.2)
activerecord-jdbcsqlite3-adapter (1.2.2)
activerecord-jdbc-adapter (~> 1.2.2)
jdbc-sqlite3 (~> 3.7.2)
activesupport (3.2.2)
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.2.7)
appraisal (0.4.1)
bundler
rake
......@@ -40,7 +37,8 @@ GEM
json (~> 1.4)
nokogiri (<= 1.5.0)
uuidtools (~> 2.1)
bouncy-castle-java (1.5.0146.1)
bourne (1.1.2)
mocha (= 0.10.5)
builder (3.0.0)
capybara (1.1.2)
mime-types (>= 1.16)
......@@ -52,6 +50,7 @@ GEM
childprocess (0.3.1)
ffi (~> 1.0.6)
cocaine (0.2.1)
coderay (1.0.5)
cucumber (1.1.9)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
......@@ -62,7 +61,6 @@ GEM
excon (0.6.6)
fakeweb (1.3.0)
ffi (1.0.11)
ffi (1.0.11-java)
fog (0.9.0)
builder
excon (~> 0.6.1)
......@@ -74,31 +72,30 @@ GEM
nokogiri (>= 1.4.4)
ruby-hmac
formatador (0.2.1)
gherkin (2.9.1)
json (>= 1.4.6)
gherkin (2.9.1-java)
gherkin (2.9.3)
json (>= 1.4.6)
httparty (0.8.1)
multi_json
multi_xml
i18n (0.6.0)
jdbc-sqlite3 (3.7.2)
jruby-openssl (0.7.6.1)
bouncy-castle-java (>= 1.5.0146.1)
json (1.6.5)
json (1.6.5-java)
json (1.6.6)
launchy (2.1.0)
addressable (~> 2.2.6)
metaclass (0.0.1)
method_source (0.7.1)
mime-types (1.18)
mocha (0.10.5)
metaclass (~> 0.0.1)
multi_json (1.1.0)
multi_json (1.2.0)
multi_xml (0.4.2)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.3.0)
nokogiri (1.4.7)
nokogiri (1.4.7-java)
weakling (>= 0.0.3)
pry (0.9.8.4)
coderay (~> 1.0.5)
method_source (~> 0.7.1)
slop (>= 2.4.4, < 3)
rack (1.4.1)
rack-test (0.6.1)
rack (>= 1.0)
......@@ -123,16 +120,15 @@ GEM
shoulda-matchers (~> 1.0.0)
shoulda-context (1.0.0)
shoulda-matchers (1.0.0)
slop (2.4.4)
sqlite3 (1.3.5)
term-ansicolor (1.0.7)
tzinfo (0.3.32)
uuidtools (2.1.2)
weakling (0.0.4-java)
xpath (0.1.4)
nokogiri (~> 1.3)
PLATFORMS
java
ruby
DEPENDENCIES
......@@ -140,6 +136,7 @@ DEPENDENCIES
appraisal (~> 0.4.0)
aruba
aws-sdk (~> 1.3.8)
bourne
bundler
capybara
cocaine (~> 0.2)
......@@ -147,8 +144,11 @@ DEPENDENCIES
fakeweb
fog
jruby-openssl
launchy
mocha
nokogiri (~> 1.4.7)
paperclip!
pry
rake
shoulda
sqlite3
When /^I print "([^\"]*)"$/ do |whatever|
puts whatever
end
Given /^I generate a new rails application$/ do
steps %{
When I run `bundle exec #{new_application_command} #{APP_NAME}`
......
require 'aruba/cucumber'
require 'capybara/cucumber'
require 'test/unit/assertions'
require 'pry'
$CUCUMBER=1
World(Test::Unit::Assertions)
Before do
......
namespace :images do
desc "Regenerate images"
task :regenerate => :environment do
require 'open-uri'
OpportunityPhoto.all.each do |photo|
begin
old_name = photo.image_file_name
new_image = open(photo.image.url(:original, escape: false))
class << new_image
def original_filename; @original_filename; end
def original_filename=(name); @original_filename = name; end
end
new_image.original_filename = old_name
photo.image = new_image
photo.save
rescue => e
puts "ERROR: #{e.message} while processing #{photo.id}"
end
end
end
end
File added
......@@ -29,8 +29,6 @@ require 'erb'
require 'digest'
require 'tempfile'
require 'paperclip/version'
require 'paperclip/upfile'
require 'paperclip/iostream'
require 'paperclip/geometry'
require 'paperclip/processor'
require 'paperclip/tempfile'
......@@ -49,6 +47,7 @@ require 'paperclip/instance_methods'
require 'paperclip/logger'
require 'paperclip/helpers'
require 'paperclip/railtie'
require 'mime/types'
require 'logger'
require 'cocaine'
......@@ -203,3 +202,12 @@ module Paperclip
end
end
end
# This stuff needs to be run after Paperclip is defined.
require 'paperclip/io_adapters/registry'
require 'paperclip/io_adapters/identity_adapter'
require 'paperclip/io_adapters/file_adapter'
require 'paperclip/io_adapters/stringio_adapter'
require 'paperclip/io_adapters/nil_adapter'
require 'paperclip/io_adapters/attachment_adapter'
require 'paperclip/io_adapters/uploaded_file_adapter'
......@@ -7,8 +7,6 @@ module Paperclip
# when the model saves, deletes when the model is destroyed, and processes
# the file upon assignment.
class Attachment
include IOStream
def self.default_options
@default_options ||= {
:convert_options => {},
......@@ -90,29 +88,16 @@ module Paperclip
# new_user.avatar = old_user.avatar
def assign uploaded_file
ensure_required_accessors!
file = Paperclip.io_adapters.for(uploaded_file)
if uploaded_file.is_a?(Paperclip::Attachment)
uploaded_filename = uploaded_file.original_filename
uploaded_file = uploaded_file.to_file(:original)
close_uploaded_file = uploaded_file.respond_to?(:close)
else
instance_write(:uploaded_file, uploaded_file) if uploaded_file
end
return nil unless valid_assignment?(uploaded_file)
uploaded_file.binmode if uploaded_file.respond_to? :binmode
self.clear
return nil if file.nil?
return nil if uploaded_file.nil?
uploaded_filename ||= uploaded_file.original_filename
stores_fingerprint = @instance.respond_to?("#{name}_fingerprint".to_sym)
@queued_for_write[:original] = to_tempfile(uploaded_file)
instance_write(:file_name, cleanup_filename(uploaded_filename.strip))
instance_write(:content_type, uploaded_file.content_type.to_s.strip)
instance_write(:file_size, uploaded_file.size.to_i)
instance_write(:fingerprint, generate_fingerprint(uploaded_file)) if stores_fingerprint
@queued_for_write[:original] = file
instance_write(:file_name, cleanup_filename(file.original_filename))
instance_write(:content_type, file.content_type)
instance_write(:file_size, file.size)
instance_write(:fingerprint, file.fingerprint) if instance_respond_to?(:fingerprint)
instance_write(:updated_at, Time.now)
@dirty = true
......@@ -120,10 +105,8 @@ module Paperclip
post_process(*@options[:only_process]) if post_processing
# Reset the file size if the original file was reprocessed.
instance_write(:file_size, @queued_for_write[:original].size.to_i)
instance_write(:fingerprint, generate_fingerprint(@queued_for_write[:original])) if stores_fingerprint
ensure
uploaded_file.close if close_uploaded_file
instance_write(:file_size, @queued_for_write[:original].size)
instance_write(:fingerprint, @queued_for_write[:original].fingerprint) if instance_respond_to?(:fingerprint)
end
# Returns the public URL of the attachment with a given style. This does
......@@ -252,16 +235,10 @@ module Paperclip
instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
end
# Returns the hash of the file as originally assigned, and lives in the
# <attachment>_fingerprint attribute of the model.
# Returns the fingerprint of the file, if one's defined. The fingerprint is
# stored in the <attachment>_fingerpring attribute of the model.
def fingerprint
if instance_read(:fingerprint)
instance_read(:fingerprint)
elsif @instance.respond_to?("#{name}_fingerprint".to_sym)
@queued_for_write[:original] && generate_fingerprint(@queued_for_write[:original])
else
nil
end
instance_read(:fingerprint)
end
# Returns the content_type of the file as originally assigned, and lives
......@@ -307,26 +284,16 @@ module Paperclip
# thumbnails forcefully, by reobtaining the original file and going through
# the post-process again.
def reprocess!(*style_args)
new_original = Tempfile.new("paperclip-reprocess")
new_original.binmode
if old_original = to_file(:original)
new_original.write( old_original.respond_to?(:get) ? old_original.get : old_original.read )
new_original.rewind
@queued_for_write = { :original => new_original }
instance_write(:updated_at, Time.now)
post_process(*style_args)
old_original.close if old_original.respond_to?(:close)
old_original.unlink if old_original.respond_to?(:unlink)
saved_only_process, @options[:only_process] = @options[:only_process], style_args
begin
assign(self)
save
else
true
rescue Errno::EACCES => e
warn "#{e} - skipping file."
false
ensure
@options[:only_process] = saved_only_process
end
rescue Errno::EACCES => e
warn "#{e} - skipping file"
false
end
# Returns true if a file has been assigned.
......@@ -336,6 +303,12 @@ module Paperclip
alias :present? :file?
# Determines whether the instance responds to this attribute. Used to prevent
# calculations on fields we won't even store.
def instance_respond_to?(attr)
instance.respond_to?(:"#{name}_#{attr}")
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
# "avatar_file_name" field (assuming the attachment is called avatar).
......@@ -428,6 +401,7 @@ module Paperclip
@queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
Paperclip.processor(processor).make(file, style.processor_options, self)
end
@queued_for_write[name] = Paperclip.io_adapters.for(@queued_for_write[name])
rescue Paperclip::Error => e
log("An error was received while processing: #{e.inspect}")
(@errors[:processing] ||= []) << e.message if @options[:whiny]
......@@ -463,8 +437,8 @@ module Paperclip
# called by storage after the writes are flushed and before @queued_for_writes is cleared
def after_flush_writes
@queued_for_write.each do |style, file|
file.close unless file.closed?
file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
# file.close unless file.closed?
# file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
end
end
......
module Paperclip
class AttachmentAdapter
def initialize(target)
@target = target
cache_current_values
end
def original_filename
@original_filename
end
def content_type
@content_type
end
def size
@size
end
def nil?
false
end
def fingerprint
@fingerprint ||= Digest::MD5.file(path).to_s
end
def read(length = nil, buffer = nil)
@tempfile.read(length, buffer)
end
def eof?
@tempfile.eof?
end
def path
@tempfile.path
end
private
def cache_current_values
@tempfile = copy_to_tempfile(@target)
@original_filename = @target.original_filename
@content_type = @target.content_type
@size = @tempfile.size || @target.size
end
def copy_to_tempfile(src)
dest = Tempfile.new(src.original_filename)
FileUtils.cp(src.path(:original), dest.path)
dest
end
end
end
Paperclip.io_adapters.register Paperclip::AttachmentAdapter do |target|
Paperclip::Attachment === target
end
require 'mime/types'
module Paperclip
# The Upfile module is a convenience module for adding uploaded-file-type methods
# to the +File+ class. Useful for testing.
# user.avatar = File.new("test/test_avatar.jpg")
module Upfile
# Infer the MIME-type of the file from the extension.
class FileAdapter
def initialize(target)
@target = target
@tempfile = copy_to_tempfile(@target)
end
def original_filename
if @target.respond_to?(:original_filename)
@target.original_filename
else
File.basename(@target.path)
end
end
def content_type
types = MIME::Types.type_for(self.original_filename)
types = MIME::Types.type_for(original_filename)
if types.length == 0
type_from_file_command
elsif types.length == 1
types.first.content_type
else
iterate_over_array_to_find_best_option(types)
best_content_type_option(types)
end
end
def iterate_over_array_to_find_best_option(types)
types.reject {|type| type.content_type.match(/\/x-/) }.first
def fingerprint
@fingerprint ||= Digest::MD5.file(path).to_s
end
def type_from_file_command
# On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
type = (self.original_filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
mime_type = (Paperclip.run("file", "-b --mime :file", :file => self.path).split(/[:;]\s+/)[0] rescue "application/x-#{type}")
mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
mime_type
def size
File.size(@tempfile)
end
# Returns the file's normal name.
def original_filename
File.basename(self.path)
def nil?
@target.nil?
end
# Returns the size of the file.
def size
File.size(self)
def read(length = nil, buffer = nil)
@tempfile.read(length, buffer)
end
end
end
if defined? StringIO
class StringIO
attr_accessor :original_filename, :content_type, :fingerprint
# We don't use this directly, but aws/sdk does.
def rewind
@tempfile.rewind
end
def original_filename
@original_filename ||= "stringio.txt"
def eof?
@tempfile.eof?
end
def content_type
@content_type ||= "text/plain"
def path
@tempfile.path
end
def fingerprint
@fingerprint ||= Digest::MD5.hexdigest(self.string)
private
def copy_to_tempfile(src)
dest = Tempfile.new(original_filename)
FileUtils.cp(src.path, dest.path)
dest
end
def best_content_type_option(types)
types.reject {|type| type.content_type.match(/\/x-/) }.first
end
def type_from_file_command
# On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
type = (self.original_filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
mime_type = (Paperclip.run("file", "-b --mime :file", :file => self.path).split(/[:;]\s+/)[0] rescue "application/x-#{type}")
mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
mime_type
end
end
end
class File #:nodoc:
include Paperclip::Upfile
Paperclip.io_adapters.register Paperclip::FileAdapter do |target|
File === target || Tempfile === target
end
module Paperclip
class IdentityAdapter
def new(adapter)
adapter
end
end
end
Paperclip.io_adapters.register Paperclip::IdentityAdapter.new do |target|
Paperclip.io_adapters.registered?(target)
end
module Paperclip
class NilAdapter
def initialize(target)
end
def original_filename
""
end
def content_type
""
end
def size
0
end
def nil?
true
end
def read(*args)
nil
end
def eof?
true
end
end
end
Paperclip.io_adapters.register Paperclip::NilAdapter do |target|
target.nil?
end
module Paperclip
class AdapterRegistry
class NoHandlerError < PaperclipError; end
attr_reader :registered_handlers
def initialize
@registered_handlers = []
end
def register(handler_class, &block)
@registered_handlers << [block, handler_class]
end
def handler_for(target)
@registered_handlers.each do |tester, handler|
return handler if tester.call(target)
end
raise NoHandlerError.new("No handler found for #{target.inspect}")
end
def registered?(target)
@registered_handlers.any? do |tester, handler|
handler === target
end
end
def for(target)
handler_for(target).new(target)
end
end
end
module Paperclip
class StringioAdapter
def initialize(target)
@target = target
@tempfile = copy_to_tempfile(@target)
end
attr_writer :original_filename, :content_type
def original_filename
@original_filename ||= @target.original_filename if @target.respond_to?(:original_filename)
@original_filename ||= "stringio.txt"
@original_filename.strip
end
def content_type
@content_type ||= @target.content_type if @target.respond_to?(:content_type)
@content_type ||= "text/plain"
@content_type.strip
end
def size
@target.size
end
def fingerprint
Digest::MD5.hexdigest(read)
end
def read(length = nil, buffer = nil)
@tempfile.read(length, buffer)
end
def eof?
@tempfile.eof?
end
def path
@tempfile.path
end
private
def copy_to_tempfile(src)
dest = Tempfile.new(original_filename)
dest.binmode
while data = src.read(16*1024)
dest.write(data)
end
dest.rewind
dest
end
end
end
Paperclip.io_adapters.register Paperclip::StringioAdapter do |target|
StringIO === target
end
module Paperclip
class UploadedFileAdapter
def initialize(target)
@target = target
@tempfile = copy_to_tempfile(@target.tempfile)
end
def original_filename
@target.original_filename
end
def content_type
@target.content_type
end
def fingerprint
@fingerprint ||= Digest::MD5.file(path).to_s
end
def size
File.size(path)
end
def nil?
false
end
def read(length = nil, buffer = nil)
@tempfile.read(length, buffer)
end
# We don't use this directly, but aws/sdk does.
def rewind
@tempfile.rewind
end
def eof?
@tempfile.eof?
end
def path
@tempfile.path
end
private
def copy_to_tempfile(src)
dest = Tempfile.new(original_filename)
FileUtils.cp(src.path, dest.path)
dest
end
end
end
Paperclip.io_adapters.register Paperclip::UploadedFileAdapter do |target|
target.class.name.include?("UploadedFile")
end
# Provides method that can be included on File-type objects (IO, StringIO, Tempfile, etc) to allow stream copying
# and Tempfile conversion.
module IOStream
# Returns a Tempfile containing the contents of the readable object.
def to_tempfile(object)
return object.to_tempfile if object.respond_to?(:to_tempfile)
name = object.respond_to?(:original_filename) ? object.original_filename : (object.respond_to?(:path) ? object.path : "stream")
tempfile = Paperclip::Tempfile.new(["stream", File.extname(name)])
tempfile.binmode
stream_to(object, tempfile)
end
# Copies one read-able object from one place to another in blocks, obviating the need to load
# the whole thing into memory. Defaults to 8k blocks. Returns a File if a String is passed
# in as the destination and returns the IO or Tempfile as passed in if one is sent as the destination.
def stream_to object, path_or_file, in_blocks_of = 8192
dstio = case path_or_file
when String then File.new(path_or_file, "wb+")
when IO then path_or_file
when Tempfile then path_or_file
end
buffer = ""
object.rewind
while object.read(in_blocks_of, buffer) do
dstio.write(buffer)
end
dstio.rewind
dstio
end
end
# Corrects a bug in Windows when asking for Tempfile size.
if defined?(Tempfile) && RUBY_PLATFORM !~ /java/
class Tempfile
def size
if @tmpfile
@tmpfile.fsync
@tmpfile.flush
@tmpfile.stat.size
else
0
end
end
end
end
......@@ -61,7 +61,7 @@ module Paperclip
protected
def type_allowed?(type)
file = StringIO.new(".")
file = Paperclip.io_adapters.for(StringIO.new("."))
file.content_type = type
@subject.attachment_for(@attachment_name).assign(file)
@subject.valid?
......
......@@ -25,8 +25,6 @@ module Paperclip
ActiveRecord::ConnectionAdapters::Table.send(:include, Paperclip::Schema)
ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Paperclip::Schema)
end
File.send(:include, Paperclip::Upfile)
end
end
end
......@@ -27,23 +27,14 @@ 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_name = default_style
if @queued_for_write[style_name]
@queued_for_write[style_name].rewind
@queued_for_write[style_name]
elsif exists?(style_name)
File.new(path(style_name), 'rb')
end
end
def flush_writes #:nodoc:
@queued_for_write.each do |style_name, file|
file.close
FileUtils.mkdir_p(File.dirname(path(style_name)))
log("saving #{path(style_name)}")
FileUtils.cp(file.path, path(style_name))
File.open(path(style_name), "wb") do |new_file|
while chunk = file.read(16 * 1024)
new_file.write(chunk)
end
end
FileUtils.chmod(0666&~File.umask, path(style_name))
end
......
......@@ -84,7 +84,7 @@ module Paperclip
:body => file,
:key => path(style),
:public => fog_public,
:content_type => file.content_type.to_s.strip
:content_type => file.content_type
))
rescue Excon::Errors::NotFound
raise if retried
......@@ -107,25 +107,6 @@ module Paperclip
@queued_for_delete = []
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)
if @queued_for_write[style]
@queued_for_write[style].rewind
@queued_for_write[style]
else
body = directory.files.get(path(style)).body
filename = path(style)
extname = File.extname(filename)
basename = File.basename(filename, extname)
file = Tempfile.new([basename, extname])
file.binmode
file.write(body)
file.rewind
file
end
end
def public_url(style = default_style)
if @options[:fog_host]
host = if @options[:fog_host].respond_to?(:call)
......@@ -133,7 +114,7 @@ module Paperclip
else
(@options[:fog_host] =~ /%d/) ? @options[:fog_host] % (path(style).hash % 4) : @options[:fog_host]
end
"#{host}/#{path(style)}"
else
if fog_credentials[:provider] == 'AWS'
......
......@@ -264,23 +264,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 = default_style
if @queued_for_write[style]
@queued_for_write[style].rewind
return @queued_for_write[style]
end
filename = path(style)
extname = File.extname(filename)
basename = File.basename(filename, extname)
file = Tempfile.new([basename, extname])
file.binmode
file.write(s3_object(style).read)
file.rewind
return file
end
def create_bucket
s3_interface.buckets.create(bucket_name)
end
......@@ -293,7 +276,7 @@ module Paperclip
acl = @s3_permissions[style] || @s3_permissions[:default]
acl = acl.call(self, style) if acl.respond_to?(:call)
write_options = {
:content_type => file.content_type.to_s.strip,
:content_type => file.content_type,
:acl => acl
}
write_options[:metadata] = @s3_metadata unless @s3_metadata.empty?
......
......@@ -45,7 +45,7 @@ namespace :paperclip do
names = Paperclip::Task.obtain_attachments(klass)
names.each do |name|
Paperclip.each_instance_with_attachment(klass, name) do |instance|
if file = instance.send(name).to_file(:original)
if file = instance.send(name)
instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
instance.send("#{name}_content_type=", file.content_type.to_s.strip)
instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
......
......@@ -34,6 +34,8 @@ Gem::Specification.new do |s|
s.add_development_dependency('appraisal', '~> 0.4.0')
s.add_development_dependency('mocha')
s.add_development_dependency('aws-sdk', '~> 1.3.8')
s.add_development_dependency('bourne')
s.add_development_dependency('sqlite3', '~> 1.3.4')
s.add_development_dependency('cucumber', '~> 1.1.0')
s.add_development_dependency('aruba')
s.add_development_dependency('nokogiri', '~> 1.4.7')
......@@ -41,5 +43,8 @@ Gem::Specification.new do |s|
s.add_development_dependency('bundler')
s.add_development_dependency('cocaine', '~> 0.2')
s.add_development_dependency('fog')
s.add_development_dependency('pry')
s.add_development_dependency('launchy')
s.add_development_dependency('rake')
s.add_development_dependency('fakeweb')
end
require './test/helper'
class AdapterRegistryTest < Test::Unit::TestCase
context "for" do
setup do
class AdapterTest
def initialize(target); end
end
@subject = Paperclip::AdapterRegistry.new
@subject.register(AdapterTest){|t| Symbol === t }
end
should "return the class registered for the adapted type" do
assert_equal AdapterTest, @subject.for(:target).class
end
end
context "registered?" do
setup do
class AdapterTest
def initialize(target); end
end
@subject = Paperclip::AdapterRegistry.new
@subject.register(AdapterTest){|t| Symbol === t }
end
should "return true when the class of this adapter has been registered" do
assert @subject.registered?(AdapterTest.new(:target))
end
should "return false when the adapter has not been registered" do
assert ! @subject.registered?(Object)
end
end
end
require './test/helper'
class AttachmentAdapterTest < Test::Unit::TestCase
def setup
rebuild_model :path => "tmp/:class/:attachment/:style/:filename"
@attachment = Dummy.new.avatar
@file = File.new(fixture_file("5k.png"))
@attachment.assign(@file)
@attachment.save
@subject = Paperclip.io_adapters.for(@attachment)
end
should "get the right filename" do
assert_equal "5k.png", @subject.original_filename
end
should "get the content type" do
assert_equal "image/png", @subject.content_type
end
should "get the file's size" do
assert_equal 4456, @subject.size
end
should "return false for a call to nil?" do
assert ! @subject.nil?
end
should "generate a MD5 hash of the contents" do
expected = Digest::MD5.file(@file.path).to_s
assert_equal expected, @subject.fingerprint
end
should "read the contents of the file" do
expected = @file.read
actual = @subject.read
assert expected.length > 0
assert_equal expected.length, actual.length
assert_equal expected, actual
end
end
......@@ -678,10 +678,7 @@ class AttachmentTest < Test::Unit::TestCase
@file = StringIO.new(".")
@file.stubs(:original_filename).returns("5k.png\n\n")
@file.stubs(:content_type).returns("image/png\n\n")
@file.stubs(:to_tempfile).returns(@file)
@dummy = Dummy.new
Paperclip::Thumbnail.expects(:make).returns(@file)
@attachment = @dummy.avatar
@dummy.avatar = @file
end
......@@ -698,23 +695,12 @@ class AttachmentTest < Test::Unit::TestCase
setup do
rebuild_model
@not_file = mock("not_file")
@tempfile = mock("tempfile")
@not_file.stubs(:nil?).returns(false)
@not_file.expects(:size).returns(10)
@tempfile.expects(:size).returns(10)
@not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
@not_file.expects(:content_type).returns("image/png\r\n")
@file = StringIO.new(".")
@file.stubs(:original_filename).returns("sheep_say_bæ.png\r\n")
@file.stubs(:content_type).returns("image/png\r\n")
@dummy = Dummy.new
@attachment = @dummy.avatar
@attachment.expects(:valid_assignment?).with(@not_file).returns(true)
@attachment.expects(:queue_existing_for_delete)
@attachment.expects(:post_process)
@attachment.expects(:to_tempfile).returns(@tempfile)
@attachment.expects(:generate_fingerprint).with(@tempfile).returns("12345")
@attachment.expects(:generate_fingerprint).with(@not_file).returns("12345")
@dummy.avatar = @not_file
@dummy.avatar = @file
end
should "not remove strange letters" do
......@@ -725,14 +711,14 @@ class AttachmentTest < Test::Unit::TestCase
context "Attachment with reserved filename" do
setup do
rebuild_model
@file = StringIO.new(".")
@file = Paperclip.io_adapters.for(StringIO.new("."))
end
context "with default configuration" do
"&$+,/:;=?@<>[]{}|\^~%# ".split(//).each do |character|
context "with character #{character}" do
setup do
@file.stubs(:original_filename).returns("file#{character}name.png")
@file.original_filename = "file#{character}name.png"
@dummy = Dummy.new
@dummy.avatar = @file
end
......@@ -796,11 +782,8 @@ class AttachmentTest < Test::Unit::TestCase
end
should "should have matching to_s and url methods" do
file = @attachment.to_file
assert file
assert_match @attachment.to_s, @attachment.url
assert_match @attachment.to_s(:small), @attachment.url(:small)
file.close
end
end
......@@ -831,7 +814,6 @@ class AttachmentTest < Test::Unit::TestCase
end
should "return nil as path when no file assigned" do
assert @attachment.to_file.nil?
assert_equal nil, @attachment.path
assert_equal nil, @attachment.path(:blah)
end
......@@ -886,10 +868,6 @@ class AttachmentTest < Test::Unit::TestCase
assert @attachment.dirty?
end
should "set uploaded_file for access beyond the paperclip lifecycle" do
assert_equal @file, @attachment.uploaded_file
end
context "and saved" do
setup do
@attachment.save
......@@ -897,11 +875,7 @@ class AttachmentTest < Test::Unit::TestCase
should "commit the files to disk" do
[:large, :medium, :small].each do |style|
io = @attachment.to_file(style)
# p "in commit to disk test, io is #{io.inspect} and @instance.id is #{@instance.id}"
assert File.exists?(io.path)
assert ! io.is_a?(::Tempfile)
io.close
assert File.exists?(@attachment.path(style))
end
end
......@@ -918,11 +892,6 @@ class AttachmentTest < Test::Unit::TestCase
end
end
should "still have its #file attribute not be nil" do
assert ! (file = @attachment.to_file).nil?
file.close
end
context "and trying to delete" do
setup do
@existing_names = @attachment.styles.keys.collect do |style|
......@@ -1047,7 +1016,7 @@ class AttachmentTest < Test::Unit::TestCase
should "return the right value when sent #avatar_file_size" do
@dummy.avatar = @file
assert_equal @file.size, @dummy.avatar.size
assert_equal File.size(@file), @dummy.avatar.size
end
context "and avatar_updated_at column" do
......@@ -1068,18 +1037,12 @@ class AttachmentTest < Test::Unit::TestCase
assert_equal now.to_i, @dummy.avatar.updated_at
end
end
should "not calculate fingerprint after save" do
@dummy.avatar = @file
@dummy.save
assert_nil @dummy.avatar.fingerprint
end
should "not calculate fingerprint before saving" do
should "not calculate fingerprint" do
@dummy.avatar = @file
assert_nil @dummy.avatar.fingerprint
end
context "and avatar_content_type column" do
setup do
ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
......@@ -1110,14 +1073,14 @@ class AttachmentTest < Test::Unit::TestCase
should "return the right value when sent #avatar_file_size" do
@dummy.avatar = @file
assert_equal @file.size, @dummy.avatar.size
assert_equal File.size(@file), @dummy.avatar.size
end
should "return the right value when saved, reloaded, and sent #avatar_file_size" do
@dummy.avatar = @file
@dummy.save
@dummy = Dummy.find(@dummy.id)
assert_equal @file.size, @dummy.avatar.size
assert_equal File.size(@file), @dummy.avatar.size
end
end
......
require './test/helper'
class FileAdapterTest < Test::Unit::TestCase
context "a new instance" do
setup do
@file = File.new(fixture_file("5k.png"))
@subject = Paperclip.io_adapters.for(@file)
end
should "get the right filename" do
assert_equal "5k.png", @subject.original_filename
end
should "get the content type" do
assert_equal "image/png", @subject.content_type
end
should "get the file's size" do
assert_equal 4456, @subject.size
end
should "return false for a call to nil?" do
assert ! @subject.nil?
end
should "generate a MD5 hash of the contents" do
expected = Digest::MD5.file(@file.path).to_s
assert_equal expected, @subject.fingerprint
end
should "read the contents of the file" do
expected = @file.read
assert expected.length > 0
assert_equal expected, @subject.read
end
end
end
......@@ -5,6 +5,7 @@ require 'test/unit'
require 'shoulda'
require 'mocha'
require 'bourne'
require 'active_record'
require 'active_record/version'
......@@ -13,6 +14,9 @@ require 'active_support/core_ext'
require 'mime/types'
require 'pathname'
require 'pathname'
require 'pry'
puts "Testing against version #{ActiveRecord::VERSION::STRING}"
`ruby -e 'exit 0'` # Prime $? with a value.
......
require './test/helper'
class IdentityAdapterTest < Test::Unit::TestCase
should "respond to #new by returning the argument" do
adapter = Paperclip::IdentityAdapter.new
assert_equal :target, adapter.new(:target)
end
end
......@@ -54,7 +54,6 @@ class IntegrationTest < Test::Unit::TestCase
context "redefining its attachment styles" do
setup do
Dummy.class_eval do
has_attached_file :avatar, :styles => { :thumb => "150x25#" }
has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
end
@d2 = Dummy.find(@dummy.id)
......@@ -71,20 +70,6 @@ class IntegrationTest < Test::Unit::TestCase
should "change the timestamp" do
assert_not_equal @original_timestamp, @d2.avatar_updated_at
end
should "clean up the old original if it is a tempfile" do
original = @d2.avatar.to_file(:original)
tf = Paperclip::Tempfile.new('original')
tf.binmode
original.binmode
tf.write(original.read)
original.close
tf.rewind
@d2.avatar.expects(:to_file).with(:original).returns(tf)
@d2.avatar.reprocess!
end
end
end
......@@ -171,7 +156,7 @@ class IntegrationTest < Test::Unit::TestCase
end
should "report the file size of the processed file and not the original" do
assert_not_equal @file.size, @dummy.avatar.size
assert_not_equal File.size(@file.path), @dummy.avatar.size
end
teardown { @file.close }
......@@ -287,6 +272,27 @@ class IntegrationTest < Test::Unit::TestCase
end
end
[000,002,022].each do |umask|
context "when the umask is #{umask}" do
setup do
rebuild_model
@dummy = Dummy.new
@file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
@umask = File.umask(umask)
end
teardown do
File.umask @umask
end
should "respect the current umask" do
@dummy.avatar = @file
@dummy.save
assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
end
end
end
context "A model with a filesystem attachment" do
setup do
rebuild_model :styles => { :large => "300x300>",
......@@ -322,8 +328,6 @@ class IntegrationTest < Test::Unit::TestCase
assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
@dummy.avatar = "not a valid file but not nil"
assert_equal File.basename(@file.path), @dummy.avatar_file_name
assert @dummy.valid?
assert @dummy.save
......@@ -362,13 +366,13 @@ class IntegrationTest < Test::Unit::TestCase
end
end
should "know the difference between good files, bad files, and not files" do
expected = @dummy.avatar.to_file
@dummy.avatar = "not a file"
assert @dummy.valid?
assert_equal expected.path, @dummy.avatar.path
expected.close
should "not abide things that don't have adapters" do
assert_raises(Paperclip::AdapterRegistry::NoHandlerError) do
@dummy.avatar = "not a file"
end
end
should "not be ok with bad files" do
@dummy.avatar = @bad_file
assert ! @dummy.valid?
end
......@@ -384,31 +388,13 @@ class IntegrationTest < Test::Unit::TestCase
should "be able to reload without saving and not have the file disappear" do
@dummy.avatar = @file
assert @dummy.save
assert @dummy.save, @dummy.errors.full_messages.inspect
@dummy.avatar.clear
assert_nil @dummy.avatar_file_name
@dummy.reload
assert_equal "5k.png", @dummy.avatar_file_name
end
[000,002,022].each do |umask|
context "when the umask is #{umask}" do
setup do
@umask = File.umask umask
end
teardown do
File.umask @umask
end
should "respect the current umask" do
@dummy.avatar = @file
@dummy.save
assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
end
end
end
context "that is assigned its file from another Paperclip attachment" do
setup do
@dummy2 = Dummy.new
......@@ -423,9 +409,9 @@ class IntegrationTest < Test::Unit::TestCase
assert @dummy.avatar = @dummy2.avatar
@dummy.save
assert_equal @dummy.avatar_file_name, @dummy2.avatar_file_name
assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
`identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
assert_equal @dummy.avatar_file_name, @dummy2.avatar_file_name
end
end
......@@ -505,7 +491,6 @@ class IntegrationTest < Test::Unit::TestCase
end
should "have the same contents as the original" do
@file.rewind
assert_equal @file.read, @files_on_s3[:original].read
end
......
require './test/helper'
class IOStreamTest < Test::Unit::TestCase
include IOStream
context "A file" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
end
teardown { @file.close }
context "that is sent #stream_to" do
context "and given a String" do
setup do
FileUtils.mkdir_p(File.join(ROOT, 'tmp'))
assert @result = stream_to(@file, File.join(ROOT, 'tmp', 'iostream.string.test'))
end
should "return a File" do
assert @result.is_a?(File)
end
should "contain the same data as the original file" do
@file.rewind; @result.rewind
assert_equal @file.read, @result.read
end
end
context "and given a Tempfile" do
setup do
tempfile = Tempfile.new('iostream.test')
tempfile.binmode
assert @result = stream_to(@file, tempfile)
end
should "return a Tempfile" do
assert @result.is_a?(Tempfile)
end
should "contain the same data as the original file" do
@file.rewind; @result.rewind
assert_equal @file.read, @result.read
end
end
end
context "that is converted #to_tempfile" do
setup do
assert @tempfile = to_tempfile(@file)
end
should "convert it to a Paperclip Tempfile" do
assert @tempfile.is_a?(Paperclip::Tempfile)
end
should "have the name be based on the original_filename" do
name = File.basename(@file.path)
extension = File.extname(name)
basename = File.basename(name, extension)
assert_match %r[^stream.*?#{Regexp.quote(extension)}], File.basename(@tempfile.path)
end
should "have the Tempfile contain the same data as the file" do
@file.rewind; @tempfile.rewind
assert_equal @file.read, @tempfile.read
end
end
end
end
......@@ -34,7 +34,7 @@ class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase
end
end
context "validates_attachment_size with infinite range" do
context "allowing anything" do
setup{ @matcher = self.class.validate_attachment_size(:avatar) }
context "given a class with an upper limit" do
......@@ -42,7 +42,7 @@ class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase
should_accept_dummy_class
end
context "given a class with no upper limit" do
context "given a class with a lower limit" do
setup { @dummy_class.validates_attachment_size :avatar, :greater_than => 1 }
should_accept_dummy_class
end
......
require './test/helper'
class NilAdapterTest < Test::Unit::TestCase
context 'a new instance' do
setup do
@subject = Paperclip.io_adapters.for(nil)
end
should "get the right filename" do
assert_equal "", @subject.original_filename
end
should "get the content type" do
assert_equal "", @subject.content_type
end
should "get the file's size" do
assert_equal 0, @subject.size
end
should "return true for a call to nil?" do
assert @subject.nil?
end
end
end
......@@ -23,20 +23,6 @@ class FileSystemTest < Test::Unit::TestCase
assert File.exists?(@dummy.avatar.path(:thumbnail))
end
should "clean up file objects" do
File.stubs(:exist?).returns(true)
Paperclip::Tempfile.any_instance.expects(:close).at_least_once()
Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once()
@dummy.save!
end
should "always be rewound when returning from #to_file" do
assert_equal 0, @dummy.avatar.to_file.pos
@dummy.avatar.to_file.seek(10)
assert_equal 0, @dummy.avatar.to_file.pos
end
context "with file that has space in file name" do
setup do
rebuild_model :styles => { :thumbnail => "25x25#" }
......
......@@ -56,14 +56,6 @@ class FogTest < Test::Unit::TestCase
assert_equal File.expand_path(File.join(File.dirname(__FILE__), "../../public/avatars/5k.png")),
@dummy.avatar.path
end
should "clean up file objects" do
File.stubs(:exist?).returns(true)
Paperclip::Tempfile.any_instance.expects(:close).at_least_once()
Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once()
@dummy.save!
end
end
setup do
......@@ -110,12 +102,14 @@ class FogTest < Test::Unit::TestCase
directory.destroy
end
# NOTE: This might not be necessary, watch for this to error
should "always be rewound when returning from #to_file" do
assert_equal 0, @dummy.avatar.to_file.pos
@dummy.avatar.to_file.seek(10)
assert_equal 0, @dummy.avatar.to_file.pos
end
# NOTE: This might not be necessary, watch for this to error
should "rewind file in flush_writes" do
@dummy.avatar.queued_for_write.each { |style, file| file.expects(:rewind).with() }
@dummy.save
......
......@@ -46,10 +46,6 @@ unless ENV["S3_BUCKET"].blank?
@dummy.destroy
end
should "still return a Tempfile when sent #to_file" do
assert_equal Paperclip::Tempfile, @dummy.avatar.to_file.class
end
context "and saved" do
setup do
@dummy.save
......@@ -58,11 +54,6 @@ unless ENV["S3_BUCKET"].blank?
should "be on S3" do
assert true
end
should "generate a tempfile with the right name" do
file = @dummy.avatar.to_file
assert_match /^original.*\.png$/, File.basename(file.path)
end
end
end
end
......
......@@ -241,7 +241,7 @@ class S3Test < Test::Unit::TestCase
'secret_access_key' => "54321"
}
file = StringIO.new(".")
file = Paperclip.io_adapters.for(StringIO.new("."))
file.original_filename = "question?mark.png"
@dummy = Dummy.new
@dummy.avatar = file
......@@ -336,17 +336,18 @@ class S3Test < Test::Unit::TestCase
assert_match %r{^avatars/stringio\.txt}, @dummy.avatar.url
end
# NOTE: This might not be necessary, watch for this to error
should "always be rewound when returning from #to_file" do
assert_equal 0, @dummy.avatar.to_file.pos
@dummy.avatar.to_file.seek(10)
assert_equal 0, @dummy.avatar.to_file.pos
end
# NOTE: This might not be necessary, watch for this to error
should "rewind file in flush_writes" do
@dummy.avatar.queued_for_write.each { |style, file| file.expects(:rewind).with() }
@dummy.save
end
end
context "Generating a secure url with an expiration" do
......@@ -555,14 +556,6 @@ class S3Test < Test::Unit::TestCase
end
end
should "delete tempfiles" do
File.stubs(:exist?).returns(true)
Paperclip::Tempfile.any_instance.expects(:close).at_least_once()
Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once()
@dummy.save!
end
context "and saved without a bucket" do
setup do
AWS::S3::BucketCollection.any_instance.expects(:create).with("testing")
......@@ -1065,13 +1058,6 @@ class S3Test < Test::Unit::TestCase
context "and saved" do
setup do
[:thumb, :original].each do |style|
object = stub
@dummy.avatar.stubs(:s3_object).with(style).returns(object)
object.expects(:write).with(anything,
:content_type => "image/png",
:acl => style == :thumb ? :public_read : :private)
end
@dummy.save
end
......
require './test/helper'
class StringioFileProxyTest < Test::Unit::TestCase
context "a new instance" do
setup do
@contents = "abc123"
@stringio = StringIO.new(@contents)
@subject = Paperclip.io_adapters.for(@stringio)
end
should "return a file name" do
assert_equal "stringio.txt", @subject.original_filename
end
should "allow us to set a name" do
@subject.original_filename = "data.txt"
assert_equal "data.txt", @subject.original_filename
end
should "return a content type" do
assert_equal "text/plain", @subject.content_type
end
should "allow us to set a content type" do
@subject.content_type = "image/jpg"
assert_equal "image/jpg", @subject.content_type
end
should "return the size of the data" do
assert_equal 6, @subject.size
end
should "generate an MD5 hash of the contents" do
assert_equal Digest::MD5.hexdigest(@contents), @subject.fingerprint
end
should "return the data contained in the StringIO" do
assert_equal "abc123", @subject.read
end
end
end
require './test/helper'
class UpfileTest < Test::Unit::TestCase
{ %w(jpg jpe jpeg) => 'image/jpeg',
%w(tif tiff) => 'image/tiff',
%w(png) => 'image/png',
%w(gif) => 'image/gif',
%w(bmp) => 'image/bmp',
%w(svg) => 'image/svg+xml',
%w(txt) => 'text/plain',
%w(htm html) => 'text/html',
%w(csv) => 'text/csv',
%w(xml) => 'application/xml',
%w(css) => 'text/css',
%w(js) => 'application/javascript',
%w(foo) => 'application/x-foo'
}.each do |extensions, content_type|
extensions.each do |extension|
should "return a content_type of #{content_type} for a file with extension .#{extension}" do
file = stub('file', :path => "basename.#{extension}")
class << file
include Paperclip::Upfile
end
assert_equal content_type, file.content_type
end
end
end
should "return a content_type of text/plain on a real file whose content_type is determined with the file command" do
file = File.new(File.join(File.dirname(__FILE__), "..", "LICENSE"))
class << file
include Paperclip::Upfile
end
assert_equal 'text/plain', file.content_type
end
{ '5k.png' => 'image/png',
'animated.gif' => 'image/gif',
'text.txt' => 'text/plain',
'twopage.pdf' => 'application/pdf'
}.each do |filename, content_type|
should "return a content type of #{content_type} from a file command for file #{filename}" do
file = File.new(File.join(File.dirname(__FILE__), "fixtures", filename))
class << file
include Paperclip::Upfile
end
assert_equal content_type, file.type_from_file_command
end
end
end
require './test/helper'
class UploadedFileAdapterTest < Test::Unit::TestCase
context "a new instance" do
setup do
class UploadedFile < OpenStruct; end
@file = UploadedFile.new(
:original_filename => "5k.png",
:content_type => "image/png",
:head => "",
:tempfile => File.new(fixture_file("5k.png"))
)
@subject = Paperclip.io_adapters.for(@file)
end
should "get the right filename" do
assert_equal "5k.png", @subject.original_filename
end
should "get the content type" do
assert_equal "image/png", @subject.content_type
end
should "get the file's size" do
assert_equal 4456, @subject.size
end
should "return false for a call to nil?" do
assert ! @subject.nil?
end
should "generate a MD5 hash of the contents" do
expected = Digest::MD5.file(@file.tempfile.path).to_s
assert_equal expected, @subject.fingerprint
end
should "read the contents of the file" do
expected = @file.tempfile.read
assert expected.length > 0
assert_equal expected, @subject.read
end
end
end
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