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
@@ -70,6 +70,7 @@ module CarrierWave
70
70
  # [remove_image?] Whether the file should be removed when store_image! is called.
71
71
  #
72
72
  # [store_image!] Stores a file that has been assigned with +image=+
73
+ # [remove_image!] Removes the uploaded file from the filesystem.
73
74
  #
74
75
  # [image_integrity_error] Returns an error object if the last file to be assigned caused an integrty error
75
76
  # [image_processing_error] Returns an error object if the last file to be assigned caused a processing error
@@ -115,9 +116,7 @@ module CarrierWave
115
116
  #
116
117
  def mount_uploader(column, uploader=nil, options={}, &block)
117
118
  unless uploader
118
- uploader = Class.new do
119
- include CarrierWave::Uploader
120
- end
119
+ uploader = Class.new(CarrierWave::Uploader::Base)
121
120
  uploader.class_eval(&block)
122
121
  end
123
122
 
@@ -127,58 +126,68 @@ module CarrierWave
127
126
  include CarrierWave::Mount::Extension
128
127
 
129
128
  class_eval <<-RUBY, __FILE__, __LINE__+1
130
- def #{column}_uploader # def image_uploader
131
- _uploader_get(:#{column}) # _uploader_get(:image)
132
- end # end
133
- #
134
- def #{column}_uploader=(uploader) # def image_uploader=(uploader)
135
- _uploader_set(:#{column}, uploader) # _uploader_set(:image, uploader)
136
- end # end
137
- #
138
- def #{column}_url(*args) # def image_url(*args)
139
- _uploader_get_url(:#{column}, *args) # _uploader_get_url(:image, *args)
140
- end # end
141
- #
142
- def #{column} # def image
143
- _uploader_get_column(:#{column}) # _uploader_get_column(:image)
144
- end # end
145
- #
146
- def #{column}=(new_file) # def image=(new_file)
147
- _uploader_set_column(:#{column}, new_file) # _uploader_set_column(:image, new_file)
148
- end # end
149
- #
150
- def #{column}_cache # def image_cache
151
- _uploader_get_cache(:#{column}) # _uploader_get_cache(:image)
152
- end # end
153
- #
154
- def #{column}_cache=(cache_name) # def image_cache=(cache_name)
155
- _uploader_set_cache(:#{column}, cache_name) # _uploader_set_cache(:image, cache_name)
156
- end # end
157
- #
158
- def remove_#{column} # def remove_image
159
- _uploader_remove[:#{column}] # _uploader_remove[:image]
160
- end # end
161
- #
162
- def remove_#{column}=(value) # def remove_image=(value)
163
- _uploader_remove[:#{column}] = value # _uploader_remove[:image] = value
164
- end # end
165
- #
166
- def remove_#{column}? # def remove_image?
167
- _uploader_remove?(:#{column}) # _uploader_remove?(:image)
168
- end # end
169
- #
170
- def store_#{column}! # def store_image!
171
- _uploader_store!(:#{column}) # _uploader_store!(:image)
172
- end # end
173
- #
174
- def #{column}_integrity_error # def image_integrity_error
175
- _uploader_integrity_errors[:#{column}] # _uploader_integrity_errors[:image]
176
- end # end
177
- #
178
- def #{column}_processing_error # def image_processing_error
179
- _uploader_processing_errors[:#{column}] # _uploader_processing_errors[:image]
180
- end # end
129
+
130
+ def #{column}
131
+ _mounter(:#{column}).uploader
132
+ end
133
+
134
+ def #{column}=(new_file)
135
+ _mounter(:#{column}).cache(new_file)
136
+ end
137
+
138
+ def #{column}?
139
+ !_mounter(:#{column}).blank?
140
+ end
141
+
142
+ def #{column}_url(*args)
143
+ _mounter(:#{column}).url(*args)
144
+ end
145
+
146
+ def #{column}_uploader
147
+ _mounter(:#{column}).uploader
148
+ end
149
+
150
+ def #{column}_uploader=(uploader)
151
+ _mounter(:#{column}).uploader = uploader
152
+ end
153
+
154
+ def #{column}_cache
155
+ _mounter(:#{column}).cache_name
156
+ end
157
+
158
+ def #{column}_cache=(cache_name)
159
+ _mounter(:#{column}).cache_name = cache_name
160
+ end
161
+
162
+ def remove_#{column}
163
+ _mounter(:#{column}).remove
164
+ end
165
+
166
+ def remove_#{column}!
167
+ _mounter(:#{column}).remove!
168
+ end
169
+
170
+ def remove_#{column}=(value)
171
+ _mounter(:#{column}).remove = value
172
+ end
173
+
174
+ def remove_#{column}?
175
+ _mounter(:#{column}).remove?
176
+ end
177
+
178
+ def store_#{column}!
179
+ _mounter(:#{column}).store!
180
+ end
181
+
182
+ def #{column}_integrity_error
183
+ _mounter(:#{column}).integrity_error
184
+ end
185
+
186
+ def #{column}_processing_error
187
+ _mounter(:#{column}).processing_error
188
+ end
181
189
  RUBY
190
+
182
191
  end
183
192
 
184
193
  module Extension
@@ -195,85 +204,92 @@ module CarrierWave
195
204
 
196
205
  private
197
206
 
198
- def _uploader_get(column)
199
- @_uploaders ||= {}
200
- @_uploaders[column] ||= self.class.uploaders[column].new(self, column)
207
+ def _mounter(column)
208
+ @_mounters ||= {}
209
+ @_mounters[column] ||= Mounter.new(self, column)
201
210
  end
202
211
 
203
- def _uploader_set(column, uploader)
204
- @_uploaders ||= {}
205
- @_uploaders[column] = uploader
206
- end
212
+ end # Extension
207
213
 
208
- def _uploader_options(column)
209
- self.class.uploader_options[column]
210
- end
214
+ # this is an internal class, used by CarrierWave::Mount so that
215
+ # we don't pollute the model with a lot of methods.
216
+ class Mounter #:nodoc:
217
+
218
+ attr_reader :column, :record, :options
211
219
 
212
- def _uploader_get_column(column)
213
- return _uploader_get(column) unless _uploader_get(column).blank?
220
+ attr_accessor :uploader, :integrity_error, :processing_error, :remove
214
221
 
215
- identifier = read_uploader(_uploader_options(column)[:mount_on] || column)
222
+ def initialize(record, column, options={})
223
+ @record = record
224
+ @column = column
225
+ @options = record.class.uploader_options[column]
226
+ end
216
227
 
217
- unless identifier.blank?
218
- _uploader_get(column).retrieve_from_store!(identifier)
219
- _uploader_get(column)
228
+ def uploader
229
+ @uploader ||= record.class.uploaders[column].new(record, column)
230
+ if @uploader.blank?
231
+ identifier = record.read_uploader(serialization_column)
232
+ @uploader.retrieve_from_store!(identifier) unless identifier.blank?
220
233
  end
234
+ return @uploader
221
235
  end
222
236
 
223
- def _uploader_set_column(column, new_file)
224
- _uploader_get(column).cache!(new_file)
225
- _uploader_integrity_errors[column] = nil
226
- _uploader_processing_errors[column] = nil
237
+ def cache(new_file)
238
+ uploader.cache!(new_file)
239
+ self.integrity_error = nil
240
+ self.processing_error = nil
227
241
  rescue CarrierWave::IntegrityError => e
228
- _uploader_integrity_errors[column] = e
229
- raise e unless _uploader_options(column)[:ignore_integrity_errors]
242
+ self.integrity_error = e
243
+ raise e unless options[:ignore_integrity_errors]
230
244
  rescue CarrierWave::ProcessingError => e
231
- _uploader_processing_errors[column] = e
232
- raise e unless _uploader_options(column)[:ignore_processing_errors]
233
- end
234
-
235
- def _uploader_get_url(column, *args)
236
- _uploader_get_column(column)
237
- _uploader_get(column).url(*args)
245
+ self.processing_error = e
246
+ raise e unless options[:ignore_processing_errors]
238
247
  end
239
248
 
240
- def _uploader_get_cache(column)
241
- _uploader_get(column).cache_name
249
+ def cache_name
250
+ uploader.cache_name
242
251
  end
243
252
 
244
- def _uploader_set_cache(column, cache_name)
245
- _uploader_get(column).retrieve_from_cache(cache_name) unless cache_name.blank?
253
+ def cache_name=(cache_name)
254
+ uploader.retrieve_from_cache!(cache_name) unless uploader.cached?
255
+ rescue CarrierWave::InvalidParameter
246
256
  end
247
257
 
248
- def _uploader_store!(column)
249
- unless _uploader_get(column).blank?
250
- if _uploader_remove?(column)
251
- _uploader_set(column, nil)
252
- write_uploader(column, '')
258
+ def store!
259
+ unless uploader.blank?
260
+ if remove?
261
+ uploader.remove!
262
+ record.write_uploader(serialization_column, '')
253
263
  else
254
- _uploader_get(column).store!
255
- write_uploader(_uploader_options(column)[:mount_on] || column, _uploader_get(column).identifier)
264
+ uploader.store!
265
+ record.write_uploader(serialization_column, uploader.identifier)
256
266
  end
257
267
  end
258
268
  end
259
269
 
260
- def _uploader_remove
261
- @_uploader_remove ||= {}
270
+ def url(*args)
271
+ uploader.url(*args)
262
272
  end
263
273
 
264
- def _uploader_remove?(column)
265
- !_uploader_remove[column].blank? and _uploader_remove[column] !~ /\A0|false$\z/
274
+ def blank?
275
+ uploader.blank?
266
276
  end
267
277
 
268
- def _uploader_integrity_errors
269
- @_uploader_integrity_errors ||= {}
278
+ def remove?
279
+ !remove.blank? and remove !~ /\A0|false$\z/
270
280
  end
271
281
 
272
- def _uploader_processing_errors
273
- @_uploader_processing_errors ||= {}
282
+ def remove!
283
+ uploader.remove!
274
284
  end
275
285
 
276
- end # Extension
286
+ private
287
+
288
+ def serialization_column
289
+ options[:mount_on] || column
290
+ end
291
+
292
+ end # Mounter
277
293
 
278
294
  end # Mount
279
295
  end # CarrierWave
@@ -17,7 +17,12 @@ module CarrierWave
17
17
  validates_integrity_of column if uploader_options[column.to_sym][:validate_integrity]
18
18
  validates_processing_of column if uploader_options[column.to_sym][:validate_processing]
19
19
 
20
- before_save do |record|
20
+ after_create do |record|
21
+ record.send("store_#{column}!")
22
+ record.save
23
+ end
24
+
25
+ before_update do |record|
21
26
  record.send("store_#{column}!")
22
27
  end
23
28
  end
@@ -11,13 +11,26 @@ module CarrierWave
11
11
  alias_method :read_uploader, :[]
12
12
  alias_method :write_uploader, :[]=
13
13
 
14
- before_save do
14
+ after_create do
15
15
  send("store_#{column}!")
16
16
  end
17
+
18
+ before_destroy do
19
+ send("remove_#{column}!")
20
+ end
21
+ end
22
+
23
+ # Determine if we're using Sequel > 2.12
24
+ #
25
+ # ==== Returns
26
+ # Bool:: True if Sequel 2.12 or higher False otherwise
27
+ def self.new_sequel?
28
+ !!(/^(2.12|3)/ =~ ::Sequel.version)
17
29
  end
18
30
 
19
31
  end # Sequel
20
32
  end # CarrierWave
21
33
 
34
+ # Sequel 3.x.x removed class hook methods and moved them to the plugin
35
+ Sequel::Model.plugin(:hook_class_methods) if CarrierWave::Sequel.new_sequel?
22
36
  Sequel::Model.send(:extend, CarrierWave::Sequel)
23
-
@@ -1,4 +1,8 @@
1
- require 'rmagick'
1
+ begin
2
+ require 'rmagick'
3
+ rescue LoadError
4
+ require 'RMagick'
5
+ end
2
6
 
3
7
  module CarrierWave
4
8
 
@@ -11,15 +15,13 @@ module CarrierWave
11
15
  #
12
16
  # And then include it in your uploader:
13
17
  #
14
- # class MyUploader
15
- # include CarrierWave::Uploader
18
+ # class MyUploader < CarrierWave::Uploader::Base
16
19
  # include CarrierWave::RMagick
17
20
  # end
18
21
  #
19
22
  # You can now use the provided helpers:
20
23
  #
21
- # class MyUploader
22
- # include CarrierWave::Uploader
24
+ # class MyUploader < CarrierWave::Uploader::Base
23
25
  # include CarrierWave::RMagick
24
26
  #
25
27
  # process :resize_to_fit => [200, 200]
@@ -29,8 +31,7 @@ module CarrierWave
29
31
  # out the RMagick docs at http://www.imagemagick.org/RMagick/doc/ for more
30
32
  # info
31
33
  #
32
- # class MyUploader
33
- # include CarrierWave::Uploader
34
+ # class MyUploader < CarrierWave::Uploader::Base
34
35
  # include CarrierWave::RMagick
35
36
  #
36
37
  # process :do_stuff => 10.0
@@ -18,6 +18,21 @@ module CarrierWave
18
18
  # to retrieve it later.
19
19
  #
20
20
  class Abstract
21
+
22
+ # Do something to destroy the file
23
+ #
24
+ # === Parameters
25
+ #
26
+ # [uploader (CarrierWave::Uploader)] an uploader object
27
+ # [identifier (String)] uniquely identifies the file
28
+ #
29
+ # === Returns
30
+ #
31
+ # [bool] True if file was remove or false
32
+ #
33
+ def self.destroy!(uploader, identifier)
34
+ false
35
+ end
21
36
 
22
37
  ##
23
38
  # Do setup specific for this storage engine
@@ -92,4 +107,4 @@ module CarrierWave
92
107
 
93
108
  end # Abstract
94
109
  end # Storage
95
- end # CarrierWave
110
+ end # CarrierWave
@@ -12,6 +12,25 @@ module CarrierWave
12
12
  @uploader = uploader
13
13
  end
14
14
 
15
+ ##
16
+ # Delete the file to the uploader's store path.
17
+ #
18
+ # === Parameters
19
+ #
20
+ # [uploader (CarrierWave::Uploader)] an uploader object
21
+ # [file (CarrierWave::SanitizedFile)] the file to store
22
+ #
23
+ # === Returns
24
+ #
25
+ # [bool] True if file was removed or false
26
+ #
27
+ def self.destroy!(uploader, file)
28
+ unless file.blank?
29
+ CarrierWave.logger.info "CarrierWave::Storage::File: removing file #{file.path}"
30
+ file.delete
31
+ end
32
+ end
33
+
15
34
  ##
16
35
  # Move the file to the uploader's store path.
17
36
  #
@@ -51,4 +70,4 @@ module CarrierWave
51
70
 
52
71
  end # File
53
72
  end # Storage
54
- end # CarrierWave
73
+ end # CarrierWave
@@ -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