carrierwave 0.9.0 → 2.1.1
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.
Potentially problematic release.
This version of carrierwave might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/README.md +362 -116
- data/lib/carrierwave/compatibility/paperclip.rb +29 -21
- 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 +238 -186
- data/lib/carrierwave/mounter.rb +188 -0
- data/lib/carrierwave/orm/activerecord.rb +60 -24
- data/lib/carrierwave/processing/mini_magick.rb +139 -78
- data/lib/carrierwave/processing/rmagick.rb +68 -23
- data/lib/carrierwave/processing.rb +0 -1
- data/lib/carrierwave/sanitized_file.rb +67 -27
- data/lib/carrierwave/storage/abstract.rb +15 -2
- data/lib/carrierwave/storage/file.rb +69 -2
- data/lib/carrierwave/storage/fog.rb +180 -41
- 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 +72 -6
- 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 +5 -69
- 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 +15 -17
- data/lib/carrierwave/uploader/proxy.rb +16 -7
- data/lib/carrierwave/uploader/remove.rb +0 -2
- data/lib/carrierwave/uploader/serialization.rb +3 -5
- data/lib/carrierwave/uploader/store.rb +17 -24
- data/lib/carrierwave/uploader/url.rb +3 -5
- data/lib/carrierwave/uploader/versions.rb +117 -86
- data/lib/carrierwave/uploader.rb +6 -2
- data/lib/carrierwave/utilities/uri.rb +5 -6
- data/lib/carrierwave/utilities.rb +1 -3
- data/lib/carrierwave/validations/active_model.rb +3 -7
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +36 -3
- data/lib/generators/templates/uploader.rb +4 -8
- data/lib/generators/uploader_generator.rb +1 -1
- metadata +195 -94
- data/lib/carrierwave/locale/cs.yml +0 -11
- data/lib/carrierwave/locale/de.yml +0 -11
- data/lib/carrierwave/locale/nl.yml +0 -11
- data/lib/carrierwave/locale/sk.yml +0 -11
- 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
|
##
|
@@ -69,6 +67,13 @@ module CarrierWave
|
|
69
67
|
e.message << " (You may need to install the rmagick gem)"
|
70
68
|
raise e
|
71
69
|
end
|
70
|
+
|
71
|
+
prepend Module.new {
|
72
|
+
def initialize(*)
|
73
|
+
super
|
74
|
+
@format = nil
|
75
|
+
end
|
76
|
+
}
|
72
77
|
end
|
73
78
|
|
74
79
|
module ClassMethods
|
@@ -135,6 +140,8 @@ module CarrierWave
|
|
135
140
|
# [Magick::Image] additional manipulations to perform
|
136
141
|
#
|
137
142
|
def resize_to_limit(width, height)
|
143
|
+
width = dimension_from width
|
144
|
+
height = dimension_from height
|
138
145
|
manipulate! do |img|
|
139
146
|
geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
|
140
147
|
new_img = img.change_geometry(geometry) do |new_width, new_height|
|
@@ -164,6 +171,8 @@ module CarrierWave
|
|
164
171
|
# [Magick::Image] additional manipulations to perform
|
165
172
|
#
|
166
173
|
def resize_to_fit(width, height)
|
174
|
+
width = dimension_from width
|
175
|
+
height = dimension_from height
|
167
176
|
manipulate! do |img|
|
168
177
|
img.resize_to_fit!(width, height)
|
169
178
|
img = yield(img) if block_given?
|
@@ -188,6 +197,8 @@ module CarrierWave
|
|
188
197
|
# [Magick::Image] additional manipulations to perform
|
189
198
|
#
|
190
199
|
def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
|
200
|
+
width = dimension_from width
|
201
|
+
height = dimension_from height
|
191
202
|
manipulate! do |img|
|
192
203
|
img.crop_resized!(width, height, gravity)
|
193
204
|
img = yield(img) if block_given?
|
@@ -213,9 +224,11 @@ module CarrierWave
|
|
213
224
|
# [Magick::Image] additional manipulations to perform
|
214
225
|
#
|
215
226
|
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
|
227
|
+
width = dimension_from width
|
228
|
+
height = dimension_from height
|
216
229
|
manipulate! do |img|
|
217
230
|
img.resize_to_fit!(width, height)
|
218
|
-
new_img = ::Magick::Image.new(width, height) { self.background_color = 'rgba(255,255,255,0)' }
|
231
|
+
new_img = ::Magick::Image.new(width, height) { self.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
|
219
232
|
if background == :transparent
|
220
233
|
filled = new_img.matte_floodfill(1, 1)
|
221
234
|
else
|
@@ -251,6 +264,28 @@ module CarrierWave
|
|
251
264
|
end
|
252
265
|
end
|
253
266
|
|
267
|
+
##
|
268
|
+
# Returns the width of the image.
|
269
|
+
#
|
270
|
+
# === Returns
|
271
|
+
#
|
272
|
+
# [Integer] the image's width in pixels
|
273
|
+
#
|
274
|
+
def width
|
275
|
+
rmagick_image.columns
|
276
|
+
end
|
277
|
+
|
278
|
+
##
|
279
|
+
# Returns the height of the image.
|
280
|
+
#
|
281
|
+
# === Returns
|
282
|
+
#
|
283
|
+
# [Integer] the image's height in pixels
|
284
|
+
#
|
285
|
+
def height
|
286
|
+
rmagick_image.rows
|
287
|
+
end
|
288
|
+
|
254
289
|
##
|
255
290
|
# Manipulate the image with RMagick. This method will load up an image
|
256
291
|
# and then pass each of its frames to the supplied block. It will then
|
@@ -315,46 +350,56 @@ module CarrierWave
|
|
315
350
|
|
316
351
|
read_block = create_info_block(options[:read])
|
317
352
|
image = ::Magick::Image.read(current_path, &read_block)
|
353
|
+
frames = ::Magick::ImageList.new
|
318
354
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
processed_frame = if block_given?
|
323
|
-
yield *[frame, index, options].take(block.arity)
|
324
|
-
else
|
325
|
-
frame
|
326
|
-
end
|
327
|
-
list << processed_frame if processed_frame
|
328
|
-
end
|
329
|
-
block_given? ? list : list.append(true)
|
330
|
-
else
|
331
|
-
frame = image.first
|
332
|
-
frame = yield( *[frame, 0, options].take(block.arity) ) if block_given?
|
333
|
-
frame
|
355
|
+
image.each_with_index do |frame, index|
|
356
|
+
frame = yield(*[frame, index, options].take(block.arity)) if block_given?
|
357
|
+
frames << frame if frame
|
334
358
|
end
|
359
|
+
frames.append(true) if block_given?
|
335
360
|
|
336
361
|
write_block = create_info_block(options[:write])
|
362
|
+
|
337
363
|
if options[:format] || @format
|
338
364
|
frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
|
365
|
+
move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
|
366
|
+
file.content_type = ::MiniMime.lookup_by_filename(move_to).content_type
|
367
|
+
file.move_to(move_to, permissions, directory_permissions)
|
339
368
|
else
|
340
369
|
frames.write(current_path, &write_block)
|
341
370
|
end
|
371
|
+
|
342
372
|
destroy_image(frames)
|
343
373
|
rescue ::Magick::ImageMagickError => e
|
344
|
-
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e
|
374
|
+
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e)
|
345
375
|
end
|
346
376
|
|
347
377
|
private
|
348
378
|
|
349
379
|
def create_info_block(options)
|
350
380
|
return nil unless options
|
351
|
-
|
352
|
-
|
353
|
-
|
381
|
+
proc do |img|
|
382
|
+
options.each do |k, v|
|
383
|
+
if v.is_a?(String) && (matches = v.match(/^["'](.+)["']/))
|
384
|
+
ActiveSupport::Deprecation.warn "Passing quoted strings like #{v} to #manipulate! is deprecated, pass them without quoting."
|
385
|
+
v = matches[1]
|
386
|
+
end
|
387
|
+
img.public_send(:"#{k}=", v)
|
388
|
+
end
|
389
|
+
end
|
354
390
|
end
|
355
391
|
|
356
392
|
def destroy_image(image)
|
357
|
-
image.
|
393
|
+
image.try(:destroy!)
|
394
|
+
end
|
395
|
+
|
396
|
+
def dimension_from(value)
|
397
|
+
return value unless value.instance_of?(Proc)
|
398
|
+
value.arity >= 1 ? value.call(self) : value.call
|
399
|
+
end
|
400
|
+
|
401
|
+
def rmagick_image
|
402
|
+
::Magick::Image.from_blob(self.read).first
|
358
403
|
end
|
359
404
|
|
360
405
|
end # RMagick
|
@@ -1,7 +1,7 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'pathname'
|
4
2
|
require 'active_support/core_ext/string/multibyte'
|
3
|
+
require 'mini_mime'
|
4
|
+
require 'mimemagic'
|
5
5
|
|
6
6
|
module CarrierWave
|
7
7
|
|
@@ -15,18 +15,19 @@ module CarrierWave
|
|
15
15
|
#
|
16
16
|
class SanitizedFile
|
17
17
|
|
18
|
-
|
18
|
+
attr_reader :file
|
19
19
|
|
20
20
|
class << self
|
21
21
|
attr_writer :sanitize_regexp
|
22
22
|
|
23
23
|
def sanitize_regexp
|
24
|
-
@sanitize_regexp ||= /[^
|
24
|
+
@sanitize_regexp ||= /[^[:word:]\.\-\+]/
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
def initialize(file)
|
29
29
|
self.file = file
|
30
|
+
@content = nil
|
30
31
|
end
|
31
32
|
|
32
33
|
##
|
@@ -108,12 +109,11 @@ module CarrierWave
|
|
108
109
|
# [String, nil] the path where the file is located.
|
109
110
|
#
|
110
111
|
def path
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
112
|
+
return if @file.blank?
|
113
|
+
if is_path?
|
114
|
+
File.expand_path(@file)
|
115
|
+
elsif @file.respond_to?(:path) && !@file.path.blank?
|
116
|
+
File.expand_path(@file.path)
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
@@ -141,8 +141,7 @@ module CarrierWave
|
|
141
141
|
# [Boolean] Whether the file exists
|
142
142
|
#
|
143
143
|
def exists?
|
144
|
-
|
145
|
-
return false
|
144
|
+
self.path.present? && File.exist?(self.path)
|
146
145
|
end
|
147
146
|
|
148
147
|
##
|
@@ -158,9 +157,9 @@ module CarrierWave
|
|
158
157
|
elsif is_path?
|
159
158
|
File.open(@file, "rb") {|file| file.read}
|
160
159
|
else
|
161
|
-
@file.
|
160
|
+
@file.try(:rewind)
|
162
161
|
@content = @file.read
|
163
|
-
@file.
|
162
|
+
@file.try(:close) unless @file.try(:closed?)
|
164
163
|
@content
|
165
164
|
end
|
166
165
|
end
|
@@ -174,19 +173,29 @@ module CarrierWave
|
|
174
173
|
# [permissions (Integer)] permissions to set on the file in its new location.
|
175
174
|
# [directory_permissions (Integer)] permissions to set on created directories.
|
176
175
|
#
|
177
|
-
def move_to(new_path, permissions=nil, directory_permissions=nil)
|
176
|
+
def move_to(new_path, permissions=nil, directory_permissions=nil, keep_filename=false)
|
178
177
|
return if self.empty?
|
179
178
|
new_path = File.expand_path(new_path)
|
180
179
|
|
181
180
|
mkdir!(new_path, directory_permissions)
|
181
|
+
move!(new_path)
|
182
|
+
chmod!(new_path, permissions)
|
183
|
+
if keep_filename
|
184
|
+
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
|
185
|
+
else
|
186
|
+
self.file = {:tempfile => new_path, :content_type => content_type}
|
187
|
+
end
|
188
|
+
self
|
189
|
+
end
|
190
|
+
##
|
191
|
+
# Helper to move file to new path.
|
192
|
+
#
|
193
|
+
def move!(new_path)
|
182
194
|
if exists?
|
183
|
-
FileUtils.mv(path, new_path) unless new_path
|
195
|
+
FileUtils.mv(path, new_path) unless File.identical?(new_path, path)
|
184
196
|
else
|
185
197
|
File.open(new_path, "wb") { |f| f.write(read) }
|
186
198
|
end
|
187
|
-
chmod!(new_path, permissions)
|
188
|
-
self.file = new_path
|
189
|
-
self
|
190
199
|
end
|
191
200
|
|
192
201
|
##
|
@@ -207,13 +216,20 @@ module CarrierWave
|
|
207
216
|
new_path = File.expand_path(new_path)
|
208
217
|
|
209
218
|
mkdir!(new_path, directory_permissions)
|
219
|
+
copy!(new_path)
|
220
|
+
chmod!(new_path, permissions)
|
221
|
+
self.class.new({:tempfile => new_path, :content_type => content_type})
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Helper to create copy of file in new path.
|
226
|
+
#
|
227
|
+
def copy!(new_path)
|
210
228
|
if exists?
|
211
229
|
FileUtils.cp(path, new_path) unless new_path == path
|
212
230
|
else
|
213
231
|
File.open(new_path, "wb") { |f| f.write(read) }
|
214
232
|
end
|
215
|
-
chmod!(new_path, permissions)
|
216
|
-
self.class.new({:tempfile => new_path, :content_type => content_type})
|
217
233
|
end
|
218
234
|
|
219
235
|
##
|
@@ -243,8 +259,10 @@ module CarrierWave
|
|
243
259
|
# [String] the content type of the file
|
244
260
|
#
|
245
261
|
def content_type
|
246
|
-
|
247
|
-
|
262
|
+
@content_type ||=
|
263
|
+
existing_content_type ||
|
264
|
+
mime_magic_content_type ||
|
265
|
+
mini_mime_content_type
|
248
266
|
end
|
249
267
|
|
250
268
|
##
|
@@ -275,7 +293,7 @@ module CarrierWave
|
|
275
293
|
if file.is_a?(Hash)
|
276
294
|
@file = file["tempfile"] || file[:tempfile]
|
277
295
|
@original_filename = file["filename"] || file[:filename]
|
278
|
-
@content_type = file["content_type"] || file[:content_type]
|
296
|
+
@content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
|
279
297
|
else
|
280
298
|
@file = file
|
281
299
|
@original_filename = nil
|
@@ -287,7 +305,7 @@ module CarrierWave
|
|
287
305
|
def mkdir!(path, directory_permissions)
|
288
306
|
options = {}
|
289
307
|
options[:mode] = directory_permissions if directory_permissions
|
290
|
-
FileUtils.mkdir_p(File.dirname(path), options) unless File.
|
308
|
+
FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
|
291
309
|
end
|
292
310
|
|
293
311
|
def chmod!(path, permissions)
|
@@ -296,14 +314,36 @@ module CarrierWave
|
|
296
314
|
|
297
315
|
# Sanitize the filename, to prevent hacking
|
298
316
|
def sanitize(name)
|
299
|
-
name = name.
|
317
|
+
name = name.tr("\\", "/") # work-around for IE
|
300
318
|
name = File.basename(name)
|
301
319
|
name = name.gsub(sanitize_regexp,"_")
|
302
320
|
name = "_#{name}" if name =~ /\A\.+\z/
|
303
|
-
name = "unnamed" if name.size
|
321
|
+
name = "unnamed" if name.size.zero?
|
304
322
|
return name.mb_chars.to_s
|
305
323
|
end
|
306
324
|
|
325
|
+
def existing_content_type
|
326
|
+
if @file.respond_to?(:content_type) && @file.content_type
|
327
|
+
@file.content_type.to_s.chomp
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def mime_magic_content_type
|
332
|
+
if path
|
333
|
+
File.open(path) do |file|
|
334
|
+
MimeMagic.by_magic(file).try(:type) || 'invalid/invalid'
|
335
|
+
end
|
336
|
+
end
|
337
|
+
rescue Errno::ENOENT
|
338
|
+
nil
|
339
|
+
end
|
340
|
+
|
341
|
+
def mini_mime_content_type
|
342
|
+
return unless path
|
343
|
+
mime_type = ::MiniMime.lookup_by_filename(path)
|
344
|
+
@content_type = (mime_type && mime_type.content_type).to_s
|
345
|
+
end
|
346
|
+
|
307
347
|
def split_extension(filename)
|
308
348
|
# regular expressions to try for identifying extensions
|
309
349
|
extension_matchers = [
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
module Storage
|
5
3
|
|
@@ -25,6 +23,21 @@ module CarrierWave
|
|
25
23
|
def retrieve!(identifier)
|
26
24
|
end
|
27
25
|
|
26
|
+
def cache!(new_file)
|
27
|
+
raise NotImplementedError.new("Need to implement #cache! if you want to use #{self.class.name} as a cache storage.")
|
28
|
+
end
|
29
|
+
|
30
|
+
def retrieve_from_cache!(identifier)
|
31
|
+
raise NotImplementedError.new("Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage.")
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete_dir!(path)
|
35
|
+
raise NotImplementedError.new("Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage.")
|
36
|
+
end
|
37
|
+
|
38
|
+
def clean_cache!(seconds)
|
39
|
+
raise NotImplementedError.new("Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage.")
|
40
|
+
end
|
28
41
|
end # Abstract
|
29
42
|
end # Storage
|
30
43
|
end # CarrierWave
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
module Storage
|
5
3
|
|
@@ -9,6 +7,10 @@ module CarrierWave
|
|
9
7
|
# pretty much it.
|
10
8
|
#
|
11
9
|
class File < Abstract
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@cache_called = nil
|
13
|
+
end
|
12
14
|
|
13
15
|
##
|
14
16
|
# Move the file to the uploader's store path.
|
@@ -51,6 +53,71 @@ module CarrierWave
|
|
51
53
|
CarrierWave::SanitizedFile.new(path)
|
52
54
|
end
|
53
55
|
|
56
|
+
##
|
57
|
+
# Stores given file to cache directory.
|
58
|
+
#
|
59
|
+
# === Parameters
|
60
|
+
#
|
61
|
+
# [new_file (File, IOString, Tempfile)] any kind of file object
|
62
|
+
#
|
63
|
+
# === Returns
|
64
|
+
#
|
65
|
+
# [CarrierWave::SanitizedFile] a sanitized file
|
66
|
+
#
|
67
|
+
def cache!(new_file)
|
68
|
+
new_file.move_to(::File.expand_path(uploader.cache_path, uploader.root), uploader.permissions, uploader.directory_permissions, true)
|
69
|
+
rescue Errno::EMLINK, Errno::ENOSPC => e
|
70
|
+
raise(e) if @cache_called
|
71
|
+
@cache_called = true
|
72
|
+
|
73
|
+
# NOTE: Remove cached files older than 10 minutes
|
74
|
+
clean_cache!(600)
|
75
|
+
|
76
|
+
cache!(new_file)
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Retrieves the file with the given cache_name from the cache.
|
81
|
+
#
|
82
|
+
# === Parameters
|
83
|
+
#
|
84
|
+
# [cache_name (String)] uniquely identifies a cache file
|
85
|
+
#
|
86
|
+
# === Raises
|
87
|
+
#
|
88
|
+
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
|
89
|
+
#
|
90
|
+
def retrieve_from_cache!(identifier)
|
91
|
+
CarrierWave::SanitizedFile.new(::File.expand_path(uploader.cache_path(identifier), uploader.root))
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Deletes a cache dir
|
96
|
+
#
|
97
|
+
def delete_dir!(path)
|
98
|
+
if path
|
99
|
+
begin
|
100
|
+
Dir.rmdir(::File.expand_path(path, uploader.root))
|
101
|
+
rescue Errno::ENOENT
|
102
|
+
# Ignore: path does not exist
|
103
|
+
rescue Errno::ENOTDIR
|
104
|
+
# Ignore: path is not a dir
|
105
|
+
rescue Errno::ENOTEMPTY, Errno::EEXIST
|
106
|
+
# Ignore: dir is not empty
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def clean_cache!(seconds)
|
112
|
+
Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), CarrierWave.root)).each do |dir|
|
113
|
+
# generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
|
114
|
+
time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
|
115
|
+
time = Time.at(*time)
|
116
|
+
if time < (Time.now.utc - seconds)
|
117
|
+
FileUtils.rm_rf(dir)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
54
121
|
end # File
|
55
122
|
end # Storage
|
56
123
|
end # CarrierWave
|