carrierwave 1.3.0 → 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 +120 -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 +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 +44 -23
  14. data/lib/carrierwave/storage/file.rb +2 -2
  15. data/lib/carrierwave/storage/fog.rb +46 -17
  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 +113 -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,7 @@
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 'marcel'
10
5
 
11
6
  module CarrierWave
12
7
 
@@ -114,12 +109,11 @@ module CarrierWave
114
109
  # [String, nil] the path where the file is located.
115
110
  #
116
111
  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
112
+ return if @file.blank?
113
+ if is_path?
114
+ File.expand_path(@file)
115
+ elsif @file.respond_to?(:path) && !@file.path.blank?
116
+ File.expand_path(@file.path)
123
117
  end
124
118
  end
125
119
 
@@ -187,9 +181,9 @@ module CarrierWave
187
181
  move!(new_path)
188
182
  chmod!(new_path, permissions)
189
183
  if keep_filename
190
- self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
184
+ self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
191
185
  else
192
- self.file = {:tempfile => new_path, :content_type => content_type}
186
+ self.file = {:tempfile => new_path, :content_type => @content_type}
193
187
  end
194
188
  self
195
189
  end
@@ -265,12 +259,10 @@ module CarrierWave
265
259
  # [String] the content type of the file
266
260
  #
267
261
  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
262
+ @content_type ||=
263
+ existing_content_type ||
264
+ marcel_magic_content_type ||
265
+ mini_mime_content_type
274
266
  end
275
267
 
276
268
  ##
@@ -313,7 +305,7 @@ module CarrierWave
313
305
  def mkdir!(path, directory_permissions)
314
306
  options = {}
315
307
  options[:mode] = directory_permissions if directory_permissions
316
- FileUtils.mkdir_p(File.dirname(path), options) unless File.exist?(File.dirname(path))
308
+ FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
317
309
  end
318
310
 
319
311
  def chmod!(path, permissions)
@@ -326,10 +318,39 @@ module CarrierWave
326
318
  name = File.basename(name)
327
319
  name = name.gsub(sanitize_regexp,"_")
328
320
  name = "_#{name}" if name =~ /\A\.+\z/
329
- name = "unnamed" if name.size == 0
321
+ name = "unnamed" if name.size.zero?
330
322
  return name.mb_chars.to_s
331
323
  end
332
324
 
325
+ def existing_content_type
326
+ if @file.respond_to?(:content_type) && @file.content_type
327
+ @file.content_type.to_s.chomp
328
+ end
329
+ end
330
+
331
+ def marcel_magic_content_type
332
+ if path
333
+ type = File.open(path) do |file|
334
+ Marcel::Magic.by_magic(file).try(:type)
335
+ end
336
+
337
+ if type.nil?
338
+ type = Marcel::Magic.by_path(path).try(:type)
339
+ type = 'invalid/invalid' unless type.nil? || type.start_with?('text/')
340
+ end
341
+
342
+ type
343
+ end
344
+ rescue Errno::ENOENT
345
+ nil
346
+ end
347
+
348
+ def mini_mime_content_type
349
+ return unless path
350
+ mime_type = ::MiniMime.lookup_by_filename(path)
351
+ @content_type = (mime_type && mime_type.content_type).to_s
352
+ end
353
+
333
354
  def split_extension(filename)
334
355
  # regular expressions to try for identifying extensions
335
356
  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)
@@ -30,7 +30,7 @@ module CarrierWave
30
30
  #
31
31
  # Google credentials contain the following keys:
32
32
  # [:google_storage_access_key_id]
33
- # [:google_storage_secrete_access_key]
33
+ # [:google_storage_secret_access_key]
34
34
  #
35
35
  #
36
36
  # Local credentials contain the following keys:
@@ -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,9 +146,10 @@ 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 }
143
- time = Time.at(*time)
149
+ # generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
150
+ matched = file.key.match(/(\d+)-\d+-\d+(?:-\d+)?/)
151
+ next unless matched
152
+ time = Time.at(matched[1].to_i)
144
153
  file.destroy if time < (Time.now.utc - seconds)
145
154
  end
146
155
  end
@@ -153,6 +162,8 @@ module CarrierWave
153
162
  end
154
163
 
155
164
  class File
165
+ DEFAULT_S3_REGION = 'us-east-1'
166
+
156
167
  include CarrierWave::Utilities::Uri
157
168
 
158
169
  ##
@@ -177,7 +188,7 @@ module CarrierWave
177
188
 
178
189
  ##
179
190
  # Return a temporary authenticated url to a private file, if available
180
- # Only supported for AWS, Rackspace and Google providers
191
+ # Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
181
192
  #
182
193
  # === Returns
183
194
  #
@@ -186,11 +197,11 @@ module CarrierWave
186
197
  # [NilClass] no authenticated url available
187
198
  #
188
199
  def authenticated_url(options = {})
189
- if ['AWS', 'Google', 'Rackspace', 'OpenStack'].include?(@uploader.fog_credentials[:provider])
200
+ if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(@uploader.fog_credentials[:provider])
190
201
  # avoid a get by using local references
191
202
  local_directory = connection.directories.new(:key => @uploader.fog_directory)
192
203
  local_file = local_directory.files.new(:key => path)
193
- expire_at = ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
204
+ expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
194
205
  case @uploader.fog_credentials[:provider]
195
206
  when 'AWS', 'Google'
196
207
  # Older versions of fog-google do not support options as a parameter
@@ -200,10 +211,11 @@ module CarrierWave
200
211
  warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
201
212
  local_file.url(expire_at)
202
213
  end
203
- when 'Rackspace'
214
+ when 'Rackspace', 'OpenStack'
204
215
  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)
216
+ when 'Aliyun'
217
+ expire_at = expire_at - Time.now
218
+ local_file.url(expire_at)
207
219
  else
208
220
  local_file.url(expire_at)
209
221
  end
@@ -218,7 +230,7 @@ module CarrierWave
218
230
  # [String] value of content-type
219
231
  #
220
232
  def content_type
221
- @content_type || !file.nil? && file.content_type
233
+ @content_type || file.try(:content_type)
222
234
  end
223
235
 
224
236
  ##
@@ -241,7 +253,9 @@ module CarrierWave
241
253
  #
242
254
  def delete
243
255
  # avoid a get by just using local reference
244
- directory.files.new(:key => path).destroy
256
+ directory.files.new(:key => path).destroy.tap do |result|
257
+ @file = nil if result
258
+ end
245
259
  end
246
260
 
247
261
  ##
@@ -373,9 +387,15 @@ module CarrierWave
373
387
  if valid_subdomain
374
388
  s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
375
389
  "#{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}"
390
+ else # directory is not a valid subdomain, so use path style for access
391
+ region = @uploader.fog_credentials[:region].to_s
392
+ host = case region
393
+ when DEFAULT_S3_REGION, ''
394
+ 's3.amazonaws.com'
395
+ else
396
+ "s3.#{region}.amazonaws.com"
397
+ end
398
+ "#{protocol}://#{host}/#{@uploader.fog_directory}/#{encoded_path}"
379
399
  end
380
400
  end
381
401
  when 'Google'
@@ -431,7 +451,7 @@ module CarrierWave
431
451
  # @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
432
452
  #
433
453
  def copy_to(new_path)
434
- connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, acl_header)
454
+ connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, copy_options)
435
455
  CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
436
456
  end
437
457
 
@@ -475,9 +495,18 @@ module CarrierWave
475
495
  @file ||= directory.files.head(path)
476
496
  end
477
497
 
498
+ def copy_options
499
+ options = {}
500
+ options.merge!(acl_header) if acl_header.present?
501
+ options['Content-Type'] ||= content_type if content_type
502
+ options.merge(@uploader.fog_attributes)
503
+ end
504
+
478
505
  def acl_header
479
506
  if fog_provider == 'AWS'
480
507
  { 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
508
+ elsif fog_provider == "Google"
509
+ @uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
481
510
  else
482
511
  {}
483
512
  end
@@ -499,7 +528,7 @@ module CarrierWave
499
528
  end
500
529
 
501
530
  def url_options_supported?(local_file)
502
- parameters = file.method(:url).parameters
531
+ parameters = local_file.method(:url).parameters
503
532
  parameters.count == 2 && parameters[1].include?(:options)
504
533
  end
505
534
  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