carrierwave 1.3.2 → 3.0.2

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 +167 -77
  9. data/lib/carrierwave/orm/activerecord.rb +15 -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 +47 -7
  35. data/lib/carrierwave/uploader/url.rb +7 -4
  36. data/lib/carrierwave/uploader/versions.rb +153 -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
20
+
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
9
33
 
10
- def initialize(record, column, options={})
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,57 @@ 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
+ uploaders.each(&:store!)
143
+ @added_uploaders += uploaders.reject(&:staged)
144
+ end
145
+
146
+ def write_identifier
147
+ return if record.frozen?
148
+
149
+ clear! if remove?
150
+
151
+ additions, remains = uploaders.partition(&:cached?)
152
+ existing_identifiers = (@removed_uploaders + remains).map(&:identifier)
153
+ additions.each do |uploader|
154
+ uploader.deduplicate(existing_identifiers)
155
+ existing_identifiers << uploader.identifier
101
156
  end
157
+
158
+ record.write_uploader(serialization_column, identifier)
102
159
  end
103
160
 
104
161
  def urls(*args)
@@ -109,50 +166,50 @@ module CarrierWave
109
166
  uploaders.none?(&:present?)
110
167
  end
111
168
 
169
+ def remove=(value)
170
+ @remove = value
171
+ write_temporary_identifier
172
+ end
173
+
112
174
  def remove?
113
- remove.present? && remove !~ /\A0|false$\z/
175
+ remove.present? && (remove.to_s !~ /\A0|false$\z/)
114
176
  end
115
177
 
116
178
  def remove!
117
- uploaders.reject(&:blank?).each(&:remove!)
179
+ uploaders.each(&:remove!)
180
+ clear!
181
+ end
182
+
183
+ def clear!
184
+ @removed_uploaders += uploaders
185
+ @remove = nil
118
186
  @uploaders = []
119
187
  end
120
188
 
189
+ def reset_changes!
190
+ @removed_uploaders = []
191
+ @added_uploaders = []
192
+ end
193
+
121
194
  def serialization_column
122
195
  option(:mount_on) || column
123
196
  end
124
197
 
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
198
+ def remove_previous
199
+ current_paths = uploaders.map(&:path)
200
+ @removed_uploaders
201
+ .reject {|uploader| current_paths.include?(uploader.path) }
202
+ .each { |uploader| uploader.remove! if uploader.remove_previously_stored_files_after_update }
203
+ reset_changes!
153
204
  end
154
205
 
155
- attr_accessor :uploader_options
206
+ def remove_added
207
+ current_paths = (@removed_uploaders + uploaders.select(&:staged)).map(&:path)
208
+ @added_uploaders
209
+ .reject {|uploader| current_paths.include?(uploader.path) }
210
+ .each { |uploader| uploader.remove! }
211
+ reset_changes!
212
+ end
156
213
 
157
214
  private
158
215
 
@@ -161,5 +218,38 @@ module CarrierWave
161
218
  self.uploader_options[name] ||= record.class.uploader_option(column, name)
162
219
  end
163
220
 
221
+ def clear_unstaged
222
+ @uploaders ||= []
223
+ staged, unstaged = @uploaders.partition(&:staged)
224
+ @uploaders = staged
225
+ @removed_uploaders += unstaged
226
+ end
227
+
228
+ def handle_error
229
+ yield
230
+ rescue CarrierWave::DownloadError => e
231
+ @download_errors << e
232
+ raise e unless option(:ignore_download_errors)
233
+ rescue CarrierWave::ProcessingError => e
234
+ @processing_errors << e
235
+ raise e unless option(:ignore_processing_errors)
236
+ rescue CarrierWave::IntegrityError => e
237
+ @integrity_errors << e
238
+ raise e unless option(:ignore_integrity_errors)
239
+ end
240
+
241
+ def write_temporary_identifier
242
+ return if record.frozen?
243
+
244
+ record.write_uploader(serialization_column, temporary_identifier)
245
+ end
246
+
247
+ def temporary_identifiers
248
+ if remove?
249
+ []
250
+ else
251
+ uploaders.map { |uploader| uploader.temporary_identifier }
252
+ end
253
+ end
164
254
  end # Mounter
165
255
  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,16 @@ 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
+ # The attribute needs to be cleared to prevent it from picked up as identifier
48
+ write_attribute(:"#{column}", nil)
49
+ super
50
+ _mounter(:"#{column}").cache(old_uploaders)
51
+ end
52
+
53
+ def write_#{column}_identifier
54
+ return unless has_attribute?(_mounter(:#{column}).serialization_column)
95
55
  super
96
56
  end
97
57
  RUBY