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