Permalink
Browse files

Validations work as expected with invalid images.

git-svn-id: https://svn.thoughtbot.com/plugins/paperclip/trunk@245 7bbfaf0e-4d1d-0410-9690-a8bb5f8ef2aa
  • Loading branch information...
1 parent 11086ba commit f99b0fd5de35356cd09bf2c74207b318fb3dea36 jyurek committed Nov 15, 2007
Showing with 175 additions and 90 deletions.
  1. +1 −1 Rakefile
  2. +106 −81 lib/paperclip.rb
  3. +8 −1 test/simply_shoulda.rb
  4. +15 −7 test/test_attachment.rb
  5. +45 −0 test/test_thumbnailer.rb
View
@@ -24,7 +24,7 @@ end
desc 'Clean up files.'
task :clean do |t|
FileUtils.rm_rf "doc"
- FileUtils.rm_rf "repository"
+ FileUtils.rm_rf "test/public"
FileUtils.rm_rf "tmp"
FileUtils.rm "test/debug.log" rescue nil
FileUtils.rm "test/paperclip.db" rescue nil
View
@@ -78,7 +78,7 @@ def name
def styles
@styles ||= thumbnails.merge(:original => nil)
end
-
+
def thumbnails
@thumbnails ||= @options[:thumbnails] || {}
end
@@ -114,9 +114,18 @@ def initialize active_record, name, definition
@name = name
@errors = []
+ # class << @errors
+ # def push_with_log arg
+ # puts "Pushing '#{arg}' onto errors."
+ # push_without_log arg
+ # end
+ # alias_method :push_without_log, :<<
+ # alias_method :<<, :push_with_log
+ # end
+
clear_files
@dirty = true
-
+
self.original_filename = @instance["#{name}_file_name"]
self.content_type = @instance["#{name}_content_type"]
self.original_file_size = @instance["#{name}_file_size"]
@@ -129,11 +138,11 @@ def assign 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
+ self[:original] = uploaded_file.read
@dirty = true
if definition.attachment_type == :image
- make_thumbnails_from uploaded_file
+ make_thumbnails_from(self[:original])
end
end
@@ -153,8 +162,7 @@ def clear_files
def for_attached_files
@files.each do |style, data|
- data.rewind if data && data.respond_to?(:rewind)
- yield style, (data.respond_to?(:read) ? data.read : data)
+ yield style, data
end
end
@@ -204,10 +212,10 @@ def url style = nil
end
interpolate( style, pattern )
end
-
+
def read style = nil
style ||= definition.default_style
- self[style] ? (self[style].rewind && self[style].read) : IO.read(file_name(style))
+ self[style] ? self[style] : IO.read(file_name(style))
end
def validate_existence *constraints
@@ -239,11 +247,11 @@ def delete_attachment complain = false
begin
FileUtils.rm file_path if file_path
rescue SystemCallError => e
- raise PaperclipError, "Could not delete thumbnail." if Paperclip.options[:whiny_deletes] || complain
+ raise PaperclipError, "could not be deleted." if Paperclip.options[:whiny_deletes] || complain
end
end
end
-
+
def file_name style = nil
style ||= definition.default_style
interpolate( style, definition.path )
@@ -267,80 +275,17 @@ def ensure_directories
def make_thumbnails_from data
begin
definition.thumbnails.each do |style, geometry|
- self[style] = make_thumbnail geometry, data
+ self[style] = Thumbnail.make(geometry, data)
end
rescue PaperclipError => e
errors << e.message
clear_files
self[:original] = data
end
end
-
- protected
-
- 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 = 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, "could not be thumbnailed. Is ImageMagick or GraphicsMagick installed and available?"
- rescue SystemCallError => e
- raise PaperclipError, "could not be thumbnailed."
- end
- if Paperclip.options[:whiny_thumbnails] && !$?.success?
- raise PaperclipError, "could not be thumbnailed because of an error with 'convert'."
- end
- thumb
- end
-
- def geometry_for_crop geometry, orig_io
- identify = 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].to_i}", src[1] / dst[1] ]
- else
- [ "#{dst[0].to_i}x", src[0] / dst[0] ]
- end
- elsif dsth
- [ "#{dst[0].to_i}x", src[0] / dst[0] ]
- else
- [ "x#{dst[1].to_i}", 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
-
+
public
def interpolations
@@ -379,7 +324,7 @@ def original_file_size= new_size
def to_s
url
end
-
+
protected
def is_a_file? data
@@ -393,6 +338,86 @@ def sanitize_filename filename
end
end
+ class Thumbnail
+ attr_accessor :geometry, :data
+ def initialize geometry, data
+ @geometry, @data = geometry, data
+ end
+
+ def self.make geometry, data
+ new(geometry, data).make
+ end
+
+ def make
+ return data if geometry.nil?
+ operator = geometry[-1,1]
+ 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."
+ end
+ if Paperclip.options[:whiny_thumbnails] && !$?.success?
+ raise PaperclipError, "could not be thumbnailed because of an error with 'convert'."
+ end
+ thumb
+ end
+
+ def geometry_for_crop
+ identify = Paperclip.path_for_command("identify")
+ piping data, :to => "#{identify} - 2>/dev/null" do |pipeout|
+ dimensions = pipeout.split[2]
+ if dimensions && (match = dimensions.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].to_i}", src[1] / dst[1] ]
+ else
+ [ "#{dst[0].to_i}x", src[0] / dst[0] ]
+ end
+ elsif dsth
+ [ "#{dst[0].to_i}x", src[0] / dst[0] ]
+ else
+ [ "x#{dst[1].to_i}", 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 ]
+ else
+ raise PaperclipError, "does not contain a valid image."
+ end
+ end
+ end
+
+ def piping data, command, &block
+ self.class.piping(data, command, &block)
+ end
+
+ def self.piping data, command, &block
+ command = command[:to] if command.respond_to?(:[]) && command[:to]
+ block ||= lambda {|d| d }
+ IO.popen(command, "w+") do |io|
+ io.write(data)
+ io.close_write
+ block.call(io.read)
+ end
+ end
+ end
module ClassMethods
def has_attached_file *attachment_names
@@ -401,7 +426,7 @@ def has_attached_file *attachment_names
include InstanceMethods
after_save :save_attached_files
before_destroy :destroy_attached_files
-
+
#class_inheritable_hash :attachment_definitions
@attachment_definitions ||= {}
@attachment_names ||= []
@@ -424,7 +449,7 @@ def has_attached_file *attachment_names
def attached_files
@attachment_names
end
-
+
def attachment_definition_for attachment
@attachment_definitions[attachment]
end
@@ -434,7 +459,7 @@ def attachment_definition_for attachment
def validates_attached_file *attachment_names
validates_each *attachment_names do |record, name, attachment|
attachment.errors.each do |error|
- record.add(name, error)
+ record.errors.add(name, error)
end
end
end
@@ -453,13 +478,13 @@ def attachment_for name
@attachments ||= {}
@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!
View
@@ -24,10 +24,17 @@ def teardown &block
def should name, &test
context_setups = @context_setups.dup
context_teardowns = @context_teardowns.dup
- define_method(["test:", @contexts, "should", name].join(" ")) do
+ 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
View
@@ -8,12 +8,6 @@ class TestAttachment < Test::Unit::TestCase
@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
@@ -45,6 +39,7 @@ class ::Foo < ActiveRecord::Base; end
end
should "be able to retrieve the data as a blob" do
+ @file.rewind
assert_equal @file.read, @foo.image.read
end
@@ -78,7 +73,7 @@ class ::Foo < ActiveRecord::Base; end
end
should "have no errors" do
- assert @foo.image.errors.blank?
+ assert @foo.image.errors.blank?, @foo.image.errors.inspect
assert @foo.errors.blank?
end
@@ -108,6 +103,19 @@ class ::Foo < ActiveRecord::Base; end
end
context "with an invalid image attached to :image" do
+ setup do
+ assert Foo.has_attached_file(:image, :thumbnails => {:small => "16x16", :medium => "100x100", :large => "250x250", :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
+
+ should "not save" do
+ assert !@foo.save
+ assert @foo.errors.on(:image)
+ assert @foo.errors.on(:image).any?{|e| e.match(/does not contain a valid image/) }
+ end
end
end
end
Oops, something went wrong.

0 comments on commit f99b0fd

Please sign in to comment.