carrierwave 2.2.0 → 3.0.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +151 -63
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -2
  4. data/lib/carrierwave/downloader/base.rb +27 -13
  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 +31 -50
  8. data/lib/carrierwave/mounter.rb +115 -50
  9. data/lib/carrierwave/orm/activerecord.rb +12 -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 +50 -79
  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 +78 -69
  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 +42 -7
  28. data/lib/carrierwave/uploader/proxy.rb +16 -3
  29. data/lib/carrierwave/uploader/store.rb +43 -6
  30. data/lib/carrierwave/uploader/url.rb +1 -1
  31. data/lib/carrierwave/uploader/versions.rb +137 -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/uploader_generator.rb +3 -3
  40. metadata +54 -46
  41. /data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +0 -0
@@ -1,8 +1,6 @@
1
1
  require 'pathname'
2
2
  require 'active_support/core_ext/string/multibyte'
3
- require 'mini_mime'
4
- require 'mimemagic'
5
- require 'mimemagic/overlay'
3
+ require 'marcel'
6
4
 
7
5
  module CarrierWave
8
6
 
@@ -15,6 +13,7 @@ module CarrierWave
15
13
  # It's probably needlessly comprehensive and complex. Help is appreciated.
16
14
  #
17
15
  class SanitizedFile
16
+ include CarrierWave::Utilities::FileName
18
17
 
19
18
  attr_reader :file
20
19
 
@@ -28,7 +27,7 @@ module CarrierWave
28
27
 
29
28
  def initialize(file)
30
29
  self.file = file
31
- @content = nil
30
+ @content = @content_type = nil
32
31
  end
33
32
 
34
33
  ##
@@ -40,7 +39,7 @@ module CarrierWave
40
39
  #
41
40
  def original_filename
42
41
  return @original_filename if @original_filename
43
- if @file and @file.respond_to?(:original_filename)
42
+ if @file && @file.respond_to?(:original_filename)
44
43
  @file.original_filename
45
44
  elsif path
46
45
  File.basename(path)
@@ -60,29 +59,6 @@ module CarrierWave
60
59
 
61
60
  alias_method :identifier, :filename
62
61
 
63
- ##
64
- # Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
65
- # this would return 'test'
66
- #
67
- # === Returns
68
- #
69
- # [String] the first part of the filename
70
- #
71
- def basename
72
- split_extension(filename)[0] if filename
73
- end
74
-
75
- ##
76
- # Returns the file extension
77
- #
78
- # === Returns
79
- #
80
- # [String] the extension
81
- #
82
- def extension
83
- split_extension(filename)[1] if filename
84
- end
85
-
86
62
  ##
87
63
  # Returns the file's size.
88
64
  #
@@ -133,7 +109,7 @@ module CarrierWave
133
109
  # [Boolean] whether the file is valid and has a non-zero size
134
110
  #
135
111
  def empty?
136
- @file.nil? || self.size.nil? || (self.size.zero? && ! self.exists?)
112
+ @file.nil? || self.size.nil? || (self.size.zero? && !self.exists?)
137
113
  end
138
114
 
139
115
  ##
@@ -152,15 +128,21 @@ module CarrierWave
152
128
  #
153
129
  # [String] contents of the file
154
130
  #
155
- def read
131
+ def read(*args)
156
132
  if @content
157
- @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
158
140
  elsif is_path?
159
- File.open(@file, "rb") {|file| file.read}
141
+ File.open(@file, "rb") {|file| file.read(*args)}
160
142
  else
161
143
  @file.try(:rewind)
162
- @content = @file.read
163
- @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?)
164
146
  @content
165
147
  end
166
148
  end
@@ -181,13 +163,10 @@ module CarrierWave
181
163
  mkdir!(new_path, directory_permissions)
182
164
  move!(new_path)
183
165
  chmod!(new_path, permissions)
184
- if keep_filename
185
- self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
186
- else
187
- self.file = {:tempfile => new_path, :content_type => @content_type}
188
- end
166
+ self.file = {tempfile: new_path, filename: keep_filename ? original_filename : nil, content_type: declared_content_type}
189
167
  self
190
168
  end
169
+
191
170
  ##
192
171
  # Helper to move file to new path.
193
172
  #
@@ -219,7 +198,7 @@ module CarrierWave
219
198
  mkdir!(new_path, directory_permissions)
220
199
  copy!(new_path)
221
200
  chmod!(new_path, permissions)
222
- self.class.new({:tempfile => new_path, :content_type => content_type})
201
+ self.class.new({tempfile: new_path, content_type: declared_content_type})
223
202
  end
224
203
 
225
204
  ##
@@ -261,9 +240,10 @@ module CarrierWave
261
240
  #
262
241
  def content_type
263
242
  @content_type ||=
264
- existing_content_type ||
265
- mime_magic_content_type ||
266
- mini_mime_content_type
243
+ identified_content_type ||
244
+ declared_content_type ||
245
+ guessed_safe_content_type ||
246
+ Marcel::MimeType::BINARY
267
247
  end
268
248
 
269
249
  ##
@@ -294,11 +274,11 @@ module CarrierWave
294
274
  if file.is_a?(Hash)
295
275
  @file = file["tempfile"] || file[:tempfile]
296
276
  @original_filename = file["filename"] || file[:filename]
297
- @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]
298
278
  else
299
279
  @file = file
300
280
  @original_filename = nil
301
- @content_type = nil
281
+ @declared_content_type = nil
302
282
  end
303
283
  end
304
284
 
@@ -315,57 +295,48 @@ module CarrierWave
315
295
 
316
296
  # Sanitize the filename, to prevent hacking
317
297
  def sanitize(name)
298
+ name = name.scrub
318
299
  name = name.tr("\\", "/") # work-around for IE
319
300
  name = File.basename(name)
320
- name = name.gsub(sanitize_regexp,"_")
301
+ name = name.gsub(sanitize_regexp, "_")
321
302
  name = "_#{name}" if name =~ /\A\.+\z/
322
303
  name = "unnamed" if name.size.zero?
323
- return name.mb_chars.to_s
304
+ name.mb_chars.to_s
324
305
  end
325
306
 
326
- def existing_content_type
327
- if @file.respond_to?(:content_type) && @file.content_type
328
- @file.content_type.to_s.chomp
329
- 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
330
312
  end
331
313
 
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
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
337
317
 
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
318
+ type = Marcel::Magic.by_path(original_filename).to_s
319
+ type if type.start_with?('text/') || type.start_with?('application/json')
320
+ end
342
321
 
343
- type
322
+ def identified_content_type
323
+ with_io do |io|
324
+ Marcel::Magic.by_magic(io).try(:type)
344
325
  end
345
326
  rescue Errno::ENOENT
346
327
  nil
347
328
  end
348
329
 
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
-
355
- def split_extension(filename)
356
- # regular expressions to try for identifying extensions
357
- extension_matchers = [
358
- /\A(.+)\.(tar\.([glx]?z|bz2))\z/, # matches "something.tar.gz"
359
- /\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
360
- ]
361
-
362
- extension_matchers.each do |regexp|
363
- if filename =~ regexp
364
- 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)
365
336
  end
337
+ elsif path
338
+ File.open(path, &block)
366
339
  end
367
- return filename, "" # In case we weren't able to split the extension
368
340
  end
369
-
370
341
  end # SanitizedFile
371
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
@@ -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:
@@ -146,9 +146,10 @@ 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
150
- time = file.key.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map { |t| t.to_i }
151
- time = Time.at(*time)
149
+ # generate_cache_id returns key formatted 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)
152
153
  file.destroy if time < (Time.now.utc - seconds)
153
154
  end
154
155
  end
@@ -161,9 +162,10 @@ module CarrierWave
161
162
  end
162
163
 
163
164
  class File
164
- DEFAULT_S3_REGION = 'us-east-1'
165
+ DEFAULT_S3_REGION = 'us-east-1'.freeze
165
166
 
166
167
  include CarrierWave::Utilities::Uri
168
+ include CarrierWave::Utilities::FileName
167
169
 
168
170
  ##
169
171
  # Current local path to file
@@ -196,27 +198,27 @@ module CarrierWave
196
198
  # [NilClass] no authenticated url available
197
199
  #
198
200
  def authenticated_url(options = {})
199
- 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)
200
202
  # avoid a get by using local references
201
203
  local_directory = connection.directories.new(:key => @uploader.fog_directory)
202
204
  local_file = local_directory.files.new(:key => path)
203
205
  expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
204
- case @uploader.fog_credentials[:provider]
205
- when 'AWS', 'Google'
206
- # Older versions of fog-google do not support options as a parameter
207
- if url_options_supported?(local_file)
208
- local_file.url(expire_at, options)
209
- else
210
- warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
211
- local_file.url(expire_at)
212
- end
213
- when 'Rackspace', 'OpenStack'
214
- connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
215
- when 'Aliyun'
216
- expire_at = expire_at - Time.now
217
- 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)
218
211
  else
212
+ warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
219
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)
220
222
  end
221
223
  end
222
224
  end
@@ -257,18 +259,6 @@ module CarrierWave
257
259
  end
258
260
  end
259
261
 
260
- ##
261
- # Return extension of file
262
- #
263
- # === Returns
264
- #
265
- # [String] extension of file or nil if the file has no extension
266
- #
267
- def extension
268
- path_elements = path.split('.')
269
- path_elements.last if path_elements.size > 1
270
- end
271
-
272
262
  ##
273
263
  # deprecated: All attributes from file (includes headers)
274
264
  #
@@ -295,16 +285,16 @@ module CarrierWave
295
285
  #
296
286
  # [String] contents of file
297
287
  def read
298
- file_body = file.body
288
+ file_body = file&.body
299
289
 
300
290
  return if file_body.nil?
301
291
  return file_body unless file_body.is_a?(::File)
302
292
 
303
- # Fog::Storage::XXX::File#body could return the source file which was upoloaded to the remote server.
304
- 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)
305
295
 
306
296
  # If the source file doesn't exist, the remote content is read
307
- @file = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
297
+ @file = nil
308
298
  file.body
309
299
  end
310
300
 
@@ -342,7 +332,7 @@ module CarrierWave
342
332
  fog_file = new_file.to_file
343
333
  @content_type ||= new_file.content_type
344
334
  @file = directory.files.create({
345
- :body => fog_file ? fog_file : new_file.read,
335
+ :body => fog_file || new_file.read,
346
336
  :content_type => @content_type,
347
337
  :key => path,
348
338
  :public => @uploader.fog_public
@@ -363,7 +353,7 @@ module CarrierWave
363
353
  #
364
354
  def public_url
365
355
  encoded_path = encode_path(path)
366
- if host = @uploader.asset_host
356
+ if (host = @uploader.asset_host)
367
357
  if host.respond_to? :call
368
358
  "#{host.call(self)}/#{encoded_path}"
369
359
  else
@@ -380,21 +370,22 @@ module CarrierWave
380
370
  protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
381
371
 
382
372
  subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
383
- valid_subdomain = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
384
-
385
- # if directory is a valid subdomain, use that style for access
386
- if valid_subdomain
387
- s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
388
- "#{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}"
389
387
  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}"
388
+ "#{protocol}://#{regional_host}/#{@uploader.fog_directory}/#{encoded_path}"
398
389
  end
399
390
  end
400
391
  when 'Google'
@@ -408,7 +399,7 @@ module CarrierWave
408
399
  end
409
400
 
410
401
  ##
411
- # Return url to file, if avaliable
402
+ # Return url to file, if available
412
403
  #
413
404
  # === Returns
414
405
  #
@@ -434,7 +425,7 @@ module CarrierWave
434
425
  # [NilClass] no file name available
435
426
  #
436
427
  def filename(options = {})
437
- return unless file_url = url(options)
428
+ return unless (file_url = url(options))
438
429
  CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
439
430
  end
440
431
 
@@ -450,10 +441,29 @@ module CarrierWave
450
441
  # @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
451
442
  #
452
443
  def copy_to(new_path)
453
- 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)
454
445
  CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
455
446
  end
456
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
+
457
467
  private
458
468
 
459
469
  ##
@@ -475,12 +485,10 @@ module CarrierWave
475
485
  # [Fog::#{provider}::Directory] containing directory
476
486
  #
477
487
  def directory
478
- @directory ||= begin
479
- connection.directories.new(
480
- :key => @uploader.fog_directory,
481
- :public => @uploader.fog_public
482
- )
483
- end
488
+ @directory ||= connection.directories.new(
489
+ :key => @uploader.fog_directory,
490
+ :public => @uploader.fog_public
491
+ )
484
492
  end
485
493
 
486
494
  ##
@@ -497,14 +505,15 @@ module CarrierWave
497
505
  def copy_options
498
506
  options = {}
499
507
  options.merge!(acl_header) if acl_header.present?
500
- options['Content-Type'] ||= content_type if content_type
508
+ options[fog_provider == "Google" ? :content_type : 'Content-Type'] ||= content_type if content_type
501
509
  options.merge(@uploader.fog_attributes)
502
510
  end
503
511
 
504
512
  def acl_header
505
- if fog_provider == 'AWS'
513
+ case fog_provider
514
+ when 'AWS'
506
515
  { 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
507
- elsif fog_provider == "Google"
516
+ when "Google"
508
517
  @uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
509
518
  else
510
519
  {}
@@ -515,14 +524,14 @@ module CarrierWave
515
524
  @uploader.fog_credentials[:provider].to_s
516
525
  end
517
526
 
518
- def read_source_file(file_body)
519
- return unless ::File.exist?(file_body.path)
527
+ def read_source_file
528
+ source_file = to_file
529
+ return unless source_file
520
530
 
521
531
  begin
522
- file_body = ::File.open(file_body.path) if file_body.closed? # Reopen if it's already closed
523
- file_body.read
532
+ source_file.read
524
533
  ensure
525
- file_body.close
534
+ source_file.close
526
535
  end
527
536
  end
528
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