salebot_uploader 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +0 -0
  3. data/lib/generators/templates/uploader.rb.erb +9 -0
  4. data/lib/generators/uploader_generator.rb +7 -0
  5. data/lib/salebot_uploader/compatibility/paperclip.rb +104 -0
  6. data/lib/salebot_uploader/downloader/base.rb +101 -0
  7. data/lib/salebot_uploader/downloader/remote_file.rb +68 -0
  8. data/lib/salebot_uploader/error.rb +8 -0
  9. data/lib/salebot_uploader/locale/en.yml +17 -0
  10. data/lib/salebot_uploader/mount.rb +446 -0
  11. data/lib/salebot_uploader/mounter.rb +255 -0
  12. data/lib/salebot_uploader/orm/activerecord.rb +68 -0
  13. data/lib/salebot_uploader/processing/mini_magick.rb +194 -0
  14. data/lib/salebot_uploader/processing/rmagick.rb +402 -0
  15. data/lib/salebot_uploader/processing/vips.rb +284 -0
  16. data/lib/salebot_uploader/processing.rb +3 -0
  17. data/lib/salebot_uploader/sanitized_file.rb +357 -0
  18. data/lib/salebot_uploader/storage/abstract.rb +41 -0
  19. data/lib/salebot_uploader/storage/file.rb +124 -0
  20. data/lib/salebot_uploader/storage/fog.rb +547 -0
  21. data/lib/salebot_uploader/storage.rb +3 -0
  22. data/lib/salebot_uploader/test/matchers.rb +398 -0
  23. data/lib/salebot_uploader/uploader/cache.rb +223 -0
  24. data/lib/salebot_uploader/uploader/callbacks.rb +33 -0
  25. data/lib/salebot_uploader/uploader/configuration.rb +184 -0
  26. data/lib/salebot_uploader/uploader/content_type_allowlist.rb +61 -0
  27. data/lib/salebot_uploader/uploader/content_type_denylist.rb +62 -0
  28. data/lib/salebot_uploader/uploader/default_url.rb +17 -0
  29. data/lib/salebot_uploader/uploader/dimension.rb +66 -0
  30. data/lib/salebot_uploader/uploader/download.rb +24 -0
  31. data/lib/salebot_uploader/uploader/extension_allowlist.rb +63 -0
  32. data/lib/salebot_uploader/uploader/extension_denylist.rb +64 -0
  33. data/lib/salebot_uploader/uploader/file_size.rb +43 -0
  34. data/lib/salebot_uploader/uploader/mountable.rb +44 -0
  35. data/lib/salebot_uploader/uploader/processing.rb +125 -0
  36. data/lib/salebot_uploader/uploader/proxy.rb +99 -0
  37. data/lib/salebot_uploader/uploader/remove.rb +21 -0
  38. data/lib/salebot_uploader/uploader/serialization.rb +28 -0
  39. data/lib/salebot_uploader/uploader/store.rb +142 -0
  40. data/lib/salebot_uploader/uploader/url.rb +44 -0
  41. data/lib/salebot_uploader/uploader/versions.rb +350 -0
  42. data/lib/salebot_uploader/uploader.rb +53 -0
  43. data/lib/salebot_uploader/utilities/file_name.rb +47 -0
  44. data/lib/salebot_uploader/utilities/uri.rb +26 -0
  45. data/lib/salebot_uploader/utilities.rb +7 -0
  46. data/lib/salebot_uploader/validations/active_model.rb +76 -0
  47. data/lib/salebot_uploader/version.rb +3 -0
  48. data/lib/salebot_uploader.rb +62 -0
  49. metadata +392 -0
@@ -0,0 +1,68 @@
1
+ require 'active_record'
2
+ require 'salebot_uploader/validations/active_model'
3
+
4
+ module SalebotUploader
5
+ module ActiveRecord
6
+
7
+ include SalebotUploader::Mount
8
+
9
+ private
10
+
11
+ def mount_base(column, uploader=nil, options={}, &block)
12
+ super
13
+
14
+ alias_method :read_uploader, :read_attribute
15
+ alias_method :write_uploader, :write_attribute
16
+ public :read_uploader
17
+ public :write_uploader
18
+
19
+ include SalebotUploader::Validations::ActiveModel
20
+
21
+ validates_integrity_of column if uploader_option(column.to_sym, :validate_integrity)
22
+ validates_processing_of column if uploader_option(column.to_sym, :validate_processing)
23
+ validates_download_of column if uploader_option(column.to_sym, :validate_download)
24
+
25
+ after_save :"store_#{column}!"
26
+ before_save :"write_#{column}_identifier"
27
+ if ::ActiveRecord.try(:run_after_transaction_callbacks_in_order_defined)
28
+ after_commit :"remove_previously_stored_#{column}", :on => :update
29
+ after_commit :"reset_previous_changes_for_#{column}"
30
+ after_commit :"mark_remove_#{column}_false", :on => :update
31
+ else
32
+ after_commit :"mark_remove_#{column}_false", :on => :update
33
+ after_commit :"reset_previous_changes_for_#{column}"
34
+ after_commit :"remove_previously_stored_#{column}", :on => :update
35
+ end
36
+ after_commit :"remove_#{column}!", :on => :destroy
37
+ after_rollback :"remove_rolled_back_#{column}"
38
+
39
+ mod = Module.new
40
+ prepend mod
41
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
42
+ # Reset cached mounter on record reload
43
+ def reload(*)
44
+ @_mounters = nil
45
+ super
46
+ end
47
+
48
+ # Reset cached mounter on record dup
49
+ def initialize_dup(other)
50
+ old_uploaders = _mounter(:"#{column}").uploaders
51
+ super
52
+ @_mounters[:"#{column}"] = nil
53
+ # The attribute needs to be cleared to prevent it from picked up as identifier
54
+ write_attribute(_mounter(:#{column}).serialization_column, nil)
55
+ _mounter(:"#{column}").cache(old_uploaders)
56
+ end
57
+
58
+ def write_#{column}_identifier
59
+ return unless has_attribute?(_mounter(:#{column}).serialization_column)
60
+ super
61
+ end
62
+ RUBY
63
+ end
64
+
65
+ end # ActiveRecord
66
+ end # SalebotUploader
67
+
68
+ ActiveRecord::Base.extend SalebotUploader::ActiveRecord
@@ -0,0 +1,194 @@
1
+ module SalebotUploader
2
+ module MiniMagick
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ require "image_processing/mini_magick"
7
+ end
8
+
9
+ module ClassMethods
10
+ def convert(format)
11
+ process :convert => format
12
+ end
13
+
14
+ def resize_to_limit(width, height)
15
+ process :resize_to_limit => [width, height]
16
+ end
17
+
18
+ def resize_to_fit(width, height)
19
+ process :resize_to_fit => [width, height]
20
+ end
21
+
22
+ def resize_to_fill(width, height, gravity='Center')
23
+ process :resize_to_fill => [width, height, gravity]
24
+ end
25
+
26
+ def resize_and_pad(width, height, background=:transparent, gravity='Center')
27
+ process :resize_and_pad => [width, height, background, gravity]
28
+ end
29
+ end
30
+ def convert(format, page=nil, &block)
31
+ minimagick!(block) do |builder|
32
+ builder = builder.convert(format)
33
+ builder = builder.loader(page: page) if page
34
+ builder
35
+ end
36
+ end
37
+
38
+ def resize_to_limit(width, height, combine_options: {}, &block)
39
+ width, height = resolve_dimensions(width, height)
40
+
41
+ minimagick!(block) do |builder|
42
+ builder.resize_to_limit(width, height)
43
+ .apply(combine_options)
44
+ end
45
+ end
46
+
47
+ def resize_to_fit(width, height, combine_options: {}, &block)
48
+ width, height = resolve_dimensions(width, height)
49
+
50
+ minimagick!(block) do |builder|
51
+ builder.resize_to_fit(width, height)
52
+ .apply(combine_options)
53
+ end
54
+ end
55
+
56
+ def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
57
+ width, height = resolve_dimensions(width, height)
58
+
59
+ minimagick!(block) do |builder|
60
+ builder.resize_to_fill(width, height, gravity: gravity)
61
+ .apply(combine_options)
62
+ end
63
+ end
64
+
65
+ def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
66
+ width, height = resolve_dimensions(width, height)
67
+
68
+ minimagick!(block) do |builder|
69
+ builder.resize_and_pad(width, height, background: background, gravity: gravity)
70
+ .apply(combine_options)
71
+ end
72
+ end
73
+
74
+ ##
75
+ # Returns the width of the image in pixels.
76
+ #
77
+ # === Returns
78
+ #
79
+ # [Integer] the image's width in pixels
80
+ #
81
+ def width
82
+ mini_magick_image[:width]
83
+ end
84
+
85
+ ##
86
+ # Returns the height of the image in pixels.
87
+ #
88
+ # === Returns
89
+ #
90
+ # [Integer] the image's height in pixels
91
+ #
92
+ def height
93
+ mini_magick_image[:height]
94
+ end
95
+
96
+ ##
97
+ # Manipulate the image with MiniMagick. This method will load up an image
98
+ # and then pass each of its frames to the supplied block. It will then
99
+ # save the image to disk.
100
+ #
101
+ # NOTE: This method exists mostly for backwards compatibility, you should
102
+ # probably use #minimagick!.
103
+ #
104
+ # === Gotcha
105
+ #
106
+ # This method assumes that the object responds to +current_path+.
107
+ # Any class that this module is mixed into must have a +current_path+ method.
108
+ # SalebotUploader::Uploader does, so you won't need to worry about this in
109
+ # most cases.
110
+ #
111
+ # === Yields
112
+ #
113
+ # [MiniMagick::Image] manipulations to perform
114
+ #
115
+ # === Raises
116
+ #
117
+ # [SalebotUploader::ProcessingError] if manipulation failed.
118
+ #
119
+ def manipulate!
120
+ cache_stored_file! if !cached?
121
+ image = ::MiniMagick::Image.open(current_path)
122
+
123
+ image = yield(image)
124
+ FileUtils.mv image.path, current_path
125
+
126
+ image.run_command("identify", current_path)
127
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
128
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
129
+ message = I18n.translate(:"errors.messages.processing_error")
130
+ raise SalebotUploader::ProcessingError, message
131
+ ensure
132
+ image.destroy! if image
133
+ end
134
+
135
+ # Process the image with MiniMagick, using the ImageProcessing gem. This
136
+ # method will build a "convert" ImageMagick command and execute it on the
137
+ # current image.
138
+ #
139
+ # === Gotcha
140
+ #
141
+ # This method assumes that the object responds to +current_path+.
142
+ # Any class that this module is mixed into must have a +current_path+ method.
143
+ # SalebotUploader::Uploader does, so you won't need to worry about this in
144
+ # most cases.
145
+ #
146
+ # === Yields
147
+ #
148
+ # [ImageProcessing::Builder] use it to define processing to be performed
149
+ #
150
+ # === Raises
151
+ #
152
+ # [SalebotUploader::ProcessingError] if processing failed.
153
+ def minimagick!(block = nil)
154
+ builder = ImageProcessing::MiniMagick.source(current_path)
155
+ builder = yield(builder)
156
+
157
+ result = builder.call
158
+ result.close
159
+
160
+ # backwards compatibility (we want to eventually move away from MiniMagick::Image)
161
+ if block
162
+ image = ::MiniMagick::Image.new(result.path, result)
163
+ image = block.call(image)
164
+ result = image.instance_variable_get(:@tempfile)
165
+ end
166
+
167
+ FileUtils.mv result.path, current_path
168
+
169
+ if File.extname(result.path) != File.extname(current_path)
170
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
171
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
172
+ file.move_to(move_to, permissions, directory_permissions)
173
+ end
174
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
175
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
176
+ message = I18n.translate(:"errors.messages.processing_error")
177
+ raise SalebotUploader::ProcessingError, message
178
+ end
179
+
180
+ private
181
+
182
+ def resolve_dimensions(*dimensions)
183
+ dimensions.map do |value|
184
+ next value unless value.instance_of?(Proc)
185
+ value.arity >= 1 ? value.call(self) : value.call
186
+ end
187
+ end
188
+
189
+ def mini_magick_image
190
+ ::MiniMagick::Image.read(read)
191
+ end
192
+
193
+ end # MiniMagick
194
+ end # SalebotUploader
@@ -0,0 +1,402 @@
1
+ module SalebotUploader
2
+
3
+ ##
4
+ # This module simplifies manipulation with RMagick by providing a set
5
+ # of convenient helper methods. If you want to use them, you'll need to
6
+ # require this file:
7
+ #
8
+ # require 'SalebotUploader/processing/rmagick'
9
+ #
10
+ # And then include it in your uploader:
11
+ #
12
+ # class MyUploader < SalebotUploader::Uploader::Base
13
+ # include SalebotUploader::RMagick
14
+ # end
15
+ #
16
+ # You can now use the provided helpers:
17
+ #
18
+ # class MyUploader < SalebotUploader::Uploader::Base
19
+ # include SalebotUploader::RMagick
20
+ #
21
+ # process :resize_to_fit => [200, 200]
22
+ # end
23
+ #
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
26
+ # info
27
+ #
28
+ # class MyUploader < SalebotUploader::Uploader::Base
29
+ # include SalebotUploader::RMagick
30
+ #
31
+ # process :do_stuff => 10.0
32
+ #
33
+ # def do_stuff(blur_factor)
34
+ # manipulate! do |img|
35
+ # img = img.sepiatone
36
+ # img = img.auto_orient
37
+ # img = img.radial_blur(blur_factor)
38
+ # end
39
+ # end
40
+ # end
41
+ #
42
+ # === Note
43
+ #
44
+ # You should be aware how RMagick handles memory. manipulate! takes care
45
+ # of freeing up memory for you, but for optimum memory usage you should
46
+ # use destructive operations as much as possible:
47
+ #
48
+ # DON'T DO THIS:
49
+ # img = img.resize_to_fit
50
+ #
51
+ # DO THIS INSTEAD:
52
+ # img.resize_to_fit!
53
+ #
54
+ # Read this for more information why:
55
+ #
56
+ # http://rubyforge.org/forum/forum.php?thread_id=1374&forum_id=1618
57
+ #
58
+ module RMagick
59
+ extend ActiveSupport::Concern
60
+
61
+ included do
62
+ begin
63
+ require "rmagick"
64
+ rescue LoadError
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
+ end
72
+
73
+ prepend Module.new {
74
+ def initialize(*)
75
+ super
76
+ @format = nil
77
+ end
78
+ }
79
+ end
80
+
81
+ module ClassMethods
82
+ def convert(format)
83
+ process :convert => format
84
+ end
85
+
86
+ def resize_to_limit(width, height)
87
+ process :resize_to_limit => [width, height]
88
+ end
89
+
90
+ def resize_to_fit(width, height)
91
+ process :resize_to_fit => [width, height]
92
+ end
93
+
94
+ def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
95
+ process :resize_to_fill => [width, height, gravity]
96
+ end
97
+
98
+ def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
99
+ process :resize_and_pad => [width, height, background, gravity]
100
+ end
101
+
102
+ def resize_to_geometry_string(geometry_string)
103
+ process :resize_to_geometry_string => [geometry_string]
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Changes the image encoding format to the given format
109
+ #
110
+ # See even http://www.imagemagick.org/RMagick/doc/magick.html#formats
111
+ #
112
+ # === Parameters
113
+ #
114
+ # [format (#to_s)] an abbreviation of the format
115
+ #
116
+ # === Yields
117
+ #
118
+ # [Magick::Image] additional manipulations to perform
119
+ #
120
+ # === Examples
121
+ #
122
+ # image.convert(:png)
123
+ #
124
+ def convert(format)
125
+ manipulate!(:format => format)
126
+ @format = format
127
+ end
128
+
129
+ ##
130
+ # Resize the image to fit within the specified dimensions while retaining
131
+ # the original aspect ratio. Will only resize the image if it is larger than the
132
+ # specified dimensions. The resulting image may be shorter or narrower than specified
133
+ # in the smaller dimension but will not be larger than the specified values.
134
+ #
135
+ # === Parameters
136
+ #
137
+ # [width (Integer)] the width to scale the image to
138
+ # [height (Integer)] the height to scale the image to
139
+ #
140
+ # === Yields
141
+ #
142
+ # [Magick::Image] additional manipulations to perform
143
+ #
144
+ def resize_to_limit(width, height)
145
+ width = dimension_from width
146
+ height = dimension_from height
147
+ manipulate! do |img|
148
+ geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
149
+ new_img = img.change_geometry(geometry) do |new_width, new_height|
150
+ img.resize(new_width, new_height)
151
+ end
152
+ destroy_image(img)
153
+ new_img = yield(new_img) if block_given?
154
+ new_img
155
+ end
156
+ end
157
+
158
+ ##
159
+ # From the RMagick documentation: "Resize the image to fit within the
160
+ # specified dimensions while retaining the original aspect ratio. The
161
+ # image may be shorter or narrower than specified in the smaller dimension
162
+ # but will not be larger than the specified values."
163
+ #
164
+ # See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fit
165
+ #
166
+ # === Parameters
167
+ #
168
+ # [width (Integer)] the width to scale the image to
169
+ # [height (Integer)] the height to scale the image to
170
+ #
171
+ # === Yields
172
+ #
173
+ # [Magick::Image] additional manipulations to perform
174
+ #
175
+ def resize_to_fit(width, height)
176
+ width = dimension_from width
177
+ height = dimension_from height
178
+ manipulate! do |img|
179
+ img.resize_to_fit!(width, height)
180
+ img = yield(img) if block_given?
181
+ img
182
+ end
183
+ end
184
+
185
+ ##
186
+ # From the RMagick documentation: "Resize the image to fit within the
187
+ # specified dimensions while retaining the aspect ratio of the original
188
+ # image. If necessary, crop the image in the larger dimension."
189
+ #
190
+ # See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fill
191
+ #
192
+ # === Parameters
193
+ #
194
+ # [width (Integer)] the width to scale the image to
195
+ # [height (Integer)] the height to scale the image to
196
+ #
197
+ # === Yields
198
+ #
199
+ # [Magick::Image] additional manipulations to perform
200
+ #
201
+ def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
202
+ width = dimension_from width
203
+ height = dimension_from height
204
+ manipulate! do |img|
205
+ img.crop_resized!(width, height, gravity)
206
+ img = yield(img) if block_given?
207
+ img
208
+ end
209
+ end
210
+
211
+ ##
212
+ # Resize the image to fit within the specified dimensions while retaining
213
+ # the original aspect ratio. If necessary, will pad the remaining area
214
+ # with the given color, which defaults to transparent (for gif and png,
215
+ # white for jpeg).
216
+ #
217
+ # === Parameters
218
+ #
219
+ # [width (Integer)] the width to scale the image to
220
+ # [height (Integer)] the height to scale the image to
221
+ # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
222
+ # [gravity (Magick::GravityType)] how to position the image
223
+ #
224
+ # === Yields
225
+ #
226
+ # [Magick::Image] additional manipulations to perform
227
+ #
228
+ def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
229
+ width = dimension_from width
230
+ height = dimension_from height
231
+ manipulate! do |img|
232
+ img.resize_to_fit!(width, height)
233
+ filled = ::Magick::Image.new(width, height) { |image| image.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
234
+ filled.composite!(img, gravity, ::Magick::OverCompositeOp)
235
+ destroy_image(img)
236
+ filled = yield(filled) if block_given?
237
+ filled
238
+ end
239
+ end
240
+
241
+ ##
242
+ # Resize the image per the provided geometry string.
243
+ #
244
+ # === Parameters
245
+ #
246
+ # [geometry_string (String)] the proportions in which to scale image
247
+ #
248
+ # === Yields
249
+ #
250
+ # [Magick::Image] additional manipulations to perform
251
+ #
252
+ def resize_to_geometry_string(geometry_string)
253
+ manipulate! do |img|
254
+ new_img = img.change_geometry(geometry_string) do |new_width, new_height|
255
+ img.resize(new_width, new_height)
256
+ end
257
+ destroy_image(img)
258
+ new_img = yield(new_img) if block_given?
259
+ new_img
260
+ end
261
+ end
262
+
263
+ ##
264
+ # Returns the width of the image.
265
+ #
266
+ # === Returns
267
+ #
268
+ # [Integer] the image's width in pixels
269
+ #
270
+ def width
271
+ rmagick_image.columns
272
+ end
273
+
274
+ ##
275
+ # Returns the height of the image.
276
+ #
277
+ # === Returns
278
+ #
279
+ # [Integer] the image's height in pixels
280
+ #
281
+ def height
282
+ rmagick_image.rows
283
+ end
284
+
285
+ ##
286
+ # Manipulate the image with RMagick. This method will load up an image
287
+ # and then pass each of its frames to the supplied block. It will then
288
+ # save the image to disk.
289
+ #
290
+ # === Gotcha
291
+ #
292
+ # This method assumes that the object responds to +current_path+.
293
+ # Any class that this module is mixed into must have a +current_path+ method.
294
+ # SalebotUploader::Uploader does, so you won't need to worry about this in
295
+ # most cases.
296
+ #
297
+ # === Yields
298
+ #
299
+ # [Magick::Image] manipulations to perform
300
+ # [Integer] Frame index if the image contains multiple frames
301
+ # [Hash] options, see below
302
+ #
303
+ # === Options
304
+ #
305
+ # The options argument to this method is also yielded as the third
306
+ # block argument.
307
+ #
308
+ # Currently, the following options are defined:
309
+ #
310
+ # ==== :write
311
+ # A hash of assignments to be evaluated in the block given to the RMagick write call.
312
+ #
313
+ # An example:
314
+ #
315
+ # manipulate! do |img, index, options|
316
+ # options[:write] = {
317
+ # :quality => 50,
318
+ # :depth => 8
319
+ # }
320
+ # img
321
+ # end
322
+ #
323
+ # This will translate to the following RMagick::Image#write call:
324
+ #
325
+ # image.write do |img|
326
+ # self.quality = 50
327
+ # self.depth = 8
328
+ # end
329
+ #
330
+ # ==== :read
331
+ # A hash of assignments to be given to the RMagick read call.
332
+ #
333
+ # The options available are identical to those for write, but are passed in directly, like this:
334
+ #
335
+ # manipulate! :read => { :density => 300 }
336
+ #
337
+ # ==== :format
338
+ # Specify the output format. If unset, the filename extension is used to determine the format.
339
+ #
340
+ # === Raises
341
+ #
342
+ # [SalebotUploader::ProcessingError] if manipulation failed.
343
+ #
344
+ def manipulate!(options={}, &block)
345
+ cache_stored_file! if !cached?
346
+
347
+ read_block = create_info_block(options[:read])
348
+ image = ::Magick::Image.read(current_path, &read_block)
349
+ frames = ::Magick::ImageList.new
350
+
351
+ image.each_with_index do |frame, index|
352
+ frame = yield(*[frame, index, options].take(block.arity)) if block_given?
353
+ frames << frame if frame
354
+ end
355
+ frames.append(true) if block_given?
356
+
357
+ write_block = create_info_block(options[:write])
358
+
359
+ if options[:format] || @format
360
+ frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
361
+ move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
362
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
363
+ file.move_to(move_to, permissions, directory_permissions)
364
+ else
365
+ frames.write(current_path, &write_block)
366
+ end
367
+
368
+ destroy_image(frames)
369
+ rescue ::Magick::ImageMagickError
370
+ raise SalebotUploader::ProcessingError, I18n.translate(:"errors.messages.processing_error")
371
+ end
372
+
373
+ private
374
+
375
+ def create_info_block(options)
376
+ return nil unless options
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
386
+ end
387
+
388
+ def destroy_image(image)
389
+ image.try(:destroy!)
390
+ end
391
+
392
+ def dimension_from(value)
393
+ return value unless value.instance_of?(Proc)
394
+ value.arity >= 1 ? value.call(self) : value.call
395
+ end
396
+
397
+ def rmagick_image
398
+ ::Magick::Image.from_blob(self.read).first
399
+ end
400
+
401
+ end # RMagick
402
+ end # SalebotUploader