durran-carrierwave 0.3.2.3

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 (91) hide show
  1. data/Generators +4 -0
  2. data/History.txt +66 -0
  3. data/LICENSE +8 -0
  4. data/Manifest.txt +89 -0
  5. data/README.rdoc +342 -0
  6. data/Rakefile +30 -0
  7. data/carrierwave.gemspec +57 -0
  8. data/cucumber.yml +2 -0
  9. data/features/caching.feature +28 -0
  10. data/features/file_storage.feature +37 -0
  11. data/features/file_storage_overridden_filename.feature +38 -0
  12. data/features/file_storage_overridden_store_dir.feature +38 -0
  13. data/features/file_storage_reversing_processor.feature +43 -0
  14. data/features/fixtures/bork.txt +1 -0
  15. data/features/fixtures/monkey.txt +1 -0
  16. data/features/mount_activerecord.feature +46 -0
  17. data/features/mount_datamapper.feature +46 -0
  18. data/features/step_definitions/activerecord_steps.rb +22 -0
  19. data/features/step_definitions/caching_steps.rb +14 -0
  20. data/features/step_definitions/datamapper_steps.rb +29 -0
  21. data/features/step_definitions/file_steps.rb +42 -0
  22. data/features/step_definitions/general_steps.rb +80 -0
  23. data/features/step_definitions/mount_steps.rb +19 -0
  24. data/features/step_definitions/store_steps.rb +18 -0
  25. data/features/support/activerecord.rb +30 -0
  26. data/features/support/datamapper.rb +7 -0
  27. data/features/support/env.rb +35 -0
  28. data/features/versions_basics.feature +50 -0
  29. data/features/versions_nested_versions.feature +70 -0
  30. data/features/versions_overridden_filename.feature +51 -0
  31. data/features/versions_overriden_store_dir.feature +41 -0
  32. data/lib/carrierwave.rb +145 -0
  33. data/lib/carrierwave/compatibility/paperclip.rb +95 -0
  34. data/lib/carrierwave/core_ext/blank.rb +46 -0
  35. data/lib/carrierwave/core_ext/inheritable_attributes.rb +104 -0
  36. data/lib/carrierwave/core_ext/module_setup.rb +51 -0
  37. data/lib/carrierwave/mount.rb +332 -0
  38. data/lib/carrierwave/orm/activerecord.rb +73 -0
  39. data/lib/carrierwave/orm/datamapper.rb +27 -0
  40. data/lib/carrierwave/orm/mongomapper.rb +27 -0
  41. data/lib/carrierwave/orm/sequel.rb +57 -0
  42. data/lib/carrierwave/processing/image_science.rb +72 -0
  43. data/lib/carrierwave/processing/rmagick.rb +286 -0
  44. data/lib/carrierwave/sanitized_file.rb +272 -0
  45. data/lib/carrierwave/storage/abstract.rb +32 -0
  46. data/lib/carrierwave/storage/file.rb +50 -0
  47. data/lib/carrierwave/storage/s3.rb +215 -0
  48. data/lib/carrierwave/test/matchers.rb +114 -0
  49. data/lib/carrierwave/uploader.rb +43 -0
  50. data/lib/carrierwave/uploader/cache.rb +116 -0
  51. data/lib/carrierwave/uploader/callbacks.rb +42 -0
  52. data/lib/carrierwave/uploader/default_path.rb +23 -0
  53. data/lib/carrierwave/uploader/extension_whitelist.rb +37 -0
  54. data/lib/carrierwave/uploader/mountable.rb +39 -0
  55. data/lib/carrierwave/uploader/paths.rb +27 -0
  56. data/lib/carrierwave/uploader/processing.rb +81 -0
  57. data/lib/carrierwave/uploader/proxy.rb +62 -0
  58. data/lib/carrierwave/uploader/remove.rb +23 -0
  59. data/lib/carrierwave/uploader/store.rb +156 -0
  60. data/lib/carrierwave/uploader/url.rb +24 -0
  61. data/lib/carrierwave/uploader/versions.rb +147 -0
  62. data/lib/generators/uploader_generator.rb +22 -0
  63. data/rails_generators/uploader/USAGE +2 -0
  64. data/rails_generators/uploader/templates/uploader.rb +47 -0
  65. data/rails_generators/uploader/uploader_generator.rb +21 -0
  66. data/script/console +10 -0
  67. data/script/destroy +14 -0
  68. data/script/generate +14 -0
  69. data/spec/compatibility/paperclip_spec.rb +43 -0
  70. data/spec/fixtures/bork.txt +1 -0
  71. data/spec/fixtures/test.jpeg +1 -0
  72. data/spec/fixtures/test.jpg +1 -0
  73. data/spec/mount_spec.rb +517 -0
  74. data/spec/orm/activerecord_spec.rb +271 -0
  75. data/spec/orm/datamapper_spec.rb +161 -0
  76. data/spec/orm/mongomapper_spec.rb +184 -0
  77. data/spec/orm/sequel_spec.rb +192 -0
  78. data/spec/sanitized_file_spec.rb +612 -0
  79. data/spec/spec_helper.rb +99 -0
  80. data/spec/uploader/cache_spec.rb +196 -0
  81. data/spec/uploader/default_path_spec.rb +68 -0
  82. data/spec/uploader/extension_whitelist_spec.rb +44 -0
  83. data/spec/uploader/mountable_spec.rb +33 -0
  84. data/spec/uploader/paths_spec.rb +22 -0
  85. data/spec/uploader/processing_spec.rb +62 -0
  86. data/spec/uploader/proxy_spec.rb +54 -0
  87. data/spec/uploader/remove_spec.rb +70 -0
  88. data/spec/uploader/store_spec.rb +274 -0
  89. data/spec/uploader/url_spec.rb +87 -0
  90. data/spec/uploader/versions_spec.rb +306 -0
  91. metadata +228 -0
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+
3
+ require 'activerecord'
4
+
5
+ module CarrierWave
6
+ module ActiveRecord
7
+
8
+ include CarrierWave::Mount
9
+
10
+ ##
11
+ # See +CarrierWave::Mount#mount_uploader+ for documentation
12
+ #
13
+ def mount_uploader(column, uploader, options={}, &block)
14
+ super
15
+
16
+ alias_method :read_uploader, :read_attribute
17
+ alias_method :write_uploader, :write_attribute
18
+
19
+ validates_integrity_of column if uploader_options[column.to_sym][:validate_integrity]
20
+ validates_processing_of column if uploader_options[column.to_sym][:validate_processing]
21
+
22
+ after_save "store_#{column}!"
23
+ before_save "write_#{column}_identifier"
24
+ after_destroy "remove_#{column}!"
25
+ end
26
+
27
+ ##
28
+ # Makes the record invalid if the file couldn't be uploaded due to an integrity error
29
+ #
30
+ # Accepts the usual parameters for validations in Rails (:if, :unless, etc...)
31
+ #
32
+ # === Note
33
+ #
34
+ # Set this key in your translations file for I18n:
35
+ #
36
+ # carrierwave:
37
+ # errors:
38
+ # integrity: 'Here be an error message'
39
+ #
40
+ def validates_integrity_of(*attrs)
41
+ options = attrs.last.is_a?(Hash) ? attrs.last : {}
42
+ options[:message] ||= I18n.t('carrierwave.errors.integrity', :default => 'is not an allowed type of file.')
43
+ validates_each(*attrs) do |record, attr, value|
44
+ record.errors.add attr, options[:message] if record.send("#{attr}_integrity_error")
45
+ end
46
+ end
47
+
48
+ ##
49
+ # Makes the record invalid if the file couldn't be processed (assuming the process failed
50
+ # with a CarrierWave::ProcessingError)
51
+ #
52
+ # Accepts the usual parameters for validations in Rails (:if, :unless, etc...)
53
+ #
54
+ # === Note
55
+ #
56
+ # Set this key in your translations file for I18n:
57
+ #
58
+ # carrierwave:
59
+ # errors:
60
+ # processing: 'Here be an error message'
61
+ #
62
+ def validates_processing_of(*attrs)
63
+ options = attrs.last.is_a?(Hash) ? attrs.last : {}
64
+ options[:message] ||= I18n.t('carrierwave.errors.processing', :default => 'failed to be processed.')
65
+ validates_each(*attrs) do |record, attr, value|
66
+ record.errors.add attr, options[:message] if record.send("#{attr}_processing_error")
67
+ end
68
+ end
69
+
70
+ end # ActiveRecord
71
+ end # CarrierWave
72
+
73
+ ActiveRecord::Base.send(:extend, CarrierWave::ActiveRecord)
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+
3
+ require 'dm-core'
4
+
5
+ module CarrierWave
6
+ module DataMapper
7
+
8
+ include CarrierWave::Mount
9
+
10
+ ##
11
+ # See +CarrierWave::Mount#mount_uploader+ for documentation
12
+ #
13
+ def mount_uploader(column, uploader, options={}, &block)
14
+ super
15
+
16
+ alias_method :read_uploader, :attribute_get
17
+ alias_method :write_uploader, :attribute_set
18
+
19
+ after :save, "store_#{column}!".to_sym
20
+ before :save, "write_#{column}_identifier".to_sym
21
+ after :destroy, "remove_#{column}!".to_sym
22
+ end
23
+
24
+ end # DataMapper
25
+ end # CarrierWave
26
+
27
+ DataMapper::Model.send(:include, CarrierWave::DataMapper)
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ require 'mongomapper'
3
+
4
+ module CarrierWave
5
+ module MongoMapper
6
+ include CarrierWave::Mount
7
+ ##
8
+ # See +CarrierWave::Mount#mount_uploader+ for documentation
9
+ #
10
+ def mount_uploader(column, uploader, options={}, &block)
11
+ # We need to set the mount_on column (or key in MongoMapper's case)
12
+ # since MongoMapper will attempt to set the filename on
13
+ # the uploader instead of the file on a Document's initialization.
14
+ options[:mount_on] ||= "#{column}_filename"
15
+ key options[:mount_on]
16
+
17
+ super
18
+ alias_method :read_uploader, :[]
19
+ alias_method :write_uploader, :[]=
20
+ after_save "store_#{column}!".to_sym
21
+ before_save "write_#{column}_identifier".to_sym
22
+ after_destroy "remove_#{column}!".to_sym
23
+ end
24
+ end # MongoMapper
25
+ end # CarrierWave
26
+
27
+ MongoMapper::Document::ClassMethods.send(:include, CarrierWave::MongoMapper)
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+
3
+ require 'sequel'
4
+
5
+ module CarrierWave
6
+ module Sequel
7
+ include CarrierWave::Mount
8
+
9
+ def mount_uploader(column, uploader)
10
+ super
11
+
12
+ alias_method :read_uploader, :[]
13
+ alias_method :write_uploader, :[]=
14
+
15
+ if CarrierWave::Sequel.new_sequel?
16
+ include CarrierWave::Sequel::Hooks
17
+ include CarrierWave::Sequel::Validations
18
+ else
19
+ after_save "store_#{column}!"
20
+ before_save "write_#{column}_identifier"
21
+ before_destroy "remove_#{column}!"
22
+ end
23
+ end
24
+
25
+ # Determine if we're using Sequel > 2.12
26
+ #
27
+ # ==== Returns
28
+ # Bool:: True if Sequel 2.12 or higher False otherwise
29
+ def self.new_sequel?
30
+ ::Sequel::Model.respond_to?(:plugin)
31
+ end
32
+ end # Sequel
33
+ end # CarrierWave
34
+
35
+ # Instance hook methods for the Sequel 3.x
36
+ module CarrierWave::Sequel::Hooks
37
+ def after_save
38
+ return false if super == false
39
+ self.class.uploaders.each_key {|column| self.send("store_#{column}!") }
40
+ end
41
+
42
+ def before_save
43
+ return false if super == false
44
+ self.class.uploaders.each_key {|column| self.send("write_#{column}_identifier") }
45
+ end
46
+
47
+ def before_destroy
48
+ return false if super == false
49
+ self.class.uploaders.each_key {|column| self.send("remove_#{column}!") }
50
+ end
51
+ end
52
+
53
+ # Instance validation methods for the Sequel 3.x
54
+ module CarrierWave::Sequel::Validations
55
+ end
56
+
57
+ Sequel::Model.send(:extend, CarrierWave::Sequel)
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ require "image_science"
4
+
5
+ module CarrierWave
6
+ module ImageScience
7
+
8
+ # Resize the image so that it will not exceed the dimensions passed
9
+ # via geometry, geometry should be a string, formatted like '200x100' where
10
+ # the first number is the height and the second is the width
11
+ def resize!( geometry )
12
+ ::ImageScience.with_image(self.current_path) do |img|
13
+ width, height = extract_dimensions(img.width, img.height, geometry)
14
+ img.resize( width, height ) do |file|
15
+ file.save( self.current_path )
16
+ end
17
+ end
18
+ end
19
+
20
+ # Resize and crop the image so that it will have the exact dimensions passed
21
+ # via geometry, geometry should be a string, formatted like '200x100' where
22
+ # the first number is the height and the second is the width
23
+ def crop_resized!( geometry )
24
+ ::ImageScience.with_image(self.current_path) do |img|
25
+ new_width, new_height = geometry.split('x').map{|i| i.to_i }
26
+
27
+ width, height = extract_dimensions_for_crop(img.width, img.height, geometry)
28
+ x_offset, y_offset = extract_placement_for_crop(width, height, geometry)
29
+
30
+ img.resize( width, height ) do |i2|
31
+
32
+ i2.with_crop( x_offset, y_offset, new_width + x_offset, new_height + y_offset) do |file|
33
+ file.save( self.current_path )
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def extract_dimensions(width, height, new_geometry, type = :resize)
42
+ new_width, new_height = convert_geometry(new_geometry)
43
+
44
+ aspect_ratio = width.to_f / height.to_f
45
+ new_aspect_ratio = new_width / new_height
46
+
47
+ if (new_aspect_ratio > aspect_ratio) ^ ( type == :crop ) # Image is too wide, the caret is the XOR operator
48
+ new_width, new_height = [ (new_height * aspect_ratio), new_height]
49
+ else #Image is too narrow
50
+ new_width, new_height = [ new_width, (new_width / aspect_ratio)]
51
+ end
52
+
53
+ [new_width, new_height].collect! { |v| v.round }
54
+ end
55
+
56
+ def extract_dimensions_for_crop(width, height, new_geometry)
57
+ extract_dimensions(width, height, new_geometry, :crop)
58
+ end
59
+
60
+ def extract_placement_for_crop(width, height, new_geometry)
61
+ new_width, new_height = convert_geometry(new_geometry)
62
+ x_offset = (width / 2.0) - (new_width / 2.0)
63
+ y_offset = (height / 2.0) - (new_height / 2.0)
64
+ [x_offset, y_offset].collect! { |v| v.round }
65
+ end
66
+
67
+ def convert_geometry(geometry)
68
+ geometry.split('x').map{|i| i.to_f }
69
+ end
70
+
71
+ end # ImageScience
72
+ end # CarrierWave
@@ -0,0 +1,286 @@
1
+ # encoding: utf-8
2
+
3
+ unless Module.const_defined?('Magick')
4
+ begin
5
+ require 'rmagick'
6
+ rescue LoadError
7
+ require 'RMagick'
8
+ end
9
+ end
10
+
11
+ module CarrierWave
12
+
13
+ ##
14
+ # This module simplifies manipulation with RMagick by providing a set
15
+ # of convenient helper methods. If you want to use them, you'll need to
16
+ # require this file:
17
+ #
18
+ # require 'carrierwave/processing/rmagick'
19
+ #
20
+ # And then include it in your uploader:
21
+ #
22
+ # class MyUploader < CarrierWave::Uploader::Base
23
+ # include CarrierWave::RMagick
24
+ # end
25
+ #
26
+ # You can now use the provided helpers:
27
+ #
28
+ # class MyUploader < CarrierWave::Uploader::Base
29
+ # include CarrierWave::RMagick
30
+ #
31
+ # process :resize_to_fit => [200, 200]
32
+ # end
33
+ #
34
+ # Or create your own helpers with the powerful manipulate! method. Check
35
+ # out the RMagick docs at http://www.imagemagick.org/RMagick/doc/ for more
36
+ # info
37
+ #
38
+ # class MyUploader < CarrierWave::Uploader::Base
39
+ # include CarrierWave::RMagick
40
+ #
41
+ # process :do_stuff => 10.0
42
+ #
43
+ # def do_stuff(blur_factor)
44
+ # manipulate! do |img|
45
+ # img = img.sepiatone
46
+ # img = img.auto_orient
47
+ # img = img.radial_blur(blur_factor)
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # === Note
53
+ #
54
+ # You should be aware how RMagick handles memory. manipulate! takes care
55
+ # of freeing up memory for you, but for optimum memory usage you should
56
+ # use destructive operations as much as possible:
57
+ #
58
+ # DON'T DO THIS:
59
+ # img = img.resize_to_fit
60
+ #
61
+ # DO THIS INSTEAD:
62
+ # img.resize_to_fit!
63
+ #
64
+ # Read this for more information why:
65
+ #
66
+ # http://rubyforge.org/forum/forum.php?thread_id=1374&forum_id=1618
67
+ #
68
+ module RMagick
69
+
70
+ def self.included(base)
71
+ super
72
+ base.extend(ClassMethods)
73
+ end
74
+
75
+ module ClassMethods
76
+ def convert(format)
77
+ process :resize_to_limit => format
78
+ end
79
+
80
+ def resize_to_limit(width, height)
81
+ process :resize_to_limit => [width, height]
82
+ end
83
+
84
+ def resize_to_fit(width, height)
85
+ process :resize_to_fit => [width, height]
86
+ end
87
+
88
+ def resize_to_fill(width, height)
89
+ process :resize_to_fill => [width, height]
90
+ end
91
+
92
+ def resize_and_pad(width, height)
93
+ process :resize_to_fit => [width, height]
94
+ end
95
+
96
+ def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
97
+ process :resize_and_pad => [width, height, background, gravity]
98
+ end
99
+ end
100
+
101
+ ##
102
+ # Changes the image encoding format to the given format
103
+ #
104
+ # See even http://www.imagemagick.org/RMagick/doc/magick.html#formats
105
+ #
106
+ # === Parameters
107
+ #
108
+ # [format (#to_s)] an abreviation of the format
109
+ #
110
+ # === Yields
111
+ #
112
+ # [Magick::Image] additional manipulations to perform
113
+ #
114
+ # === Examples
115
+ #
116
+ # image.convert(:png)
117
+ #
118
+ def convert(format)
119
+ manipulate! do |img|
120
+ img.format = format.to_s.upcase
121
+ img = yield(img) if block_given?
122
+ img
123
+ end
124
+ end
125
+
126
+ ##
127
+ # Resize the image to fit within the specified dimensions while retaining
128
+ # the original aspect ratio. Will only resize the image if it is larger than the
129
+ # specified dimensions. The resulting image may be shorter or narrower than specified
130
+ # in the smaller dimension but will not be larger than the specified values.
131
+ #
132
+ # === Parameters
133
+ #
134
+ # [width (Integer)] the width to scale the image to
135
+ # [height (Integer)] the height to scale the image to
136
+ #
137
+ # === Yields
138
+ #
139
+ # [Magick::Image] additional manipulations to perform
140
+ #
141
+ def resize_to_limit(width, height)
142
+ manipulate! do |img|
143
+ geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
144
+ new_img = img.change_geometry(geometry) do |new_width, new_height|
145
+ img.resize(new_width, new_height)
146
+ end
147
+ destroy_image(img)
148
+ new_img = yield(new_img) if block_given?
149
+ new_img
150
+ end
151
+ end
152
+
153
+ ##
154
+ # From the RMagick documentation: "Resize the image to fit within the
155
+ # specified dimensions while retaining the original aspect ratio. The
156
+ # image may be shorter or narrower than specified in the smaller dimension
157
+ # but will not be larger than the specified values."
158
+ #
159
+ # See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fit
160
+ #
161
+ # === Parameters
162
+ #
163
+ # [width (Integer)] the width to scale the image to
164
+ # [height (Integer)] the height to scale the image to
165
+ #
166
+ # === Yields
167
+ #
168
+ # [Magick::Image] additional manipulations to perform
169
+ #
170
+ def resize_to_fit(width, height)
171
+ manipulate! do |img|
172
+ img.resize_to_fit!(width, height)
173
+ img = yield(img) if block_given?
174
+ img
175
+ end
176
+ end
177
+
178
+ alias_method :resize, :resize_to_fit
179
+
180
+ ##
181
+ # From the RMagick documentation: "Resize the image to fit within the
182
+ # specified dimensions while retaining the aspect ratio of the original
183
+ # image. If necessary, crop the image in the larger dimension."
184
+ #
185
+ # See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fill
186
+ #
187
+ # === Parameters
188
+ #
189
+ # [width (Integer)] the width to scale the image to
190
+ # [height (Integer)] the height to scale the image to
191
+ #
192
+ # === Yields
193
+ #
194
+ # [Magick::Image] additional manipulations to perform
195
+ #
196
+ def resize_to_fill(width, height)
197
+ manipulate! do |img|
198
+ img.resize_to_fill!(width, height)
199
+ img = yield(img) if block_given?
200
+ img
201
+ end
202
+ end
203
+
204
+ alias_method :crop_resized, :resize_to_fill
205
+
206
+ ##
207
+ # Resize the image to fit within the specified dimensions while retaining
208
+ # the original aspect ratio. If necessary, will pad the remaining area
209
+ # with the given color, which defaults to transparent (for gif and png,
210
+ # white for jpeg).
211
+ #
212
+ # === Parameters
213
+ #
214
+ # [width (Integer)] the width to scale the image to
215
+ # [height (Integer)] the height to scale the image to
216
+ # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
217
+ # [gravity (Magick::GravityType)] how to position the image
218
+ #
219
+ # === Yields
220
+ #
221
+ # [Magick::Image] additional manipulations to perform
222
+ #
223
+ def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
224
+ manipulate! do |img|
225
+ img.resize_to_fit!(width, height)
226
+ new_img = ::Magick::Image.new(width, height)
227
+ if background == :transparent
228
+ filled = new_img.matte_floodfill(1, 1)
229
+ else
230
+ filled = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
231
+ end
232
+ destroy_image(new_img)
233
+ filled.composite!(img, gravity, ::Magick::OverCompositeOp)
234
+ destroy_image(img)
235
+ filled = yield(filled) if block_given?
236
+ filled
237
+ end
238
+ end
239
+
240
+ ##
241
+ # Manipulate the image with RMagick. This method will load up an image
242
+ # and then pass each of its frames to the supplied block. It will then
243
+ # save the image to disk.
244
+ #
245
+ # === Gotcha
246
+ #
247
+ # This method assumes that the object responds to +current_path+.
248
+ # Any class that this module is mixed into must have a +current_path+ method.
249
+ # CarrierWave::Uploader does, so you won't need to worry about this in
250
+ # most cases.
251
+ #
252
+ # === Yields
253
+ #
254
+ # [Magick::Image] manipulations to perform
255
+ #
256
+ # === Raises
257
+ #
258
+ # [CarrierWave::ProcessingError] if manipulation failed.
259
+ #
260
+ def manipulate!
261
+ image = ::Magick::Image.read(current_path)
262
+
263
+ if image.size > 1
264
+ list = ::Magick::ImageList.new
265
+ image.each do |frame|
266
+ list << yield( frame )
267
+ end
268
+ list.write(current_path)
269
+ destroy_image(list)
270
+ else
271
+ frame = image.first
272
+ yield( frame ).write(current_path)
273
+ destroy_image(frame)
274
+ end
275
+ rescue ::Magick::ImageMagickError => e
276
+ raise CarrierWave::ProcessingError.new("Failed to manipulate with rmagick, maybe it is not an image? Original Error: #{e}")
277
+ end
278
+
279
+ private
280
+
281
+ def destroy_image(image)
282
+ image.destroy! if image.respond_to?(:destroy!)
283
+ end
284
+
285
+ end # RMagick
286
+ end # CarrierWave