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
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
|
5
3
|
##
|
@@ -62,11 +60,20 @@ module CarrierWave
|
|
62
60
|
|
63
61
|
included do
|
64
62
|
begin
|
63
|
+
require "rmagick"
|
64
|
+
rescue LoadError
|
65
65
|
require "RMagick"
|
66
66
|
rescue LoadError => e
|
67
67
|
e.message << " (You may need to install the rmagick gem)"
|
68
68
|
raise e
|
69
69
|
end
|
70
|
+
|
71
|
+
prepend Module.new {
|
72
|
+
def initialize(*)
|
73
|
+
super
|
74
|
+
@format = nil
|
75
|
+
end
|
76
|
+
}
|
70
77
|
end
|
71
78
|
|
72
79
|
module ClassMethods
|
@@ -133,6 +140,8 @@ module CarrierWave
|
|
133
140
|
# [Magick::Image] additional manipulations to perform
|
134
141
|
#
|
135
142
|
def resize_to_limit(width, height)
|
143
|
+
width = dimension_from width
|
144
|
+
height = dimension_from height
|
136
145
|
manipulate! do |img|
|
137
146
|
geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
|
138
147
|
new_img = img.change_geometry(geometry) do |new_width, new_height|
|
@@ -162,6 +171,8 @@ module CarrierWave
|
|
162
171
|
# [Magick::Image] additional manipulations to perform
|
163
172
|
#
|
164
173
|
def resize_to_fit(width, height)
|
174
|
+
width = dimension_from width
|
175
|
+
height = dimension_from height
|
165
176
|
manipulate! do |img|
|
166
177
|
img.resize_to_fit!(width, height)
|
167
178
|
img = yield(img) if block_given?
|
@@ -186,6 +197,8 @@ module CarrierWave
|
|
186
197
|
# [Magick::Image] additional manipulations to perform
|
187
198
|
#
|
188
199
|
def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
|
200
|
+
width = dimension_from width
|
201
|
+
height = dimension_from height
|
189
202
|
manipulate! do |img|
|
190
203
|
img.crop_resized!(width, height, gravity)
|
191
204
|
img = yield(img) if block_given?
|
@@ -211,6 +224,8 @@ module CarrierWave
|
|
211
224
|
# [Magick::Image] additional manipulations to perform
|
212
225
|
#
|
213
226
|
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
|
227
|
+
width = dimension_from width
|
228
|
+
height = dimension_from height
|
214
229
|
manipulate! do |img|
|
215
230
|
img.resize_to_fit!(width, height)
|
216
231
|
new_img = ::Magick::Image.new(width, height) { self.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
|
@@ -249,6 +264,28 @@ module CarrierWave
|
|
249
264
|
end
|
250
265
|
end
|
251
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
|
+
|
252
289
|
##
|
253
290
|
# Manipulate the image with RMagick. This method will load up an image
|
254
291
|
# and then pass each of its frames to the supplied block. It will then
|
@@ -316,33 +353,53 @@ module CarrierWave
|
|
316
353
|
frames = ::Magick::ImageList.new
|
317
354
|
|
318
355
|
image.each_with_index do |frame, index|
|
319
|
-
frame = yield
|
356
|
+
frame = yield(*[frame, index, options].take(block.arity)) if block_given?
|
320
357
|
frames << frame if frame
|
321
358
|
end
|
322
359
|
frames.append(true) if block_given?
|
323
360
|
|
324
361
|
write_block = create_info_block(options[:write])
|
362
|
+
|
325
363
|
if options[:format] || @format
|
326
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)
|
327
368
|
else
|
328
369
|
frames.write(current_path, &write_block)
|
329
370
|
end
|
371
|
+
|
330
372
|
destroy_image(frames)
|
331
373
|
rescue ::Magick::ImageMagickError => e
|
332
|
-
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)
|
333
375
|
end
|
334
376
|
|
335
377
|
private
|
336
378
|
|
337
379
|
def create_info_block(options)
|
338
380
|
return nil unless options
|
339
|
-
|
340
|
-
|
341
|
-
|
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
|
342
390
|
end
|
343
391
|
|
344
392
|
def destroy_image(image)
|
345
|
-
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
|
346
403
|
end
|
347
404
|
|
348
405
|
end # RMagick
|
@@ -1,8 +1,7 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'pathname'
|
4
2
|
require 'active_support/core_ext/string/multibyte'
|
5
|
-
require '
|
3
|
+
require 'mini_mime'
|
4
|
+
require 'mimemagic'
|
6
5
|
|
7
6
|
module CarrierWave
|
8
7
|
|
@@ -16,18 +15,19 @@ module CarrierWave
|
|
16
15
|
#
|
17
16
|
class SanitizedFile
|
18
17
|
|
19
|
-
|
18
|
+
attr_reader :file
|
20
19
|
|
21
20
|
class << self
|
22
21
|
attr_writer :sanitize_regexp
|
23
22
|
|
24
23
|
def sanitize_regexp
|
25
|
-
@sanitize_regexp ||= /[^
|
24
|
+
@sanitize_regexp ||= /[^[:word:]\.\-\+]/
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
29
28
|
def initialize(file)
|
30
29
|
self.file = file
|
30
|
+
@content = nil
|
31
31
|
end
|
32
32
|
|
33
33
|
##
|
@@ -109,12 +109,11 @@ module CarrierWave
|
|
109
109
|
# [String, nil] the path where the file is located.
|
110
110
|
#
|
111
111
|
def path
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
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)
|
118
117
|
end
|
119
118
|
end
|
120
119
|
|
@@ -142,8 +141,7 @@ module CarrierWave
|
|
142
141
|
# [Boolean] Whether the file exists
|
143
142
|
#
|
144
143
|
def exists?
|
145
|
-
|
146
|
-
return false
|
144
|
+
self.path.present? && File.exist?(self.path)
|
147
145
|
end
|
148
146
|
|
149
147
|
##
|
@@ -159,9 +157,9 @@ module CarrierWave
|
|
159
157
|
elsif is_path?
|
160
158
|
File.open(@file, "rb") {|file| file.read}
|
161
159
|
else
|
162
|
-
@file.
|
160
|
+
@file.try(:rewind)
|
163
161
|
@content = @file.read
|
164
|
-
@file.
|
162
|
+
@file.try(:close) unless @file.try(:closed?)
|
165
163
|
@content
|
166
164
|
end
|
167
165
|
end
|
@@ -175,19 +173,29 @@ module CarrierWave
|
|
175
173
|
# [permissions (Integer)] permissions to set on the file in its new location.
|
176
174
|
# [directory_permissions (Integer)] permissions to set on created directories.
|
177
175
|
#
|
178
|
-
def move_to(new_path, permissions=nil, directory_permissions=nil)
|
176
|
+
def move_to(new_path, permissions=nil, directory_permissions=nil, keep_filename=false)
|
179
177
|
return if self.empty?
|
180
178
|
new_path = File.expand_path(new_path)
|
181
179
|
|
182
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)
|
183
194
|
if exists?
|
184
|
-
FileUtils.mv(path, new_path) unless new_path
|
195
|
+
FileUtils.mv(path, new_path) unless File.identical?(new_path, path)
|
185
196
|
else
|
186
197
|
File.open(new_path, "wb") { |f| f.write(read) }
|
187
198
|
end
|
188
|
-
chmod!(new_path, permissions)
|
189
|
-
self.file = new_path
|
190
|
-
self
|
191
199
|
end
|
192
200
|
|
193
201
|
##
|
@@ -208,13 +216,20 @@ module CarrierWave
|
|
208
216
|
new_path = File.expand_path(new_path)
|
209
217
|
|
210
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)
|
211
228
|
if exists?
|
212
229
|
FileUtils.cp(path, new_path) unless new_path == path
|
213
230
|
else
|
214
231
|
File.open(new_path, "wb") { |f| f.write(read) }
|
215
232
|
end
|
216
|
-
chmod!(new_path, permissions)
|
217
|
-
self.class.new({:tempfile => new_path, :content_type => content_type})
|
218
233
|
end
|
219
234
|
|
220
235
|
##
|
@@ -244,12 +259,10 @@ module CarrierWave
|
|
244
259
|
# [String] the content type of the file
|
245
260
|
#
|
246
261
|
def content_type
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
@content_type = ::MIME::Types.type_for(path).first.to_s
|
252
|
-
end
|
262
|
+
@content_type ||=
|
263
|
+
existing_content_type ||
|
264
|
+
mime_magic_content_type ||
|
265
|
+
mini_mime_content_type
|
253
266
|
end
|
254
267
|
|
255
268
|
##
|
@@ -280,7 +293,7 @@ module CarrierWave
|
|
280
293
|
if file.is_a?(Hash)
|
281
294
|
@file = file["tempfile"] || file[:tempfile]
|
282
295
|
@original_filename = file["filename"] || file[:filename]
|
283
|
-
@content_type = file["content_type"] || file[:content_type]
|
296
|
+
@content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
|
284
297
|
else
|
285
298
|
@file = file
|
286
299
|
@original_filename = nil
|
@@ -292,7 +305,7 @@ module CarrierWave
|
|
292
305
|
def mkdir!(path, directory_permissions)
|
293
306
|
options = {}
|
294
307
|
options[:mode] = directory_permissions if directory_permissions
|
295
|
-
FileUtils.mkdir_p(File.dirname(path), options) unless File.
|
308
|
+
FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
|
296
309
|
end
|
297
310
|
|
298
311
|
def chmod!(path, permissions)
|
@@ -301,14 +314,36 @@ module CarrierWave
|
|
301
314
|
|
302
315
|
# Sanitize the filename, to prevent hacking
|
303
316
|
def sanitize(name)
|
304
|
-
name = name.
|
317
|
+
name = name.tr("\\", "/") # work-around for IE
|
305
318
|
name = File.basename(name)
|
306
319
|
name = name.gsub(sanitize_regexp,"_")
|
307
320
|
name = "_#{name}" if name =~ /\A\.+\z/
|
308
|
-
name = "unnamed" if name.size
|
321
|
+
name = "unnamed" if name.size.zero?
|
309
322
|
return name.mb_chars.to_s
|
310
323
|
end
|
311
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
|
+
|
312
347
|
def split_extension(filename)
|
313
348
|
# regular expressions to try for identifying extensions
|
314
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
|