carrierwave 1.3.3 → 2.2.3

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

Potentially problematic release.


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

Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +117 -43
  3. data/lib/carrierwave/downloader/base.rb +93 -0
  4. data/lib/carrierwave/downloader/remote_file.rb +65 -0
  5. data/lib/carrierwave/locale/en.yml +5 -4
  6. data/lib/carrierwave/mount.rb +25 -19
  7. data/lib/carrierwave/mounter.rb +70 -47
  8. data/lib/carrierwave/orm/activerecord.rb +14 -8
  9. data/lib/carrierwave/processing/mini_magick.rb +100 -117
  10. data/lib/carrierwave/processing/rmagick.rb +2 -2
  11. data/lib/carrierwave/processing/vips.rb +284 -0
  12. data/lib/carrierwave/processing.rb +1 -0
  13. data/lib/carrierwave/sanitized_file.rb +38 -16
  14. data/lib/carrierwave/storage/file.rb +2 -2
  15. data/lib/carrierwave/storage/fog.rb +44 -13
  16. data/lib/carrierwave/storage.rb +1 -0
  17. data/lib/carrierwave/uploader/cache.rb +24 -16
  18. data/lib/carrierwave/uploader/configuration.rb +28 -15
  19. data/lib/carrierwave/uploader/content_type_blacklist.rb +17 -8
  20. data/lib/carrierwave/uploader/content_type_whitelist.rb +20 -8
  21. data/lib/carrierwave/uploader/download.rb +2 -123
  22. data/lib/carrierwave/uploader/extension_blacklist.rb +18 -10
  23. data/lib/carrierwave/uploader/extension_whitelist.rb +19 -10
  24. data/lib/carrierwave/uploader/mountable.rb +6 -0
  25. data/lib/carrierwave/uploader/proxy.rb +2 -2
  26. data/lib/carrierwave/uploader/serialization.rb +1 -1
  27. data/lib/carrierwave/uploader/store.rb +5 -3
  28. data/lib/carrierwave/uploader/url.rb +6 -3
  29. data/lib/carrierwave/uploader/versions.rb +43 -13
  30. data/lib/carrierwave/uploader.rb +0 -9
  31. data/lib/carrierwave/validations/active_model.rb +3 -3
  32. data/lib/carrierwave/version.rb +1 -1
  33. data/lib/carrierwave.rb +4 -0
  34. data/lib/generators/templates/uploader.rb +2 -2
  35. metadata +105 -32
@@ -3,14 +3,17 @@ module CarrierWave
3
3
  # this is an internal class, used by CarrierWave::Mount so that
4
4
  # we don't pollute the model with a lot of methods.
5
5
  class Mounter #:nodoc:
6
- attr_reader :column, :record, :remote_urls, :integrity_error,
7
- :processing_error, :download_error
6
+ attr_reader :column, :record, :remote_urls, :integrity_errors,
7
+ :processing_errors, :download_errors
8
8
  attr_accessor :remove, :remote_request_headers
9
9
 
10
10
  def initialize(record, column, options={})
11
11
  @record = record
12
12
  @column = column
13
13
  @options = record.class.uploader_options[column]
14
+ @download_errors = []
15
+ @processing_errors = []
16
+ @integrity_errors = []
14
17
  end
15
18
 
16
19
  def uploader_class
@@ -38,21 +41,30 @@ module CarrierWave
38
41
  end
39
42
 
40
43
  def cache(new_files)
41
- return if not new_files or new_files == ""
44
+ return if !new_files.is_a?(Array) && new_files.blank?
45
+ old_uploaders = uploaders
42
46
  @uploaders = new_files.map do |new_file|
43
- uploader = blank_uploader
44
- uploader.cache!(new_file)
45
- uploader
46
- end
47
-
48
- @integrity_error = nil
49
- @processing_error = nil
50
- rescue CarrierWave::IntegrityError => e
51
- @integrity_error = e
52
- raise e unless option(:ignore_integrity_errors)
53
- rescue CarrierWave::ProcessingError => e
54
- @processing_error = e
55
- raise e unless option(:ignore_processing_errors)
47
+ handle_error do
48
+ if new_file.is_a?(String)
49
+ if (uploader = old_uploaders.detect { |uploader| uploader.identifier == new_file })
50
+ uploader.staged = true
51
+ uploader
52
+ else
53
+ begin
54
+ uploader = blank_uploader
55
+ uploader.retrieve_from_cache!(new_file)
56
+ uploader
57
+ rescue CarrierWave::InvalidParameter
58
+ nil
59
+ end
60
+ end
61
+ else
62
+ uploader = blank_uploader
63
+ uploader.cache!(new_file)
64
+ uploader
65
+ end
66
+ end
67
+ end.compact
56
68
  end
57
69
 
58
70
  def cache_names
@@ -60,45 +72,37 @@ module CarrierWave
60
72
  end
61
73
 
62
74
  def cache_names=(cache_names)
63
- return if not cache_names or cache_names == "" or uploaders.any?(&:cached?)
64
- @uploaders = cache_names.map do |cache_name|
65
- uploader = blank_uploader
66
- uploader.retrieve_from_cache!(cache_name)
67
- uploader
75
+ cache_names = cache_names.reject(&:blank?)
76
+ return if cache_names.blank?
77
+ clear_unstaged
78
+ cache_names.each do |cache_name|
79
+ begin
80
+ uploader = blank_uploader
81
+ uploader.retrieve_from_cache!(cache_name)
82
+ @uploaders << uploader
83
+ rescue CarrierWave::InvalidParameter
84
+ # ignore
85
+ end
68
86
  end
69
- rescue CarrierWave::InvalidParameter
70
87
  end
71
88
 
72
89
  def remote_urls=(urls)
73
- return if not urls or urls == "" or urls.all?(&:blank?)
90
+ return if urls.blank? || urls.all?(&:blank?)
74
91
 
75
92
  @remote_urls = urls
76
- @download_error = nil
77
- @integrity_error = nil
78
93
 
79
- @uploaders = urls.zip(remote_request_headers || []).map do |url, header|
80
- uploader = blank_uploader
81
- uploader.download!(url, header || {})
82
- uploader
94
+ clear_unstaged
95
+ urls.zip(remote_request_headers || []).each do |url, header|
96
+ handle_error do
97
+ uploader = blank_uploader
98
+ uploader.download!(url, header || {})
99
+ @uploaders << uploader
100
+ end
83
101
  end
84
-
85
- rescue CarrierWave::DownloadError => e
86
- @download_error = e
87
- raise e unless option(:ignore_download_errors)
88
- rescue CarrierWave::ProcessingError => e
89
- @processing_error = e
90
- raise e unless option(:ignore_processing_errors)
91
- rescue CarrierWave::IntegrityError => e
92
- @integrity_error = e
93
- raise e unless option(:ignore_integrity_errors)
94
102
  end
95
103
 
96
104
  def store!
97
- if remove?
98
- remove!
99
- else
100
- uploaders.reject(&:blank?).each(&:store!)
101
- end
105
+ uploaders.reject(&:blank?).each(&:store!)
102
106
  end
103
107
 
104
108
  def urls(*args)
@@ -118,6 +122,10 @@ module CarrierWave
118
122
  @uploaders = []
119
123
  end
120
124
 
125
+ def clear!
126
+ @uploaders = []
127
+ end
128
+
121
129
  def serialization_column
122
130
  option(:mount_on) || column
123
131
  end
@@ -146,9 +154,7 @@ module CarrierWave
146
154
  end.path
147
155
  end
148
156
  before.each do |uploader|
149
- if uploader.remove_previously_stored_files_after_update and not after_paths.include?(uploader.path)
150
- uploader.remove!
151
- end
157
+ uploader.remove! if uploader.remove_previously_stored_files_after_update && !after_paths.include?(uploader.path)
152
158
  end
153
159
  end
154
160
 
@@ -161,5 +167,22 @@ module CarrierWave
161
167
  self.uploader_options[name] ||= record.class.uploader_option(column, name)
162
168
  end
163
169
 
170
+ def clear_unstaged
171
+ @uploaders ||= []
172
+ @uploaders.keep_if(&:staged)
173
+ end
174
+
175
+ def handle_error
176
+ yield
177
+ rescue CarrierWave::DownloadError => e
178
+ @download_errors << e
179
+ raise e unless option(:ignore_download_errors)
180
+ rescue CarrierWave::ProcessingError => e
181
+ @processing_errors << e
182
+ raise e unless option(:ignore_processing_errors)
183
+ rescue CarrierWave::IntegrityError => e
184
+ @integrity_errors << e
185
+ raise e unless option(:ignore_integrity_errors)
186
+ end
164
187
  end # Mounter
165
188
  end # CarrierWave
@@ -12,7 +12,9 @@ module CarrierWave
12
12
  def mount_uploader(column, uploader=nil, options={}, &block)
13
13
  super
14
14
 
15
- class_eval <<-RUBY, __FILE__, __LINE__+1
15
+ mod = Module.new
16
+ prepend mod
17
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
16
18
  def remote_#{column}_url=(url)
17
19
  column = _mounter(:#{column}).serialization_column
18
20
  __send__(:"\#{column}_will_change!")
@@ -27,7 +29,9 @@ module CarrierWave
27
29
  def mount_uploaders(column, uploader=nil, options={}, &block)
28
30
  super
29
31
 
30
- class_eval <<-RUBY, __FILE__, __LINE__+1
32
+ mod = Module.new
33
+ prepend mod
34
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
31
35
  def remote_#{column}_urls=(url)
32
36
  column = _mounter(:#{column}).serialization_column
33
37
  __send__(:"\#{column}_will_change!")
@@ -52,15 +56,16 @@ module CarrierWave
52
56
  validates_processing_of column if uploader_option(column.to_sym, :validate_processing)
53
57
  validates_download_of column if uploader_option(column.to_sym, :validate_download)
54
58
 
55
- after_save :"store_#{column}!"
56
59
  before_save :"write_#{column}_identifier"
60
+ after_save :"store_previous_changes_for_#{column}"
57
61
  after_commit :"remove_#{column}!", :on => :destroy
58
62
  after_commit :"mark_remove_#{column}_false", :on => :update
59
-
60
- after_save :"store_previous_changes_for_#{column}"
61
63
  after_commit :"remove_previously_stored_#{column}", :on => :update
64
+ after_commit :"store_#{column}!", :on => [:create, :update]
62
65
 
63
- class_eval <<-RUBY, __FILE__, __LINE__+1
66
+ mod = Module.new
67
+ prepend mod
68
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
64
69
  def #{column}=(new_file)
65
70
  column = _mounter(:#{column}).serialization_column
66
71
  if !(new_file.blank? && __send__(:#{column}).blank?)
@@ -72,8 +77,9 @@ module CarrierWave
72
77
 
73
78
  def remove_#{column}=(value)
74
79
  column = _mounter(:#{column}).serialization_column
75
- __send__(:"\#{column}_will_change!")
76
- super
80
+ result = super
81
+ __send__(:"\#{column}_will_change!") if _mounter(:#{column}).remove?
82
+ result
77
83
  end
78
84
 
79
85
  def remove_#{column}!
@@ -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
@@ -109,12 +98,11 @@ module CarrierWave
109
98
  #
110
99
  # image.convert(:png)
111
100
  #
112
- def convert(format, page=nil)
113
- @format = format
114
- @page = page
115
- manipulate! do |img|
116
- img = yield(img) if block_given?
117
- img
101
+ def convert(format, page=nil, &block)
102
+ minimagick!(block) do |builder|
103
+ builder = builder.convert(format)
104
+ builder = builder.loader(page: page) if page
105
+ builder
118
106
  end
119
107
  end
120
108
 
@@ -128,21 +116,18 @@ module CarrierWave
128
116
  #
129
117
  # [width (Integer)] the width to scale the image to
130
118
  # [height (Integer)] the height to scale the image to
119
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
131
120
  #
132
121
  # === Yields
133
122
  #
134
123
  # [MiniMagick::Image] additional manipulations to perform
135
124
  #
136
- def resize_to_limit(width, height, combine_options: {})
137
- width = dimension_from width
138
- height = dimension_from height
139
- manipulate! do |img|
140
- img.combine_options do |cmd|
141
- cmd.resize "#{width}x#{height}>"
142
- append_combine_options cmd, combine_options
143
- end
144
- img = yield(img) if block_given?
145
- img
125
+ def resize_to_limit(width, height, combine_options: {}, &block)
126
+ width, height = resolve_dimensions(width, height)
127
+
128
+ minimagick!(block) do |builder|
129
+ builder.resize_to_limit(width, height)
130
+ .apply(combine_options)
146
131
  end
147
132
  end
148
133
 
@@ -155,21 +140,18 @@ module CarrierWave
155
140
  #
156
141
  # [width (Integer)] the width to scale the image to
157
142
  # [height (Integer)] the height to scale the image to
143
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
158
144
  #
159
145
  # === Yields
160
146
  #
161
147
  # [MiniMagick::Image] additional manipulations to perform
162
148
  #
163
- def resize_to_fit(width, height, combine_options: {})
164
- width = dimension_from width
165
- height = dimension_from height
166
- manipulate! do |img|
167
- img.combine_options do |cmd|
168
- cmd.resize "#{width}x#{height}"
169
- append_combine_options cmd, combine_options
170
- end
171
- img = yield(img) if block_given?
172
- img
149
+ def resize_to_fit(width, height, combine_options: {}, &block)
150
+ width, height = resolve_dimensions(width, height)
151
+
152
+ minimagick!(block) do |builder|
153
+ builder.resize_to_fit(width, height)
154
+ .apply(combine_options)
173
155
  end
174
156
  end
175
157
 
@@ -183,37 +165,18 @@ module CarrierWave
183
165
  # [width (Integer)] the width to scale the image to
184
166
  # [height (Integer)] the height to scale the image to
185
167
  # [gravity (String)] the current gravity suggestion (default: 'Center'; options: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast')
168
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
186
169
  #
187
170
  # === Yields
188
171
  #
189
172
  # [MiniMagick::Image] additional manipulations to perform
190
173
  #
191
- def resize_to_fill(width, height, gravity = 'Center', combine_options: {})
192
- width = dimension_from width
193
- height = dimension_from height
194
- manipulate! do |img|
195
- cols, rows = img[:dimensions]
196
- img.combine_options do |cmd|
197
- if width != cols || height != rows
198
- scale_x = width/cols.to_f
199
- scale_y = height/rows.to_f
200
- if scale_x >= scale_y
201
- cols = (scale_x * (cols + 0.5)).round
202
- rows = (scale_x * (rows + 0.5)).round
203
- cmd.resize "#{cols}"
204
- else
205
- cols = (scale_y * (cols + 0.5)).round
206
- rows = (scale_y * (rows + 0.5)).round
207
- cmd.resize "x#{rows}"
208
- end
209
- end
210
- cmd.gravity gravity
211
- cmd.background "rgba(255,255,255,0.0)"
212
- cmd.extent "#{width}x#{height}" if cols != width || rows != height
213
- append_combine_options cmd, combine_options
214
- end
215
- img = yield(img) if block_given?
216
- img
174
+ def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
175
+ width, height = resolve_dimensions(width, height)
176
+
177
+ minimagick!(block) do |builder|
178
+ builder.resize_to_fill(width, height, gravity: gravity)
179
+ .apply(combine_options)
217
180
  end
218
181
  end
219
182
 
@@ -232,28 +195,18 @@ module CarrierWave
232
195
  # [height (Integer)] the height to scale the image to
233
196
  # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
234
197
  # [gravity (String)] how to position the image
198
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
235
199
  #
236
200
  # === Yields
237
201
  #
238
202
  # [MiniMagick::Image] additional manipulations to perform
239
203
  #
240
- def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {})
241
- width = dimension_from width
242
- height = dimension_from height
243
- manipulate! do |img|
244
- img.combine_options do |cmd|
245
- cmd.thumbnail "#{width}x#{height}>"
246
- if background == :transparent
247
- cmd.background "rgba(255, 255, 255, 0.0)"
248
- else
249
- cmd.background background
250
- end
251
- cmd.gravity gravity
252
- cmd.extent "#{width}x#{height}"
253
- append_combine_options cmd, combine_options
254
- end
255
- img = yield(img) if block_given?
256
- img
204
+ def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
205
+ width, height = resolve_dimensions(width, height)
206
+
207
+ minimagick!(block) do |builder|
208
+ builder.resize_and_pad(width, height, background: background, gravity: gravity)
209
+ .apply(combine_options)
257
210
  end
258
211
  end
259
212
 
@@ -284,6 +237,9 @@ module CarrierWave
284
237
  # and then pass each of its frames to the supplied block. It will then
285
238
  # save the image to disk.
286
239
  #
240
+ # NOTE: This method exists mostly for backwards compatibility, you should
241
+ # probably use #minimagick!.
242
+ #
287
243
  # === Gotcha
288
244
  #
289
245
  # This method assumes that the object responds to +current_path+.
@@ -303,20 +259,55 @@ module CarrierWave
303
259
  cache_stored_file! if !cached?
304
260
  image = ::MiniMagick::Image.open(current_path)
305
261
 
306
- begin
307
- image.format(@format.to_s.downcase, @page) if @format
308
- image = yield(image)
309
- image.write(current_path)
262
+ image = yield(image)
263
+ FileUtils.mv image.path, current_path
310
264
 
311
- if @format
312
- move_to = current_path.chomp(File.extname(current_path)) + ".#{@format}"
313
- file.content_type = ::MIME::Types.type_for(move_to).first.to_s
314
- file.move_to(move_to, permissions, directory_permissions)
315
- end
265
+ image.run_command("identify", current_path)
266
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
267
+ message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e)
268
+ raise CarrierWave::ProcessingError, message
269
+ ensure
270
+ image.destroy! if image
271
+ end
316
272
 
317
- image.run_command("identify", current_path)
318
- ensure
319
- image.destroy!
273
+ # Process the image with MiniMagick, using the ImageProcessing gem. This
274
+ # method will build a "convert" ImageMagick command and execute it on the
275
+ # current image.
276
+ #
277
+ # === Gotcha
278
+ #
279
+ # This method assumes that the object responds to +current_path+.
280
+ # Any class that this module is mixed into must have a +current_path+ method.
281
+ # CarrierWave::Uploader does, so you won't need to worry about this in
282
+ # most cases.
283
+ #
284
+ # === Yields
285
+ #
286
+ # [ImageProcessing::Builder] use it to define processing to be performed
287
+ #
288
+ # === Raises
289
+ #
290
+ # [CarrierWave::ProcessingError] if processing failed.
291
+ def minimagick!(block = nil)
292
+ builder = ImageProcessing::MiniMagick.source(current_path)
293
+ builder = yield(builder)
294
+
295
+ result = builder.call
296
+ result.close
297
+
298
+ # backwards compatibility (we want to eventually move away from MiniMagick::Image)
299
+ if block
300
+ image = ::MiniMagick::Image.new(result.path, result)
301
+ image = block.call(image)
302
+ result = image.instance_variable_get(:@tempfile)
303
+ end
304
+
305
+ FileUtils.mv result.path, current_path
306
+
307
+ if File.extname(result.path) != File.extname(current_path)
308
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
309
+ file.content_type = ::MiniMime.lookup_by_filename(move_to).content_type
310
+ file.move_to(move_to, permissions, directory_permissions)
320
311
  end
321
312
  rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
322
313
  message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e)
@@ -325,21 +316,13 @@ module CarrierWave
325
316
 
326
317
  private
327
318
 
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
319
+ def resolve_dimensions(*dimensions)
320
+ dimensions.map do |value|
321
+ next value unless value.instance_of?(Proc)
322
+ value.arity >= 1 ? value.call(self) : value.call
335
323
  end
336
324
  end
337
325
 
338
- def dimension_from(value)
339
- return value unless value.instance_of?(Proc)
340
- value.arity >= 1 ? value.call(self) : value.call
341
- end
342
-
343
326
  def mini_magick_image
344
327
  ::MiniMagick::Image.read(read)
345
328
  end
@@ -228,7 +228,7 @@ module CarrierWave
228
228
  height = dimension_from height
229
229
  manipulate! do |img|
230
230
  img.resize_to_fit!(width, height)
231
- new_img = ::Magick::Image.new(width, height) { self.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
231
+ new_img = ::Magick::Image.new(width, height) { |img| img.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
232
232
  if background == :transparent
233
233
  filled = new_img.matte_floodfill(1, 1)
234
234
  else
@@ -363,7 +363,7 @@ module CarrierWave
363
363
  if options[:format] || @format
364
364
  frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
365
365
  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
366
+ file.content_type = ::MiniMime.lookup_by_filename(move_to).content_type
367
367
  file.move_to(move_to, permissions, directory_permissions)
368
368
  else
369
369
  frames.write(current_path, &write_block)