carrierwave 0.11.2 → 3.0.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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +452 -178
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -4
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/error.rb +1 -0
  7. data/lib/carrierwave/locale/en.yml +11 -5
  8. data/lib/carrierwave/mount.rb +212 -182
  9. data/lib/carrierwave/mounter.rb +255 -0
  10. data/lib/carrierwave/orm/activerecord.rb +22 -33
  11. data/lib/carrierwave/processing/mini_magick.rb +140 -84
  12. data/lib/carrierwave/processing/rmagick.rb +72 -21
  13. data/lib/carrierwave/processing/vips.rb +284 -0
  14. data/lib/carrierwave/processing.rb +1 -1
  15. data/lib/carrierwave/sanitized_file.rb +83 -84
  16. data/lib/carrierwave/storage/abstract.rb +16 -3
  17. data/lib/carrierwave/storage/file.rb +71 -3
  18. data/lib/carrierwave/storage/fog.rb +215 -57
  19. data/lib/carrierwave/storage.rb +1 -9
  20. data/lib/carrierwave/test/matchers.rb +88 -19
  21. data/lib/carrierwave/uploader/cache.rb +75 -45
  22. data/lib/carrierwave/uploader/callbacks.rb +1 -3
  23. data/lib/carrierwave/uploader/configuration.rb +80 -16
  24. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  25. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  26. data/lib/carrierwave/uploader/default_url.rb +3 -5
  27. data/lib/carrierwave/uploader/dimension.rb +66 -0
  28. data/lib/carrierwave/uploader/download.rb +4 -74
  29. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  30. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  31. data/lib/carrierwave/uploader/file_size.rb +43 -0
  32. data/lib/carrierwave/uploader/mountable.rb +13 -8
  33. data/lib/carrierwave/uploader/processing.rb +48 -13
  34. data/lib/carrierwave/uploader/proxy.rb +20 -9
  35. data/lib/carrierwave/uploader/remove.rb +0 -2
  36. data/lib/carrierwave/uploader/serialization.rb +2 -4
  37. data/lib/carrierwave/uploader/store.rb +59 -28
  38. data/lib/carrierwave/uploader/url.rb +8 -7
  39. data/lib/carrierwave/uploader/versions.rb +170 -122
  40. data/lib/carrierwave/uploader.rb +12 -10
  41. data/lib/carrierwave/utilities/file_name.rb +47 -0
  42. data/lib/carrierwave/utilities/uri.rb +14 -12
  43. data/lib/carrierwave/utilities.rb +1 -3
  44. data/lib/carrierwave/validations/active_model.rb +7 -11
  45. data/lib/carrierwave/version.rb +1 -1
  46. data/lib/carrierwave.rb +39 -21
  47. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +5 -9
  48. data/lib/generators/uploader_generator.rb +3 -3
  49. metadata +132 -80
  50. data/lib/carrierwave/locale/cs.yml +0 -11
  51. data/lib/carrierwave/locale/de.yml +0 -11
  52. data/lib/carrierwave/locale/el.yml +0 -11
  53. data/lib/carrierwave/locale/es.yml +0 -11
  54. data/lib/carrierwave/locale/fr.yml +0 -11
  55. data/lib/carrierwave/locale/ja.yml +0 -11
  56. data/lib/carrierwave/locale/nb.yml +0 -11
  57. data/lib/carrierwave/locale/nl.yml +0 -11
  58. data/lib/carrierwave/locale/pl.yml +0 -11
  59. data/lib/carrierwave/locale/pt-BR.yml +0 -11
  60. data/lib/carrierwave/locale/pt-PT.yml +0 -11
  61. data/lib/carrierwave/locale/ru.yml +0 -11
  62. data/lib/carrierwave/locale/sk.yml +0 -11
  63. data/lib/carrierwave/locale/tr.yml +0 -11
  64. data/lib/carrierwave/processing/mime_types.rb +0 -74
  65. data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
  66. data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
  67. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -47
  68. data/lib/carrierwave/uploader/extension_whitelist.rb +0 -49
  69. data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -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,218 @@ module CarrierWave
135
132
  # end
136
133
  #
137
134
  def mount_uploader(column, uploader=nil, options={}, &block)
138
- include CarrierWave::Mount::Extension
135
+ mount_base(column, uploader, options.merge(multiple: false), &block)
139
136
 
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
150
-
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 #{column}_identifier
174
+ _mounter(:#{column}).read_identifiers[0]
175
+ end
176
+
177
+ def #{column}_integrity_error
178
+ #{column}_integrity_errors.last
179
+ end
180
+
181
+ def #{column}_processing_error
182
+ #{column}_processing_errors.last
183
+ end
184
+
185
+ def #{column}_download_error
186
+ #{column}_download_errors.last
187
+ end
188
+ RUBY
189
+ end
190
+
191
+ ##
192
+ # Mounts the given uploader on the given array column. This means that
193
+ # assigning and reading from the array column will upload and retrieve
194
+ # multiple files. Supposing that a User class has an uploader mounted on
195
+ # images, and that images can store an array, for example it could be a
196
+ # PostgreSQL JSON column. You can assign and retrieve files like this:
197
+ #
198
+ # @user.images # => []
199
+ # @user.images = [some_file_object]
200
+ # @user.images # => [<Uploader>]
201
+ #
202
+ # @user.images[0].url # => '/some_url.png'
203
+ #
204
+ # It is also possible (but not recommended) to omit the uploader, which
205
+ # will create an anonymous uploader class.
206
+ #
207
+ # Passing a block makes it possible to customize the uploader. This can be
208
+ # convenient for brevity, but if there is any significant logic in the
209
+ # uploader, you should do the right thing and have it in its own file.
210
+ #
211
+ # === Added instance methods
212
+ #
213
+ # Supposing a class has used +mount_uploaders+ to mount an uploader on a column
214
+ # named +images+, in that case the following methods will be added to the class:
215
+ #
216
+ # [images] Returns an array of uploaders for each uploaded file
217
+ # [images=] Caches the given files
218
+ #
219
+ # [images_urls] Returns the urls to the uploaded files
220
+ #
221
+ # [images_cache] Returns a string that identifies the cache location of the files
222
+ # [images_cache=] Retrieves the files from the cache based on the given cache name
223
+ #
224
+ # [remote_image_urls] Returns previously cached remote urls
225
+ # [remote_image_urls=] Retrieve files from the given remote urls
226
+ #
227
+ # [remove_images] An attribute reader that can be used with a checkbox to mark the files for removal
228
+ # [remove_images=] An attribute writer that can be used with a checkbox to mark the files for removal
229
+ # [remove_images?] Whether the files should be removed when store_image! is called.
230
+ #
231
+ # [store_images!] Stores all files that have been assigned with +images=+
232
+ # [remove_images!] Removes the uploaded file from the filesystem.
233
+ #
234
+ # [image_integrity_errors] Returns error objects of files which failed to pass integrity check
235
+ # [image_processing_errors] Returns error objects of files which failed to be processed
236
+ # [image_download_errors] Returns error objects of files which failed to be downloaded
237
+ #
238
+ # [image_identifiers] Reads out the identifiers of the files
239
+ #
240
+ # === Parameters
241
+ #
242
+ # [column (Symbol)] the attribute to mount this uploader on
243
+ # [uploader (CarrierWave::Uploader)] the uploader class to mount
244
+ # [options (Hash{Symbol => Object})] a set of options
245
+ # [&block (Proc)] customize anonymous uploaders
246
+ #
247
+ # === Options
248
+ #
249
+ # [:mount_on => Symbol] if the name of the column to be serialized to differs you can override it using this option
250
+ # [:ignore_integrity_errors => Boolean] if set to true, integrity errors will result in caching failing silently
251
+ # [:ignore_processing_errors => Boolean] if set to true, processing errors will result in caching failing silently
252
+ #
253
+ # === Examples
254
+ #
255
+ # Mounting uploaders on different columns.
256
+ #
257
+ # class Song
258
+ # mount_uploaders :lyrics, LyricsUploader
259
+ # mount_uploaders :alternative_lyrics, LyricsUploader
260
+ # mount_uploaders :files, SongUploader
261
+ # end
262
+ #
263
+ # This will add an anonymous uploader with only the default settings:
264
+ #
265
+ # class Data
266
+ # mount_uploaders :csv_files
267
+ # end
268
+ #
269
+ # this will add an anonymous uploader overriding the store_dir:
270
+ #
271
+ # class Product
272
+ # mount_uploaders :blueprints do
273
+ # def store_dir
274
+ # 'blueprints'
275
+ # end
276
+ # end
277
+ # end
278
+ #
279
+ def mount_uploaders(column, uploader=nil, options={}, &block)
280
+ mount_base(column, uploader, options.merge(multiple: true), &block)
281
+
282
+ mod = Module.new
283
+ include mod
284
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
285
+
286
+ def #{column}
287
+ _mounter(:#{column}).uploaders
288
+ end
289
+
290
+ def #{column}=(new_files)
291
+ _mounter(:#{column}).cache(new_files)
292
+ end
293
+
294
+ def #{column}_urls(*args)
295
+ _mounter(:#{column}).urls(*args)
296
+ end
297
+
298
+ def #{column}_cache
299
+ names = _mounter(:#{column}).cache_names
300
+ names.to_json if names.present?
301
+ end
302
+
303
+ def #{column}_cache=(cache_name)
304
+ _mounter(:#{column}).cache_names = JSON.parse(cache_name) if cache_name.present?
305
+ end
306
+
307
+ def remote_#{column}_urls
308
+ _mounter(:#{column}).remote_urls
309
+ end
310
+
311
+ def remote_#{column}_urls=(urls)
312
+ _mounter(:#{column}).remote_urls = urls
313
+ end
314
+
315
+ def remote_#{column}_request_headers=(headers)
316
+ _mounter(:#{column}).remote_request_headers = headers
317
+ end
318
+
319
+ def #{column}_identifiers
320
+ _mounter(:#{column}).read_identifiers
321
+ end
322
+ RUBY
323
+ end
324
+
325
+ private
326
+
327
+ def mount_base(column, uploader=nil, options={}, &block)
328
+ include CarrierWave::Mount::Extension
329
+
330
+ uploader = build_uploader(uploader, column, &block)
331
+ uploaders[column.to_sym] = uploader
332
+ uploader_options[column.to_sym] = options
333
+
334
+ # Make sure to write over accessors directly defined on the class.
335
+ # Simply super to the included module below.
336
+ class_eval <<-RUBY, __FILE__, __LINE__+1
337
+ def #{column}; super; end
338
+ def #{column}=(new_file); super; end
339
+ RUBY
340
+
341
+ mod = Module.new
342
+ include mod
343
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
344
+
345
+ def #{column}?
346
+ _mounter(:#{column}).present?
188
347
  end
189
348
 
190
349
  def remove_#{column}
@@ -193,6 +352,8 @@ module CarrierWave
193
352
 
194
353
  def remove_#{column}!
195
354
  _mounter(:#{column}).remove!
355
+ self.remove_#{column} = true
356
+ write_#{column}_identifier
196
357
  end
197
358
 
198
359
  def remove_#{column}=(value)
@@ -207,64 +368,48 @@ module CarrierWave
207
368
  _mounter(:#{column}).store!
208
369
  end
209
370
 
210
- def #{column}_integrity_error
211
- _mounter(:#{column}).integrity_error
371
+ def #{column}_integrity_errors
372
+ _mounter(:#{column}).integrity_errors
212
373
  end
213
374
 
214
- def #{column}_processing_error
215
- _mounter(:#{column}).processing_error
375
+ def #{column}_processing_errors
376
+ _mounter(:#{column}).processing_errors
216
377
  end
217
378
 
218
- def #{column}_download_error
219
- _mounter(:#{column}).download_error
379
+ def #{column}_download_errors
380
+ _mounter(:#{column}).download_errors
220
381
  end
221
382
 
222
383
  def write_#{column}_identifier
223
384
  _mounter(:#{column}).write_identifier
224
385
  end
225
386
 
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
387
+ def mark_remove_#{column}_false
388
+ _mounter(:#{column}).remove = false
236
389
  end
237
390
 
238
- def find_previous_model_for_#{column}
239
- self.class.find(to_key.first)
391
+ def reset_previous_changes_for_#{column}
392
+ _mounter(:#{column}).reset_changes!
240
393
  end
241
394
 
242
395
  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
396
+ _mounter(:#{column}).remove_previous
247
397
  end
248
398
 
249
- def mark_remove_#{column}_false
250
- _mounter(:#{column}).remove = false
399
+ def remove_rolled_back_#{column}
400
+ _mounter(:#{column}).remove_added
251
401
  end
252
-
253
402
  RUBY
254
403
  end
255
404
 
256
- private
257
-
258
- def build_uploader(uploader, &block)
259
- return uploader if uploader && !block_given?
405
+ def build_uploader(uploader, column, &block)
406
+ uploader ||= CarrierWave::Uploader::Base
407
+ return uploader unless block_given?
260
408
 
261
- uploader = Class.new(uploader || CarrierWave::Uploader::Base)
262
- const_set("Uploader#{uploader.object_id}".gsub('-', '_'), uploader)
409
+ uploader = Class.new(uploader)
410
+ const_set("CarrierWave#{column.to_s.camelize}Uploader", uploader)
263
411
 
264
- if block_given?
265
- uploader.class_eval(&block)
266
- uploader.recursively_apply_block_to_versions(&block)
267
- end
412
+ uploader.class_eval(&block)
268
413
 
269
414
  uploader
270
415
  end
@@ -285,127 +430,12 @@ module CarrierWave
285
430
 
286
431
  def _mounter(column)
287
432
  # We cannot memoize in frozen objects :(
288
- return Mounter.new(self, column) if frozen?
433
+ return Mounter.build(self, column) if frozen?
289
434
  @_mounters ||= {}
290
- @_mounters[column] ||= Mounter.new(self, column)
435
+ @_mounters[column] ||= Mounter.build(self, column)
291
436
  end
292
437
 
293
438
  end # Extension
294
439
 
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
440
  end # Mount
411
441
  end # CarrierWave