carrierwave 0.10.0 → 1.2.0

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +248 -116
  3. data/lib/carrierwave/compatibility/paperclip.rb +0 -2
  4. data/lib/carrierwave/error.rb +1 -0
  5. data/lib/carrierwave/locale/en.yml +7 -4
  6. data/lib/carrierwave/mount.rb +219 -176
  7. data/lib/carrierwave/mounter.rb +165 -0
  8. data/lib/carrierwave/orm/activerecord.rb +50 -21
  9. data/lib/carrierwave/processing/mini_magick.rb +82 -15
  10. data/lib/carrierwave/processing/rmagick.rb +47 -4
  11. data/lib/carrierwave/processing.rb +0 -1
  12. data/lib/carrierwave/sanitized_file.rb +38 -18
  13. data/lib/carrierwave/storage/abstract.rb +15 -2
  14. data/lib/carrierwave/storage/file.rb +65 -2
  15. data/lib/carrierwave/storage/fog.rb +106 -31
  16. data/lib/carrierwave/storage.rb +0 -7
  17. data/lib/carrierwave/test/matchers.rb +77 -12
  18. data/lib/carrierwave/uploader/cache.rb +58 -30
  19. data/lib/carrierwave/uploader/callbacks.rb +0 -2
  20. data/lib/carrierwave/uploader/configuration.rb +51 -8
  21. data/lib/carrierwave/uploader/content_type_blacklist.rb +48 -0
  22. data/lib/carrierwave/uploader/content_type_whitelist.rb +48 -0
  23. data/lib/carrierwave/uploader/default_url.rb +3 -5
  24. data/lib/carrierwave/uploader/download.rb +15 -8
  25. data/lib/carrierwave/uploader/extension_blacklist.rb +14 -10
  26. data/lib/carrierwave/uploader/extension_whitelist.rb +13 -10
  27. data/lib/carrierwave/uploader/file_size.rb +43 -0
  28. data/lib/carrierwave/uploader/magic_mime_blacklist.rb +94 -0
  29. data/lib/carrierwave/uploader/magic_mime_whitelist.rb +94 -0
  30. data/lib/carrierwave/uploader/mountable.rb +7 -8
  31. data/lib/carrierwave/uploader/processing.rb +10 -10
  32. data/lib/carrierwave/uploader/proxy.rb +5 -7
  33. data/lib/carrierwave/uploader/remove.rb +0 -2
  34. data/lib/carrierwave/uploader/serialization.rb +1 -3
  35. data/lib/carrierwave/uploader/store.rb +5 -23
  36. data/lib/carrierwave/uploader/url.rb +3 -5
  37. data/lib/carrierwave/uploader/versions.rb +75 -82
  38. data/lib/carrierwave/uploader.rb +6 -2
  39. data/lib/carrierwave/utilities/uri.rb +5 -6
  40. data/lib/carrierwave/utilities.rb +0 -3
  41. data/lib/carrierwave/validations/active_model.rb +3 -5
  42. data/lib/carrierwave/version.rb +1 -1
  43. data/lib/carrierwave.rb +12 -10
  44. data/lib/generators/templates/uploader.rb +4 -6
  45. metadata +37 -61
  46. data/lib/carrierwave/locale/cs.yml +0 -11
  47. data/lib/carrierwave/locale/de.yml +0 -11
  48. data/lib/carrierwave/locale/el.yml +0 -11
  49. data/lib/carrierwave/locale/es.yml +0 -11
  50. data/lib/carrierwave/locale/fr.yml +0 -11
  51. data/lib/carrierwave/locale/ja.yml +0 -11
  52. data/lib/carrierwave/locale/nb.yml +0 -11
  53. data/lib/carrierwave/locale/nl.yml +0 -11
  54. data/lib/carrierwave/locale/pl.yml +0 -11
  55. data/lib/carrierwave/locale/pt-BR.yml +0 -11
  56. data/lib/carrierwave/locale/pt-PT.yml +0 -11
  57. data/lib/carrierwave/locale/ru.yml +0 -11
  58. data/lib/carrierwave/locale/sk.yml +0 -11
  59. data/lib/carrierwave/locale/tr.yml +0 -11
  60. data/lib/carrierwave/processing/mime_types.rb +0 -74
  61. data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Compatibility
5
3
 
@@ -4,4 +4,5 @@ module CarrierWave
4
4
  class InvalidParameter < UploadError; end
5
5
  class ProcessingError < UploadError; end
6
6
  class DownloadError < UploadError; end
7
+ class UnknownStorageError < StandardError; end
7
8
  end
@@ -4,8 +4,11 @@ en:
4
4
  carrierwave_processing_error: failed to be processed
5
5
  carrierwave_integrity_error: is not of an allowed file type
6
6
  carrierwave_download_error: could not be downloaded
7
- extension_white_list_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
8
- extension_black_list_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
9
- rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image? Original Error: %{e}"
10
- mime_types_processing_error: "Failed to process file with MIME::Types, maybe not valid content-type? Original Error: %{e}"
7
+ extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
8
+ extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
9
+ content_type_whitelist_error: "You are not allowed to upload %{content_type} files"
10
+ content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
11
+ rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
11
12
  mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
13
+ min_size_error: "File size should be greater than %{min_size}"
14
+ max_size_error: "File size should be less than %{max_size}"
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
 
5
3
  ##
@@ -62,7 +60,7 @@ module CarrierWave
62
60
  # will create an anonymous uploader class.
63
61
  #
64
62
  # Passing a block makes it possible to customize the uploader. This can be
65
- # convenient for brevity, but if there is any significatnt logic in the
63
+ # convenient for brevity, but if there is any significant logic in the
66
64
  # uploader, you should do the right thing and have it in its own file.
67
65
  #
68
66
  # === Added instance methods
@@ -92,7 +90,6 @@ module CarrierWave
92
90
  # [image_processing_error] Returns an error object if the last file to be assigned caused a processing error
93
91
  # [image_download_error] Returns an error object if the last file to be remotely assigned caused a download error
94
92
  #
95
- # [write_image_identifier] Uses the write_uploader method to set the identifier.
96
93
  # [image_identifier] Reads out the identifier of the file
97
94
  #
98
95
  # === Parameters
@@ -135,56 +132,247 @@ module CarrierWave
135
132
  # end
136
133
  #
137
134
  def mount_uploader(column, uploader=nil, options={}, &block)
138
- include CarrierWave::Mount::Extension
139
-
140
- uploader = build_uploader(uploader, &block)
141
- uploaders[column.to_sym] = uploader
142
- uploader_options[column.to_sym] = options
143
-
144
- # Make sure to write over accessors directly defined on the class.
145
- # Simply super to the included module below.
146
- class_eval <<-RUBY, __FILE__, __LINE__+1
147
- def #{column}; super; end
148
- def #{column}=(new_file); super; end
149
- RUBY
135
+ mount_base(column, uploader, options, &block)
150
136
 
151
- # Mixing this in as a Module instead of class_evaling directly, so we
152
- # can maintain the ability to super to any of these methods from within
153
- # the class.
154
137
  mod = Module.new
155
138
  include mod
156
139
  mod.class_eval <<-RUBY, __FILE__, __LINE__+1
157
140
 
158
141
  def #{column}
159
- _mounter(:#{column}).uploader
142
+ _mounter(:#{column}).uploaders[0] ||= _mounter(:#{column}).blank_uploader
160
143
  end
161
144
 
162
145
  def #{column}=(new_file)
163
- _mounter(:#{column}).cache(new_file)
164
- end
165
-
166
- def #{column}?
167
- _mounter(:#{column}).present?
146
+ _mounter(:#{column}).cache([new_file])
168
147
  end
169
148
 
170
149
  def #{column}_url(*args)
171
- _mounter(:#{column}).url(*args)
150
+ #{column}.url(*args)
172
151
  end
173
152
 
174
153
  def #{column}_cache
175
- _mounter(:#{column}).cache_name
154
+ _mounter(:#{column}).cache_names[0]
176
155
  end
177
156
 
178
157
  def #{column}_cache=(cache_name)
179
- _mounter(:#{column}).cache_name = cache_name
158
+ _mounter(:#{column}).cache_names = [cache_name]
180
159
  end
181
160
 
182
161
  def remote_#{column}_url
183
- _mounter(:#{column}).remote_url
162
+ [_mounter(:#{column}).remote_urls].flatten[0]
184
163
  end
185
164
 
186
165
  def remote_#{column}_url=(url)
187
- _mounter(:#{column}).remote_url = url
166
+ _mounter(:#{column}).remote_urls = [url]
167
+ end
168
+
169
+ def remote_#{column}_request_header=(header)
170
+ _mounter(:#{column}).remote_request_headers = [header]
171
+ end
172
+
173
+ def write_#{column}_identifier
174
+ return if frozen?
175
+ mounter = _mounter(:#{column})
176
+
177
+ if mounter.remove?
178
+ write_uploader(mounter.serialization_column, nil)
179
+ elsif mounter.identifiers.first
180
+ write_uploader(mounter.serialization_column, mounter.identifiers.first)
181
+ end
182
+ end
183
+
184
+ def #{column}_identifier
185
+ _mounter(:#{column}).read_identifiers[0]
186
+ end
187
+
188
+ def store_previous_changes_for_#{column}
189
+ attribute_changes = ::ActiveRecord.version.to_s.to_f >= 5.1 ? saved_changes : changes
190
+ @_previous_changes_for_#{column} = attribute_changes[_mounter(:#{column}).serialization_column]
191
+ end
192
+
193
+ def remove_previously_stored_#{column}
194
+ before, after = @_previous_changes_for_#{column}
195
+ _mounter(:#{column}).remove_previous([before], [after])
196
+ end
197
+ RUBY
198
+ end
199
+
200
+ ##
201
+ # Mounts the given uploader on the given array column. This means that
202
+ # assigning and reading from the array column will upload and retrieve
203
+ # multiple files. Supposing that a User class has an uploader mounted on
204
+ # images, and that images can store an array, for example it could be a
205
+ # PostgreSQL JSON column. You can assign and retrieve files like this:
206
+ #
207
+ # @user.images # => []
208
+ # @user.images = [some_file_object]
209
+ # @user.images # => [<Uploader>]
210
+ #
211
+ # @user.images[0].url # => '/some_url.png'
212
+ #
213
+ # It is also possible (but not recommended) to omit the uploader, which
214
+ # will create an anonymous uploader class.
215
+ #
216
+ # Passing a block makes it possible to customize the uploader. This can be
217
+ # convenient for brevity, but if there is any significant logic in the
218
+ # uploader, you should do the right thing and have it in its own file.
219
+ #
220
+ # === Added instance methods
221
+ #
222
+ # Supposing a class has used +mount_uploaders+ to mount an uploader on a column
223
+ # named +images+, in that case the following methods will be added to the class:
224
+ #
225
+ # [images] Returns an array of uploaders for each uploaded file
226
+ # [images=] Caches the given files
227
+ #
228
+ # [images_urls] Returns the urls to the uploaded files
229
+ #
230
+ # [images_cache] Returns a string that identifies the cache location of the files
231
+ # [images_cache=] Retrieves the files from the cache based on the given cache name
232
+ #
233
+ # [remote_image_urls] Returns previously cached remote urls
234
+ # [remote_image_urls=] Retrieve files from the given remote urls
235
+ #
236
+ # [remove_images] An attribute reader that can be used with a checkbox to mark the files for removal
237
+ # [remove_images=] An attribute writer that can be used with a checkbox to mark the files for removal
238
+ # [remove_images?] Whether the files should be removed when store_image! is called.
239
+ #
240
+ # [store_images!] Stores all files that have been assigned with +images=+
241
+ # [remove_images!] Removes the uploaded file from the filesystem.
242
+ #
243
+ # [images_integrity_error] Returns an error object if the last files to be assigned caused an integrity error
244
+ # [images_processing_error] Returns an error object if the last files to be assigned caused a processing error
245
+ # [images_download_error] Returns an error object if the last files to be remotely assigned caused a download error
246
+ #
247
+ # [image_identifiers] Reads out the identifiers of the files
248
+ #
249
+ # === Parameters
250
+ #
251
+ # [column (Symbol)] the attribute to mount this uploader on
252
+ # [uploader (CarrierWave::Uploader)] the uploader class to mount
253
+ # [options (Hash{Symbol => Object})] a set of options
254
+ # [&block (Proc)] customize anonymous uploaders
255
+ #
256
+ # === Options
257
+ #
258
+ # [:mount_on => Symbol] if the name of the column to be serialized to differs you can override it using this option
259
+ # [:ignore_integrity_errors => Boolean] if set to true, integrity errors will result in caching failing silently
260
+ # [:ignore_processing_errors => Boolean] if set to true, processing errors will result in caching failing silently
261
+ #
262
+ # === Examples
263
+ #
264
+ # Mounting uploaders on different columns.
265
+ #
266
+ # class Song
267
+ # mount_uploaders :lyrics, LyricsUploader
268
+ # mount_uploaders :alternative_lyrics, LyricsUploader
269
+ # mount_uploaders :files, SongUploader
270
+ # end
271
+ #
272
+ # This will add an anonymous uploader with only the default settings:
273
+ #
274
+ # class Data
275
+ # mount_uploaders :csv_files
276
+ # end
277
+ #
278
+ # this will add an anonymous uploader overriding the store_dir:
279
+ #
280
+ # class Product
281
+ # mount_uploaders :blueprints do
282
+ # def store_dir
283
+ # 'blueprints'
284
+ # end
285
+ # end
286
+ # end
287
+ #
288
+ def mount_uploaders(column, uploader=nil, options={}, &block)
289
+ mount_base(column, uploader, options, &block)
290
+
291
+ mod = Module.new
292
+ include mod
293
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
294
+
295
+ def #{column}
296
+ _mounter(:#{column}).uploaders
297
+ end
298
+
299
+ def #{column}=(new_files)
300
+ _mounter(:#{column}).cache(new_files)
301
+ end
302
+
303
+ def #{column}_urls(*args)
304
+ _mounter(:#{column}).urls(*args)
305
+ end
306
+
307
+ def #{column}_cache
308
+ names = _mounter(:#{column}).cache_names
309
+ names.to_json if names.present?
310
+ end
311
+
312
+ def #{column}_cache=(cache_name)
313
+ _mounter(:#{column}).cache_names = JSON.parse(cache_name) if cache_name.present?
314
+ end
315
+
316
+ def remote_#{column}_urls
317
+ _mounter(:#{column}).remote_urls
318
+ end
319
+
320
+ def remote_#{column}_urls=(urls)
321
+ _mounter(:#{column}).remote_urls = urls
322
+ end
323
+
324
+ def remote_#{column}_request_headers=(headers)
325
+ _mounter(:#{column}).remote_request_headers = headers
326
+ end
327
+
328
+ def write_#{column}_identifier
329
+ return if frozen?
330
+ mounter = _mounter(:#{column})
331
+
332
+ if mounter.remove?
333
+ write_uploader(mounter.serialization_column, nil)
334
+ elsif mounter.identifiers.any?
335
+ write_uploader(mounter.serialization_column, mounter.identifiers)
336
+ end
337
+ end
338
+
339
+ def #{column}_identifiers
340
+ _mounter(:#{column}).read_identifiers
341
+ end
342
+
343
+ def store_previous_changes_for_#{column}
344
+ attribute_changes = ::ActiveRecord.version.to_s.to_f >= 5.1 ? saved_changes : changes
345
+ @_previous_changes_for_#{column} = attribute_changes[_mounter(:#{column}).serialization_column]
346
+ end
347
+
348
+ def remove_previously_stored_#{column}
349
+ _mounter(:#{column}).remove_previous(*@_previous_changes_for_#{column})
350
+ end
351
+ RUBY
352
+ end
353
+
354
+ private
355
+
356
+ def mount_base(column, uploader=nil, options={}, &block)
357
+ include CarrierWave::Mount::Extension
358
+
359
+ uploader = build_uploader(uploader, &block)
360
+ uploaders[column.to_sym] = uploader
361
+ uploader_options[column.to_sym] = options
362
+
363
+ # Make sure to write over accessors directly defined on the class.
364
+ # Simply super to the included module below.
365
+ class_eval <<-RUBY, __FILE__, __LINE__+1
366
+ def #{column}; super; end
367
+ def #{column}=(new_file); super; end
368
+ RUBY
369
+
370
+ mod = Module.new
371
+ include mod
372
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
373
+
374
+ def #{column}?
375
+ _mounter(:#{column}).present?
188
376
  end
189
377
 
190
378
  def remove_#{column}
@@ -219,47 +407,17 @@ module CarrierWave
219
407
  _mounter(:#{column}).download_error
220
408
  end
221
409
 
222
- def write_#{column}_identifier
223
- _mounter(:#{column}).write_identifier
224
- end
225
-
226
- def #{column}_identifier
227
- _mounter(:#{column}).identifier
228
- end
229
-
230
- def store_previous_model_for_#{column}
231
- serialization_column = _mounter(:#{column}).serialization_column
232
-
233
- if #{column}.remove_previously_stored_files_after_update && send(:"\#{serialization_column}_changed?")
234
- @previous_model_for_#{column} ||= self.find_previous_model_for_#{column}
235
- end
236
- end
237
-
238
- def find_previous_model_for_#{column}
239
- self.class.find(to_key.first)
240
- end
241
-
242
- def remove_previously_stored_#{column}
243
- if @previous_model_for_#{column} && @previous_model_for_#{column}.#{column}.path != #{column}.path
244
- @previous_model_for_#{column}.#{column}.remove!
245
- @previous_model_for_#{column} = nil
246
- end
247
- end
248
-
249
410
  def mark_remove_#{column}_false
250
411
  _mounter(:#{column}).remove = false
251
412
  end
252
-
253
413
  RUBY
254
414
  end
255
415
 
256
- private
257
-
258
416
  def build_uploader(uploader, &block)
259
417
  return uploader if uploader && !block_given?
260
418
 
261
419
  uploader = Class.new(uploader || CarrierWave::Uploader::Base)
262
- const_set("Uploader#{uploader.object_id}".gsub('-', '_'), uploader)
420
+ const_set("Uploader#{uploader.object_id}".tr('-', '_'), uploader)
263
421
 
264
422
  if block_given?
265
423
  uploader.class_eval(&block)
@@ -292,120 +450,5 @@ module CarrierWave
292
450
 
293
451
  end # Extension
294
452
 
295
- # this is an internal class, used by CarrierWave::Mount so that
296
- # we don't pollute the model with a lot of methods.
297
- class Mounter #:nodoc:
298
- attr_reader :column, :record, :remote_url, :integrity_error, :processing_error, :download_error
299
- attr_accessor :remove
300
-
301
- def initialize(record, column, options={})
302
- @record = record
303
- @column = column
304
- @options = record.class.uploader_options[column]
305
- end
306
-
307
- def write_identifier
308
- return if record.frozen?
309
-
310
- if remove?
311
- record.write_uploader(serialization_column, nil)
312
- elsif uploader.identifier.present?
313
- record.write_uploader(serialization_column, uploader.identifier)
314
- end
315
- end
316
-
317
- def identifier
318
- record.read_uploader(serialization_column)
319
- end
320
-
321
- def uploader
322
- @uploader ||= record.class.uploaders[column].new(record, column)
323
- @uploader.retrieve_from_store!(identifier) if @uploader.blank? && identifier.present?
324
-
325
- @uploader
326
- end
327
-
328
- def cache(new_file)
329
- uploader.cache!(new_file)
330
- @integrity_error = nil
331
- @processing_error = nil
332
- rescue CarrierWave::IntegrityError => e
333
- @integrity_error = e
334
- raise e unless option(:ignore_integrity_errors)
335
- rescue CarrierWave::ProcessingError => e
336
- @processing_error = e
337
- raise e unless option(:ignore_processing_errors)
338
- end
339
-
340
- def cache_name
341
- uploader.cache_name
342
- end
343
-
344
- def cache_name=(cache_name)
345
- uploader.retrieve_from_cache!(cache_name) unless uploader.cached?
346
- rescue CarrierWave::InvalidParameter
347
- end
348
-
349
- def remote_url=(url)
350
- return if url.blank?
351
-
352
- @remote_url = url
353
- @download_error = nil
354
- @integrity_error = nil
355
-
356
- uploader.download!(url)
357
-
358
- rescue CarrierWave::DownloadError => e
359
- @download_error = e
360
- raise e unless option(:ignore_download_errors)
361
- rescue CarrierWave::ProcessingError => e
362
- @processing_error = e
363
- raise e unless option(:ignore_processing_errors)
364
- rescue CarrierWave::IntegrityError => e
365
- @integrity_error = e
366
- raise e unless option(:ignore_integrity_errors)
367
- end
368
-
369
- def store!
370
- return if uploader.blank?
371
-
372
- if remove?
373
- uploader.remove!
374
- else
375
- uploader.store!
376
- end
377
- end
378
-
379
- def url(*args)
380
- uploader.url(*args)
381
- end
382
-
383
- def blank?
384
- uploader.blank?
385
- end
386
-
387
- def remove?
388
- remove.present? && remove !~ /\A0|false$\z/
389
- end
390
-
391
- def remove!
392
- uploader.remove!
393
- end
394
-
395
- def serialization_column
396
- option(:mount_on) || column
397
- end
398
-
399
- attr_accessor :uploader_options
400
-
401
- private
402
-
403
- def option(name)
404
- self.uploader_options ||= {}
405
- self.uploader_options[name] ||= record.class.uploader_option(column, name)
406
- end
407
-
408
- end # Mounter
409
-
410
453
  end # Mount
411
454
  end # CarrierWave
@@ -0,0 +1,165 @@
1
+ module CarrierWave
2
+
3
+ # this is an internal class, used by CarrierWave::Mount so that
4
+ # we don't pollute the model with a lot of methods.
5
+ class Mounter #:nodoc:
6
+ attr_reader :column, :record, :remote_urls, :integrity_error,
7
+ :processing_error, :download_error
8
+ attr_accessor :remove, :remote_request_headers
9
+
10
+ def initialize(record, column, options={})
11
+ @record = record
12
+ @column = column
13
+ @options = record.class.uploader_options[column]
14
+ end
15
+
16
+ def uploader_class
17
+ record.class.uploaders[column]
18
+ end
19
+
20
+ def blank_uploader
21
+ uploader_class.new(record, column)
22
+ end
23
+
24
+ def identifiers
25
+ uploaders.map(&:identifier)
26
+ end
27
+
28
+ def read_identifiers
29
+ [record.read_uploader(serialization_column)].flatten.reject(&:blank?)
30
+ end
31
+
32
+ def uploaders
33
+ @uploaders ||= read_identifiers.map do |identifier|
34
+ uploader = blank_uploader
35
+ uploader.retrieve_from_store!(identifier) if identifier.present?
36
+ uploader
37
+ end
38
+ end
39
+
40
+ def cache(new_files)
41
+ return if not new_files or new_files == ""
42
+ @uploaders = new_files.map do |new_file|
43
+ uploader = blank_uploader
44
+ uploader.cache!(new_file)
45
+ uploader
46
+ end
47
+
48
+ @integrity_error = nil
49
+ @processing_error = nil
50
+ rescue CarrierWave::IntegrityError => e
51
+ @integrity_error = e
52
+ raise e unless option(:ignore_integrity_errors)
53
+ rescue CarrierWave::ProcessingError => e
54
+ @processing_error = e
55
+ raise e unless option(:ignore_processing_errors)
56
+ end
57
+
58
+ def cache_names
59
+ uploaders.map(&:cache_name).compact
60
+ end
61
+
62
+ def cache_names=(cache_names)
63
+ return if not cache_names or cache_names == "" or uploaders.any?(&:cached?)
64
+ @uploaders = cache_names.map do |cache_name|
65
+ uploader = blank_uploader
66
+ uploader.retrieve_from_cache!(cache_name)
67
+ uploader
68
+ end
69
+ rescue CarrierWave::InvalidParameter
70
+ end
71
+
72
+ def remote_urls=(urls)
73
+ return if not urls or urls == "" or urls.all?(&:blank?)
74
+
75
+ @remote_urls = urls
76
+ @download_error = nil
77
+ @integrity_error = nil
78
+
79
+ @uploaders = urls.zip(remote_request_headers || []).map do |url, header|
80
+ uploader = blank_uploader
81
+ uploader.download!(url, header || {})
82
+ uploader
83
+ end
84
+
85
+ rescue CarrierWave::DownloadError => e
86
+ @download_error = e
87
+ raise e unless option(:ignore_download_errors)
88
+ rescue CarrierWave::ProcessingError => e
89
+ @processing_error = e
90
+ raise e unless option(:ignore_processing_errors)
91
+ rescue CarrierWave::IntegrityError => e
92
+ @integrity_error = e
93
+ raise e unless option(:ignore_integrity_errors)
94
+ end
95
+
96
+ def store!
97
+ if remove?
98
+ remove!
99
+ else
100
+ uploaders.reject(&:blank?).each(&:store!)
101
+ end
102
+ end
103
+
104
+ def urls(*args)
105
+ uploaders.map { |u| u.url(*args) }
106
+ end
107
+
108
+ def blank?
109
+ uploaders.none?(&:present?)
110
+ end
111
+
112
+ def remove?
113
+ remove.present? && remove !~ /\A0|false$\z/
114
+ end
115
+
116
+ def remove!
117
+ uploaders.reject(&:blank?).each(&:remove!)
118
+ @uploaders = []
119
+ end
120
+
121
+ def serialization_column
122
+ option(:mount_on) || column
123
+ end
124
+
125
+ def remove_previous(before=nil, after=nil)
126
+ after ||= []
127
+ return unless before
128
+
129
+ # both 'before' and 'after' can be string when 'mount_on' option is set
130
+ before = before.reject(&:blank?).map do |value|
131
+ if value.is_a?(String)
132
+ uploader = blank_uploader
133
+ uploader.retrieve_from_store!(value)
134
+ uploader
135
+ else
136
+ value
137
+ end
138
+ end
139
+ after_paths = after.reject(&:blank?).map do |value|
140
+ if value.is_a?(String)
141
+ uploader = blank_uploader
142
+ uploader.retrieve_from_store!(value)
143
+ uploader
144
+ else
145
+ value
146
+ end.path
147
+ end
148
+ before.each do |uploader|
149
+ if uploader.remove_previously_stored_files_after_update and not after_paths.include?(uploader.path)
150
+ uploader.remove!
151
+ end
152
+ end
153
+ end
154
+
155
+ attr_accessor :uploader_options
156
+
157
+ private
158
+
159
+ def option(name)
160
+ self.uploader_options ||= {}
161
+ self.uploader_options[name] ||= record.class.uploader_option(column, name)
162
+ end
163
+
164
+ end # Mounter
165
+ end # CarrierWave