carrierwave 1.3.2 → 3.0.0

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +235 -91
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -2
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/locale/en.yml +9 -6
  7. data/lib/carrierwave/mount.rb +48 -61
  8. data/lib/carrierwave/mounter.rb +165 -77
  9. data/lib/carrierwave/orm/activerecord.rb +13 -55
  10. data/lib/carrierwave/processing/mini_magick.rb +108 -123
  11. data/lib/carrierwave/processing/rmagick.rb +11 -15
  12. data/lib/carrierwave/processing/vips.rb +284 -0
  13. data/lib/carrierwave/processing.rb +1 -0
  14. data/lib/carrierwave/sanitized_file.rb +60 -66
  15. data/lib/carrierwave/storage/abstract.rb +5 -5
  16. data/lib/carrierwave/storage/file.rb +6 -5
  17. data/lib/carrierwave/storage/fog.rb +101 -62
  18. data/lib/carrierwave/storage.rb +1 -0
  19. data/lib/carrierwave/test/matchers.rb +11 -7
  20. data/lib/carrierwave/uploader/cache.rb +40 -24
  21. data/lib/carrierwave/uploader/callbacks.rb +1 -1
  22. data/lib/carrierwave/uploader/configuration.rb +38 -19
  23. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  24. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  25. data/lib/carrierwave/uploader/dimension.rb +66 -0
  26. data/lib/carrierwave/uploader/download.rb +2 -123
  27. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  28. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  29. data/lib/carrierwave/uploader/file_size.rb +2 -2
  30. data/lib/carrierwave/uploader/mountable.rb +6 -0
  31. data/lib/carrierwave/uploader/processing.rb +42 -7
  32. data/lib/carrierwave/uploader/proxy.rb +17 -4
  33. data/lib/carrierwave/uploader/serialization.rb +1 -1
  34. data/lib/carrierwave/uploader/store.rb +46 -7
  35. data/lib/carrierwave/uploader/url.rb +7 -4
  36. data/lib/carrierwave/uploader/versions.rb +140 -105
  37. data/lib/carrierwave/uploader.rb +10 -17
  38. data/lib/carrierwave/utilities/file_name.rb +47 -0
  39. data/lib/carrierwave/utilities/uri.rb +14 -11
  40. data/lib/carrierwave/utilities.rb +1 -0
  41. data/lib/carrierwave/validations/active_model.rb +7 -9
  42. data/lib/carrierwave/version.rb +1 -1
  43. data/lib/carrierwave.rb +13 -17
  44. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +2 -2
  45. data/lib/generators/uploader_generator.rb +3 -3
  46. metadata +100 -33
  47. data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
  48. data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
  49. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -51
  50. data/lib/carrierwave/uploader/extension_whitelist.rb +0 -52
@@ -2,15 +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 #:nodoc:
6
- attr_reader :column, :record, :remote_urls, :integrity_error,
7
- :processing_error, :download_error
8
- attr_accessor :remove, :remote_request_headers
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
9
20
 
10
- def initialize(record, column, options={})
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
37
+
38
+ def initialize(record, column)
11
39
  @record = record
12
40
  @column = column
13
41
  @options = record.class.uploader_options[column]
42
+ @download_errors = []
43
+ @processing_errors = []
44
+ @integrity_errors = []
45
+
46
+ @removed_uploaders = []
47
+ @added_uploaders = []
14
48
  end
15
49
 
16
50
  def uploader_class
@@ -32,27 +66,38 @@ module CarrierWave
32
66
  def uploaders
33
67
  @uploaders ||= read_identifiers.map do |identifier|
34
68
  uploader = blank_uploader
35
- uploader.retrieve_from_store!(identifier) if identifier.present?
69
+ uploader.retrieve_from_store!(identifier)
36
70
  uploader
37
71
  end
38
72
  end
39
73
 
40
74
  def cache(new_files)
41
- return if not new_files or new_files == ""
75
+ return if !new_files.is_a?(Array) && new_files.blank?
76
+ old_uploaders = uploaders
42
77
  @uploaders = new_files.map do |new_file|
43
- uploader = blank_uploader
44
- uploader.cache!(new_file)
45
- uploader
46
- end
47
-
48
- @integrity_error = nil
49
- @processing_error = nil
50
- rescue CarrierWave::IntegrityError => e
51
- @integrity_error = e
52
- raise e unless option(:ignore_integrity_errors)
53
- rescue CarrierWave::ProcessingError => e
54
- @processing_error = e
55
- raise e unless option(:ignore_processing_errors)
78
+ handle_error do
79
+ if new_file.is_a?(String)
80
+ if (uploader = old_uploaders.detect { |old_uploader| old_uploader.identifier == new_file })
81
+ uploader.staged = true
82
+ uploader
83
+ else
84
+ begin
85
+ uploader = blank_uploader
86
+ uploader.retrieve_from_cache!(new_file)
87
+ uploader
88
+ rescue CarrierWave::InvalidParameter
89
+ nil
90
+ end
91
+ end
92
+ else
93
+ uploader = blank_uploader
94
+ uploader.cache!(new_file)
95
+ uploader
96
+ end
97
+ end
98
+ end.reject(&:blank?)
99
+ @removed_uploaders += (old_uploaders - @uploaders)
100
+ write_temporary_identifier
56
101
  end
57
102
 
58
103
  def cache_names
@@ -60,45 +105,55 @@ module CarrierWave
60
105
  end
61
106
 
62
107
  def cache_names=(cache_names)
63
- return if not cache_names or cache_names == "" or uploaders.any?(&:cached?)
64
- @uploaders = cache_names.map do |cache_name|
108
+ cache_names = cache_names.reject(&:blank?)
109
+ return if cache_names.blank?
110
+ clear_unstaged
111
+ cache_names.each do |cache_name|
65
112
  uploader = blank_uploader
66
113
  uploader.retrieve_from_cache!(cache_name)
67
- uploader
114
+ @uploaders << uploader
115
+ rescue CarrierWave::InvalidParameter
116
+ # ignore
68
117
  end
69
- rescue CarrierWave::InvalidParameter
118
+ write_temporary_identifier
70
119
  end
71
120
 
72
121
  def remote_urls=(urls)
73
- return if not urls or urls == "" or urls.all?(&:blank?)
74
-
122
+ if urls.nil?
123
+ urls = []
124
+ else
125
+ urls = Array.wrap(urls).reject(&:blank?)
126
+ return if urls.blank?
127
+ end
75
128
  @remote_urls = urls
76
- @download_error = nil
77
- @integrity_error = nil
78
129
 
79
- @uploaders = urls.zip(remote_request_headers || []).map do |url, header|
80
- uploader = blank_uploader
81
- uploader.download!(url, header || {})
82
- uploader
130
+ clear_unstaged
131
+ @remote_urls.zip(remote_request_headers || []) do |url, header|
132
+ handle_error do
133
+ uploader = blank_uploader
134
+ uploader.download!(url, header || {})
135
+ @uploaders << uploader
136
+ end
83
137
  end
84
-
85
- rescue CarrierWave::DownloadError => e
86
- @download_error = e
87
- raise e unless option(:ignore_download_errors)
88
- rescue CarrierWave::ProcessingError => e
89
- @processing_error = e
90
- raise e unless option(:ignore_processing_errors)
91
- rescue CarrierWave::IntegrityError => e
92
- @integrity_error = e
93
- raise e unless option(:ignore_integrity_errors)
138
+ write_temporary_identifier
94
139
  end
95
140
 
96
141
  def store!
97
- if remove?
98
- remove!
99
- else
100
- uploaders.reject(&:blank?).each(&:store!)
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
101
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)
102
157
  end
103
158
 
104
159
  def urls(*args)
@@ -109,50 +164,50 @@ module CarrierWave
109
164
  uploaders.none?(&:present?)
110
165
  end
111
166
 
167
+ def remove=(value)
168
+ @remove = value
169
+ write_temporary_identifier
170
+ end
171
+
112
172
  def remove?
113
- remove.present? && remove !~ /\A0|false$\z/
173
+ remove.present? && (remove.to_s !~ /\A0|false$\z/)
114
174
  end
115
175
 
116
176
  def remove!
117
- uploaders.reject(&:blank?).each(&:remove!)
177
+ uploaders.each(&:remove!)
178
+ clear!
179
+ end
180
+
181
+ def clear!
182
+ @removed_uploaders += uploaders
183
+ @remove = nil
118
184
  @uploaders = []
119
185
  end
120
186
 
187
+ def reset_changes!
188
+ @removed_uploaders = []
189
+ @added_uploaders = []
190
+ end
191
+
121
192
  def serialization_column
122
193
  option(:mount_on) || column
123
194
  end
124
195
 
125
- def remove_previous(before=nil, after=nil)
126
- after ||= []
127
- return unless before
128
-
129
- # both 'before' and 'after' can be string when 'mount_on' option is set
130
- before = before.reject(&:blank?).map do |value|
131
- if value.is_a?(String)
132
- uploader = blank_uploader
133
- uploader.retrieve_from_store!(value)
134
- uploader
135
- else
136
- value
137
- end
138
- end
139
- after_paths = after.reject(&:blank?).map do |value|
140
- if value.is_a?(String)
141
- uploader = blank_uploader
142
- uploader.retrieve_from_store!(value)
143
- uploader
144
- else
145
- value
146
- end.path
147
- end
148
- before.each do |uploader|
149
- if uploader.remove_previously_stored_files_after_update and not after_paths.include?(uploader.path)
150
- uploader.remove!
151
- end
152
- 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!
153
202
  end
154
203
 
155
- attr_accessor :uploader_options
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
156
211
 
157
212
  private
158
213
 
@@ -161,5 +216,38 @@ module CarrierWave
161
216
  self.uploader_options[name] ||= record.class.uploader_option(column, name)
162
217
  end
163
218
 
219
+ def clear_unstaged
220
+ @uploaders ||= []
221
+ staged, unstaged = @uploaders.partition(&:staged)
222
+ @uploaders = staged
223
+ @removed_uploaders += unstaged
224
+ end
225
+
226
+ def handle_error
227
+ yield
228
+ rescue CarrierWave::DownloadError => e
229
+ @download_errors << e
230
+ raise e unless option(:ignore_download_errors)
231
+ rescue CarrierWave::ProcessingError => e
232
+ @processing_errors << e
233
+ raise e unless option(:ignore_processing_errors)
234
+ rescue CarrierWave::IntegrityError => e
235
+ @integrity_errors << e
236
+ raise e unless option(:ignore_integrity_errors)
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
164
252
  end # Mounter
165
253
  end # CarrierWave
@@ -6,36 +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
- class_eval <<-RUBY, __FILE__, __LINE__+1
16
- def remote_#{column}_url=(url)
17
- column = _mounter(:#{column}).serialization_column
18
- __send__(:"\#{column}_will_change!")
19
- super
20
- end
21
- RUBY
22
- end
23
-
24
- ##
25
- # See +CarrierWave::Mount#mount_uploaders+ for documentation
26
- #
27
- def mount_uploaders(column, uploader=nil, options={}, &block)
28
- super
29
-
30
- class_eval <<-RUBY, __FILE__, __LINE__+1
31
- def remote_#{column}_urls=(url)
32
- column = _mounter(:#{column}).serialization_column
33
- __send__(:"\#{column}_will_change!")
34
- super
35
- end
36
- RUBY
37
- end
38
-
39
9
  private
40
10
 
41
11
  def mount_base(column, uploader=nil, options={}, &block)
@@ -57,32 +27,13 @@ module CarrierWave
57
27
  after_commit :"remove_#{column}!", :on => :destroy
58
28
  after_commit :"mark_remove_#{column}_false", :on => :update
59
29
 
60
- after_save :"store_previous_changes_for_#{column}"
30
+ after_commit :"reset_previous_changes_for_#{column}"
61
31
  after_commit :"remove_previously_stored_#{column}", :on => :update
32
+ after_rollback :"remove_rolled_back_#{column}"
62
33
 
63
- class_eval <<-RUBY, __FILE__, __LINE__+1
64
- def #{column}=(new_file)
65
- column = _mounter(:#{column}).serialization_column
66
- if !(new_file.blank? && __send__(:#{column}).blank?)
67
- __send__(:"\#{column}_will_change!")
68
- end
69
-
70
- super
71
- end
72
-
73
- def remove_#{column}=(value)
74
- column = _mounter(:#{column}).serialization_column
75
- __send__(:"\#{column}_will_change!")
76
- super
77
- end
78
-
79
- def remove_#{column}!
80
- self.remove_#{column} = true
81
- write_#{column}_identifier
82
- self.remove_#{column} = false
83
- super
84
- end
85
-
34
+ mod = Module.new
35
+ prepend mod
36
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
86
37
  # Reset cached mounter on record reload
87
38
  def reload(*)
88
39
  @_mounters = nil
@@ -91,7 +42,14 @@ module CarrierWave
91
42
 
92
43
  # Reset cached mounter on record dup
93
44
  def initialize_dup(other)
94
- @_mounters = nil
45
+ old_uploaders = _mounter(:"#{column}").uploaders
46
+ @_mounters[:"#{column}"] = nil
47
+ super
48
+ _mounter(:"#{column}").cache(old_uploaders)
49
+ end
50
+
51
+ def write_#{column}_identifier
52
+ return unless has_attribute?(_mounter(:#{column}).serialization_column)
95
53
  super
96
54
  end
97
55
  RUBY