carrierwave 1.3.1 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of carrierwave might be problematic. Click here for more details.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +238 -91
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -2
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/locale/en.yml +9 -6
  7. data/lib/carrierwave/mount.rb +53 -61
  8. data/lib/carrierwave/mounter.rb +167 -77
  9. data/lib/carrierwave/orm/activerecord.rb +15 -55
  10. data/lib/carrierwave/processing/mini_magick.rb +108 -123
  11. data/lib/carrierwave/processing/rmagick.rb +20 -18
  12. data/lib/carrierwave/processing/vips.rb +284 -0
  13. data/lib/carrierwave/processing.rb +1 -0
  14. data/lib/carrierwave/sanitized_file.rb +66 -73
  15. data/lib/carrierwave/storage/abstract.rb +5 -5
  16. data/lib/carrierwave/storage/file.rb +6 -5
  17. data/lib/carrierwave/storage/fog.rb +101 -64
  18. data/lib/carrierwave/storage.rb +1 -0
  19. data/lib/carrierwave/test/matchers.rb +11 -7
  20. data/lib/carrierwave/uploader/cache.rb +40 -24
  21. data/lib/carrierwave/uploader/callbacks.rb +1 -1
  22. data/lib/carrierwave/uploader/configuration.rb +38 -19
  23. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  24. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  25. data/lib/carrierwave/uploader/dimension.rb +66 -0
  26. data/lib/carrierwave/uploader/download.rb +2 -80
  27. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  28. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  29. data/lib/carrierwave/uploader/file_size.rb +2 -2
  30. data/lib/carrierwave/uploader/mountable.rb +6 -0
  31. data/lib/carrierwave/uploader/processing.rb +42 -7
  32. data/lib/carrierwave/uploader/proxy.rb +17 -4
  33. data/lib/carrierwave/uploader/serialization.rb +1 -1
  34. data/lib/carrierwave/uploader/store.rb +47 -7
  35. data/lib/carrierwave/uploader/url.rb +7 -4
  36. data/lib/carrierwave/uploader/versions.rb +153 -105
  37. data/lib/carrierwave/uploader.rb +10 -17
  38. data/lib/carrierwave/utilities/file_name.rb +47 -0
  39. data/lib/carrierwave/utilities/uri.rb +14 -11
  40. data/lib/carrierwave/utilities.rb +1 -0
  41. data/lib/carrierwave/validations/active_model.rb +7 -9
  42. data/lib/carrierwave/version.rb +1 -1
  43. data/lib/carrierwave.rb +13 -17
  44. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +3 -3
  45. data/lib/generators/uploader_generator.rb +3 -3
  46. metadata +104 -38
  47. data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
  48. data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
  49. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -51
  50. data/lib/carrierwave/uploader/extension_whitelist.rb +0 -52
@@ -21,9 +21,11 @@ module CarrierWave
21
21
  # process :resize_to_fit => [200, 200]
22
22
  # end
23
23
  #
24
- # Or create your own helpers with the powerful manipulate! method. Check
25
- # out the ImageMagick docs at http://www.imagemagick.org/script/command-line-options.php for more
26
- # info
24
+ # Or create your own helpers with the powerful minimagick! method, which
25
+ # yields an ImageProcessing::Builder object. Check out the ImageProcessing
26
+ # docs at http://github.com/janko-m/image_processing and the list of all
27
+ # available ImageMagick options at
28
+ # http://www.imagemagick.org/script/command-line-options.php for more info.
27
29
  #
28
30
  # class MyUploader < CarrierWave::Uploader::Base
29
31
  # include CarrierWave::MiniMagick
@@ -31,23 +33,22 @@ module CarrierWave
31
33
  # process :radial_blur => 10
32
34
  #
33
35
  # def radial_blur(amount)
34
- # manipulate! do |img|
35
- # img.radial_blur(amount)
36
- # img = yield(img) if block_given?
37
- # img
36
+ # minimagick! do |builder|
37
+ # builder.radial_blur(amount)
38
+ # builder = yield(builder) if block_given?
39
+ # builder
38
40
  # end
39
41
  # end
40
42
  # end
41
43
  #
42
44
  # === Note
43
45
  #
44
- # MiniMagick is a mini replacement for RMagick that uses the command line
45
- # tool "mogrify" for image manipulation.
46
+ # The ImageProcessing gem uses MiniMagick, a mini replacement for RMagick
47
+ # that uses ImageMagick command-line tools, to build a "convert" command that
48
+ # performs the processing.
46
49
  #
47
50
  # You can find more information here:
48
51
  #
49
- # http://mini_magick.rubyforge.org/
50
- # and
51
52
  # https://github.com/minimagick/minimagick/
52
53
  #
53
54
  #
@@ -55,19 +56,7 @@ module CarrierWave
55
56
  extend ActiveSupport::Concern
56
57
 
57
58
  included do
58
- begin
59
- require "mini_magick"
60
- rescue LoadError => e
61
- e.message << " (You may need to install the mini_magick gem)"
62
- raise e
63
- end
64
-
65
- prepend Module.new {
66
- def initialize(*)
67
- super
68
- @format = nil
69
- end
70
- }
59
+ require "image_processing/mini_magick"
71
60
  end
72
61
 
73
62
  module ClassMethods
@@ -99,7 +88,7 @@ module CarrierWave
99
88
  #
100
89
  # === Parameters
101
90
  #
102
- # [format (#to_s)] an abreviation of the format
91
+ # [format (#to_s)] an abbreviation of the format
103
92
  #
104
93
  # === Yields
105
94
  #
@@ -109,12 +98,11 @@ module CarrierWave
109
98
  #
110
99
  # image.convert(:png)
111
100
  #
112
- def convert(format, page=nil)
113
- @format = format
114
- @page = page
115
- manipulate! do |img|
116
- img = yield(img) if block_given?
117
- img
101
+ def convert(format, page=nil, &block)
102
+ minimagick!(block) do |builder|
103
+ builder = builder.convert(format)
104
+ builder = builder.loader(page: page) if page
105
+ builder
118
106
  end
119
107
  end
120
108
 
@@ -128,21 +116,18 @@ module CarrierWave
128
116
  #
129
117
  # [width (Integer)] the width to scale the image to
130
118
  # [height (Integer)] the height to scale the image to
119
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
131
120
  #
132
121
  # === Yields
133
122
  #
134
123
  # [MiniMagick::Image] additional manipulations to perform
135
124
  #
136
- def resize_to_limit(width, height, combine_options: {})
137
- width = dimension_from width
138
- height = dimension_from height
139
- manipulate! do |img|
140
- img.combine_options do |cmd|
141
- cmd.resize "#{width}x#{height}>"
142
- append_combine_options cmd, combine_options
143
- end
144
- img = yield(img) if block_given?
145
- img
125
+ def resize_to_limit(width, height, combine_options: {}, &block)
126
+ width, height = resolve_dimensions(width, height)
127
+
128
+ minimagick!(block) do |builder|
129
+ builder.resize_to_limit(width, height)
130
+ .apply(combine_options)
146
131
  end
147
132
  end
148
133
 
@@ -155,21 +140,18 @@ module CarrierWave
155
140
  #
156
141
  # [width (Integer)] the width to scale the image to
157
142
  # [height (Integer)] the height to scale the image to
143
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
158
144
  #
159
145
  # === Yields
160
146
  #
161
147
  # [MiniMagick::Image] additional manipulations to perform
162
148
  #
163
- def resize_to_fit(width, height, combine_options: {})
164
- width = dimension_from width
165
- height = dimension_from height
166
- manipulate! do |img|
167
- img.combine_options do |cmd|
168
- cmd.resize "#{width}x#{height}"
169
- append_combine_options cmd, combine_options
170
- end
171
- img = yield(img) if block_given?
172
- img
149
+ def resize_to_fit(width, height, combine_options: {}, &block)
150
+ width, height = resolve_dimensions(width, height)
151
+
152
+ minimagick!(block) do |builder|
153
+ builder.resize_to_fit(width, height)
154
+ .apply(combine_options)
173
155
  end
174
156
  end
175
157
 
@@ -183,37 +165,18 @@ module CarrierWave
183
165
  # [width (Integer)] the width to scale the image to
184
166
  # [height (Integer)] the height to scale the image to
185
167
  # [gravity (String)] the current gravity suggestion (default: 'Center'; options: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast')
168
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
186
169
  #
187
170
  # === Yields
188
171
  #
189
172
  # [MiniMagick::Image] additional manipulations to perform
190
173
  #
191
- def resize_to_fill(width, height, gravity = 'Center', combine_options: {})
192
- width = dimension_from width
193
- height = dimension_from height
194
- manipulate! do |img|
195
- cols, rows = img[:dimensions]
196
- img.combine_options do |cmd|
197
- if width != cols || height != rows
198
- scale_x = width/cols.to_f
199
- scale_y = height/rows.to_f
200
- if scale_x >= scale_y
201
- cols = (scale_x * (cols + 0.5)).round
202
- rows = (scale_x * (rows + 0.5)).round
203
- cmd.resize "#{cols}"
204
- else
205
- cols = (scale_y * (cols + 0.5)).round
206
- rows = (scale_y * (rows + 0.5)).round
207
- cmd.resize "x#{rows}"
208
- end
209
- end
210
- cmd.gravity gravity
211
- cmd.background "rgba(255,255,255,0.0)"
212
- cmd.extent "#{width}x#{height}" if cols != width || rows != height
213
- append_combine_options cmd, combine_options
214
- end
215
- img = yield(img) if block_given?
216
- img
174
+ def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
175
+ width, height = resolve_dimensions(width, height)
176
+
177
+ minimagick!(block) do |builder|
178
+ builder.resize_to_fill(width, height, gravity: gravity)
179
+ .apply(combine_options)
217
180
  end
218
181
  end
219
182
 
@@ -232,28 +195,18 @@ module CarrierWave
232
195
  # [height (Integer)] the height to scale the image to
233
196
  # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
234
197
  # [gravity (String)] how to position the image
198
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
235
199
  #
236
200
  # === Yields
237
201
  #
238
202
  # [MiniMagick::Image] additional manipulations to perform
239
203
  #
240
- def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {})
241
- width = dimension_from width
242
- height = dimension_from height
243
- manipulate! do |img|
244
- img.combine_options do |cmd|
245
- cmd.thumbnail "#{width}x#{height}>"
246
- if background == :transparent
247
- cmd.background "rgba(255, 255, 255, 0.0)"
248
- else
249
- cmd.background background
250
- end
251
- cmd.gravity gravity
252
- cmd.extent "#{width}x#{height}"
253
- append_combine_options cmd, combine_options
254
- end
255
- img = yield(img) if block_given?
256
- img
204
+ def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
205
+ width, height = resolve_dimensions(width, height)
206
+
207
+ minimagick!(block) do |builder|
208
+ builder.resize_and_pad(width, height, background: background, gravity: gravity)
209
+ .apply(combine_options)
257
210
  end
258
211
  end
259
212
 
@@ -284,6 +237,9 @@ module CarrierWave
284
237
  # and then pass each of its frames to the supplied block. It will then
285
238
  # save the image to disk.
286
239
  #
240
+ # NOTE: This method exists mostly for backwards compatibility, you should
241
+ # probably use #minimagick!.
242
+ #
287
243
  # === Gotcha
288
244
  #
289
245
  # This method assumes that the object responds to +current_path+.
@@ -303,46 +259,75 @@ module CarrierWave
303
259
  cache_stored_file! if !cached?
304
260
  image = ::MiniMagick::Image.open(current_path)
305
261
 
306
- begin
307
- image.format(@format.to_s.downcase, @page) if @format
308
- image = yield(image)
309
- image.write(current_path)
262
+ image = yield(image)
263
+ FileUtils.mv image.path, current_path
310
264
 
311
- if @format
312
- move_to = current_path.chomp(File.extname(current_path)) + ".#{@format}"
313
- file.content_type = ::MIME::Types.type_for(move_to).first.to_s
314
- file.move_to(move_to, permissions, directory_permissions)
315
- end
316
-
317
- image.run_command("identify", current_path)
318
- ensure
319
- image.destroy!
320
- end
265
+ image.run_command("identify", current_path)
321
266
  rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
322
- message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e)
267
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
268
+ message = I18n.translate(:"errors.messages.processing_error")
323
269
  raise CarrierWave::ProcessingError, message
270
+ ensure
271
+ image.destroy! if image
324
272
  end
325
273
 
326
- private
274
+ # Process the image with MiniMagick, using the ImageProcessing gem. This
275
+ # method will build a "convert" ImageMagick command and execute it on the
276
+ # current image.
277
+ #
278
+ # === Gotcha
279
+ #
280
+ # This method assumes that the object responds to +current_path+.
281
+ # Any class that this module is mixed into must have a +current_path+ method.
282
+ # CarrierWave::Uploader does, so you won't need to worry about this in
283
+ # most cases.
284
+ #
285
+ # === Yields
286
+ #
287
+ # [ImageProcessing::Builder] use it to define processing to be performed
288
+ #
289
+ # === Raises
290
+ #
291
+ # [CarrierWave::ProcessingError] if processing failed.
292
+ def minimagick!(block = nil)
293
+ builder = ImageProcessing::MiniMagick.source(current_path)
294
+ builder = yield(builder)
295
+
296
+ result = builder.call
297
+ result.close
327
298
 
328
- def append_combine_options(cmd, combine_options)
329
- combine_options.each do |method, options|
330
- if options.nil?
331
- cmd.send(method)
332
- else
333
- cmd.send(method, options)
334
- end
335
- end
299
+ # backwards compatibility (we want to eventually move away from MiniMagick::Image)
300
+ if block
301
+ image = ::MiniMagick::Image.new(result.path, result)
302
+ image = block.call(image)
303
+ result = image.instance_variable_get(:@tempfile)
336
304
  end
337
305
 
338
- def dimension_from(value)
339
- return value unless value.instance_of?(Proc)
340
- value.arity >= 1 ? value.call(self) : value.call
306
+ FileUtils.mv result.path, current_path
307
+
308
+ if File.extname(result.path) != File.extname(current_path)
309
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
310
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
311
+ file.move_to(move_to, permissions, directory_permissions)
341
312
  end
313
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
314
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
315
+ message = I18n.translate(:"errors.messages.processing_error")
316
+ raise CarrierWave::ProcessingError, message
317
+ end
318
+
319
+ private
342
320
 
343
- def mini_magick_image
344
- ::MiniMagick::Image.read(read)
321
+ def resolve_dimensions(*dimensions)
322
+ dimensions.map do |value|
323
+ next value unless value.instance_of?(Proc)
324
+ value.arity >= 1 ? value.call(self) : value.call
345
325
  end
326
+ end
327
+
328
+ def mini_magick_image
329
+ ::MiniMagick::Image.read(read)
330
+ end
346
331
 
347
332
  end # MiniMagick
348
333
  end # CarrierWave
@@ -62,10 +62,12 @@ module CarrierWave
62
62
  begin
63
63
  require "rmagick"
64
64
  rescue LoadError
65
- require "RMagick"
66
- rescue LoadError => e
67
- e.message << " (You may need to install the rmagick gem)"
68
- raise e
65
+ begin
66
+ require "RMagick"
67
+ rescue LoadError => e
68
+ e.message << " (You may need to install the rmagick gem)"
69
+ raise e
70
+ end
69
71
  end
70
72
 
71
73
  prepend Module.new {
@@ -109,7 +111,7 @@ module CarrierWave
109
111
  #
110
112
  # === Parameters
111
113
  #
112
- # [format (#to_s)] an abreviation of the format
114
+ # [format (#to_s)] an abbreviation of the format
113
115
  #
114
116
  # === Yields
115
117
  #
@@ -228,13 +230,7 @@ module CarrierWave
228
230
  height = dimension_from height
229
231
  manipulate! do |img|
230
232
  img.resize_to_fit!(width, height)
231
- new_img = ::Magick::Image.new(width, height) { self.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
232
- if background == :transparent
233
- filled = new_img.matte_floodfill(1, 1)
234
- else
235
- filled = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
236
- end
237
- destroy_image(new_img)
233
+ filled = ::Magick::Image.new(width, height) { |image| image.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
238
234
  filled.composite!(img, gravity, ::Magick::OverCompositeOp)
239
235
  destroy_image(img)
240
236
  filled = yield(filled) if block_given?
@@ -363,24 +359,30 @@ module CarrierWave
363
359
  if options[:format] || @format
364
360
  frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
365
361
  move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
366
- file.content_type = ::MIME::Types.type_for(move_to).first.to_s
362
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
367
363
  file.move_to(move_to, permissions, directory_permissions)
368
364
  else
369
365
  frames.write(current_path, &write_block)
370
366
  end
371
367
 
372
368
  destroy_image(frames)
373
- rescue ::Magick::ImageMagickError => e
374
- raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e)
369
+ rescue ::Magick::ImageMagickError
370
+ raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.processing_error")
375
371
  end
376
372
 
377
373
  private
378
374
 
379
375
  def create_info_block(options)
380
376
  return nil unless options
381
- assignments = options.map { |k, v| "self.#{k} = #{v}" }
382
- code = "lambda { |img| " + assignments.join(";") + "}"
383
- eval code
377
+ proc do |img|
378
+ options.each do |k, v|
379
+ if v.is_a?(String) && (matches = v.match(/^["'](.+)["']/))
380
+ ActiveSupport::Deprecation.warn "Passing quoted strings like #{v} to #manipulate! is deprecated, pass them without quoting."
381
+ v = matches[1]
382
+ end
383
+ img.public_send(:"#{k}=", v)
384
+ end
385
+ end
384
386
  end
385
387
 
386
388
  def destroy_image(image)