carrierwave 0.11.2 → 3.1.2

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.
Files changed (69) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +495 -173
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -4
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/error.rb +1 -0
  7. data/lib/carrierwave/locale/en.yml +11 -5
  8. data/lib/carrierwave/mount.rb +217 -182
  9. data/lib/carrierwave/mounter.rb +257 -0
  10. data/lib/carrierwave/orm/activerecord.rb +29 -35
  11. data/lib/carrierwave/processing/mini_magick.rb +169 -84
  12. data/lib/carrierwave/processing/rmagick.rb +107 -25
  13. data/lib/carrierwave/processing/vips.rb +315 -0
  14. data/lib/carrierwave/processing.rb +1 -1
  15. data/lib/carrierwave/sanitized_file.rb +105 -87
  16. data/lib/carrierwave/storage/abstract.rb +16 -3
  17. data/lib/carrierwave/storage/file.rb +71 -3
  18. data/lib/carrierwave/storage/fog.rb +228 -57
  19. data/lib/carrierwave/storage.rb +1 -9
  20. data/lib/carrierwave/test/matchers.rb +88 -19
  21. data/lib/carrierwave/uploader/cache.rb +75 -45
  22. data/lib/carrierwave/uploader/callbacks.rb +1 -3
  23. data/lib/carrierwave/uploader/configuration.rb +84 -16
  24. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  25. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  26. data/lib/carrierwave/uploader/default_url.rb +3 -5
  27. data/lib/carrierwave/uploader/dimension.rb +66 -0
  28. data/lib/carrierwave/uploader/download.rb +4 -74
  29. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  30. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  31. data/lib/carrierwave/uploader/file_size.rb +43 -0
  32. data/lib/carrierwave/uploader/mountable.rb +13 -8
  33. data/lib/carrierwave/uploader/processing.rb +51 -13
  34. data/lib/carrierwave/uploader/proxy.rb +20 -9
  35. data/lib/carrierwave/uploader/remove.rb +0 -2
  36. data/lib/carrierwave/uploader/serialization.rb +2 -4
  37. data/lib/carrierwave/uploader/store.rb +85 -28
  38. data/lib/carrierwave/uploader/url.rb +8 -7
  39. data/lib/carrierwave/uploader/versions.rb +175 -125
  40. data/lib/carrierwave/uploader.rb +12 -10
  41. data/lib/carrierwave/utilities/file_name.rb +47 -0
  42. data/lib/carrierwave/utilities/uri.rb +14 -12
  43. data/lib/carrierwave/utilities.rb +1 -3
  44. data/lib/carrierwave/validations/active_model.rb +7 -11
  45. data/lib/carrierwave/version.rb +1 -1
  46. data/lib/carrierwave.rb +48 -21
  47. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +8 -11
  48. data/lib/generators/uploader_generator.rb +3 -3
  49. metadata +124 -86
  50. data/lib/carrierwave/locale/cs.yml +0 -11
  51. data/lib/carrierwave/locale/de.yml +0 -11
  52. data/lib/carrierwave/locale/el.yml +0 -11
  53. data/lib/carrierwave/locale/es.yml +0 -11
  54. data/lib/carrierwave/locale/fr.yml +0 -11
  55. data/lib/carrierwave/locale/ja.yml +0 -11
  56. data/lib/carrierwave/locale/nb.yml +0 -11
  57. data/lib/carrierwave/locale/nl.yml +0 -11
  58. data/lib/carrierwave/locale/pl.yml +0 -11
  59. data/lib/carrierwave/locale/pt-BR.yml +0 -11
  60. data/lib/carrierwave/locale/pt-PT.yml +0 -11
  61. data/lib/carrierwave/locale/ru.yml +0 -11
  62. data/lib/carrierwave/locale/sk.yml +0 -11
  63. data/lib/carrierwave/locale/tr.yml +0 -11
  64. data/lib/carrierwave/processing/mime_types.rb +0 -74
  65. data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
  66. data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
  67. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -47
  68. data/lib/carrierwave/uploader/extension_whitelist.rb +0 -49
  69. data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Mountable
@@ -7,13 +5,14 @@ module CarrierWave
7
5
  attr_reader :model, :mounted_as
8
6
 
9
7
  ##
10
- # If a model is given as the first parameter, it will be stored in the uploader, and
11
- # available throught +#model+. Likewise, mounted_as stores the name of the column
12
- # where this instance of the uploader is mounted. These values can then be used inside
13
- # your uploader.
8
+ # If a model is given as the first parameter, it will be stored in the
9
+ # uploader, and available through +#model+. Likewise, mounted_as stores
10
+ # the name of the column where this instance of the uploader is mounted.
11
+ # These values can then be used inside your uploader.
14
12
  #
15
- # If you do not wish to mount your uploaders with the ORM extensions in -more then you
16
- # can override this method inside your uploader. Just be sure to call +super+
13
+ # If you do not wish to mount your uploaders with the ORM extensions in
14
+ # -more then you can override this method inside your uploader. Just be
15
+ # sure to call +super+
17
16
  #
18
17
  # === Parameters
19
18
  #
@@ -34,6 +33,12 @@ module CarrierWave
34
33
  @mounted_as = mounted_as
35
34
  end
36
35
 
36
+ ##
37
+ # Returns array index of given uploader within currently mounted uploaders
38
+ #
39
+ def index
40
+ model.__send__(:_mounter, mounted_as).uploaders.index(self)
41
+ end
37
42
  end # Mountable
38
43
  end # Uploader
39
44
  end # CarrierWave
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Processing
@@ -11,7 +9,7 @@ module CarrierWave
11
9
  class_attribute :processors, :instance_writer => false
12
10
  self.processors = []
13
11
 
14
- after :cache, :process!
12
+ before :cache, :process!
15
13
  end
16
14
 
17
15
  module ClassMethods
@@ -20,7 +18,7 @@ module CarrierWave
20
18
  # Adds a processor callback which applies operations as a file is uploaded.
21
19
  # The argument may be the name of any method of the uploader, expressed as a symbol,
22
20
  # or a list of such methods, or a hash where the key is a method and the value is
23
- # an array of arguments to call the method with
21
+ # an array of arguments to call the method with. Also accepts an :if or :unless condition
24
22
  #
25
23
  # === Parameters
26
24
  #
@@ -33,6 +31,7 @@ module CarrierWave
33
31
  # process :sepiatone, :vignette
34
32
  # process :scale => [200, 200]
35
33
  # process :scale => [200, 200], :if => :image?
34
+ # process :scale => [200, 200], :unless => :disallowed_image_type?
36
35
  # process :sepiatone, :if => :image?
37
36
  #
38
37
  # def sepiatone
@@ -51,6 +50,10 @@ module CarrierWave
51
50
  # ...
52
51
  # end
53
52
  #
53
+ # def disallowed_image_type?
54
+ # ...
55
+ # end
56
+ #
54
57
  # end
55
58
  #
56
59
  def process(*args)
@@ -59,12 +62,20 @@ module CarrierWave
59
62
  hash.merge!(arg)
60
63
  end
61
64
 
62
- condition = new_processors.delete(:if)
65
+ condition_type = new_processors.keys.detect { |key| [:if, :unless].include?(key) }
66
+ condition = new_processors.delete(:if) || new_processors.delete(:unless)
63
67
  new_processors.each do |processor, processor_args|
64
- self.processors += [[processor, processor_args, condition]]
68
+ self.processors += [[processor, processor_args, condition, condition_type]]
69
+
70
+ if processor == :convert
71
+ # Treat :convert specially, since it should trigger the file extension change
72
+ force_extension processor_args
73
+ if condition
74
+ warn "Use of 'process convert: format' with conditionals has an issue and doesn't work correctly. See https://github.com/carrierwaveuploader/carrierwave/issues/2723 for details. "
75
+ end
76
+ end
65
77
  end
66
78
  end
67
-
68
79
  end # ClassMethods
69
80
 
70
81
  ##
@@ -73,18 +84,45 @@ module CarrierWave
73
84
  def process!(new_file=nil)
74
85
  return unless enable_processing
75
86
 
76
- self.class.processors.each do |method, args, condition|
77
- if(condition)
78
- if condition.respond_to?(:call)
79
- next unless condition.call(self, :args => args, :method => method, :file => new_file)
87
+ with_callbacks(:process, new_file) do
88
+ self.class.processors.each do |method, args, condition, condition_type|
89
+ if condition && condition_type == :if
90
+ if condition.respond_to?(:call)
91
+ next unless condition.call(self, :args => args, :method => method, :file => new_file)
92
+ else
93
+ next unless self.send(condition, new_file)
94
+ end
95
+ elsif condition && condition_type == :unless
96
+ if condition.respond_to?(:call)
97
+ next if condition.call(self, :args => args, :method => method, :file => new_file)
98
+ elsif self.send(condition, new_file)
99
+ next
100
+ end
101
+ end
102
+
103
+ if args.is_a? Array
104
+ kwargs, args = args.partition { |arg| arg.is_a? Hash }
105
+ end
106
+
107
+ if kwargs.present?
108
+ kwargs = kwargs.reduce(:merge)
109
+ self.send(method, *args, **kwargs)
80
110
  else
81
- next unless self.send(condition, new_file)
111
+ self.send(method, *args)
82
112
  end
83
113
  end
84
- self.send(method, *args)
85
114
  end
86
115
  end
87
116
 
117
+ private
118
+
119
+ def forcing_extension(filename)
120
+ if force_extension && filename
121
+ Pathname.new(filename).sub_ext(".#{force_extension.to_s.delete_prefix('.')}").to_s
122
+ else
123
+ filename
124
+ end
125
+ end
88
126
  end # Processing
89
127
  end # Uploader
90
128
  end # CarrierWave
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Proxy
@@ -19,20 +17,33 @@ module CarrierWave
19
17
  # [String] the path where the file is currently located.
20
18
  #
21
19
  def current_path
22
- file.path if file.respond_to?(:path)
20
+ file.try(:path)
23
21
  end
24
22
 
25
23
  alias_method :path, :current_path
26
24
 
27
25
  ##
28
- # Returns a string that uniquely identifies the last stored file
26
+ # Returns a string that uniquely identifies the retrieved or last stored file
29
27
  #
30
28
  # === Returns
31
29
  #
32
30
  # [String] uniquely identifies a file
33
31
  #
34
32
  def identifier
35
- storage.identifier if storage.respond_to?(:identifier)
33
+ @identifier || (file && storage.try(:identifier))
34
+ end
35
+
36
+ ##
37
+ # Returns a String which is to be used as a temporary value which gets assigned to the column.
38
+ # The purpose is to mark the column that it will be updated. Finally before the save it will be
39
+ # overwritten by the #identifier value, which is usually #filename.
40
+ #
41
+ # === Returns
42
+ #
43
+ # [String] a temporary_identifier, by default the value of #cache_name is used
44
+ #
45
+ def temporary_identifier
46
+ cache_name || @identifier
36
47
  end
37
48
 
38
49
  ##
@@ -42,8 +53,8 @@ module CarrierWave
42
53
  #
43
54
  # [String] contents of the file
44
55
  #
45
- def read
46
- file.read if file.respond_to?(:read)
56
+ def read(*args)
57
+ file.try(:read, *args)
47
58
  end
48
59
 
49
60
  ##
@@ -54,7 +65,7 @@ module CarrierWave
54
65
  # [Integer] size of the file
55
66
  #
56
67
  def size
57
- file.respond_to?(:size) ? file.size : 0
68
+ file.try(:size) || 0
58
69
  end
59
70
 
60
71
  ##
@@ -80,7 +91,7 @@ module CarrierWave
80
91
  # [String] content type of the file
81
92
  #
82
93
  def content_type
83
- file.respond_to?(:content_type) ? file.content_type : nil
94
+ file.try(:content_type)
84
95
  end
85
96
 
86
97
  end # Proxy
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Remove
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require "json"
4
2
  require "active_support/core_ext/hash"
5
3
 
@@ -9,11 +7,11 @@ module CarrierWave
9
7
  extend ActiveSupport::Concern
10
8
 
11
9
  def serializable_hash(options = nil)
12
- {"url" => url}.merge Hash[versions.map { |name, version| [name, { "url" => version.url }] }]
10
+ {"url" => url}.merge Hash[versions.map { |name, version| [name.to_s, { "url" => version.url }] }]
13
11
  end
14
12
 
15
13
  def as_json(options=nil)
16
- Hash[mounted_as || "uploader", serializable_hash]
14
+ serializable_hash
17
15
  end
18
16
 
19
17
  def to_json(options=nil)
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Store
@@ -9,6 +7,29 @@ module CarrierWave
9
7
  include CarrierWave::Uploader::Configuration
10
8
  include CarrierWave::Uploader::Cache
11
9
 
10
+ included do
11
+ prepend Module.new {
12
+ def initialize(*)
13
+ super
14
+ @file, @filename, @cache_id, @identifier, @deduplication_index = nil
15
+ end
16
+ }
17
+
18
+ after :store, :show_warning_when_filename_is_unavailable
19
+
20
+ class_attribute :filename_safeguard_checked
21
+ end
22
+
23
+ module ClassMethods
24
+ private
25
+
26
+ def inherited(subclass)
27
+ # To perform the filename safeguard check once per a class
28
+ self.filename_safeguard_checked = false
29
+ super
30
+ end
31
+ end
32
+
12
33
  ##
13
34
  # Override this in your Uploader to change the filename.
14
35
  #
@@ -27,9 +48,26 @@ module CarrierWave
27
48
  @filename
28
49
  end
29
50
 
51
+ ##
52
+ # Returns a filename which doesn't conflict with already-stored files.
53
+ #
54
+ # === Returns
55
+ #
56
+ # [String] the filename with suffix added for deduplication
57
+ #
58
+ def deduplicated_filename
59
+ return unless filename
60
+ return filename unless @deduplication_index
61
+
62
+ parts = filename.split('.')
63
+ basename = parts.shift
64
+ basename.sub!(/ ?\(\d+\)\z/, '')
65
+ ([basename.to_s + (@deduplication_index > 1 ? "(#{@deduplication_index})" : '')] + parts).join('.')
66
+ end
67
+
30
68
  ##
31
69
  # Calculates the path where the file should be stored. If +for_file+ is given, it will be
32
- # used as the filename, otherwise +CarrierWave::Uploader#filename+ is assumed.
70
+ # used as the identifier, otherwise +CarrierWave::Uploader#identifier+ is assumed.
33
71
  #
34
72
  # === Parameters
35
73
  #
@@ -39,7 +77,7 @@ module CarrierWave
39
77
  #
40
78
  # [String] the store path
41
79
  #
42
- def store_path(for_file=filename)
80
+ def store_path(for_file=identifier)
43
81
  File.join([store_dir, full_filename(for_file)].compact)
44
82
  end
45
83
 
@@ -53,32 +91,18 @@ module CarrierWave
53
91
  # [new_file (File, IOString, Tempfile)] any kind of file object
54
92
  #
55
93
  def store!(new_file=nil)
56
- cache!(new_file) if new_file && ((@cache_id != parent_cache_id) || @cache_id.nil?)
57
- if @file and @cache_id
94
+ cache!(new_file) if new_file && !cached?
95
+ if !cache_only && @file && @cache_id
58
96
  with_callbacks(:store, new_file) do
59
97
  new_file = storage.store!(@file)
60
- @file.delete if (delete_tmp_file_after_storage && ! move_to_store)
61
- delete_cache_id
98
+ if delete_tmp_file_after_storage
99
+ @file.delete unless move_to_store
100
+ cache_storage.delete_dir!(cache_path(nil))
101
+ end
62
102
  @file = new_file
63
- @cache_id = nil
64
- end
65
- end
66
- end
67
-
68
- ##
69
- # Deletes a cache id (tmp dir in cache)
70
- #
71
- def delete_cache_id
72
- if @cache_id
73
- path = File.expand_path(File.join(cache_dir, @cache_id), CarrierWave.root)
74
- begin
75
- Dir.rmdir(path)
76
- rescue Errno::ENOENT
77
- # Ignore: path does not exist
78
- rescue Errno::ENOTDIR
79
- # Ignore: path is not a dir
80
- rescue Errno::ENOTEMPTY, Errno::EEXIST
81
- # Ignore: dir is not empty
103
+ @identifier = storage.identifier
104
+ @original_filename = @cache_id = @deduplication_index = nil
105
+ @staged = false
82
106
  end
83
107
  end
84
108
  end
@@ -93,13 +117,46 @@ module CarrierWave
93
117
  def retrieve_from_store!(identifier)
94
118
  with_callbacks(:retrieve_from_store, identifier) do
95
119
  @file = storage.retrieve!(identifier)
120
+ @identifier = identifier
121
+ end
122
+ end
123
+
124
+ ##
125
+ # Look for an identifier which doesn't collide with the given already-stored identifiers.
126
+ # It is done by adding a index number as the suffix.
127
+ # For example, if there's 'image.jpg' and the @deduplication_index is set to 2,
128
+ # The stored file will be named as 'image(2).jpg'.
129
+ #
130
+ # === Parameters
131
+ #
132
+ # [current_identifiers (Array[String])] List of identifiers for already-stored files
133
+ #
134
+ def deduplicate(current_identifiers)
135
+ @deduplication_index = nil
136
+ return unless current_identifiers.include?(identifier)
137
+
138
+ (1..current_identifiers.size + 1).each do |i|
139
+ @deduplication_index = i
140
+ break unless current_identifiers.include?(identifier)
96
141
  end
97
142
  end
98
143
 
99
144
  private
100
145
 
101
146
  def full_filename(for_file)
102
- for_file
147
+ forcing_extension(for_file)
148
+ end
149
+
150
+ def show_warning_when_filename_is_unavailable(_)
151
+ return if self.class.filename_safeguard_checked
152
+ self.class.filename_safeguard_checked = true
153
+ return if filename
154
+
155
+ warn <<~MESSAGE
156
+ [WARNING] Your uploader's #filename method defined at #{method(:filename).source_location.join(':')} didn't return value after storing the file.
157
+ It's likely that the method is safeguarded with `if original_filename`, which were necessary for pre-3.x CarrierWave but is no longer needed.
158
+ Removing it is recommended, as it is known to cause issues depending on the use case: https://github.com/carrierwaveuploader/carrierwave/issues/2708
159
+ MESSAGE
103
160
  end
104
161
 
105
162
  def storage
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Url
@@ -17,12 +15,15 @@ module CarrierWave
17
15
  # [String] the location where this file is accessible via a url
18
16
  #
19
17
  def url(options = {})
20
- if file.respond_to?(:url) and not file.url.blank?
21
- file.method(:url).arity == 0 ? file.url : file.url(options)
22
- elsif file.respond_to?(:path)
23
- path = encode_path(file.path.gsub(File.expand_path(root), ''))
18
+ if file.respond_to?(:url)
19
+ tmp_url = file.method(:url).arity.zero? ? file.url : file.url(options)
20
+ return tmp_url if tmp_url.present?
21
+ end
22
+
23
+ if file.respond_to?(:path)
24
+ path = encode_path(file.path.sub(File.expand_path(root), ''))
24
25
 
25
- if host = asset_host
26
+ if (host = asset_host)
26
27
  if host.respond_to? :call
27
28
  "#{host.call(file)}#{path}"
28
29
  else