carrierwave 1.3.4 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +278 -86
- data/lib/carrierwave/compatibility/paperclip.rb +4 -2
- data/lib/carrierwave/downloader/base.rb +101 -0
- data/lib/carrierwave/downloader/remote_file.rb +68 -0
- data/lib/carrierwave/locale/en.yml +9 -6
- data/lib/carrierwave/mount.rb +53 -61
- data/lib/carrierwave/mounter.rb +169 -77
- data/lib/carrierwave/orm/activerecord.rb +23 -58
- data/lib/carrierwave/processing/mini_magick.rb +137 -123
- data/lib/carrierwave/processing/rmagick.rb +47 -20
- data/lib/carrierwave/processing/vips.rb +315 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +91 -70
- data/lib/carrierwave/storage/abstract.rb +5 -5
- data/lib/carrierwave/storage/file.rb +6 -5
- data/lib/carrierwave/storage/fog.rb +114 -62
- data/lib/carrierwave/storage.rb +1 -0
- data/lib/carrierwave/test/matchers.rb +11 -7
- data/lib/carrierwave/uploader/cache.rb +40 -24
- data/lib/carrierwave/uploader/callbacks.rb +1 -1
- data/lib/carrierwave/uploader/configuration.rb +43 -20
- data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
- data/lib/carrierwave/uploader/content_type_denylist.rb +65 -0
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/download.rb +2 -123
- data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
- data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
- data/lib/carrierwave/uploader/file_size.rb +2 -2
- data/lib/carrierwave/uploader/mountable.rb +6 -0
- data/lib/carrierwave/uploader/processing.rb +45 -7
- data/lib/carrierwave/uploader/proxy.rb +17 -4
- data/lib/carrierwave/uploader/serialization.rb +1 -1
- data/lib/carrierwave/uploader/store.rb +73 -7
- data/lib/carrierwave/uploader/url.rb +7 -4
- data/lib/carrierwave/uploader/versions.rb +161 -111
- data/lib/carrierwave/uploader.rb +10 -17
- data/lib/carrierwave/utilities/file_name.rb +47 -0
- data/lib/carrierwave/utilities/uri.rb +14 -11
- data/lib/carrierwave/utilities.rb +1 -0
- data/lib/carrierwave/validations/active_model.rb +7 -9
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +22 -17
- data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +13 -4
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +115 -44
- data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
- data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
- data/lib/carrierwave/uploader/extension_blacklist.rb +0 -51
- data/lib/carrierwave/uploader/extension_whitelist.rb +0 -52
data/lib/carrierwave/mounter.rb
CHANGED
|
@@ -2,15 +2,49 @@ module CarrierWave
|
|
|
2
2
|
|
|
3
3
|
# this is an internal class, used by CarrierWave::Mount so that
|
|
4
4
|
# we don't pollute the model with a lot of methods.
|
|
5
|
-
class Mounter
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
class Mounter # :nodoc:
|
|
6
|
+
class Single < Mounter # :nodoc
|
|
7
|
+
def identifier
|
|
8
|
+
uploaders.first&.identifier
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def temporary_identifier
|
|
12
|
+
temporary_identifiers.first
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Multiple < Mounter # :nodoc
|
|
17
|
+
def identifier
|
|
18
|
+
uploaders.map(&:identifier).presence
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def temporary_identifier
|
|
22
|
+
temporary_identifiers.presence
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.build(record, column)
|
|
27
|
+
if record.class.uploader_options[column][:multiple]
|
|
28
|
+
Multiple.new(record, column)
|
|
29
|
+
else
|
|
30
|
+
Single.new(record, column)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
9
33
|
|
|
10
|
-
|
|
34
|
+
attr_reader :column, :record, :remote_urls, :remove,
|
|
35
|
+
:integrity_errors, :processing_errors, :download_errors
|
|
36
|
+
attr_accessor :remote_request_headers, :uploader_options
|
|
37
|
+
|
|
38
|
+
def initialize(record, column)
|
|
11
39
|
@record = record
|
|
12
40
|
@column = column
|
|
13
41
|
@options = record.class.uploader_options[column]
|
|
42
|
+
@download_errors = []
|
|
43
|
+
@processing_errors = []
|
|
44
|
+
@integrity_errors = []
|
|
45
|
+
|
|
46
|
+
@removed_uploaders = []
|
|
47
|
+
@added_uploaders = []
|
|
14
48
|
end
|
|
15
49
|
|
|
16
50
|
def uploader_class
|
|
@@ -32,27 +66,38 @@ module CarrierWave
|
|
|
32
66
|
def uploaders
|
|
33
67
|
@uploaders ||= read_identifiers.map do |identifier|
|
|
34
68
|
uploader = blank_uploader
|
|
35
|
-
uploader.retrieve_from_store!(identifier)
|
|
69
|
+
uploader.retrieve_from_store!(identifier)
|
|
36
70
|
uploader
|
|
37
71
|
end
|
|
38
72
|
end
|
|
39
73
|
|
|
40
74
|
def cache(new_files)
|
|
41
|
-
return if
|
|
75
|
+
return if !new_files.is_a?(Array) && new_files.blank?
|
|
76
|
+
old_uploaders = uploaders
|
|
42
77
|
@uploaders = new_files.map do |new_file|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
handle_error do
|
|
79
|
+
if new_file.is_a?(String)
|
|
80
|
+
if (uploader = old_uploaders.detect { |old_uploader| old_uploader.identifier == new_file })
|
|
81
|
+
uploader.staged = true
|
|
82
|
+
uploader
|
|
83
|
+
else
|
|
84
|
+
begin
|
|
85
|
+
uploader = blank_uploader
|
|
86
|
+
uploader.retrieve_from_cache!(new_file)
|
|
87
|
+
uploader
|
|
88
|
+
rescue CarrierWave::InvalidParameter
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
uploader = blank_uploader
|
|
94
|
+
uploader.cache!(new_file)
|
|
95
|
+
uploader
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end.reject(&:blank?)
|
|
99
|
+
@removed_uploaders += (old_uploaders - @uploaders)
|
|
100
|
+
write_temporary_identifier
|
|
56
101
|
end
|
|
57
102
|
|
|
58
103
|
def cache_names
|
|
@@ -60,45 +105,58 @@ module CarrierWave
|
|
|
60
105
|
end
|
|
61
106
|
|
|
62
107
|
def cache_names=(cache_names)
|
|
63
|
-
|
|
64
|
-
|
|
108
|
+
cache_names = cache_names.reject(&:blank?)
|
|
109
|
+
return if cache_names.blank?
|
|
110
|
+
clear_unstaged
|
|
111
|
+
cache_names.each do |cache_name|
|
|
65
112
|
uploader = blank_uploader
|
|
66
113
|
uploader.retrieve_from_cache!(cache_name)
|
|
67
|
-
uploader
|
|
114
|
+
@uploaders << uploader
|
|
115
|
+
rescue CarrierWave::InvalidParameter
|
|
116
|
+
# ignore
|
|
68
117
|
end
|
|
69
|
-
|
|
118
|
+
write_temporary_identifier
|
|
70
119
|
end
|
|
71
120
|
|
|
72
121
|
def remote_urls=(urls)
|
|
73
|
-
|
|
74
|
-
|
|
122
|
+
if urls.nil?
|
|
123
|
+
urls = []
|
|
124
|
+
else
|
|
125
|
+
urls = Array.wrap(urls).reject(&:blank?)
|
|
126
|
+
return if urls.blank?
|
|
127
|
+
end
|
|
128
|
+
@download_errors.clear
|
|
75
129
|
@remote_urls = urls
|
|
76
|
-
@download_error = nil
|
|
77
|
-
@integrity_error = nil
|
|
78
130
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
131
|
+
clear_unstaged
|
|
132
|
+
@remote_urls.zip(remote_request_headers || []) do |url, header|
|
|
133
|
+
handle_error do
|
|
134
|
+
uploader = blank_uploader
|
|
135
|
+
uploader.download!(url, header || {})
|
|
136
|
+
@uploaders << uploader
|
|
137
|
+
end
|
|
83
138
|
end
|
|
84
|
-
|
|
85
|
-
rescue CarrierWave::DownloadError => e
|
|
86
|
-
@download_error = e
|
|
87
|
-
raise e unless option(:ignore_download_errors)
|
|
88
|
-
rescue CarrierWave::ProcessingError => e
|
|
89
|
-
@processing_error = e
|
|
90
|
-
raise e unless option(:ignore_processing_errors)
|
|
91
|
-
rescue CarrierWave::IntegrityError => e
|
|
92
|
-
@integrity_error = e
|
|
93
|
-
raise e unless option(:ignore_integrity_errors)
|
|
139
|
+
write_temporary_identifier
|
|
94
140
|
end
|
|
95
141
|
|
|
96
142
|
def store!
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
143
|
+
uploaders.each(&:store!)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def write_identifier
|
|
147
|
+
return if record.frozen?
|
|
148
|
+
|
|
149
|
+
clear! if remove?
|
|
150
|
+
|
|
151
|
+
additions, remains = uploaders.partition(&:cached?)
|
|
152
|
+
existing_identifiers = (@removed_uploaders + remains).map(&:identifier)
|
|
153
|
+
additions.each do |uploader|
|
|
154
|
+
uploader.deduplicate(existing_identifiers)
|
|
155
|
+
existing_identifiers << uploader.identifier
|
|
101
156
|
end
|
|
157
|
+
@added_uploaders += additions
|
|
158
|
+
|
|
159
|
+
record.write_uploader(serialization_column, identifier)
|
|
102
160
|
end
|
|
103
161
|
|
|
104
162
|
def urls(*args)
|
|
@@ -109,50 +167,51 @@ module CarrierWave
|
|
|
109
167
|
uploaders.none?(&:present?)
|
|
110
168
|
end
|
|
111
169
|
|
|
170
|
+
def remove=(value)
|
|
171
|
+
uploaders # Ensure that uploaders are initialized based on the current value
|
|
172
|
+
@remove = value
|
|
173
|
+
write_temporary_identifier
|
|
174
|
+
end
|
|
175
|
+
|
|
112
176
|
def remove?
|
|
113
|
-
remove.present? && (remove
|
|
177
|
+
remove.present? && (remove.to_s !~ /\A0|false$\z/)
|
|
114
178
|
end
|
|
115
179
|
|
|
116
180
|
def remove!
|
|
117
|
-
uploaders.
|
|
181
|
+
uploaders.each(&:remove!)
|
|
182
|
+
clear!
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def clear!
|
|
186
|
+
@removed_uploaders += uploaders
|
|
187
|
+
@remove = nil
|
|
118
188
|
@uploaders = []
|
|
119
189
|
end
|
|
120
190
|
|
|
191
|
+
def reset_changes!
|
|
192
|
+
@removed_uploaders = []
|
|
193
|
+
@added_uploaders = []
|
|
194
|
+
end
|
|
195
|
+
|
|
121
196
|
def serialization_column
|
|
122
197
|
option(:mount_on) || column
|
|
123
198
|
end
|
|
124
199
|
|
|
125
|
-
def remove_previous
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if value.is_a?(String)
|
|
132
|
-
uploader = blank_uploader
|
|
133
|
-
uploader.retrieve_from_store!(value)
|
|
134
|
-
uploader
|
|
135
|
-
else
|
|
136
|
-
value
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
after_paths = after.reject(&:blank?).map do |value|
|
|
140
|
-
if value.is_a?(String)
|
|
141
|
-
uploader = blank_uploader
|
|
142
|
-
uploader.retrieve_from_store!(value)
|
|
143
|
-
uploader
|
|
144
|
-
else
|
|
145
|
-
value
|
|
146
|
-
end.path
|
|
147
|
-
end
|
|
148
|
-
before.each do |uploader|
|
|
149
|
-
if uploader.remove_previously_stored_files_after_update and not after_paths.include?(uploader.path)
|
|
150
|
-
uploader.remove!
|
|
151
|
-
end
|
|
152
|
-
end
|
|
200
|
+
def remove_previous
|
|
201
|
+
current_paths = uploaders.map(&:path)
|
|
202
|
+
@removed_uploaders
|
|
203
|
+
.reject {|uploader| current_paths.include?(uploader.path) }
|
|
204
|
+
.each { |uploader| uploader.remove! if uploader.remove_previously_stored_files_after_update }
|
|
205
|
+
reset_changes!
|
|
153
206
|
end
|
|
154
207
|
|
|
155
|
-
|
|
208
|
+
def remove_added
|
|
209
|
+
current_paths = (@removed_uploaders + uploaders.select(&:staged)).map(&:path)
|
|
210
|
+
@added_uploaders
|
|
211
|
+
.reject {|uploader| current_paths.include?(uploader.path) }
|
|
212
|
+
.each { |uploader| uploader.remove! }
|
|
213
|
+
reset_changes!
|
|
214
|
+
end
|
|
156
215
|
|
|
157
216
|
private
|
|
158
217
|
|
|
@@ -161,5 +220,38 @@ module CarrierWave
|
|
|
161
220
|
self.uploader_options[name] ||= record.class.uploader_option(column, name)
|
|
162
221
|
end
|
|
163
222
|
|
|
223
|
+
def clear_unstaged
|
|
224
|
+
uploaders # Ensure that uploaders are initialized based on the current value
|
|
225
|
+
staged, unstaged = @uploaders.partition(&:staged)
|
|
226
|
+
@uploaders = staged
|
|
227
|
+
@removed_uploaders += unstaged
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def handle_error
|
|
231
|
+
yield
|
|
232
|
+
rescue CarrierWave::DownloadError => e
|
|
233
|
+
@download_errors << e
|
|
234
|
+
raise e unless option(:ignore_download_errors)
|
|
235
|
+
rescue CarrierWave::ProcessingError => e
|
|
236
|
+
@processing_errors << e
|
|
237
|
+
raise e unless option(:ignore_processing_errors)
|
|
238
|
+
rescue CarrierWave::IntegrityError => e
|
|
239
|
+
@integrity_errors << e
|
|
240
|
+
raise e unless option(:ignore_integrity_errors)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def write_temporary_identifier
|
|
244
|
+
return if record.frozen?
|
|
245
|
+
|
|
246
|
+
record.write_uploader(serialization_column, temporary_identifier)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def temporary_identifiers
|
|
250
|
+
if remove?
|
|
251
|
+
[]
|
|
252
|
+
else
|
|
253
|
+
uploaders.map { |uploader| uploader.temporary_identifier }
|
|
254
|
+
end
|
|
255
|
+
end
|
|
164
256
|
end # Mounter
|
|
165
257
|
end # CarrierWave
|
|
@@ -6,36 +6,6 @@ module CarrierWave
|
|
|
6
6
|
|
|
7
7
|
include CarrierWave::Mount
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
# See +CarrierWave::Mount#mount_uploader+ for documentation
|
|
11
|
-
#
|
|
12
|
-
def mount_uploader(column, uploader=nil, options={}, &block)
|
|
13
|
-
super
|
|
14
|
-
|
|
15
|
-
class_eval <<-RUBY, __FILE__, __LINE__+1
|
|
16
|
-
def remote_#{column}_url=(url)
|
|
17
|
-
column = _mounter(:#{column}).serialization_column
|
|
18
|
-
__send__(:"\#{column}_will_change!")
|
|
19
|
-
super
|
|
20
|
-
end
|
|
21
|
-
RUBY
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
##
|
|
25
|
-
# See +CarrierWave::Mount#mount_uploaders+ for documentation
|
|
26
|
-
#
|
|
27
|
-
def mount_uploaders(column, uploader=nil, options={}, &block)
|
|
28
|
-
super
|
|
29
|
-
|
|
30
|
-
class_eval <<-RUBY, __FILE__, __LINE__+1
|
|
31
|
-
def remote_#{column}_urls=(url)
|
|
32
|
-
column = _mounter(:#{column}).serialization_column
|
|
33
|
-
__send__(:"\#{column}_will_change!")
|
|
34
|
-
super
|
|
35
|
-
end
|
|
36
|
-
RUBY
|
|
37
|
-
end
|
|
38
|
-
|
|
39
9
|
private
|
|
40
10
|
|
|
41
11
|
def mount_base(column, uploader=nil, options={}, &block)
|
|
@@ -54,35 +24,21 @@ module CarrierWave
|
|
|
54
24
|
|
|
55
25
|
after_save :"store_#{column}!"
|
|
56
26
|
before_save :"write_#{column}_identifier"
|
|
27
|
+
if ::ActiveRecord.try(:run_after_transaction_callbacks_in_order_defined)
|
|
28
|
+
after_commit :"remove_previously_stored_#{column}", :on => :update
|
|
29
|
+
after_commit :"reset_previous_changes_for_#{column}"
|
|
30
|
+
after_commit :"mark_remove_#{column}_false", :on => :update
|
|
31
|
+
else
|
|
32
|
+
after_commit :"mark_remove_#{column}_false", :on => :update
|
|
33
|
+
after_commit :"reset_previous_changes_for_#{column}"
|
|
34
|
+
after_commit :"remove_previously_stored_#{column}", :on => :update
|
|
35
|
+
end
|
|
57
36
|
after_commit :"remove_#{column}!", :on => :destroy
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
after_save :"store_previous_changes_for_#{column}"
|
|
61
|
-
after_commit :"remove_previously_stored_#{column}", :on => :update
|
|
62
|
-
|
|
63
|
-
class_eval <<-RUBY, __FILE__, __LINE__+1
|
|
64
|
-
def #{column}=(new_file)
|
|
65
|
-
column = _mounter(:#{column}).serialization_column
|
|
66
|
-
if !(new_file.blank? && __send__(:#{column}).blank?)
|
|
67
|
-
__send__(:"\#{column}_will_change!")
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
super
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def remove_#{column}=(value)
|
|
74
|
-
column = _mounter(:#{column}).serialization_column
|
|
75
|
-
__send__(:"\#{column}_will_change!")
|
|
76
|
-
super
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def remove_#{column}!
|
|
80
|
-
self.remove_#{column} = true
|
|
81
|
-
write_#{column}_identifier
|
|
82
|
-
self.remove_#{column} = false
|
|
83
|
-
super
|
|
84
|
-
end
|
|
37
|
+
after_rollback :"remove_rolled_back_#{column}"
|
|
85
38
|
|
|
39
|
+
mod = Module.new
|
|
40
|
+
prepend mod
|
|
41
|
+
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
|
|
86
42
|
# Reset cached mounter on record reload
|
|
87
43
|
def reload(*)
|
|
88
44
|
@_mounters = nil
|
|
@@ -91,7 +47,16 @@ module CarrierWave
|
|
|
91
47
|
|
|
92
48
|
# Reset cached mounter on record dup
|
|
93
49
|
def initialize_dup(other)
|
|
94
|
-
|
|
50
|
+
old_uploaders = _mounter(:"#{column}").uploaders
|
|
51
|
+
super
|
|
52
|
+
@_mounters[:"#{column}"] = nil
|
|
53
|
+
# The attribute needs to be cleared to prevent it from picked up as identifier
|
|
54
|
+
write_uploader(_mounter(:#{column}).serialization_column, nil)
|
|
55
|
+
_mounter(:"#{column}").cache(old_uploaders)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def write_#{column}_identifier
|
|
59
|
+
return unless has_attribute?(_mounter(:#{column}).serialization_column)
|
|
95
60
|
super
|
|
96
61
|
end
|
|
97
62
|
RUBY
|