carrierwave 1.3.0 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of carrierwave might be problematic. Click here for more details.

Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +120 -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 +71 -48
  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 +11 -5
  11. data/lib/carrierwave/processing/vips.rb +284 -0
  12. data/lib/carrierwave/processing.rb +1 -0
  13. data/lib/carrierwave/sanitized_file.rb +44 -23
  14. data/lib/carrierwave/storage/file.rb +2 -2
  15. data/lib/carrierwave/storage/fog.rb +46 -17
  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 -80
  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/proxy.rb +2 -2
  26. data/lib/carrierwave/uploader/serialization.rb +1 -1
  27. data/lib/carrierwave/uploader/store.rb +5 -3
  28. data/lib/carrierwave/uploader/url.rb +6 -3
  29. data/lib/carrierwave/uploader/versions.rb +43 -13
  30. data/lib/carrierwave/uploader.rb +0 -9
  31. data/lib/carrierwave/validations/active_model.rb +3 -3
  32. data/lib/carrierwave/version.rb +1 -1
  33. data/lib/carrierwave.rb +4 -0
  34. data/lib/generators/templates/uploader.rb +2 -2
  35. metadata +113 -21
@@ -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 =~ /#{item}/
55
+ end
44
56
  end
45
57
 
46
58
  end # ContentTypeWhitelist
@@ -1,5 +1,3 @@
1
- require 'open-uri'
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Download
@@ -9,64 +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
- @file = nil
17
- end
18
-
19
- def original_filename
20
- filename = filename_from_header || filename_from_uri
21
- mime_type = MIME::Types[file.content_type].first
22
- unless File.extname(filename).present? || mime_type.blank?
23
- filename = "#{filename}.#{mime_type.extensions.first}"
24
- end
25
- filename
26
- end
27
-
28
- def respond_to?(*args)
29
- super or file.respond_to?(*args)
30
- end
31
-
32
- def http?
33
- @uri.scheme =~ /^https?$/
34
- end
35
-
36
- private
37
-
38
- def file
39
- if @file.blank?
40
- headers = @remote_headers.
41
- reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
42
-
43
- @file = Kernel.open(@uri.to_s, headers)
44
- @file = @file.is_a?(String) ? StringIO.new(@file) : @file
45
- end
46
- @file
47
-
48
- rescue StandardError => e
49
- raise CarrierWave::DownloadError, "could not download file: #{e.message}"
50
- end
51
-
52
- def filename_from_header
53
- if file.meta.include? 'content-disposition'
54
- match = file.meta['content-disposition'].match(/filename="?([^"]+)/)
55
- return match[1] unless match.nil? || match[1].empty?
56
- end
57
- end
58
-
59
- def filename_from_uri
60
- URI.decode(File.basename(file.base_uri.path))
61
- end
62
-
63
- def method_missing(*args, &block)
64
- file.send(*args, &block)
65
- end
66
- end
67
-
68
10
  ##
69
- # Caches the file by downloading it from the given URL.
11
+ # Caches the file by downloading it from the given URL, using downloader.
70
12
  #
71
13
  # === Parameters
72
14
  #
@@ -74,29 +16,9 @@ module CarrierWave
74
16
  # [remote_headers (Hash)] Request headers
75
17
  #
76
18
  def download!(uri, remote_headers = {})
77
- processed_uri = process_uri(uri)
78
- file = RemoteFile.new(processed_uri, remote_headers)
79
- 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)
80
20
  cache!(file)
81
21
  end
82
-
83
- ##
84
- # Processes the given URL by parsing and escaping it. Public to allow overriding.
85
- #
86
- # === Parameters
87
- #
88
- # [url (String)] The URL where the remote file is stored
89
- #
90
- def process_uri(uri)
91
- URI.parse(uri)
92
- rescue URI::InvalidURIError
93
- uri_parts = uri.split('?')
94
- # regexp from Ruby's URI::Parser#regexp[:UNSAFE], with [] specifically removed
95
- encoded_uri = URI.encode(uri_parts.shift, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/)
96
- encoded_uri << '?' << URI.encode(uri_parts.join('?')) if uri_parts.any?
97
- URI.parse(encoded_uri) rescue raise CarrierWave::DownloadError, "couldn't parse URL"
98
- end
99
-
100
22
  end # Download
101
23
  end # Uploader
102
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
@@ -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
@@ -23,7 +23,7 @@ module CarrierWave
23
23
  prepend Module.new {
24
24
  def initialize(*)
25
25
  super
26
- @versions = nil
26
+ @versions, @versions_to_cache, @versions_to_store = nil
27
27
  end
28
28
  }
29
29
  end
@@ -220,16 +220,18 @@ module CarrierWave
220
220
  # Recreate versions and reprocess them. This can be used to recreate
221
221
  # versions if their parameters somehow have changed.
222
222
  #
223
- def recreate_versions!(*versions)
223
+ def recreate_versions!(*names)
224
224
  # Some files could possibly not be stored on the local disk. This
225
225
  # doesn't play nicely with processing. Make sure that we're only
226
226
  # processing a cached file
227
227
  #
228
228
  # The call to store! will trigger the necessary callbacks to both
229
229
  # process this version and all sub-versions
230
- if versions.any?
231
- file = sanitized_file if !cached?
232
- store_versions!(file, versions)
230
+
231
+ if names.any?
232
+ set_versions_to_cache_and_store(names)
233
+ store!(file)
234
+ reset_versions_to_cache_and_store
233
235
  else
234
236
  cache! if !cached?
235
237
  store!
@@ -237,6 +239,39 @@ module CarrierWave
237
239
  end
238
240
 
239
241
  private
242
+
243
+ def set_versions_to_cache_and_store(names)
244
+ @versions_to_cache = source_versions_of(names)
245
+ @versions_to_store = active_versions_with_names_in(@versions_to_cache + names)
246
+ end
247
+
248
+ def reset_versions_to_cache_and_store
249
+ @versions_to_cache, @versions_to_store = nil, nil
250
+ end
251
+
252
+ def versions_to_cache
253
+ @versions_to_cache || dependent_versions
254
+ end
255
+
256
+ def versions_to_store
257
+ @versions_to_store || active_versions
258
+ end
259
+
260
+ def source_versions_of(requested_names)
261
+ versions.inject([]) do |sources, (name, uploader)|
262
+ next sources unless requested_names.include?(name)
263
+ next sources unless source_name = uploader.class.version_options[:from_version]
264
+
265
+ sources << [source_name, versions[source_name]]
266
+ end.uniq
267
+ end
268
+
269
+ def active_versions_with_names_in(names)
270
+ active_versions.select do |pretendent_name, uploader|
271
+ names.include?(pretendent_name)
272
+ end
273
+ end
274
+
240
275
  def assign_parent_cache_id(file)
241
276
  active_versions.each do |name, uploader|
242
277
  uploader.parent_cache_id = @cache_id
@@ -270,19 +305,14 @@ module CarrierWave
270
305
  end
271
306
 
272
307
  def cache_versions!(new_file)
273
- dependent_versions.each do |name, v|
308
+ versions_to_cache.each do |name, v|
274
309
  v.send(:cache_id=, @cache_id)
275
310
  v.cache!(new_file)
276
311
  end
277
312
  end
278
313
 
279
- def store_versions!(new_file, versions=nil)
280
- if versions
281
- active = Hash[active_versions]
282
- versions.each { |v| active[v].try(:store!, new_file) } unless active.empty?
283
- else
284
- active_versions.each { |name, v| v.store!(new_file) }
285
- end
314
+ def store_versions!(new_file)
315
+ versions_to_store.each { |name, v| v.store!(new_file) }
286
316
  end
287
317
 
288
318
  def remove_versions!
@@ -43,15 +43,6 @@ module CarrierWave
43
43
  class Base
44
44
  attr_reader :file
45
45
 
46
- ##
47
- # Workaround for class_attribute malfunction when used with Module#prepend
48
- #
49
- if RUBY_VERSION < '2.1.0'
50
- def self.singleton_class?
51
- !ancestors.include? self
52
- end
53
- end
54
-
55
46
  include CarrierWave::Uploader::Configuration
56
47
  include CarrierWave::Uploader::Callbacks
57
48
  include CarrierWave::Uploader::Proxy