carrierwave 2.2.1 → 3.0.1

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +137 -67
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -2
  4. data/lib/carrierwave/downloader/base.rb +27 -13
  5. data/lib/carrierwave/downloader/remote_file.rb +12 -9
  6. data/lib/carrierwave/locale/en.yml +5 -3
  7. data/lib/carrierwave/mount.rb +31 -50
  8. data/lib/carrierwave/mounter.rb +115 -50
  9. data/lib/carrierwave/orm/activerecord.rb +14 -60
  10. data/lib/carrierwave/processing/mini_magick.rb +15 -13
  11. data/lib/carrierwave/processing/rmagick.rb +11 -15
  12. data/lib/carrierwave/processing/vips.rb +12 -12
  13. data/lib/carrierwave/sanitized_file.rb +49 -77
  14. data/lib/carrierwave/storage/abstract.rb +5 -5
  15. data/lib/carrierwave/storage/file.rb +6 -5
  16. data/lib/carrierwave/storage/fog.rb +75 -67
  17. data/lib/carrierwave/test/matchers.rb +11 -7
  18. data/lib/carrierwave/uploader/cache.rb +18 -10
  19. data/lib/carrierwave/uploader/callbacks.rb +1 -1
  20. data/lib/carrierwave/uploader/configuration.rb +10 -4
  21. data/lib/carrierwave/uploader/{content_type_whitelist.rb → content_type_allowlist.rb} +17 -15
  22. data/lib/carrierwave/uploader/{content_type_blacklist.rb → content_type_denylist.rb} +19 -14
  23. data/lib/carrierwave/uploader/dimension.rb +66 -0
  24. data/lib/carrierwave/uploader/{extension_whitelist.rb → extension_allowlist.rb} +17 -15
  25. data/lib/carrierwave/uploader/{extension_blacklist.rb → extension_denylist.rb} +18 -13
  26. data/lib/carrierwave/uploader/file_size.rb +2 -2
  27. data/lib/carrierwave/uploader/processing.rb +42 -7
  28. data/lib/carrierwave/uploader/proxy.rb +16 -3
  29. data/lib/carrierwave/uploader/store.rb +44 -6
  30. data/lib/carrierwave/uploader/url.rb +1 -1
  31. data/lib/carrierwave/uploader/versions.rb +150 -132
  32. data/lib/carrierwave/uploader.rb +10 -8
  33. data/lib/carrierwave/utilities/file_name.rb +47 -0
  34. data/lib/carrierwave/utilities/uri.rb +14 -11
  35. data/lib/carrierwave/utilities.rb +1 -0
  36. data/lib/carrierwave/validations/active_model.rb +4 -6
  37. data/lib/carrierwave/version.rb +1 -1
  38. data/lib/carrierwave.rb +9 -17
  39. data/lib/generators/uploader_generator.rb +3 -3
  40. metadata +31 -43
  41. /data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +0 -0
@@ -24,7 +24,8 @@ module CarrierWave
24
24
  # [String] a cache id in the format TIMEINT-PID-COUNTER-RND
25
25
  #
26
26
  def self.generate_cache_id
27
- [Time.now.utc.to_i,
27
+ [
28
+ Time.now.utc.to_i,
28
29
  SecureRandom.random_number(1_000_000_000_000_000),
29
30
  '%04d' % (CarrierWave::CacheCounter.increment % 10_000),
30
31
  '%04d' % SecureRandom.random_number(10_000)
@@ -64,7 +65,7 @@ module CarrierWave
64
65
  # It's recommended that you keep cache files in one place only.
65
66
  #
66
67
  def clean_cached_files!(seconds=60*60*24)
67
- (cache_storage || storage).new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
68
+ (cache_storage || storage).new(new).clean_cache!(seconds)
68
69
  end
69
70
  end
70
71
 
@@ -102,7 +103,7 @@ module CarrierWave
102
103
  # [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
103
104
  #
104
105
  def cache_name
105
- File.join(cache_id, full_original_filename) if cache_id && original_filename
106
+ File.join(cache_id, original_filename) if cache_id && original_filename
106
107
  end
107
108
 
108
109
  ##
@@ -110,7 +111,7 @@ module CarrierWave
110
111
  #
111
112
  # By default, cache!() uses copy_to(), which operates by copying the file
112
113
  # to the cache, then deleting the original file. If move_to_cache() is
113
- # overriden to return true, then cache!() uses move_to(), which simply
114
+ # overridden to return true, then cache!() uses move_to(), which simply
114
115
  # moves the file to the cache. Useful for large files.
115
116
  #
116
117
  # === Parameters
@@ -129,6 +130,7 @@ module CarrierWave
129
130
 
130
131
  self.cache_id = CarrierWave.generate_cache_id unless cache_id
131
132
 
133
+ @identifier = nil
132
134
  @staged = true
133
135
  @filename = new_file.filename
134
136
  self.original_filename = new_file.filename
@@ -165,7 +167,7 @@ module CarrierWave
165
167
  self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
166
168
  @staged = true
167
169
  @filename = original_filename
168
- @file = cache_storage.retrieve_from_cache!(full_filename(original_filename))
170
+ @file = cache_storage.retrieve_from_cache!(full_original_filename)
169
171
  end
170
172
  end
171
173
 
@@ -180,20 +182,21 @@ module CarrierWave
180
182
  #
181
183
  # [String] the cache path
182
184
  #
183
- def cache_path(for_file=full_filename(original_filename))
185
+ def cache_path(for_file=full_original_filename)
184
186
  File.join(*[cache_dir, @cache_id, for_file].compact)
185
187
  end
186
188
 
189
+ protected
190
+
191
+ attr_reader :cache_id
192
+
187
193
  private
188
194
 
189
195
  def workfile_path(for_file=original_filename)
190
196
  File.join(CarrierWave.tmp_path, @cache_id, version_name.to_s, for_file)
191
197
  end
192
198
 
193
- attr_reader :cache_id, :original_filename
194
-
195
- # We can override the full_original_filename method in other modules
196
- alias_method :full_original_filename, :original_filename
199
+ attr_reader :original_filename
197
200
 
198
201
  def cache_id=(cache_id)
199
202
  # Earlier version used 3 part cache_id. Thus we should allow for
@@ -210,6 +213,11 @@ module CarrierWave
210
213
  def cache_storage
211
214
  @cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
212
215
  end
216
+
217
+ # We can override the full_original_filename method in other modules
218
+ def full_original_filename
219
+ forcing_extension(original_filename)
220
+ end
213
221
  end # Cache
214
222
  end # Uploader
215
223
  end # CarrierWave
@@ -5,7 +5,7 @@ module CarrierWave
5
5
 
6
6
  included do
7
7
  class_attribute :_before_callbacks, :_after_callbacks,
8
- :instance_writer => false
8
+ :instance_writer => false
9
9
  self._before_callbacks = Hash.new []
10
10
  self._after_callbacks = Hash.new []
11
11
  end
@@ -24,6 +24,7 @@ module CarrierWave
24
24
  add_config :move_to_store
25
25
  add_config :remove_previously_stored_files_after_update
26
26
  add_config :downloader
27
+ add_config :force_extension
27
28
 
28
29
  # fog
29
30
  add_deprecated_config :fog_provider
@@ -44,6 +45,8 @@ module CarrierWave
44
45
  add_config :validate_download
45
46
  add_config :mount_on
46
47
  add_config :cache_only
48
+ add_config :download_retry_count
49
+ add_config :download_retry_wait_time
47
50
 
48
51
  # set default values
49
52
  reset_config
@@ -77,7 +80,7 @@ module CarrierWave
77
80
  def storage(storage = nil)
78
81
  case storage
79
82
  when Symbol
80
- if storage_engine = storage_engines[storage]
83
+ if (storage_engine = storage_engines[storage])
81
84
  self._storage = eval storage_engine
82
85
  else
83
86
  raise CarrierWave::UnknownStorageError, "Unknown storage: #{storage}"
@@ -123,7 +126,7 @@ module CarrierWave
123
126
  @#{name} = nil
124
127
 
125
128
  def self.#{name}(value=nil)
126
- @#{name} = value if value
129
+ @#{name} = value unless value.nil?
127
130
  return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
128
131
  name = superclass.#{name}
129
132
  return nil if name.nil? && !instance_variable_defined?(:@#{name})
@@ -179,8 +182,8 @@ module CarrierWave
179
182
  #
180
183
  def reset_config
181
184
  configure do |config|
182
- config.permissions = 0644
183
- config.directory_permissions = 0755
185
+ config.permissions = 0o644
186
+ config.directory_permissions = 0o755
184
187
  config.storage_engines = {
185
188
  :file => "CarrierWave::Storage::File",
186
189
  :fog => "CarrierWave::Storage::Fog"
@@ -200,6 +203,7 @@ module CarrierWave
200
203
  config.move_to_store = false
201
204
  config.remove_previously_stored_files_after_update = true
202
205
  config.downloader = CarrierWave::Downloader::Base
206
+ config.force_extension = false
203
207
  config.ignore_integrity_errors = true
204
208
  config.ignore_processing_errors = true
205
209
  config.ignore_download_errors = true
@@ -210,6 +214,8 @@ module CarrierWave
210
214
  config.base_path = CarrierWave.base_path
211
215
  config.enable_processing = true
212
216
  config.ensure_multipart_form = true
217
+ config.download_retry_count = 0
218
+ config.download_retry_wait_time = 5
213
219
  end
214
220
  end
215
221
  end
@@ -1,10 +1,10 @@
1
1
  module CarrierWave
2
2
  module Uploader
3
- module ContentTypeWhitelist
3
+ module ContentTypeAllowlist
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- before :cache, :check_content_type_whitelist!
7
+ before :cache, :check_content_type_allowlist!
8
8
  end
9
9
 
10
10
  ##
@@ -29,32 +29,34 @@ module CarrierWave
29
29
  # end
30
30
  #
31
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
32
  end
38
33
 
39
34
  private
40
35
 
41
- def check_content_type_whitelist!(new_file)
42
- return unless content_type_allowlist
36
+ def check_content_type_allowlist!(new_file)
37
+ allowlist = content_type_allowlist
38
+ if !allowlist && respond_to?(:content_type_whitelist) && content_type_whitelist
39
+ ActiveSupport::Deprecation.warn "#content_type_whitelist is deprecated, use #content_type_allowlist instead." unless instance_variable_defined?(:@content_type_whitelist_warned)
40
+ @content_type_whitelist_warned = true
41
+ allowlist = content_type_whitelist
42
+ end
43
+
44
+ return unless allowlist
43
45
 
44
46
  content_type = new_file.content_type
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")
47
+ if !allowlisted_content_type?(allowlist, content_type)
48
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_allowlist_error", content_type: content_type,
49
+ allowed_types: Array(allowlist).join(", "), default: :"errors.messages.content_type_whitelist_error")
48
50
  end
49
51
  end
50
52
 
51
- def whitelisted_content_type?(content_type)
52
- Array(content_type_allowlist).any? do |item|
53
+ def allowlisted_content_type?(allowlist, content_type)
54
+ Array(allowlist).any? do |item|
53
55
  item = Regexp.quote(item) if item.class != Regexp
54
56
  content_type =~ /#{item}/
55
57
  end
56
58
  end
57
59
 
58
- end # ContentTypeWhitelist
60
+ end # ContentTypeAllowlist
59
61
  end # Uploader
60
62
  end # CarrierWave
@@ -1,10 +1,10 @@
1
1
  module CarrierWave
2
2
  module Uploader
3
- module ContentTypeBlacklist
3
+ module ContentTypeDenylist
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- before :cache, :check_content_type_blacklist!
7
+ before :cache, :check_content_type_denylist!
8
8
  end
9
9
 
10
10
  ##
@@ -29,29 +29,34 @@ module CarrierWave
29
29
  # end
30
30
  #
31
31
  def content_type_denylist
32
- if respond_to?(:content_type_blacklist)
32
+ end
33
+
34
+ private
35
+
36
+ def check_content_type_denylist!(new_file)
37
+ denylist = content_type_denylist
38
+ if !denylist && respond_to?(:content_type_blacklist) && content_type_blacklist
33
39
  ActiveSupport::Deprecation.warn "#content_type_blacklist is deprecated, use #content_type_denylist instead." unless instance_variable_defined?(:@content_type_blacklist_warned)
34
40
  @content_type_blacklist_warned = true
35
- content_type_blacklist
41
+ denylist = content_type_blacklist
36
42
  end
37
- end
38
43
 
39
- private
44
+ return unless denylist
40
45
 
41
- def check_content_type_blacklist!(new_file)
42
- return unless content_type_denylist
46
+ ActiveSupport::Deprecation.warn "Use of #content_type_denylist is deprecated for the security reason, use #content_type_allowlist instead to explicitly state what are safe to accept" unless instance_variable_defined?(:@content_type_denylist_warned)
47
+ @content_type_denylist_warned = true
43
48
 
44
49
  content_type = new_file.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")
50
+ if denylisted_content_type?(denylist, content_type)
51
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_denylist_error",
52
+ content_type: content_type, default: :"errors.messages.content_type_blacklist_error")
48
53
  end
49
54
  end
50
55
 
51
- def blacklisted_content_type?(content_type)
52
- Array(content_type_denylist).any? { |item| content_type =~ /#{item}/ }
56
+ def denylisted_content_type?(denylist, content_type)
57
+ Array(denylist).any? { |item| content_type =~ /#{item}/ }
53
58
  end
54
59
 
55
- end # ContentTypeBlacklist
60
+ end # ContentTypeDenylist
56
61
  end # Uploader
57
62
  end # CarrierWave
@@ -0,0 +1,66 @@
1
+ require 'active_support'
2
+
3
+ module CarrierWave
4
+ module Uploader
5
+ module Dimension
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ before :cache, :check_dimensions!
10
+ end
11
+
12
+ ##
13
+ # Override this method in your uploader to provide a Range of width which
14
+ # are allowed to be uploaded.
15
+ # === Returns
16
+ #
17
+ # [NilClass, Range] a width range which are permitted to be uploaded
18
+ #
19
+ # === Examples
20
+ #
21
+ # def width_range
22
+ # 1000..2000
23
+ # end
24
+ #
25
+ def width_range; end
26
+
27
+ ##
28
+ # Override this method in your uploader to provide a Range of height which
29
+ # are allowed to be uploaded.
30
+ # === Returns
31
+ #
32
+ # [NilClass, Range] a height range which are permitted to be uploaded
33
+ #
34
+ # === Examples
35
+ #
36
+ # def height_range
37
+ # 1000..
38
+ # end
39
+ #
40
+ def height_range; end
41
+
42
+ private
43
+
44
+ def check_dimensions!(new_file)
45
+ # NOTE: Skip the check for resized images
46
+ return if version_name.present?
47
+ return unless width_range || height_range
48
+
49
+ unless respond_to?(:width) || respond_to?(:height)
50
+ raise 'You need to include one of CarrierWave::MiniMagick, CarrierWave::RMagick, or CarrierWave::Vips to perform image dimension validation'
51
+ end
52
+
53
+ if width_range&.begin && width < width_range.begin
54
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.min_width_error", :min_width => ActiveSupport::NumberHelper.number_to_delimited(width_range.begin))
55
+ elsif width_range&.end && width > width_range.end
56
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.max_width_error", :max_width => ActiveSupport::NumberHelper.number_to_delimited(width_range.end))
57
+ elsif height_range&.begin && height < height_range.begin
58
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.min_height_error", :min_height => ActiveSupport::NumberHelper.number_to_delimited(height_range.begin))
59
+ elsif height_range&.end && height > height_range.end
60
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.max_height_error", :max_height => ActiveSupport::NumberHelper.number_to_delimited(height_range.end))
61
+ end
62
+ end
63
+
64
+ end # Dimension
65
+ end # Uploader
66
+ end # CarrierWave
@@ -1,10 +1,10 @@
1
1
  module CarrierWave
2
2
  module Uploader
3
- module ExtensionWhitelist
3
+ module ExtensionAllowlist
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- before :cache, :check_extension_whitelist!
7
+ before :cache, :check_extension_allowlist!
8
8
  end
9
9
 
10
10
  ##
@@ -32,30 +32,32 @@ module CarrierWave
32
32
  # end
33
33
  #
34
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
35
  end
41
36
 
42
37
  private
43
38
 
44
- def check_extension_whitelist!(new_file)
45
- return unless extension_allowlist
39
+ def check_extension_allowlist!(new_file)
40
+ allowlist = extension_allowlist
41
+ if !allowlist && respond_to?(:extension_whitelist) && extension_whitelist
42
+ ActiveSupport::Deprecation.warn "#extension_whitelist is deprecated, use #extension_allowlist instead." unless instance_variable_defined?(:@extension_whitelist_warned)
43
+ @extension_whitelist_warned = true
44
+ allowlist = extension_whitelist
45
+ end
46
+
47
+ return unless allowlist
46
48
 
47
49
  extension = new_file.extension.to_s
48
- if !whitelisted_extension?(extension)
50
+ if !allowlisted_extension?(allowlist, extension)
49
51
  # 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")
52
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_allowlist_error", extension: new_file.extension.inspect,
53
+ allowed_types: Array(allowlist).join(", "), default: :"errors.messages.extension_whitelist_error")
52
54
  end
53
55
  end
54
56
 
55
- def whitelisted_extension?(extension)
57
+ def allowlisted_extension?(allowlist, extension)
56
58
  downcase_extension = extension.downcase
57
- Array(extension_allowlist).any? { |item| downcase_extension =~ /\A#{item}\z/i }
59
+ Array(allowlist).any? { |item| downcase_extension =~ /\A#{item}\z/i }
58
60
  end
59
- end # ExtensionWhitelist
61
+ end # ExtensionAllowlist
60
62
  end # Uploader
61
63
  end # CarrierWave
@@ -1,10 +1,10 @@
1
1
  module CarrierWave
2
2
  module Uploader
3
- module ExtensionBlacklist
3
+ module ExtensionDenylist
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- before :cache, :check_extension_blacklist!
7
+ before :cache, :check_extension_denylist!
8
8
  end
9
9
 
10
10
  ##
@@ -32,27 +32,32 @@ module CarrierWave
32
32
  # end
33
33
  #
34
34
  def extension_denylist
35
- if respond_to?(:extension_blacklist)
35
+ end
36
+
37
+ private
38
+
39
+ def check_extension_denylist!(new_file)
40
+ denylist = extension_denylist
41
+ if !denylist && respond_to?(:extension_blacklist) && extension_blacklist
36
42
  ActiveSupport::Deprecation.warn "#extension_blacklist is deprecated, use #extension_denylist instead." unless instance_variable_defined?(:@extension_blacklist_warned)
37
43
  @extension_blacklist_warned = true
38
- extension_blacklist
44
+ denylist = extension_blacklist
39
45
  end
40
- end
41
46
 
42
- private
47
+ return unless denylist
43
48
 
44
- def check_extension_blacklist!(new_file)
45
- return unless extension_denylist
49
+ ActiveSupport::Deprecation.warn "Use of #extension_denylist is deprecated for the security reason, use #extension_allowlist instead to explicitly state what are safe to accept" unless instance_variable_defined?(:@extension_denylist_warned)
50
+ @extension_denylist_warned = true
46
51
 
47
52
  extension = new_file.extension.to_s
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")
53
+ if denylisted_extension?(denylist, extension)
54
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_denylist_error", extension: new_file.extension.inspect,
55
+ prohibited_types: Array(extension_denylist).join(", "), default: :"errors.messages.extension_blacklist_error")
51
56
  end
52
57
  end
53
58
 
54
- def blacklisted_extension?(extension)
55
- Array(extension_denylist).any? { |item| extension =~ /\A#{item}\z/i }
59
+ def denylisted_extension?(denylist, extension)
60
+ Array(denylist).any? { |item| extension =~ /\A#{item}\z/i }
56
61
  end
57
62
  end
58
63
  end
@@ -14,7 +14,7 @@ module CarrierWave
14
14
  # are allowed to be uploaded.
15
15
  # === Returns
16
16
  #
17
- # [NilClass, Range] a size range which are permitted to be uploaded
17
+ # [NilClass, Range] a size range (in bytes) which are permitted to be uploaded
18
18
  #
19
19
  # === Examples
20
20
  #
@@ -24,7 +24,7 @@ module CarrierWave
24
24
  #
25
25
  def size_range; end
26
26
 
27
- private
27
+ private
28
28
 
29
29
  def check_size!(new_file)
30
30
  size = new_file.size
@@ -18,7 +18,7 @@ module CarrierWave
18
18
  # Adds a processor callback which applies operations as a file is uploaded.
19
19
  # The argument may be the name of any method of the uploader, expressed as a symbol,
20
20
  # or a list of such methods, or a hash where the key is a method and the value is
21
- # an array of arguments to call the method with
21
+ # an array of arguments to call the method with. Also accepts an :if or :unless condition
22
22
  #
23
23
  # === Parameters
24
24
  #
@@ -31,6 +31,7 @@ module CarrierWave
31
31
  # process :sepiatone, :vignette
32
32
  # process :scale => [200, 200]
33
33
  # process :scale => [200, 200], :if => :image?
34
+ # process :scale => [200, 200], :unless => :disallowed_image_type?
34
35
  # process :sepiatone, :if => :image?
35
36
  #
36
37
  # def sepiatone
@@ -49,6 +50,10 @@ module CarrierWave
49
50
  # ...
50
51
  # end
51
52
  #
53
+ # def disallowed_image_type?
54
+ # ...
55
+ # end
56
+ #
52
57
  # end
53
58
  #
54
59
  def process(*args)
@@ -57,12 +62,17 @@ module CarrierWave
57
62
  hash.merge!(arg)
58
63
  end
59
64
 
60
- condition = new_processors.delete(:if)
65
+ condition_type = new_processors.keys.detect { |key| [:if, :unless].include?(key) }
66
+ condition = new_processors.delete(:if) || new_processors.delete(:unless)
61
67
  new_processors.each do |processor, processor_args|
62
- self.processors += [[processor, processor_args, condition]]
68
+ self.processors += [[processor, processor_args, condition, condition_type]]
69
+
70
+ if processor == :convert
71
+ # Treat :convert specially, since it should trigger the file extension change
72
+ force_extension processor_args
73
+ end
63
74
  end
64
75
  end
65
-
66
76
  end # ClassMethods
67
77
 
68
78
  ##
@@ -72,19 +82,44 @@ module CarrierWave
72
82
  return unless enable_processing
73
83
 
74
84
  with_callbacks(:process, new_file) do
75
- self.class.processors.each do |method, args, condition|
76
- if(condition)
85
+ self.class.processors.each do |method, args, condition, condition_type|
86
+ if condition && condition_type == :if
77
87
  if condition.respond_to?(:call)
78
88
  next unless condition.call(self, :args => args, :method => method, :file => new_file)
79
89
  else
80
90
  next unless self.send(condition, new_file)
81
91
  end
92
+ elsif condition && condition_type == :unless
93
+ if condition.respond_to?(:call)
94
+ next if condition.call(self, :args => args, :method => method, :file => new_file)
95
+ elsif self.send(condition, new_file)
96
+ next
97
+ end
98
+ end
99
+
100
+ if args.is_a? Array
101
+ kwargs, args = args.partition { |arg| arg.is_a? Hash }
102
+ end
103
+
104
+ if kwargs.present?
105
+ kwargs = kwargs.reduce(:merge)
106
+ self.send(method, *args, **kwargs)
107
+ else
108
+ self.send(method, *args)
82
109
  end
83
- self.send(method, *args)
84
110
  end
85
111
  end
86
112
  end
87
113
 
114
+ private
115
+
116
+ def forcing_extension(filename)
117
+ if force_extension && filename
118
+ Pathname.new(filename).sub_ext(".#{force_extension.to_s.delete_prefix('.')}").to_s
119
+ else
120
+ filename
121
+ end
122
+ end
88
123
  end # Processing
89
124
  end # Uploader
90
125
  end # CarrierWave
@@ -30,7 +30,20 @@ module CarrierWave
30
30
  # [String] uniquely identifies a file
31
31
  #
32
32
  def identifier
33
- @identifier || storage.try(:identifier)
33
+ @identifier || (file && storage.try(:identifier))
34
+ end
35
+
36
+ ##
37
+ # Returns a String which is to be used as a temporary value which gets assigned to the column.
38
+ # The purpose is to mark the column that it will be updated. Finally before the save it will be
39
+ # overwritten by the #identifier value, which is usually #filename.
40
+ #
41
+ # === Returns
42
+ #
43
+ # [String] a temporary_identifier, by default @original_filename is used
44
+ #
45
+ def temporary_identifier
46
+ @original_filename || @identifier
34
47
  end
35
48
 
36
49
  ##
@@ -40,8 +53,8 @@ module CarrierWave
40
53
  #
41
54
  # [String] contents of the file
42
55
  #
43
- def read
44
- file.try(:read)
56
+ def read(*args)
57
+ file.try(:read, *args)
45
58
  end
46
59
 
47
60
  ##