jnicklas-carrierwave 0.2.2 → 0.2.3

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 (46) hide show
  1. data/README.rdoc +35 -20
  2. data/Rakefile +1 -1
  3. data/lib/carrierwave/compatibility/paperclip.rb +91 -0
  4. data/lib/carrierwave/core_ext/inheritable_attributes.rb +102 -0
  5. data/lib/carrierwave/core_ext/module_setup.rb +49 -0
  6. data/lib/carrierwave/mount.rb +119 -103
  7. data/lib/carrierwave/orm/activerecord.rb +6 -1
  8. data/lib/carrierwave/orm/sequel.rb +15 -2
  9. data/lib/carrierwave/processing/rmagick.rb +8 -7
  10. data/lib/carrierwave/storage/abstract.rb +16 -1
  11. data/lib/carrierwave/storage/file.rb +20 -1
  12. data/lib/carrierwave/uploader/cache.rb +114 -0
  13. data/lib/carrierwave/uploader/callbacks.rb +40 -0
  14. data/lib/carrierwave/uploader/default_path.rb +21 -0
  15. data/lib/carrierwave/uploader/extension_whitelist.rb +35 -0
  16. data/lib/carrierwave/uploader/mountable.rb +37 -0
  17. data/lib/carrierwave/uploader/paths.rb +25 -0
  18. data/lib/carrierwave/uploader/processing.rb +79 -0
  19. data/lib/carrierwave/uploader/proxy.rb +60 -0
  20. data/lib/carrierwave/uploader/remove.rb +21 -0
  21. data/lib/carrierwave/uploader/store.rb +154 -0
  22. data/lib/carrierwave/uploader/url.rb +22 -0
  23. data/lib/carrierwave/uploader/versions.rb +145 -0
  24. data/lib/carrierwave/uploader.rb +31 -593
  25. data/lib/carrierwave.rb +55 -7
  26. data/lib/generators/uploader_generator.rb +1 -1
  27. data/rails_generators/uploader/templates/uploader.rb +12 -8
  28. data/spec/compatibility/paperclip_spec.rb +41 -0
  29. data/spec/mount_spec.rb +88 -25
  30. data/spec/orm/activerecord_spec.rb +7 -9
  31. data/spec/orm/datamapper_spec.rb +7 -9
  32. data/spec/orm/sequel_spec.rb +47 -32
  33. data/spec/spec_helper.rb +13 -0
  34. data/spec/uploader/cache_spec.rb +194 -0
  35. data/spec/uploader/default_path_spec.rb +66 -0
  36. data/spec/uploader/extension_whitelist_spec.rb +42 -0
  37. data/spec/uploader/mountable_spec.rb +31 -0
  38. data/spec/uploader/paths_spec.rb +20 -0
  39. data/spec/uploader/processing_spec.rb +60 -0
  40. data/spec/uploader/proxy_spec.rb +52 -0
  41. data/spec/uploader/remove_spec.rb +65 -0
  42. data/spec/uploader/store_spec.rb +260 -0
  43. data/spec/uploader/url_spec.rb +85 -0
  44. data/spec/uploader/versions_spec.rb +275 -0
  45. metadata +34 -3
  46. data/spec/uploader_spec.rb +0 -887
@@ -1,603 +1,41 @@
1
1
  module CarrierWave
2
2
 
3
3
  ##
4
- # An uploader is a class that allows you to easily handle the caching and storage of
5
- # uploaded files. Please refer to the README for configuration options.
6
- #
7
- # Once you have an uploader you can use it in isolation:
8
- #
9
- # my_uploader = MyUploader.new
10
- # my_uploader.cache!(File.open(path_to_file))
11
- # my_uploader.retrieve_from_store!('monkey.png')
12
- #
13
- # Alternatively, you can mount it on an ORM or other persistence layer, with
14
- # +CarrierWave::Mount#mount_uploader+. There are extensions for activerecord and datamapper
15
- # these are *very* simple (they are only a dozen lines of code), so adding your own should
16
- # be trivial.
4
+ # See CarrierWave::Uploader::Base
17
5
  #
18
6
  module Uploader
19
7
 
20
- def self.append_features(base) #:nodoc:
21
- super
22
- base.extend(ClassMethods)
23
- end
24
-
25
- ##
26
- # Generates a unique cache id for use in the caching system
27
- #
28
- # === Returns
29
- #
30
- # [String] a cache id in the format YYYYMMDD-HHMM-PID-RND
31
- #
32
- def self.generate_cache_id
33
- Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
34
- end
35
-
36
- module ClassMethods
37
-
38
- ##
39
- # Lists processor callbacks declared
40
- #
41
- # === Returns
42
- #
43
- # [Array[Array[Symbol, Array]]] a list of processor callbacks which have been declared for this uploader
44
- #
45
- def processors
46
- @processors ||= []
47
- end
48
-
49
- ##
50
- # Adds a processor callback which applies operations as a file is uploaded.
51
- # The argument may be the name of any method of the uploader, expressed as a symbol,
52
- # or a list of such methods, or a hash where the key is a method and the value is
53
- # an array of arguments to call the method with
54
- #
55
- # === Parameters
56
- #
57
- # args (*Symbol, Hash{Symbol => Array[]})
58
- #
59
- # === Examples
60
- #
61
- # class MyUploader
62
- # include CarrierWave::Uploader
63
- #
64
- # process :sepiatone, :vignette
65
- # process :scale => [200, 200]
66
- #
67
- # def sepiatone
68
- # ...
69
- # end
70
- #
71
- # def vignette
72
- # ...
73
- # end
74
- #
75
- # def scale(height, width)
76
- # ...
77
- # end
78
- # end
79
- #
80
- def process(*args)
81
- args.each do |arg|
82
- if arg.is_a?(Hash)
83
- arg.each do |method, args|
84
- processors.push([method, args])
85
- end
86
- else
87
- processors.push([arg, []])
88
- end
89
- end
90
- end
91
-
92
- ##
93
- # Sets the storage engine to be used when storing files with this uploader.
94
- # Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
95
- # method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
96
- # be added to CarrierWave.config[:storage_engines] so they can be referred
97
- # to by a symbol, which should be more convenient
98
- #
99
- # If no argument is given, it will simply return the currently used storage engine.
100
- #
101
- # === Parameters
102
- #
103
- # [storage (Symbol, Class)] The storage engine to use for this uploader
104
- #
105
- # === Returns
106
- #
107
- # [Class] the storage engine to be used with this uploader
108
- #
109
- # === Examples
110
- #
111
- # storage :file
112
- # storage CarrierWave::Storage::File
113
- # storage MyCustomStorageEngine
114
- #
115
- def storage(storage = nil)
116
- if storage.is_a?(Symbol)
117
- @storage = get_storage_by_symbol(storage)
118
- @storage.setup!
119
- elsif storage
120
- @storage = storage
121
- @storage.setup!
122
- elsif @storage.nil?
123
- # Get the storage from the superclass if there is one
124
- @storage = superclass.storage rescue nil
125
- end
126
- if @storage.nil?
127
- # If we were not able to find a store any other way, setup the default store
128
- @storage ||= get_storage_by_symbol(CarrierWave.config[:storage])
129
- @storage.setup!
130
- end
131
- return @storage
132
- end
133
-
134
- alias_method :storage=, :storage
135
-
136
- def version_names
137
- @version_names ||= []
138
- end
139
-
140
- ##
141
- # Adds a new version to this uploader
142
- #
143
- # === Parameters
144
- #
145
- # [name (#to_sym)] name of the version
146
- # [&block (Proc)] a block to eval on this version of the uploader
147
- #
148
- def version(name, &block)
149
- name = name.to_sym
150
- unless versions[name]
151
- versions[name] = Class.new(self)
152
- versions[name].version_names.push(*version_names)
153
- versions[name].version_names.push(name)
154
- class_eval <<-RUBY
155
- def #{name}
156
- versions[:#{name}]
157
- end
158
- RUBY
159
- end
160
- versions[name].class_eval(&block) if block
161
- versions[name]
162
- end
163
-
164
- ##
165
- # === Returns
166
- #
167
- # [Hash{Symbol => Class}] a list of versions available for this uploader
168
- #
169
- def versions
170
- @versions ||= {}
171
- end
172
-
173
- private
174
-
175
- def get_storage_by_symbol(symbol)
176
- eval(CarrierWave.config[:storage_engines][symbol])
177
- end
178
-
179
- end # ClassMethods
180
-
181
- attr_reader :file, :model, :mounted_as
182
-
183
- ##
184
- # If a model is given as the first parameter, it will stored in the uploader, and
185
- # available throught +#model+. Likewise, mounted_as stores the name of the column
186
- # where this instance of the uploader is mounted. These values can then be used inside
187
- # your uploader.
188
- #
189
- # If you do not wish to mount your uploaders with the ORM extensions in -more then you
190
- # can override this method inside your uploader.
191
- #
192
- # === Parameters
193
- #
194
- # [model (Object)] Any kind of model object
195
- # [mounted_as (Symbol)] The name of the column where this uploader is mounted
196
- #
197
- # === Examples
198
- #
199
- # class MyUploader
200
- # include CarrierWave::Uploader
201
- #
202
- # def store_dir
203
- # File.join('public', 'files', mounted_as, model.permalink)
204
- # end
205
- # end
206
- #
207
- def initialize(model=nil, mounted_as=nil)
208
- @model = model
209
- @mounted_as = mounted_as
210
- end
211
-
212
- ##
213
- # === Returns
214
- #
215
- # [Boolean] Whether the uploaded file is blank
216
- #
217
- def blank?
218
- !file or file.blank?
219
- end
220
-
221
- ##
222
- # Apply all process callbacks added through CarrierWave.process
223
- #
224
- def process!
225
- self.class.processors.each do |method, args|
226
- self.send(method, *args)
227
- end
228
- end
229
-
230
- ##
231
- # === Returns
232
- #
233
- # [String] the path where the file is currently located.
234
- #
235
- def current_path
236
- file.path if file.respond_to?(:path)
237
- end
238
-
239
- alias_method :path, :current_path
240
-
241
- ##
242
- # Returns a hash mapping the name of each version of the uploader to an instance of it
243
- #
244
- # === Returns
245
- #
246
- # [Hash{Symbol => CarrierWave::Uploader}] a list of uploader instances
247
- #
248
- def versions
249
- return @versions if @versions
250
- @versions = {}
251
- self.class.versions.each do |name, klass|
252
- @versions[name] = klass.new(model, mounted_as)
253
- end
254
- @versions
255
- end
256
-
257
- ##
258
- # === Returns
259
- #
260
- # [String] the location where this file is accessible via a url
261
- #
262
- def url(*args)
263
- if(args.first)
264
- # recursively proxy to version
265
- versions[args.first.to_sym].url(*args[1..-1])
266
- else
267
- if file.respond_to?(:url) and not file.url.blank?
268
- file.url
269
- elsif current_path
270
- File.expand_path(current_path).gsub(File.expand_path(public), '')
271
- end
272
- end
273
- end
274
-
275
- alias_method :to_s, :url
276
-
277
8
  ##
278
- # Returns a string that uniquely identifies the last stored file
279
- #
280
- # === Returns
281
- #
282
- # [String] uniquely identifies a file
283
- #
284
- def identifier
285
- file.identifier if file.respond_to?(:identifier)
286
- end
287
-
288
- ##
289
- # Read the contents of the file
290
- #
291
- # === Returns
292
- #
293
- # [String] contents of the file
294
- #
295
- def read
296
- file.read if file.respond_to?(:read)
297
- end
298
-
299
- ##
300
- # Fetches the size of the currently stored/cached file
301
- #
302
- # === Returns
303
- #
304
- # [Integer] size of the file
305
- #
306
- def size
307
- file.respond_to?(:size) ? file.size : 0
308
- end
309
-
310
- ##
311
- # Override this in your Uploader to change the filename.
312
- #
313
- # Be careful using record ids as filenames. If the filename is stored in the database
314
- # the record id will be nil when the filename is set. Don't use record ids unless you
315
- # understand this limitation.
316
- #
317
- # Do not use the version_name in the filename, as it will prevent versions from being
318
- # loaded correctly.
319
- #
320
- # === Returns
321
- #
322
- # [String] a filename
323
- #
324
- def filename
325
- @filename
326
- end
327
-
328
- ##
329
- # === Returns
330
- #
331
- # [String] the name of this version of the uploader
332
- #
333
- def version_name
334
- self.class.version_names.join('_').to_sym unless self.class.version_names.blank?
335
- end
336
-
337
- ##
338
- # === Returns
339
- #
340
- # [String] the directory that is the root of the application
341
- #
342
- def root
343
- CarrierWave.config[:root]
344
- end
345
-
346
- ##
347
- # === Returns
348
- #
349
- # [String] the directory where files will be publically accessible
350
- #
351
- def public
352
- CarrierWave.config[:public]
353
- end
354
-
355
- ##
356
- # Override this method in your uploader to provide a white list of extensions which
357
- # are allowed to be uploaded.
358
- #
359
- # === Returns
360
- #
361
- # [NilClass, Array[String]] a white list of extensions which are allowed to be uploaded
362
- #
363
- # === Examples
364
- #
365
- # def extension_white_list
366
- # %w(jpg jpeg gif png)
367
- # end
368
- #
369
- def extension_white_list; end
370
-
371
- ####################
372
- ## Cache
373
- ####################
374
-
375
- ##
376
- #
377
- def cached?
378
- @cache_id
379
- end
380
-
381
- ##
382
- # Override this in your Uploader to change the directory where files are cached.
383
- #
384
- # === Returns
385
- #
386
- # [String] a directory
387
- #
388
- def cache_dir
389
- CarrierWave.config[:cache_dir]
390
- end
391
-
392
- ##
393
- # Returns a String which uniquely identifies the currently cached file for later retrieval
394
- #
395
- # === Returns
396
- #
397
- # [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
398
- #
399
- def cache_name
400
- File.join(cache_id, [version_name, original_filename].compact.join('_')) if cache_id and original_filename
401
- end
402
-
403
- ##
404
- # Caches the given file unless a file has already been cached, stored or retrieved.
405
- #
406
- # === Parameters
407
- #
408
- # [new_file (File, IOString, Tempfile)] any kind of file object
409
- #
410
- # === Raises
411
- #
412
- # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
413
- #
414
- def cache(new_file)
415
- cache!(new_file) unless file
416
- end
417
-
418
- ##
419
- # Caches the given file. Calls process! to trigger any process callbacks.
420
- #
421
- # === Parameters
422
- #
423
- # [new_file (File, IOString, Tempfile)] any kind of file object
424
- #
425
- # === Raises
426
- #
427
- # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
428
- #
429
- def cache!(new_file)
430
- new_file = CarrierWave::SanitizedFile.new(new_file)
431
- raise CarrierWave::FormNotMultipart if new_file.is_path?
432
-
433
- unless new_file.empty?
434
- if extension_white_list and not extension_white_list.include?(new_file.extension.to_s)
435
- raise CarrierWave::IntegrityError, "You are not allowed to upload #{new_file.extension.inspect} files, allowed types: #{extension_white_list.inspect}"
436
- end
437
-
438
- self.cache_id = CarrierWave::Uploader.generate_cache_id unless cache_id
439
-
440
- @file = new_file
441
-
442
- @filename = new_file.filename
443
- self.original_filename = new_file.filename
444
-
445
- if CarrierWave.config[:cache_to_cache_dir]
446
- @file = @file.copy_to(cache_path, CarrierWave.config[:permissions])
447
- end
448
-
449
- process!
450
-
451
- versions.each do |name, v|
452
- v.send(:cache_id=, cache_id)
453
- v.cache!(new_file)
454
- end
455
- end
456
- end
457
-
458
- ##
459
- # Retrieves the file with the given cache_name from the cache, unless a file has
460
- # already been cached, stored or retrieved.
461
- #
462
- # === Parameters
463
- #
464
- # [cache_name (String)] uniquely identifies a cache file
465
- #
466
- def retrieve_from_cache(cache_name)
467
- retrieve_from_cache!(cache_name) unless file
468
- rescue CarrierWave::InvalidParameter
469
- end
470
-
471
- ##
472
- # Retrieves the file with the given cache_name from the cache.
473
- #
474
- # === Parameters
475
- #
476
- # [cache_name (String)] uniquely identifies a cache file
477
- #
478
- # === Raises
479
- #
480
- # [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
481
- #
482
- def retrieve_from_cache!(cache_name)
483
- self.cache_id, self.original_filename = cache_name.split('/', 2)
484
- @filename = original_filename
485
- @file = CarrierWave::SanitizedFile.new(cache_path)
486
- versions.each { |name, v| v.retrieve_from_cache!(cache_name) }
487
- end
488
-
489
- ####################
490
- ## STORE
491
- ####################
492
-
493
- ##
494
- # Override this in your Uploader to change the directory where the file backend stores files.
495
- #
496
- # Other backends may or may not use this method, depending on their specific needs.
497
- #
498
- # === Returns
499
- #
500
- # [String] a directory
501
- #
502
- def store_dir
503
- CarrierWave.config[:store_dir]
504
- end
505
-
506
- ##
507
- # Calculates the path where the file should be stored. If +for_file+ is given, it will be
508
- # used as the filename, otherwise +CarrierWave::Uploader#filename+ is assumed.
509
- #
510
- # === Parameters
511
- #
512
- # [for_file (String)] name of the file <optional>
513
- #
514
- # === Returns
515
- #
516
- # [String] the store path
517
- #
518
- def store_path(for_file=filename)
519
- File.join(store_dir, [version_name, for_file].compact.join('_'))
520
- end
521
-
522
- ##
523
- # Stores the file by passing it to this Uploader's storage engine, unless a file has
524
- # already been cached, stored or retrieved.
525
- #
526
- # If CarrierWave.config[:use_cache] is true, it will first cache the file
527
- # and apply any process callbacks before uploading it.
528
- #
529
- # === Parameters
530
- #
531
- # [new_file (File, IOString, Tempfile)] any kind of file object
532
- #
533
- def store(new_file)
534
- store!(new_file) unless file
535
- end
536
-
537
- ##
538
- # Stores the file by passing it to this Uploader's storage engine.
539
- #
540
- # If new_file is omitted, a previously cached file will be stored.
541
- #
542
- # === Parameters
543
- #
544
- # [new_file (File, IOString, Tempfile)] any kind of file object
545
- #
546
- def store!(new_file=nil)
547
- cache!(new_file) if new_file
548
- if @file and @cache_id
549
- @file = storage.store!(self, @file)
550
- @cache_id = nil
551
- versions.each { |name, v| v.store!(new_file) }
552
- end
553
- end
554
-
555
- ##
556
- # Retrieves the file from the storage, unless a file has
557
- # already been cached, stored or retrieved.
558
- #
559
- # === Parameters
560
- #
561
- # [identifier (String)] uniquely identifies the file to retrieve
562
- #
563
- def retrieve_from_store(identifier)
564
- retrieve_from_store!(identifier) unless file
565
- rescue CarrierWave::InvalidParameter
566
- end
567
-
568
- ##
569
- # Retrieves the file from the storage.
570
- #
571
- # === Parameters
572
- #
573
- # [identifier (String)] uniquely identifies the file to retrieve
574
- #
575
- def retrieve_from_store!(identifier)
576
- @file = storage.retrieve!(self, identifier)
577
- versions.each { |name, v| v.retrieve_from_store!(identifier) }
578
- end
579
-
580
- private
581
-
582
- def cache_path
583
- File.expand_path(File.join(cache_dir, cache_name), public)
584
- end
585
-
586
- def storage
587
- self.class.storage
588
- end
589
-
590
- attr_reader :cache_id, :original_filename
591
-
592
- def cache_id=(cache_id)
593
- raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
594
- @cache_id = cache_id
595
- end
596
-
597
- def original_filename=(filename)
598
- raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~ /\A[a-z0-9\.\-\+_]+\z/i
599
- @original_filename = filename
600
- end
9
+ # An uploader is a class that allows you to easily handle the caching and storage of
10
+ # uploaded files. Please refer to the README for configuration options.
11
+ #
12
+ # Once you have an uploader you can use it in isolation:
13
+ #
14
+ # my_uploader = MyUploader.new
15
+ # my_uploader.cache!(File.open(path_to_file))
16
+ # my_uploader.retrieve_from_store!('monkey.png')
17
+ #
18
+ # Alternatively, you can mount it on an ORM or other persistence layer, with
19
+ # +CarrierWave::Mount#mount_uploader+. There are extensions for activerecord and datamapper
20
+ # these are *very* simple (they are only a dozen lines of code), so adding your own should
21
+ # be trivial.
22
+ #
23
+ class Base
24
+ attr_reader :file
25
+
26
+ use CarrierWave::Uploader::Paths
27
+ use CarrierWave::Uploader::Callbacks
28
+ use CarrierWave::Uploader::Proxy
29
+ use CarrierWave::Uploader::Url
30
+ use CarrierWave::Uploader::Mountable
31
+ use CarrierWave::Uploader::Cache
32
+ use CarrierWave::Uploader::Store
33
+ use CarrierWave::Uploader::Remove
34
+ use CarrierWave::Uploader::ExtensionWhitelist
35
+ use CarrierWave::Uploader::DefaultPath
36
+ use CarrierWave::Uploader::Processing
37
+ use CarrierWave::Uploader::Versions
38
+ end # Base
601
39
 
602
40
  end # Uploader
603
41
  end # CarrierWave