carrierwave 1.3.4 → 2.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +117 -43
  3. data/lib/carrierwave/downloader/base.rb +93 -0
  4. data/lib/carrierwave/downloader/remote_file.rb +65 -0
  5. data/lib/carrierwave/locale/en.yml +5 -4
  6. data/lib/carrierwave/mount.rb +25 -19
  7. data/lib/carrierwave/mounter.rb +70 -47
  8. data/lib/carrierwave/orm/activerecord.rb +14 -8
  9. data/lib/carrierwave/processing/mini_magick.rb +100 -117
  10. data/lib/carrierwave/processing/rmagick.rb +1 -1
  11. data/lib/carrierwave/processing/vips.rb +284 -0
  12. data/lib/carrierwave/processing.rb +1 -0
  13. data/lib/carrierwave/sanitized_file.rb +38 -16
  14. data/lib/carrierwave/storage/file.rb +2 -2
  15. data/lib/carrierwave/storage/fog.rb +44 -13
  16. data/lib/carrierwave/storage.rb +1 -0
  17. data/lib/carrierwave/uploader/cache.rb +24 -16
  18. data/lib/carrierwave/uploader/configuration.rb +28 -15
  19. data/lib/carrierwave/uploader/content_type_blacklist.rb +17 -8
  20. data/lib/carrierwave/uploader/content_type_whitelist.rb +20 -8
  21. data/lib/carrierwave/uploader/download.rb +2 -123
  22. data/lib/carrierwave/uploader/extension_blacklist.rb +18 -10
  23. data/lib/carrierwave/uploader/extension_whitelist.rb +19 -10
  24. data/lib/carrierwave/uploader/mountable.rb +6 -0
  25. data/lib/carrierwave/uploader/processing.rb +11 -1
  26. data/lib/carrierwave/uploader/proxy.rb +2 -2
  27. data/lib/carrierwave/uploader/serialization.rb +1 -1
  28. data/lib/carrierwave/uploader/store.rb +5 -3
  29. data/lib/carrierwave/uploader/url.rb +6 -3
  30. data/lib/carrierwave/uploader/versions.rb +43 -13
  31. data/lib/carrierwave/uploader.rb +0 -9
  32. data/lib/carrierwave/validations/active_model.rb +3 -3
  33. data/lib/carrierwave/version.rb +1 -1
  34. data/lib/carrierwave.rb +4 -0
  35. data/lib/generators/templates/uploader.rb +2 -2
  36. metadata +100 -27
@@ -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
@@ -119,16 +122,8 @@ module CarrierWave
119
122
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
120
123
  @#{name} = nil
121
124
 
122
- def self.eager_load_fog(fog_credentials)
123
- # see #1198. This will hopefully no longer be necessary after fog 2.0
124
- require self.fog_provider
125
- require 'carrierwave/storage/fog'
126
- Fog::Storage.new(fog_credentials) if fog_credentials.present?
127
- end unless defined? eager_load_fog
128
-
129
125
  def self.#{name}(value=nil)
130
126
  @#{name} = value if value
131
- eager_load_fog(value) if value && '#{name}' == 'fog_credentials'
132
127
  return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
133
128
  name = superclass.#{name}
134
129
  return nil if name.nil? && !instance_variable_defined?(:@#{name})
@@ -136,12 +131,10 @@ module CarrierWave
136
131
  end
137
132
 
138
133
  def self.#{name}=(value)
139
- eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
140
134
  @#{name} = value
141
135
  end
142
136
 
143
137
  def #{name}=(value)
144
- self.class.eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
145
138
  @#{name} = value
146
139
  end
147
140
 
@@ -157,6 +150,26 @@ module CarrierWave
157
150
  RUBY
158
151
  end
159
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
+
160
173
  def configure
161
174
  yield self
162
175
  end
@@ -173,8 +186,7 @@ module CarrierWave
173
186
  :fog => "CarrierWave::Storage::Fog"
174
187
  }
175
188
  config.storage = :file
176
- config.cache_storage = :file
177
- config.fog_provider = 'fog'
189
+ config.cache_storage = nil
178
190
  config.fog_attributes = {}
179
191
  config.fog_credentials = {}
180
192
  config.fog_public = true
@@ -187,6 +199,7 @@ module CarrierWave
187
199
  config.move_to_cache = false
188
200
  config.move_to_store = false
189
201
  config.remove_previously_stored_files_after_update = true
202
+ config.downloader = CarrierWave::Downloader::Base
190
203
  config.ignore_integrity_errors = true
191
204
  config.ignore_processing_errors = true
192
205
  config.ignore_download_errors = true
@@ -8,39 +8,48 @@ module CarrierWave
8
8
  end
9
9
 
10
10
  ##
11
- # Override this method in your uploader to provide a blacklist of files content types
11
+ # Override this method in your uploader to provide a denylist of files content types
12
12
  # which are not allowed to be uploaded.
13
13
  # Not only strings but Regexp are allowed as well.
14
14
  #
15
15
  # === Returns
16
16
  #
17
- # [NilClass, String, Regexp, Array[String, Regexp]] a blacklist of content types which are not allowed to be uploaded
17
+ # [NilClass, String, Regexp, Array[String, Regexp]] a denylist of content types which are not allowed to be uploaded
18
18
  #
19
19
  # === Examples
20
20
  #
21
- # def content_type_blacklist
21
+ # def content_type_denylist
22
22
  # %w(text/json application/json)
23
23
  # end
24
24
  #
25
25
  # Basically the same, but using a Regexp:
26
26
  #
27
- # def content_type_blacklist
27
+ # def content_type_denylist
28
28
  # [/(text|application)\/json/]
29
29
  # end
30
30
  #
31
- def content_type_blacklist; end
31
+ def content_type_denylist
32
+ if respond_to?(:content_type_blacklist)
33
+ ActiveSupport::Deprecation.warn "#content_type_blacklist is deprecated, use #content_type_denylist instead." unless instance_variable_defined?(:@content_type_blacklist_warned)
34
+ @content_type_blacklist_warned = true
35
+ content_type_blacklist
36
+ end
37
+ end
32
38
 
33
39
  private
34
40
 
35
41
  def check_content_type_blacklist!(new_file)
42
+ return unless content_type_denylist
43
+
36
44
  content_type = new_file.content_type
37
- if content_type_blacklist && blacklisted_content_type?(content_type)
38
- raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_blacklist_error", content_type: content_type)
45
+ if blacklisted_content_type?(content_type)
46
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_blacklist_error",
47
+ content_type: content_type, default: :"errors.messages.content_type_denylist_error")
39
48
  end
40
49
  end
41
50
 
42
51
  def blacklisted_content_type?(content_type)
43
- Array(content_type_blacklist).any? { |item| content_type =~ /#{item}/ }
52
+ Array(content_type_denylist).any? { |item| content_type =~ /#{item}/ }
44
53
  end
45
54
 
46
55
  end # ContentTypeBlacklist
@@ -8,39 +8,51 @@ module CarrierWave
8
8
  end
9
9
 
10
10
  ##
11
- # Override this method in your uploader to provide a whitelist of files content types
11
+ # Override this method in your uploader to provide an allowlist of files content types
12
12
  # which are allowed to be uploaded.
13
13
  # Not only strings but Regexp are allowed as well.
14
14
  #
15
15
  # === Returns
16
16
  #
17
- # [NilClass, String, Regexp, Array[String, Regexp]] a whitelist of content types which are allowed to be uploaded
17
+ # [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of content types which are allowed to be uploaded
18
18
  #
19
19
  # === Examples
20
20
  #
21
- # def content_type_whitelist
21
+ # def content_type_allowlist
22
22
  # %w(text/json application/json)
23
23
  # end
24
24
  #
25
25
  # Basically the same, but using a Regexp:
26
26
  #
27
- # def content_type_whitelist
27
+ # def content_type_allowlist
28
28
  # [/(text|application)\/json/]
29
29
  # end
30
30
  #
31
- def content_type_whitelist; end
31
+ def content_type_allowlist
32
+ if respond_to?(:content_type_whitelist)
33
+ ActiveSupport::Deprecation.warn "#content_type_whitelist is deprecated, use #content_type_allowlist instead." unless instance_variable_defined?(:@content_type_whitelist_warned)
34
+ @content_type_whitelist_warned = true
35
+ content_type_whitelist
36
+ end
37
+ end
32
38
 
33
39
  private
34
40
 
35
41
  def check_content_type_whitelist!(new_file)
42
+ return unless content_type_allowlist
43
+
36
44
  content_type = new_file.content_type
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, allowed_types: Array(content_type_whitelist).join(", "))
45
+ if !whitelisted_content_type?(content_type)
46
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type,
47
+ allowed_types: Array(content_type_allowlist).join(", "), default: :"errors.messages.content_type_allowlist_error")
39
48
  end
40
49
  end
41
50
 
42
51
  def whitelisted_content_type?(content_type)
43
- Array(content_type_whitelist).any? { |item| content_type =~ /#{item}/ }
52
+ Array(content_type_allowlist).any? do |item|
53
+ item = Regexp.quote(item) if item.class != Regexp
54
+ content_type =~ /\A#{item}/
55
+ end
44
56
  end
45
57
 
46
58
  end # ContentTypeWhitelist
@@ -1,6 +1,3 @@
1
- require 'open-uri'
2
- require 'ssrf_filter'
3
-
4
1
  module CarrierWave
5
2
  module Uploader
6
3
  module Download
@@ -10,87 +7,8 @@ module CarrierWave
10
7
  include CarrierWave::Uploader::Configuration
11
8
  include CarrierWave::Uploader::Cache
12
9
 
13
- class RemoteFile
14
- attr_reader :uri
15
-
16
- def initialize(uri, remote_headers = {}, skip_ssrf_protection: false)
17
- @uri = uri
18
- @remote_headers = remote_headers.reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
19
- @file, @content_type, @headers = nil
20
- @skip_ssrf_protection = skip_ssrf_protection
21
- end
22
-
23
- def original_filename
24
- filename = filename_from_header || filename_from_uri
25
- mime_type = MIME::Types[content_type].first
26
- unless File.extname(filename).present? || mime_type.blank?
27
- filename = "#{filename}.#{mime_type.extensions.first}"
28
- end
29
- filename
30
- end
31
-
32
- def respond_to?(*args)
33
- super or file.respond_to?(*args)
34
- end
35
-
36
- def http?
37
- @uri.scheme =~ /^https?$/
38
- end
39
-
40
- def content_type
41
- @content_type || 'application/octet-stream'
42
- end
43
-
44
- def headers
45
- @headers || {}
46
- end
47
-
48
- private
49
-
50
- def file
51
- if @file.blank?
52
- if @skip_ssrf_protection
53
- @file = (URI.respond_to?(:open) ? URI : Kernel).open(@uri.to_s, @remote_headers)
54
- @file = @file.is_a?(String) ? StringIO.new(@file) : @file
55
- @content_type = @file.content_type
56
- @headers = @file.meta
57
- @uri = @file.base_uri
58
- else
59
- request = nil
60
- response = SsrfFilter.get(@uri, headers: @remote_headers) do |req|
61
- request = req
62
- end
63
- response.value
64
- @file = StringIO.new(response.body)
65
- @content_type = response.content_type
66
- @headers = response
67
- @uri = request.uri
68
- end
69
- end
70
- @file
71
-
72
- rescue StandardError => e
73
- raise CarrierWave::DownloadError, "could not download file: #{e.message}"
74
- end
75
-
76
- def filename_from_header
77
- if headers['content-disposition']
78
- match = headers['content-disposition'].match(/filename="?([^"]+)/)
79
- return match[1] unless match.nil? || match[1].empty?
80
- end
81
- end
82
-
83
- def filename_from_uri
84
- URI::DEFAULT_PARSER.unescape(File.basename(@uri.path))
85
- end
86
-
87
- def method_missing(*args, &block)
88
- file.send(*args, &block)
89
- end
90
- end
91
-
92
10
  ##
93
- # Caches the file by downloading it from the given URL.
11
+ # Caches the file by downloading it from the given URL, using downloader.
94
12
  #
95
13
  # === Parameters
96
14
  #
@@ -98,48 +16,9 @@ module CarrierWave
98
16
  # [remote_headers (Hash)] Request headers
99
17
  #
100
18
  def download!(uri, remote_headers = {})
101
- processed_uri = process_uri(uri)
102
- file = RemoteFile.new(processed_uri, remote_headers, skip_ssrf_protection: skip_ssrf_protection?(processed_uri))
103
- 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)
104
20
  cache!(file)
105
21
  end
106
-
107
- ##
108
- # Processes the given URL by parsing and escaping it. Public to allow overriding.
109
- #
110
- # === Parameters
111
- #
112
- # [url (String)] The URL where the remote file is stored
113
- #
114
- def process_uri(uri)
115
- URI.parse(uri)
116
- rescue URI::InvalidURIError
117
- uri_parts = uri.split('?')
118
- # regexp from Ruby's URI::Parser#regexp[:UNSAFE], with [] specifically removed
119
- encoded_uri = URI::DEFAULT_PARSER.escape(uri_parts.shift, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/)
120
- encoded_uri << '?' << URI::DEFAULT_PARSER.escape(uri_parts.join('?')) if uri_parts.any?
121
- URI.parse(encoded_uri) rescue raise CarrierWave::DownloadError, "couldn't parse URL"
122
- end
123
-
124
- ##
125
- # If this returns true, SSRF protection will be bypassed.
126
- # You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.
127
- #
128
- # === Parameters
129
- #
130
- # [uri (URI)] The URI where the remote file is stored
131
- #
132
- # === Examples
133
- #
134
- # class MyUploader < CarrierWave::Uploader::Base
135
- # def skip_ssrf_protection?(uri)
136
- # uri.hostname == 'localhost' && uri.port == 80
137
- # end
138
- # end
139
- #
140
- def skip_ssrf_protection?(uri)
141
- false
142
- end
143
22
  end # Download
144
23
  end # Uploader
145
24
  end # CarrierWave
@@ -8,43 +8,51 @@ module CarrierWave
8
8
  end
9
9
 
10
10
  ##
11
- # Override this method in your uploader to provide a black list of extensions which
11
+ # Override this method in your uploader to provide a denylist of extensions which
12
12
  # are prohibited to be uploaded. Compares the file's extension case insensitive.
13
13
  # Furthermore, not only strings but Regexp are allowed as well.
14
14
  #
15
- # When using a Regexp in the black list, `\A` and `\z` are automatically added to
15
+ # When using a Regexp in the denylist, `\A` and `\z` are automatically added to
16
16
  # the Regexp expression, also case insensitive.
17
17
  #
18
18
  # === Returns
19
19
 
20
- # [NilClass, String, Regexp, Array[String, Regexp]] a black list of extensions which are prohibited to be uploaded
20
+ # [NilClass, String, Regexp, Array[String, Regexp]] a deny list of extensions which are prohibited to be uploaded
21
21
  #
22
22
  # === Examples
23
23
  #
24
- # def extension_blacklist
24
+ # def extension_denylist
25
25
  # %w(swf tiff)
26
26
  # end
27
27
  #
28
28
  # Basically the same, but using a Regexp:
29
29
  #
30
- # def extension_blacklist
30
+ # def extension_denylist
31
31
  # [/swf/, 'tiff']
32
32
  # end
33
33
  #
34
-
35
- def extension_blacklist; end
34
+ def extension_denylist
35
+ if respond_to?(:extension_blacklist)
36
+ ActiveSupport::Deprecation.warn "#extension_blacklist is deprecated, use #extension_denylist instead." unless instance_variable_defined?(:@extension_blacklist_warned)
37
+ @extension_blacklist_warned = true
38
+ extension_blacklist
39
+ end
40
+ end
36
41
 
37
42
  private
38
43
 
39
44
  def check_extension_blacklist!(new_file)
45
+ return unless extension_denylist
46
+
40
47
  extension = new_file.extension.to_s
41
- if extension_blacklist && blacklisted_extension?(extension)
42
- raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_blacklist_error", extension: new_file.extension.inspect, prohibited_types: Array(extension_blacklist).join(", "))
48
+ if blacklisted_extension?(extension)
49
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_blacklist_error", extension: new_file.extension.inspect,
50
+ prohibited_types: Array(extension_denylist).join(", "), default: :"errors.messages.extension_denylist_error")
43
51
  end
44
52
  end
45
53
 
46
54
  def blacklisted_extension?(extension)
47
- Array(extension_blacklist).any? { |item| extension =~ /\A#{item}\z/i }
55
+ Array(extension_denylist).any? { |item| extension =~ /\A#{item}\z/i }
48
56
  end
49
57
  end
50
58
  end
@@ -8,45 +8,54 @@ module CarrierWave
8
8
  end
9
9
 
10
10
  ##
11
- # Override this method in your uploader to provide a white list of extensions which
11
+ # Override this method in your uploader to provide an allowlist of extensions which
12
12
  # are allowed to be uploaded. Compares the file's extension case insensitive.
13
13
  # Furthermore, not only strings but Regexp are allowed as well.
14
14
  #
15
- # When using a Regexp in the white list, `\A` and `\z` are automatically added to
15
+ # When using a Regexp in the allowlist, `\A` and `\z` are automatically added to
16
16
  # the Regexp expression, also case insensitive.
17
17
  #
18
18
  # === Returns
19
19
  #
20
- # [NilClass, String, Regexp, Array[String, Regexp]] a white list of extensions which are allowed to be uploaded
20
+ # [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of extensions which are allowed to be uploaded
21
21
  #
22
22
  # === Examples
23
23
  #
24
- # def extension_whitelist
24
+ # def extension_allowlist
25
25
  # %w(jpg jpeg gif png)
26
26
  # end
27
27
  #
28
28
  # Basically the same, but using a Regexp:
29
29
  #
30
- # def extension_whitelist
30
+ # def extension_allowlist
31
31
  # [/jpe?g/, 'gif', 'png']
32
32
  # end
33
33
  #
34
- def extension_whitelist; end
34
+ def extension_allowlist
35
+ if respond_to?(:extension_whitelist)
36
+ ActiveSupport::Deprecation.warn "#extension_whitelist is deprecated, use #extension_allowlist instead." unless instance_variable_defined?(:@extension_whitelist_warned)
37
+ @extension_whitelist_warned = true
38
+ extension_whitelist
39
+ end
40
+ end
35
41
 
36
42
  private
37
43
 
38
44
  def check_extension_whitelist!(new_file)
45
+ return unless extension_allowlist
46
+
39
47
  extension = new_file.extension.to_s
40
- if extension_whitelist && !whitelisted_extension?(extension)
41
- raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_whitelist_error", extension: new_file.extension.inspect, allowed_types: Array(extension_whitelist).join(", "))
48
+ if !whitelisted_extension?(extension)
49
+ # Look for whitelist first, then fallback to allowlist
50
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_whitelist_error", extension: new_file.extension.inspect,
51
+ allowed_types: Array(extension_allowlist).join(", "), default: :"errors.messages.extension_allowlist_error")
42
52
  end
43
53
  end
44
54
 
45
55
  def whitelisted_extension?(extension)
46
56
  downcase_extension = extension.downcase
47
- Array(extension_whitelist).any? { |item| downcase_extension =~ /\A#{item}\z/i }
57
+ Array(extension_allowlist).any? { |item| downcase_extension =~ /\A#{item}\z/i }
48
58
  end
49
-
50
59
  end # ExtensionWhitelist
51
60
  end # Uploader
52
61
  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
@@ -80,7 +80,17 @@ module CarrierWave
80
80
  next unless self.send(condition, new_file)
81
81
  end
82
82
  end
83
- self.send(method, *args)
83
+
84
+ if args.is_a? Array
85
+ kwargs, args = args.partition { |arg| arg.is_a? Hash }
86
+ end
87
+
88
+ if kwargs.present?
89
+ kwargs = kwargs.reduce(:merge)
90
+ self.send(method, *args, **kwargs)
91
+ else
92
+ self.send(method, *args)
93
+ end
84
94
  end
85
95
  end
86
96
  end
@@ -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
  ##
@@ -7,7 +7,7 @@ module CarrierWave
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  def serializable_hash(options = nil)
10
- {"url" => url}.merge Hash[versions.map { |name, version| [name, { "url" => version.url }] }]
10
+ {"url" => url}.merge Hash[versions.map { |name, version| [name.to_s, { "url" => version.url }] }]
11
11
  end
12
12
 
13
13
  def as_json(options=nil)
@@ -11,7 +11,7 @@ module CarrierWave
11
11
  prepend Module.new {
12
12
  def initialize(*)
13
13
  super
14
- @file, @filename, @cache_id = nil
14
+ @file, @filename, @cache_id, @identifier = nil
15
15
  end
16
16
  }
17
17
  end
@@ -61,7 +61,7 @@ module CarrierWave
61
61
  #
62
62
  def store!(new_file=nil)
63
63
  cache!(new_file) if new_file && ((@cache_id != parent_cache_id) || @cache_id.nil?)
64
- if !cache_only and @file and @cache_id
64
+ if !cache_only && @file && @cache_id
65
65
  with_callbacks(:store, new_file) do
66
66
  new_file = storage.store!(@file)
67
67
  if delete_tmp_file_after_storage
@@ -69,7 +69,8 @@ module CarrierWave
69
69
  cache_storage.delete_dir!(cache_path(nil))
70
70
  end
71
71
  @file = new_file
72
- @cache_id = nil
72
+ @cache_id = @identifier = nil
73
+ @staged = false
73
74
  end
74
75
  end
75
76
  end
@@ -84,6 +85,7 @@ module CarrierWave
84
85
  def retrieve_from_store!(identifier)
85
86
  with_callbacks(:retrieve_from_store, identifier) do
86
87
  @file = storage.retrieve!(identifier)
88
+ @identifier = identifier
87
89
  end
88
90
  end
89
91
 
@@ -15,9 +15,12 @@ module CarrierWave
15
15
  # [String] the location where this file is accessible via a url
16
16
  #
17
17
  def url(options = {})
18
- if file.respond_to?(:url) and not (tmp_url = file.url).blank?
19
- file.method(:url).arity == 0 ? tmp_url : file.url(options)
20
- elsif file.respond_to?(:path)
18
+ if file.respond_to?(:url)
19
+ tmp_url = file.method(:url).arity.zero? ? file.url : file.url(options)
20
+ return tmp_url if tmp_url.present?
21
+ end
22
+
23
+ if file.respond_to?(:path)
21
24
  path = encode_path(file.path.sub(File.expand_path(root), ''))
22
25
 
23
26
  if host = asset_host