carrierwave 2.2.6 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
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 +13 -10
  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 +21 -62
  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 +154 -136
  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 +32 -44
@@ -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
- Marcel::MimeType.for(declared_type: @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
+ Marcel::MimeType.for(declared_type: @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