futuresinc-attachment_fu 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. data/VERSION.yml +1 -1
  2. data/attachment_fu.gemspec +11 -11
  3. data/init.rb +1 -1
  4. data/lib/attachment_fu/backends/cloud_file_backend.rb +209 -0
  5. data/lib/attachment_fu/backends/db_file_backend.rb +37 -0
  6. data/lib/attachment_fu/backends/file_system_backend.rb +124 -0
  7. data/lib/attachment_fu/backends/s3_backend.rb +392 -0
  8. data/lib/attachment_fu/processors/core_image_processor.rb +55 -0
  9. data/lib/attachment_fu/processors/gd2_processor.rb +53 -0
  10. data/lib/attachment_fu/processors/image_science_processor.rb +60 -0
  11. data/lib/attachment_fu/processors/mini_magick_processor.rb +131 -0
  12. data/lib/attachment_fu/processors/rmagick_processor.rb +56 -0
  13. data/lib/attachment_fu.rb +543 -0
  14. data/test/backends/remote/cloudfiles_test.rb +3 -3
  15. data/test/backends/remote/s3_test.rb +3 -3
  16. data/test/basic_test.rb +2 -2
  17. data/test/extra_attachment_test.rb +2 -2
  18. metadata +11 -11
  19. data/lib/technoweenie/attachment_fu/backends/cloud_file_backend.rb +0 -211
  20. data/lib/technoweenie/attachment_fu/backends/db_file_backend.rb +0 -39
  21. data/lib/technoweenie/attachment_fu/backends/file_system_backend.rb +0 -126
  22. data/lib/technoweenie/attachment_fu/backends/s3_backend.rb +0 -394
  23. data/lib/technoweenie/attachment_fu/processors/core_image_processor.rb +0 -59
  24. data/lib/technoweenie/attachment_fu/processors/gd2_processor.rb +0 -54
  25. data/lib/technoweenie/attachment_fu/processors/image_science_processor.rb +0 -61
  26. data/lib/technoweenie/attachment_fu/processors/mini_magick_processor.rb +0 -132
  27. data/lib/technoweenie/attachment_fu/processors/rmagick_processor.rb +0 -57
  28. data/lib/technoweenie/attachment_fu.rb +0 -545
@@ -1,394 +0,0 @@
1
- module Technoweenie # :nodoc:
2
- module AttachmentFu # :nodoc:
3
- module Backends
4
- # = AWS::S3 Storage Backend
5
- #
6
- # Enables use of {Amazon's Simple Storage Service}[http://aws.amazon.com/s3] as a storage mechanism
7
- #
8
- # == Requirements
9
- #
10
- # Requires the {AWS::S3 Library}[http://amazon.rubyforge.org] for S3 by Marcel Molina Jr. installed either
11
- # as a gem or a as a Rails plugin.
12
- #
13
- # == Configuration
14
- #
15
- # Configuration is done via <tt>RAILS_ROOT/config/amazon_s3.yml</tt> and is loaded according to the <tt>RAILS_ENV</tt>.
16
- # The minimum connection options that you must specify are a bucket name, your access key id and your secret access key.
17
- # If you don't already have your access keys, all you need to sign up for the S3 service is an account at Amazon.
18
- # You can sign up for S3 and get access keys by visiting http://aws.amazon.com/s3.
19
- #
20
- # If you wish to use Amazon CloudFront to serve the files, you can also specify a distibution domain for the bucket.
21
- # To read more about CloudFront, visit http://aws.amazon.com/cloudfront
22
- #
23
- # Example configuration (RAILS_ROOT/config/amazon_s3.yml)
24
- #
25
- # development:
26
- # bucket_name: appname_development
27
- # access_key_id: <your key>
28
- # secret_access_key: <your key>
29
- # distribution_domain: XXXX.cloudfront.net
30
- #
31
- # test:
32
- # bucket_name: appname_test
33
- # access_key_id: <your key>
34
- # secret_access_key: <your key>
35
- # distribution_domain: XXXX.cloudfront.net
36
- #
37
- # production:
38
- # bucket_name: appname
39
- # access_key_id: <your key>
40
- # secret_access_key: <your key>
41
- # distribution_domain: XXXX.cloudfront.net
42
- #
43
- # You can change the location of the config path by passing a full path to the :s3_config_path option.
44
- #
45
- # has_attachment :storage => :s3, :s3_config_path => (RAILS_ROOT + '/config/s3.yml')
46
- #
47
- # === Required configuration parameters
48
- #
49
- # * <tt>:access_key_id</tt> - The access key id for your S3 account. Provided by Amazon.
50
- # * <tt>:secret_access_key</tt> - The secret access key for your S3 account. Provided by Amazon.
51
- # * <tt>:bucket_name</tt> - A unique bucket name (think of the bucket_name as being like a database name).
52
- #
53
- # If any of these required arguments is missing, a MissingAccessKey exception will be raised from AWS::S3.
54
- #
55
- # == About bucket names
56
- #
57
- # Bucket names have to be globaly unique across the S3 system. And you can only have up to 100 of them,
58
- # so it's a good idea to think of a bucket as being like a database, hence the correspondance in this
59
- # implementation to the development, test, and production environments.
60
- #
61
- # The number of objects you can store in a bucket is, for all intents and purposes, unlimited.
62
- #
63
- # === Optional configuration parameters
64
- #
65
- # * <tt>:server</tt> - The server to make requests to. Defaults to <tt>s3.amazonaws.com</tt>.
66
- # * <tt>:port</tt> - The port to the requests should be made on. Defaults to 80 or 443 if <tt>:use_ssl</tt> is set.
67
- # * <tt>:use_ssl</tt> - If set to true, <tt>:port</tt> will be implicitly set to 443, unless specified otherwise. Defaults to false.
68
- # * <tt>:distribution_domain</tt> - The CloudFront distribution domain for the bucket. This can either be the assigned
69
- # distribution domain (ie. XXX.cloudfront.net) or a chosen domain using a CNAME. See CloudFront for more details.
70
- #
71
- # == Usage
72
- #
73
- # To specify S3 as the storage mechanism for a model, set the acts_as_attachment <tt>:storage</tt> option to <tt>:s3</tt>.
74
- #
75
- # class Photo < ActiveRecord::Base
76
- # has_attachment :storage => :s3
77
- # end
78
- #
79
- # === Customizing the path
80
- #
81
- # By default, files are prefixed using a pseudo hierarchy in the form of <tt>:table_name/:id</tt>, which results
82
- # in S3 urls that look like: http(s)://:server/:bucket_name/:table_name/:id/:filename with :table_name
83
- # representing the customizable portion of the path. You can customize this prefix using the <tt>:path_prefix</tt>
84
- # option:
85
- #
86
- # class Photo < ActiveRecord::Base
87
- # has_attachment :storage => :s3, :path_prefix => 'my/custom/path'
88
- # end
89
- #
90
- # Which would result in URLs like <tt>http(s)://:server/:bucket_name/my/custom/path/:id/:filename.</tt>
91
- #
92
- # === Using different bucket names on different models
93
- #
94
- # By default the bucket name that the file will be stored to is the one specified by the
95
- # <tt>:bucket_name</tt> key in the amazon_s3.yml file. You can use the <tt>:bucket_key</tt> option
96
- # to overide this behavior on a per model basis. For instance if you want a bucket that will hold
97
- # only Photos you can do this:
98
- #
99
- # class Photo < ActiveRecord::Base
100
- # has_attachment :storage => :s3, :bucket_key => :photo_bucket_name
101
- # end
102
- #
103
- # And then your amazon_s3.yml file needs to look like this.
104
- #
105
- # development:
106
- # bucket_name: appname_development
107
- # access_key_id: <your key>
108
- # secret_access_key: <your key>
109
- #
110
- # test:
111
- # bucket_name: appname_test
112
- # access_key_id: <your key>
113
- # secret_access_key: <your key>
114
- #
115
- # production:
116
- # bucket_name: appname
117
- # photo_bucket_name: appname_photos
118
- # access_key_id: <your key>
119
- # secret_access_key: <your key>
120
- #
121
- # If the bucket_key you specify is not there in a certain environment then attachment_fu will
122
- # default to the <tt>bucket_name</tt> key. This way you only have to create special buckets
123
- # this can be helpful if you only need special buckets in certain environments.
124
- #
125
- # === Permissions
126
- #
127
- # By default, files are stored on S3 with public access permissions. You can customize this using
128
- # the <tt>:s3_access</tt> option to <tt>has_attachment</tt>. Available values are
129
- # <tt>:private</tt>, <tt>:public_read_write</tt>, and <tt>:authenticated_read</tt>.
130
- #
131
- # === Other options
132
- #
133
- # Of course, all the usual configuration options apply, such as content_type and thumbnails:
134
- #
135
- # class Photo < ActiveRecord::Base
136
- # has_attachment :storage => :s3, :content_type => ['application/pdf', :image], :resize_to => 'x50'
137
- # has_attachment :storage => :s3, :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
138
- # end
139
- #
140
- # === Accessing S3 URLs
141
- #
142
- # You can get an object's URL using the s3_url accessor. For example, assuming that for your postcard app
143
- # you had a bucket name like 'postcard_world_development', and an attachment model called Photo:
144
- #
145
- # @postcard.s3_url # => http(s)://s3.amazonaws.com/postcard_world_development/photos/1/mexico.jpg
146
- #
147
- # The resulting url is in the form: http(s)://:server/:bucket_name/:table_name/:id/:file.
148
- # The optional thumbnail argument will output the thumbnail's filename (if any).
149
- #
150
- # Additionally, you can get an object's base path relative to the bucket root using
151
- # <tt>base_path</tt>:
152
- #
153
- # @photo.file_base_path # => photos/1
154
- #
155
- # And the full path (including the filename) using <tt>full_filename</tt>:
156
- #
157
- # @photo.full_filename # => photos/
158
- #
159
- # Niether <tt>base_path</tt> or <tt>full_filename</tt> include the bucket name as part of the path.
160
- # You can retrieve the bucket name using the <tt>bucket_name</tt> method.
161
- #
162
- # === Accessing CloudFront URLs
163
- #
164
- # You can get an object's CloudFront URL using the cloudfront_url accessor. Using the example from above:
165
- # @postcard.cloudfront_url # => http://XXXX.cloudfront.net/photos/1/mexico.jpg
166
- #
167
- # The resulting url is in the form: http://:distribution_domain/:table_name/:id/:file
168
- #
169
- # If you set :cloudfront to true in your model, the public_filename will be the CloudFront
170
- # URL, not the S3 URL.
171
- module S3Backend
172
- class RequiredLibraryNotFoundError < StandardError; end
173
- class ConfigFileNotFoundError < StandardError; end
174
-
175
- def self.included(base) #:nodoc:
176
- mattr_reader :bucket_name, :s3_config
177
-
178
- begin
179
- require 'aws/s3'
180
- include AWS::S3
181
- rescue LoadError
182
- raise RequiredLibraryNotFoundError.new('AWS::S3 could not be loaded')
183
- end
184
-
185
- begin
186
- @@s3_config_path = base.attachment_options[:s3_config_path] || (RAILS_ROOT + '/config/amazon_s3.yml')
187
- @@s3_config = @@s3_config = YAML.load(ERB.new(File.read(@@s3_config_path)).result)[RAILS_ENV].symbolize_keys
188
- #rescue
189
- # raise ConfigFileNotFoundError.new('File %s not found' % @@s3_config_path)
190
- end
191
-
192
- bucket_key = base.attachment_options[:bucket_key]
193
-
194
- if bucket_key and s3_config[bucket_key.to_sym]
195
- eval_string = "def bucket_name()\n \"#{s3_config[bucket_key.to_sym]}\"\nend"
196
- else
197
- eval_string = "def bucket_name()\n \"#{s3_config[:bucket_name]}\"\nend"
198
- end
199
- base.class_eval(eval_string, __FILE__, __LINE__)
200
-
201
- Base.establish_connection!(s3_config.slice(:access_key_id, :secret_access_key, :server, :port, :use_ssl, :persistent, :proxy))
202
-
203
- # Bucket.create(@@bucket_name)
204
-
205
- base.before_update :rename_file
206
- end
207
-
208
- def self.protocol
209
- @protocol ||= s3_config[:use_ssl] ? 'https://' : 'http://'
210
- end
211
-
212
- def self.hostname
213
- @hostname ||= s3_config[:server] || AWS::S3::DEFAULT_HOST
214
- end
215
-
216
- def self.port_string
217
- @port_string ||= (s3_config[:port].nil? || s3_config[:port] == (s3_config[:use_ssl] ? 443 : 80)) ? '' : ":#{s3_config[:port]}"
218
- end
219
-
220
- def self.distribution_domain
221
- @distribution_domain = s3_config[:distribution_domain]
222
- end
223
-
224
- module ClassMethods
225
- def s3_protocol
226
- Technoweenie::AttachmentFu::Backends::S3Backend.protocol
227
- end
228
-
229
- def s3_hostname
230
- Technoweenie::AttachmentFu::Backends::S3Backend.hostname
231
- end
232
-
233
- def s3_port_string
234
- Technoweenie::AttachmentFu::Backends::S3Backend.port_string
235
- end
236
-
237
- def cloudfront_distribution_domain
238
- Technoweenie::AttachmentFu::Backends::S3Backend.distribution_domain
239
- end
240
- end
241
-
242
- # Overwrites the base filename writer in order to store the old filename
243
- def filename=(value)
244
- @old_filename = filename unless filename.nil? || @old_filename
245
- write_attribute :filename, sanitize_filename(value)
246
- end
247
-
248
- # The attachment ID used in the full path of a file
249
- def attachment_path_id
250
- ((respond_to?(:parent_id) && parent_id) || id).to_s
251
- end
252
-
253
- # The pseudo hierarchy containing the file relative to the bucket name
254
- # Example: <tt>:table_name/:id</tt>
255
- def base_path
256
- File.join(attachment_options[:path_prefix], attachment_path_id)
257
- end
258
-
259
- # The full path to the file relative to the bucket name
260
- # Example: <tt>:table_name/:id/:filename</tt>
261
- def full_filename(thumbnail = nil)
262
- File.join(base_path, thumbnail_name_for(thumbnail))
263
- end
264
-
265
- # All public objects are accessible via a GET request to the S3 servers. You can generate a
266
- # url for an object using the s3_url method.
267
- #
268
- # @photo.s3_url
269
- #
270
- # The resulting url is in the form: <tt>http(s)://:server/:bucket_name/:table_name/:id/:file</tt> where
271
- # the <tt>:server</tt> variable defaults to <tt>AWS::S3 URL::DEFAULT_HOST</tt> (s3.amazonaws.com) and can be
272
- # set using the configuration parameters in <tt>RAILS_ROOT/config/amazon_s3.yml</tt>.
273
- #
274
- # The optional thumbnail argument will output the thumbnail's filename (if any).
275
- def s3_url(thumbnail = nil)
276
- File.join(s3_protocol + s3_hostname + s3_port_string, bucket_name, full_filename(thumbnail))
277
- end
278
-
279
- # All public objects are accessible via a GET request to CloudFront. You can generate a
280
- # url for an object using the cloudfront_url method.
281
- #
282
- # @photo.cloudfront_url
283
- #
284
- # The resulting url is in the form: <tt>http://:distribution_domain/:table_name/:id/:file</tt> using
285
- # the <tt>:distribution_domain</tt> variable set in the configuration parameters in <tt>RAILS_ROOT/config/amazon_s3.yml</tt>.
286
- #
287
- # The optional thumbnail argument will output the thumbnail's filename (if any).
288
- def cloudfront_url(thumbnail = nil)
289
- "http://" + cloudfront_distribution_domain + "/" + full_filename(thumbnail)
290
- end
291
-
292
- def public_filename(*args)
293
- if attachment_options[:cloudfront]
294
- cloudfront_url(args)
295
- else
296
- s3_url(args)
297
- end
298
- end
299
-
300
- # All private objects are accessible via an authenticated GET request to the S3 servers. You can generate an
301
- # authenticated url for an object like this:
302
- #
303
- # @photo.authenticated_s3_url
304
- #
305
- # By default authenticated urls expire 5 minutes after they were generated.
306
- #
307
- # Expiration options can be specified either with an absolute time using the <tt>:expires</tt> option,
308
- # or with a number of seconds relative to now with the <tt>:expires_in</tt> option:
309
- #
310
- # # Absolute expiration date (October 13th, 2025)
311
- # @photo.authenticated_s3_url(:expires => Time.mktime(2025,10,13).to_i)
312
- #
313
- # # Expiration in five hours from now
314
- # @photo.authenticated_s3_url(:expires_in => 5.hours)
315
- #
316
- # You can specify whether the url should go over SSL with the <tt>:use_ssl</tt> option.
317
- # By default, the ssl settings for the current connection will be used:
318
- #
319
- # @photo.authenticated_s3_url(:use_ssl => true)
320
- #
321
- # Finally, the optional thumbnail argument will output the thumbnail's filename (if any):
322
- #
323
- # @photo.authenticated_s3_url('thumbnail', :expires_in => 5.hours, :use_ssl => true)
324
- def authenticated_s3_url(*args)
325
- options = args.extract_options!
326
- options[:expires_in] = options[:expires_in].to_i if options[:expires_in]
327
- thumbnail = args.shift
328
- S3Object.url_for(full_filename(thumbnail), bucket_name, options)
329
- end
330
-
331
- def create_temp_file
332
- write_to_temp_file current_data
333
- end
334
-
335
- def current_data
336
- S3Object.value full_filename, bucket_name
337
- end
338
-
339
- def s3_protocol
340
- Technoweenie::AttachmentFu::Backends::S3Backend.protocol
341
- end
342
-
343
- def s3_hostname
344
- Technoweenie::AttachmentFu::Backends::S3Backend.hostname
345
- end
346
-
347
- def s3_port_string
348
- Technoweenie::AttachmentFu::Backends::S3Backend.port_string
349
- end
350
-
351
- def cloudfront_distribution_domain
352
- Technoweenie::AttachmentFu::Backends::S3Backend.distribution_domain
353
- end
354
-
355
- protected
356
- # Called in the after_destroy callback
357
- def destroy_file
358
- S3Object.delete full_filename, bucket_name
359
- end
360
-
361
- def rename_file
362
- return unless @old_filename && @old_filename != filename
363
-
364
- old_full_filename = File.join(base_path, @old_filename)
365
-
366
- S3Object.rename(
367
- old_full_filename,
368
- full_filename,
369
- bucket_name,
370
- :access => attachment_options[:s3_access]
371
- )
372
-
373
- @old_filename = nil
374
- true
375
- end
376
-
377
- def save_to_storage
378
- if save_attachment?
379
- S3Object.store(
380
- full_filename,
381
- (temp_path ? File.open(temp_path) : temp_data),
382
- bucket_name,
383
- :content_type => content_type,
384
- :access => attachment_options[:s3_access]
385
- )
386
- end
387
-
388
- @old_filename = nil
389
- true
390
- end
391
- end
392
- end
393
- end
394
- end
@@ -1,59 +0,0 @@
1
- require 'red_artisan/core_image/processor'
2
-
3
- module Technoweenie # :nodoc:
4
- module AttachmentFu # :nodoc:
5
- module Processors
6
- module CoreImageProcessor
7
- def self.included(base)
8
- base.send :extend, ClassMethods
9
- base.alias_method_chain :process_attachment, :processing
10
- end
11
-
12
- module ClassMethods
13
- def with_image(file, &block)
14
- block.call OSX::CIImage.from(file)
15
- end
16
- end
17
-
18
- protected
19
- def process_attachment_with_processing
20
- return unless process_attachment_without_processing
21
- with_image do |img|
22
- self.width = img.extent.size.width if respond_to?(:width)
23
- self.height = img.extent.size.height if respond_to?(:height)
24
- resize_image_or_thumbnail! img
25
- callback_with_args :after_resize, img
26
- end if image?
27
- end
28
-
29
- # Performs the actual resizing operation for a thumbnail
30
- def resize_image(img, size)
31
- processor = ::RedArtisan::CoreImage::Processor.new(img)
32
- size = size.first if size.is_a?(Array) && size.length == 1
33
- if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
34
- if size.is_a?(Fixnum)
35
- processor.fit(size)
36
- else
37
- processor.resize(size[0], size[1])
38
- end
39
- else
40
- new_size = [img.extent.size.width, img.extent.size.height] / size.to_s
41
- processor.resize(new_size[0], new_size[1])
42
- end
43
-
44
- processor.render do |result|
45
- self.width = result.extent.size.width if respond_to?(:width)
46
- self.height = result.extent.size.height if respond_to?(:height)
47
-
48
- # Get a new temp_path for the image before saving
49
- temp_paths.unshift Tempfile.new(random_tempfile_filename, Technoweenie::AttachmentFu.tempfile_path).path
50
- result.save self.temp_path, OSX::NSJPEGFileType
51
- self.size = File.size(self.temp_path)
52
- end
53
- end
54
- end
55
- end
56
- end
57
- end
58
-
59
-
@@ -1,54 +0,0 @@
1
- require 'rubygems'
2
- require 'gd2'
3
- module Technoweenie # :nodoc:
4
- module AttachmentFu # :nodoc:
5
- module Processors
6
- module Gd2Processor
7
- def self.included(base)
8
- base.send :extend, ClassMethods
9
- base.alias_method_chain :process_attachment, :processing
10
- end
11
-
12
- module ClassMethods
13
- # Yields a block containing a GD2 Image for the given binary data.
14
- def with_image(file, &block)
15
- im = GD2::Image.import(file)
16
- block.call(im)
17
- end
18
- end
19
-
20
- protected
21
- def process_attachment_with_processing
22
- return unless process_attachment_without_processing && image?
23
- with_image do |img|
24
- resize_image_or_thumbnail! img
25
- self.width = img.width
26
- self.height = img.height
27
- callback_with_args :after_resize, img
28
- end
29
- end
30
-
31
- # Performs the actual resizing operation for a thumbnail
32
- def resize_image(img, size)
33
- size = size.first if size.is_a?(Array) && size.length == 1
34
- if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
35
- if size.is_a?(Fixnum)
36
- # Borrowed from image science's #thumbnail method and adapted
37
- # for this.
38
- scale = size.to_f / (img.width > img.height ? img.width.to_f : img.height.to_f)
39
- img.resize!((img.width * scale).round(1), (img.height * scale).round(1), false)
40
- else
41
- img.resize!(size.first, size.last, false)
42
- end
43
- else
44
- w, h = [img.width, img.height] / size.to_s
45
- img.resize!(w, h, false)
46
- end
47
- temp_paths.unshift random_tempfile_filename
48
- self.size = img.export(self.temp_path)
49
- end
50
-
51
- end
52
- end
53
- end
54
- end
@@ -1,61 +0,0 @@
1
- require 'image_science'
2
- module Technoweenie # :nodoc:
3
- module AttachmentFu # :nodoc:
4
- module Processors
5
- module ImageScienceProcessor
6
- def self.included(base)
7
- base.send :extend, ClassMethods
8
- base.alias_method_chain :process_attachment, :processing
9
- end
10
-
11
- module ClassMethods
12
- # Yields a block containing an Image Science image for the given binary data.
13
- def with_image(file, &block)
14
- ::ImageScience.with_image file, &block
15
- end
16
- end
17
-
18
- protected
19
- def process_attachment_with_processing
20
- return unless process_attachment_without_processing && image?
21
- with_image do |img|
22
- self.width = img.width if respond_to?(:width)
23
- self.height = img.height if respond_to?(:height)
24
- resize_image_or_thumbnail! img
25
- end
26
- end
27
-
28
- # Performs the actual resizing operation for a thumbnail
29
- def resize_image(img, size)
30
- # create a dummy temp file to write to
31
- # ImageScience doesn't handle all gifs properly, so it converts them to
32
- # pngs for thumbnails. It has something to do with trying to save gifs
33
- # with a larger palette than 256 colors, which is all the gif format
34
- # supports.
35
- filename.sub! /gif$/, 'png'
36
- content_type.sub!(/gif$/, 'png')
37
- temp_paths.unshift write_to_temp_file(filename)
38
- grab_dimensions = lambda do |img|
39
- self.width = img.width if respond_to?(:width)
40
- self.height = img.height if respond_to?(:height)
41
- img.save self.temp_path
42
- self.size = File.size(self.temp_path)
43
- callback_with_args :after_resize, img
44
- end
45
-
46
- size = size.first if size.is_a?(Array) && size.length == 1
47
- if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
48
- if size.is_a?(Fixnum)
49
- img.thumbnail(size, &grab_dimensions)
50
- else
51
- img.resize(size[0], size[1], &grab_dimensions)
52
- end
53
- else
54
- new_size = [img.width, img.height] / size.to_s
55
- img.resize(new_size[0], new_size[1], &grab_dimensions)
56
- end
57
- end
58
- end
59
- end
60
- end
61
- end