carrierwave 3.0.0.beta → 3.0.0.rc

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -64
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -2
  4. data/lib/carrierwave/downloader/base.rb +12 -11
  5. data/lib/carrierwave/downloader/remote_file.rb +5 -6
  6. data/lib/carrierwave/locale/en.yml +4 -0
  7. data/lib/carrierwave/mount.rb +31 -82
  8. data/lib/carrierwave/mounter.rb +115 -50
  9. data/lib/carrierwave/orm/activerecord.rb +8 -59
  10. data/lib/carrierwave/processing/mini_magick.rb +13 -11
  11. data/lib/carrierwave/processing/rmagick.rb +7 -11
  12. data/lib/carrierwave/processing/vips.rb +9 -9
  13. data/lib/carrierwave/sanitized_file.rb +47 -37
  14. data/lib/carrierwave/storage/abstract.rb +5 -5
  15. data/lib/carrierwave/storage/file.rb +4 -3
  16. data/lib/carrierwave/storage/fog.rb +69 -50
  17. data/lib/carrierwave/test/matchers.rb +11 -7
  18. data/lib/carrierwave/uploader/cache.rb +17 -9
  19. data/lib/carrierwave/uploader/callbacks.rb +1 -1
  20. data/lib/carrierwave/uploader/configuration.rb +8 -4
  21. data/lib/carrierwave/uploader/dimension.rb +66 -0
  22. data/lib/carrierwave/uploader/file_size.rb +2 -2
  23. data/lib/carrierwave/uploader/processing.rb +21 -7
  24. data/lib/carrierwave/uploader/proxy.rb +16 -3
  25. data/lib/carrierwave/uploader/store.rb +43 -6
  26. data/lib/carrierwave/uploader/url.rb +1 -1
  27. data/lib/carrierwave/uploader/versions.rb +126 -134
  28. data/lib/carrierwave/uploader.rb +2 -0
  29. data/lib/carrierwave/utilities/file_name.rb +2 -2
  30. data/lib/carrierwave/utilities/uri.rb +14 -11
  31. data/lib/carrierwave/validations/active_model.rb +4 -6
  32. data/lib/carrierwave/version.rb +1 -1
  33. data/lib/carrierwave.rb +8 -7
  34. data/lib/generators/uploader_generator.rb +3 -3
  35. metadata +18 -3
  36. /data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +0 -0
@@ -2,18 +2,49 @@ module CarrierWave
2
2
 
3
3
  # this is an internal class, used by CarrierWave::Mount so that
4
4
  # we don't pollute the model with a lot of methods.
5
- class Mounter #:nodoc:
6
- attr_reader :column, :record, :remote_urls, :integrity_errors,
7
- :processing_errors, :download_errors
8
- attr_accessor :remove, :remote_request_headers
5
+ class Mounter # :nodoc:
6
+ class Single < Mounter # :nodoc
7
+ def identifier
8
+ uploaders.first&.identifier
9
+ end
10
+
11
+ def temporary_identifier
12
+ temporary_identifiers.first
13
+ end
14
+ end
15
+
16
+ class Multiple < Mounter # :nodoc
17
+ def identifier
18
+ uploaders.map(&:identifier).presence
19
+ end
20
+
21
+ def temporary_identifier
22
+ temporary_identifiers.presence
23
+ end
24
+ end
25
+
26
+ def self.build(record, column)
27
+ if record.class.uploader_options[column][:multiple]
28
+ Multiple.new(record, column)
29
+ else
30
+ Single.new(record, column)
31
+ end
32
+ end
33
+
34
+ attr_reader :column, :record, :remote_urls, :remove,
35
+ :integrity_errors, :processing_errors, :download_errors
36
+ attr_accessor :remote_request_headers, :uploader_options
9
37
 
10
- def initialize(record, column, options={})
38
+ def initialize(record, column)
11
39
  @record = record
12
40
  @column = column
13
41
  @options = record.class.uploader_options[column]
14
42
  @download_errors = []
15
43
  @processing_errors = []
16
44
  @integrity_errors = []
45
+
46
+ @removed_uploaders = []
47
+ @added_uploaders = []
17
48
  end
18
49
 
19
50
  def uploader_class
@@ -35,7 +66,7 @@ module CarrierWave
35
66
  def uploaders
36
67
  @uploaders ||= read_identifiers.map do |identifier|
37
68
  uploader = blank_uploader
38
- uploader.retrieve_from_store!(identifier) if identifier.present?
69
+ uploader.retrieve_from_store!(identifier)
39
70
  uploader
40
71
  end
41
72
  end
@@ -46,7 +77,7 @@ module CarrierWave
46
77
  @uploaders = new_files.map do |new_file|
47
78
  handle_error do
48
79
  if new_file.is_a?(String)
49
- if (uploader = old_uploaders.detect { |uploader| uploader.identifier == new_file })
80
+ if (uploader = old_uploaders.detect { |old_uploader| old_uploader.identifier == new_file })
50
81
  uploader.staged = true
51
82
  uploader
52
83
  else
@@ -64,7 +95,9 @@ module CarrierWave
64
95
  uploader
65
96
  end
66
97
  end
67
- end.compact
98
+ end.reject(&:blank?)
99
+ @removed_uploaders += (old_uploaders - @uploaders)
100
+ write_temporary_identifier
68
101
  end
69
102
 
70
103
  def cache_names
@@ -76,33 +109,51 @@ module CarrierWave
76
109
  return if cache_names.blank?
77
110
  clear_unstaged
78
111
  cache_names.each do |cache_name|
79
- begin
80
- uploader = blank_uploader
81
- uploader.retrieve_from_cache!(cache_name)
82
- @uploaders << uploader
83
- rescue CarrierWave::InvalidParameter
84
- # ignore
85
- end
112
+ uploader = blank_uploader
113
+ uploader.retrieve_from_cache!(cache_name)
114
+ @uploaders << uploader
115
+ rescue CarrierWave::InvalidParameter
116
+ # ignore
86
117
  end
118
+ write_temporary_identifier
87
119
  end
88
120
 
89
121
  def remote_urls=(urls)
90
- return if urls.blank? || urls.all?(&:blank?)
91
-
122
+ if urls.nil?
123
+ urls = []
124
+ else
125
+ urls = Array.wrap(urls).reject(&:blank?)
126
+ return if urls.blank?
127
+ end
92
128
  @remote_urls = urls
93
129
 
94
130
  clear_unstaged
95
- urls.zip(remote_request_headers || []) do |url, header|
131
+ @remote_urls.zip(remote_request_headers || []) do |url, header|
96
132
  handle_error do
97
133
  uploader = blank_uploader
98
134
  uploader.download!(url, header || {})
99
135
  @uploaders << uploader
100
136
  end
101
137
  end
138
+ write_temporary_identifier
102
139
  end
103
140
 
104
141
  def store!
105
- uploaders.reject(&:blank?).each(&:store!)
142
+ additions, remains = uploaders.partition(&:cached?)
143
+ existing_paths = (@removed_uploaders + remains).map(&:store_path)
144
+ additions.each do |uploader|
145
+ uploader.deduplicate(existing_paths)
146
+ uploader.store!
147
+ existing_paths << uploader.store_path
148
+ end
149
+ @added_uploaders += additions
150
+ end
151
+
152
+ def write_identifier
153
+ return if record.frozen?
154
+
155
+ clear! if remove?
156
+ record.write_uploader(serialization_column, identifier)
106
157
  end
107
158
 
108
159
  def urls(*args)
@@ -113,52 +164,50 @@ module CarrierWave
113
164
  uploaders.none?(&:present?)
114
165
  end
115
166
 
167
+ def remove=(value)
168
+ @remove = value
169
+ write_temporary_identifier
170
+ end
171
+
116
172
  def remove?
117
- remove.present? && (remove == true || remove !~ /\A0|false$\z/)
173
+ remove.present? && (remove.to_s !~ /\A0|false$\z/)
118
174
  end
119
175
 
120
176
  def remove!
121
- uploaders.reject(&:blank?).each(&:remove!)
122
- @uploaders = []
177
+ uploaders.each(&:remove!)
178
+ clear!
123
179
  end
124
180
 
125
181
  def clear!
182
+ @removed_uploaders += uploaders
183
+ @remove = nil
126
184
  @uploaders = []
127
185
  end
128
186
 
187
+ def reset_changes!
188
+ @removed_uploaders = []
189
+ @added_uploaders = []
190
+ end
191
+
129
192
  def serialization_column
130
193
  option(:mount_on) || column
131
194
  end
132
195
 
133
- def remove_previous(before=nil, after=nil)
134
- after ||= []
135
- return unless before
136
-
137
- # both 'before' and 'after' can be string when 'mount_on' option is set
138
- before = before.reject(&:blank?).map do |value|
139
- if value.is_a?(String)
140
- uploader = blank_uploader
141
- uploader.retrieve_from_store!(value)
142
- uploader
143
- else
144
- value
145
- end
146
- end
147
- after_paths = after.reject(&:blank?).map do |value|
148
- if value.is_a?(String)
149
- uploader = blank_uploader
150
- uploader.retrieve_from_store!(value)
151
- uploader
152
- else
153
- value
154
- end.path
155
- end
156
- before.each do |uploader|
157
- uploader.remove! if uploader.remove_previously_stored_files_after_update && !after_paths.include?(uploader.path)
158
- end
196
+ def remove_previous
197
+ current_paths = uploaders.map(&:path)
198
+ @removed_uploaders
199
+ .reject {|uploader| current_paths.include?(uploader.path) }
200
+ .each { |uploader| uploader.remove! if uploader.remove_previously_stored_files_after_update }
201
+ reset_changes!
159
202
  end
160
203
 
161
- attr_accessor :uploader_options
204
+ def remove_added
205
+ current_paths = (@removed_uploaders + @uploaders.select(&:staged)).map(&:path)
206
+ @added_uploaders
207
+ .reject {|uploader| current_paths.include?(uploader.path) }
208
+ .each { |uploader| uploader.remove! }
209
+ reset_changes!
210
+ end
162
211
 
163
212
  private
164
213
 
@@ -169,7 +218,9 @@ module CarrierWave
169
218
 
170
219
  def clear_unstaged
171
220
  @uploaders ||= []
172
- @uploaders.keep_if(&:staged)
221
+ staged, unstaged = @uploaders.partition(&:staged)
222
+ @uploaders = staged
223
+ @removed_uploaders += unstaged
173
224
  end
174
225
 
175
226
  def handle_error
@@ -184,5 +235,19 @@ module CarrierWave
184
235
  @integrity_errors << e
185
236
  raise e unless option(:ignore_integrity_errors)
186
237
  end
238
+
239
+ def write_temporary_identifier
240
+ return if record.frozen?
241
+
242
+ record.write_uploader(serialization_column, temporary_identifier)
243
+ end
244
+
245
+ def temporary_identifiers
246
+ if remove?
247
+ []
248
+ else
249
+ uploaders.map { |uploader| uploader.temporary_identifier }
250
+ end
251
+ end
187
252
  end # Mounter
188
253
  end # CarrierWave
@@ -6,40 +6,6 @@ module CarrierWave
6
6
 
7
7
  include CarrierWave::Mount
8
8
 
9
- ##
10
- # See +CarrierWave::Mount#mount_uploader+ for documentation
11
- #
12
- def mount_uploader(column, uploader=nil, options={}, &block)
13
- super
14
-
15
- mod = Module.new
16
- prepend mod
17
- mod.class_eval <<-RUBY, __FILE__, __LINE__+1
18
- def remote_#{column}_url=(url)
19
- column = _mounter(:#{column}).serialization_column
20
- __send__(:"\#{column}_will_change!")
21
- super
22
- end
23
- RUBY
24
- end
25
-
26
- ##
27
- # See +CarrierWave::Mount#mount_uploaders+ for documentation
28
- #
29
- def mount_uploaders(column, uploader=nil, options={}, &block)
30
- super
31
-
32
- mod = Module.new
33
- prepend mod
34
- mod.class_eval <<-RUBY, __FILE__, __LINE__+1
35
- def remote_#{column}_urls=(url)
36
- column = _mounter(:#{column}).serialization_column
37
- __send__(:"\#{column}_will_change!")
38
- super
39
- end
40
- RUBY
41
- end
42
-
43
9
  private
44
10
 
45
11
  def mount_base(column, uploader=nil, options={}, &block)
@@ -61,7 +27,6 @@ module CarrierWave
61
27
  after_commit :"remove_#{column}!", :on => :destroy
62
28
  after_commit :"mark_remove_#{column}_false", :on => :update
63
29
 
64
- after_save :"store_previous_changes_for_#{column}"
65
30
  after_commit :"reset_previous_changes_for_#{column}"
66
31
  after_commit :"remove_previously_stored_#{column}", :on => :update
67
32
  after_rollback :"remove_rolled_back_#{column}"
@@ -69,29 +34,6 @@ module CarrierWave
69
34
  mod = Module.new
70
35
  prepend mod
71
36
  mod.class_eval <<-RUBY, __FILE__, __LINE__+1
72
- def #{column}=(new_file)
73
- column = _mounter(:#{column}).serialization_column
74
- if !(new_file.blank? && __send__(:#{column}).blank?)
75
- __send__(:"\#{column}_will_change!")
76
- end
77
-
78
- super
79
- end
80
-
81
- def remove_#{column}=(value)
82
- column = _mounter(:#{column}).serialization_column
83
- result = super
84
- __send__(:"\#{column}_will_change!") if _mounter(:#{column}).remove?
85
- result
86
- end
87
-
88
- def remove_#{column}!
89
- self.remove_#{column} = true
90
- write_#{column}_identifier
91
- self.remove_#{column} = false
92
- super
93
- end
94
-
95
37
  # Reset cached mounter on record reload
96
38
  def reload(*)
97
39
  @_mounters = nil
@@ -100,7 +42,14 @@ module CarrierWave
100
42
 
101
43
  # Reset cached mounter on record dup
102
44
  def initialize_dup(other)
103
- @_mounters = nil
45
+ old_uploaders = _mounter(:"#{column}").uploaders
46
+ @_mounters[:"#{column}"] = nil
47
+ super
48
+ _mounter(:"#{column}").cache(old_uploaders)
49
+ end
50
+
51
+ def write_#{column}_identifier
52
+ return unless has_attribute?(_mounter(:#{column}).serialization_column)
104
53
  super
105
54
  end
106
55
  RUBY
@@ -263,7 +263,8 @@ module CarrierWave
263
263
  FileUtils.mv image.path, current_path
264
264
 
265
265
  image.run_command("identify", current_path)
266
- rescue ::MiniMagick::Error, ::MiniMagick::Invalid
266
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
267
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
267
268
  message = I18n.translate(:"errors.messages.processing_error")
268
269
  raise CarrierWave::ProcessingError, message
269
270
  ensure
@@ -309,23 +310,24 @@ module CarrierWave
309
310
  file.content_type = Marcel::Magic.by_path(move_to).try(:type)
310
311
  file.move_to(move_to, permissions, directory_permissions)
311
312
  end
312
- rescue ::MiniMagick::Error, ::MiniMagick::Invalid
313
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
314
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
313
315
  message = I18n.translate(:"errors.messages.processing_error")
314
316
  raise CarrierWave::ProcessingError, message
315
317
  end
316
318
 
317
- private
319
+ private
318
320
 
319
- def resolve_dimensions(*dimensions)
320
- dimensions.map do |value|
321
- next value unless value.instance_of?(Proc)
322
- value.arity >= 1 ? value.call(self) : value.call
323
- end
321
+ def resolve_dimensions(*dimensions)
322
+ dimensions.map do |value|
323
+ next value unless value.instance_of?(Proc)
324
+ value.arity >= 1 ? value.call(self) : value.call
324
325
  end
326
+ end
325
327
 
326
- def mini_magick_image
327
- ::MiniMagick::Image.read(read)
328
- end
328
+ def mini_magick_image
329
+ ::MiniMagick::Image.read(read)
330
+ end
329
331
 
330
332
  end # MiniMagick
331
333
  end # CarrierWave
@@ -62,10 +62,12 @@ module CarrierWave
62
62
  begin
63
63
  require "rmagick"
64
64
  rescue LoadError
65
- require "RMagick"
66
- rescue LoadError => e
67
- e.message << " (You may need to install the rmagick gem)"
68
- raise e
65
+ begin
66
+ require "RMagick"
67
+ rescue LoadError => e
68
+ e.message << " (You may need to install the rmagick gem)"
69
+ raise e
70
+ end
69
71
  end
70
72
 
71
73
  prepend Module.new {
@@ -228,13 +230,7 @@ module CarrierWave
228
230
  height = dimension_from height
229
231
  manipulate! do |img|
230
232
  img.resize_to_fit!(width, height)
231
- new_img = ::Magick::Image.new(width, height) { |img| img.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
232
- if background == :transparent
233
- filled = new_img.matte_floodfill(1, 1)
234
- else
235
- filled = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
236
- end
237
- destroy_image(new_img)
233
+ filled = ::Magick::Image.new(width, height) { |image| image.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
238
234
  filled.composite!(img, gravity, ::Magick::OverCompositeOp)
239
235
  destroy_image(img)
240
236
  filled = yield(filled) if block_given?
@@ -267,18 +267,18 @@ module CarrierWave
267
267
  raise CarrierWave::ProcessingError, message
268
268
  end
269
269
 
270
- private
270
+ private
271
271
 
272
- def resolve_dimensions(*dimensions)
273
- dimensions.map do |value|
274
- next value unless value.instance_of?(Proc)
275
- value.arity >= 1 ? value.call(self) : value.call
276
- end
272
+ def resolve_dimensions(*dimensions)
273
+ dimensions.map do |value|
274
+ next value unless value.instance_of?(Proc)
275
+ value.arity >= 1 ? value.call(self) : value.call
277
276
  end
277
+ end
278
278
 
279
- def vips_image
280
- ::Vips::Image.new_from_buffer(read, "")
281
- end
279
+ def vips_image
280
+ ::Vips::Image.new_from_buffer(read, "")
281
+ end
282
282
 
283
283
  end # Vips
284
284
  end # CarrierWave
@@ -27,7 +27,7 @@ module CarrierWave
27
27
 
28
28
  def initialize(file)
29
29
  self.file = file
30
- @content = nil
30
+ @content = @content_type = nil
31
31
  end
32
32
 
33
33
  ##
@@ -39,7 +39,7 @@ module CarrierWave
39
39
  #
40
40
  def original_filename
41
41
  return @original_filename if @original_filename
42
- if @file and @file.respond_to?(:original_filename)
42
+ if @file && @file.respond_to?(:original_filename)
43
43
  @file.original_filename
44
44
  elsif path
45
45
  File.basename(path)
@@ -109,7 +109,7 @@ module CarrierWave
109
109
  # [Boolean] whether the file is valid and has a non-zero size
110
110
  #
111
111
  def empty?
112
- @file.nil? || self.size.nil? || (self.size.zero? && ! self.exists?)
112
+ @file.nil? || self.size.nil? || (self.size.zero? && !self.exists?)
113
113
  end
114
114
 
115
115
  ##
@@ -128,14 +128,20 @@ module CarrierWave
128
128
  #
129
129
  # [String] contents of the file
130
130
  #
131
- def read
131
+ def read(*args)
132
132
  if @content
133
- @content
133
+ if args.empty?
134
+ @content
135
+ else
136
+ length, outbuf = args
137
+ raise ArgumentError, "outbuf argument not supported since the content is already loaded" if outbuf
138
+ @content[0, length]
139
+ end
134
140
  elsif is_path?
135
- File.open(@file, "rb") {|file| file.read}
141
+ File.open(@file, "rb") {|file| file.read(*args)}
136
142
  else
137
143
  @file.try(:rewind)
138
- @content = @file.read
144
+ @content = @file.read(*args)
139
145
  @file.try(:close) unless @file.class.ancestors.include?(::StringIO) || @file.try(:closed?)
140
146
  @content
141
147
  end
@@ -157,13 +163,10 @@ module CarrierWave
157
163
  mkdir!(new_path, directory_permissions)
158
164
  move!(new_path)
159
165
  chmod!(new_path, permissions)
160
- if keep_filename
161
- self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
162
- else
163
- self.file = {:tempfile => new_path, :content_type => @content_type}
164
- end
166
+ self.file = {tempfile: new_path, filename: keep_filename ? original_filename : nil, content_type: declared_content_type}
165
167
  self
166
168
  end
169
+
167
170
  ##
168
171
  # Helper to move file to new path.
169
172
  #
@@ -195,7 +198,7 @@ module CarrierWave
195
198
  mkdir!(new_path, directory_permissions)
196
199
  copy!(new_path)
197
200
  chmod!(new_path, permissions)
198
- self.class.new({:tempfile => new_path, :content_type => content_type})
201
+ self.class.new({tempfile: new_path, content_type: declared_content_type})
199
202
  end
200
203
 
201
204
  ##
@@ -237,9 +240,10 @@ module CarrierWave
237
240
  #
238
241
  def content_type
239
242
  @content_type ||=
240
- existing_content_type ||
241
- marcel_magic_by_mime_type ||
242
- marcel_magic_by_path
243
+ identified_content_type ||
244
+ declared_content_type ||
245
+ guessed_safe_content_type ||
246
+ Marcel::MimeType::BINARY
243
247
  end
244
248
 
245
249
  ##
@@ -270,11 +274,11 @@ module CarrierWave
270
274
  if file.is_a?(Hash)
271
275
  @file = file["tempfile"] || file[:tempfile]
272
276
  @original_filename = file["filename"] || file[:filename]
273
- @content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
277
+ @declared_content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
274
278
  else
275
279
  @file = file
276
280
  @original_filename = nil
277
- @content_type = nil
281
+ @declared_content_type = nil
278
282
  end
279
283
  end
280
284
 
@@ -294,39 +298,45 @@ module CarrierWave
294
298
  name = name.scrub
295
299
  name = name.tr("\\", "/") # work-around for IE
296
300
  name = File.basename(name)
297
- name = name.gsub(sanitize_regexp,"_")
301
+ name = name.gsub(sanitize_regexp, "_")
298
302
  name = "_#{name}" if name =~ /\A\.+\z/
299
303
  name = "unnamed" if name.size.zero?
300
- return name.mb_chars.to_s
304
+ name.mb_chars.to_s
301
305
  end
302
306
 
303
- def existing_content_type
304
- if @file.respond_to?(:content_type) && @file.content_type
305
- @file.content_type.to_s.chomp
306
- end
307
+ def declared_content_type
308
+ @declared_content_type ||
309
+ if @file.respond_to?(:content_type) && @file.content_type
310
+ @file.content_type.to_s.chomp
311
+ end
307
312
  end
308
313
 
309
- def marcel_magic_by_mime_type
314
+ # Guess content type from its file extension. Limit what to be returned to prevent spoofing.
315
+ def guessed_safe_content_type
310
316
  return unless path
311
317
 
312
- type = File.open(path) do |file|
313
- Marcel::Magic.by_magic(file).try(:type)
314
- end
318
+ type = Marcel::Magic.by_path(original_filename).to_s
319
+ type if type.start_with?('text/') || type.start_with?('application/json')
320
+ end
315
321
 
316
- if type.nil?
317
- type = Marcel::Magic.by_path(path).try(:type)
318
- type = 'invalid/invalid' unless type.nil? || type.start_with?('text/') || type.start_with?('application/json')
322
+ def identified_content_type
323
+ with_io do |io|
324
+ Marcel::Magic.by_magic(io).try(:type)
319
325
  end
320
-
321
- type
322
326
  rescue Errno::ENOENT
323
327
  nil
324
328
  end
325
329
 
326
- def marcel_magic_by_path
327
- return unless path
328
-
329
- Marcel::Magic.by_path(path).to_s
330
+ def with_io(&block)
331
+ if file.is_a?(IO)
332
+ begin
333
+ yield file
334
+ ensure
335
+ file.try(:rewind)
336
+ end
337
+ elsif path
338
+ File.open(path, &block)
339
+ end
330
340
  end
331
341
  end # SanitizedFile
332
342
  end # CarrierWave
@@ -14,7 +14,7 @@ module CarrierWave
14
14
  end
15
15
 
16
16
  def identifier
17
- uploader.filename
17
+ uploader.deduplicated_filename
18
18
  end
19
19
 
20
20
  def store!(file)
@@ -24,19 +24,19 @@ module CarrierWave
24
24
  end
25
25
 
26
26
  def cache!(new_file)
27
- raise NotImplementedError.new("Need to implement #cache! if you want to use #{self.class.name} as a cache storage.")
27
+ raise NotImplementedError, "Need to implement #cache! if you want to use #{self.class.name} as a cache storage."
28
28
  end
29
29
 
30
30
  def retrieve_from_cache!(identifier)
31
- raise NotImplementedError.new("Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage.")
31
+ raise NotImplementedError, "Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage."
32
32
  end
33
33
 
34
34
  def delete_dir!(path)
35
- raise NotImplementedError.new("Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage.")
35
+ raise NotImplementedError, "Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage."
36
36
  end
37
37
 
38
38
  def clean_cache!(seconds)
39
- raise NotImplementedError.new("Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage.")
39
+ raise NotImplementedError, "Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage."
40
40
  end
41
41
  end # Abstract
42
42
  end # Storage
@@ -109,10 +109,11 @@ module CarrierWave
109
109
  end
110
110
 
111
111
  def clean_cache!(seconds)
112
- Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), CarrierWave.root)).each do |dir|
112
+ Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), uploader.root)).each do |dir|
113
113
  # generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
114
- time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
115
- time = Time.at(*time)
114
+ matched = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first
115
+ next unless matched
116
+ time = Time.at(matched[0].to_i)
116
117
  if time < (Time.now.utc - seconds)
117
118
  FileUtils.rm_rf(dir)
118
119
  end