norman-has_image 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -0
- data/README.textile +193 -0
- data/Rakefile +3 -1
- data/init.rb +1 -1
- data/lib/has_image.rb +152 -104
- data/lib/has_image/processor.rb +58 -31
- data/lib/has_image/storage.rb +72 -45
- data/test/storage_test.rb +44 -2
- data/test_rails/complex_pic_test.rb +42 -0
- data/test_rails/pic_test.rb +72 -9
- data/test_rails/schema.rb +7 -1
- data/test_rails/test_helper.rb +1 -3
- metadata +6 -9
- data/README +0 -161
- data/test_rails/pic.rb +0 -3
data/lib/has_image/processor.rb
CHANGED
@@ -9,24 +9,32 @@ module HasImage
|
|
9
9
|
|
10
10
|
class << self
|
11
11
|
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
12
|
+
# The form of an {extended geometry string}[http://www.imagemagick.org/script/command-line-options.php?#resize] is:
|
13
|
+
#
|
14
|
+
# <width>x<height>{+-}<xoffset>{+-}<yoffset>{%}{!}{<}{>}
|
15
15
|
def geometry_string_valid?(string)
|
16
16
|
string =~ /\A[\d]*x[\d]*([+-][0-9][+-][0-9])?[%@!<>^]?\Z/
|
17
17
|
end
|
18
18
|
|
19
|
-
# Arg should be either a file
|
20
|
-
# "identify" command and looks for an exit status indicating an error.
|
21
|
-
# there is no error, then ImageMagick has identified the file as
|
22
|
-
# it can work with and it will be converted to the desired
|
19
|
+
# Arg should be either a file or a path. This runs ImageMagick's
|
20
|
+
# "identify" command and looks for an exit status indicating an error.
|
21
|
+
# If there is no error, then ImageMagick has identified the file as
|
22
|
+
# something it can work with and it will be converted to the desired
|
23
|
+
# output format.
|
24
|
+
#
|
25
|
+
# Note that this is used in place of any mimetype checks. HasImage
|
26
|
+
# assumes that is ImageMagick is able to process the file, then it's OK.
|
27
|
+
# Since ImageMagick can be compiled with many different options for
|
28
|
+
# supporting various file types (or not) this is safer and more complete
|
29
|
+
# than checking the mime type.
|
23
30
|
def valid?(arg)
|
24
31
|
arg.close if arg.respond_to?(:close) && !arg.closed?
|
25
32
|
silence_stderr do
|
26
33
|
`identify #{arg.respond_to?(:path) ? arg.path : arg.to_s}`
|
27
34
|
$? == 0
|
28
35
|
end
|
29
|
-
end
|
36
|
+
end
|
37
|
+
|
30
38
|
end
|
31
39
|
|
32
40
|
# The constuctor should be invoked with the options set by has_image.
|
@@ -34,25 +42,47 @@ module HasImage
|
|
34
42
|
@options = options
|
35
43
|
end
|
36
44
|
|
37
|
-
#
|
38
|
-
# format if necessary.
|
39
|
-
#
|
40
|
-
|
41
|
-
|
45
|
+
# Creates the resized image, and transforms it to the desired output
|
46
|
+
# format if necessary.
|
47
|
+
#
|
48
|
+
# +size+ should be a valid ImageMagick {geometry string}[http://www.imagemagick.org/script/command-line-options.php#resize].
|
49
|
+
# +format+ should be an image format supported by ImageMagick, e.g. "PNG", "JPEG"
|
50
|
+
# If a block is given, it yields the processed image file as a file-like object using IO#read.
|
51
|
+
def process(file, size = options[:resize_to], format = options[:convert_to])
|
52
|
+
unless size.blank? || Processor.geometry_string_valid?(size)
|
42
53
|
raise InvalidGeometryError.new('"%s" is not a valid ImageMagick geometry string' % size)
|
43
54
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
resize_image(size)
|
50
|
-
return @image
|
55
|
+
with_image(file) do |image|
|
56
|
+
convert_image(image, format) if format
|
57
|
+
resize_image(image, size) if size
|
58
|
+
yield IO.read(image.path) if block_given?
|
59
|
+
image
|
51
60
|
end
|
52
|
-
rescue MiniMagick::MiniMagickError
|
53
|
-
raise ProcessorError.new("That doesn't look like an image file.")
|
54
61
|
end
|
62
|
+
alias_method :resize, :process #Backwards-compat
|
55
63
|
|
64
|
+
# Gets the given +dimension+ (width/height) from the image file at +path+.
|
65
|
+
def measure(path, dimension)
|
66
|
+
MiniMagick::Image.from_file(path)[dimension.to_sym]
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
# Operates on the image with MiniMagick. Yields a MiniMagick::Image object.
|
71
|
+
def with_image(file)
|
72
|
+
path = file.respond_to?(:path) ? file.path : file
|
73
|
+
file.close if file.respond_to?(:close) && !file.closed?
|
74
|
+
silence_stderr do
|
75
|
+
begin
|
76
|
+
image = MiniMagick::Image.from_file(path)
|
77
|
+
yield image
|
78
|
+
rescue MiniMagick::MiniMagickError
|
79
|
+
raise ProcessorError.new("#{path} doesn't look like an image file.")
|
80
|
+
ensure
|
81
|
+
image.tempfile.close! if defined?(image) && image
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
56
86
|
# Image resizing is placed in a separate method for easy monkey-patching.
|
57
87
|
# This is intended to be invoked from resize, rather than directly.
|
58
88
|
# By default, the following ImageMagick functionality is invoked:
|
@@ -62,8 +92,10 @@ module HasImage
|
|
62
92
|
# * gravity[http://www.imagemagick.org/script/command-line-options.php#gravity]
|
63
93
|
# * extent[http://www.imagemagick.org/script/command-line-options.php#extent]
|
64
94
|
# * quality[http://www.imagemagick.org/script/command-line-options.php#quality]
|
65
|
-
|
66
|
-
|
95
|
+
#
|
96
|
+
# +image+ should be a MiniMagick::Image. +size+ should be a geometry string.
|
97
|
+
def resize_image(image, size)
|
98
|
+
image.combine_options do |commands|
|
67
99
|
commands.send("auto-orient".to_sym)
|
68
100
|
commands.strip
|
69
101
|
# Fixed-dimension images
|
@@ -79,13 +111,8 @@ module HasImage
|
|
79
111
|
end
|
80
112
|
end
|
81
113
|
|
82
|
-
|
83
|
-
|
84
|
-
# This was placed in a separate method largely to facilitate debugging
|
85
|
-
# and profiling.
|
86
|
-
def convert_image
|
87
|
-
return if @image[:format] == options[:convert_to]
|
88
|
-
@image.format(options[:convert_to])
|
114
|
+
def convert_image(image, format=options[:convert_to])
|
115
|
+
image.format(format) unless image[:format] == format
|
89
116
|
end
|
90
117
|
|
91
118
|
end
|
data/lib/has_image/storage.rb
CHANGED
@@ -11,6 +11,8 @@ module HasImage
|
|
11
11
|
# storage mechanism for Amazon AWS, Photobucket, DBFile, SFTP, or whatever
|
12
12
|
# you want.
|
13
13
|
class Storage
|
14
|
+
class_inheritable_accessor :thumbnail_separator
|
15
|
+
write_inheritable_attribute :thumbnail_separator, '_'
|
14
16
|
|
15
17
|
attr_accessor :image_data, :options, :temp_file
|
16
18
|
|
@@ -21,17 +23,29 @@ module HasImage
|
|
21
23
|
# to this problem fails with high ids, such as those created by
|
22
24
|
# db:fixture:load. This version scales to large ids more gracefully.
|
23
25
|
# Thanks to Adrian Mugnolo for the fix.
|
26
|
+
#++
|
27
|
+
# FIXME: collides with IDs with more than 8 digits
|
28
|
+
#--
|
24
29
|
def partitioned_path(id, *args)
|
25
30
|
["%04d" % ((id.to_i / 1e4) % 1e4), "%04d" % (id.to_i % 1e4)].concat(args)
|
26
31
|
end
|
27
|
-
|
32
|
+
|
33
|
+
def id_from_partitioned_path(partitioned_path)
|
34
|
+
partitioned_path.join.to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def id_from_path(path)
|
38
|
+
path = path.split('/') if path.is_a?(String)
|
39
|
+
path_partitions = 2
|
40
|
+
id_from_partitioned_path(path.first(path_partitions))
|
41
|
+
end
|
42
|
+
|
28
43
|
# By default, simply accepts and returns the id of the object. This is
|
29
44
|
# here to allow you to monkey patch this method, for example, if you
|
30
45
|
# wish instead to generate and return a UUID.
|
31
46
|
def generated_file_name(*args)
|
32
47
|
return args.first.to_param.to_s
|
33
48
|
end
|
34
|
-
|
35
49
|
end
|
36
50
|
|
37
51
|
# The constuctor should be invoked with the options set by has_image.
|
@@ -49,7 +63,7 @@ module HasImage
|
|
49
63
|
else
|
50
64
|
image_data.rewind
|
51
65
|
@temp_file = Tempfile.new 'has_image_data_%s' % Storage.generated_file_name
|
52
|
-
@temp_file.write(image_data.read)
|
66
|
+
@temp_file.write(image_data.read)
|
53
67
|
end
|
54
68
|
end
|
55
69
|
|
@@ -65,24 +79,23 @@ module HasImage
|
|
65
79
|
@temp_file.size > options[:max_size]
|
66
80
|
end
|
67
81
|
|
68
|
-
# A tip of the hat to attachment_fu.
|
69
|
-
alias uploaded_data= image_data=
|
70
|
-
|
71
|
-
# A tip of the hat to attachment_fu.
|
72
|
-
alias uploaded_data image_data
|
73
|
-
|
74
82
|
# Invokes the processor to resize the image(s) and the installs them to
|
75
83
|
# the appropriate directory.
|
76
84
|
def install_images(object)
|
77
85
|
generated_name = Storage.generated_file_name(object)
|
78
86
|
install_main_image(object.has_image_id, generated_name)
|
79
|
-
|
87
|
+
generate_thumbnails(object.has_image_id, generated_name) if thumbnails_needed?
|
80
88
|
return generated_name
|
81
89
|
ensure
|
82
90
|
@temp_file.close! if !@temp_file.closed?
|
83
91
|
@temp_file = nil
|
84
92
|
end
|
85
93
|
|
94
|
+
# Measures the given dimension using the processor
|
95
|
+
def measure(path, dimension)
|
96
|
+
processor.measure(path, dimension)
|
97
|
+
end
|
98
|
+
|
86
99
|
# Gets the "web" path for an image. For example:
|
87
100
|
#
|
88
101
|
# /photos/0000/0001/3er0zs.jpg
|
@@ -108,10 +121,32 @@ module HasImage
|
|
108
121
|
!(image_too_small? || image_too_big?)
|
109
122
|
end
|
110
123
|
|
111
|
-
|
112
|
-
|
124
|
+
# Write the thumbnails to the install directory - probably somewhere under
|
125
|
+
# RAILS_ROOT/public.
|
126
|
+
def generate_thumbnails(id, name)
|
127
|
+
ensure_directory_exists!(id)
|
128
|
+
options[:thumbnails].keys.each { |thumb_name| generate_thumbnail(id, name, thumb_name) }
|
129
|
+
end
|
130
|
+
alias_method :regenerate_thumbnails, :generate_thumbnails #Backwards-compat
|
131
|
+
|
132
|
+
def generate_thumbnail(id, name, thumb_name)
|
133
|
+
size_spec = options[:thumbnails][thumb_name.to_sym]
|
134
|
+
raise StorageError unless size_spec
|
135
|
+
ensure_directory_exists!(id)
|
136
|
+
File.open absolute_path(id, name, thumb_name), "w" do |thumbnail_destination|
|
137
|
+
processor.process absolute_path(id, name), size_spec do |thumbnail_data|
|
138
|
+
thumbnail_destination.write thumbnail_data
|
139
|
+
end
|
140
|
+
end
|
113
141
|
end
|
114
|
-
|
142
|
+
|
143
|
+
# Gets the full local filesystem path for an image. For example:
|
144
|
+
#
|
145
|
+
# /var/sites/example.com/production/public/photos/0000/0001/3er0zs.jpg
|
146
|
+
def filesystem_path_for(object, thumbnail = nil)
|
147
|
+
File.join(path_for(object.has_image_id), file_name_for(object.send(options[:column]), thumbnail))
|
148
|
+
end
|
149
|
+
|
115
150
|
protected
|
116
151
|
|
117
152
|
# Gets the extension to append to the image. Transforms "jpeg" to "jpg."
|
@@ -129,49 +164,42 @@ module HasImage
|
|
129
164
|
#
|
130
165
|
# "abc123_thumb.jpg"
|
131
166
|
#
|
132
|
-
#
|
167
|
+
# It uses an underscore to separatore parts by default, but that is configurable
|
168
|
+
# by setting HasImage::Storage.thumbnail_separator
|
133
169
|
def file_name_for(*args)
|
134
|
-
"%s.%s" % [args.compact.join(
|
170
|
+
"%s.%s" % [args.compact.join(self.class.thumbnail_separator), extension]
|
135
171
|
end
|
136
172
|
|
137
|
-
#
|
173
|
+
# Get the full path for the id. For example:
|
138
174
|
#
|
139
|
-
#
|
140
|
-
def
|
141
|
-
|
175
|
+
# /var/sites/example.org/production/public/photos/0000/0001
|
176
|
+
def path_for(id)
|
177
|
+
debugger if $debug
|
178
|
+
File.join(options[:base_path], options[:path_prefix], Storage.partitioned_path(id))
|
142
179
|
end
|
143
180
|
|
144
|
-
|
145
|
-
|
146
|
-
|
181
|
+
def absolute_path(id, *args)
|
182
|
+
File.join(path_for(id), file_name_for(*args))
|
183
|
+
end
|
184
|
+
|
185
|
+
def ensure_directory_exists!(id)
|
147
186
|
FileUtils.mkdir_p path_for(id)
|
148
|
-
main = processor.resize(@temp_file, @options[:resize_to])
|
149
|
-
file = File.open(File.join(path_for(id), file_name_for(name)), "w")
|
150
|
-
file.write(IO.read(main.path))
|
151
|
-
file.close
|
152
|
-
main.tempfile.close!
|
153
187
|
end
|
154
188
|
|
155
|
-
# Write the
|
189
|
+
# Write the main image to the install directory - probably somewhere under
|
156
190
|
# RAILS_ROOT/public.
|
157
|
-
def
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
file = File.open(File.join(path_for(id), file_name_for(name, thumb_name)), "w")
|
164
|
-
file.write(IO.read(thumb.tempfile.path))
|
165
|
-
file.close
|
166
|
-
thumb.tempfile.close!
|
191
|
+
def install_main_image(id, name)
|
192
|
+
ensure_directory_exists!(id)
|
193
|
+
File.open absolute_path(id, name), "w" do |final_destination|
|
194
|
+
processor.process(@temp_file) do |processed_image|
|
195
|
+
final_destination.write processed_image
|
196
|
+
end
|
167
197
|
end
|
168
198
|
end
|
169
|
-
|
170
|
-
#
|
171
|
-
|
172
|
-
|
173
|
-
def path_for(id)
|
174
|
-
File.join(options[:base_path], options[:path_prefix], Storage.partitioned_path(id))
|
199
|
+
|
200
|
+
# used in #install_images
|
201
|
+
def thumbnails_needed?
|
202
|
+
!options[:thumbnails].empty? && options[:auto_generate_thumbnails]
|
175
203
|
end
|
176
204
|
|
177
205
|
# Instantiates the processor using the options set in my contructor (if
|
@@ -180,7 +208,6 @@ module HasImage
|
|
180
208
|
def processor
|
181
209
|
@processor ||= Processor.new(options)
|
182
210
|
end
|
183
|
-
|
184
211
|
end
|
185
212
|
|
186
213
|
end
|
data/test/storage_test.rb
CHANGED
@@ -11,7 +11,9 @@ class StorageTest < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def default_options
|
14
|
-
|
14
|
+
mock_class = "test"
|
15
|
+
mock_class.stubs(:table_name).returns('tests')
|
16
|
+
HasImage.default_options_for(mock_class).merge(
|
15
17
|
:base_path => File.join(File.dirname(__FILE__), '..', 'tmp')
|
16
18
|
)
|
17
19
|
end
|
@@ -23,6 +25,25 @@ class StorageTest < Test::Unit::TestCase
|
|
23
25
|
def test_partitioned_path_doesnt_collide_with_high_ids
|
24
26
|
assert_not_equal HasImage::Storage.partitioned_path(867792732),
|
25
27
|
HasImage::Storage.partitioned_path(867792731)
|
28
|
+
# FIXME: collisions when IDs have more than 8 digits
|
29
|
+
# assert_not_equal HasImage::Storage.partitioned_path(967792731),
|
30
|
+
# HasImage::Storage.partitioned_path(967792731)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_id_from_partitioned_path
|
34
|
+
assert_equal 123, HasImage::Storage.id_from_partitioned_path(HasImage::Storage.partitioned_path(123))
|
35
|
+
assert_equal 56, HasImage::Storage.id_from_partitioned_path(HasImage::Storage.partitioned_path(56))
|
36
|
+
assert_equal 67792732, HasImage::Storage.id_from_partitioned_path(HasImage::Storage.partitioned_path(67792732))
|
37
|
+
# FIXME: for IDs with more than 8 digits partitioned path is destructive
|
38
|
+
# assert_equal 867792731, HasImage::Storage.id_from_partitioned_path(HasImage::Storage.partitioned_path(867792731))
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_id_from_path_accepts_array
|
42
|
+
assert_equal 123, HasImage::Storage.id_from_path(['0000','0123','image_something.jpg'])
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_id_from_path_accepts_path
|
46
|
+
assert_equal 12345, HasImage::Storage.id_from_path('0001/2345/0123/image_something.jpg')
|
26
47
|
end
|
27
48
|
|
28
49
|
def test_generated_file_name
|
@@ -45,6 +66,17 @@ class StorageTest < Test::Unit::TestCase
|
|
45
66
|
pic = stub(:has_image_file => "my+pic", :has_image_id => 1)
|
46
67
|
assert_equal "/tests/0000/0001/my%2Bpic_square.jpg", @storage.public_path_for(pic, :square)
|
47
68
|
end
|
69
|
+
|
70
|
+
def test_name_generation_takes_into_account_thumbnail_separator_constant
|
71
|
+
old_separator = HasImage::Storage.thumbnail_separator
|
72
|
+
|
73
|
+
@storage = HasImage::Storage.new(default_options.merge(:thumbnails => {:schick => '22x22'}, :base_path => '/public'))
|
74
|
+
HasImage::Storage.thumbnail_separator = '.'
|
75
|
+
pic = stub(:has_image_file => "pic", :has_image_id => 1)
|
76
|
+
assert_equal "/tests/0000/0001/pic.schick.jpg", @storage.public_path_for(pic, :schick)
|
77
|
+
|
78
|
+
HasImage::Storage.thumbnail_separator = old_separator
|
79
|
+
end
|
48
80
|
|
49
81
|
def test_escape_file_name_for_http
|
50
82
|
@storage = HasImage::Storage.new(default_options.merge(:base_path => '/public'))
|
@@ -83,7 +115,17 @@ class StorageTest < Test::Unit::TestCase
|
|
83
115
|
:one => "100x100", :two => "200x200"}))
|
84
116
|
@storage.image_data = temp_file("image.jpg")
|
85
117
|
@name = @storage.install_images(stub(:has_image_id => 1))
|
86
|
-
assert @storage.remove_images(stub(:has_image_id => 1), @name)
|
118
|
+
assert @storage.remove_images(stub(:has_image_id => 1), @name)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_install_images_doesnt_automatically_generate_thumbnails_if_that_option_is_set
|
122
|
+
@storage = HasImage::Storage.new(default_options.merge(
|
123
|
+
:thumbnails => {:two => "200x200"},
|
124
|
+
:auto_generate_thumbnails => false
|
125
|
+
))
|
126
|
+
@storage.image_data = temp_file("image.jpg")
|
127
|
+
@storage.expects(:generate_thumbnails).never
|
128
|
+
@storage.install_images(stub(:has_image_id => 1))
|
87
129
|
end
|
88
130
|
|
89
131
|
def test_image_not_too_small
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ComplexPic < ActiveRecord::Base
|
4
|
+
has_image
|
5
|
+
end
|
6
|
+
|
7
|
+
class ComplexPicTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
# Note: Be sure to not set the whole options hash in your tests below
|
10
|
+
ComplexPic.has_image_options = HasImage.default_options_for(ComplexPic)
|
11
|
+
ComplexPic.has_image_options[:column] = :filename
|
12
|
+
ComplexPic.has_image_options[:base_path] = File.join(RAILS_ROOT, 'tmp')
|
13
|
+
ComplexPic.has_image_options[:resize_to] = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
FileUtils.rm_rf(File.join(RAILS_ROOT, 'tmp', 'complex_pics'))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_save_width_to_db_on_create
|
21
|
+
@pic = ComplexPic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
22
|
+
assert_equal 1916, @pic[:width]
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_save_height_to_db_on_create
|
26
|
+
@pic = ComplexPic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
27
|
+
assert_equal 1990, @pic[:height]
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_use_value_from_db_in_height_reader
|
31
|
+
@pic = ComplexPic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
32
|
+
@pic[:height] = 60_000
|
33
|
+
assert_equal 60_000, @pic.height
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_should_use_value_from_db_in_width_reader
|
37
|
+
@pic = ComplexPic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
38
|
+
@pic[:width] = 60_000
|
39
|
+
assert_equal 60_000, @pic.width
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/test_rails/pic_test.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
+
class Pic < ActiveRecord::Base
|
4
|
+
has_image
|
5
|
+
end
|
6
|
+
|
7
|
+
class PicWithDifferentTableName < ActiveRecord::Base
|
8
|
+
set_table_name 'pics'
|
9
|
+
end
|
10
|
+
|
3
11
|
class PicTest < Test::Unit::TestCase
|
4
|
-
|
5
12
|
def setup
|
13
|
+
# Note: Be sure to not set the whole options hash in your tests below
|
6
14
|
Pic.has_image_options = HasImage.default_options_for(Pic)
|
7
|
-
Pic.has_image_options[:base_path] = File.join(RAILS_ROOT, '
|
15
|
+
Pic.has_image_options[:base_path] = File.join(RAILS_ROOT, 'tmp')
|
8
16
|
end
|
9
|
-
|
17
|
+
|
10
18
|
def teardown
|
11
19
|
FileUtils.rm_rf(File.join(RAILS_ROOT, 'tmp', 'pics'))
|
12
20
|
end
|
13
|
-
|
21
|
+
|
14
22
|
def test_should_be_valid
|
15
23
|
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
16
24
|
assert @pic.valid? , "#{@pic.errors.full_messages.to_sentence}"
|
@@ -47,14 +55,37 @@ class PicTest < Test::Unit::TestCase
|
|
47
55
|
assert @pic.save!
|
48
56
|
end
|
49
57
|
|
50
|
-
def
|
51
|
-
Pic.has_image_options = HasImage.default_options_for(Pic).merge(
|
52
|
-
:thumbnails => {:small => "100x100", :tiny => "16x16"})
|
58
|
+
def test_finding_from_url_path
|
53
59
|
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
54
60
|
@pic.save!
|
55
|
-
|
61
|
+
path = HasImage::Storage.partitioned_path @pic.id
|
62
|
+
assert_equal @pic, Pic.from_partitioned_path(path)
|
56
63
|
end
|
57
64
|
|
65
|
+
def test_default_options_respect_table_name
|
66
|
+
assert_equal 'pics', HasImage.default_options_for(PicWithDifferentTableName)[:path_prefix]
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_generate_thumbnails_on_create
|
70
|
+
Pic.has_image_options[:thumbnails] = {:tiny => "16x16"}
|
71
|
+
@pic = Pic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
72
|
+
assert File.exist?(@pic.absolute_path(:tiny))
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_doesnt_generate_thumbnails_if_option_disabled
|
76
|
+
Pic.has_image_options[:thumbnails] = {:tiny => "16x16"}
|
77
|
+
Pic.has_image_options[:auto_generate_thumbnails] = false
|
78
|
+
@pic = Pic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
79
|
+
assert !File.exist?(@pic.absolute_path(:tiny))
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_regenerate_thumbnails_succeeds
|
83
|
+
Pic.has_image_options[:thumbnails] = {:small => "100x100", :tiny => "16x16"}
|
84
|
+
|
85
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
86
|
+
@pic.save!
|
87
|
+
assert @pic.regenerate_thumbnails
|
88
|
+
end
|
58
89
|
|
59
90
|
def test_create_model_without_setting_image_data
|
60
91
|
assert Pic.new.save!
|
@@ -66,6 +97,13 @@ class PicTest < Test::Unit::TestCase
|
|
66
97
|
assert @pic.destroy
|
67
98
|
end
|
68
99
|
|
100
|
+
def test_destroy_should_not_remove_image_file_if_option_is_set
|
101
|
+
Pic.has_image_options[:delete] = false
|
102
|
+
@pic = Pic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
103
|
+
@pic.destroy
|
104
|
+
assert File.exist?(@pic.absolute_path)
|
105
|
+
end
|
106
|
+
|
69
107
|
def test_destroy_model_with_images_already_deleted_from_filesystem
|
70
108
|
@pic = Pic.new
|
71
109
|
@pic.save!
|
@@ -86,5 +124,30 @@ class PicTest < Test::Unit::TestCase
|
|
86
124
|
assert @pic.valid?
|
87
125
|
end
|
88
126
|
|
89
|
-
|
127
|
+
def test_dimension_getters
|
128
|
+
Pic.has_image_options[:resize_to] = "100x200"
|
129
|
+
pic = Pic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
130
|
+
assert_equal 100, pic.width
|
131
|
+
assert_equal 200, pic.height
|
132
|
+
assert_equal '100x200', pic.image_size
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_image_isnt_resized_when_resize_to_set_to_nil
|
136
|
+
Pic.has_image_options[:resize_to] = nil
|
137
|
+
pic = Pic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
138
|
+
|
139
|
+
assert_equal 1916, pic.width
|
140
|
+
assert_equal 1990, pic.height
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_image_isnt_resized_but_converted_when_resize_to_set_to_nil
|
144
|
+
Pic.has_image_options[:resize_to] = nil
|
145
|
+
Pic.has_image_options[:convert_to] = 'PNG'
|
146
|
+
pic = Pic.create!(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
147
|
+
|
148
|
+
assert_equal 'PNG', MiniMagick::Image.from_file(pic.absolute_path)[:format]
|
149
|
+
assert_equal 1916, pic.width
|
150
|
+
assert_equal 1990, pic.height
|
151
|
+
end
|
90
152
|
|
153
|
+
end
|