carrierwave 0.2.1 → 0.2.3

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 (47) hide show
  1. data/README.rdoc +35 -20
  2. data/Rakefile +1 -1
  3. data/lib/carrierwave.rb +55 -7
  4. data/lib/carrierwave/compatibility/paperclip.rb +91 -0
  5. data/lib/carrierwave/core_ext/inheritable_attributes.rb +102 -0
  6. data/lib/carrierwave/core_ext/module_setup.rb +49 -0
  7. data/lib/carrierwave/mount.rb +119 -103
  8. data/lib/carrierwave/orm/activerecord.rb +6 -1
  9. data/lib/carrierwave/orm/sequel.rb +15 -2
  10. data/lib/carrierwave/processing/rmagick.rb +8 -7
  11. data/lib/carrierwave/storage/abstract.rb +16 -1
  12. data/lib/carrierwave/storage/file.rb +20 -1
  13. data/lib/carrierwave/uploader.rb +31 -593
  14. data/lib/carrierwave/uploader/cache.rb +114 -0
  15. data/lib/carrierwave/uploader/callbacks.rb +40 -0
  16. data/lib/carrierwave/uploader/default_path.rb +21 -0
  17. data/lib/carrierwave/uploader/extension_whitelist.rb +35 -0
  18. data/lib/carrierwave/uploader/mountable.rb +37 -0
  19. data/lib/carrierwave/uploader/paths.rb +25 -0
  20. data/lib/carrierwave/uploader/processing.rb +79 -0
  21. data/lib/carrierwave/uploader/proxy.rb +60 -0
  22. data/lib/carrierwave/uploader/remove.rb +21 -0
  23. data/lib/carrierwave/uploader/store.rb +154 -0
  24. data/lib/carrierwave/uploader/url.rb +22 -0
  25. data/lib/carrierwave/uploader/versions.rb +145 -0
  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/test.log +1717 -0
  35. data/spec/uploader/cache_spec.rb +194 -0
  36. data/spec/uploader/default_path_spec.rb +66 -0
  37. data/spec/uploader/extension_whitelist_spec.rb +42 -0
  38. data/spec/uploader/mountable_spec.rb +31 -0
  39. data/spec/uploader/paths_spec.rb +20 -0
  40. data/spec/uploader/processing_spec.rb +60 -0
  41. data/spec/uploader/proxy_spec.rb +52 -0
  42. data/spec/uploader/remove_spec.rb +65 -0
  43. data/spec/uploader/store_spec.rb +260 -0
  44. data/spec/uploader/url_spec.rb +85 -0
  45. data/spec/uploader/versions_spec.rb +275 -0
  46. metadata +30 -3
  47. data/spec/uploader_spec.rb +0 -887
@@ -0,0 +1,114 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Cache
4
+
5
+ depends_on CarrierWave::Uploader::Paths
6
+ depends_on CarrierWave::Uploader::Callbacks
7
+
8
+ ##
9
+ # Returns true if the uploader has been cached
10
+ #
11
+ # === Returns
12
+ #
13
+ # [Bool] whether the current file is cached
14
+ #
15
+ def cached?
16
+ @cache_id
17
+ end
18
+
19
+ ##
20
+ # Override this in your Uploader to change the directory where files are cached.
21
+ #
22
+ # === Returns
23
+ #
24
+ # [String] a directory
25
+ #
26
+ def cache_dir
27
+ CarrierWave.config[:cache_dir]
28
+ end
29
+
30
+ ##
31
+ # Returns a String which uniquely identifies the currently cached file for later retrieval
32
+ #
33
+ # === Returns
34
+ #
35
+ # [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
36
+ #
37
+ def cache_name
38
+ File.join(cache_id, full_original_filename) if cache_id and original_filename
39
+ end
40
+
41
+ ##
42
+ # Caches the given file. Calls process! to trigger any process callbacks.
43
+ #
44
+ # === Parameters
45
+ #
46
+ # [new_file (File, IOString, Tempfile)] any kind of file object
47
+ #
48
+ # === Raises
49
+ #
50
+ # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
51
+ #
52
+ def cache!(new_file)
53
+ new_file = CarrierWave::SanitizedFile.new(new_file)
54
+ raise CarrierWave::FormNotMultipart if new_file.is_path?
55
+
56
+ unless new_file.empty?
57
+ with_callbacks(:cache, new_file) do
58
+ self.cache_id = CarrierWave.generate_cache_id unless cache_id
59
+
60
+ @filename = new_file.filename
61
+ self.original_filename = new_file.filename
62
+
63
+ if CarrierWave.config[:cache_to_cache_dir]
64
+ @file = new_file.copy_to(cache_path, CarrierWave.config[:permissions])
65
+ else
66
+ @file = new_file
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Retrieves the file with the given cache_name from the cache.
74
+ #
75
+ # === Parameters
76
+ #
77
+ # [cache_name (String)] uniquely identifies a cache file
78
+ #
79
+ # === Raises
80
+ #
81
+ # [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
82
+ #
83
+ def retrieve_from_cache!(cache_name)
84
+ with_callbacks(:retrieve_from_cache, cache_name) do
85
+ self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
86
+ @filename = original_filename
87
+ @file = CarrierWave::SanitizedFile.new(cache_path)
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def cache_path
94
+ File.expand_path(File.join(cache_dir, cache_name), public)
95
+ end
96
+
97
+ attr_reader :cache_id, :original_filename
98
+
99
+ # We can override the full_original_filename method in other modules
100
+ alias_method :full_original_filename, :original_filename
101
+
102
+ def cache_id=(cache_id)
103
+ raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
104
+ @cache_id = cache_id
105
+ end
106
+
107
+ def original_filename=(filename)
108
+ raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~ /\A[a-z0-9\.\-\+_]+\z/i
109
+ @original_filename = filename
110
+ end
111
+
112
+ end # Cache
113
+ end # Uploader
114
+ end # CarrierWave
@@ -0,0 +1,40 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Callbacks
4
+
5
+ setup do
6
+ extlib_inheritable_accessor :_before_callbacks, :_after_callbacks
7
+ end
8
+
9
+ def with_callbacks(kind, *args)
10
+ self.class._before_callback(kind).each { |callback| self.send(callback, *args) }
11
+ yield
12
+ self.class._after_callback(kind).each { |callback| self.send(callback, *args) }
13
+ end
14
+
15
+ module ClassMethods
16
+
17
+ def _before_callback(kind) #:nodoc:
18
+ self._before_callbacks ||= {}
19
+ self._before_callbacks[kind] ||= []
20
+ self._before_callbacks[kind]
21
+ end
22
+
23
+ def _after_callback(kind) #:nodoc:
24
+ self._after_callbacks ||= {}
25
+ self._after_callbacks[kind] ||= []
26
+ self._after_callbacks[kind]
27
+ end
28
+
29
+ def before(kind, callback)
30
+ _before_callback(kind) << callback
31
+ end
32
+
33
+ def after(kind, callback)
34
+ _after_callback(kind) << callback
35
+ end
36
+ end # ClassMethods
37
+
38
+ end # Url
39
+ end # Uploader
40
+ end # CarrierWave
@@ -0,0 +1,21 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module DefaultPath
4
+
5
+ def initialize(*args)
6
+ super
7
+ if default_path
8
+ @file = CarrierWave::SanitizedFile.new(File.expand_path(default_path, public))
9
+ def @file.blank?; true; end
10
+ end
11
+ end
12
+
13
+ ##
14
+ # Override this method in your uploader to provide a default path
15
+ # in case no file has been cached/stored yet.
16
+ #
17
+ def default_path; end
18
+
19
+ end # DefaultPath
20
+ end # Uploader
21
+ end # CarrierWave
@@ -0,0 +1,35 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module ExtensionWhitelist
4
+
5
+ setup do
6
+ before :cache, :check_whitelist!
7
+ end
8
+
9
+ ##
10
+ # Override this method in your uploader to provide a white list of extensions which
11
+ # are allowed to be uploaded.
12
+ #
13
+ # === Returns
14
+ #
15
+ # [NilClass, Array[String]] a white list of extensions which are allowed to be uploaded
16
+ #
17
+ # === Examples
18
+ #
19
+ # def extension_white_list
20
+ # %w(jpg jpeg gif png)
21
+ # end
22
+ #
23
+ def extension_white_list; end
24
+
25
+ private
26
+
27
+ def check_whitelist!(new_file)
28
+ if extension_white_list and not extension_white_list.include?(new_file.extension.to_s)
29
+ raise CarrierWave::IntegrityError, "You are not allowed to upload #{new_file.extension.inspect} files, allowed types: #{extension_white_list.inspect}"
30
+ end
31
+ end
32
+
33
+ end # ExtensionWhitelist
34
+ end # Uploader
35
+ end # CarrierWave
@@ -0,0 +1,37 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Mountable
4
+
5
+ attr_reader :model, :mounted_as
6
+
7
+ ##
8
+ # If a model is given as the first parameter, it will stored in the uploader, and
9
+ # available throught +#model+. Likewise, mounted_as stores the name of the column
10
+ # where this instance of the uploader is mounted. These values can then be used inside
11
+ # your uploader.
12
+ #
13
+ # If you do not wish to mount your uploaders with the ORM extensions in -more then you
14
+ # can override this method inside your uploader. Just be sure to call +super+
15
+ #
16
+ # === Parameters
17
+ #
18
+ # [model (Object)] Any kind of model object
19
+ # [mounted_as (Symbol)] The name of the column where this uploader is mounted
20
+ #
21
+ # === Examples
22
+ #
23
+ # class MyUploader < CarrierWave::Uploader::Base
24
+ #
25
+ # def store_dir
26
+ # File.join('public', 'files', mounted_as, model.permalink)
27
+ # end
28
+ # end
29
+ #
30
+ def initialize(model=nil, mounted_as=nil)
31
+ @model = model
32
+ @mounted_as = mounted_as
33
+ end
34
+
35
+ end # Mountable
36
+ end # Uploader
37
+ end # CarrierWave
@@ -0,0 +1,25 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Paths
4
+
5
+ ##
6
+ # === Returns
7
+ #
8
+ # [String] the directory that is the root of the application
9
+ #
10
+ def root
11
+ CarrierWave.config[:root]
12
+ end
13
+
14
+ ##
15
+ # === Returns
16
+ #
17
+ # [String] the directory where files will be publically accessible
18
+ #
19
+ def public
20
+ CarrierWave.config[:public]
21
+ end
22
+
23
+ end # Paths
24
+ end # Uploader
25
+ end # CarrierWave
@@ -0,0 +1,79 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Processing
4
+
5
+ depends_on CarrierWave::Uploader::Callbacks
6
+
7
+ setup do
8
+ after :cache, :process!
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ ##
14
+ # Lists processor callbacks declared
15
+ #
16
+ # === Returns
17
+ #
18
+ # [Array[Array[Symbol, Array]]] a list of processor callbacks which have been declared for this uploader
19
+ #
20
+ def processors
21
+ @processors ||= []
22
+ end
23
+
24
+ ##
25
+ # Adds a processor callback which applies operations as a file is uploaded.
26
+ # The argument may be the name of any method of the uploader, expressed as a symbol,
27
+ # or a list of such methods, or a hash where the key is a method and the value is
28
+ # an array of arguments to call the method with
29
+ #
30
+ # === Parameters
31
+ #
32
+ # args (*Symbol, Hash{Symbol => Array[]})
33
+ #
34
+ # === Examples
35
+ #
36
+ # class MyUploader < CarrierWave::Uploader::Base
37
+ #
38
+ # process :sepiatone, :vignette
39
+ # process :scale => [200, 200]
40
+ #
41
+ # def sepiatone
42
+ # ...
43
+ # end
44
+ #
45
+ # def vignette
46
+ # ...
47
+ # end
48
+ #
49
+ # def scale(height, width)
50
+ # ...
51
+ # end
52
+ # end
53
+ #
54
+ def process(*args)
55
+ args.each do |arg|
56
+ if arg.is_a?(Hash)
57
+ arg.each do |method, args|
58
+ processors.push([method, args])
59
+ end
60
+ else
61
+ processors.push([arg, []])
62
+ end
63
+ end
64
+ end
65
+
66
+ end # ClassMethods
67
+
68
+ ##
69
+ # Apply all process callbacks added through CarrierWave.process
70
+ #
71
+ def process!(new_file=nil)
72
+ self.class.processors.each do |method, args|
73
+ self.send(method, *args)
74
+ end
75
+ end
76
+
77
+ end # Processing
78
+ end # Uploader
79
+ end # CarrierWave
@@ -0,0 +1,60 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Proxy
4
+
5
+ ##
6
+ # === Returns
7
+ #
8
+ # [Boolean] Whether the uploaded file is blank
9
+ #
10
+ def blank?
11
+ !file or file.blank?
12
+ end
13
+
14
+ ##
15
+ # === Returns
16
+ #
17
+ # [String] the path where the file is currently located.
18
+ #
19
+ def current_path
20
+ file.path if file.respond_to?(:path)
21
+ end
22
+
23
+ alias_method :path, :current_path
24
+
25
+ ##
26
+ # Returns a string that uniquely identifies the last stored file
27
+ #
28
+ # === Returns
29
+ #
30
+ # [String] uniquely identifies a file
31
+ #
32
+ def identifier
33
+ file.identifier if file.respond_to?(:identifier)
34
+ end
35
+
36
+ ##
37
+ # Read the contents of the file
38
+ #
39
+ # === Returns
40
+ #
41
+ # [String] contents of the file
42
+ #
43
+ def read
44
+ file.read if file.respond_to?(:read)
45
+ end
46
+
47
+ ##
48
+ # Fetches the size of the currently stored/cached file
49
+ #
50
+ # === Returns
51
+ #
52
+ # [Integer] size of the file
53
+ #
54
+ def size
55
+ file.respond_to?(:size) ? file.size : 0
56
+ end
57
+
58
+ end # Proxy
59
+ end # Uploader
60
+ end # CarrierWave
@@ -0,0 +1,21 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Remove
4
+
5
+ depends_on CarrierWave::Uploader::Callbacks
6
+
7
+ ##
8
+ # Removes the file and reset it
9
+ #
10
+ def remove!
11
+ with_callbacks(:remove) do
12
+ CarrierWave.logger.info 'CarrierWave: removing file'
13
+ storage.destroy!(self, file)
14
+ @file = nil
15
+ @cache_id = nil
16
+ end
17
+ end
18
+
19
+ end # Remove
20
+ end # Uploader
21
+ end # CarrierWave
@@ -0,0 +1,154 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ module Store
4
+
5
+ depends_on CarrierWave::Uploader::Paths
6
+ depends_on CarrierWave::Uploader::Callbacks
7
+ depends_on CarrierWave::Uploader::Cache
8
+
9
+ module ClassMethods
10
+
11
+ ##
12
+ # Sets the storage engine to be used when storing files with this uploader.
13
+ # Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
14
+ # method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
15
+ # be added to CarrierWave.config[:storage_engines] so they can be referred
16
+ # to by a symbol, which should be more convenient
17
+ #
18
+ # If no argument is given, it will simply return the currently used storage engine.
19
+ #
20
+ # === Parameters
21
+ #
22
+ # [storage (Symbol, Class)] The storage engine to use for this uploader
23
+ #
24
+ # === Returns
25
+ #
26
+ # [Class] the storage engine to be used with this uploader
27
+ #
28
+ # === Examples
29
+ #
30
+ # storage :file
31
+ # storage CarrierWave::Storage::File
32
+ # storage MyCustomStorageEngine
33
+ #
34
+ def storage(storage = nil)
35
+ if storage.is_a?(Symbol)
36
+ @storage = get_storage_by_symbol(storage)
37
+ @storage.setup!
38
+ elsif storage
39
+ @storage = storage
40
+ @storage.setup!
41
+ elsif @storage.nil?
42
+ # Get the storage from the superclass if there is one
43
+ @storage = superclass.storage rescue nil
44
+ end
45
+ if @storage.nil?
46
+ # If we were not able to find a store any other way, setup the default store
47
+ @storage ||= get_storage_by_symbol(CarrierWave.config[:storage])
48
+ @storage.setup!
49
+ end
50
+ return @storage
51
+ end
52
+
53
+ alias_method :storage=, :storage
54
+
55
+ private
56
+
57
+ def get_storage_by_symbol(symbol)
58
+ eval(CarrierWave.config[:storage_engines][symbol])
59
+ end
60
+
61
+ end
62
+
63
+ ##
64
+ # Override this in your Uploader to change the filename.
65
+ #
66
+ # Be careful using record ids as filenames. If the filename is stored in the database
67
+ # the record id will be nil when the filename is set. Don't use record ids unless you
68
+ # understand this limitation.
69
+ #
70
+ # Do not use the version_name in the filename, as it will prevent versions from being
71
+ # loaded correctly.
72
+ #
73
+ # === Returns
74
+ #
75
+ # [String] a filename
76
+ #
77
+ def filename
78
+ @filename
79
+ end
80
+
81
+ ##
82
+ # Override this in your Uploader to change the directory where the file backend stores files.
83
+ #
84
+ # Other backends may or may not use this method, depending on their specific needs.
85
+ #
86
+ # === Returns
87
+ #
88
+ # [String] a directory
89
+ #
90
+ def store_dir
91
+ CarrierWave.config[:store_dir]
92
+ end
93
+
94
+ ##
95
+ # Calculates the path where the file should be stored. If +for_file+ is given, it will be
96
+ # used as the filename, otherwise +CarrierWave::Uploader#filename+ is assumed.
97
+ #
98
+ # === Parameters
99
+ #
100
+ # [for_file (String)] name of the file <optional>
101
+ #
102
+ # === Returns
103
+ #
104
+ # [String] the store path
105
+ #
106
+ def store_path(for_file=filename)
107
+ File.join(store_dir, full_filename(for_file))
108
+ end
109
+
110
+ ##
111
+ # Stores the file by passing it to this Uploader's storage engine.
112
+ #
113
+ # If new_file is omitted, a previously cached file will be stored.
114
+ #
115
+ # === Parameters
116
+ #
117
+ # [new_file (File, IOString, Tempfile)] any kind of file object
118
+ #
119
+ def store!(new_file=nil)
120
+ cache!(new_file) if new_file
121
+ if @file and @cache_id
122
+ with_callbacks(:store, new_file) do
123
+ @file = storage.store!(self, @file)
124
+ @cache_id = nil
125
+ end
126
+ end
127
+ end
128
+
129
+ ##
130
+ # Retrieves the file from the storage.
131
+ #
132
+ # === Parameters
133
+ #
134
+ # [identifier (String)] uniquely identifies the file to retrieve
135
+ #
136
+ def retrieve_from_store!(identifier)
137
+ with_callbacks(:retrieve_from_store, identifier) do
138
+ @file = storage.retrieve!(self, identifier)
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ def full_filename(for_file)
145
+ for_file
146
+ end
147
+
148
+ def storage
149
+ self.class.storage
150
+ end
151
+
152
+ end # Store
153
+ end # Uploader
154
+ end # CarrierWave