carrierwave 1.3.4 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +278 -86
  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 +169 -77
  9. data/lib/carrierwave/orm/activerecord.rb +23 -58
  10. data/lib/carrierwave/processing/mini_magick.rb +137 -123
  11. data/lib/carrierwave/processing/rmagick.rb +47 -20
  12. data/lib/carrierwave/processing/vips.rb +315 -0
  13. data/lib/carrierwave/processing.rb +1 -0
  14. data/lib/carrierwave/sanitized_file.rb +83 -70
  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 +114 -62
  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 +42 -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 -123
  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 +45 -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 +73 -7
  35. data/lib/carrierwave/uploader/url.rb +7 -4
  36. data/lib/carrierwave/uploader/versions.rb +161 -111
  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 +22 -17
  44. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +5 -4
  45. data/lib/generators/uploader_generator.rb +3 -3
  46. metadata +94 -47
  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
@@ -90,6 +79,10 @@ module CarrierWave
90
79
  def resize_and_pad(width, height, background=:transparent, gravity='Center')
91
80
  process :resize_and_pad => [width, height, background, gravity]
92
81
  end
82
+
83
+ def crop(left, top, width, height)
84
+ process :crop => [left, top, width, height]
85
+ end
93
86
  end
94
87
 
95
88
  ##
@@ -99,7 +92,7 @@ module CarrierWave
99
92
  #
100
93
  # === Parameters
101
94
  #
102
- # [format (#to_s)] an abreviation of the format
95
+ # [format (#to_s)] an abbreviation of the format
103
96
  #
104
97
  # === Yields
105
98
  #
@@ -109,12 +102,11 @@ module CarrierWave
109
102
  #
110
103
  # image.convert(:png)
111
104
  #
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
105
+ def convert(format, page=nil, &block)
106
+ minimagick!(block) do |builder|
107
+ builder = builder.convert(format)
108
+ builder = builder.loader(page: page) if page
109
+ builder
118
110
  end
119
111
  end
120
112
 
@@ -128,21 +120,18 @@ module CarrierWave
128
120
  #
129
121
  # [width (Integer)] the width to scale the image to
130
122
  # [height (Integer)] the height to scale the image to
123
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
131
124
  #
132
125
  # === Yields
133
126
  #
134
127
  # [MiniMagick::Image] additional manipulations to perform
135
128
  #
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
129
+ def resize_to_limit(width, height, combine_options: {}, &block)
130
+ width, height = resolve_dimensions(width, height)
131
+
132
+ minimagick!(block) do |builder|
133
+ builder.resize_to_limit(width, height)
134
+ .apply(combine_options)
146
135
  end
147
136
  end
148
137
 
@@ -155,21 +144,18 @@ module CarrierWave
155
144
  #
156
145
  # [width (Integer)] the width to scale the image to
157
146
  # [height (Integer)] the height to scale the image to
147
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
158
148
  #
159
149
  # === Yields
160
150
  #
161
151
  # [MiniMagick::Image] additional manipulations to perform
162
152
  #
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
153
+ def resize_to_fit(width, height, combine_options: {}, &block)
154
+ width, height = resolve_dimensions(width, height)
155
+
156
+ minimagick!(block) do |builder|
157
+ builder.resize_to_fit(width, height)
158
+ .apply(combine_options)
173
159
  end
174
160
  end
175
161
 
@@ -183,37 +169,18 @@ module CarrierWave
183
169
  # [width (Integer)] the width to scale the image to
184
170
  # [height (Integer)] the height to scale the image to
185
171
  # [gravity (String)] the current gravity suggestion (default: 'Center'; options: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast')
172
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
186
173
  #
187
174
  # === Yields
188
175
  #
189
176
  # [MiniMagick::Image] additional manipulations to perform
190
177
  #
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
178
+ def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
179
+ width, height = resolve_dimensions(width, height)
180
+
181
+ minimagick!(block) do |builder|
182
+ builder.resize_to_fill(width, height, gravity: gravity)
183
+ .apply(combine_options)
217
184
  end
218
185
  end
219
186
 
@@ -232,28 +199,43 @@ module CarrierWave
232
199
  # [height (Integer)] the height to scale the image to
233
200
  # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
234
201
  # [gravity (String)] how to position the image
202
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
203
+ #
204
+ # === Yields
205
+ #
206
+ # [MiniMagick::Image] additional manipulations to perform
207
+ #
208
+ def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
209
+ width, height = resolve_dimensions(width, height)
210
+
211
+ minimagick!(block) do |builder|
212
+ builder.resize_and_pad(width, height, background: background, gravity: gravity)
213
+ .apply(combine_options)
214
+ end
215
+ end
216
+
217
+ ##
218
+ # Crop the image to the contents of a box positioned at [left] and [top], with the dimensions given
219
+ # by [width] and [height]. The original image bottom/right edge is preserved if the cropping box falls
220
+ # outside the image bounds.
221
+ #
222
+ # === Parameters
223
+ #
224
+ # [left (integer)] left edge of area to extract
225
+ # [top (integer)] top edge of area to extract
226
+ # [width (Integer)] width of area to extract
227
+ # [height (Integer)] height of area to extract
235
228
  #
236
229
  # === Yields
237
230
  #
238
231
  # [MiniMagick::Image] additional manipulations to perform
239
232
  #
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
233
+ def crop(left, top, width, height, combine_options: {}, &block)
234
+ width, height = resolve_dimensions(width, height)
235
+
236
+ minimagick!(block) do |builder|
237
+ builder.crop(left, top, width, height)
238
+ .apply(combine_options)
257
239
  end
258
240
  end
259
241
 
@@ -284,6 +266,9 @@ module CarrierWave
284
266
  # and then pass each of its frames to the supplied block. It will then
285
267
  # save the image to disk.
286
268
  #
269
+ # NOTE: This method exists mostly for backwards compatibility, you should
270
+ # probably use #minimagick!.
271
+ #
287
272
  # === Gotcha
288
273
  #
289
274
  # This method assumes that the object responds to +current_path+.
@@ -303,46 +288,75 @@ module CarrierWave
303
288
  cache_stored_file! if !cached?
304
289
  image = ::MiniMagick::Image.open(current_path)
305
290
 
306
- begin
307
- image.format(@format.to_s.downcase, @page) if @format
308
- image = yield(image)
309
- image.write(current_path)
310
-
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
291
+ image = yield(image)
292
+ FileUtils.mv image.path, current_path
316
293
 
317
- image.run_command("identify", current_path)
318
- ensure
319
- image.destroy!
320
- end
294
+ ::MiniMagick::Image.new(current_path).identify
321
295
  rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
322
- message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e)
296
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found|delegate failed)/
297
+ message = I18n.translate(:"errors.messages.processing_error")
323
298
  raise CarrierWave::ProcessingError, message
299
+ ensure
300
+ image.destroy! if image
324
301
  end
325
302
 
326
- private
303
+ # Process the image with MiniMagick, using the ImageProcessing gem. This
304
+ # method will build a "convert" ImageMagick command and execute it on the
305
+ # current image.
306
+ #
307
+ # === Gotcha
308
+ #
309
+ # This method assumes that the object responds to +current_path+.
310
+ # Any class that this module is mixed into must have a +current_path+ method.
311
+ # CarrierWave::Uploader does, so you won't need to worry about this in
312
+ # most cases.
313
+ #
314
+ # === Yields
315
+ #
316
+ # [ImageProcessing::Builder] use it to define processing to be performed
317
+ #
318
+ # === Raises
319
+ #
320
+ # [CarrierWave::ProcessingError] if processing failed.
321
+ def minimagick!(block = nil)
322
+ builder = ImageProcessing::MiniMagick.source(current_path)
323
+ builder = yield(builder)
324
+
325
+ result = builder.call
326
+ result.close
327
327
 
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
328
+ # backwards compatibility (we want to eventually move away from MiniMagick::Image)
329
+ if block
330
+ image = ::MiniMagick::Image.new(result.path, result)
331
+ image = block.call(image)
332
+ result = image.instance_variable_get(:@tempfile)
336
333
  end
337
334
 
338
- def dimension_from(value)
339
- return value unless value.instance_of?(Proc)
340
- value.arity >= 1 ? value.call(self) : value.call
335
+ FileUtils.mv result.path, current_path
336
+
337
+ if File.extname(result.path) != File.extname(current_path)
338
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
339
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
340
+ file.move_to(move_to, permissions, directory_permissions)
341
341
  end
342
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
343
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
344
+ message = I18n.translate(:"errors.messages.processing_error")
345
+ raise CarrierWave::ProcessingError, message
346
+ end
347
+
348
+ private
342
349
 
343
- def mini_magick_image
344
- ::MiniMagick::Image.read(read)
350
+ def resolve_dimensions(*dimensions)
351
+ dimensions.map do |value|
352
+ next value unless value.instance_of?(Proc)
353
+ value.arity >= 1 ? value.call(self) : value.call
345
354
  end
355
+ end
356
+
357
+ def mini_magick_image
358
+ ::MiniMagick::Image.read(read)
359
+ end
346
360
 
347
361
  end # MiniMagick
348
362
  end # CarrierWave
@@ -22,7 +22,7 @@ module CarrierWave
22
22
  # end
23
23
  #
24
24
  # Or create your own helpers with the powerful manipulate! method. Check
25
- # out the RMagick docs at http://www.imagemagick.org/RMagick/doc/ for more
25
+ # out the RMagick docs at https://rmagick.github.io/ for more
26
26
  # info
27
27
  #
28
28
  # class MyUploader < CarrierWave::Uploader::Base
@@ -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 {
@@ -100,16 +102,20 @@ module CarrierWave
100
102
  def resize_to_geometry_string(geometry_string)
101
103
  process :resize_to_geometry_string => [geometry_string]
102
104
  end
105
+
106
+ def crop(left, top, width, height)
107
+ process :crop => [left, top, width, height]
108
+ end
103
109
  end
104
110
 
105
111
  ##
106
112
  # Changes the image encoding format to the given format
107
113
  #
108
- # See even http://www.imagemagick.org/RMagick/doc/magick.html#formats
114
+ # See even https://rmagick.github.io/magick.html#formats
109
115
  #
110
116
  # === Parameters
111
117
  #
112
- # [format (#to_s)] an abreviation of the format
118
+ # [format (#to_s)] an abbreviation of the format
113
119
  #
114
120
  # === Yields
115
121
  #
@@ -159,7 +165,7 @@ module CarrierWave
159
165
  # image may be shorter or narrower than specified in the smaller dimension
160
166
  # but will not be larger than the specified values."
161
167
  #
162
- # See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fit
168
+ # See even https://rmagick.github.io/image3.html#resize_to_fit
163
169
  #
164
170
  # === Parameters
165
171
  #
@@ -185,7 +191,7 @@ module CarrierWave
185
191
  # specified dimensions while retaining the aspect ratio of the original
186
192
  # image. If necessary, crop the image in the larger dimension."
187
193
  #
188
- # See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fill
194
+ # See even https://rmagick.github.io/image3.html#resize_to_fill
189
195
  #
190
196
  # === Parameters
191
197
  #
@@ -228,13 +234,7 @@ module CarrierWave
228
234
  height = dimension_from height
229
235
  manipulate! do |img|
230
236
  img.resize_to_fit!(width, height)
231
- new_img = ::Magick::Image.new(width, height) { |img| img.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)
237
+ filled = ::Magick::Image.new(width, height) { |image| image.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
238
238
  filled.composite!(img, gravity, ::Magick::OverCompositeOp)
239
239
  destroy_image(img)
240
240
  filled = yield(filled) if block_given?
@@ -264,6 +264,33 @@ module CarrierWave
264
264
  end
265
265
  end
266
266
 
267
+ ##
268
+ # Crop the image to the contents of a box positioned at [left] and [top], with the dimensions given
269
+ # by [width] and [height]. The original image bottom/right edge is preserved if the cropping box falls
270
+ # outside the image bounds.
271
+ #
272
+ # === Parameters
273
+ #
274
+ # [left (integer)] left edge of area to extract
275
+ # [top (integer)] top edge of area to extract
276
+ # [width (Integer)] width of area to extract
277
+ # [height (Integer)] height of area to extract
278
+ #
279
+ # === Yields
280
+ #
281
+ # [Magick::Image] additional manipulations to perform
282
+ #
283
+ def crop(left, top, width, height, combine_options: {})
284
+ width = dimension_from width
285
+ height = dimension_from height
286
+
287
+ manipulate! do |img|
288
+ img.crop!(left, top, width, height)
289
+ img = yield(img) if block_given?
290
+ img
291
+ end
292
+ end
293
+
267
294
  ##
268
295
  # Returns the width of the image.
269
296
  #
@@ -363,15 +390,15 @@ module CarrierWave
363
390
  if options[:format] || @format
364
391
  frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
365
392
  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
393
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
367
394
  file.move_to(move_to, permissions, directory_permissions)
368
395
  else
369
396
  frames.write(current_path, &write_block)
370
397
  end
371
398
 
372
399
  destroy_image(frames)
373
- rescue ::Magick::ImageMagickError => e
374
- raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e)
400
+ rescue ::Magick::ImageMagickError
401
+ raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.processing_error")
375
402
  end
376
403
 
377
404
  private
@@ -381,7 +408,7 @@ module CarrierWave
381
408
  proc do |img|
382
409
  options.each do |k, v|
383
410
  if v.is_a?(String) && (matches = v.match(/^["'](.+)["']/))
384
- ActiveSupport::Deprecation.warn "Passing quoted strings like #{v} to #manipulate! is deprecated, pass them without quoting."
411
+ CarrierWave.deprecator.warn "Passing quoted strings like #{v} to #manipulate! is deprecated, pass them without quoting."
385
412
  v = matches[1]
386
413
  end
387
414
  img.public_send(:"#{k}=", v)