Commit 2784b380 by jyurek

Huge refactoring.

git-svn-id: https://svn.thoughtbot.com/plugins/paperclip/trunk@390 7bbfaf0e-4d1d-0410-9690-a8bb5f8ef2aa
parent c77ba25e
...@@ -11,7 +11,7 @@ See the documentation for the +has_attached_file+ method for options. ...@@ -11,7 +11,7 @@ See the documentation for the +has_attached_file+ method for options.
In your model: In your model:
class User < ActiveRecord::Base class User < ActiveRecord::Base
has_attached_file :avatar, :thumbnails => { :medium => "300x300>", :thumb => "100x100>" } has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end end
In your edit and new views: In your edit and new views:
...@@ -23,12 +23,11 @@ In your edit and new views: ...@@ -23,12 +23,11 @@ In your edit and new views:
In your controller: In your controller:
def create def create
@user = User.create( params[:user][:avatar] ) @user = User.create( params[:user] )
end end
In your show view: In your show view:
<%= image_tag @user.avatar.url %> <%= image_tag @user.avatar.url %>
<%= image_tag @user.avatar.url(:original) %>
<%= image_tag @user.avatar.url(:medium) %> <%= image_tag @user.avatar.url(:medium) %>
<%= image_tag @user.avatar.url(:thumb) %> <%= image_tag @user.avatar.url(:thumb) %>
# Install hook code here
module Paperclip
# Defines the geometry of an image.
class Geometry
attr_accessor :height, :width, :modifier
# Gives a Geometry representing the given height and width
def initialize width = nil, height = nil, modifier = nil
height = nil if height == ""
width = nil if width == ""
@height = (height || width).to_f
@width = (width || height).to_f
@modifier = modifier
end
# Uses ImageMagick to determing the dimensions of a file, passed in as either a
# File or path.
def self.from_file file
file = file.path if file.respond_to? "path"
parse(`#{Paperclip.path_for_command('identify')} "#{file}" 2>/dev/null`) ||
raise(Errno::ENOENT, file)
end
# Parses a "WxH" formatted string, where W is the width and H is the height.
def self.parse string
if match = (string && string.match(/\b(\d*)x(\d*)([><\#\@\%^!])?\b/))
Geometry.new(*match[1,3])
end
end
# True if the dimensions represent a square
def square?
height == width
end
# True if the dimensions represent a horizontal rectangle
def horizontal?
height < width
end
# True if the dimensions represent a vertical rectangle
def vertical?
height > width
end
# The aspect ratio of the dimensions.
def aspect
width / height
end
# Returns the larger of the two dimensions
def larger
[height, width].max
end
# Returns the smaller of the two dimensions
def smaller
[height, width].min
end
# Returns the width and height in a format suitable to be passed to Geometry.parse
def to_s
"%dx%d" % [width, height]
end
# Same as to_s
def inspect
to_s
end
# Returns the scaling and cropping geometries (in string-based ImageMagick format)
# neccessary to transform this Geometry into the Geometry given. If crop is true,
# then it is assumed the destination Geometry will be the exact final resolution.
# In this case, the source Geometry is scaled so that an image containing the
# destination Geometry would be completely filled by the source image, and any
# overhanging image would be cropped. Useful for square thumbnail images. The cropping
# is weighted at the center of the Geometry.
def transformation_to dst, crop = false
ratio = Geometry.new( dst.width / self.width, dst.height / self.height )
if crop
scale_geometry, scale = if ratio.horizontal? || ratio.square?
[ "%dx" % dst.width, ratio.width ]
else
[ "x%d" % dst.height, ratio.height ]
end
crop_geometry = if ratio.horizontal? || ratio.square?
"%dx%d+%d+%d" % [ dst.width, dst.height, 0, (self.height * scale - dst.height) / 2 ]
else
"%dx%d+%d+%d" % [ dst.width, dst.height, (self.width * scale - dst.width) / 2, 0 ]
end
else
scale_geometry = dst.to_s
end
[ scale_geometry, crop_geometry ]
end
end
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
tempfile = Tempfile.new("stream")
self.stream_to(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. If this module is included in both
# both StringIO and Tempfile, then either can have its data copied anywhere else without typing
# worries or memory overhead worries. 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 path_or_file, in_blocks_of = 8192
dstio = case path_or_file
when String then File.new(path_or_file, "w+")
when IO then path_or_file
when Tempfile then path_or_file
end
buffer = ""
self.rewind
while self.read(in_blocks_of, buffer) do
dstio.write(buffer)
end
dstio.rewind
dstio
end
end
class IO
include IOStream
end
%w( Tempfile StringIO ).each do |klass|
if Object.const_defined? klass
Object.const_get(klass).class_eval do
include IOStream
end
end
end
module Paperclip
module Storage
module Filesystem
def write_attachment
ensure_directories
each_unsaved do |style, data|
File.open( file_name(style), "w" ) do |file|
file.rewind
file.write(data)
end
end
end
def read_attachment style = nil
IO.read(file_name(style))
end
def delete_attachment complain = false
styles.each do |style|
file_path = file_name(style)
begin
FileUtils.rm file_path if file_path
rescue SystemCallError => e
raise PaperclipError, "could not be deleted." if Paperclip.options[:whiny_deletes] || complain
end
end
end
def attachment_exists? style = nil
File.exists?( file_name(style) )
end
def file_name style = nil
style ||= definition.default_style
interpolate( style, definition.path )
end
def ensure_directories
each_unsaved do |style, file|
dirname = File.dirname( file_name(style) )
FileUtils.mkdir_p dirname
end
end
end
end
end
\ No newline at end of file
module Paperclip
module Storage
module S3
def self.extended(base)
Paperclip.options[:s3] ||= {}
Paperclip::Attachment.interpolations[:bucket] = lambda{|style, atch| atch.definition.bucket }
access_key, secret_key = credentials
require 'aws/s3'
AWS::S3::Base.establish_connection!(
:access_key_id => access_key,
:secret_access_key => secret_key,
:persistent => Paperclip.options[:s3][:persistent] || true
)
class << base
alias_method_chain :url, :s3
end
end
def self.credentials
if credentials_file
creds = YAML.load_file(credentials_file)
creds = creds[RAILS_ENV] || creds if Object.const_defined?("RAILS_ENV")
[ creds['access_key_id'], creds['secret_access_key'] ]
else
[ Paperclip.options[:s3][:access_key_id], Paperclip.options[:s3][:secret_access_key] ]
end
end
def self.credentials_file
@file ||= [ Paperclip.options[:s3][:credentials_file], File.join(RAILS_ROOT, "config", "s3.yml") ].compact.find do |f|
File.exists?(f)
end
end
def url_with_s3 style = nil
http_host = definition.s3_host || "http://s3.amazonaws.com"
"#{http_host}/#{bucket}#{url_without_s3(style)}"
end
def file_name style = nil
style ||= definition.default_style
interpolate( style, definition.url )
end
def bucket
definition.bucket
end
def ensure_bucket
begin
AWS::S3::Bucket.create(bucket)
bucket
rescue AWS::S3::S3Exception => e
raise Paperclip::PaperclipError, "You are not allowed access to the bucket '#{bucket_name}'."
end
end
def stream style = nil, &block
AWS::S3::S3Object.stream( file_name(style), bucket, &block )
end
# These four methods are the primary interface for the storage module.
# Everything above this is support for these methods.
def attachment_exists? style = nil
AWS::S3::S3Object.exists?( file_name(style), bucket )
end
def write_attachment
ensure_bucket
each_unsaved do |style, data|
AWS::S3::S3Object.store( file_name(style), data, bucket, :access => definition.s3_access || :public_read )
end
end
def read_attachment style = nil
AWS::S3::S3Object.value( file_name(style), bucket )
end
def delete_attachment complain = false
styles.each do |style, data|
AWS::S3::S3Object.delete( file_name(style), bucket )
end
end
end
end
end
\ No newline at end of file
module Paperclip module Paperclip
class Thumbnail class Thumbnail
class Geometry attr_accessor :file, :current_geometry, :target_geometry, :format, :whiny_thumbnails
attr_accessor :height, :width
def initialize width = nil, height = nil
@height = (height || width).to_f
@width = (width || height).to_f
end
def self.parse string # Creates a Thumbnail object set to work on the +file+ given. It
if match = (string && string.match(/(\d*)x(\d*)/)) # will attempt to transform the image into one defined by +target_geometry+
Geometry.new(*match[1,2]) # which is a "WxH"-style string. +format+ will be inferred from the +file+
end # unless specified. Thumbnail creation will raise no errors unless
end # +whiny_thumbnails+ is true (which it is, by default.
def initialize file, target_geometry, format = nil, whiny_thumbnails = true
@file = file
@crop = target_geometry[-1,1] == '#'
@target_geometry = Geometry.parse target_geometry
@current_geometry = Geometry.from_file file
@whiny_thumbnails = whiny_thumbnails
def square? @current_format = File.extname(@file.path)
height == width @basename = File.basename(@file.path, @current_format)
end
def horizontal? @format = format
height < width
end end
def vertical? # Creates a thumbnail, as specified in +initialize+, +make+s it, and returns the
height > width # resulting Tempfile.
def self.make file, dimensions, format = nil, whiny_thumbnails = true
new(file, dimensions, format, whiny_thumbnails).make
end end
def aspect # Returns true if the +target_geometry+ is meant to crop.
width / height def crop?
end @crop
def larger
[height, width].max
end
def smaller
[height, width].min
end
def to_s
"#{width}x#{height}"
end
def inspect
to_s
end
end
attr_accessor :geometry, :data
def initialize geometry, data, whiny_thumbnails = nil
@geometry, @data = geometry, data
@whiny_thumbnails = Paperclip.options[:whiny_thumbnails]
@whiny_thumbnails = whiny_thumbnails unless whiny_thumbnails.nil?
end
def self.make geometry, data, whiny_thumbnails = nil
new(geometry, data, whiny_thumbnails).make
end end
# Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
# that contains the new image.
def make def make
return data if geometry.nil? src = @file
operator = geometry[-1,1] dst = Tempfile.new([@basename, @format].compact.join("."))
begin
scale_geometry = geometry
scale_geometry, crop_geometry = geometry_for_crop if operator == '#'
convert = Paperclip.path_for_command("convert")
command = "#{convert} - -scale '#{scale_geometry}' #{operator == '#' ? "-crop '#{crop_geometry}'" : ""} - 2>/dev/null"
thumb = piping data, :to => command
rescue Errno::EPIPE => e
raise PaperclipError, "could not be thumbnailed. Is ImageMagick or GraphicsMagick installed and available?"
rescue SystemCallError => e
raise PaperclipError, "could not be thumbnailed."
rescue PaperclipError
raise if @whiny_thumbnails
end
if @whiny_thumbnails && !$?.success?
raise PaperclipError, "could not be thumbnailed because of an error with 'convert'."
end
thumb
end
def geometry_for_crop command = <<-end_command
identify = Paperclip.path_for_command("identify") #{ Paperclip.path_for_command('convert') }
piping data, :to => "#{identify} - 2>/dev/null" do |pipeout| "#{ File.expand_path(src.path) }"
dimensions = pipeout.split[2] #{ transformation_command }
if src = Geometry.parse(dimensions) "#{ File.expand_path(dst.path) }"
dst = Geometry.parse(geometry) end_command
success = system(command.gsub(/\s+/, " "))
ratio = Geometry.new( dst.width / src.width, dst.height / src.height ) if success && $?.exitstatus != 0 && @whiny_thumbnails
scale_geometry, scale = if ratio.horizontal? || ratio.square? raise PaperclipError, "There was an error processing this thumbnail"
[ "%dx" % dst.width, ratio.width ]
else
[ "x%d" % dst.height, ratio.height ]
end end
crop_geometry = if ratio.horizontal? || ratio.square? dst
"%dx%d+%d+%d" % [ dst.width, dst.height, 0, (src.height * scale - dst.height) / 2 ]
else
"%dx%d+%d+%d" % [ dst.width, dst.height, (src.width * scale - dst.width) / 2, 0 ]
end end
[ scale_geometry, crop_geometry ] # Returns the command ImageMagick's +convert+ needs to transform the image
else # into the thumbnail.
raise PaperclipError, "does not contain a valid image." def transformation_command
end scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
trans = "-scale #{scale}"
trans << " -crop #{crop} +repage" if crop
trans
end end
end end
def piping data, command, &block # Due to how ImageMagick handles its image format conversion and how Tempfile
self.class.piping(data, command, &block) # handles its naming scheme, it is necessary to override how Tempfile makes
end # its names so as to allow for file extensions. Idea taken from the comments
# on this blog post:
def self.piping data, command, &block # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
command = command[:to] if command.respond_to?(:[]) && command[:to] class Tempfile < ::Tempfile
block ||= lambda {|d| d } # Replaces Tempfile's +make_tmpname+ with one that honors file extensions.
IO.popen(command, "w+") do |io| def make_tmpname(basename, n)
io.write(data) extension = File.extname(basename)
io.close_write sprintf("%s,%d,%d%s", File.basename(basename, extension), $$, n, extension)
block.call(io.read)
end
end end
end end
end end
...@@ -26,3 +26,7 @@ module Paperclip ...@@ -26,3 +26,7 @@ module Paperclip
end end
end end
class File #:nodoc:
include Paperclip::Upfile
end
...@@ -2,7 +2,7 @@ require 'environment' ...@@ -2,7 +2,7 @@ require 'environment'
def obtain_class def obtain_class
class_name = ENV['CLASS'] || ENV['class'] class_name = ENV['CLASS'] || ENV['class']
@klass = eval(class_name) @klass = Object.const_get(class_name)
end end
def obtain_attachments def obtain_attachments
...@@ -10,7 +10,7 @@ def obtain_attachments ...@@ -10,7 +10,7 @@ def obtain_attachments
if !name.blank? && @klass.attachment_names.include?(name) if !name.blank? && @klass.attachment_names.include?(name)
[ name ] [ name ]
else else
@klass.attachment_names @klass.attachment_definitions.keys
end end
end end
...@@ -21,12 +21,17 @@ namespace :paperclip do ...@@ -21,12 +21,17 @@ namespace :paperclip do
instances = klass.find(:all) instances = klass.find(:all)
names = obtain_attachments names = obtain_attachments
puts "Regenerating thumbnails for #{instances.length} instances:" puts "Regenerating thumbnails for #{instances.length} instances of #{klass.name}:"
instances.each do |instance| instances.each do |instance|
names.each do |name| names.each do |name|
instance.send("process_#{name}_thumbnails") result = if instance.send("#{ name }?")
instance.send(name).send("post_process")
instance.send(name).save
else
true
end
print result ? "." : "x"; $stdout.flush
end end
print instance.save ? "." : "x"; $stdout.flush
end end
puts " Done." puts " Done."
end end
......
...@@ -2,3 +2,4 @@ test: ...@@ -2,3 +2,4 @@ test:
adapter: sqlite3 adapter: sqlite3
#dbfile: paperclip.db #dbfile: paperclip.db
database: ":memory:" database: ":memory:"
This is not an image.
require 'rubygems'
require 'test/unit'
require 'shoulda'
require 'mocha'
require 'tempfile'
require 'active_record'
require 'ruby-debug'
ROOT = File.join(File.dirname(__FILE__), '..')
RAILS_ROOT = ROOT
$LOAD_PATH << File.join(ROOT, 'lib')
$LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
require File.join(ROOT, 'lib', 'paperclip.rb')
FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
ActiveRecord::Base.establish_connection(config[ENV['RAILS_ENV'] || 'test'])
def rebuild_model options = {}
ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
table.column :avatar_file_name, :string
table.column :avatar_content_type, :string
table.column :avatar_file_size, :integer
end
ActiveRecord::Base.send(:include, Paperclip)
Object.send(:remove_const, "Dummy") rescue nil
Object.const_set("Dummy", Class.new(ActiveRecord::Base))
Dummy.class_eval do
include Paperclip
has_attached_file :avatar, options
end
end
class << Test::Unit::TestCase
def context name, &block
(@contexts ||= []) << name
(@context_blocks ||= []) << block
saved_setups = (@context_setups ||= []).dup
saved_teardowns = (@context_teardowns ||= []).dup
self.instance_eval(&block)
@context_setups = saved_setups
@context_teardowns = saved_teardowns
@contexts.pop
@context_blocks.pop
end
def setup &block
@context_setups << block
end
def teardown &block
@context_teardowns << block
end
def should name, &test
context_setups = @context_setups.dup
context_teardowns = @context_teardowns.dup
define_method(["test:", @contexts, "should", name].flatten.join(" ")) do
context_setups.each { |setup| self.instance_eval(&setup) }
self.instance_eval(&test)
context_teardowns.each { |teardown| self.instance_eval(&teardown) }
end
end
def should_eventually name
define_method(["test:", @contexts, "should eventually", name].flatten.join(" ")) do
STDOUT.print "X"
assert true
end
end
end
\ No newline at end of file
require 'test/unit' require 'test/helper'
require File.dirname(__FILE__) + "/test_helper.rb"
class TestAttachment < Test::Unit::TestCase class Dummy
context "An attachment" do # This is a dummy class
setup do end
@dummy = {}
@definition = Paperclip::AttachmentDefinition.new("thing", {})
@attachment = Paperclip::Attachment.new(@dummy, "thing", @definition)
end
end
context "The class Foo" do
setup do
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_file_size, :integer
table.column :document_file_name, :string
table.column :document_content_type, :string
table.column :document_file_size, :integer
end
Object.send(:remove_const, :Foo) rescue nil
class ::Foo < ActiveRecord::Base; end
end
context "with an image attached to :image" do
setup do
assert Foo.has_attached_file(:image)
@foo = Foo.new
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "test_image.jpg"))
assert_nothing_raised{ @foo.image = @file }
end
should "be able to have a file assigned with :image=" do
assert_equal "test_image.jpg", @foo.image.original_filename
assert_equal "image/jpg", @foo.image.content_type
end
should "be able to retrieve the data as a blob" do
@file.rewind
assert_equal @file.read, @foo.image.read
end
context "and saved" do class AttachmentTest < Test::Unit::TestCase
context "An attachment" do
setup do setup do
assert @foo.save @default_options = {
end :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
}
should "have no errors" do @instance = stub
assert @foo.image.errors.blank? @instance.stubs(:id).returns(41)
assert @foo.errors.blank? @instance.stubs(:class).returns(Dummy)
end @instance.stubs(:[]).with(:test_file_name).returns("5k.png")
@instance.stubs(:[]).with(:test_content_type).returns("image/png")
should "have a file on the filesystem" do @instance.stubs(:[]).with(:test_file_size).returns(12345)
assert @foo.image.send(:file_name) @attachment = Paperclip::Attachment.new(:test, @instance, @default_options)
assert File.file?(@foo.image.send(:file_name)), @foo.image.send(:file_name) @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"))
assert File.size(@foo.image.send(:file_name)) > 0 end
assert_match /405x375/, `identify '#{@foo.image.send(:file_name)}'`
assert_equal IO.read(@file.path), @foo.image.read context "when expecting three styles" do
end
context "and then deleted" do
setup do setup do
assert @foo.destroy @attachment = Paperclip::Attachment.new(:test, @instance, @default_options.merge({
end :styles => { :large => ["400x400", :png],
:medium => ["100x100", :gif],
should "have no errors" do :small => ["32x32#", :jpg]}
assert @foo.image.errors.blank? }))
end
should "have no files on the filesystem" do
assert !File.file?(@foo.image.send(:file_name)), @foo.image.send(:file_name)
assert !File.exist?(@foo.image.send(:file_name)), @foo.image.send(:file_name)
end
end end
context "and then set to null and resaved" do context "and assigned a file" do
setup do setup do
@foo.image = nil @instance.expects(:[]=).with(:test_file_name, File.basename(@file.path))
assert @foo.save @instance.expects(:[]=).with(:test_content_type, "image/png")
@instance.expects(:[]=).with(:test_file_size, @file.size)
@instance.expects(:[]=).with(:test_file_name, nil)
@instance.expects(:[]=).with(:test_content_type, nil)
@instance.expects(:[]=).with(:test_file_size, nil)
@attachment.assign(@file)
end end
should "have no errors" do should "be dirty" do
assert @foo.image.errors.blank? assert @attachment.dirty?
end end
should "have no files on the filesystem" do should "have its image and attachments as tempfiles" do
assert !File.file?(@foo.image.send(:file_name)), @foo.image.send(:file_name) [nil, :large, :medium, :small].each do |style|
assert !File.exist?(@foo.image.send(:file_name)), @foo.image.send(:file_name) assert File.exists?(@attachment.to_io(style))
end
end
end end
end end
context "with an image with thumbnails attached to :image and saved" do context "and saved" do
setup do setup do
assert Foo.has_attached_file(:image, :styles => {:small => "16x16", :medium => "100x100", :large => "250x250", :square => "32x32#"}) @attachment.save
@foo = Foo.new
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "test_image.jpg"))
assert_nothing_raised{ @foo.image = @file }
assert @foo.save
end
should "have no errors" do
assert @foo.image.errors.blank?, @foo.image.errors.inspect
assert @foo.errors.blank?
end
[:original, :small, :medium, :large, :square].each do |style|
should "have a file for #{style} on the filesystem" do
assert @foo.image.send(:file_name)
assert File.file?(@foo.image.send(:file_name)), @foo.image.send(:file_name)
assert File.size(@foo.image.send(:file_name)) > 0
assert_equal IO.read(@file.path), @foo.image.read
end
should "return the correct urls when asked for the #{style} image" do
assert_equal "/foos/images/1/#{style}_test_image.jpg", @foo.image.url(style)
end
end
should "produce the correct dimensions when each style is identified" do
assert_match /16x15/, `identify '#{@foo.image.send(:file_name, :small)}'`
assert_match /32x32/, `identify '#{@foo.image.send(:file_name, :square)}'`
assert_match /100x93/, `identify '#{@foo.image.send(:file_name, :medium)}'`
assert_match /250x231/, `identify '#{@foo.image.send(:file_name, :large)}'`
assert_match /405x375/, `identify '#{@foo.image.send(:file_name, :original)}'`
end
end end
context "with an image with thumbnails attached to :image and a document attached to :document" do should "commit the files to disk" do
[nil, :large, :medium, :small].each do |style|
io = @attachment.to_io(style)
assert File.exists?(io)
assert ! io.is_a?(::Tempfile)
end end
context "with an invalid image with a square thumbnail attached to :image" do
setup do
assert Foo.has_attached_file(:image, :styles => {:square => "32x32#"})
assert Foo.validates_attached_file(:image)
@foo = Foo.new
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "test_invalid_image.jpg"))
assert_nothing_raised{ @foo.image = @file }
end end
should "not save and should report errors from identify" do should "save the files as the right formats and sizes" do
assert !@foo.save [[:large, 400, 61, "PNG"], [:medium, 100, 15, "GIF"], [:small, 32, 32, "JPEG"]].each do |style|
assert @foo.errors.on(:image) out = `identify -format "%w %h %b %m" #{@attachment.to_io(style.first).path}`
assert @foo.errors.on(:image).any?{|e| e.match(/does not contain a valid image/) }, @foo.errors.on(:image) width, height, size, format = out.split(" ")
assert_equal style[1].to_s, width.to_s
assert_equal style[2].to_s, height.to_s
assert_equal style[3].to_s, format.to_s
end end
end end
context "with an invalid image attached to :image" do
setup do
assert Foo.has_attached_file(:image, :styles => {:sorta_square => "32x32"})
assert Foo.validates_attached_file(:image)
@foo = Foo.new
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "test_invalid_image.jpg"))
assert_nothing_raised{ @foo.image = @file }
end end
should "not save and should report errors from convert" do
assert !@foo.save
assert @foo.errors.on(:image)
assert @foo.errors.on(:image).any?{|e| e.match(/because of an error/) }, @foo.errors.on(:image)
end end
end end
end end
......
require 'test/unit'
require File.dirname(__FILE__) + "/test_helper.rb"
class TestAttachmentDefinition < Test::Unit::TestCase
context "Attachment definitions" do
should "allow overriding options" do
not_expected = Paperclip::AttachmentDefinition.defaults[:path]
Paperclip::AttachmentDefinition.defaults[:path] = "123"
assert_not_equal not_expected, Paperclip::AttachmentDefinition.defaults[:path]
assert_equal "123", Paperclip::AttachmentDefinition.defaults[:path]
end
should "accept options that override defaults" do
@def = Paperclip::AttachmentDefinition.new "attachment", :path => "123", :delete_on_destroy => false
assert_not_equal Paperclip::AttachmentDefinition.defaults[:path], @def.path
assert_not_equal Paperclip::AttachmentDefinition.defaults[:delete_on_destroy], @def.delete_on_destroy
assert_equal "123", @def.path
assert_equal false, @def.delete_on_destroy
end
end
context "An attachment defintion" do
setup do
@options = {
:path => "/home/stuff/place",
:url => "/attachments/:attachment/:name",
:custom_definition => :boogie!,
:styles => {:thumb => "100x100", :large => "300x300>"},
:validates_existance => true,
:validates_size => [0, 2048]
}
@def = Paperclip::AttachmentDefinition.new "attachment", @options
end
should "automatically look in the hash for missing methods" do
assert ! @def.respond_to?(:custom_defintion)
assert_equal :boogie!, @def.custom_definition
end
should "be able to read options using attribute readers" do
(@options.keys - [:styles]).each do |key|
assert_equal @options[key], @def.send(key)
end
end
should "return styles as the styles option plus the original" do
assert_equal( (@options[:styles].keys + [:original]).map(&:to_s).sort.uniq, @def.styles.keys.map(&:to_s).sort )
end
should "return all validations when sent :validations" do
assert @def.validations[:existance] == true, @def.validations[:existance]
assert @def.validations[:size] == [0, 2048], @def.validations[:size]
end
end
end
\ No newline at end of file
require 'rubygems'
require 'test/unit'
require 'shoulda'
require File.join(File.dirname(__FILE__), '..', 'lib', 'paperclip', 'geometry.rb')
class PaperclipTest < Test::Unit::TestCase
context "Paperclip::Geometry" do
should "correctly report its given dimensions" do
assert @geo = Paperclip::Geometry.new(1024, 768)
assert_equal 1024, @geo.width
assert_equal 768, @geo.height
end
should "correctly create a square if the height dimension is missing" do
assert @geo = Paperclip::Geometry.new(1024)
assert_equal 1024, @geo.width
assert_equal 1024, @geo.height
end
should "correctly create a square if the width dimension is missing" do
assert @geo = Paperclip::Geometry.new(nil, 768)
assert_equal 768, @geo.width
assert_equal 768, @geo.height
end
should "be generated from a WxH-formatted string" do
assert @geo = Paperclip::Geometry.parse("800x600")
assert_equal 800, @geo.width
assert_equal 600, @geo.height
end
should "be generated from a xH-formatted string" do
assert @geo = Paperclip::Geometry.parse("x600")
assert_equal 600, @geo.width
assert_equal 600, @geo.height
end
should "be generated from a Wx-formatted string" do
assert @geo = Paperclip::Geometry.parse("800x")
assert_equal 800, @geo.width
assert_equal 800, @geo.height
end
should "be generated from a file" do
file = Dir.glob("/Users/jyurek/Pictures/*.jpg").first
file = File.new(file)
assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
assert @geo.height > 0
assert @geo.width > 0
end
should "be generated from a file path" do
file = Dir.glob("/Users/jyurek/Pictures/*.jpg").first
assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
assert @geo.height > 0
assert @geo.width > 0
end
should "not generate from a bad file" do
file = "/home/This File Does Not Exist.omg"
assert_raise(Errno::ENOENT){ @geo = Paperclip::Geometry.from_file(file) }
end
[['vertical', 900, 1440, true, false, false, 1440, 900, 0.625],
['horizontal', 1024, 768, false, true, false, 1024, 768, 1.3333],
['square', 100, 100, false, false, true, 100, 100, 1]].each do |args|
context "performing calculations on a #{args[0]} viewport" do
setup do
@geo = Paperclip::Geometry.new(args[1], args[2])
end
should "#{args[3] ? "" : "not"} be vertical" do
assert_equal args[3], @geo.vertical?
end
should "#{args[4] ? "" : "not"} be horizontal" do
assert_equal args[4], @geo.horizontal?
end
should "#{args[5] ? "" : "not"} be square" do
assert_equal args[5], @geo.square?
end
should "report that #{args[6]} is the larger dimension" do
assert_equal args[6], @geo.larger
end
should "report that #{args[7]} is the smaller dimension" do
assert_equal args[7], @geo.smaller
end
should "have an aspect ratio of #{args[8]}" do
assert_in_delta args[8], @geo.aspect, 0.0001
end
end
end
[[ [1000, 100], [64, 64], "x64", "64x64+288+0" ],
[ [100, 1000], [50, 950], "x950", "50x950+22+0" ],
[ [100, 1000], [50, 25], "50x", "50x25+0+237" ]]. each do |args|
context "of #{args[0].inspect} and given a Geometry #{args[1].inspect} and sent transform_to" do
setup do
@geo = Paperclip::Geometry.new(*args[0])
@dst = Paperclip::Geometry.new(*args[1])
@scale, @crop = @geo.transformation_to @dst, true
end
should "be able to return the correct scaling transformation geometry #{args[2]}" do
assert_equal args[2], @scale
end
should "be able to return the correct crop transformation geometry #{args[3]}" do
assert_equal args[3], @crop
end
end
end
end
end
require 'rubygems'
require 'test/unit'
require 'active_record'
require 'active_record/fixtures'
require 'fileutils'
require 'pp'
require File.dirname(__FILE__) + "/simply_shoulda.rb"
require File.dirname(__FILE__) + "/../init.rb"
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
ActiveRecord::Base.establish_connection(config[ENV['RAILS_ENV'] || 'test'])
Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
RAILS_ROOT = "test" unless Object.const_defined? "RAILS_ROOT"
class Test::Unit::TestCase #:nodoc:
def create_fixtures(*table_names)
if block_given?
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
else
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
end
end
def self.load_all_fixtures
all_fixtures = Dir.glob("#{File.dirname(__FILE__)}/fixtures/*.yml").collect do |f|
puts "Loading fixture '#{f}'"
File.basename(f).gsub(/\.yml$/, "").to_sym
end
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, all_fixtures)
end
# Turn off transactional fixtures if you're working with MyISAM tables in MySQL
self.use_transactional_fixtures = true
# Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
self.use_instantiated_fixtures = false
# Add more helper methods to be used by all tests here...
end
\ No newline at end of file
require 'test/helper.rb'
class PaperclipTest < Test::Unit::TestCase
context "A model with an attachment" do
setup do
rebuild_model :styles => { :large => "300x300>",
:medium => "100x100",
:thumb => ["32x32#", :gif] },
:default_style => :medium,
:url => "/:attachment/:class/:style/:id/:basename.:extension",
:path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
end
should "integrate" do
@dummy = Dummy.new
@file = File.new(File.join(FIXTURES_DIR, "5k.png"))
@bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"))
assert @dummy.avatar = @file
assert @dummy.valid?
assert @dummy.save
assert_equal "100x15", `identify -format "%wx%h" #{@dummy.avatar.to_io.path}`.chomp
assert_equal "434x66", `identify -format "%wx%h" #{@dummy.avatar.to_io(:original).path}`.chomp
assert_equal "300x46", `identify -format "%wx%h" #{@dummy.avatar.to_io(:large).path}`.chomp
assert_equal "100x15", `identify -format "%wx%h" #{@dummy.avatar.to_io(:medium).path}`.chomp
assert_equal "32x32", `identify -format "%wx%h" #{@dummy.avatar.to_io(:thumb).path}`.chomp
saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_io(s).path }
@d2 = Dummy.find(@dummy.id)
assert_equal "100x15", `identify -format "%wx%h" #{@dummy.avatar.to_io.path}`.chomp
assert_equal "434x66", `identify -format "%wx%h" #{@dummy.avatar.to_io(:original).path}`.chomp
assert_equal "300x46", `identify -format "%wx%h" #{@d2.avatar.to_io(:large).path}`.chomp
assert_equal "100x15", `identify -format "%wx%h" #{@d2.avatar.to_io(:medium).path}`.chomp
assert_equal "32x32", `identify -format "%wx%h" #{@d2.avatar.to_io(:thumb).path}`.chomp
@dummy.avatar = nil
assert_nil @dummy.avatar_file_name
assert @dummy.valid?
assert @dummy.save
saved_paths.each do |p|
assert ! File.exists?(p)
end
@d2 = Dummy.find(@dummy.id)
assert_nil @d2.avatar_file_name
@d2.avatar = @bad_file
assert ! @d2.valid?
@d2.avatar = nil
assert @d2.valid?
Dummy.validates_attachment_presence :avatar
@d3 = Dummy.find(@d2.id)
@d3.avatar = @file
assert @d3.valid?
@d3.avatar = @bad_file
assert ! @d3.valid?
@d3.avatar = nil
assert ! @d3.valid?
@dummy.avatar = @file
assert @dummy.save
@dummy.avatar = nil
assert_nil @dummy.avatar_file_name
@dummy.reload
assert_equal "5k.png", @dummy.avatar_file_name
end
end
end
require 'rubygems'
require 'test/unit'
require 'stringio'
require 'tempfile'
require 'shoulda'
require File.join(File.dirname(__FILE__), '..', 'lib', 'paperclip', 'iostream.rb')
class IOStreamTest < Test::Unit::TestCase
context "IOStream" do
should "be included in IO, File, Tempfile, and StringIO" do
[IO, File, Tempfile, StringIO].each do |klass|
assert klass.included_modules.include?(IOStream), "Not in #{klass}"
end
end
end
context "A file" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"))
end
context "that is sent #stream_to" do
[["/tmp/iostream.string.test", File],
[Tempfile.new('iostream.test'), Tempfile]].each do |args|
context "and given a #{args[0].class.to_s}" do
setup do
assert @result = @file.stream_to(args[0])
end
should "return a #{args[1].to_s}" do
assert @result.is_a?(args[1])
end
should "contain the same data as the original file" do
@file.rewind; @result.rewind
assert_equal @file.read, @result.read
end
end
end
end
context "that is sent #to_tempfile" do
setup do
assert @tempfile = @file.to_tempfile
end
should "convert it to a Tempfile" do
assert @tempfile.is_a?(Tempfile)
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
require 'test/unit' require 'test/helper.rb'
require File.dirname(__FILE__) + "/test_helper.rb"
class TestPaperclip < Test::Unit::TestCase class PaperclipTest < Test::Unit::TestCase
context "An ActiveRecord model with an 'avatar' attachment" do
context "Paperclip" do
should "allow overriding options" do
[:image_magick_path, :whiny_deletes, :whiny_thumbnails].each do |option|
not_expected = Paperclip.options[option]
Paperclip.options[option] = "123"
assert_equal "123", Paperclip.options[option]
assert_not_equal not_expected, Paperclip.options[option]
end
end
should "give the correct path for a command" do
expected = "/usr/bin/wtf"
Paperclip.options[:image_magick_path] = "/usr/bin"
assert_equal expected, Paperclip.path_for_command("wtf")
expected = "wtf"
Paperclip.options[:image_magick_path] = nil
assert_equal expected, Paperclip.path_for_command("wtf")
end
context "being used on improper class Improper" do
setup do setup do
ActiveRecord::Base.connection.create_table :impropers, :force => true do |table| rebuild_model
# Empty table
end end
Object.send(:remove_const, :Improper) rescue nil
class ::Improper < ActiveRecord::Base; end should "have an #avatar method" do
assert Dummy.new.respond_to?(:avatar)
end end
should "raises an error when an attachment is defined" do should "have an #avatar= method" do
assert_raises(Paperclip::PaperclipError){ Improper.has_attached_file :image } assert Dummy.new.respond_to?(:avatar=)
end end
[:file_name, :content_type].each do |column| [[:presence, nil, "5k.png", nil],
context "which has only the #{column} column" do [:size, {:in => 1..10240}, "5k.png", "12k.png"]].each do |args|
context "with #{args[0]} validations" do
setup do setup do
ActiveRecord::Base.connection.create_table :impropers, :force => true do |table| Dummy.class_eval do
table.column :"image_#{column}", :string send(*[:"validates_attachment_#{args[0]}", :avatar, args[1]].compact)
end
Object.send(:remove_const, :Improper) rescue nil
class ::Improper < ActiveRecord::Base; end
end
should "raises an error when an attachment is defined" do
assert_raises(Paperclip::PaperclipError){ Improper.has_attached_file :image }
end
end
end end
@dummy = Dummy.new
end end
context "being used on class Foo" do context "and a valid file" do
setup do setup do
ActiveRecord::Base.connection.create_table :foos, :force => true do |table| @file = args[2] && File.new(File.join(FIXTURES_DIR, args[2]))
table.column :image_file_name, :string end
table.column :image_content_type, :string
table.column :image_file_size, :integer
table.column :document_file_name, :string should "not have any errors" do
table.column :document_content_type, :string @dummy.avatar = @file
table.column :document_file_size, :integer assert @dummy.avatar.valid?
assert_equal 0, @dummy.avatar.errors.length
end end
Object.send(:remove_const, :Foo) rescue nil
class ::Foo < ActiveRecord::Base; end
end end
should "be able to assign a default attachment" do context "and an invalid file" do
assert Foo.has_attached_file(:image) setup do
assert_equal [:image], Foo.attached_files @file = args[3] && File.new(File.join(FIXTURES_DIR, args[3]))
foo = Foo.new
assert foo.respond_to?(:image)
assert foo.image.is_a?(Paperclip::Attachment)
end end
should "be able to assign two attachments separately" do should "have errors" do
assert Foo.has_attached_file(:image) @dummy.avatar = @file
assert Foo.has_attached_file(:document) assert ! @dummy.avatar.valid?
assert_equal [:image, :document], Foo.attached_files assert_equal 1, @dummy.avatar.errors.length
foo = Foo.new
assert foo.respond_to?(:image)
assert foo.respond_to?(:document)
assert foo.image.is_a?(Paperclip::Attachment)
assert foo.document.is_a?(Paperclip::Attachment)
assert foo.image != foo.document
end end
should "be able to assign two attachments simultaneously" do
assert Foo.has_attached_file(:image, :document)
assert_equal [:image, :document], Foo.attached_files
foo = Foo.new
assert foo.respond_to?(:image)
assert foo.respond_to?(:document)
assert foo.image.is_a?(Paperclip::Attachment)
assert foo.document.is_a?(Paperclip::Attachment)
assert foo.image != foo.document
end end
should "be able to set options on attachments" do
assert Foo.has_attached_file :image, :thumbnails => {:thumb => "100x100"}
assert_equal [:image], Foo.attached_files
assert_equal( {:thumb => "100x100"}, Foo.attachment_definition_for(:image).thumbnails )
foo = Foo.new
assert foo.respond_to?(:image)
assert foo.image.is_a?(Paperclip::Attachment)
end end
end end
end end
end end
require 'rubygems'
require 'test/unit'
require 'shoulda'
require 'mocha'
require 'tempfile'
require File.join(File.dirname(__FILE__), '..', 'lib', 'paperclip', 'geometry.rb')
require File.join(File.dirname(__FILE__), '..', 'lib', 'paperclip', 'thumbnail.rb')
class ThumbnailTest < Test::Unit::TestCase
context "A Paperclip Tempfile" do
setup do
@tempfile = Paperclip::Tempfile.new("file.jpg")
end
should "have its path contain a real extension" do
assert_equal ".jpg", File.extname(@tempfile.path)
end
should "be a real Tempfile" do
assert @tempfile.is_a?(::Tempfile)
end
end
context "Another Paperclip Tempfile" do
setup do
@tempfile = Paperclip::Tempfile.new("file")
end
should "not have an extension if not given one" do
assert_equal "", File.extname(@tempfile.path)
end
should "still be a real Tempfile" do
assert @tempfile.is_a?(::Tempfile)
end
end
context "An image" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"))
end
context "being thumbnailed at 100x50 with cropping" do
setup do
@thumb = Paperclip::Thumbnail.new(@file, "100x50#")
end
should "report its correct current and target geometries" do
assert_equal "100x50", @thumb.target_geometry.to_s
assert_equal "434x66", @thumb.current_geometry.to_s
end
should "report its correct format" do
assert_nil @thumb.format
end
should "have whiny_thumbnails turned on by default" do
assert @thumb.whiny_thumbnails
end
should "send the right command to convert when sent #make" do
@thumb.expects(:system).with do |arg|
arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}"\s+-scale\s+x50\s+-crop\s+100x50\+114\+0\s+\+repage\s+".*?"}
end
@thumb.make
end
should "create the thumbnail when sent #make" do
dst = @thumb.make
assert_match /100x50/, `identify #{dst.path}`
end
end
end
end
require 'test/unit'
require File.dirname(__FILE__) + "/test_helper.rb"
class TestThumbnailer < Test::Unit::TestCase
context "The Thumbnailer" do
should "calculate geometries for cropping images" do
@file = IO.read(File.join(File.dirname(__FILE__), "fixtures", "test_image.jpg"))
assert_equal ["50x", "50x25+0+10"], Paperclip::Thumbnail.new("50x25", @file).geometry_for_crop
assert_equal ["x50", "50x50+2+0"], Paperclip::Thumbnail.new("50x50", @file).geometry_for_crop
assert_equal ["x50", "25x50+14+0"], Paperclip::Thumbnail.new("25x50", @file).geometry_for_crop
end
should "be able to pipe commands" do
doc = %w(one two three four five)
expected = %w(five four one three two)
assert_equal expected, Paperclip::Thumbnail.piping(doc.join("\n"), "sort").split("\n")
assert_equal "Hello, World!\n", Paperclip::Thumbnail.piping("World", "ruby -e 'puts %Q{Hello, \#{STDIN.read}!}'")
end
[
[:square, 125, 125],
[:long, 53, 225],
[:tall, 225, 53],
[:tiny, 16, 16]
].each do |size, width, height|
context "given a #{size} image" do
setup do
@base_file = IO.read(File.join(File.dirname(__FILE__), "fixtures", "test_image.jpg"))
@file = Paperclip::Thumbnail.piping(@base_file, "convert - -scale '#{width}x#{height}!' -")
assert_match /#{width}x#{height}/, Paperclip::Thumbnailer.piping(@file, "identify -")
@targets = {
:small => "50x50",
:same => nil,
:large => "100x100",
:shrink_to_large => "100x100>",
:crop_medium => "75x75#"
}
end
should_eventually "generate correct thumbnails for the image"
end
end
end
end
\ No newline at end of file
require 'test/unit'
require File.dirname(__FILE__) + "/test_helper.rb"
class TestUpfile < Test::Unit::TestCase
context "Using Upfile" do
setup do
File.send :include, Paperclip::Upfile
@filename = File.join(File.dirname(__FILE__), "fixtures", "test_image.jpg")
@file = File.new(@filename)
end
should "allow File objects to respond as uploaded files do" do
assert_respond_to @file, :original_filename
assert_respond_to @file, :content_type
assert_respond_to @file, :size
assert_equal "test_image.jpg", @file.original_filename
assert_equal "image/jpg", @file.content_type
assert_equal @file.stat.size, @file.size
end
end
end
\ No newline at end of file
# Uninstall hook code here
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