carrierwave 0.11.2 → 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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +495 -173
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -4
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/error.rb +1 -0
  7. data/lib/carrierwave/locale/en.yml +11 -5
  8. data/lib/carrierwave/mount.rb +217 -182
  9. data/lib/carrierwave/mounter.rb +257 -0
  10. data/lib/carrierwave/orm/activerecord.rb +29 -35
  11. data/lib/carrierwave/processing/mini_magick.rb +169 -84
  12. data/lib/carrierwave/processing/rmagick.rb +107 -25
  13. data/lib/carrierwave/processing/vips.rb +315 -0
  14. data/lib/carrierwave/processing.rb +1 -1
  15. data/lib/carrierwave/sanitized_file.rb +105 -87
  16. data/lib/carrierwave/storage/abstract.rb +16 -3
  17. data/lib/carrierwave/storage/file.rb +71 -3
  18. data/lib/carrierwave/storage/fog.rb +228 -57
  19. data/lib/carrierwave/storage.rb +1 -9
  20. data/lib/carrierwave/test/matchers.rb +88 -19
  21. data/lib/carrierwave/uploader/cache.rb +75 -45
  22. data/lib/carrierwave/uploader/callbacks.rb +1 -3
  23. data/lib/carrierwave/uploader/configuration.rb +84 -16
  24. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  25. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  26. data/lib/carrierwave/uploader/default_url.rb +3 -5
  27. data/lib/carrierwave/uploader/dimension.rb +66 -0
  28. data/lib/carrierwave/uploader/download.rb +4 -74
  29. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  30. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  31. data/lib/carrierwave/uploader/file_size.rb +43 -0
  32. data/lib/carrierwave/uploader/mountable.rb +13 -8
  33. data/lib/carrierwave/uploader/processing.rb +51 -13
  34. data/lib/carrierwave/uploader/proxy.rb +20 -9
  35. data/lib/carrierwave/uploader/remove.rb +0 -2
  36. data/lib/carrierwave/uploader/serialization.rb +2 -4
  37. data/lib/carrierwave/uploader/store.rb +85 -28
  38. data/lib/carrierwave/uploader/url.rb +8 -7
  39. data/lib/carrierwave/uploader/versions.rb +175 -125
  40. data/lib/carrierwave/uploader.rb +12 -10
  41. data/lib/carrierwave/utilities/file_name.rb +47 -0
  42. data/lib/carrierwave/utilities/uri.rb +14 -12
  43. data/lib/carrierwave/utilities.rb +1 -3
  44. data/lib/carrierwave/validations/active_model.rb +7 -11
  45. data/lib/carrierwave/version.rb +1 -1
  46. data/lib/carrierwave.rb +48 -21
  47. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +8 -11
  48. data/lib/generators/uploader_generator.rb +3 -3
  49. metadata +124 -86
  50. data/lib/carrierwave/locale/cs.yml +0 -11
  51. data/lib/carrierwave/locale/de.yml +0 -11
  52. data/lib/carrierwave/locale/el.yml +0 -11
  53. data/lib/carrierwave/locale/es.yml +0 -11
  54. data/lib/carrierwave/locale/fr.yml +0 -11
  55. data/lib/carrierwave/locale/ja.yml +0 -11
  56. data/lib/carrierwave/locale/nb.yml +0 -11
  57. data/lib/carrierwave/locale/nl.yml +0 -11
  58. data/lib/carrierwave/locale/pl.yml +0 -11
  59. data/lib/carrierwave/locale/pt-BR.yml +0 -11
  60. data/lib/carrierwave/locale/pt-PT.yml +0 -11
  61. data/lib/carrierwave/locale/ru.yml +0 -11
  62. data/lib/carrierwave/locale/sk.yml +0 -11
  63. data/lib/carrierwave/locale/tr.yml +0 -11
  64. data/lib/carrierwave/processing/mime_types.rb +0 -74
  65. data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
  66. data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
  67. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -47
  68. data/lib/carrierwave/uploader/extension_whitelist.rb +0 -49
  69. data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
 
5
3
  ##
@@ -23,9 +21,11 @@ module CarrierWave
23
21
  # process :resize_to_fit => [200, 200]
24
22
  # end
25
23
  #
26
- # Or create your own helpers with the powerful manipulate! method. Check
27
- # out the ImageMagick docs at http://www.imagemagick.org/script/command-line-options.php for more
28
- # 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.
29
29
  #
30
30
  # class MyUploader < CarrierWave::Uploader::Base
31
31
  # include CarrierWave::MiniMagick
@@ -33,35 +33,30 @@ module CarrierWave
33
33
  # process :radial_blur => 10
34
34
  #
35
35
  # def radial_blur(amount)
36
- # manipulate! do |img|
37
- # img.radial_blur(amount)
38
- # img = yield(img) if block_given?
39
- # img
36
+ # minimagick! do |builder|
37
+ # builder.radial_blur(amount)
38
+ # builder = yield(builder) if block_given?
39
+ # builder
40
40
  # end
41
41
  # end
42
+ # end
42
43
  #
43
44
  # === Note
44
45
  #
45
- # MiniMagick is a mini replacement for RMagick that uses the command line
46
- # 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.
47
49
  #
48
50
  # You can find more information here:
49
51
  #
50
- # http://mini_magick.rubyforge.org/
51
- # and
52
- # https://github.com/minimagic/minimagick/
52
+ # https://github.com/minimagick/minimagick/
53
53
  #
54
54
  #
55
55
  module MiniMagick
56
56
  extend ActiveSupport::Concern
57
57
 
58
58
  included do
59
- begin
60
- require "mini_magick"
61
- rescue LoadError => e
62
- e.message << " (You may need to install the mini_magick gem)"
63
- raise e
64
- end
59
+ require "image_processing/mini_magick"
65
60
  end
66
61
 
67
62
  module ClassMethods
@@ -81,9 +76,13 @@ module CarrierWave
81
76
  process :resize_to_fill => [width, height, gravity]
82
77
  end
83
78
 
84
- def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
79
+ def resize_and_pad(width, height, background=:transparent, gravity='Center')
85
80
  process :resize_and_pad => [width, height, background, gravity]
86
81
  end
82
+
83
+ def crop(left, top, width, height)
84
+ process :crop => [left, top, width, height]
85
+ end
87
86
  end
88
87
 
89
88
  ##
@@ -93,7 +92,7 @@ module CarrierWave
93
92
  #
94
93
  # === Parameters
95
94
  #
96
- # [format (#to_s)] an abreviation of the format
95
+ # [format (#to_s)] an abbreviation of the format
97
96
  #
98
97
  # === Yields
99
98
  #
@@ -103,12 +102,11 @@ module CarrierWave
103
102
  #
104
103
  # image.convert(:png)
105
104
  #
106
- def convert(format)
107
- @format = format
108
- manipulate! do |img|
109
- img.format(format.to_s.downcase)
110
- img = yield(img) if block_given?
111
- 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
112
110
  end
113
111
  end
114
112
 
@@ -122,16 +120,18 @@ module CarrierWave
122
120
  #
123
121
  # [width (Integer)] the width to scale the image to
124
122
  # [height (Integer)] the height to scale the image to
123
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
125
124
  #
126
125
  # === Yields
127
126
  #
128
127
  # [MiniMagick::Image] additional manipulations to perform
129
128
  #
130
- def resize_to_limit(width, height)
131
- manipulate! do |img|
132
- img.resize "#{width}x#{height}>"
133
- img = yield(img) if block_given?
134
- 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)
135
135
  end
136
136
  end
137
137
 
@@ -144,16 +144,18 @@ module CarrierWave
144
144
  #
145
145
  # [width (Integer)] the width to scale the image to
146
146
  # [height (Integer)] the height to scale the image to
147
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
147
148
  #
148
149
  # === Yields
149
150
  #
150
151
  # [MiniMagick::Image] additional manipulations to perform
151
152
  #
152
- def resize_to_fit(width, height)
153
- manipulate! do |img|
154
- img.resize "#{width}x#{height}"
155
- img = yield(img) if block_given?
156
- 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)
157
159
  end
158
160
  end
159
161
 
@@ -167,34 +169,18 @@ module CarrierWave
167
169
  # [width (Integer)] the width to scale the image to
168
170
  # [height (Integer)] the height to scale the image to
169
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
170
173
  #
171
174
  # === Yields
172
175
  #
173
176
  # [MiniMagick::Image] additional manipulations to perform
174
177
  #
175
- def resize_to_fill(width, height, gravity = 'Center')
176
- manipulate! do |img|
177
- cols, rows = img[:dimensions]
178
- img.combine_options do |cmd|
179
- if width != cols || height != rows
180
- scale_x = width/cols.to_f
181
- scale_y = height/rows.to_f
182
- if scale_x >= scale_y
183
- cols = (scale_x * (cols + 0.5)).round
184
- rows = (scale_x * (rows + 0.5)).round
185
- cmd.resize "#{cols}"
186
- else
187
- cols = (scale_y * (cols + 0.5)).round
188
- rows = (scale_y * (rows + 0.5)).round
189
- cmd.resize "x#{rows}"
190
- end
191
- end
192
- cmd.gravity gravity
193
- cmd.background "rgba(255,255,255,0.0)"
194
- cmd.extent "#{width}x#{height}" if cols != width || rows != height
195
- end
196
- img = yield(img) if block_given?
197
- 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)
198
184
  end
199
185
  end
200
186
 
@@ -213,33 +199,76 @@ module CarrierWave
213
199
  # [height (Integer)] the height to scale the image to
214
200
  # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
215
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
216
228
  #
217
229
  # === Yields
218
230
  #
219
231
  # [MiniMagick::Image] additional manipulations to perform
220
232
  #
221
- def resize_and_pad(width, height, background=:transparent, gravity='Center')
222
- manipulate! do |img|
223
- img.combine_options do |cmd|
224
- cmd.thumbnail "#{width}x#{height}>"
225
- if background == :transparent
226
- cmd.background "rgba(255, 255, 255, 0.0)"
227
- else
228
- cmd.background background
229
- end
230
- cmd.gravity gravity
231
- cmd.extent "#{width}x#{height}"
232
- end
233
- img = yield(img) if block_given?
234
- 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)
235
239
  end
236
240
  end
237
241
 
242
+ ##
243
+ # Returns the width of the image in pixels.
244
+ #
245
+ # === Returns
246
+ #
247
+ # [Integer] the image's width in pixels
248
+ #
249
+ def width
250
+ mini_magick_image[:width]
251
+ end
252
+
253
+ ##
254
+ # Returns the height of the image in pixels.
255
+ #
256
+ # === Returns
257
+ #
258
+ # [Integer] the image's height in pixels
259
+ #
260
+ def height
261
+ mini_magick_image[:height]
262
+ end
263
+
238
264
  ##
239
265
  # Manipulate the image with MiniMagick. This method will load up an image
240
266
  # and then pass each of its frames to the supplied block. It will then
241
267
  # save the image to disk.
242
268
  #
269
+ # NOTE: This method exists mostly for backwards compatibility, you should
270
+ # probably use #minimagick!.
271
+ #
243
272
  # === Gotcha
244
273
  #
245
274
  # This method assumes that the object responds to +current_path+.
@@ -259,19 +288,75 @@ module CarrierWave
259
288
  cache_stored_file! if !cached?
260
289
  image = ::MiniMagick::Image.open(current_path)
261
290
 
262
- begin
263
- image.format(@format.to_s.downcase) if @format
264
- image = yield(image)
265
- image.write(current_path)
266
- image.run_command("identify", current_path)
267
- ensure
268
- image.destroy!
291
+ image = yield(image)
292
+ FileUtils.mv image.path, current_path
293
+
294
+ ::MiniMagick::Image.new(current_path).identify
295
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => 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")
298
+ raise CarrierWave::ProcessingError, message
299
+ ensure
300
+ image.destroy! if image
301
+ end
302
+
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
+
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)
333
+ end
334
+
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)
269
341
  end
270
342
  rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
271
- default = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :locale => :en)
272
- message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :default => default)
343
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
344
+ message = I18n.translate(:"errors.messages.processing_error")
273
345
  raise CarrierWave::ProcessingError, message
274
346
  end
275
347
 
348
+ private
349
+
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
354
+ end
355
+ end
356
+
357
+ def mini_magick_image
358
+ ::MiniMagick::Image.read(read)
359
+ end
360
+
276
361
  end # MiniMagick
277
362
  end # CarrierWave
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
 
5
3
  ##
@@ -24,7 +22,7 @@ module CarrierWave
24
22
  # end
25
23
  #
26
24
  # Or create your own helpers with the powerful manipulate! method. Check
27
- # 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
28
26
  # info
29
27
  #
30
28
  # class MyUploader < CarrierWave::Uploader::Base
@@ -64,11 +62,20 @@ module CarrierWave
64
62
  begin
65
63
  require "rmagick"
66
64
  rescue LoadError
67
- require "RMagick"
68
- rescue LoadError => e
69
- e.message << " (You may need to install the rmagick gem)"
70
- 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
71
71
  end
72
+
73
+ prepend Module.new {
74
+ def initialize(*)
75
+ super
76
+ @format = nil
77
+ end
78
+ }
72
79
  end
73
80
 
74
81
  module ClassMethods
@@ -95,16 +102,20 @@ module CarrierWave
95
102
  def resize_to_geometry_string(geometry_string)
96
103
  process :resize_to_geometry_string => [geometry_string]
97
104
  end
105
+
106
+ def crop(left, top, width, height)
107
+ process :crop => [left, top, width, height]
108
+ end
98
109
  end
99
110
 
100
111
  ##
101
112
  # Changes the image encoding format to the given format
102
113
  #
103
- # See even http://www.imagemagick.org/RMagick/doc/magick.html#formats
114
+ # See even https://rmagick.github.io/magick.html#formats
104
115
  #
105
116
  # === Parameters
106
117
  #
107
- # [format (#to_s)] an abreviation of the format
118
+ # [format (#to_s)] an abbreviation of the format
108
119
  #
109
120
  # === Yields
110
121
  #
@@ -135,6 +146,8 @@ module CarrierWave
135
146
  # [Magick::Image] additional manipulations to perform
136
147
  #
137
148
  def resize_to_limit(width, height)
149
+ width = dimension_from width
150
+ height = dimension_from height
138
151
  manipulate! do |img|
139
152
  geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
140
153
  new_img = img.change_geometry(geometry) do |new_width, new_height|
@@ -152,7 +165,7 @@ module CarrierWave
152
165
  # image may be shorter or narrower than specified in the smaller dimension
153
166
  # but will not be larger than the specified values."
154
167
  #
155
- # 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
156
169
  #
157
170
  # === Parameters
158
171
  #
@@ -164,6 +177,8 @@ module CarrierWave
164
177
  # [Magick::Image] additional manipulations to perform
165
178
  #
166
179
  def resize_to_fit(width, height)
180
+ width = dimension_from width
181
+ height = dimension_from height
167
182
  manipulate! do |img|
168
183
  img.resize_to_fit!(width, height)
169
184
  img = yield(img) if block_given?
@@ -176,7 +191,7 @@ module CarrierWave
176
191
  # specified dimensions while retaining the aspect ratio of the original
177
192
  # image. If necessary, crop the image in the larger dimension."
178
193
  #
179
- # 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
180
195
  #
181
196
  # === Parameters
182
197
  #
@@ -188,6 +203,8 @@ module CarrierWave
188
203
  # [Magick::Image] additional manipulations to perform
189
204
  #
190
205
  def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
206
+ width = dimension_from width
207
+ height = dimension_from height
191
208
  manipulate! do |img|
192
209
  img.crop_resized!(width, height, gravity)
193
210
  img = yield(img) if block_given?
@@ -213,15 +230,11 @@ module CarrierWave
213
230
  # [Magick::Image] additional manipulations to perform
214
231
  #
215
232
  def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
233
+ width = dimension_from width
234
+ height = dimension_from height
216
235
  manipulate! do |img|
217
236
  img.resize_to_fit!(width, height)
218
- new_img = ::Magick::Image.new(width, height) { self.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
219
- if background == :transparent
220
- filled = new_img.matte_floodfill(1, 1)
221
- else
222
- filled = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
223
- end
224
- 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 }
225
238
  filled.composite!(img, gravity, ::Magick::OverCompositeOp)
226
239
  destroy_image(img)
227
240
  filled = yield(filled) if block_given?
@@ -251,6 +264,55 @@ module CarrierWave
251
264
  end
252
265
  end
253
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
+
294
+ ##
295
+ # Returns the width of the image.
296
+ #
297
+ # === Returns
298
+ #
299
+ # [Integer] the image's width in pixels
300
+ #
301
+ def width
302
+ rmagick_image.columns
303
+ end
304
+
305
+ ##
306
+ # Returns the height of the image.
307
+ #
308
+ # === Returns
309
+ #
310
+ # [Integer] the image's height in pixels
311
+ #
312
+ def height
313
+ rmagick_image.rows
314
+ end
315
+
254
316
  ##
255
317
  # Manipulate the image with RMagick. This method will load up an image
256
318
  # and then pass each of its frames to the supplied block. It will then
@@ -318,33 +380,53 @@ module CarrierWave
318
380
  frames = ::Magick::ImageList.new
319
381
 
320
382
  image.each_with_index do |frame, index|
321
- frame = yield *[frame, index, options].take(block.arity) if block_given?
383
+ frame = yield(*[frame, index, options].take(block.arity)) if block_given?
322
384
  frames << frame if frame
323
385
  end
324
386
  frames.append(true) if block_given?
325
387
 
326
388
  write_block = create_info_block(options[:write])
389
+
327
390
  if options[:format] || @format
328
391
  frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
392
+ move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
393
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
394
+ file.move_to(move_to, permissions, directory_permissions)
329
395
  else
330
396
  frames.write(current_path, &write_block)
331
397
  end
398
+
332
399
  destroy_image(frames)
333
- rescue ::Magick::ImageMagickError => e
334
- raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e, :default => I18n.translate(:"errors.messages.rmagick_processing_error", :e => e, :locale => :en))
400
+ rescue ::Magick::ImageMagickError
401
+ raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.processing_error")
335
402
  end
336
403
 
337
404
  private
338
405
 
339
406
  def create_info_block(options)
340
407
  return nil unless options
341
- assignments = options.map { |k, v| "self.#{k} = #{v}" }
342
- code = "lambda { |img| " + assignments.join(";") + "}"
343
- eval code
408
+ proc do |img|
409
+ options.each do |k, v|
410
+ if v.is_a?(String) && (matches = v.match(/^["'](.+)["']/))
411
+ CarrierWave.deprecator.warn "Passing quoted strings like #{v} to #manipulate! is deprecated, pass them without quoting."
412
+ v = matches[1]
413
+ end
414
+ img.public_send(:"#{k}=", v)
415
+ end
416
+ end
344
417
  end
345
418
 
346
419
  def destroy_image(image)
347
- image.destroy! if image.respond_to?(:destroy!)
420
+ image.try(:destroy!)
421
+ end
422
+
423
+ def dimension_from(value)
424
+ return value unless value.instance_of?(Proc)
425
+ value.arity >= 1 ? value.call(self) : value.call
426
+ end
427
+
428
+ def rmagick_image
429
+ ::Magick::Image.from_blob(self.read).first
348
430
  end
349
431
 
350
432
  end # RMagick