carrierwave 1.2.1 → 2.1.1

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.

@@ -1,12 +1,7 @@
1
1
  require 'pathname'
2
2
  require 'active_support/core_ext/string/multibyte'
3
-
4
- begin
5
- # Use mime/types/columnar if available, for reduced memory usage
6
- require 'mime/types/columnar'
7
- rescue LoadError
8
- require 'mime/types'
9
- end
3
+ require 'mini_mime'
4
+ require 'mimemagic'
10
5
 
11
6
  module CarrierWave
12
7
 
@@ -20,7 +15,7 @@ module CarrierWave
20
15
  #
21
16
  class SanitizedFile
22
17
 
23
- attr_accessor :file
18
+ attr_reader :file
24
19
 
25
20
  class << self
26
21
  attr_writer :sanitize_regexp
@@ -32,6 +27,7 @@ module CarrierWave
32
27
 
33
28
  def initialize(file)
34
29
  self.file = file
30
+ @content = nil
35
31
  end
36
32
 
37
33
  ##
@@ -113,12 +109,11 @@ module CarrierWave
113
109
  # [String, nil] the path where the file is located.
114
110
  #
115
111
  def path
116
- unless @file.blank?
117
- if is_path?
118
- File.expand_path(@file)
119
- elsif @file.respond_to?(:path) and not @file.path.blank?
120
- File.expand_path(@file.path)
121
- end
112
+ return if @file.blank?
113
+ if is_path?
114
+ File.expand_path(@file)
115
+ elsif @file.respond_to?(:path) && !@file.path.blank?
116
+ File.expand_path(@file.path)
122
117
  end
123
118
  end
124
119
 
@@ -264,12 +259,10 @@ module CarrierWave
264
259
  # [String] the content type of the file
265
260
  #
266
261
  def content_type
267
- return @content_type if @content_type
268
- if @file.respond_to?(:content_type) and @file.content_type
269
- @content_type = @file.content_type.to_s.chomp
270
- elsif path
271
- @content_type = ::MIME::Types.type_for(path).first.to_s
272
- end
262
+ @content_type ||=
263
+ existing_content_type ||
264
+ mime_magic_content_type ||
265
+ mini_mime_content_type
273
266
  end
274
267
 
275
268
  ##
@@ -312,7 +305,7 @@ module CarrierWave
312
305
  def mkdir!(path, directory_permissions)
313
306
  options = {}
314
307
  options[:mode] = directory_permissions if directory_permissions
315
- FileUtils.mkdir_p(File.dirname(path), options) unless File.exist?(File.dirname(path))
308
+ FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
316
309
  end
317
310
 
318
311
  def chmod!(path, permissions)
@@ -325,10 +318,32 @@ module CarrierWave
325
318
  name = File.basename(name)
326
319
  name = name.gsub(sanitize_regexp,"_")
327
320
  name = "_#{name}" if name =~ /\A\.+\z/
328
- name = "unnamed" if name.size == 0
321
+ name = "unnamed" if name.size.zero?
329
322
  return name.mb_chars.to_s
330
323
  end
331
324
 
325
+ def existing_content_type
326
+ if @file.respond_to?(:content_type) && @file.content_type
327
+ @file.content_type.to_s.chomp
328
+ end
329
+ end
330
+
331
+ def mime_magic_content_type
332
+ if path
333
+ File.open(path) do |file|
334
+ MimeMagic.by_magic(file).try(:type) || 'invalid/invalid'
335
+ end
336
+ end
337
+ rescue Errno::ENOENT
338
+ nil
339
+ end
340
+
341
+ def mini_mime_content_type
342
+ return unless path
343
+ mime_type = ::MiniMime.lookup_by_filename(path)
344
+ @content_type = (mime_type && mime_type.content_type).to_s
345
+ end
346
+
332
347
  def split_extension(filename)
333
348
  # regular expressions to try for identifying extensions
334
349
  extension_matchers = [
@@ -7,6 +7,10 @@ module CarrierWave
7
7
  # pretty much it.
8
8
  #
9
9
  class File < Abstract
10
+ def initialize(*)
11
+ super
12
+ @cache_called = nil
13
+ end
10
14
 
11
15
  ##
12
16
  # Move the file to the uploader's store path.
@@ -62,7 +66,7 @@ module CarrierWave
62
66
  #
63
67
  def cache!(new_file)
64
68
  new_file.move_to(::File.expand_path(uploader.cache_path, uploader.root), uploader.permissions, uploader.directory_permissions, true)
65
- rescue Errno::EMLINK => e
69
+ rescue Errno::EMLINK, Errno::ENOSPC => e
66
70
  raise(e) if @cache_called
67
71
  @cache_called = true
68
72
 
@@ -106,8 +110,8 @@ module CarrierWave
106
110
 
107
111
  def clean_cache!(seconds)
108
112
  Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), CarrierWave.root)).each do |dir|
109
- # generate_cache_id returns key formated TIMEINT-PID-COUNTER-RND
110
- time = dir.scan(/(\d+)-\d+-\d+-\d+/).first.map(&:to_i)
113
+ # generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
114
+ time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
111
115
  time = Time.at(*time)
112
116
  if time < (Time.now.utc - seconds)
113
117
  FileUtils.rm_rf(dir)
@@ -60,6 +60,14 @@ module CarrierWave
60
60
  def connection_cache
61
61
  @connection_cache ||= {}
62
62
  end
63
+
64
+ def eager_load
65
+ # see #1198. This will hopefully no longer be necessary in future release of fog
66
+ fog_credentials = CarrierWave::Uploader::Base.fog_credentials
67
+ if fog_credentials.present?
68
+ CarrierWave::Storage::Fog.connection_cache[fog_credentials] ||= ::Fog::Storage.new(fog_credentials)
69
+ end
70
+ end
63
71
  end
64
72
 
65
73
  ##
@@ -138,8 +146,8 @@ module CarrierWave
138
146
  :key => uploader.fog_directory,
139
147
  :public => uploader.fog_public
140
148
  ).files.all(:prefix => uploader.cache_dir).each do |file|
141
- # generate_cache_id returns key formated TIMEINT-PID-COUNTER-RND
142
- time = file.key.scan(/(\d+)-\d+-\d+-\d+/).first.map { |t| t.to_i }
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 }
143
151
  time = Time.at(*time)
144
152
  file.destroy if time < (Time.now.utc - seconds)
145
153
  end
@@ -153,6 +161,8 @@ module CarrierWave
153
161
  end
154
162
 
155
163
  class File
164
+ DEFAULT_S3_REGION = 'us-east-1'
165
+
156
166
  include CarrierWave::Utilities::Uri
157
167
 
158
168
  ##
@@ -177,7 +187,7 @@ module CarrierWave
177
187
 
178
188
  ##
179
189
  # Return a temporary authenticated url to a private file, if available
180
- # Only supported for AWS, Rackspace and Google providers
190
+ # Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
181
191
  #
182
192
  # === Returns
183
193
  #
@@ -186,18 +196,25 @@ module CarrierWave
186
196
  # [NilClass] no authenticated url available
187
197
  #
188
198
  def authenticated_url(options = {})
189
- if ['AWS', 'Google', 'Rackspace', 'OpenStack'].include?(@uploader.fog_credentials[:provider])
199
+ if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(@uploader.fog_credentials[:provider])
190
200
  # avoid a get by using local references
191
201
  local_directory = connection.directories.new(:key => @uploader.fog_directory)
192
202
  local_file = local_directory.files.new(:key => path)
193
- expire_at = ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
203
+ expire_at = options[:expire_at] || ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
194
204
  case @uploader.fog_credentials[:provider]
195
- when 'AWS'
196
- local_file.url(expire_at, options)
197
- when 'Rackspace'
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'
198
214
  connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
199
- when 'OpenStack'
200
- connection.get_object_https_url(@uploader.fog_directory, path, expire_at)
215
+ when 'Aliyun'
216
+ expire_at = expire_at - Time.now
217
+ local_file.url(expire_at)
201
218
  else
202
219
  local_file.url(expire_at)
203
220
  end
@@ -212,7 +229,7 @@ module CarrierWave
212
229
  # [String] value of content-type
213
230
  #
214
231
  def content_type
215
- @content_type || !file.nil? && file.content_type
232
+ @content_type || file.try(:content_type)
216
233
  end
217
234
 
218
235
  ##
@@ -235,7 +252,9 @@ module CarrierWave
235
252
  #
236
253
  def delete
237
254
  # avoid a get by just using local reference
238
- directory.files.new(:key => path).destroy
255
+ directory.files.new(:key => path).destroy.tap do |result|
256
+ @file = nil if result
257
+ end
239
258
  end
240
259
 
241
260
  ##
@@ -266,7 +285,7 @@ module CarrierWave
266
285
  end
267
286
 
268
287
  def initialize(uploader, base, path)
269
- @uploader, @base, @path = uploader, base, path
288
+ @uploader, @base, @path, @content_type = uploader, base, path, nil
270
289
  end
271
290
 
272
291
  ##
@@ -276,6 +295,16 @@ module CarrierWave
276
295
  #
277
296
  # [String] contents of file
278
297
  def read
298
+ file_body = file.body
299
+
300
+ return if file_body.nil?
301
+ return file_body unless file_body.is_a?(::File)
302
+
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)
305
+
306
+ # If the source file doesn't exist, the remote content is read
307
+ @file = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
279
308
  file.body
280
309
  end
281
310
 
@@ -313,7 +342,7 @@ module CarrierWave
313
342
  fog_file = new_file.to_file
314
343
  @content_type ||= new_file.content_type
315
344
  @file = directory.files.create({
316
- :body => (fog_file ? fog_file : new_file).read,
345
+ :body => fog_file ? fog_file : new_file.read,
317
346
  :content_type => @content_type,
318
347
  :key => path,
319
348
  :public => @uploader.fog_public
@@ -342,20 +371,30 @@ module CarrierWave
342
371
  end
343
372
  else
344
373
  # AWS/Google optimized for speed over correctness
345
- case @uploader.fog_credentials[:provider].to_s
374
+ case fog_provider
346
375
  when 'AWS'
347
376
  # check if some endpoint is set in fog_credentials
348
377
  if @uploader.fog_credentials.has_key?(:endpoint)
349
378
  "#{@uploader.fog_credentials[:endpoint]}/#{@uploader.fog_directory}/#{encoded_path}"
350
379
  else
351
380
  protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
381
+
382
+ 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
+
352
385
  # if directory is a valid subdomain, use that style for access
353
- if @uploader.fog_directory.to_s =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
386
+ if valid_subdomain
354
387
  s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
355
388
  "#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
356
- else
357
- # directory is not a valid subdomain, so use path style for access
358
- "#{protocol}://s3.amazonaws.com/#{@uploader.fog_directory}/#{encoded_path}"
389
+ 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}"
359
398
  end
360
399
  end
361
400
  when 'Google'
@@ -456,7 +495,31 @@ module CarrierWave
456
495
  end
457
496
 
458
497
  def acl_header
459
- {'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private'}
498
+ if fog_provider == 'AWS'
499
+ { 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
500
+ else
501
+ {}
502
+ end
503
+ end
504
+
505
+ def fog_provider
506
+ @uploader.fog_credentials[:provider].to_s
507
+ end
508
+
509
+ def read_source_file(file_body)
510
+ return unless ::File.exist?(file_body.path)
511
+
512
+ begin
513
+ file_body = ::File.open(file_body.path) if file_body.closed? # Reopen if it's already closed
514
+ file_body.read
515
+ ensure
516
+ file_body.close
517
+ end
518
+ end
519
+
520
+ def url_options_supported?(local_file)
521
+ parameters = local_file.method(:url).parameters
522
+ parameters.count == 2 && parameters[1].include?(:options)
460
523
  end
461
524
  end
462
525
 
@@ -1,2 +1,3 @@
1
1
  require "carrierwave/storage/abstract"
2
2
  require "carrierwave/storage/file"
3
+ require "carrierwave/storage/fog"
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module CarrierWave
2
4
 
3
5
  class FormNotMultipart < UploadError
@@ -23,9 +25,9 @@ module CarrierWave
23
25
  #
24
26
  def self.generate_cache_id
25
27
  [Time.now.utc.to_i,
26
- Process.pid,
27
- '%04d' % (CarrierWave::CacheCounter.increment % 1000),
28
- '%04d' % rand(9999)
28
+ SecureRandom.random_number(1_000_000_000_000_000),
29
+ '%04d' % (CarrierWave::CacheCounter.increment % 10_000),
30
+ '%04d' % SecureRandom.random_number(10_000)
29
31
  ].map(&:to_s).join('-')
30
32
  end
31
33
 
@@ -36,6 +38,16 @@ module CarrierWave
36
38
  include CarrierWave::Uploader::Callbacks
37
39
  include CarrierWave::Uploader::Configuration
38
40
 
41
+ included do
42
+ prepend Module.new {
43
+ def initialize(*)
44
+ super
45
+ @staged = false
46
+ end
47
+ }
48
+ attr_accessor :staged
49
+ end
50
+
39
51
  module ClassMethods
40
52
 
41
53
  ##
@@ -52,7 +64,7 @@ module CarrierWave
52
64
  # It's recommended that you keep cache files in one place only.
53
65
  #
54
66
  def clean_cached_files!(seconds=60*60*24)
55
- cache_storage.new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
67
+ (cache_storage || storage).new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
56
68
  end
57
69
  end
58
70
 
@@ -78,14 +90,8 @@ module CarrierWave
78
90
  end
79
91
 
80
92
  def sanitized_file
81
- _content = file.read
82
- if _content.is_a?(File) # could be if storage is Fog
83
- sanitized = CarrierWave::Storage::Fog.new(self).retrieve!(File.basename(_content.path))
84
- else
85
- sanitized = SanitizedFile.new :tempfile => StringIO.new(_content),
86
- :filename => File.basename(path), :content_type => file.content_type
87
- end
88
- sanitized
93
+ ActiveSupport::Deprecation.warn('#sanitized_file is deprecated, use #file instead.')
94
+ file
89
95
  end
90
96
 
91
97
  ##
@@ -96,7 +102,7 @@ module CarrierWave
96
102
  # [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
97
103
  #
98
104
  def cache_name
99
- File.join(cache_id, full_original_filename) if cache_id and original_filename
105
+ File.join(cache_id, full_original_filename) if cache_id && original_filename
100
106
  end
101
107
 
102
108
  ##
@@ -115,7 +121,7 @@ module CarrierWave
115
121
  #
116
122
  # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
117
123
  #
118
- def cache!(new_file = sanitized_file)
124
+ def cache!(new_file = file)
119
125
  new_file = CarrierWave::SanitizedFile.new(new_file)
120
126
  return if new_file.empty?
121
127
 
@@ -123,6 +129,7 @@ module CarrierWave
123
129
 
124
130
  self.cache_id = CarrierWave.generate_cache_id unless cache_id
125
131
 
132
+ @staged = true
126
133
  @filename = new_file.filename
127
134
  self.original_filename = new_file.filename
128
135
 
@@ -156,6 +163,7 @@ module CarrierWave
156
163
  def retrieve_from_cache!(cache_name)
157
164
  with_callbacks(:retrieve_from_cache, cache_name) do
158
165
  self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
166
+ @staged = true
159
167
  @filename = original_filename
160
168
  @file = cache_storage.retrieve_from_cache!(full_filename(original_filename))
161
169
  end
@@ -200,7 +208,7 @@ module CarrierWave
200
208
  end
201
209
 
202
210
  def cache_storage
203
- @cache_storage ||= self.class.cache_storage.new(self)
211
+ @cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
204
212
  end
205
213
  end # Cache
206
214
  end # Uploader
@@ -1,3 +1,5 @@
1
+ require 'carrierwave/downloader/base'
2
+
1
3
  module CarrierWave
2
4
 
3
5
  module Uploader
@@ -21,9 +23,10 @@ module CarrierWave
21
23
  add_config :move_to_cache
22
24
  add_config :move_to_store
23
25
  add_config :remove_previously_stored_files_after_update
26
+ add_config :downloader
24
27
 
25
28
  # fog
26
- add_config :fog_provider
29
+ add_deprecated_config :fog_provider
27
30
  add_config :fog_attributes
28
31
  add_config :fog_credentials
29
32
  add_config :fog_directory
@@ -107,8 +110,8 @@ module CarrierWave
107
110
  # cache_storage CarrierWave::Storage::File
108
111
  # cache_storage MyCustomStorageEngine
109
112
  #
110
- def cache_storage(storage = nil)
111
- if storage
113
+ def cache_storage(storage = false)
114
+ unless storage == false
112
115
  self._cache_storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
113
116
  end
114
117
  _cache_storage
@@ -117,16 +120,10 @@ module CarrierWave
117
120
 
118
121
  def add_config(name)
119
122
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
120
- def self.eager_load_fog(fog_credentials)
121
- # see #1198. This will hopefully no longer be necessary after fog 2.0
122
- require self.fog_provider
123
- require 'carrierwave/storage/fog'
124
- Fog::Storage.new(fog_credentials) if fog_credentials.present?
125
- end
123
+ @#{name} = nil
126
124
 
127
125
  def self.#{name}(value=nil)
128
126
  @#{name} = value if value
129
- eager_load_fog(value) if value && '#{name}' == 'fog_credentials'
130
127
  return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
131
128
  name = superclass.#{name}
132
129
  return nil if name.nil? && !instance_variable_defined?(:@#{name})
@@ -134,12 +131,10 @@ module CarrierWave
134
131
  end
135
132
 
136
133
  def self.#{name}=(value)
137
- eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
138
134
  @#{name} = value
139
135
  end
140
136
 
141
137
  def #{name}=(value)
142
- self.class.eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
143
138
  @#{name} = value
144
139
  end
145
140
 
@@ -155,6 +150,26 @@ module CarrierWave
155
150
  RUBY
156
151
  end
157
152
 
153
+ def add_deprecated_config(name)
154
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
155
+ def self.#{name}(value=nil)
156
+ ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
157
+ end
158
+
159
+ def self.#{name}=(value)
160
+ ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
161
+ end
162
+
163
+ def #{name}=(value)
164
+ ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
165
+ end
166
+
167
+ def #{name}
168
+ ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
169
+ end
170
+ RUBY
171
+ end
172
+
158
173
  def configure
159
174
  yield self
160
175
  end
@@ -171,8 +186,7 @@ module CarrierWave
171
186
  :fog => "CarrierWave::Storage::Fog"
172
187
  }
173
188
  config.storage = :file
174
- config.cache_storage = :file
175
- config.fog_provider = 'fog'
189
+ config.cache_storage = nil
176
190
  config.fog_attributes = {}
177
191
  config.fog_credentials = {}
178
192
  config.fog_public = true
@@ -185,6 +199,7 @@ module CarrierWave
185
199
  config.move_to_cache = false
186
200
  config.move_to_store = false
187
201
  config.remove_previously_stored_files_after_update = true
202
+ config.downloader = CarrierWave::Downloader::Base
188
203
  config.ignore_integrity_errors = true
189
204
  config.ignore_processing_errors = true
190
205
  config.ignore_download_errors = true
@@ -35,7 +35,7 @@ module CarrierWave
35
35
  def check_content_type_whitelist!(new_file)
36
36
  content_type = new_file.content_type
37
37
  if content_type_whitelist && !whitelisted_content_type?(content_type)
38
- raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type)
38
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type, allowed_types: Array(content_type_whitelist).join(", "))
39
39
  end
40
40
  end
41
41
 
@@ -1,5 +1,3 @@
1
- require 'open-uri'
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Download
@@ -9,63 +7,8 @@ module CarrierWave
9
7
  include CarrierWave::Uploader::Configuration
10
8
  include CarrierWave::Uploader::Cache
11
9
 
12
- class RemoteFile
13
- def initialize(uri, remote_headers = {})
14
- @uri = uri
15
- @remote_headers = remote_headers
16
- end
17
-
18
- def original_filename
19
- filename = filename_from_header || filename_from_uri
20
- mime_type = MIME::Types[file.content_type].first
21
- unless File.extname(filename).present? || mime_type.blank?
22
- filename = "#{filename}.#{mime_type.extensions.first}"
23
- end
24
- filename
25
- end
26
-
27
- def respond_to?(*args)
28
- super or file.respond_to?(*args)
29
- end
30
-
31
- def http?
32
- @uri.scheme =~ /^https?$/
33
- end
34
-
35
- private
36
-
37
- def file
38
- if @file.blank?
39
- headers = @remote_headers.
40
- reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
41
-
42
- @file = Kernel.open(@uri.to_s, headers)
43
- @file = @file.is_a?(String) ? StringIO.new(@file) : @file
44
- end
45
- @file
46
-
47
- rescue StandardError => e
48
- raise CarrierWave::DownloadError, "could not download file: #{e.message}"
49
- end
50
-
51
- def filename_from_header
52
- if file.meta.include? 'content-disposition'
53
- match = file.meta['content-disposition'].match(/filename="?([^"]+)/)
54
- return match[1] unless match.nil? || match[1].empty?
55
- end
56
- end
57
-
58
- def filename_from_uri
59
- URI.decode(File.basename(file.base_uri.path))
60
- end
61
-
62
- def method_missing(*args, &block)
63
- file.send(*args, &block)
64
- end
65
- end
66
-
67
10
  ##
68
- # Caches the file by downloading it from the given URL.
11
+ # Caches the file by downloading it from the given URL, using downloader.
69
12
  #
70
13
  # === Parameters
71
14
  #
@@ -73,29 +16,9 @@ module CarrierWave
73
16
  # [remote_headers (Hash)] Request headers
74
17
  #
75
18
  def download!(uri, remote_headers = {})
76
- processed_uri = process_uri(uri)
77
- file = RemoteFile.new(processed_uri, remote_headers)
78
- raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http?
19
+ file = downloader.new(self).download(uri, remote_headers)
79
20
  cache!(file)
80
21
  end
81
-
82
- ##
83
- # Processes the given URL by parsing and escaping it. Public to allow overriding.
84
- #
85
- # === Parameters
86
- #
87
- # [url (String)] The URL where the remote file is stored
88
- #
89
- def process_uri(uri)
90
- URI.parse(uri)
91
- rescue URI::InvalidURIError
92
- uri_parts = uri.split('?')
93
- # regexp from Ruby's URI::Parser#regexp[:UNSAFE], with [] specifically removed
94
- encoded_uri = URI.encode(uri_parts.shift, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/)
95
- encoded_uri << '?' << URI.encode(uri_parts.join('?')) if uri_parts.any?
96
- URI.parse(encoded_uri) rescue raise CarrierWave::DownloadError, "couldn't parse URL"
97
- end
98
-
99
22
  end # Download
100
23
  end # Uploader
101
24
  end # CarrierWave
@@ -33,6 +33,12 @@ module CarrierWave
33
33
  @mounted_as = mounted_as
34
34
  end
35
35
 
36
+ ##
37
+ # Returns array index of given uploader within currently mounted uploaders
38
+ #
39
+ def index
40
+ model.__send__(:_mounter, mounted_as).uploaders.index(self)
41
+ end
36
42
  end # Mountable
37
43
  end # Uploader
38
44
  end # CarrierWave
@@ -23,14 +23,14 @@ module CarrierWave
23
23
  alias_method :path, :current_path
24
24
 
25
25
  ##
26
- # Returns a string that uniquely identifies the last stored file
26
+ # Returns a string that uniquely identifies the retrieved or last stored file
27
27
  #
28
28
  # === Returns
29
29
  #
30
30
  # [String] uniquely identifies a file
31
31
  #
32
32
  def identifier
33
- storage.try(:identifier)
33
+ @identifier || storage.try(:identifier)
34
34
  end
35
35
 
36
36
  ##