carrierwave 0.11.2 → 1.0.0

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.

Potentially problematic release.


This version of carrierwave might be problematic. Click here for more details.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +215 -116
  3. data/lib/carrierwave/compatibility/paperclip.rb +0 -2
  4. data/lib/carrierwave/error.rb +1 -0
  5. data/lib/carrierwave/locale/en.yml +7 -4
  6. data/lib/carrierwave/mount.rb +217 -176
  7. data/lib/carrierwave/mounter.rb +164 -0
  8. data/lib/carrierwave/orm/activerecord.rb +49 -20
  9. data/lib/carrierwave/processing/mini_magick.rb +64 -13
  10. data/lib/carrierwave/processing/rmagick.rb +31 -3
  11. data/lib/carrierwave/processing.rb +0 -1
  12. data/lib/carrierwave/sanitized_file.rb +43 -38
  13. data/lib/carrierwave/storage/abstract.rb +15 -2
  14. data/lib/carrierwave/storage/file.rb +65 -2
  15. data/lib/carrierwave/storage/fog.rb +101 -27
  16. data/lib/carrierwave/storage.rb +0 -9
  17. data/lib/carrierwave/test/matchers.rb +77 -12
  18. data/lib/carrierwave/uploader/cache.rb +40 -26
  19. data/lib/carrierwave/uploader/callbacks.rb +0 -2
  20. data/lib/carrierwave/uploader/configuration.rb +48 -6
  21. data/lib/carrierwave/uploader/default_url.rb +3 -5
  22. data/lib/carrierwave/uploader/download.rb +10 -7
  23. data/lib/carrierwave/uploader/extension_blacklist.rb +14 -10
  24. data/lib/carrierwave/uploader/extension_whitelist.rb +12 -10
  25. data/lib/carrierwave/uploader/file_size.rb +41 -0
  26. data/lib/carrierwave/uploader/magic_mime_blacklist.rb +94 -0
  27. data/lib/carrierwave/uploader/magic_mime_whitelist.rb +94 -0
  28. data/lib/carrierwave/uploader/mountable.rb +7 -8
  29. data/lib/carrierwave/uploader/processing.rb +1 -3
  30. data/lib/carrierwave/uploader/proxy.rb +5 -7
  31. data/lib/carrierwave/uploader/remove.rb +0 -2
  32. data/lib/carrierwave/uploader/serialization.rb +1 -3
  33. data/lib/carrierwave/uploader/store.rb +5 -23
  34. data/lib/carrierwave/uploader/url.rb +1 -3
  35. data/lib/carrierwave/uploader/versions.rb +75 -82
  36. data/lib/carrierwave/uploader.rb +2 -2
  37. data/lib/carrierwave/utilities/uri.rb +4 -7
  38. data/lib/carrierwave/utilities.rb +0 -3
  39. data/lib/carrierwave/validations/active_model.rb +0 -2
  40. data/lib/carrierwave/version.rb +1 -1
  41. data/lib/carrierwave.rb +8 -10
  42. data/lib/generators/templates/uploader.rb +3 -5
  43. metadata +41 -95
  44. data/lib/carrierwave/locale/cs.yml +0 -11
  45. data/lib/carrierwave/locale/de.yml +0 -11
  46. data/lib/carrierwave/locale/el.yml +0 -11
  47. data/lib/carrierwave/locale/es.yml +0 -11
  48. data/lib/carrierwave/locale/fr.yml +0 -11
  49. data/lib/carrierwave/locale/ja.yml +0 -11
  50. data/lib/carrierwave/locale/nb.yml +0 -11
  51. data/lib/carrierwave/locale/nl.yml +0 -11
  52. data/lib/carrierwave/locale/pl.yml +0 -11
  53. data/lib/carrierwave/locale/pt-BR.yml +0 -11
  54. data/lib/carrierwave/locale/pt-PT.yml +0 -11
  55. data/lib/carrierwave/locale/ru.yml +0 -11
  56. data/lib/carrierwave/locale/sk.yml +0 -11
  57. data/lib/carrierwave/locale/tr.yml +0 -11
  58. data/lib/carrierwave/processing/mime_types.rb +0 -74
  59. data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Storage
5
3
 
@@ -25,7 +23,7 @@ module CarrierWave
25
23
  # [:aws_access_key_id]
26
24
  # [:aws_secret_access_key]
27
25
  # [:region] (optional) defaults to 'us-east-1'
28
- # :region should be one of ['eu-west-1', 'us-east-1', 'ap-southeast-1', 'us-west-1', 'ap-northeast-1']
26
+ # :region should be one of ['eu-west-1', 'us-east-1', 'ap-southeast-1', 'us-west-1', 'ap-northeast-1', 'eu-central-1']
29
27
  #
30
28
  #
31
29
  # Google credentials contain the following keys:
@@ -94,6 +92,57 @@ module CarrierWave
94
92
  CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path(identifier))
95
93
  end
96
94
 
95
+ ##
96
+ # Stores given file to cache directory.
97
+ #
98
+ # === Parameters
99
+ #
100
+ # [new_file (File, IOString, Tempfile)] any kind of file object
101
+ #
102
+ # === Returns
103
+ #
104
+ # [CarrierWave::SanitizedFile] a sanitized file
105
+ #
106
+ def cache!(new_file)
107
+ f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.cache_path)
108
+ f.store(new_file)
109
+ f
110
+ end
111
+
112
+ ##
113
+ # Retrieves the file with the given cache_name from the cache.
114
+ #
115
+ # === Parameters
116
+ #
117
+ # [cache_name (String)] uniquely identifies a cache file
118
+ #
119
+ # === Raises
120
+ #
121
+ # [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
122
+ #
123
+ def retrieve_from_cache!(identifier)
124
+ CarrierWave::Storage::Fog::File.new(uploader, self, uploader.cache_path(identifier))
125
+ end
126
+
127
+ ##
128
+ # Deletes a cache dir
129
+ #
130
+ def delete_dir!(path)
131
+ # do nothing, because there's no such things as 'empty directory'
132
+ end
133
+
134
+ def clean_cache!(seconds)
135
+ connection.directories.new(
136
+ :key => uploader.fog_directory,
137
+ :public => uploader.fog_public
138
+ ).files.all(:prefix => uploader.cache_dir).each do |file|
139
+ # generate_cache_id returns key formated TIMEINT-PID-COUNTER-RND
140
+ time = file.key.scan(/(\d+)-\d+-\d+-\d+/).first.map { |t| t.to_i }
141
+ time = Time.at(*time)
142
+ file.destroy if time < (Time.now.utc - seconds)
143
+ end
144
+ end
145
+
97
146
  def connection
98
147
  @connection ||= begin
99
148
  options = credentials = uploader.fog_credentials
@@ -139,15 +188,17 @@ module CarrierWave
139
188
  # avoid a get by using local references
140
189
  local_directory = connection.directories.new(:key => @uploader.fog_directory)
141
190
  local_file = local_directory.files.new(:key => path)
142
- if @uploader.fog_credentials[:provider] == "AWS"
143
- local_file.url(::Fog::Time.now + @uploader.fog_authenticated_url_expiration, options)
144
- elsif ['Rackspace', 'OpenStack'].include?(@uploader.fog_credentials[:provider])
145
- connection.get_object_https_url(@uploader.fog_directory, path, ::Fog::Time.now + @uploader.fog_authenticated_url_expiration)
146
- else
147
- local_file.url(::Fog::Time.now + @uploader.fog_authenticated_url_expiration)
191
+ expire_at = ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
192
+ case @uploader.fog_credentials[:provider]
193
+ when 'AWS'
194
+ local_file.url(expire_at, options)
195
+ when 'Rackspace'
196
+ connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
197
+ when 'OpenStack'
198
+ connection.get_object_https_url(@uploader.fog_directory, path, expire_at)
199
+ else
200
+ local_file.url(expire_at)
148
201
  end
149
- else
150
- nil
151
202
  end
152
203
  end
153
204
 
@@ -159,7 +210,7 @@ module CarrierWave
159
210
  # [String] value of content-type
160
211
  #
161
212
  def content_type
162
- @content_type || file.content_type
213
+ @content_type || !file.nil? && file.content_type
163
214
  end
164
215
 
165
216
  ##
@@ -234,7 +285,7 @@ module CarrierWave
234
285
  # [Integer] size of file body
235
286
  #
236
287
  def size
237
- file.content_length
288
+ file.nil? ? 0 : file.content_length
238
289
  end
239
290
 
240
291
  ##
@@ -254,15 +305,19 @@ module CarrierWave
254
305
  #
255
306
  # [Boolean] true on success or raises error
256
307
  def store(new_file)
257
- fog_file = new_file.to_file
258
- @content_type ||= new_file.content_type
259
- @file = directory.files.create({
260
- :body => fog_file ? fog_file : new_file.read,
261
- :content_type => @content_type,
262
- :key => path,
263
- :public => @uploader.fog_public
264
- }.merge(@uploader.fog_attributes))
265
- fog_file.close if fog_file && !fog_file.closed?
308
+ if new_file.is_a?(self.class)
309
+ new_file.copy_to(path)
310
+ else
311
+ fog_file = new_file.to_file
312
+ @content_type ||= new_file.content_type
313
+ @file = directory.files.create({
314
+ :body => (fog_file ? fog_file : new_file).read,
315
+ :content_type => @content_type,
316
+ :key => path,
317
+ :public => @uploader.fog_public
318
+ }.merge(@uploader.fog_attributes))
319
+ fog_file.close if fog_file && !fog_file.closed?
320
+ end
266
321
  true
267
322
  end
268
323
 
@@ -285,7 +340,7 @@ module CarrierWave
285
340
  end
286
341
  else
287
342
  # AWS/Google optimized for speed over correctness
288
- case @uploader.fog_credentials[:provider]
343
+ case @uploader.fog_credentials[:provider].to_s
289
344
  when 'AWS'
290
345
  # check if some endpoint is set in fog_credentials
291
346
  if @uploader.fog_credentials.has_key?(:endpoint)
@@ -301,7 +356,8 @@ module CarrierWave
301
356
  end
302
357
  end
303
358
  when 'Google'
304
- "https://commondatastorage.googleapis.com/#{@uploader.fog_directory}/#{encoded_path}"
359
+ # https://cloud.google.com/storage/docs/access-public-data
360
+ "https://storage.googleapis.com/#{@uploader.fog_directory}/#{encoded_path}"
305
361
  else
306
362
  # avoid a get by just using local reference
307
363
  directory.files.new(:key => path).public_url
@@ -336,9 +392,24 @@ module CarrierWave
336
392
  # [NilClass] no file name available
337
393
  #
338
394
  def filename(options = {})
339
- if file_url = url(options)
340
- URI.decode(file_url).gsub(/.*\/(.*?$)/, '\1').split('?').first
341
- end
395
+ return unless file_url = url(options)
396
+ CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
397
+ end
398
+
399
+ ##
400
+ # Creates a copy of this file and returns it.
401
+ #
402
+ # === Parameters
403
+ #
404
+ # [new_path (String)] The path where the file should be copied to.
405
+ #
406
+ # === Returns
407
+ #
408
+ # @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
409
+ #
410
+ def copy_to(new_path)
411
+ connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, acl_header)
412
+ CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
342
413
  end
343
414
 
344
415
  private
@@ -381,6 +452,9 @@ module CarrierWave
381
452
  @file ||= directory.files.head(path)
382
453
  end
383
454
 
455
+ def acl_header
456
+ {'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private'}
457
+ end
384
458
  end
385
459
 
386
460
  end # Fog
@@ -1,11 +1,2 @@
1
1
  require "carrierwave/storage/abstract"
2
2
  require "carrierwave/storage/file"
3
-
4
- %w(aws google openstack rackspace).each do |fog_dependency|
5
- begin
6
- require "fog/#{fog_dependency}"
7
- rescue LoadError
8
- end
9
- end
10
-
11
- require "carrierwave/storage/fog" if defined?(Fog)
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Test
5
3
 
@@ -23,13 +21,16 @@ module CarrierWave
23
21
  "expected #{@actual.inspect} to be identical to #{@expected.inspect}"
24
22
  end
25
23
 
26
- def negative_failure_message
24
+ def failure_message_when_negated
27
25
  "expected #{@actual.inspect} to not be identical to #{@expected.inspect}"
28
26
  end
29
27
 
30
28
  def description
31
29
  "be identical to #{@expected.inspect}"
32
30
  end
31
+
32
+ # RSpec 2 compatibility:
33
+ alias_method :negative_failure_message, :failure_message_when_negated
33
34
  end
34
35
 
35
36
  def be_identical_to(expected)
@@ -51,13 +52,16 @@ module CarrierWave
51
52
  "expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
52
53
  end
53
54
 
54
- def negative_failure_message
55
+ def failure_message_when_negated
55
56
  "expected #{@actual.current_path.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
56
57
  end
57
58
 
58
59
  def description
59
60
  "have permissions #{@expected.to_s(8)}"
60
61
  end
62
+
63
+ # RSpec 2 compatibility:
64
+ alias_method :negative_failure_message, :failure_message_when_negated
61
65
  end
62
66
 
63
67
  def have_permissions(expected)
@@ -79,13 +83,16 @@ module CarrierWave
79
83
  "expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
80
84
  end
81
85
 
82
- def negative_failure_message
86
+ def failure_message_when_negated
83
87
  "expected #{File.dirname @actual.current_path.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
84
88
  end
85
89
 
86
90
  def description
87
91
  "have permissions #{@expected.to_s(8)}"
88
92
  end
93
+
94
+ # RSpec 2 compatibility:
95
+ alias_method :negative_failure_message, :failure_message_when_negated
89
96
  end
90
97
 
91
98
  def have_directory_permissions(expected)
@@ -110,13 +117,16 @@ module CarrierWave
110
117
  "expected #{@actual.current_path.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
111
118
  end
112
119
 
113
- def negative_failure_message
120
+ def failure_message_when_negated
114
121
  "expected #{@actual.current_path.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
115
122
  end
116
123
 
117
124
  def description
118
125
  "be no larger than #{@width} by #{@height}"
119
126
  end
127
+
128
+ # RSpec 2 compatibility:
129
+ alias_method :negative_failure_message, :failure_message_when_negated
120
130
  end
121
131
 
122
132
  def be_no_larger_than(width, height)
@@ -141,13 +151,16 @@ module CarrierWave
141
151
  "expected #{@actual.current_path.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
142
152
  end
143
153
 
144
- def negative_failure_message
154
+ def failure_message_when_negated
145
155
  "expected #{@actual.current_path.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
146
156
  end
147
157
 
148
158
  def description
149
159
  "have an exact size of #{@width} by #{@height}"
150
160
  end
161
+
162
+ # RSpec 2 compatibility:
163
+ alias_method :negative_failure_message, :failure_message_when_negated
151
164
  end
152
165
 
153
166
  def have_dimensions(width, height)
@@ -171,13 +184,16 @@ module CarrierWave
171
184
  "expected #{@actual.current_path.inspect} to have an exact size of #{@height}, but it was #{@actual_height}."
172
185
  end
173
186
 
174
- def negative_failure_message
187
+ def failure_message_when_negated
175
188
  "expected #{@actual.current_path.inspect} not to have an exact size of #{@height}, but it did."
176
189
  end
177
190
 
178
191
  def description
179
192
  "have an exact height of #{@height}"
180
193
  end
194
+
195
+ # RSpec 2 compatibility:
196
+ alias_method :negative_failure_message, :failure_message_when_negated
181
197
  end
182
198
 
183
199
  def have_height(height)
@@ -201,13 +217,16 @@ module CarrierWave
201
217
  "expected #{@actual.current_path.inspect} to have an exact size of #{@width}, but it was #{@actual_width}."
202
218
  end
203
219
 
204
- def negative_failure_message
220
+ def failure_message_when_negated
205
221
  "expected #{@actual.current_path.inspect} not to have an exact size of #{@width}, but it did."
206
222
  end
207
223
 
208
224
  def description
209
225
  "have an exact width of #{@width}"
210
226
  end
227
+
228
+ # RSpec 2 compatibility:
229
+ alias_method :negative_failure_message, :failure_message_when_negated
211
230
  end
212
231
 
213
232
  def have_width(width)
@@ -231,13 +250,16 @@ module CarrierWave
231
250
  "expected #{@actual.current_path.inspect} to be no wider than #{@width}, but it was #{@actual_width}."
232
251
  end
233
252
 
234
- def negative_failure_message
253
+ def failure_message_when_negated
235
254
  "expected #{@actual.current_path.inspect} not to be wider than #{@width}, but it is."
236
255
  end
237
256
 
238
257
  def description
239
258
  "have a width less than or equal to #{@width}"
240
259
  end
260
+
261
+ # RSpec 2 compatibility:
262
+ alias_method :negative_failure_message, :failure_message_when_negated
241
263
  end
242
264
 
243
265
  def be_no_wider_than(width)
@@ -261,19 +283,55 @@ module CarrierWave
261
283
  "expected #{@actual.current_path.inspect} to be no taller than #{@height}, but it was #{@actual_height}."
262
284
  end
263
285
 
264
- def negative_failure_message
286
+ def failure_message_when_negated
265
287
  "expected #{@actual.current_path.inspect} not to be taller than #{@height}, but it is."
266
288
  end
267
289
 
268
290
  def description
269
291
  "have a height less than or equal to #{@height}"
270
292
  end
293
+
294
+ # RSpec 2 compatibility:
295
+ alias_method :negative_failure_message, :failure_message_when_negated
271
296
  end
272
297
 
273
298
  def be_no_taller_than(height)
274
299
  BeNoTallerThan.new(height)
275
300
  end
276
301
 
302
+ class BeFormat # :nodoc:
303
+ def initialize(expected)
304
+ @expected = expected
305
+ end
306
+
307
+ def matches?(actual)
308
+ @actual = actual
309
+ # Satisfy expectation here. Return false or raise an error if it's not met.
310
+ image = ImageLoader.load_image(@actual.current_path)
311
+ @actual_expected = image.format
312
+ !@expected.nil? && @actual_expected.casecmp(@expected).zero?
313
+ end
314
+
315
+ def failure_message
316
+ "expected #{@actual.current_path.inspect} to have #{@expected} format, but it was #{@actual_expected}."
317
+ end
318
+
319
+ def failure_message_when_negated
320
+ "expected #{@actual.current_path.inspect} not to have #{@expected} format, but it did."
321
+ end
322
+
323
+ def description
324
+ "have #{@expected} format"
325
+ end
326
+
327
+ # RSpec 2 compatibility:
328
+ alias_method :negative_failure_message, :failure_message_when_negated
329
+ end
330
+
331
+ def be_format(expected)
332
+ BeFormat.new(expected)
333
+ end
334
+
277
335
  class ImageLoader # :nodoc:
278
336
  def self.load_image(filename)
279
337
  if defined? ::MiniMagick
@@ -303,6 +361,10 @@ module CarrierWave
303
361
  image.rows
304
362
  end
305
363
 
364
+ def format
365
+ image.format
366
+ end
367
+
306
368
  def initialize(filename)
307
369
  @image = ::Magick::Image.read(filename).first
308
370
  end
@@ -318,6 +380,10 @@ module CarrierWave
318
380
  image[:height]
319
381
  end
320
382
 
383
+ def format
384
+ image[:format]
385
+ end
386
+
321
387
  def initialize(filename)
322
388
  @image = ::MiniMagick::Image.open(filename)
323
389
  end
@@ -326,4 +392,3 @@ module CarrierWave
326
392
  end # Matchers
327
393
  end # Test
328
394
  end # CarrierWave
329
-
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
 
5
3
  class FormNotMultipart < UploadError
@@ -54,13 +52,7 @@ module CarrierWave
54
52
  # It's recommended that you keep cache files in one place only.
55
53
  #
56
54
  def clean_cached_files!(seconds=60*60*24)
57
- Dir.glob(File.expand_path(File.join(cache_dir, '*'), CarrierWave.root)).each do |dir|
58
- time = dir.scan(/(\d+)-\d+-\d+/).first.map { |t| t.to_i }
59
- time = Time.at(*time)
60
- if time < (Time.now.utc - seconds)
61
- FileUtils.rm_rf(dir)
62
- end
63
- end
55
+ cache_storage.new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
64
56
  end
65
57
  end
66
58
 
@@ -89,10 +81,8 @@ module CarrierWave
89
81
  _content = file.read
90
82
  if _content.is_a?(File) # could be if storage is Fog
91
83
  sanitized = CarrierWave::Storage::Fog.new(self).retrieve!(File.basename(_content.path))
92
- sanitized.read if sanitized.exists?
93
-
94
84
  else
95
- sanitized = SanitizedFile.new :tempfile => StringIO.new(file.read),
85
+ sanitized = SanitizedFile.new :tempfile => StringIO.new(_content),
96
86
  :filename => File.basename(path), :content_type => file.content_type
97
87
  end
98
88
  sanitized
@@ -127,22 +117,28 @@ module CarrierWave
127
117
  #
128
118
  def cache!(new_file = sanitized_file)
129
119
  new_file = CarrierWave::SanitizedFile.new(new_file)
120
+ return if new_file.empty?
121
+
122
+ raise CarrierWave::FormNotMultipart if new_file.is_path? && ensure_multipart_form
130
123
 
131
- unless new_file.empty?
132
- raise CarrierWave::FormNotMultipart if new_file.is_path? && ensure_multipart_form
124
+ self.cache_id = CarrierWave.generate_cache_id unless cache_id
133
125
 
134
- with_callbacks(:cache, new_file) do
135
- self.cache_id = CarrierWave.generate_cache_id unless cache_id
126
+ @filename = new_file.filename
127
+ self.original_filename = new_file.filename
136
128
 
137
- @filename = new_file.filename
138
- self.original_filename = new_file.filename
129
+ begin
130
+ # first, create a workfile on which we perform processings
131
+ if move_to_cache
132
+ @file = new_file.move_to(File.expand_path(workfile_path, root), permissions, directory_permissions)
133
+ else
134
+ @file = new_file.copy_to(File.expand_path(workfile_path, root), permissions, directory_permissions)
135
+ end
139
136
 
140
- if move_to_cache
141
- @file = new_file.move_to(cache_path, permissions, directory_permissions)
142
- else
143
- @file = new_file.copy_to(cache_path, permissions, directory_permissions)
144
- end
137
+ with_callbacks(:cache, @file) do
138
+ @file = cache_storage.cache!(@file)
145
139
  end
140
+ ensure
141
+ FileUtils.rm_rf(workfile_path(''))
146
142
  end
147
143
  end
148
144
 
@@ -161,14 +157,29 @@ module CarrierWave
161
157
  with_callbacks(:retrieve_from_cache, cache_name) do
162
158
  self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
163
159
  @filename = original_filename
164
- @file = CarrierWave::SanitizedFile.new(cache_path)
160
+ @file = cache_storage.retrieve_from_cache!(full_filename(original_filename))
165
161
  end
166
162
  end
167
163
 
164
+ ##
165
+ # Calculates the path where the cache file should be stored.
166
+ #
167
+ # === Parameters
168
+ #
169
+ # [for_file (String)] name of the file <optional>
170
+ #
171
+ # === Returns
172
+ #
173
+ # [String] the cache path
174
+ #
175
+ def cache_path(for_file=full_filename(original_filename))
176
+ File.join(*[cache_dir, @cache_id, for_file].compact)
177
+ end
178
+
168
179
  private
169
180
 
170
- def cache_path
171
- File.expand_path(File.join(cache_dir, cache_name), root)
181
+ def workfile_path(for_file=original_filename)
182
+ File.join(CarrierWave.tmp_path, @cache_id, version_name.to_s, for_file)
172
183
  end
173
184
 
174
185
  attr_reader :cache_id, :original_filename
@@ -188,6 +199,9 @@ module CarrierWave
188
199
  @original_filename = filename
189
200
  end
190
201
 
202
+ def cache_storage
203
+ @cache_storage ||= self.class.cache_storage.new(self)
204
+ end
191
205
  end # Cache
192
206
  end # Uploader
193
207
  end # CarrierWave
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
  module Uploader
5
3
  module Callbacks
@@ -5,7 +5,7 @@ module CarrierWave
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- class_attribute :_storage, :instance_writer => false
8
+ class_attribute :_storage, :_cache_storage, :instance_writer => false
9
9
 
10
10
  add_config :root
11
11
  add_config :base_path
@@ -23,6 +23,7 @@ module CarrierWave
23
23
  add_config :remove_previously_stored_files_after_update
24
24
 
25
25
  # fog
26
+ add_config :fog_provider
26
27
  add_config :fog_attributes
27
28
  add_config :fog_credentials
28
29
  add_config :fog_directory
@@ -38,6 +39,7 @@ module CarrierWave
38
39
  add_config :validate_processing
39
40
  add_config :validate_download
40
41
  add_config :mount_on
42
+ add_config :cache_only
41
43
 
42
44
  # set default values
43
45
  reset_config
@@ -69,17 +71,55 @@ module CarrierWave
69
71
  # storage MyCustomStorageEngine
70
72
  #
71
73
  def storage(storage = nil)
72
- if storage
73
- self._storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
74
+ case storage
75
+ when Symbol
76
+ if storage_engine = storage_engines[storage]
77
+ self._storage = eval storage_engine
78
+ else
79
+ raise CarrierWave::UnknownStorageError, "Unknown storage: #{storage}"
80
+ end
81
+ when nil
82
+ storage
83
+ else
84
+ self._storage = storage
74
85
  end
75
86
  _storage
76
87
  end
77
88
  alias_method :storage=, :storage
78
89
 
90
+ ##
91
+ # Sets the cache storage engine to be used when storing cache files with this uploader.
92
+ # Same as .storage except for required methods being #cache!(CarrierWave::SanitizedFile),
93
+ # #retrieve_from_cache! and #delete_dir!.
94
+ #
95
+ # === Parameters
96
+ #
97
+ # [storage (Symbol, Class)] The cache storage engine to use for this uploader
98
+ #
99
+ # === Returns
100
+ #
101
+ # [Class] the cache storage engine to be used with this uploader
102
+ #
103
+ # === Examples
104
+ #
105
+ # cache_storage :file
106
+ # cache_storage CarrierWave::Storage::File
107
+ # cache_storage MyCustomStorageEngine
108
+ #
109
+ def cache_storage(storage = nil)
110
+ if storage
111
+ self._cache_storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
112
+ end
113
+ _cache_storage
114
+ end
115
+ alias_method :cache_storage=, :cache_storage
116
+
79
117
  def add_config(name)
80
118
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
81
119
  def self.eager_load_fog(fog_credentials)
82
120
  # see #1198. This will hopefully no longer be necessary after fog 2.0
121
+ require self.fog_provider
122
+ require 'carrierwave/storage/fog'
83
123
  Fog::Storage.new(fog_credentials) if fog_credentials.present?
84
124
  end
85
125
 
@@ -93,12 +133,12 @@ module CarrierWave
93
133
  end
94
134
 
95
135
  def self.#{name}=(value)
96
- eager_load_fog(value) if '#{name}' == 'fog_credentials'
136
+ eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
97
137
  @#{name} = value
98
138
  end
99
139
 
100
140
  def #{name}=(value)
101
- self.class.eager_load_fog(value) if '#{name}' == 'fog_credentials'
141
+ self.class.eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
102
142
  @#{name} = value
103
143
  end
104
144
 
@@ -107,7 +147,7 @@ module CarrierWave
107
147
  value = self.class.#{name} unless instance_variable_defined?(:@#{name})
108
148
  if value.instance_of?(Proc)
109
149
  value.arity >= 1 ? value.call(self) : value.call
110
- else
150
+ else
111
151
  value
112
152
  end
113
153
  end
@@ -130,6 +170,8 @@ module CarrierWave
130
170
  :fog => "CarrierWave::Storage::Fog"
131
171
  }
132
172
  config.storage = :file
173
+ config.cache_storage = :file
174
+ config.fog_provider = 'fog'
133
175
  config.fog_attributes = {}
134
176
  config.fog_credentials = {}
135
177
  config.fog_public = true