carrierwave 2.2.5 → 3.0.5

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +137 -67
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -2
  4. data/lib/carrierwave/downloader/base.rb +19 -11
  5. data/lib/carrierwave/downloader/remote_file.rb +12 -9
  6. data/lib/carrierwave/locale/en.yml +5 -3
  7. data/lib/carrierwave/mount.rb +36 -50
  8. data/lib/carrierwave/mounter.rb +117 -50
  9. data/lib/carrierwave/orm/activerecord.rb +14 -60
  10. data/lib/carrierwave/processing/mini_magick.rb +15 -13
  11. data/lib/carrierwave/processing/rmagick.rb +11 -15
  12. data/lib/carrierwave/processing/vips.rb +12 -12
  13. data/lib/carrierwave/sanitized_file.rb +49 -77
  14. data/lib/carrierwave/storage/abstract.rb +5 -5
  15. data/lib/carrierwave/storage/file.rb +6 -5
  16. data/lib/carrierwave/storage/fog.rb +74 -66
  17. data/lib/carrierwave/test/matchers.rb +11 -7
  18. data/lib/carrierwave/uploader/cache.rb +18 -10
  19. data/lib/carrierwave/uploader/callbacks.rb +1 -1
  20. data/lib/carrierwave/uploader/configuration.rb +10 -4
  21. data/lib/carrierwave/uploader/{content_type_whitelist.rb → content_type_allowlist.rb} +17 -15
  22. data/lib/carrierwave/uploader/{content_type_blacklist.rb → content_type_denylist.rb} +19 -14
  23. data/lib/carrierwave/uploader/dimension.rb +66 -0
  24. data/lib/carrierwave/uploader/{extension_whitelist.rb → extension_allowlist.rb} +17 -15
  25. data/lib/carrierwave/uploader/{extension_blacklist.rb → extension_denylist.rb} +18 -13
  26. data/lib/carrierwave/uploader/file_size.rb +2 -2
  27. data/lib/carrierwave/uploader/processing.rb +31 -6
  28. data/lib/carrierwave/uploader/proxy.rb +16 -3
  29. data/lib/carrierwave/uploader/store.rb +44 -6
  30. data/lib/carrierwave/uploader/url.rb +1 -1
  31. data/lib/carrierwave/uploader/versions.rb +150 -132
  32. data/lib/carrierwave/uploader.rb +10 -8
  33. data/lib/carrierwave/utilities/file_name.rb +47 -0
  34. data/lib/carrierwave/utilities/uri.rb +14 -11
  35. data/lib/carrierwave/utilities.rb +1 -0
  36. data/lib/carrierwave/validations/active_model.rb +4 -6
  37. data/lib/carrierwave/version.rb +1 -1
  38. data/lib/carrierwave.rb +9 -17
  39. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +1 -1
  40. data/lib/generators/uploader_generator.rb +3 -3
  41. metadata +27 -53
@@ -1,6 +1,5 @@
1
1
  require 'pathname'
2
2
  require 'active_support/core_ext/string/multibyte'
3
- require 'mini_mime'
4
3
  require 'marcel'
5
4
 
6
5
  module CarrierWave
@@ -14,6 +13,7 @@ module CarrierWave
14
13
  # It's probably needlessly comprehensive and complex. Help is appreciated.
15
14
  #
16
15
  class SanitizedFile
16
+ include CarrierWave::Utilities::FileName
17
17
 
18
18
  attr_reader :file
19
19
 
@@ -27,7 +27,7 @@ module CarrierWave
27
27
 
28
28
  def initialize(file)
29
29
  self.file = file
30
- @content = nil
30
+ @content = @content_type = nil
31
31
  end
32
32
 
33
33
  ##
@@ -39,7 +39,7 @@ module CarrierWave
39
39
  #
40
40
  def original_filename
41
41
  return @original_filename if @original_filename
42
- if @file and @file.respond_to?(:original_filename)
42
+ if @file && @file.respond_to?(:original_filename)
43
43
  @file.original_filename
44
44
  elsif path
45
45
  File.basename(path)
@@ -59,29 +59,6 @@ module CarrierWave
59
59
 
60
60
  alias_method :identifier, :filename
61
61
 
62
- ##
63
- # Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
64
- # this would return 'test'
65
- #
66
- # === Returns
67
- #
68
- # [String] the first part of the filename
69
- #
70
- def basename
71
- split_extension(filename)[0] if filename
72
- end
73
-
74
- ##
75
- # Returns the file extension
76
- #
77
- # === Returns
78
- #
79
- # [String] the extension
80
- #
81
- def extension
82
- split_extension(filename)[1] if filename
83
- end
84
-
85
62
  ##
86
63
  # Returns the file's size.
87
64
  #
@@ -132,7 +109,7 @@ module CarrierWave
132
109
  # [Boolean] whether the file is valid and has a non-zero size
133
110
  #
134
111
  def empty?
135
- @file.nil? || self.size.nil? || (self.size.zero? && ! self.exists?)
112
+ @file.nil? || self.size.nil? || (self.size.zero? && !self.exists?)
136
113
  end
137
114
 
138
115
  ##
@@ -151,15 +128,21 @@ module CarrierWave
151
128
  #
152
129
  # [String] contents of the file
153
130
  #
154
- def read
131
+ def read(*args)
155
132
  if @content
156
- @content
133
+ if args.empty?
134
+ @content
135
+ else
136
+ length, outbuf = args
137
+ raise ArgumentError, "outbuf argument not supported since the content is already loaded" if outbuf
138
+ @content[0, length]
139
+ end
157
140
  elsif is_path?
158
- File.open(@file, "rb") {|file| file.read}
141
+ File.open(@file, "rb") {|file| file.read(*args)}
159
142
  else
160
143
  @file.try(:rewind)
161
- @content = @file.read
162
- @file.try(:close) unless @file.try(:closed?)
144
+ @content = @file.read(*args)
145
+ @file.try(:close) unless @file.class.ancestors.include?(::StringIO) || @file.try(:closed?)
163
146
  @content
164
147
  end
165
148
  end
@@ -180,13 +163,10 @@ module CarrierWave
180
163
  mkdir!(new_path, directory_permissions)
181
164
  move!(new_path)
182
165
  chmod!(new_path, permissions)
183
- if keep_filename
184
- self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
185
- else
186
- self.file = {:tempfile => new_path, :content_type => @content_type}
187
- end
166
+ self.file = {tempfile: new_path, filename: keep_filename ? original_filename : nil, content_type: declared_content_type}
188
167
  self
189
168
  end
169
+
190
170
  ##
191
171
  # Helper to move file to new path.
192
172
  #
@@ -218,7 +198,7 @@ module CarrierWave
218
198
  mkdir!(new_path, directory_permissions)
219
199
  copy!(new_path)
220
200
  chmod!(new_path, permissions)
221
- self.class.new({:tempfile => new_path, :content_type => content_type})
201
+ self.class.new({tempfile: new_path, content_type: declared_content_type})
222
202
  end
223
203
 
224
204
  ##
@@ -260,9 +240,10 @@ module CarrierWave
260
240
  #
261
241
  def content_type
262
242
  @content_type ||=
263
- existing_content_type ||
264
- marcel_magic_content_type ||
265
- mini_mime_content_type
243
+ identified_content_type ||
244
+ declared_content_type ||
245
+ guessed_safe_content_type ||
246
+ Marcel::MimeType::BINARY
266
247
  end
267
248
 
268
249
  ##
@@ -293,11 +274,11 @@ module CarrierWave
293
274
  if file.is_a?(Hash)
294
275
  @file = file["tempfile"] || file[:tempfile]
295
276
  @original_filename = file["filename"] || file[:filename]
296
- @content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
277
+ @declared_content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
297
278
  else
298
279
  @file = file
299
280
  @original_filename = nil
300
- @content_type = nil
281
+ @declared_content_type = nil
301
282
  end
302
283
  end
303
284
 
@@ -314,57 +295,48 @@ module CarrierWave
314
295
 
315
296
  # Sanitize the filename, to prevent hacking
316
297
  def sanitize(name)
298
+ name = name.scrub
317
299
  name = name.tr("\\", "/") # work-around for IE
318
300
  name = File.basename(name)
319
- name = name.gsub(sanitize_regexp,"_")
301
+ name = name.gsub(sanitize_regexp, "_")
320
302
  name = "_#{name}" if name =~ /\A\.+\z/
321
303
  name = "unnamed" if name.size.zero?
322
- return name.mb_chars.to_s
304
+ name.mb_chars.to_s
323
305
  end
324
306
 
325
- def existing_content_type
326
- if @file.respond_to?(:content_type) && @file.content_type
327
- @file.content_type.to_s.chomp
328
- end
307
+ def declared_content_type
308
+ @declared_content_type ||
309
+ if @file.respond_to?(:content_type) && @file.content_type
310
+ @file.content_type.to_s.chomp
311
+ end
329
312
  end
330
313
 
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
314
+ # Guess content type from its file extension. Limit what to be returned to prevent spoofing.
315
+ def guessed_safe_content_type
316
+ return unless path
336
317
 
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
318
+ type = Marcel::Magic.by_path(original_filename).to_s
319
+ type if type.start_with?('text/') || type.start_with?('application/json')
320
+ end
341
321
 
342
- type
322
+ def identified_content_type
323
+ with_io do |io|
324
+ Marcel::Magic.by_magic(io).try(:type)
343
325
  end
344
326
  rescue Errno::ENOENT
345
327
  nil
346
328
  end
347
329
 
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
-
354
- def split_extension(filename)
355
- # regular expressions to try for identifying extensions
356
- extension_matchers = [
357
- /\A(.+)\.(tar\.([glx]?z|bz2))\z/, # matches "something.tar.gz"
358
- /\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
359
- ]
360
-
361
- extension_matchers.each do |regexp|
362
- if filename =~ regexp
363
- return $1, $2
330
+ def with_io(&block)
331
+ if file.is_a?(IO)
332
+ begin
333
+ yield file
334
+ ensure
335
+ file.try(:rewind)
364
336
  end
337
+ elsif path
338
+ File.open(path, &block)
365
339
  end
366
- return filename, "" # In case we weren't able to split the extension
367
340
  end
368
-
369
341
  end # SanitizedFile
370
342
  end # CarrierWave
@@ -14,7 +14,7 @@ module CarrierWave
14
14
  end
15
15
 
16
16
  def identifier
17
- uploader.filename
17
+ uploader.deduplicated_filename
18
18
  end
19
19
 
20
20
  def store!(file)
@@ -24,19 +24,19 @@ module CarrierWave
24
24
  end
25
25
 
26
26
  def cache!(new_file)
27
- raise NotImplementedError.new("Need to implement #cache! if you want to use #{self.class.name} as a cache storage.")
27
+ raise NotImplementedError, "Need to implement #cache! if you want to use #{self.class.name} as a cache storage."
28
28
  end
29
29
 
30
30
  def retrieve_from_cache!(identifier)
31
- raise NotImplementedError.new("Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage.")
31
+ raise NotImplementedError, "Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage."
32
32
  end
33
33
 
34
34
  def delete_dir!(path)
35
- raise NotImplementedError.new("Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage.")
35
+ raise NotImplementedError, "Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage."
36
36
  end
37
37
 
38
38
  def clean_cache!(seconds)
39
- raise NotImplementedError.new("Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage.")
39
+ raise NotImplementedError, "Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage."
40
40
  end
41
41
  end # Abstract
42
42
  end # Storage
@@ -17,7 +17,7 @@ module CarrierWave
17
17
  #
18
18
  # By default, store!() uses copy_to(), which operates by copying the file
19
19
  # from the cache to the store, then deleting the file from the cache.
20
- # If move_to_store() is overriden to return true, then store!() uses move_to(),
20
+ # If move_to_store() is overridden to return true, then store!() uses move_to(),
21
21
  # which simply moves the file from cache to store. Useful for large files.
22
22
  #
23
23
  # === Parameters
@@ -109,10 +109,11 @@ module CarrierWave
109
109
  end
110
110
 
111
111
  def clean_cache!(seconds)
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)
115
- time = Time.at(*time)
112
+ Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), uploader.root)).each do |dir|
113
+ # generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
114
+ matched = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first
115
+ next unless matched
116
+ time = Time.at(matched[0].to_i)
116
117
  if time < (Time.now.utc - seconds)
117
118
  FileUtils.rm_rf(dir)
118
119
  end
@@ -146,7 +146,7 @@ module CarrierWave
146
146
  :key => uploader.fog_directory,
147
147
  :public => uploader.fog_public
148
148
  ).files.all(:prefix => uploader.cache_dir).each do |file|
149
- # generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
149
+ # generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
150
150
  matched = file.key.match(/(\d+)-\d+-\d+(?:-\d+)?/)
151
151
  next unless matched
152
152
  time = Time.at(matched[1].to_i)
@@ -162,9 +162,10 @@ module CarrierWave
162
162
  end
163
163
 
164
164
  class File
165
- DEFAULT_S3_REGION = 'us-east-1'
165
+ DEFAULT_S3_REGION = 'us-east-1'.freeze
166
166
 
167
167
  include CarrierWave::Utilities::Uri
168
+ include CarrierWave::Utilities::FileName
168
169
 
169
170
  ##
170
171
  # Current local path to file
@@ -197,27 +198,27 @@ module CarrierWave
197
198
  # [NilClass] no authenticated url available
198
199
  #
199
200
  def authenticated_url(options = {})
200
- if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(@uploader.fog_credentials[:provider])
201
+ if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(fog_provider)
201
202
  # avoid a get by using local references
202
203
  local_directory = connection.directories.new(:key => @uploader.fog_directory)
203
204
  local_file = local_directory.files.new(:key => path)
204
205
  expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
205
- case @uploader.fog_credentials[:provider]
206
- when 'AWS', 'Google'
207
- # Older versions of fog-google do not support options as a parameter
208
- if url_options_supported?(local_file)
209
- local_file.url(expire_at, options)
210
- else
211
- warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
212
- local_file.url(expire_at)
213
- end
214
- when 'Rackspace', 'OpenStack'
215
- connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
216
- when 'Aliyun'
217
- expire_at = expire_at - Time.now
218
- local_file.url(expire_at)
206
+ case fog_provider
207
+ when 'AWS', 'Google'
208
+ # Older versions of fog-google do not support options as a parameter
209
+ if url_options_supported?(local_file)
210
+ local_file.url(expire_at, options)
219
211
  else
212
+ warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
220
213
  local_file.url(expire_at)
214
+ end
215
+ when 'Rackspace', 'OpenStack'
216
+ connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
217
+ when 'Aliyun'
218
+ expire_at -= Time.now
219
+ local_file.url(expire_at)
220
+ else
221
+ local_file.url(expire_at)
221
222
  end
222
223
  end
223
224
  end
@@ -258,18 +259,6 @@ module CarrierWave
258
259
  end
259
260
  end
260
261
 
261
- ##
262
- # Return extension of file
263
- #
264
- # === Returns
265
- #
266
- # [String] extension of file or nil if the file has no extension
267
- #
268
- def extension
269
- path_elements = path.split('.')
270
- path_elements.last if path_elements.size > 1
271
- end
272
-
273
262
  ##
274
263
  # deprecated: All attributes from file (includes headers)
275
264
  #
@@ -296,16 +285,16 @@ module CarrierWave
296
285
  #
297
286
  # [String] contents of file
298
287
  def read
299
- file_body = file.body
288
+ file_body = file&.body
300
289
 
301
290
  return if file_body.nil?
302
291
  return file_body unless file_body.is_a?(::File)
303
292
 
304
- # Fog::Storage::XXX::File#body could return the source file which was upoloaded to the remote server.
305
- read_source_file(file_body) if ::File.exist?(file_body.path)
293
+ # Fog::Storage::XXX::File#body could return the source file which was uploaded to the remote server.
294
+ return read_source_file if ::File.exist?(file_body.path)
306
295
 
307
296
  # If the source file doesn't exist, the remote content is read
308
- @file = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
297
+ @file = nil
309
298
  file.body
310
299
  end
311
300
 
@@ -343,7 +332,7 @@ module CarrierWave
343
332
  fog_file = new_file.to_file
344
333
  @content_type ||= new_file.content_type
345
334
  @file = directory.files.create({
346
- :body => fog_file ? fog_file : new_file.read,
335
+ :body => fog_file || new_file.read,
347
336
  :content_type => @content_type,
348
337
  :key => path,
349
338
  :public => @uploader.fog_public
@@ -364,7 +353,7 @@ module CarrierWave
364
353
  #
365
354
  def public_url
366
355
  encoded_path = encode_path(path)
367
- if host = @uploader.asset_host
356
+ if (host = @uploader.asset_host)
368
357
  if host.respond_to? :call
369
358
  "#{host.call(self)}/#{encoded_path}"
370
359
  else
@@ -381,21 +370,22 @@ module CarrierWave
381
370
  protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
382
371
 
383
372
  subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
384
- valid_subdomain = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
385
-
386
- # if directory is a valid subdomain, use that style for access
387
- if valid_subdomain
388
- s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
389
- "#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
373
+ # To use the virtual-hosted style, the bucket name needs to be representable as a subdomain
374
+ use_virtual_hosted_style = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
375
+
376
+ region = @uploader.fog_credentials[:region].to_s
377
+ regional_host = case region
378
+ when DEFAULT_S3_REGION, ''
379
+ 's3.amazonaws.com'
380
+ else
381
+ "s3.#{region}.amazonaws.com"
382
+ end
383
+
384
+ if use_virtual_hosted_style
385
+ regional_host = 's3-accelerate.amazonaws.com' if @uploader.fog_aws_accelerate
386
+ "#{protocol}://#{@uploader.fog_directory}.#{regional_host}/#{encoded_path}"
390
387
  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}"
388
+ "#{protocol}://#{regional_host}/#{@uploader.fog_directory}/#{encoded_path}"
399
389
  end
400
390
  end
401
391
  when 'Google'
@@ -409,7 +399,7 @@ module CarrierWave
409
399
  end
410
400
 
411
401
  ##
412
- # Return url to file, if avaliable
402
+ # Return url to file, if available
413
403
  #
414
404
  # === Returns
415
405
  #
@@ -435,7 +425,7 @@ module CarrierWave
435
425
  # [NilClass] no file name available
436
426
  #
437
427
  def filename(options = {})
438
- return unless file_url = url(options)
428
+ return unless (file_url = url(options))
439
429
  CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
440
430
  end
441
431
 
@@ -451,10 +441,29 @@ module CarrierWave
451
441
  # @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
452
442
  #
453
443
  def copy_to(new_path)
454
- connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, copy_options)
444
+ file.copy(@uploader.fog_directory, new_path, copy_options)
455
445
  CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
456
446
  end
457
447
 
448
+ ##
449
+ # Return the local file
450
+ #
451
+ # === Returns
452
+ #
453
+ # [File] The local file as Ruby's File class
454
+ # or
455
+ # [NilClass] When there's no file, or the file is remotely stored
456
+ #
457
+ def to_file
458
+ return nil unless file.body.is_a? ::File
459
+
460
+ if file.body.closed?
461
+ ::File.open(file.body.path) # Reopen if it's already closed
462
+ else
463
+ file.body
464
+ end
465
+ end
466
+
458
467
  private
459
468
 
460
469
  ##
@@ -476,12 +485,10 @@ module CarrierWave
476
485
  # [Fog::#{provider}::Directory] containing directory
477
486
  #
478
487
  def directory
479
- @directory ||= begin
480
- connection.directories.new(
481
- :key => @uploader.fog_directory,
482
- :public => @uploader.fog_public
483
- )
484
- end
488
+ @directory ||= connection.directories.new(
489
+ :key => @uploader.fog_directory,
490
+ :public => @uploader.fog_public
491
+ )
485
492
  end
486
493
 
487
494
  ##
@@ -498,14 +505,15 @@ module CarrierWave
498
505
  def copy_options
499
506
  options = {}
500
507
  options.merge!(acl_header) if acl_header.present?
501
- options['Content-Type'] ||= content_type if content_type
508
+ options[fog_provider == "Google" ? :content_type : 'Content-Type'] ||= content_type if content_type
502
509
  options.merge(@uploader.fog_attributes)
503
510
  end
504
511
 
505
512
  def acl_header
506
- if fog_provider == 'AWS'
513
+ case fog_provider
514
+ when 'AWS'
507
515
  { 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
508
- elsif fog_provider == "Google"
516
+ when "Google"
509
517
  @uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
510
518
  else
511
519
  {}
@@ -516,14 +524,14 @@ module CarrierWave
516
524
  @uploader.fog_credentials[:provider].to_s
517
525
  end
518
526
 
519
- def read_source_file(file_body)
520
- return unless ::File.exist?(file_body.path)
527
+ def read_source_file
528
+ source_file = to_file
529
+ return unless source_file
521
530
 
522
531
  begin
523
- file_body = ::File.open(file_body.path) if file_body.closed? # Reopen if it's already closed
524
- file_body.read
532
+ source_file.read
525
533
  ensure
526
- file_body.close
534
+ source_file.close
527
535
  end
528
536
  end
529
537
 
@@ -45,11 +45,11 @@ module CarrierWave
45
45
  def matches?(actual)
46
46
  @actual = actual
47
47
  # Satisfy expectation here. Return false or raise an error if it's not met.
48
- (File.stat(@actual.path).mode & 0777) == @expected
48
+ (File.stat(@actual.path).mode & 0o777) == @expected
49
49
  end
50
50
 
51
51
  def failure_message
52
- "expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
52
+ "expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
53
53
  end
54
54
 
55
55
  def failure_message_when_negated
@@ -76,11 +76,11 @@ module CarrierWave
76
76
  def matches?(actual)
77
77
  @actual = actual
78
78
  # Satisfy expectation here. Return false or raise an error if it's not met.
79
- (File.stat(File.dirname @actual.path).mode & 0777) == @expected
79
+ (File.stat(File.dirname(@actual.path)).mode & 0o777) == @expected
80
80
  end
81
81
 
82
82
  def failure_message
83
- "expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
83
+ "expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
84
84
  end
85
85
 
86
86
  def failure_message_when_negated
@@ -341,9 +341,11 @@ module CarrierWave
341
341
  begin
342
342
  require 'rmagick'
343
343
  rescue LoadError
344
- require 'RMagick'
345
- rescue LoadError
346
- puts "WARNING: Failed to require rmagick, image processing may fail!"
344
+ begin
345
+ require 'RMagick'
346
+ rescue LoadError
347
+ puts "WARNING: Failed to require rmagick, image processing may fail!"
348
+ end
347
349
  end
348
350
  end
349
351
  MagickWrapper.new(filename)
@@ -353,6 +355,7 @@ module CarrierWave
353
355
 
354
356
  class MagickWrapper # :nodoc:
355
357
  attr_reader :image
358
+
356
359
  def width
357
360
  image.columns
358
361
  end
@@ -372,6 +375,7 @@ module CarrierWave
372
375
 
373
376
  class MiniMagickWrapper # :nodoc:
374
377
  attr_reader :image
378
+
375
379
  def width
376
380
  image[:width]
377
381
  end