miloops-attachment_fu 3.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG +63 -0
  2. data/LICENSE +20 -0
  3. data/README +253 -0
  4. data/Rakefile +22 -0
  5. data/amazon_s3.yml.tpl +17 -0
  6. data/install.rb +7 -0
  7. data/lib/geometry.rb +96 -0
  8. data/lib/pothoven-attachment_fu.rb +23 -0
  9. data/lib/technoweenie/attachment_fu.rb +578 -0
  10. data/lib/technoweenie/attachment_fu/backends/cloud_file_backend.rb +211 -0
  11. data/lib/technoweenie/attachment_fu/backends/db_file_backend.rb +39 -0
  12. data/lib/technoweenie/attachment_fu/backends/file_system_backend.rb +126 -0
  13. data/lib/technoweenie/attachment_fu/backends/s3_backend.rb +394 -0
  14. data/lib/technoweenie/attachment_fu/processors/core_image_processor.rb +66 -0
  15. data/lib/technoweenie/attachment_fu/processors/gd2_processor.rb +59 -0
  16. data/lib/technoweenie/attachment_fu/processors/image_science_processor.rb +80 -0
  17. data/lib/technoweenie/attachment_fu/processors/mini_magick_processor.rb +142 -0
  18. data/lib/technoweenie/attachment_fu/processors/rmagick_processor.rb +66 -0
  19. data/rackspace_cloudfiles.yml.tpl +14 -0
  20. data/test/backends/db_file_test.rb +16 -0
  21. data/test/backends/file_system_test.rb +143 -0
  22. data/test/backends/remote/cloudfiles_test.rb +102 -0
  23. data/test/backends/remote/s3_test.rb +119 -0
  24. data/test/base_attachment_tests.rb +77 -0
  25. data/test/basic_test.rb +120 -0
  26. data/test/database.yml +18 -0
  27. data/test/extra_attachment_test.rb +67 -0
  28. data/test/fixtures/attachment.rb +304 -0
  29. data/test/fixtures/files/fake/rails.png +0 -0
  30. data/test/fixtures/files/foo.txt +1 -0
  31. data/test/fixtures/files/rails.jpg +0 -0
  32. data/test/fixtures/files/rails.png +0 -0
  33. data/test/geometry_test.rb +114 -0
  34. data/test/processors/core_image_test.rb +58 -0
  35. data/test/processors/gd2_test.rb +51 -0
  36. data/test/processors/image_science_test.rb +54 -0
  37. data/test/processors/mini_magick_test.rb +122 -0
  38. data/test/processors/rmagick_test.rb +272 -0
  39. data/test/schema.rb +136 -0
  40. data/test/test_helper.rb +180 -0
  41. data/test/validation_test.rb +55 -0
  42. data/vendor/red_artisan/core_image/filters/color.rb +27 -0
  43. data/vendor/red_artisan/core_image/filters/effects.rb +31 -0
  44. data/vendor/red_artisan/core_image/filters/perspective.rb +25 -0
  45. data/vendor/red_artisan/core_image/filters/quality.rb +25 -0
  46. data/vendor/red_artisan/core_image/filters/scale.rb +47 -0
  47. data/vendor/red_artisan/core_image/filters/watermark.rb +32 -0
  48. data/vendor/red_artisan/core_image/processor.rb +123 -0
  49. metadata +98 -0
data/test/database.yml ADDED
@@ -0,0 +1,18 @@
1
+ sqlite:
2
+ :adapter: sqlite
3
+ :database: attachment_fu_plugin.sqlite.db
4
+ sqlite3:
5
+ :adapter: sqlite3
6
+ :database: attachment_fu_plugin.sqlite3.db
7
+ postgresql:
8
+ :adapter: postgresql
9
+ :username: postgres
10
+ :password: postgres
11
+ :database: attachment_fu_plugin_test
12
+ :min_messages: ERROR
13
+ mysql:
14
+ :adapter: mysql
15
+ :host: localhost
16
+ :username: rails
17
+ :password:
18
+ :database: attachment_fu_plugin_test
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class OrphanAttachmentTest < Test::Unit::TestCase
4
+ include BaseAttachmentTests
5
+ attachment_model OrphanAttachment
6
+
7
+ def test_should_create_image_from_uploaded_file
8
+ assert_created do
9
+ attachment = upload_file :filename => '/files/rails.png'
10
+ assert_valid attachment
11
+ assert !attachment.db_file.new_record? if attachment.respond_to?(:db_file)
12
+ assert attachment.image?
13
+ assert !attachment.size.zero?
14
+ end
15
+ end
16
+
17
+ def test_should_create_file_from_uploaded_file
18
+ assert_created do
19
+ attachment = upload_file :filename => '/files/foo.txt'
20
+ assert_valid attachment
21
+ assert !attachment.db_file.new_record? if attachment.respond_to?(:db_file)
22
+ assert attachment.image?
23
+ assert !attachment.size.zero?
24
+ end
25
+ end
26
+
27
+ def test_should_create_file_from_merb_temp_file
28
+ assert_created do
29
+ attachment = upload_merb_file :filename => '/files/foo.txt'
30
+ assert_valid attachment
31
+ assert !attachment.db_file.new_record? if attachment.respond_to?(:db_file)
32
+ assert attachment.image?
33
+ assert !attachment.size.zero?
34
+ end
35
+ end
36
+
37
+ def test_should_create_image_from_uploaded_file_with_custom_content_type
38
+ assert_created do
39
+ attachment = upload_file :content_type => 'foo/bar', :filename => '/files/rails.png'
40
+ assert_valid attachment
41
+ assert !attachment.image?
42
+ assert !attachment.db_file.new_record? if attachment.respond_to?(:db_file)
43
+ assert !attachment.size.zero?
44
+ #assert_equal 1784, attachment.size
45
+ end
46
+ end
47
+
48
+ def test_should_create_thumbnail
49
+ attachment = upload_file :filename => '/files/rails.png'
50
+
51
+ assert_raise Technoweenie::AttachmentFu::ThumbnailError do
52
+ attachment.create_or_update_thumbnail(attachment.create_temp_file, 'thumb', 50, 50)
53
+ end
54
+ end
55
+
56
+ def test_should_create_thumbnail_with_geometry_string
57
+ attachment = upload_file :filename => '/files/rails.png'
58
+
59
+ assert_raise Technoweenie::AttachmentFu::ThumbnailError do
60
+ attachment.create_or_update_thumbnail(attachment.create_temp_file, 'thumb', 'x50')
61
+ end
62
+ end
63
+ end
64
+
65
+ class MinimalAttachmentTest < OrphanAttachmentTest
66
+ attachment_model MinimalAttachment
67
+ end
@@ -0,0 +1,304 @@
1
+ class Attachment < ActiveRecord::Base
2
+ @@saves = 0
3
+ cattr_accessor :saves
4
+ has_attachment :processor => :rmagick
5
+ validates_as_attachment
6
+ after_save do |record|
7
+ self.saves += 1
8
+ end
9
+ end
10
+
11
+ class LowerQualityAttachment < Attachment
12
+ set_table_name 'attachments'
13
+ has_attachment :resize_to => [55,55], :jpeg_quality => 50
14
+ end
15
+
16
+ class SmallAttachment < Attachment
17
+ has_attachment :max_size => 1.kilobyte
18
+ end
19
+
20
+ class BigAttachment < Attachment
21
+ has_attachment :size => 1.megabyte..2.megabytes
22
+ end
23
+
24
+ class PdfAttachment < Attachment
25
+ has_attachment :content_type => 'pdf'
26
+ end
27
+
28
+ class DocAttachment < Attachment
29
+ has_attachment :content_type => %w(pdf doc txt)
30
+ end
31
+
32
+ class ImageAttachment < Attachment
33
+ has_attachment :content_type => :image, :resize_to => [50,50]
34
+ end
35
+
36
+ class ImageOrPdfAttachment < Attachment
37
+ has_attachment :content_type => ['pdf', :image], :resize_to => 'x50'
38
+ end
39
+
40
+ class ImageWithThumbsAttachment < Attachment
41
+ has_attachment :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }, :resize_to => [55,55]
42
+ # after_resize do |record, img|
43
+ # record.aspect_ratio = img.columns.to_f / img.rows.to_f
44
+ # end
45
+ end
46
+
47
+ class ImageWithPerThumbJpegAttachment < Attachment
48
+ has_attachment :resize_to => '500x500!',
49
+ :thumbnails => { :thumb => '50x50!', :large => '300x300!', :avatar => '64x64!' },
50
+ :jpeg_quality => { :thumb => 90, '<5000' => 85, '>=5000' => 75, :large => 0x200 | 75 }
51
+ end
52
+
53
+ class ImageWithPolymorphicThumbsAttachment < Attachment
54
+ belongs_to :imageable, :polymorphic => true
55
+ has_attachment :thumbnails => {
56
+ :thumb => [50, 50],
57
+ :geometry => 'x50',
58
+ :products => { :large_thumb => '169x169!', :zoomed => '500x500>' },
59
+ :editorials => { :fullsize => '150x100>' },
60
+ 'User' => { :avatar => '64x64!' }
61
+ }
62
+ >>>>>>> 33222f02ee9657d0ea9e4654b06cea7ed49cb85b
63
+ end
64
+
65
+ class FileAttachment < ActiveRecord::Base
66
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files', :processor => :rmagick
67
+ validates_as_attachment
68
+ end
69
+
70
+ class FileAttachmentWithStringId < ActiveRecord::Base
71
+ set_table_name 'file_attachments_with_string_id'
72
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files', :processor => :rmagick
73
+ validates_as_attachment
74
+
75
+ before_validation :auto_generate_id
76
+ before_save :auto_generate_id
77
+ @@last_id = 0
78
+
79
+ private
80
+ def auto_generate_id
81
+ @@last_id += 1
82
+ self.id = "id_#{@@last_id}"
83
+ end
84
+ end
85
+
86
+ class FileAttachmentWithUuid < ActiveRecord::Base
87
+ set_table_name 'file_attachments_with_string_id'
88
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files', :processor => :rmagick, :uuid_primary_key => true
89
+ validates_as_attachment
90
+
91
+ before_validation :auto_generate_id
92
+ before_save :auto_generate_id
93
+ @@last_id = 0
94
+
95
+ private
96
+ def auto_generate_id
97
+ @@last_id += 1
98
+ self.id = "%0127dx" % @@last_id
99
+ end
100
+ end
101
+
102
+ class ImageFileAttachment < FileAttachment
103
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
104
+ :content_type => :image, :resize_to => [50,50]
105
+ end
106
+
107
+ class ImageWithThumbsFileAttachment < FileAttachment
108
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
109
+ :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }, :resize_to => [55,55]
110
+ <<<<<<< HEAD
111
+ def after_resize(img)
112
+ self.aspect_ratio = img.columns.to_f / img.rows.to_f
113
+ end
114
+ =======
115
+ # after_resize do |record, img|
116
+ # record.aspect_ratio = img.columns.to_f / img.rows.to_f
117
+ # end
118
+ >>>>>>> 33222f02ee9657d0ea9e4654b06cea7ed49cb85b
119
+ end
120
+
121
+ class ImageWithThumbsClassFileAttachment < FileAttachment
122
+ # use file_system_path to test backwards compatibility
123
+ has_attachment :file_system_path => 'vendor/plugins/attachment_fu/test/files',
124
+ :thumbnails => { :thumb => [50, 50] }, :resize_to => [55,55],
125
+ :thumbnail_class => 'ImageThumbnail'
126
+ end
127
+
128
+ class ImageThumbnail < FileAttachment
129
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files/thumbnails'
130
+ end
131
+
132
+ # no parent
133
+ class OrphanAttachment < ActiveRecord::Base
134
+ has_attachment :processor => :rmagick
135
+ validates_as_attachment
136
+ end
137
+
138
+ # no filename, no size, no content_type
139
+ class MinimalAttachment < ActiveRecord::Base
140
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files', :processor => :rmagick
141
+ validates_as_attachment
142
+
143
+ def filename
144
+ "#{id}.file"
145
+ end
146
+ end
147
+
148
+ begin
149
+ class ImageScienceAttachment < ActiveRecord::Base
150
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
151
+ :processor => :image_science, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55
152
+ end
153
+
154
+ class ImageScienceLowerQualityAttachment < ActiveRecord::Base
155
+ set_table_name 'image_science_attachments'
156
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
157
+ :processor => :image_science, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55,
158
+ :jpeg_quality => 75
159
+ end
160
+
161
+ class ImageScienceWithPerThumbJpegAttachment < ImageScienceAttachment
162
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
163
+ :processor => :image_science,
164
+ :resize_to => '100x100',
165
+ :thumbnails => { :thumb => [50, 50], :editorial => '300x120', :avatar => '64x64!' },
166
+ :jpeg_quality => { :thumb => 90, '<5000' => 80, '>=5000' => 75 }
167
+ end
168
+ rescue MissingSourceFile
169
+ puts $!.message
170
+ puts "no ImageScience"
171
+ end
172
+
173
+ begin
174
+ class CoreImageAttachment < ActiveRecord::Base
175
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
176
+ :processor => :core_image, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55
177
+ end
178
+
179
+ class LowerQualityCoreImageAttachment < CoreImageAttachment
180
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
181
+ :processor => :core_image, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55,
182
+ :jpeg_quality => 50
183
+ end
184
+
185
+ class CoreImageWithPerThumbJpegAttachment < CoreImageAttachment
186
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
187
+ :processor => :core_image,
188
+ :resize_to => '100x100',
189
+ :thumbnails => { :thumb => [50, 50], :editorial => '300x120', :avatar => '64x64!' },
190
+ :jpeg_quality => { :thumb => 90, '<5000' => 80, '>=5000' => 75 }
191
+ end
192
+ rescue MissingSourceFile
193
+ puts $!.message
194
+ puts "no CoreImage"
195
+ end
196
+
197
+ begin
198
+ class MiniMagickAttachment < ActiveRecord::Base
199
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
200
+ :processor => :mini_magick, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55
201
+ end
202
+
203
+ class ImageThumbnailCrop < MiniMagickAttachment
204
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
205
+ :thumbnails => { :square => "50x50c", :vertical => "30x60c", :horizontal => "60x30c"}
206
+
207
+ # TODO this is a bad duplication, this method is in the MiniMagick Processor
208
+ def self.calculate_offset(image_width,image_height,image_aspect,thumb_width,thumb_height,thumb_aspect)
209
+ # only crop if image is not smaller in both dimensions
210
+
211
+ # special cases, image smaller in one dimension then thumbsize
212
+ if image_width < thumb_width
213
+ offset = (image_height / 2) - (thumb_height / 2)
214
+ command = "#{image_width}x#{thumb_height}+0+#{offset}"
215
+ elsif image_height < thumb_height
216
+ offset = (image_width / 2) - (thumb_width / 2)
217
+ command = "#{thumb_width}x#{image_height}+#{offset}+0"
218
+
219
+ # normal thumbnail generation
220
+ # calculate height and offset y, width is fixed
221
+ elsif (image_aspect <= thumb_aspect or image_width < thumb_width) and image_height > thumb_height
222
+ height = image_width / thumb_aspect
223
+ offset = (image_height / 2) - (height / 2)
224
+ command = "#{image_width}x#{height}+0+#{offset}"
225
+ # calculate width and offset x, height is fixed
226
+ else
227
+ width = image_height * thumb_aspect
228
+ offset = (image_width / 2) - (width / 2)
229
+ command = "#{width}x#{image_height}+#{offset}+0"
230
+ end
231
+ # crop image
232
+ command
233
+ end
234
+ end
235
+
236
+ class LowerQualityMiniMagickAttachment < ActiveRecord::Base
237
+ set_table_name 'mini_magick_attachments'
238
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
239
+ :processor => :mini_magick, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55,
240
+ :jpeg_quality => 50
241
+ end
242
+
243
+ class MiniMagickWithPerThumbJpegAttachment < MiniMagickAttachment
244
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
245
+ :processor => :mini_magick,
246
+ :resize_to => '100x100',
247
+ :thumbnails => { :thumb => [50, 50], :editorial => '300x120', :avatar => '64x64!' },
248
+ :jpeg_quality => { :thumb => 90, '<5000' => 80, '>=5000' => 75 }
249
+ end
250
+
251
+ rescue MissingSourceFile
252
+ puts $!.message
253
+ puts "no Mini Magick"
254
+ end
255
+
256
+ begin
257
+ class GD2Attachment < ActiveRecord::Base
258
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
259
+ :processor => :gd2, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55
260
+ end
261
+
262
+ class LowerQualityGD2Attachment < GD2Attachment
263
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
264
+ :processor => :gd2, :thumbnails => { :thumb => [50, 51], :geometry => '31>', :aspect => '25x25!' }, :resize_to => 55,
265
+ :jpeg_quality => 50
266
+ end
267
+
268
+ class GD2WithPerThumbJpegAttachment < GD2Attachment
269
+ has_attachment :path_prefix => 'vendor/plugins/attachment_fu/test/files',
270
+ :processor => :gd2,
271
+ :resize_to => '100x100',
272
+ :thumbnails => { :thumb => [50, 50], :editorial => '300x120', :avatar => '64x64!' },
273
+ :jpeg_quality => { :thumb => 90, '<5000' => 80, '>=5000' => 75 }
274
+ end
275
+ rescue MissingSourceFile
276
+ puts $!.message
277
+ puts "no GD2"
278
+ end
279
+
280
+
281
+ begin
282
+ class S3Attachment < ActiveRecord::Base
283
+ has_attachment :storage => :s3, :processor => :rmagick, :s3_config_path => File.join(File.dirname(__FILE__), '../amazon_s3.yml')
284
+ validates_as_attachment
285
+ end
286
+
287
+ class CloudFilesAttachment < ActiveRecord::Base
288
+ has_attachment :storage => :cloud_files, :processor => :rmagick, :cloudfiles_config_path => File.join(File.dirname(__FILE__), '../rackspace_cloudfiles.yml')
289
+ validates_as_attachment
290
+ end
291
+
292
+ class S3WithPathPrefixAttachment < S3Attachment
293
+ has_attachment :storage => :s3, :path_prefix => 'some/custom/path/prefix', :processor => :rmagick
294
+ validates_as_attachment
295
+ end
296
+
297
+ class CloudFilesWithPathPrefixAttachment < CloudFilesAttachment
298
+ has_attachment :storage => :cloud_files, :path_prefix => 'some/custom/path/prefix', :processor => :rmagick
299
+ validates_as_attachment
300
+ end
301
+
302
+ rescue
303
+ puts "S3 error: #{$!}"
304
+ end
Binary file
@@ -0,0 +1 @@
1
+ foo
Binary file
Binary file
@@ -0,0 +1,114 @@
1
+ require 'test/unit'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/geometry')) unless Object.const_defined?(:Geometry)
3
+
4
+ class GeometryTest < Test::Unit::TestCase
5
+ def test_should_resize
6
+ assert_geometry 50, 64,
7
+ "50x50" => [39, 50],
8
+ "60x60" => [47, 60],
9
+ "100x100" => [78, 100]
10
+ end
11
+
12
+ def test_should_resize_no_width
13
+ assert_geometry 50, 64,
14
+ "x50" => [39, 50],
15
+ "x60" => [47, 60],
16
+ "x100" => [78, 100]
17
+ end
18
+
19
+ def test_should_resize_no_height
20
+ assert_geometry 50, 64,
21
+ "50" => [50, 64],
22
+ "60" => [60, 77],
23
+ "100" => [100, 128]
24
+ end
25
+
26
+ def test_should_resize_no_height_with_x
27
+ assert_geometry 50, 64,
28
+ "50x" => [50, 64],
29
+ "60x" => [60, 77],
30
+ "100x" => [100, 128]
31
+ end
32
+
33
+ def test_should_resize_with_percent
34
+ assert_geometry 50, 64,
35
+ "50x50%" => [25, 32],
36
+ "60x60%" => [30, 38],
37
+ "120x112%" => [60, 72]
38
+ end
39
+
40
+ def test_should_resize_with_percent_and_no_width
41
+ assert_geometry 50, 64,
42
+ "x50%" => [50, 32],
43
+ "x60%" => [50, 38],
44
+ "x112%" => [50, 72]
45
+ end
46
+
47
+ def test_should_resize_with_percent_and_no_height
48
+ assert_geometry 50, 64,
49
+ "50%" => [25, 32],
50
+ "60%" => [30, 38],
51
+ "120%" => [60, 77]
52
+ end
53
+
54
+ def test_should_resize_with_less
55
+ assert_geometry 50, 64,
56
+ "50x50<" => [50, 64],
57
+ "60x60<" => [50, 64],
58
+ "100x100<" => [78, 100],
59
+ "100x112<" => [88, 112],
60
+ "40x70<" => [50, 64]
61
+ end
62
+
63
+ def test_should_resize_with_less_and_no_width
64
+ assert_geometry 50, 64,
65
+ "x50<" => [50, 64],
66
+ "x60<" => [50, 64],
67
+ "x100<" => [78, 100]
68
+ end
69
+
70
+ def test_should_resize_with_less_and_no_height
71
+ assert_geometry 50, 64,
72
+ "50<" => [50, 64],
73
+ "60<" => [60, 77],
74
+ "100<" => [100, 128]
75
+ end
76
+
77
+ def test_should_resize_with_greater
78
+ assert_geometry 50, 64,
79
+ "50x50>" => [39, 50],
80
+ "60x60>" => [47, 60],
81
+ "100x100>" => [50, 64],
82
+ "100x112>" => [50, 64],
83
+ "40x70>" => [40, 51]
84
+ end
85
+
86
+ def test_should_resize_with_greater_and_no_width
87
+ assert_geometry 50, 64,
88
+ "x40>" => [31, 40],
89
+ "x60>" => [47, 60],
90
+ "x100>" => [50, 64]
91
+ end
92
+
93
+ def test_should_resize_with_greater_and_no_height
94
+ assert_geometry 50, 64,
95
+ "40>" => [40, 51],
96
+ "60>" => [50, 64],
97
+ "100>" => [50, 64]
98
+ end
99
+
100
+ def test_should_resize_with_aspect
101
+ assert_geometry 50, 64,
102
+ "35x35!" => [35, 35],
103
+ "70x70!" => [70, 70]
104
+ end
105
+
106
+ protected
107
+ def assert_geometry(width, height, values)
108
+ values.each do |geo, result|
109
+ # run twice to verify the Geometry string isn't modified after a run
110
+ geo = Geometry.from_s(geo)
111
+ 2.times { assert_equal result, [width, height] / geo }
112
+ end
113
+ end
114
+ end