carrierwave 0.10.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 (61) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +307 -121
  3. data/lib/carrierwave/compatibility/paperclip.rb +0 -2
  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 +229 -180
  9. data/lib/carrierwave/mounter.rb +188 -0
  10. data/lib/carrierwave/orm/activerecord.rb +59 -24
  11. data/lib/carrierwave/processing/mini_magick.rb +137 -83
  12. data/lib/carrierwave/processing/rmagick.rb +65 -8
  13. data/lib/carrierwave/processing.rb +0 -1
  14. data/lib/carrierwave/sanitized_file.rb +67 -32
  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 +177 -39
  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 +71 -13
  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 +4 -74
  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 +10 -10
  32. data/lib/carrierwave/uploader/proxy.rb +6 -8
  33. data/lib/carrierwave/uploader/remove.rb +0 -2
  34. data/lib/carrierwave/uploader/serialization.rb +2 -4
  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 +123 -93
  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 +34 -8
  44. data/lib/generators/templates/uploader.rb +4 -8
  45. metadata +130 -57
  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
 
@@ -0,0 +1,83 @@
1
+ require 'open-uri'
2
+ require 'ssrf_filter'
3
+ require 'addressable'
4
+ require 'carrierwave/downloader/remote_file'
5
+
6
+ module CarrierWave
7
+ module Downloader
8
+ class Base
9
+ attr_reader :uploader
10
+
11
+ def initialize(uploader)
12
+ @uploader = uploader
13
+ end
14
+
15
+ ##
16
+ # Downloads a file from given URL and returns a RemoteFile.
17
+ #
18
+ # === Parameters
19
+ #
20
+ # [url (String)] The URL where the remote file is stored
21
+ # [remote_headers (Hash)] Request headers
22
+ #
23
+ def download(url, remote_headers = {})
24
+ headers = remote_headers.
25
+ reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
26
+ uri = process_uri(url.to_s)
27
+ begin
28
+ if skip_ssrf_protection?(uri)
29
+ response = OpenURI.open_uri(process_uri(url.to_s), headers)
30
+ else
31
+ request = nil
32
+ response = SsrfFilter.get(uri, headers: headers) do |req|
33
+ request = req
34
+ end
35
+ response.uri = request.uri
36
+ response.value
37
+ end
38
+ rescue StandardError => e
39
+ raise CarrierWave::DownloadError, "could not download file: #{e.message}"
40
+ end
41
+ CarrierWave::Downloader::RemoteFile.new(response)
42
+ end
43
+
44
+ ##
45
+ # Processes the given URL by parsing and escaping it. Public to allow overriding.
46
+ #
47
+ # === Parameters
48
+ #
49
+ # [url (String)] The URL where the remote file is stored
50
+ #
51
+ def process_uri(uri)
52
+ uri_parts = uri.split('?')
53
+ encoded_uri = Addressable::URI.parse(uri_parts.shift).normalize.to_s
54
+ encoded_uri << '?' << Addressable::URI.encode(uri_parts.join('?')).gsub('%5B', '[').gsub('%5D', ']') if uri_parts.any?
55
+ URI.parse(encoded_uri)
56
+ rescue URI::InvalidURIError, Addressable::URI::InvalidURIError
57
+ raise CarrierWave::DownloadError, "couldn't parse URL: #{uri}"
58
+ end
59
+
60
+ ##
61
+ # If this returns true, SSRF protection will be bypassed.
62
+ # You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.
63
+ #
64
+ # === Parameters
65
+ #
66
+ # [uri (URI)] The URI where the remote file is stored
67
+ #
68
+ # === Examples
69
+ #
70
+ # class CarrierWave::Downloader::CustomDownloader < CarrierWave::Downloader::Base
71
+ # def skip_ssrf_protection?(uri)
72
+ # uri.hostname == 'localhost' && uri.port == 80
73
+ # end
74
+ # end
75
+ #
76
+ # my_uploader.downloader = CarrierWave::Downloader::CustomDownloader
77
+ #
78
+ def skip_ssrf_protection?(uri)
79
+ false
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,65 @@
1
+ module CarrierWave
2
+ module Downloader
3
+ class RemoteFile
4
+ attr_reader :file, :uri
5
+
6
+ def initialize(file)
7
+ case file
8
+ when String
9
+ @file = StringIO.new(file)
10
+ when Net::HTTPResponse
11
+ @file = StringIO.new(file.body)
12
+ @content_type = file.content_type
13
+ @headers = file
14
+ @uri = file.uri
15
+ else
16
+ @file = file
17
+ @content_type = file.content_type
18
+ @headers = file.meta
19
+ @uri = file.base_uri
20
+ end
21
+ end
22
+
23
+ def content_type
24
+ @content_type || 'application/octet-stream'
25
+ end
26
+
27
+ def headers
28
+ @headers || {}
29
+ end
30
+
31
+ def original_filename
32
+ filename = filename_from_header || filename_from_uri
33
+ mime_type = MiniMime.lookup_by_content_type(content_type)
34
+ unless File.extname(filename).present? || mime_type.blank?
35
+ filename = "#{filename}.#{mime_type.extension}"
36
+ end
37
+ filename
38
+ end
39
+
40
+ def respond_to?(*args)
41
+ super || file.respond_to?(*args)
42
+ end
43
+
44
+ private
45
+
46
+ def filename_from_header
47
+ return nil unless headers['content-disposition']
48
+
49
+ match = headers['content-disposition'].match(/filename=(?:"([^"]+)"|([^";]+))/)
50
+ return nil unless match
51
+
52
+ match[1].presence || match[2].presence
53
+ end
54
+
55
+ def filename_from_uri
56
+ CGI.unescape(File.basename(uri.path))
57
+ end
58
+
59
+ def method_missing(*args, &block)
60
+ file.send(*args, &block)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -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, allowed types: %{allowed_types}"
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,131 +132,298 @@ 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]
188
167
  end
189
168
 
190
- def remove_#{column}
191
- _mounter(:#{column}).remove
169
+ def remote_#{column}_request_header=(header)
170
+ _mounter(:#{column}).remote_request_headers = [header]
192
171
  end
193
172
 
194
- def remove_#{column}!
195
- _mounter(:#{column}).remove!
196
- end
197
-
198
- def remove_#{column}=(value)
199
- _mounter(:#{column}).remove = value
200
- end
173
+ def write_#{column}_identifier
174
+ return if frozen?
175
+ mounter = _mounter(:#{column})
201
176
 
202
- def remove_#{column}?
203
- _mounter(:#{column}).remove?
177
+ mounter.clear! if mounter.remove?
178
+ write_uploader(mounter.serialization_column, mounter.identifiers.first)
204
179
  end
205
180
 
206
- def store_#{column}!
207
- _mounter(:#{column}).store!
181
+ def #{column}_identifier
182
+ _mounter(:#{column}).read_identifiers[0]
208
183
  end
209
184
 
210
185
  def #{column}_integrity_error
211
- _mounter(:#{column}).integrity_error
186
+ #{column}_integrity_errors.last
212
187
  end
213
188
 
214
189
  def #{column}_processing_error
215
- _mounter(:#{column}).processing_error
190
+ #{column}_processing_errors.last
216
191
  end
217
192
 
218
193
  def #{column}_download_error
219
- _mounter(:#{column}).download_error
194
+ #{column}_download_errors.last
220
195
  end
221
196
 
222
- def write_#{column}_identifier
223
- _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]
224
200
  end
225
201
 
226
- def #{column}_identifier
227
- _mounter(:#{column}).identifier
202
+ def remove_previously_stored_#{column}
203
+ before, after = @_previous_changes_for_#{column}
204
+ _mounter(:#{column}).remove_previous([before], [after])
205
+ end
206
+ RUBY
207
+ end
208
+
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)
299
+
300
+ mod = Module.new
301
+ include mod
302
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
303
+
304
+ def #{column}
305
+ _mounter(:#{column}).uploaders
228
306
  end
229
307
 
230
- def store_previous_model_for_#{column}
231
- serialization_column = _mounter(:#{column}).serialization_column
308
+ def #{column}=(new_files)
309
+ _mounter(:#{column}).cache(new_files)
310
+ end
232
311
 
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
312
+ def #{column}_urls(*args)
313
+ _mounter(:#{column}).urls(*args)
236
314
  end
237
315
 
238
- def find_previous_model_for_#{column}
239
- self.class.find(to_key.first)
316
+ def #{column}_cache
317
+ names = _mounter(:#{column}).cache_names
318
+ names.to_json if names.present?
240
319
  end
241
320
 
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
321
+ def #{column}_cache=(cache_name)
322
+ _mounter(:#{column}).cache_names = JSON.parse(cache_name) if cache_name.present?
247
323
  end
248
324
 
249
- def mark_remove_#{column}_false
250
- _mounter(:#{column}).remove = false
325
+ def remote_#{column}_urls
326
+ _mounter(:#{column}).remote_urls
327
+ end
328
+
329
+ def remote_#{column}_urls=(urls)
330
+ _mounter(:#{column}).remote_urls = urls
251
331
  end
252
332
 
333
+ def remote_#{column}_request_headers=(headers)
334
+ _mounter(:#{column}).remote_request_headers = headers
335
+ end
336
+
337
+ def write_#{column}_identifier
338
+ return if frozen?
339
+ mounter = _mounter(:#{column})
340
+
341
+ mounter.clear! if mounter.remove?
342
+ write_uploader(mounter.serialization_column, mounter.identifiers.presence)
343
+ end
344
+
345
+ def #{column}_identifiers
346
+ _mounter(:#{column}).read_identifiers
347
+ end
348
+
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
353
+
354
+ def remove_previously_stored_#{column}
355
+ _mounter(:#{column}).remove_previous(*@_previous_changes_for_#{column})
356
+ end
253
357
  RUBY
254
358
  end
255
359
 
256
360
  private
257
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
379
+
380
+ def #{column}?
381
+ _mounter(:#{column}).present?
382
+ end
383
+
384
+ def remove_#{column}
385
+ _mounter(:#{column}).remove
386
+ end
387
+
388
+ def remove_#{column}!
389
+ _mounter(:#{column}).remove!
390
+ end
391
+
392
+ def remove_#{column}=(value)
393
+ _mounter(:#{column}).remove = value
394
+ end
395
+
396
+ def remove_#{column}?
397
+ _mounter(:#{column}).remove?
398
+ end
399
+
400
+ def store_#{column}!
401
+ _mounter(:#{column}).store!
402
+ end
403
+
404
+ def #{column}_integrity_errors
405
+ _mounter(:#{column}).integrity_errors
406
+ end
407
+
408
+ def #{column}_processing_errors
409
+ _mounter(:#{column}).processing_errors
410
+ end
411
+
412
+ def #{column}_download_errors
413
+ _mounter(:#{column}).download_errors
414
+ end
415
+
416
+ def mark_remove_#{column}_false
417
+ _mounter(:#{column}).remove = false
418
+ end
419
+ RUBY
420
+ end
421
+
258
422
  def build_uploader(uploader, &block)
259
423
  return uploader if uploader && !block_given?
260
424
 
261
425
  uploader = Class.new(uploader || CarrierWave::Uploader::Base)
262
- const_set("Uploader#{uploader.object_id}".gsub('-', '_'), uploader)
426
+ const_set("Uploader#{uploader.object_id}".tr('-', '_'), uploader)
263
427
 
264
428
  if block_given?
265
429
  uploader.class_eval(&block)
@@ -292,120 +456,5 @@ module CarrierWave
292
456
 
293
457
  end # Extension
294
458
 
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
459
  end # Mount
411
460
  end # CarrierWave