Commit 11086ba6 by jyurek

Re-refactoring, plus the beginning of lots of tests.

git-svn-id: https://svn.thoughtbot.com/plugins/paperclip/trunk@242 7bbfaf0e-4d1d-0410-9690-a8bb5f8ef2aa
parent 26444ced
...@@ -8,7 +8,7 @@ task :default => [:clean, :test] ...@@ -8,7 +8,7 @@ task :default => [:clean, :test]
desc 'Test the paperclip plugin.' desc 'Test the paperclip plugin.'
Rake::TestTask.new(:test) do |t| Rake::TestTask.new(:test) do |t|
t.libs << 'lib' << 'profile' t.libs << 'lib' << 'profile'
t.pattern = 'test/**/*_test.rb' t.pattern = 'test/**/test_*.rb'
t.verbose = true t.verbose = true
end end
......
...@@ -56,8 +56,8 @@ module Paperclip ...@@ -56,8 +56,8 @@ module Paperclip
class AttachmentDefinition class AttachmentDefinition
def self.defaults def self.defaults
@defaults ||= { @defaults ||= {
:path => ":rails_root/public/:class/:attachment/:id/:style_:name", :path => ":rails_root/public/:class/:attachment/:id/:style_:filename",
:url => "/:class/:attachment/:id/:style_:name", :url => "/:class/:attachment/:id/:style_:filename",
:missing_url => "/:class/:attachment/:style_missing.png", :missing_url => "/:class/:attachment/:style_missing.png",
:attachment_type => :image, :attachment_type => :image,
:thumbnails => {}, :thumbnails => {},
...@@ -80,7 +80,7 @@ module Paperclip ...@@ -80,7 +80,7 @@ module Paperclip
end end
def thumbnails def thumbnails
@thumbnails ||= @options[:thumbnails] @thumbnails ||= @options[:thumbnails] || {}
end end
def validate thing, *constraints def validate thing, *constraints
...@@ -90,8 +90,8 @@ module Paperclip ...@@ -90,8 +90,8 @@ module Paperclip
def validations def validations
@validations ||= @options.inject({}) do |valids, opts| @validations ||= @options.inject({}) do |valids, opts|
key, val = opts key, val = opts
if (m = key.to_s.match(/^validate_(.+)/)) if (m = key.to_s.match(/^validates?_(.+)/))
valids[m[1]] = val valids[m[1].to_sym] = val
end end
valids valids
end end
...@@ -108,9 +108,9 @@ module Paperclip ...@@ -108,9 +108,9 @@ module Paperclip
class Attachment class Attachment
attr_reader :name, :instance, :original_filename, :content_type, :original_file_size, :definition, :errors attr_reader :name, :instance, :original_filename, :content_type, :original_file_size, :definition, :errors
def initialize name, active_record, definition def initialize active_record, name, definition
@instance = active_record @instance = active_record
@definition = defintiion @definition = definition
@name = name @name = name
@errors = [] @errors = []
...@@ -132,7 +132,7 @@ module Paperclip ...@@ -132,7 +132,7 @@ module Paperclip
self[:original] = uploaded_file self[:original] = uploaded_file
@dirty = true @dirty = true
if definition.type == :image if definition.attachment_type == :image
make_thumbnails_from uploaded_file make_thumbnails_from uploaded_file
end end
end end
...@@ -147,7 +147,7 @@ module Paperclip ...@@ -147,7 +147,7 @@ module Paperclip
def clear_files def clear_files
@files = {} @files = {}
definition.styles.each{|style| @files[style] = nil } definition.styles.each{|style, geo| @files[style] = nil }
@dirty = false @dirty = false
end end
...@@ -180,17 +180,18 @@ module Paperclip ...@@ -180,17 +180,18 @@ module Paperclip
clear_files clear_files
end end
def queue_destroy(complain = false) def destroy(complain = false)
returning true do returning true do
@delete_on_save = true @delete_on_save = true
@complain_on_delete = complain @complain_on_delete = complain
self.original_filename = nil self.original_filename = nil
self.content_type = nil self.content_type = nil
self.original_file_size = nil
clear_files clear_files
end end
end end
def destroy def destroy!
delete_attachment if definition.delete_on_destroy delete_attachment if definition.delete_on_destroy
end end
...@@ -205,7 +206,8 @@ module Paperclip ...@@ -205,7 +206,8 @@ module Paperclip
end end
def read style = nil def read style = nil
self[style] ? self[style].read : IO.read(file_name(style)) style ||= definition.default_style
self[style] ? (self[style].rewind && self[style].read) : IO.read(file_name(style))
end end
def validate_existence *constraints def validate_existence *constraints
...@@ -296,7 +298,7 @@ module Paperclip ...@@ -296,7 +298,7 @@ module Paperclip
raise PaperclipError, "could not be thumbnailed." raise PaperclipError, "could not be thumbnailed."
end end
if Paperclip.options[:whiny_thumbnails] && !$?.success? if Paperclip.options[:whiny_thumbnails] && !$?.success?
raise PaperclipError, "could not be thumbaniled because of an error with 'convert'." raise PaperclipError, "could not be thumbnailed because of an error with 'convert'."
end end
thumb thumb
end end
...@@ -316,14 +318,14 @@ module Paperclip ...@@ -316,14 +318,14 @@ module Paperclip
scale_geometry, scale = if dst[0] == dst[1] scale_geometry, scale = if dst[0] == dst[1]
if srch if srch
[ "x#{dst[1]}", src[1] / dst[1] ] [ "x#{dst[1].to_i}", src[1] / dst[1] ]
else else
[ "#{dst[0]}x", src[0] / dst[0] ] [ "#{dst[0].to_i}x", src[0] / dst[0] ]
end end
elsif dsth elsif dsth
[ "#{dst[0]}x", src[0] / dst[0] ] [ "#{dst[0].to_i}x", src[0] / dst[0] ]
else else
[ "x#{dst[1]}", src[1] / dst[1] ] [ "x#{dst[1].to_i}", src[1] / dst[1] ]
end end
crop_geometry = if dsth crop_geometry = if dsth
...@@ -348,14 +350,16 @@ module Paperclip ...@@ -348,14 +350,16 @@ module Paperclip
:class => lambda{|style| self.instance.class.to_s.underscore.pluralize }, :class => lambda{|style| self.instance.class.to_s.underscore.pluralize },
:style => lambda{|style| style.to_s }, :style => lambda{|style| style.to_s },
:attachment => lambda{|style| self.name.to_s.pluralize }, :attachment => lambda{|style| self.name.to_s.pluralize },
:filename => lambda{|style| self.original_filename } :filename => lambda{|style| self.original_filename },
:basename => lambda{|style| self.original_filename.gsub(/\..*$/, "") },
:extension => lambda{|style| self.original_filename.gsub(/^.*./, "") }
} }
end end
def interpolate style, source def interpolate style, source
returning source.dup do |s| returning source.dup do |s|
interpolations.each do |key, proc| interpolations.each do |key, proc|
s.gsub!(/:#{key}/){ proc.call(instance, style) } s.gsub!(/:#{key}/){ proc.call(style) }
end end
end end
end end
...@@ -395,8 +399,13 @@ module Paperclip ...@@ -395,8 +399,13 @@ module Paperclip
options = attachment_names.last.is_a?(Hash) ? attachment_names.pop : {} options = attachment_names.last.is_a?(Hash) ? attachment_names.pop : {}
include InstanceMethods include InstanceMethods
after_save :save_attached_files
before_destroy :destroy_attached_files
#class_inheritable_hash :attachment_definitions #class_inheritable_hash :attachment_definitions
@attachment_definitions ||= {} @attachment_definitions ||= {}
@attachment_names ||= []
@attachment_names += attachment_names
attachment_names.each do |aname| attachment_names.each do |aname|
whine_about_columns_for aname whine_about_columns_for aname
...@@ -413,7 +422,7 @@ module Paperclip ...@@ -413,7 +422,7 @@ module Paperclip
end end
def attached_files def attached_files
@attachment_definitions.keys @attachment_names
end end
def attachment_definition_for attachment def attachment_definition_for attachment
...@@ -442,7 +451,19 @@ module Paperclip ...@@ -442,7 +451,19 @@ module Paperclip
module InstanceMethods #:nodoc: module InstanceMethods #:nodoc:
def attachment_for name def attachment_for name
@attachments ||= {} @attachments ||= {}
@attachments[name] ||= Attachment.new(self, name) @attachments[name] ||= Attachment.new(self, name, self.class.attachment_definition_for(name))
end
def save_attached_files
@attachments.each do |name, attachment|
attachment.save
end
end
def destroy_attached_files
@attachments.each do |name, attachment|
attachment.destroy!
end
end end
end end
...@@ -462,7 +483,7 @@ module Paperclip ...@@ -462,7 +483,7 @@ module Paperclip
# Returns the file's normal name. # Returns the file's normal name.
def original_filename def original_filename
self.path File.basename(self.path)
end end
# Returns the size of the file. # Returns the size of the file.
......
module Thoughtbot
module Paperclip
class Attachment
attr_reader :name, :instance, :original_filename, :content_type, :original_file_size, :definition, :errors
def initialize name, active_record
@instance = active_record
@definition = @instance.class.attachment_definitions[name]
@name = name
@errors = []
self.original_filename = @instance["#{name}_file_name"]
self.content_type = @instance["#{name}_content_type"]
self.original_file_size = @instance["#{name}_file_size"]
@files = {}
@dirty = false
self.class.send :include, definition.storage_module
end
def assign uploaded_file
uploaded_file = fetch_uri(uploaded_file) if uploaded_file.is_a? URI
return queue_destroy if uploaded_file.nil?
return unless is_a_file? uploaded_file
self.original_filename = sanitize_filename(uploaded_file.original_filename)
self.content_type = uploaded_file.content_type
self.original_file_size = uploaded_file.size
self[:original] = uploaded_file
@dirty = true
if definition.type == :image
make_thumbnails_from uploaded_file
end
end
def [](style)
@files ||= {}
@files[style]
end
alias_method :file_for, :[]
def []=(style, data)
@files ||= {}
@files[style] = data
end
def clear_files
@files = nil
@dirty = false
end
def for_attached_files
@files.each do |style, data|
if data
data.rewind if data.respond_to? :rewind
yield style, (data.respond_to?(:read) ? data.read : data)
end
end
end
def dirty?
@dirty
end
# Validations
def valid?
definition.validations.each do |validation, constraints|
send("validate_#{validation}", *constraints)
end
errors.empty?
end
# ActiveRecord Callbacks
def save
write_attachment if dirty?
delete_attachment if @delete_on_save
@delete_on_save = false
clear_files
end
def queue_destroy(complain = false)
returning true do
@delete_on_save = true
@complain_on_delete = complain
self.original_filename = nil
self.content_type = nil
clear_files
end
end
def destroy
delete_attachment if definition.delete_on_destroy
end
# Image Methods
def make_thumbnails_from data
begin
definition.styles.each do |style, geometry|
self[style] = make_thumbnail geometry, data
end
rescue PaperclipError => e
errors << e.message
clear_files
self[:original] = data
end
end
def make_thumbnail geometry, data
return data if geometry.nil?
operator = geometry[-1,1]
begin
geometry, crop_geometry = geometry_for_crop(geometry, data) if operator == '#'
convert = Thoughtbot::Paperclip.path_for_command("convert")
command = "#{convert} - -scale '#{geometry}' #{operator == '#' ? "-crop '#{crop_geometry}'" : ""} - 2>/dev/null"
thumb = IO.popen(command, "w+") do |io|
data.rewind
io.write(data.read)
io.close_write
StringIO.new(io.read)
end
rescue Errno::EPIPE => e
raise PaperclipError.new(self), "could not be thumbnailed. Is ImageMagick or GraphicsMagick installed and available?"
rescue SystemCallError => e
raise PaperclipError.new(self), "could not be thumbnailed."
end
if Thoughtbot::Paperclip.options[:whiny_thumbnails] && !$?.success?
raise PaperclipError.new(self), "could not be thumbaniled because of an error with 'convert'."
end
thumb
end
def geometry_for_crop geometry, orig_io
identify = Thoughtbot::Paperclip.path_for_command("identify")
IO.popen("#{identify} -", "w+") do |io|
orig_io.rewind
io.write(orig_io.read)
io.close_write
if match = io.read.split[2].match(/(\d+)x(\d+)/)
src = match[1,2].map(&:to_f)
srch = src[0] > src[1]
dst = geometry.match(/(\d+)x(\d+)/)[1,2].map(&:to_f)
dsth = dst[0] > dst[1]
ar = src[0] / src[1]
scale_geometry, scale = if dst[0] == dst[1]
if srch
[ "x#{dst[1]}", src[1] / dst[1] ]
else
[ "#{dst[0]}x", src[0] / dst[0] ]
end
elsif dsth
[ "#{dst[0]}x", src[0] / dst[0] ]
else
[ "x#{dst[1]}", src[1] / dst[1] ]
end
crop_geometry = if dsth
"%dx%d+%d+%d" % [ dst[0], dst[1], 0, (src[1] / scale - dst[1]) / 2 ]
else
"%dx%d+%d+%d" % [ dst[0], dst[1], (src[0] / scale - dst[0]) / 2, 0 ]
end
[ scale_geometry, crop_geometry ]
end
end
end
# Helper Methods
def interpolate style, source
returning source.dup do |s|
s.gsub!(/:rails_root/, RAILS_ROOT)
s.gsub!(/:id/, instance.id.to_s) if instance.id
s.gsub!(/:class/, instance.class.to_s.underscore.pluralize)
s.gsub!(/:style/, style.to_s)
s.gsub!(/:attachment/, name.to_s.pluralize)
if original_filename
file_bits = original_filename.split(".")
s.gsub!(/:name/, original_filename)
s.gsub!(/:base/, [file_bits[0], *file_bits[1..-2]].join("."))
s.gsub!(/:ext/, file_bits.last )
end
end
end
def original_filename= new_name
instance["#{name}_file_name"] = @original_filename = new_name
end
def content_type= new_type
instance["#{name}_content_type"] = @content_type = new_type
end
def original_file_size= new_size
instance["#{name}_file_size"] = @original_file_size = new_size
end
def fetch_uri uri
image = if uri.scheme == 'file'
path = url.gsub(%r{^file://}, '/')
open(path)
else
require 'open-uri'
uri
end
begin
data = StringIO.new(image.read)
uri.extend(Upfile)
class << data
attr_accessor :original_filename, :content_type
end
data.original_filename = uri.original_filename
data.content_type = uri.content_type
data
rescue OpenURI::HTTPError => e
errors << "The file at #{uri.to_s} could not be found."
return nil
end
end
def is_a_file? data
[:size, :content_type, :original_filename, :read].map do |meth|
data.respond_to? meth
end.all?
end
def sanitize_filename filename
File.basename(filename).gsub(/[^\w\.\_]/,'_')
end
protected :sanitize_filename
def to_s
url
end
end
end
end
\ No newline at end of file
module Thoughtbot
module Paperclip
class AttachmentDefinition
def self.defaults
{
:path_prefix => ":rails_root/public",
:url_prefix => "",
:path => ":attachment/:id/:style_:name",
:url => nil,
:attachment_type => :image,
:thumbnails => {},
:delete_on_destroy => true,
:default_style => :original,
:missing_url => "",
:missing_path => "",
:storage => :filesystem
}
end
def initialize name, options
@name = name
@options = AttachmentDefinition.defaults.merge options
end
def name
@name
end
def styles
unless @styles
@styles = @options[:thumbnails]
@styles[:original] = nil
end
@styles
end
def validate thing, *constraints
@options[:"validate_#{thing}"] = (constraints.length == 1 ? constraints.first : constraints)
end
def validations
@validations ||= @options.inject({}) do |valids, opts|
key, val = opts
if (m = key.to_s.match(/^validate_(.+)/))
valids[m[1]] = val
end
valids
end
end
def storage_module
@storage_module ||= Thoughtbot::Paperclip::Storage.const_get(@options[:storage].to_s.camelize)
end
def type
@options[:attachment_type]
end
def default_style
@options[:default_style]
end
def path_prefix
@options[:path_prefix]
end
def url_prefix
@options[:url_prefix]
end
def path
@options[:path]
end
def url
@options[:url]
end
def missing_file_name
@options[:missing_path]
end
def missing_url
@options[:missing_url]
end
def delete_on_destroy
@options[:delete_on_destroy]
end
end
end
end
\ No newline at end of file
module Thoughtbot
module Paperclip
module Storage
# == Filesystem
# Typically, Paperclip stores your files in the filesystem, so that Apache (or whatever your
# main asset server is) can easily access your files without Rails having to be called all
# the time.
module Filesystem
def file_name style = nil
style ||= definition.default_style
pattern = if original_filename && instance.id
File.join(definition.path_prefix, definition.path)
else
definition.missing_file_name
end
interpolate( style, pattern )
end
def url style = nil
style ||= definition.default_style
pattern = if original_filename && instance.id
[definition.url_prefix, definition.url || definition.path].compact.join("/")
else
definition.missing_url
end
interpolate( style, pattern )
end
def write_attachment
ensure_directories
for_attached_files do |style, data|
File.open( file_name(style), "w" ) do |file|
file.rewind
file.write(data)
end
end
end
def delete_attachment complain = false
definition.styles.keys.each do |style|
file_path = file_name(style)
begin
FileUtils.rm file_path if file_path
rescue SystemCallError => e
raise PaperclipError.new(self), "Could not delete thumbnail." if Thoughtbot::Paperclip.options[:whiny_deletes] || complain
end
end
end
def file_exists?(style)
style ||= definition.default_style
dirty? ? file_for(style) : File.exists?( file_name(style) )
end
def validate_existence *constraints
definition.styles.keys.each do |style|
errors << "requires a valid #{style} file." unless file_exists?(style)
end
end
def validate_size *constraints
errors << "file too large. Must be under #{constraints.last} bytes." if original_file_size > constraints.last
errors << "file too small. Must be over #{constraints.first} bytes." if original_file_size <= constraints.first
end
private
def ensure_directories
for_attached_files do |style, file|
dirname = File.dirname( file_name(style) )
FileUtils.mkdir_p dirname
end
end
end
end
end
end
\ No newline at end of file
module Thoughtbot
module Paperclip
module ClassMethods
def has_attached_file_with_s3 *attachment_names
has_attached_file_without_s3 *attachment_names
access_key = secret_key = ""
if file_name = s3_credentials_file
creds = YAML.load_file(file_name)
creds = creds[RAILS_ENV] || creds if Object.const_defined?("RAILS_ENV")
access_key = creds['access_key_id']
secret_key = creds['secret_access_key']
else
access_key = Thoughtbot::Paperclip.options[:s3_access_key_id]
secret_key = Thoughtbot::Paperclip.options[:s3_secret_access_key]
end
if definition.storage_module == Thoughtbot::Paperclip::Storage::S3
require 'aws/s3'
AWS::S3::Base.establish_connection!(
:access_key_id => access_key,
:secret_access_key => secret_key,
:persistent => Thoughtbot::Paperclip.options[:s3_persistent] || true
)
end
end
alias_method_chain :has_attached_file, :s3
private
def s3_credentials_file
[ Thoughtbot::Paperclip.options[:s3_credentials_file], File.join(RAILS_ROOT, "config", "s3.yml") ].compact.each do |f|
return f if File.exists?(f)
end
nil
end
end
class AttachmentDefinition
def s3_access
@options[:s3_access_privilege]
end
end
module Storage
# == Amazon S3 Storage
# Paperclip can store your files in Amazon's S3 Web Service. You can keep your keys in an s3.yml
# file inside the +config+ directory, similarly to your database.yml.
#
# access_key_id: 12345
# secret_access_key: 212434...4656456
#
# You can also environment-namespace the entries like you would in your database.yml:
#
# development:
# access_key_id: 12345
# secret_access_key: 212434...4656456
# production:
# access_key_id: abcde
# secret_access_key: gbkjhg...wgbrtjh
#
# The location of this file is configurable. You don't even have to use it if you don't want. Both
# the file's location or the keys themselves may be located in the Thoughtbot::Paperclip.options
# hash. The S3-related options in this hash are all prefixed with "s3".
#
# Thoughtbot::Paperclip.options = {
# :s3_persistent => true,
# :s3_credentials_file => "/home/me/.private/s3.yml"
# }
#
# This configuration is best placed in your +environment.rb+ or env-specific file.
# The complete list of options is as follows:
# * s3_access_key_id: The Amazon S3 ID you were given to access your account.
# * s3_secret_access_key: The secret key supplied by Amazon. This should be kept far away from prying
# eyes, which is why it's suggested that you keep these keys in a separate file that
# only you and the database can read.
# * s3_credentials_file: The path to the file where your credentials are kept in YAML format, as
# described above.
# * s3_persistent: Maintains an HTTP connection to the Amazon service if possible.
module S3
def file_name style = nil
style ||= definition.default_style
pattern = if original_filename && instance.id
definition.url
else
definition.missing_url
end
interpolate( style, pattern )
end
def url style = nil
"http://s3.amazonaws.com/#{bucket}/#{file_name(style)}"
end
def write_attachment
bucket = ensure_bucket
for_attached_files do |style, data|
AWS::S3::S3Object.store( file_name(style), data, bucket, :access => definition.s3_access || :public_read )
end
end
def delete_attachment complain = false
for_attached_files do |style, data|
begin
AWS::S3::S3Object.delete( file_name(style), bucket )
rescue AWS::S3::ResponseError => error
raise
end
end
end
def file_exists?(style)
style ||= definition.default_style
dirty? ? file_for(style) : AWS::S3::S3Object.exists?( file_name(style), bucket )
end
def validate_existence *constraints
definition.styles.keys.each do |style|
errors << "requires a valid #{style} file." unless file_exists?(style)
end
end
def validate_size *constraints
errors << "file too large. Must be under #{constraints.last} bytes." if original_file_size > constraints.last
errors << "file too small. Must be over #{constraints.first} bytes." if original_file_size <= constraints.first
end
private
def bucket
interpolate(nil, definition.url_prefix)
end
def ensure_bucket
begin
bucket_name = bucket
AWS::S3::Bucket.create(bucket_name)
bucket_name
rescue AWS::S3::S3Exception => e
raise Thoughtbot::Paperclip::PaperclipError.new(attachment), "You are not allowed access to the bucket '#{bucket_name}'."
end
end
end
end
end
end
\ No newline at end of file
module Thoughtbot
module Paperclip
# The Upfile module is a convenience module for adding uploaded-file-looking methods
# to the +File+ class. Useful for testing.
# f = File.new("test/test_avatar.jpg")
# f.original_filename # => "test_avatar.jpg"
# f.content_type # => "image/jpg"
# user.avatar = f
module Upfile
# Infer the MIME-type of the file from the extension.
def content_type
type = self.path.match(/\.(\w+)$/)[1]
case type
when "jpg", "png", "gif" then "image/#{type}"
when "txt", "csv", "xml", "html", "htm" then "text/#{type}"
else "x-application/#{type}"
end
end
# Returns the file's normal name.
def original_filename
self.path
end
# Returns the size of the file.
def size
File.size(self)
end
end
end
end
File.send(:include, Thoughtbot::Paperclip::Upfile)
\ No newline at end of file
begin
ActiveRecord::Base.connection.create_table :foos, :force => true do |table|
table.column :image_file_name, :string
table.column :image_content_type, :string
table.column :image_file_size, :integer
end
ActiveRecord::Base.connection.create_table :bars, :force => true do |table|
table.column :document_file_name, :string
table.column :document_content_type, :string
table.column :document_file_size, :integer
end
ActiveRecord::Base.connection.create_table :non_standards, :force => true do |table|
table.column :resume_file_name, :string
table.column :resume_content_type, :string
table.column :resume_file_size, :integer
table.column :avatar_file_name, :string
table.column :avatar_content_type, :string
table.column :avatar_file_size, :integer
end
ActiveRecord::Base.connection.create_table :ess_threes, :force => true do |table|
table.column :resume_file_name, :string
table.column :resume_content_type, :string
table.column :resume_file_size, :integer
table.column :avatar_file_name, :string
table.column :avatar_content_type, :string
table.column :avatar_file_size, :integer
end
ActiveRecord::Base.connection.create_table :negatives, :force => true do |table|
table.column :this_is_the_wrong_name_file_name, :string
end
rescue Exception
end
class Foo < ActiveRecord::Base
has_attached_file :image, :attachment_type => :image,
:thumbnails => { :thumb => "100x100>", :medium => "300x300>" },
:path_prefix => "./repository"
end
class Bar < ActiveRecord::Base
has_attached_file :document, :attachment_type => :document,
:path_prefix => "./repository"
validates_attached_file :document
end
class NonStandard < ActiveRecord::Base
has_attached_file :resume, :attachment_type => :document,
:path_prefix => "/tmp",
:path => ":attachment_:id_:name",
:missing_url => "/:class/:style/:attachment/404.txt"
has_attached_file :avatar, :attachment_type => :image,
:thumbnails => { :cropped => "200x10#",
:bigger => "1000x1000",
:smaller => "200x200>",
:square => "150x150#" },
:path_prefix => "./repository",
:path => ":class/:attachment/:id/:style_:name",
:default_style => :square,
:missing_url => "/:class/:style/:attachment/404.png"
end
# class EssThree < ActiveRecord::Base
# has_attached_file :resume, :attachment_type => :document,
# :path_prefix => "paperclip/test",
# :path => ":attachment_:id_:name",
# :missing_url => "/:class/:style/:attachment/404.txt",
# :storage => :S3
# has_attached_file :avatar, :attachment_type => :image,
# :thumbnails => { :cropped => "200x10#",
# :bigger => "1000x1000",
# :smaller => "200x200>",
# :square => "150x150#" },
# :path_prefix => "paperclip/test/images",
# :path => ":class/:attachment/:id/:style_:name",
# :default_style => :square,
# :missing_url => "/:class/:style/:attachment/404.png",
# :storage => :S3
# end
class Negative < ActiveRecord::Base
end
\ No newline at end of file
require 'test/unit'
require File.dirname(__FILE__) + "/test_helper.rb"
class TestAttachment < Test::Unit::TestCase
context "An attachment" do
setup do
@dummy = {}
@definition = Paperclip::AttachmentDefinition.new("thing", {})
@attachment = Paperclip::Attachment.new(@dummy, "thing", @definition)
end
should "calculate geometries for cropping images" do
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "test_image.jpg"))
assert_equal ["50x", "50x25+0+10"], @attachment.send(:geometry_for_crop, "50x25", @file)
assert_equal ["x50", "50x50+2+0"], @attachment.send(:geometry_for_crop, "50x50", @file)
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
assert_equal @file.read, @foo.image.read
end
context "and saved" do
setup do
assert @foo.save
end
should "have no errors" do
assert @foo.image.errors.blank?
assert @foo.errors.blank?
end
should "have a file 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_match /405x375/, `identify '#{@foo.image.send(:file_name)}'`
assert_equal IO.read(@file.path), @foo.image.read
end
end
end
context "with an image with thumbnails attached to :image and saved" do
setup do
assert Foo.has_attached_file(:image, :thumbnails => {:small => "16x16", :medium => "100x100", :large => "250x250", :square => "32x32#"})
@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?
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
context "with an image with thumbnails attached to :image and a document attached to :document" do
end
context "with an invalid image attached to :image" do
end
end
end
\ No newline at end of file
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!,
:thumbnails => {: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.each do |key|
assert_equal @options[key], @def.send(key)
end
end
should "return styles as thumbnails plus the original" do
assert( (@def.thumbnails.keys + [:original]).map(&:to_s).sort == @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
...@@ -5,6 +5,9 @@ require 'active_record/fixtures' ...@@ -5,6 +5,9 @@ require 'active_record/fixtures'
require 'fileutils' require 'fileutils'
require 'pp' 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')) config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log") ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
ActiveRecord::Base.establish_connection(config[ENV['RAILS_ENV'] || 'test']) ActiveRecord::Base.establish_connection(config[ENV['RAILS_ENV'] || 'test'])
......
require 'test/unit' require 'test/unit'
require File.dirname(__FILE__) + "/test_helper.rb" require File.dirname(__FILE__) + "/test_helper.rb"
require File.dirname(__FILE__) + "/simply_shoulda.rb"
require File.dirname(__FILE__) + "/../init.rb"
class PaperclipTest < Test::Unit::TestCase class TestPaperclip < Test::Unit::TestCase
context "Paperclip" do context "Paperclip" do
should "allow overriding options" do should "allow overriding options" do
...@@ -16,6 +14,38 @@ class PaperclipTest < Test::Unit::TestCase ...@@ -16,6 +14,38 @@ class PaperclipTest < Test::Unit::TestCase
expected = "/usr/bin/wtf" expected = "/usr/bin/wtf"
Paperclip.options[:image_magick_path] = "/usr/bin" Paperclip.options[:image_magick_path] = "/usr/bin"
assert_equal expected, Paperclip.path_for_command("wtf") 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 class Improper" do
setup do
ActiveRecord::Base.connection.create_table :impropers, :force => true do |table|
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
[:file_name, :content_type].each do |column|
context "which has only the #{column} column" do
setup do
ActiveRecord::Base.connection.create_table :impropers, :force => true do |table|
table.column :"image_#{column}", :string
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 end
context "being used on class Foo" do context "being used on class Foo" do
...@@ -36,23 +66,41 @@ class PaperclipTest < Test::Unit::TestCase ...@@ -36,23 +66,41 @@ class PaperclipTest < Test::Unit::TestCase
should "be able to assign a default attachment" do should "be able to assign a default attachment" do
assert Foo.has_attached_file(:image) assert Foo.has_attached_file(:image)
assert_equal [:image], Foo.attached_files assert_equal [:image], Foo.attached_files
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 "be able to assign two attachments separately" do
assert Foo.has_attached_file(:image) assert Foo.has_attached_file(:image)
assert Foo.has_attached_file(:document) assert Foo.has_attached_file(:document)
assert_equal [:image, :document], Foo.attached_files 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 assign two attachments simultaneously" do should "be able to assign two attachments simultaneously" do
assert Foo.has_attached_file(:image, :document) assert Foo.has_attached_file(:image, :document)
assert_equal [:image, :document], Foo.attached_files 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 should "be able to set options on attachments" do
assert Foo.has_attached_file :image, :thumbnails => {:thumb => "100x100"} assert Foo.has_attached_file :image, :thumbnails => {:thumb => "100x100"}
assert_equal [:image], Foo.attached_files assert_equal [:image], Foo.attached_files
assert_equal( {:thumb => "100x100"}, Foo.attachment_definition_for(:image).thumbnails ) 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
......
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
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