carrierwave 1.3.1 → 2.2.0

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.

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 +102 -43
  3. data/lib/carrierwave/downloader/base.rb +87 -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 +71 -48
  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 +11 -5
  11. data/lib/carrierwave/processing/vips.rb +284 -0
  12. data/lib/carrierwave/processing.rb +1 -0
  13. data/lib/carrierwave/sanitized_file.rb +45 -23
  14. data/lib/carrierwave/storage/file.rb +2 -2
  15. data/lib/carrierwave/storage/fog.rb +42 -14
  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 -80
  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 +93 -21
@@ -0,0 +1,284 @@
1
+ module CarrierWave
2
+
3
+ ##
4
+ # This module simplifies manipulation with vips 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 'carrierwave/processing/vips'
9
+ #
10
+ # And then include it in your uploader:
11
+ #
12
+ # class MyUploader < CarrierWave::Uploader::Base
13
+ # include CarrierWave::Vips
14
+ # end
15
+ #
16
+ # You can now use the provided helpers:
17
+ #
18
+ # class MyUploader < CarrierWave::Uploader::Base
19
+ # include CarrierWave::Vips
20
+ #
21
+ # process :resize_to_fit => [200, 200]
22
+ # end
23
+ #
24
+ # Or create your own helpers with the powerful vips! 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 Vips options at
28
+ # https://libvips.github.io/libvips/API/current/using-cli.html for more info.
29
+ #
30
+ # class MyUploader < CarrierWave::Uploader::Base
31
+ # include CarrierWave::Vips
32
+ #
33
+ # process :radial_blur => 10
34
+ #
35
+ # def radial_blur(amount)
36
+ # vips! do |builder|
37
+ # builder.radial_blur(amount)
38
+ # builder = yield(builder) if block_given?
39
+ # builder
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ # === Note
45
+ #
46
+ # The ImageProcessing gem uses ruby-vips, a binding for the vips image
47
+ # library. You can find more information here:
48
+ #
49
+ # https://github.com/libvips/ruby-vips
50
+ #
51
+ #
52
+ module Vips
53
+ extend ActiveSupport::Concern
54
+
55
+ included do
56
+ require "image_processing/vips"
57
+ # We need to disable caching since we're editing images in place.
58
+ ::Vips.cache_set_max(0)
59
+ end
60
+
61
+ module ClassMethods
62
+ def convert(format)
63
+ process :convert => format
64
+ end
65
+
66
+ def resize_to_limit(width, height)
67
+ process :resize_to_limit => [width, height]
68
+ end
69
+
70
+ def resize_to_fit(width, height)
71
+ process :resize_to_fit => [width, height]
72
+ end
73
+
74
+ def resize_to_fill(width, height, gravity='centre')
75
+ process :resize_to_fill => [width, height, gravity]
76
+ end
77
+
78
+ def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil)
79
+ process :resize_and_pad => [width, height, background, gravity, alpha]
80
+ end
81
+ end
82
+
83
+ ##
84
+ # Changes the image encoding format to the given format
85
+ #
86
+ # See https://libvips.github.io/libvips/API/current/using-cli.html#using-command-line-conversion
87
+ #
88
+ # === Parameters
89
+ #
90
+ # [format (#to_s)] an abbreviation of the format
91
+ #
92
+ # === Yields
93
+ #
94
+ # [Vips::Image] additional manipulations to perform
95
+ #
96
+ # === Examples
97
+ #
98
+ # image.convert(:png)
99
+ #
100
+ def convert(format, page=nil)
101
+ vips! do |builder|
102
+ builder = builder.convert(format)
103
+ builder = builder.loader(page: page) if page
104
+ builder
105
+ end
106
+ end
107
+
108
+ ##
109
+ # Resize the image to fit within the specified dimensions while retaining
110
+ # the original aspect ratio. Will only resize the image if it is larger than the
111
+ # specified dimensions. The resulting image may be shorter or narrower than specified
112
+ # in the smaller dimension but will not be larger than the specified values.
113
+ #
114
+ # === Parameters
115
+ #
116
+ # [width (Integer)] the width to scale the image to
117
+ # [height (Integer)] the height to scale the image to
118
+ # [combine_options (Hash)] additional Vips options to apply before resizing
119
+ #
120
+ # === Yields
121
+ #
122
+ # [Vips::Image] additional manipulations to perform
123
+ #
124
+ def resize_to_limit(width, height, combine_options: {})
125
+ width, height = resolve_dimensions(width, height)
126
+
127
+ vips! do |builder|
128
+ builder.resize_to_limit(width, height)
129
+ .apply(combine_options)
130
+ end
131
+ end
132
+
133
+ ##
134
+ # Resize the image to fit within the specified dimensions while retaining
135
+ # the original aspect ratio. The image may be shorter or narrower than
136
+ # specified in the smaller dimension but will not be larger than the specified values.
137
+ #
138
+ # === Parameters
139
+ #
140
+ # [width (Integer)] the width to scale the image to
141
+ # [height (Integer)] the height to scale the image to
142
+ # [combine_options (Hash)] additional Vips options to apply before resizing
143
+ #
144
+ # === Yields
145
+ #
146
+ # [Vips::Image] additional manipulations to perform
147
+ #
148
+ def resize_to_fit(width, height, combine_options: {})
149
+ width, height = resolve_dimensions(width, height)
150
+
151
+ vips! do |builder|
152
+ builder.resize_to_fit(width, height)
153
+ .apply(combine_options)
154
+ end
155
+ end
156
+
157
+ ##
158
+ # Resize the image to fit within the specified dimensions while retaining
159
+ # the aspect ratio of the original image. If necessary, crop the image in the
160
+ # larger dimension.
161
+ #
162
+ # === Parameters
163
+ #
164
+ # [width (Integer)] the width to scale the image to
165
+ # [height (Integer)] the height to scale the image to
166
+ # [combine_options (Hash)] additional vips options to apply before resizing
167
+ #
168
+ # === Yields
169
+ #
170
+ # [Vips::Image] additional manipulations to perform
171
+ #
172
+ def resize_to_fill(width, height, _gravity = nil, combine_options: {})
173
+ width, height = resolve_dimensions(width, height)
174
+
175
+ vips! do |builder|
176
+ builder.resize_to_fill(width, height).apply(combine_options)
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Resize the image to fit within the specified dimensions while retaining
182
+ # the original aspect ratio. If necessary, will pad the remaining area
183
+ # with the given color, which defaults to transparent (for gif and png,
184
+ # white for jpeg).
185
+ #
186
+ # See https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsCompassDirection
187
+ # for gravity options.
188
+ #
189
+ # === Parameters
190
+ #
191
+ # [width (Integer)] the width to scale the image to
192
+ # [height (Integer)] the height to scale the image to
193
+ # [background (List, nil)] the color of the background as a RGB, like [0, 255, 255], nil indicates transparent
194
+ # [gravity (String)] how to position the image
195
+ # [alpha (Boolean, nil)] pad the image with the alpha channel if supported
196
+ # [combine_options (Hash)] additional vips options to apply before resizing
197
+ #
198
+ # === Yields
199
+ #
200
+ # [Vips::Image] additional manipulations to perform
201
+ #
202
+ def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil, combine_options: {})
203
+ width, height = resolve_dimensions(width, height)
204
+
205
+ vips! do |builder|
206
+ builder.resize_and_pad(width, height, background: background, gravity: gravity, alpha: alpha)
207
+ .apply(combine_options)
208
+ end
209
+ end
210
+
211
+ ##
212
+ # Returns the width of the image in pixels.
213
+ #
214
+ # === Returns
215
+ #
216
+ # [Integer] the image's width in pixels
217
+ #
218
+ def width
219
+ vips_image.width
220
+ end
221
+
222
+ ##
223
+ # Returns the height of the image in pixels.
224
+ #
225
+ # === Returns
226
+ #
227
+ # [Integer] the image's height in pixels
228
+ #
229
+ def height
230
+ vips_image.height
231
+ end
232
+
233
+ # Process the image with vip, using the ImageProcessing gem. This
234
+ # method will build a "convert" vips command and execute it on the
235
+ # current image.
236
+ #
237
+ # === Gotcha
238
+ #
239
+ # This method assumes that the object responds to +current_path+.
240
+ # Any class that this module is mixed into must have a +current_path+ method.
241
+ # CarrierWave::Uploader does, so you won't need to worry about this in
242
+ # most cases.
243
+ #
244
+ # === Yields
245
+ #
246
+ # [ImageProcessing::Builder] use it to define processing to be performed
247
+ #
248
+ # === Raises
249
+ #
250
+ # [CarrierWave::ProcessingError] if processing failed.
251
+ def vips!
252
+ builder = ImageProcessing::Vips.source(current_path)
253
+ builder = yield(builder)
254
+
255
+ result = builder.call
256
+ result.close
257
+
258
+ FileUtils.mv result.path, current_path
259
+
260
+ if File.extname(result.path) != File.extname(current_path)
261
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
262
+ file.content_type = ::MiniMime.lookup_by_filename(move_to).content_type
263
+ file.move_to(move_to, permissions, directory_permissions)
264
+ end
265
+ rescue ::Vips::Error => e
266
+ message = I18n.translate(:"errors.messages.vips_processing_error", :e => e)
267
+ raise CarrierWave::ProcessingError, message
268
+ end
269
+
270
+ private
271
+
272
+ def resolve_dimensions(*dimensions)
273
+ dimensions.map do |value|
274
+ next value unless value.instance_of?(Proc)
275
+ value.arity >= 1 ? value.call(self) : value.call
276
+ end
277
+ end
278
+
279
+ def vips_image
280
+ ::Vips::Image.new_from_buffer(read, "")
281
+ end
282
+
283
+ end # Vips
284
+ end # CarrierWave
@@ -1,2 +1,3 @@
1
1
  require "carrierwave/processing/rmagick"
2
2
  require "carrierwave/processing/mini_magick"
3
+ require "carrierwave/processing/vips"
@@ -1,12 +1,8 @@
1
1
  require 'pathname'
2
2
  require 'active_support/core_ext/string/multibyte'
3
-
4
- begin
5
- # Use mime/types/columnar if available, for reduced memory usage
6
- require 'mime/types/columnar'
7
- rescue LoadError
8
- require 'mime/types'
9
- end
3
+ require 'mini_mime'
4
+ require 'mimemagic'
5
+ require 'mimemagic/overlay'
10
6
 
11
7
  module CarrierWave
12
8
 
@@ -114,12 +110,11 @@ module CarrierWave
114
110
  # [String, nil] the path where the file is located.
115
111
  #
116
112
  def path
117
- unless @file.blank?
118
- if is_path?
119
- File.expand_path(@file)
120
- elsif @file.respond_to?(:path) and not @file.path.blank?
121
- File.expand_path(@file.path)
122
- end
113
+ return if @file.blank?
114
+ if is_path?
115
+ File.expand_path(@file)
116
+ elsif @file.respond_to?(:path) && !@file.path.blank?
117
+ File.expand_path(@file.path)
123
118
  end
124
119
  end
125
120
 
@@ -187,9 +182,9 @@ module CarrierWave
187
182
  move!(new_path)
188
183
  chmod!(new_path, permissions)
189
184
  if keep_filename
190
- self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
185
+ self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
191
186
  else
192
- self.file = {:tempfile => new_path, :content_type => content_type}
187
+ self.file = {:tempfile => new_path, :content_type => @content_type}
193
188
  end
194
189
  self
195
190
  end
@@ -265,12 +260,10 @@ module CarrierWave
265
260
  # [String] the content type of the file
266
261
  #
267
262
  def content_type
268
- return @content_type if @content_type
269
- if @file.respond_to?(:content_type) and @file.content_type
270
- @content_type = @file.content_type.to_s.chomp
271
- elsif path
272
- @content_type = ::MIME::Types.type_for(path).first.to_s
273
- end
263
+ @content_type ||=
264
+ existing_content_type ||
265
+ mime_magic_content_type ||
266
+ mini_mime_content_type
274
267
  end
275
268
 
276
269
  ##
@@ -313,7 +306,7 @@ module CarrierWave
313
306
  def mkdir!(path, directory_permissions)
314
307
  options = {}
315
308
  options[:mode] = directory_permissions if directory_permissions
316
- FileUtils.mkdir_p(File.dirname(path), options) unless File.exist?(File.dirname(path))
309
+ FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
317
310
  end
318
311
 
319
312
  def chmod!(path, permissions)
@@ -326,10 +319,39 @@ module CarrierWave
326
319
  name = File.basename(name)
327
320
  name = name.gsub(sanitize_regexp,"_")
328
321
  name = "_#{name}" if name =~ /\A\.+\z/
329
- name = "unnamed" if name.size == 0
322
+ name = "unnamed" if name.size.zero?
330
323
  return name.mb_chars.to_s
331
324
  end
332
325
 
326
+ def existing_content_type
327
+ if @file.respond_to?(:content_type) && @file.content_type
328
+ @file.content_type.to_s.chomp
329
+ end
330
+ end
331
+
332
+ def mime_magic_content_type
333
+ if path
334
+ type = File.open(path) do |file|
335
+ MimeMagic.by_magic(file).try(:type)
336
+ end
337
+
338
+ if type.nil?
339
+ type = ::MiniMime.lookup_by_filename(path).try(:content_type)
340
+ type = 'invalid/invalid' unless type.nil? || type.start_with?('text/')
341
+ end
342
+
343
+ type
344
+ end
345
+ rescue Errno::ENOENT
346
+ nil
347
+ end
348
+
349
+ def mini_mime_content_type
350
+ return unless path
351
+ mime_type = ::MiniMime.lookup_by_filename(path)
352
+ @content_type = (mime_type && mime_type.content_type).to_s
353
+ end
354
+
333
355
  def split_extension(filename)
334
356
  # regular expressions to try for identifying extensions
335
357
  extension_matchers = [
@@ -110,8 +110,8 @@ module CarrierWave
110
110
 
111
111
  def clean_cache!(seconds)
112
112
  Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), CarrierWave.root)).each do |dir|
113
- # generate_cache_id returns key formated TIMEINT-PID-COUNTER-RND
114
- time = dir.scan(/(\d+)-\d+-\d+-\d+/).first.map(&:to_i)
113
+ # generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
114
+ time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
115
115
  time = Time.at(*time)
116
116
  if time < (Time.now.utc - seconds)
117
117
  FileUtils.rm_rf(dir)
@@ -60,6 +60,14 @@ module CarrierWave
60
60
  def connection_cache
61
61
  @connection_cache ||= {}
62
62
  end
63
+
64
+ def eager_load
65
+ # see #1198. This will hopefully no longer be necessary in future release of fog
66
+ fog_credentials = CarrierWave::Uploader::Base.fog_credentials
67
+ if fog_credentials.present?
68
+ CarrierWave::Storage::Fog.connection_cache[fog_credentials] ||= ::Fog::Storage.new(fog_credentials)
69
+ end
70
+ end
63
71
  end
64
72
 
65
73
  ##
@@ -138,8 +146,8 @@ module CarrierWave
138
146
  :key => uploader.fog_directory,
139
147
  :public => uploader.fog_public
140
148
  ).files.all(:prefix => uploader.cache_dir).each do |file|
141
- # generate_cache_id returns key formated TIMEINT-PID-COUNTER-RND
142
- time = file.key.scan(/(\d+)-\d+-\d+-\d+/).first.map { |t| t.to_i }
149
+ # generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
150
+ time = file.key.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map { |t| t.to_i }
143
151
  time = Time.at(*time)
144
152
  file.destroy if time < (Time.now.utc - seconds)
145
153
  end
@@ -153,6 +161,8 @@ module CarrierWave
153
161
  end
154
162
 
155
163
  class File
164
+ DEFAULT_S3_REGION = 'us-east-1'
165
+
156
166
  include CarrierWave::Utilities::Uri
157
167
 
158
168
  ##
@@ -177,7 +187,7 @@ module CarrierWave
177
187
 
178
188
  ##
179
189
  # Return a temporary authenticated url to a private file, if available
180
- # Only supported for AWS, Rackspace and Google providers
190
+ # Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
181
191
  #
182
192
  # === Returns
183
193
  #
@@ -186,11 +196,11 @@ module CarrierWave
186
196
  # [NilClass] no authenticated url available
187
197
  #
188
198
  def authenticated_url(options = {})
189
- if ['AWS', 'Google', 'Rackspace', 'OpenStack'].include?(@uploader.fog_credentials[:provider])
199
+ if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(@uploader.fog_credentials[:provider])
190
200
  # avoid a get by using local references
191
201
  local_directory = connection.directories.new(:key => @uploader.fog_directory)
192
202
  local_file = local_directory.files.new(:key => path)
193
- expire_at = ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
203
+ expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
194
204
  case @uploader.fog_credentials[:provider]
195
205
  when 'AWS', 'Google'
196
206
  # Older versions of fog-google do not support options as a parameter
@@ -200,10 +210,11 @@ module CarrierWave
200
210
  warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
201
211
  local_file.url(expire_at)
202
212
  end
203
- when 'Rackspace'
213
+ when 'Rackspace', 'OpenStack'
204
214
  connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
205
- when 'OpenStack'
206
- connection.get_object_https_url(@uploader.fog_directory, path, expire_at)
215
+ when 'Aliyun'
216
+ expire_at = expire_at - Time.now
217
+ local_file.url(expire_at)
207
218
  else
208
219
  local_file.url(expire_at)
209
220
  end
@@ -218,7 +229,7 @@ module CarrierWave
218
229
  # [String] value of content-type
219
230
  #
220
231
  def content_type
221
- @content_type || !file.nil? && file.content_type
232
+ @content_type || file.try(:content_type)
222
233
  end
223
234
 
224
235
  ##
@@ -241,7 +252,9 @@ module CarrierWave
241
252
  #
242
253
  def delete
243
254
  # avoid a get by just using local reference
244
- directory.files.new(:key => path).destroy
255
+ directory.files.new(:key => path).destroy.tap do |result|
256
+ @file = nil if result
257
+ end
245
258
  end
246
259
 
247
260
  ##
@@ -373,9 +386,15 @@ module CarrierWave
373
386
  if valid_subdomain
374
387
  s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
375
388
  "#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
376
- else
377
- # directory is not a valid subdomain, so use path style for access
378
- "#{protocol}://s3.amazonaws.com/#{@uploader.fog_directory}/#{encoded_path}"
389
+ else # directory is not a valid subdomain, so use path style for access
390
+ region = @uploader.fog_credentials[:region].to_s
391
+ host = case region
392
+ when DEFAULT_S3_REGION, ''
393
+ 's3.amazonaws.com'
394
+ else
395
+ "s3.#{region}.amazonaws.com"
396
+ end
397
+ "#{protocol}://#{host}/#{@uploader.fog_directory}/#{encoded_path}"
379
398
  end
380
399
  end
381
400
  when 'Google'
@@ -431,7 +450,7 @@ module CarrierWave
431
450
  # @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
432
451
  #
433
452
  def copy_to(new_path)
434
- connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, acl_header)
453
+ connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, copy_options)
435
454
  CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
436
455
  end
437
456
 
@@ -475,9 +494,18 @@ module CarrierWave
475
494
  @file ||= directory.files.head(path)
476
495
  end
477
496
 
497
+ def copy_options
498
+ options = {}
499
+ options.merge!(acl_header) if acl_header.present?
500
+ options['Content-Type'] ||= content_type if content_type
501
+ options.merge(@uploader.fog_attributes)
502
+ end
503
+
478
504
  def acl_header
479
505
  if fog_provider == 'AWS'
480
506
  { 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
507
+ elsif fog_provider == "Google"
508
+ @uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
481
509
  else
482
510
  {}
483
511
  end
@@ -1,2 +1,3 @@
1
1
  require "carrierwave/storage/abstract"
2
2
  require "carrierwave/storage/file"
3
+ require "carrierwave/storage/fog"
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module CarrierWave
2
4
 
3
5
  class FormNotMultipart < UploadError
@@ -23,9 +25,9 @@ module CarrierWave
23
25
  #
24
26
  def self.generate_cache_id
25
27
  [Time.now.utc.to_i,
26
- Process.pid,
27
- '%04d' % (CarrierWave::CacheCounter.increment % 1000),
28
- '%04d' % rand(9999)
28
+ SecureRandom.random_number(1_000_000_000_000_000),
29
+ '%04d' % (CarrierWave::CacheCounter.increment % 10_000),
30
+ '%04d' % SecureRandom.random_number(10_000)
29
31
  ].map(&:to_s).join('-')
30
32
  end
31
33
 
@@ -36,6 +38,16 @@ module CarrierWave
36
38
  include CarrierWave::Uploader::Callbacks
37
39
  include CarrierWave::Uploader::Configuration
38
40
 
41
+ included do
42
+ prepend Module.new {
43
+ def initialize(*)
44
+ super
45
+ @staged = false
46
+ end
47
+ }
48
+ attr_accessor :staged
49
+ end
50
+
39
51
  module ClassMethods
40
52
 
41
53
  ##
@@ -52,7 +64,7 @@ module CarrierWave
52
64
  # It's recommended that you keep cache files in one place only.
53
65
  #
54
66
  def clean_cached_files!(seconds=60*60*24)
55
- cache_storage.new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
67
+ (cache_storage || storage).new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
56
68
  end
57
69
  end
58
70
 
@@ -64,7 +76,7 @@ module CarrierWave
64
76
  # [Bool] whether the current file is cached
65
77
  #
66
78
  def cached?
67
- @cache_id
79
+ !!@cache_id
68
80
  end
69
81
 
70
82
  ##
@@ -78,14 +90,8 @@ module CarrierWave
78
90
  end
79
91
 
80
92
  def sanitized_file
81
- _content = file.read
82
- if _content.is_a?(File) # could be if storage is Fog
83
- sanitized = CarrierWave::Storage::Fog.new(self).retrieve!(File.basename(_content.path))
84
- else
85
- sanitized = SanitizedFile.new :tempfile => StringIO.new(_content),
86
- :filename => File.basename(path), :content_type => file.content_type
87
- end
88
- sanitized
93
+ ActiveSupport::Deprecation.warn('#sanitized_file is deprecated, use #file instead.')
94
+ file
89
95
  end
90
96
 
91
97
  ##
@@ -96,7 +102,7 @@ module CarrierWave
96
102
  # [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
97
103
  #
98
104
  def cache_name
99
- File.join(cache_id, full_original_filename) if cache_id and original_filename
105
+ File.join(cache_id, full_original_filename) if cache_id && original_filename
100
106
  end
101
107
 
102
108
  ##
@@ -115,7 +121,7 @@ module CarrierWave
115
121
  #
116
122
  # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
117
123
  #
118
- def cache!(new_file = sanitized_file)
124
+ def cache!(new_file = file)
119
125
  new_file = CarrierWave::SanitizedFile.new(new_file)
120
126
  return if new_file.empty?
121
127
 
@@ -123,6 +129,7 @@ module CarrierWave
123
129
 
124
130
  self.cache_id = CarrierWave.generate_cache_id unless cache_id
125
131
 
132
+ @staged = true
126
133
  @filename = new_file.filename
127
134
  self.original_filename = new_file.filename
128
135
 
@@ -156,6 +163,7 @@ module CarrierWave
156
163
  def retrieve_from_cache!(cache_name)
157
164
  with_callbacks(:retrieve_from_cache, cache_name) do
158
165
  self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
166
+ @staged = true
159
167
  @filename = original_filename
160
168
  @file = cache_storage.retrieve_from_cache!(full_filename(original_filename))
161
169
  end
@@ -200,7 +208,7 @@ module CarrierWave
200
208
  end
201
209
 
202
210
  def cache_storage
203
- @cache_storage ||= self.class.cache_storage.new(self)
211
+ @cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
204
212
  end
205
213
  end # Cache
206
214
  end # Uploader