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.
- checksums.yaml +5 -5
- data/README.md +307 -121
- data/lib/carrierwave/compatibility/paperclip.rb +0 -2
- data/lib/carrierwave/downloader/base.rb +83 -0
- data/lib/carrierwave/downloader/remote_file.rb +65 -0
- data/lib/carrierwave/error.rb +1 -0
- data/lib/carrierwave/locale/en.yml +7 -4
- data/lib/carrierwave/mount.rb +229 -180
- data/lib/carrierwave/mounter.rb +188 -0
- data/lib/carrierwave/orm/activerecord.rb +59 -24
- data/lib/carrierwave/processing/mini_magick.rb +137 -83
- data/lib/carrierwave/processing/rmagick.rb +65 -8
- data/lib/carrierwave/processing.rb +0 -1
- data/lib/carrierwave/sanitized_file.rb +67 -32
- data/lib/carrierwave/storage/abstract.rb +15 -2
- data/lib/carrierwave/storage/file.rb +69 -2
- data/lib/carrierwave/storage/fog.rb +177 -39
- data/lib/carrierwave/storage.rb +1 -7
- data/lib/carrierwave/test/matchers.rb +77 -12
- data/lib/carrierwave/uploader/cache.rb +74 -38
- data/lib/carrierwave/uploader/callbacks.rb +0 -2
- data/lib/carrierwave/uploader/configuration.rb +71 -13
- data/lib/carrierwave/uploader/content_type_blacklist.rb +48 -0
- data/lib/carrierwave/uploader/content_type_whitelist.rb +48 -0
- data/lib/carrierwave/uploader/default_url.rb +3 -5
- data/lib/carrierwave/uploader/download.rb +4 -74
- data/lib/carrierwave/uploader/extension_blacklist.rb +14 -10
- data/lib/carrierwave/uploader/extension_whitelist.rb +13 -10
- data/lib/carrierwave/uploader/file_size.rb +43 -0
- data/lib/carrierwave/uploader/mountable.rb +13 -8
- data/lib/carrierwave/uploader/processing.rb +10 -10
- data/lib/carrierwave/uploader/proxy.rb +6 -8
- data/lib/carrierwave/uploader/remove.rb +0 -2
- data/lib/carrierwave/uploader/serialization.rb +2 -4
- data/lib/carrierwave/uploader/store.rb +17 -24
- data/lib/carrierwave/uploader/url.rb +3 -5
- data/lib/carrierwave/uploader/versions.rb +123 -93
- data/lib/carrierwave/uploader.rb +6 -2
- data/lib/carrierwave/utilities/uri.rb +5 -6
- data/lib/carrierwave/utilities.rb +0 -3
- data/lib/carrierwave/validations/active_model.rb +3 -5
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +34 -8
- data/lib/generators/templates/uploader.rb +4 -8
- metadata +130 -57
- data/lib/carrierwave/locale/cs.yml +0 -11
- data/lib/carrierwave/locale/de.yml +0 -11
- data/lib/carrierwave/locale/el.yml +0 -11
- data/lib/carrierwave/locale/es.yml +0 -11
- data/lib/carrierwave/locale/fr.yml +0 -11
- data/lib/carrierwave/locale/ja.yml +0 -11
- data/lib/carrierwave/locale/nb.yml +0 -11
- data/lib/carrierwave/locale/nl.yml +0 -11
- data/lib/carrierwave/locale/pl.yml +0 -11
- data/lib/carrierwave/locale/pt-BR.yml +0 -11
- data/lib/carrierwave/locale/pt-PT.yml +0 -11
- data/lib/carrierwave/locale/ru.yml +0 -11
- data/lib/carrierwave/locale/sk.yml +0 -11
- data/lib/carrierwave/locale/tr.yml +0 -11
- data/lib/carrierwave/processing/mime_types.rb +0 -74
- data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -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
|
+
|
data/lib/carrierwave/error.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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}"
|
data/lib/carrierwave/mount.rb
CHANGED
@@ -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
|
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
|
-
|
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}).
|
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
|
-
|
150
|
+
#{column}.url(*args)
|
172
151
|
end
|
173
152
|
|
174
153
|
def #{column}_cache
|
175
|
-
_mounter(:#{column}).
|
154
|
+
_mounter(:#{column}).cache_names[0]
|
176
155
|
end
|
177
156
|
|
178
157
|
def #{column}_cache=(cache_name)
|
179
|
-
_mounter(:#{column}).
|
158
|
+
_mounter(:#{column}).cache_names = [cache_name]
|
180
159
|
end
|
181
160
|
|
182
161
|
def remote_#{column}_url
|
183
|
-
_mounter(:#{column}).
|
162
|
+
[_mounter(:#{column}).remote_urls].flatten[0]
|
184
163
|
end
|
185
164
|
|
186
165
|
def remote_#{column}_url=(url)
|
187
|
-
_mounter(:#{column}).
|
166
|
+
_mounter(:#{column}).remote_urls = [url]
|
188
167
|
end
|
189
168
|
|
190
|
-
def
|
191
|
-
_mounter(:#{column}).
|
169
|
+
def remote_#{column}_request_header=(header)
|
170
|
+
_mounter(:#{column}).remote_request_headers = [header]
|
192
171
|
end
|
193
172
|
|
194
|
-
def
|
195
|
-
|
196
|
-
|
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
|
-
|
203
|
-
|
177
|
+
mounter.clear! if mounter.remove?
|
178
|
+
write_uploader(mounter.serialization_column, mounter.identifiers.first)
|
204
179
|
end
|
205
180
|
|
206
|
-
def
|
207
|
-
_mounter(:#{column}).
|
181
|
+
def #{column}_identifier
|
182
|
+
_mounter(:#{column}).read_identifiers[0]
|
208
183
|
end
|
209
184
|
|
210
185
|
def #{column}_integrity_error
|
211
|
-
|
186
|
+
#{column}_integrity_errors.last
|
212
187
|
end
|
213
188
|
|
214
189
|
def #{column}_processing_error
|
215
|
-
|
190
|
+
#{column}_processing_errors.last
|
216
191
|
end
|
217
192
|
|
218
193
|
def #{column}_download_error
|
219
|
-
|
194
|
+
#{column}_download_errors.last
|
220
195
|
end
|
221
196
|
|
222
|
-
def
|
223
|
-
|
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}
|
227
|
-
|
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
|
231
|
-
|
308
|
+
def #{column}=(new_files)
|
309
|
+
_mounter(:#{column}).cache(new_files)
|
310
|
+
end
|
232
311
|
|
233
|
-
|
234
|
-
|
235
|
-
end
|
312
|
+
def #{column}_urls(*args)
|
313
|
+
_mounter(:#{column}).urls(*args)
|
236
314
|
end
|
237
315
|
|
238
|
-
def
|
239
|
-
|
316
|
+
def #{column}_cache
|
317
|
+
names = _mounter(:#{column}).cache_names
|
318
|
+
names.to_json if names.present?
|
240
319
|
end
|
241
320
|
|
242
|
-
def
|
243
|
-
|
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
|
250
|
-
_mounter(:#{column}).
|
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}".
|
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
|