carrierwave 2.2.6 → 3.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +137 -67
- data/lib/carrierwave/compatibility/paperclip.rb +4 -2
- data/lib/carrierwave/downloader/base.rb +19 -11
- data/lib/carrierwave/downloader/remote_file.rb +13 -10
- data/lib/carrierwave/locale/en.yml +5 -3
- data/lib/carrierwave/mount.rb +36 -50
- data/lib/carrierwave/mounter.rb +117 -50
- data/lib/carrierwave/orm/activerecord.rb +21 -62
- data/lib/carrierwave/processing/mini_magick.rb +15 -13
- data/lib/carrierwave/processing/rmagick.rb +11 -15
- data/lib/carrierwave/processing/vips.rb +12 -12
- data/lib/carrierwave/sanitized_file.rb +49 -77
- data/lib/carrierwave/storage/abstract.rb +5 -5
- data/lib/carrierwave/storage/file.rb +6 -5
- data/lib/carrierwave/storage/fog.rb +74 -66
- data/lib/carrierwave/test/matchers.rb +11 -7
- data/lib/carrierwave/uploader/cache.rb +18 -10
- data/lib/carrierwave/uploader/callbacks.rb +1 -1
- data/lib/carrierwave/uploader/configuration.rb +10 -4
- data/lib/carrierwave/uploader/{content_type_whitelist.rb → content_type_allowlist.rb} +17 -15
- data/lib/carrierwave/uploader/{content_type_blacklist.rb → content_type_denylist.rb} +19 -14
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/{extension_whitelist.rb → extension_allowlist.rb} +17 -15
- data/lib/carrierwave/uploader/{extension_blacklist.rb → extension_denylist.rb} +18 -13
- data/lib/carrierwave/uploader/file_size.rb +2 -2
- data/lib/carrierwave/uploader/processing.rb +31 -6
- data/lib/carrierwave/uploader/proxy.rb +16 -3
- data/lib/carrierwave/uploader/store.rb +44 -6
- data/lib/carrierwave/uploader/url.rb +1 -1
- data/lib/carrierwave/uploader/versions.rb +154 -136
- data/lib/carrierwave/uploader.rb +10 -8
- data/lib/carrierwave/utilities/file_name.rb +47 -0
- data/lib/carrierwave/utilities/uri.rb +14 -11
- data/lib/carrierwave/utilities.rb +1 -0
- data/lib/carrierwave/validations/active_model.rb +4 -6
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +9 -17
- data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +1 -1
- data/lib/generators/uploader_generator.rb +3 -3
- 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
|
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? && !
|
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
|
-
|
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
|
-
|
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({:
|
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
|
-
|
264
|
-
|
265
|
-
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
304
|
+
name.mb_chars.to_s
|
323
305
|
end
|
324
306
|
|
325
|
-
def
|
326
|
-
|
327
|
-
|
328
|
-
|
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
|
-
|
332
|
-
|
333
|
-
|
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
|
-
|
338
|
-
|
339
|
-
|
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
|
-
|
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
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
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.
|
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
|
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
|
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
|
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
|
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
|
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, '*'),
|
113
|
-
# generate_cache_id returns key
|
114
|
-
|
115
|
-
|
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
|
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?(
|
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
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
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
|
305
|
-
read_source_file
|
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
|
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
|
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
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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
|
-
|
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
|
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
|
-
|
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 ||=
|
480
|
-
|
481
|
-
|
482
|
-
|
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
|
-
|
513
|
+
case fog_provider
|
514
|
+
when 'AWS'
|
507
515
|
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
508
|
-
|
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
|
520
|
-
|
527
|
+
def read_source_file
|
528
|
+
source_file = to_file
|
529
|
+
return unless source_file
|
521
530
|
|
522
531
|
begin
|
523
|
-
|
524
|
-
file_body.read
|
532
|
+
source_file.read
|
525
533
|
ensure
|
526
|
-
|
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 &
|
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 &
|
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
|
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 &
|
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
|
-
|
345
|
-
|
346
|
-
|
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
|