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.

Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +307 -121
  3. data/lib/carrierwave/compatibility/paperclip.rb +0 -2
  4. data/lib/carrierwave/downloader/base.rb +83 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +65 -0
  6. data/lib/carrierwave/error.rb +1 -0
  7. data/lib/carrierwave/locale/en.yml +7 -4
  8. data/lib/carrierwave/mount.rb +229 -180
  9. data/lib/carrierwave/mounter.rb +188 -0
  10. data/lib/carrierwave/orm/activerecord.rb +59 -24
  11. data/lib/carrierwave/processing/mini_magick.rb +137 -83
  12. data/lib/carrierwave/processing/rmagick.rb +65 -8
  13. data/lib/carrierwave/processing.rb +0 -1
  14. data/lib/carrierwave/sanitized_file.rb +67 -32
  15. data/lib/carrierwave/storage/abstract.rb +15 -2
  16. data/lib/carrierwave/storage/file.rb +69 -2
  17. data/lib/carrierwave/storage/fog.rb +177 -39
  18. data/lib/carrierwave/storage.rb +1 -7
  19. data/lib/carrierwave/test/matchers.rb +77 -12
  20. data/lib/carrierwave/uploader/cache.rb +74 -38
  21. data/lib/carrierwave/uploader/callbacks.rb +0 -2
  22. data/lib/carrierwave/uploader/configuration.rb +71 -13
  23. data/lib/carrierwave/uploader/content_type_blacklist.rb +48 -0
  24. data/lib/carrierwave/uploader/content_type_whitelist.rb +48 -0
  25. data/lib/carrierwave/uploader/default_url.rb +3 -5
  26. data/lib/carrierwave/uploader/download.rb +4 -74
  27. data/lib/carrierwave/uploader/extension_blacklist.rb +14 -10
  28. data/lib/carrierwave/uploader/extension_whitelist.rb +13 -10
  29. data/lib/carrierwave/uploader/file_size.rb +43 -0
  30. data/lib/carrierwave/uploader/mountable.rb +13 -8
  31. data/lib/carrierwave/uploader/processing.rb +10 -10
  32. data/lib/carrierwave/uploader/proxy.rb +6 -8
  33. data/lib/carrierwave/uploader/remove.rb +0 -2
  34. data/lib/carrierwave/uploader/serialization.rb +2 -4
  35. data/lib/carrierwave/uploader/store.rb +17 -24
  36. data/lib/carrierwave/uploader/url.rb +3 -5
  37. data/lib/carrierwave/uploader/versions.rb +123 -93
  38. data/lib/carrierwave/uploader.rb +6 -2
  39. data/lib/carrierwave/utilities/uri.rb +5 -6
  40. data/lib/carrierwave/utilities.rb +0 -3
  41. data/lib/carrierwave/validations/active_model.rb +3 -5
  42. data/lib/carrierwave/version.rb +1 -1
  43. data/lib/carrierwave.rb +34 -8
  44. data/lib/generators/templates/uploader.rb +4 -8
  45. metadata +130 -57
  46. data/lib/carrierwave/locale/cs.yml +0 -11
  47. data/lib/carrierwave/locale/de.yml +0 -11
  48. data/lib/carrierwave/locale/el.yml +0 -11
  49. data/lib/carrierwave/locale/es.yml +0 -11
  50. data/lib/carrierwave/locale/fr.yml +0 -11
  51. data/lib/carrierwave/locale/ja.yml +0 -11
  52. data/lib/carrierwave/locale/nb.yml +0 -11
  53. data/lib/carrierwave/locale/nl.yml +0 -11
  54. data/lib/carrierwave/locale/pl.yml +0 -11
  55. data/lib/carrierwave/locale/pt-BR.yml +0 -11
  56. data/lib/carrierwave/locale/pt-PT.yml +0 -11
  57. data/lib/carrierwave/locale/ru.yml +0 -11
  58. data/lib/carrierwave/locale/sk.yml +0 -11
  59. data/lib/carrierwave/locale/tr.yml +0 -11
  60. data/lib/carrierwave/processing/mime_types.rb +0 -74
  61. 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 *[frame, index, options].take(block.arity) if block_given?
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, :default => I18n.translate(:"errors.messages.rmagick_processing_error", :e => e, :locale => :en))
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
- assignments = options.map { |k, v| "self.#{k} = #{v}" }
340
- code = "lambda { |img| " + assignments.join(";") + "}"
341
- eval code
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.destroy! if image.respond_to?(:destroy!)
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,3 +1,2 @@
1
1
  require "carrierwave/processing/rmagick"
2
2
  require "carrierwave/processing/mini_magick"
3
- require "carrierwave/processing/mime_types"
@@ -1,8 +1,7 @@
1
- # encoding: utf-8
2
-
3
1
  require 'pathname'
4
2
  require 'active_support/core_ext/string/multibyte'
5
- require 'mime/types'
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
- attr_accessor :file
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 ||= /[^a-zA-Z0-9\.\-\+_]/
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
- unless @file.blank?
113
- if is_path?
114
- File.expand_path(@file)
115
- elsif @file.respond_to?(:path) and not @file.path.blank?
116
- File.expand_path(@file.path)
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
- return File.exists?(self.path) if self.path
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.rewind if @file.respond_to?(:rewind)
160
+ @file.try(:rewind)
163
161
  @content = @file.read
164
- @file.close if @file.respond_to?(:close) && @file.respond_to?(:closed?) && !@file.closed?
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 == 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
- return @content_type if @content_type
248
- if @file.respond_to?(:content_type) and @file.content_type
249
- @content_type = @file.content_type.to_s.chomp
250
- elsif path
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.exists?(File.dirname(path))
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.gsub("\\", "/") # work-around for IE
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 == 0
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