carrierwave 2.2.1 → 3.0.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 +4 -4
- data/README.md +137 -67
- data/lib/carrierwave/compatibility/paperclip.rb +4 -2
- data/lib/carrierwave/downloader/base.rb +27 -13
- data/lib/carrierwave/downloader/remote_file.rb +12 -9
- data/lib/carrierwave/locale/en.yml +5 -3
- data/lib/carrierwave/mount.rb +31 -50
- data/lib/carrierwave/mounter.rb +115 -50
- data/lib/carrierwave/orm/activerecord.rb +14 -60
- data/lib/carrierwave/processing/mini_magick.rb +15 -13
- data/lib/carrierwave/processing/rmagick.rb +11 -15
- data/lib/carrierwave/processing/vips.rb +12 -12
- data/lib/carrierwave/sanitized_file.rb +49 -77
- data/lib/carrierwave/storage/abstract.rb +5 -5
- data/lib/carrierwave/storage/file.rb +6 -5
- data/lib/carrierwave/storage/fog.rb +75 -67
- data/lib/carrierwave/test/matchers.rb +11 -7
- data/lib/carrierwave/uploader/cache.rb +18 -10
- data/lib/carrierwave/uploader/callbacks.rb +1 -1
- data/lib/carrierwave/uploader/configuration.rb +10 -4
- data/lib/carrierwave/uploader/{content_type_whitelist.rb → content_type_allowlist.rb} +17 -15
- data/lib/carrierwave/uploader/{content_type_blacklist.rb → content_type_denylist.rb} +19 -14
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/{extension_whitelist.rb → extension_allowlist.rb} +17 -15
- data/lib/carrierwave/uploader/{extension_blacklist.rb → extension_denylist.rb} +18 -13
- data/lib/carrierwave/uploader/file_size.rb +2 -2
- data/lib/carrierwave/uploader/processing.rb +42 -7
- data/lib/carrierwave/uploader/proxy.rb +16 -3
- data/lib/carrierwave/uploader/store.rb +44 -6
- data/lib/carrierwave/uploader/url.rb +1 -1
- data/lib/carrierwave/uploader/versions.rb +150 -132
- data/lib/carrierwave/uploader.rb +10 -8
- 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 +4 -6
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +9 -17
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +31 -43
- /data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +0 -0
data/lib/carrierwave/mounter.rb
CHANGED
@@ -2,18 +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
|
33
|
+
|
34
|
+
attr_reader :column, :record, :remote_urls, :remove,
|
35
|
+
:integrity_errors, :processing_errors, :download_errors
|
36
|
+
attr_accessor :remote_request_headers, :uploader_options
|
9
37
|
|
10
|
-
def initialize(record, column
|
38
|
+
def initialize(record, column)
|
11
39
|
@record = record
|
12
40
|
@column = column
|
13
41
|
@options = record.class.uploader_options[column]
|
14
42
|
@download_errors = []
|
15
43
|
@processing_errors = []
|
16
44
|
@integrity_errors = []
|
45
|
+
|
46
|
+
@removed_uploaders = []
|
47
|
+
@added_uploaders = []
|
17
48
|
end
|
18
49
|
|
19
50
|
def uploader_class
|
@@ -35,7 +66,7 @@ module CarrierWave
|
|
35
66
|
def uploaders
|
36
67
|
@uploaders ||= read_identifiers.map do |identifier|
|
37
68
|
uploader = blank_uploader
|
38
|
-
uploader.retrieve_from_store!(identifier)
|
69
|
+
uploader.retrieve_from_store!(identifier)
|
39
70
|
uploader
|
40
71
|
end
|
41
72
|
end
|
@@ -46,7 +77,7 @@ module CarrierWave
|
|
46
77
|
@uploaders = new_files.map do |new_file|
|
47
78
|
handle_error do
|
48
79
|
if new_file.is_a?(String)
|
49
|
-
if (uploader = old_uploaders.detect { |
|
80
|
+
if (uploader = old_uploaders.detect { |old_uploader| old_uploader.identifier == new_file })
|
50
81
|
uploader.staged = true
|
51
82
|
uploader
|
52
83
|
else
|
@@ -64,7 +95,9 @@ module CarrierWave
|
|
64
95
|
uploader
|
65
96
|
end
|
66
97
|
end
|
67
|
-
end.
|
98
|
+
end.reject(&:blank?)
|
99
|
+
@removed_uploaders += (old_uploaders - @uploaders)
|
100
|
+
write_temporary_identifier
|
68
101
|
end
|
69
102
|
|
70
103
|
def cache_names
|
@@ -76,33 +109,51 @@ module CarrierWave
|
|
76
109
|
return if cache_names.blank?
|
77
110
|
clear_unstaged
|
78
111
|
cache_names.each do |cache_name|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# ignore
|
85
|
-
end
|
112
|
+
uploader = blank_uploader
|
113
|
+
uploader.retrieve_from_cache!(cache_name)
|
114
|
+
@uploaders << uploader
|
115
|
+
rescue CarrierWave::InvalidParameter
|
116
|
+
# ignore
|
86
117
|
end
|
118
|
+
write_temporary_identifier
|
87
119
|
end
|
88
120
|
|
89
121
|
def remote_urls=(urls)
|
90
|
-
|
91
|
-
|
122
|
+
if urls.nil?
|
123
|
+
urls = []
|
124
|
+
else
|
125
|
+
urls = Array.wrap(urls).reject(&:blank?)
|
126
|
+
return if urls.blank?
|
127
|
+
end
|
92
128
|
@remote_urls = urls
|
93
129
|
|
94
130
|
clear_unstaged
|
95
|
-
|
131
|
+
@remote_urls.zip(remote_request_headers || []) do |url, header|
|
96
132
|
handle_error do
|
97
133
|
uploader = blank_uploader
|
98
134
|
uploader.download!(url, header || {})
|
99
135
|
@uploaders << uploader
|
100
136
|
end
|
101
137
|
end
|
138
|
+
write_temporary_identifier
|
102
139
|
end
|
103
140
|
|
104
141
|
def store!
|
105
|
-
uploaders.
|
142
|
+
additions, remains = uploaders.partition(&:cached?)
|
143
|
+
existing_paths = (@removed_uploaders + remains).map(&:store_path)
|
144
|
+
additions.each do |uploader|
|
145
|
+
uploader.deduplicate(existing_paths)
|
146
|
+
uploader.store!
|
147
|
+
existing_paths << uploader.store_path
|
148
|
+
end
|
149
|
+
@added_uploaders += additions
|
150
|
+
end
|
151
|
+
|
152
|
+
def write_identifier
|
153
|
+
return if record.frozen?
|
154
|
+
|
155
|
+
clear! if remove?
|
156
|
+
record.write_uploader(serialization_column, identifier)
|
106
157
|
end
|
107
158
|
|
108
159
|
def urls(*args)
|
@@ -113,52 +164,50 @@ module CarrierWave
|
|
113
164
|
uploaders.none?(&:present?)
|
114
165
|
end
|
115
166
|
|
167
|
+
def remove=(value)
|
168
|
+
@remove = value
|
169
|
+
write_temporary_identifier
|
170
|
+
end
|
171
|
+
|
116
172
|
def remove?
|
117
|
-
remove.present? && (remove
|
173
|
+
remove.present? && (remove.to_s !~ /\A0|false$\z/)
|
118
174
|
end
|
119
175
|
|
120
176
|
def remove!
|
121
|
-
uploaders.
|
122
|
-
|
177
|
+
uploaders.each(&:remove!)
|
178
|
+
clear!
|
123
179
|
end
|
124
180
|
|
125
181
|
def clear!
|
182
|
+
@removed_uploaders += uploaders
|
183
|
+
@remove = nil
|
126
184
|
@uploaders = []
|
127
185
|
end
|
128
186
|
|
187
|
+
def reset_changes!
|
188
|
+
@removed_uploaders = []
|
189
|
+
@added_uploaders = []
|
190
|
+
end
|
191
|
+
|
129
192
|
def serialization_column
|
130
193
|
option(:mount_on) || column
|
131
194
|
end
|
132
195
|
|
133
|
-
def remove_previous
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
if value.is_a?(String)
|
140
|
-
uploader = blank_uploader
|
141
|
-
uploader.retrieve_from_store!(value)
|
142
|
-
uploader
|
143
|
-
else
|
144
|
-
value
|
145
|
-
end
|
146
|
-
end
|
147
|
-
after_paths = after.reject(&:blank?).map do |value|
|
148
|
-
if value.is_a?(String)
|
149
|
-
uploader = blank_uploader
|
150
|
-
uploader.retrieve_from_store!(value)
|
151
|
-
uploader
|
152
|
-
else
|
153
|
-
value
|
154
|
-
end.path
|
155
|
-
end
|
156
|
-
before.each do |uploader|
|
157
|
-
uploader.remove! if uploader.remove_previously_stored_files_after_update && !after_paths.include?(uploader.path)
|
158
|
-
end
|
196
|
+
def remove_previous
|
197
|
+
current_paths = uploaders.map(&:path)
|
198
|
+
@removed_uploaders
|
199
|
+
.reject {|uploader| current_paths.include?(uploader.path) }
|
200
|
+
.each { |uploader| uploader.remove! if uploader.remove_previously_stored_files_after_update }
|
201
|
+
reset_changes!
|
159
202
|
end
|
160
203
|
|
161
|
-
|
204
|
+
def remove_added
|
205
|
+
current_paths = (@removed_uploaders + uploaders.select(&:staged)).map(&:path)
|
206
|
+
@added_uploaders
|
207
|
+
.reject {|uploader| current_paths.include?(uploader.path) }
|
208
|
+
.each { |uploader| uploader.remove! }
|
209
|
+
reset_changes!
|
210
|
+
end
|
162
211
|
|
163
212
|
private
|
164
213
|
|
@@ -169,7 +218,9 @@ module CarrierWave
|
|
169
218
|
|
170
219
|
def clear_unstaged
|
171
220
|
@uploaders ||= []
|
172
|
-
@uploaders.
|
221
|
+
staged, unstaged = @uploaders.partition(&:staged)
|
222
|
+
@uploaders = staged
|
223
|
+
@removed_uploaders += unstaged
|
173
224
|
end
|
174
225
|
|
175
226
|
def handle_error
|
@@ -184,5 +235,19 @@ module CarrierWave
|
|
184
235
|
@integrity_errors << e
|
185
236
|
raise e unless option(:ignore_integrity_errors)
|
186
237
|
end
|
238
|
+
|
239
|
+
def write_temporary_identifier
|
240
|
+
return if record.frozen?
|
241
|
+
|
242
|
+
record.write_uploader(serialization_column, temporary_identifier)
|
243
|
+
end
|
244
|
+
|
245
|
+
def temporary_identifiers
|
246
|
+
if remove?
|
247
|
+
[]
|
248
|
+
else
|
249
|
+
uploaders.map { |uploader| uploader.temporary_identifier }
|
250
|
+
end
|
251
|
+
end
|
187
252
|
end # Mounter
|
188
253
|
end # CarrierWave
|
@@ -6,40 +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
|
-
mod = Module.new
|
16
|
-
prepend mod
|
17
|
-
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
|
18
|
-
def remote_#{column}_url=(url)
|
19
|
-
column = _mounter(:#{column}).serialization_column
|
20
|
-
__send__(:"\#{column}_will_change!")
|
21
|
-
super
|
22
|
-
end
|
23
|
-
RUBY
|
24
|
-
end
|
25
|
-
|
26
|
-
##
|
27
|
-
# See +CarrierWave::Mount#mount_uploaders+ for documentation
|
28
|
-
#
|
29
|
-
def mount_uploaders(column, uploader=nil, options={}, &block)
|
30
|
-
super
|
31
|
-
|
32
|
-
mod = Module.new
|
33
|
-
prepend mod
|
34
|
-
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
|
35
|
-
def remote_#{column}_urls=(url)
|
36
|
-
column = _mounter(:#{column}).serialization_column
|
37
|
-
__send__(:"\#{column}_will_change!")
|
38
|
-
super
|
39
|
-
end
|
40
|
-
RUBY
|
41
|
-
end
|
42
|
-
|
43
9
|
private
|
44
10
|
|
45
11
|
def mount_base(column, uploader=nil, options={}, &block)
|
@@ -56,39 +22,18 @@ module CarrierWave
|
|
56
22
|
validates_processing_of column if uploader_option(column.to_sym, :validate_processing)
|
57
23
|
validates_download_of column if uploader_option(column.to_sym, :validate_download)
|
58
24
|
|
25
|
+
after_save :"store_#{column}!"
|
59
26
|
before_save :"write_#{column}_identifier"
|
60
|
-
after_save :"store_previous_changes_for_#{column}"
|
61
27
|
after_commit :"remove_#{column}!", :on => :destroy
|
62
28
|
after_commit :"mark_remove_#{column}_false", :on => :update
|
29
|
+
|
30
|
+
after_commit :"reset_previous_changes_for_#{column}"
|
63
31
|
after_commit :"remove_previously_stored_#{column}", :on => :update
|
64
|
-
|
32
|
+
after_rollback :"remove_rolled_back_#{column}"
|
65
33
|
|
66
34
|
mod = Module.new
|
67
35
|
prepend mod
|
68
36
|
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
|
69
|
-
def #{column}=(new_file)
|
70
|
-
column = _mounter(:#{column}).serialization_column
|
71
|
-
if !(new_file.blank? && __send__(:#{column}).blank?)
|
72
|
-
__send__(:"\#{column}_will_change!")
|
73
|
-
end
|
74
|
-
|
75
|
-
super
|
76
|
-
end
|
77
|
-
|
78
|
-
def remove_#{column}=(value)
|
79
|
-
column = _mounter(:#{column}).serialization_column
|
80
|
-
result = super
|
81
|
-
__send__(:"\#{column}_will_change!") if _mounter(:#{column}).remove?
|
82
|
-
result
|
83
|
-
end
|
84
|
-
|
85
|
-
def remove_#{column}!
|
86
|
-
self.remove_#{column} = true
|
87
|
-
write_#{column}_identifier
|
88
|
-
self.remove_#{column} = false
|
89
|
-
super
|
90
|
-
end
|
91
|
-
|
92
37
|
# Reset cached mounter on record reload
|
93
38
|
def reload(*)
|
94
39
|
@_mounters = nil
|
@@ -97,7 +42,16 @@ module CarrierWave
|
|
97
42
|
|
98
43
|
# Reset cached mounter on record dup
|
99
44
|
def initialize_dup(other)
|
100
|
-
|
45
|
+
old_uploaders = _mounter(:"#{column}").uploaders
|
46
|
+
@_mounters[:"#{column}"] = nil
|
47
|
+
# The attribute needs to be cleared to prevent it from picked up as identifier
|
48
|
+
write_attribute(:"#{column}", nil)
|
49
|
+
super
|
50
|
+
_mounter(:"#{column}").cache(old_uploaders)
|
51
|
+
end
|
52
|
+
|
53
|
+
def write_#{column}_identifier
|
54
|
+
return unless has_attribute?(_mounter(:#{column}).serialization_column)
|
101
55
|
super
|
102
56
|
end
|
103
57
|
RUBY
|
@@ -88,7 +88,7 @@ module CarrierWave
|
|
88
88
|
#
|
89
89
|
# === Parameters
|
90
90
|
#
|
91
|
-
# [format (#to_s)] an
|
91
|
+
# [format (#to_s)] an abbreviation of the format
|
92
92
|
#
|
93
93
|
# === Yields
|
94
94
|
#
|
@@ -264,7 +264,8 @@ module CarrierWave
|
|
264
264
|
|
265
265
|
image.run_command("identify", current_path)
|
266
266
|
rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
|
267
|
-
message
|
267
|
+
raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
|
268
|
+
message = I18n.translate(:"errors.messages.processing_error")
|
268
269
|
raise CarrierWave::ProcessingError, message
|
269
270
|
ensure
|
270
271
|
image.destroy! if image
|
@@ -306,26 +307,27 @@ module CarrierWave
|
|
306
307
|
|
307
308
|
if File.extname(result.path) != File.extname(current_path)
|
308
309
|
move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
|
309
|
-
file.content_type = ::
|
310
|
+
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
|
310
311
|
file.move_to(move_to, permissions, directory_permissions)
|
311
312
|
end
|
312
313
|
rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
|
313
|
-
message
|
314
|
+
raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
|
315
|
+
message = I18n.translate(:"errors.messages.processing_error")
|
314
316
|
raise CarrierWave::ProcessingError, message
|
315
317
|
end
|
316
318
|
|
317
|
-
|
319
|
+
private
|
318
320
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
end
|
321
|
+
def resolve_dimensions(*dimensions)
|
322
|
+
dimensions.map do |value|
|
323
|
+
next value unless value.instance_of?(Proc)
|
324
|
+
value.arity >= 1 ? value.call(self) : value.call
|
324
325
|
end
|
326
|
+
end
|
325
327
|
|
326
|
-
|
327
|
-
|
328
|
-
|
328
|
+
def mini_magick_image
|
329
|
+
::MiniMagick::Image.read(read)
|
330
|
+
end
|
329
331
|
|
330
332
|
end # MiniMagick
|
331
333
|
end # CarrierWave
|
@@ -62,10 +62,12 @@ module CarrierWave
|
|
62
62
|
begin
|
63
63
|
require "rmagick"
|
64
64
|
rescue LoadError
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
begin
|
66
|
+
require "RMagick"
|
67
|
+
rescue LoadError => e
|
68
|
+
e.message << " (You may need to install the rmagick gem)"
|
69
|
+
raise e
|
70
|
+
end
|
69
71
|
end
|
70
72
|
|
71
73
|
prepend Module.new {
|
@@ -109,7 +111,7 @@ module CarrierWave
|
|
109
111
|
#
|
110
112
|
# === Parameters
|
111
113
|
#
|
112
|
-
# [format (#to_s)] an
|
114
|
+
# [format (#to_s)] an abbreviation of the format
|
113
115
|
#
|
114
116
|
# === Yields
|
115
117
|
#
|
@@ -228,13 +230,7 @@ module CarrierWave
|
|
228
230
|
height = dimension_from height
|
229
231
|
manipulate! do |img|
|
230
232
|
img.resize_to_fit!(width, height)
|
231
|
-
|
232
|
-
if background == :transparent
|
233
|
-
filled = new_img.matte_floodfill(1, 1)
|
234
|
-
else
|
235
|
-
filled = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
|
236
|
-
end
|
237
|
-
destroy_image(new_img)
|
233
|
+
filled = ::Magick::Image.new(width, height) { |image| image.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
|
238
234
|
filled.composite!(img, gravity, ::Magick::OverCompositeOp)
|
239
235
|
destroy_image(img)
|
240
236
|
filled = yield(filled) if block_given?
|
@@ -363,15 +359,15 @@ module CarrierWave
|
|
363
359
|
if options[:format] || @format
|
364
360
|
frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
|
365
361
|
move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
|
366
|
-
file.content_type = ::
|
362
|
+
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
|
367
363
|
file.move_to(move_to, permissions, directory_permissions)
|
368
364
|
else
|
369
365
|
frames.write(current_path, &write_block)
|
370
366
|
end
|
371
367
|
|
372
368
|
destroy_image(frames)
|
373
|
-
rescue ::Magick::ImageMagickError
|
374
|
-
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.
|
369
|
+
rescue ::Magick::ImageMagickError
|
370
|
+
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.processing_error")
|
375
371
|
end
|
376
372
|
|
377
373
|
private
|
@@ -259,26 +259,26 @@ module CarrierWave
|
|
259
259
|
|
260
260
|
if File.extname(result.path) != File.extname(current_path)
|
261
261
|
move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
|
262
|
-
file.content_type = ::
|
262
|
+
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
|
263
263
|
file.move_to(move_to, permissions, directory_permissions)
|
264
264
|
end
|
265
|
-
rescue ::Vips::Error
|
266
|
-
message = I18n.translate(:"errors.messages.
|
265
|
+
rescue ::Vips::Error
|
266
|
+
message = I18n.translate(:"errors.messages.processing_error")
|
267
267
|
raise CarrierWave::ProcessingError, message
|
268
268
|
end
|
269
269
|
|
270
|
-
|
270
|
+
private
|
271
271
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
end
|
272
|
+
def resolve_dimensions(*dimensions)
|
273
|
+
dimensions.map do |value|
|
274
|
+
next value unless value.instance_of?(Proc)
|
275
|
+
value.arity >= 1 ? value.call(self) : value.call
|
277
276
|
end
|
277
|
+
end
|
278
278
|
|
279
|
-
|
280
|
-
|
281
|
-
|
279
|
+
def vips_image
|
280
|
+
::Vips::Image.new_from_buffer(read, "")
|
281
|
+
end
|
282
282
|
|
283
283
|
end # Vips
|
284
284
|
end # CarrierWave
|