jnicklas-carrierwave 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,420 @@
1
+ module CarrierWave
2
+ class Uploader
3
+
4
+ class << self
5
+
6
+ ##
7
+ # Returns a list of processor callbacks which have been declared for this uploader
8
+ #
9
+ # @return [String]
10
+ #
11
+ def processors
12
+ @processors ||= []
13
+ end
14
+
15
+ ##
16
+ # Adds a processor callback which applies operations as a file is uploaded.
17
+ # The argument may be the name of any method of the uploader, expressed as a symbol,
18
+ # or a list of such methods, or a hash where the key is a method and the value is
19
+ # an array of arguments to call the method with
20
+ #
21
+ # @param [*Symbol, Hash{Symbol => Array[]}] args
22
+ # @example
23
+ # class MyUploader < CarrierWave::Uploader
24
+ # process :sepiatone, :vignette
25
+ # process :scale => [200, 200]
26
+ #
27
+ # def sepiatone
28
+ # ...
29
+ # end
30
+ #
31
+ # def vignette
32
+ # ...
33
+ # end
34
+ #
35
+ # def scale(height, width)
36
+ # ...
37
+ # end
38
+ # end
39
+ #
40
+ def process(*args)
41
+ args.each do |arg|
42
+ if arg.is_a?(Hash)
43
+ arg.each do |method, args|
44
+ processors.push([method, args])
45
+ end
46
+ else
47
+ processors.push([arg, []])
48
+ end
49
+ end
50
+ end
51
+
52
+ ##
53
+ # Sets the storage engine to be used when storing files with this uploader.
54
+ # Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
55
+ # method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
56
+ # be added to CarrierWave.config[:storage_engines] so they can be referred
57
+ # to by a symbol, which should be more convenient
58
+ #
59
+ # If no argument is given, it will simply return the currently used storage engine.
60
+ #
61
+ # @param [Symbol, Class] storage The storage engine to use for this uploader
62
+ # @return [Class] the storage engine to be used with this uploader
63
+ # @example
64
+ # storage :file
65
+ # storage CarrierWave::Storage::File
66
+ # storage MyCustomStorageEngine
67
+ #
68
+ def storage(storage = nil)
69
+ if storage.is_a?(Symbol)
70
+ @storage = get_storage_by_symbol(storage)
71
+ @storage.setup!
72
+ elsif storage
73
+ @storage = storage
74
+ @storage.setup!
75
+ elsif @storage.nil?
76
+ # Get the storage from the superclass if there is one
77
+ @storage = superclass.storage rescue nil
78
+ end
79
+ if @storage.nil?
80
+ # If we were not able to find a store any other way, setup the default store
81
+ @storage ||= get_storage_by_symbol(CarrierWave.config[:storage])
82
+ @storage.setup!
83
+ end
84
+ return @storage
85
+ end
86
+
87
+ alias_method :storage=, :storage
88
+
89
+ attr_accessor :version_name
90
+
91
+ ##
92
+ # Adds a new version to this uploader
93
+ #
94
+ # @param [#to_sym] name name of the version
95
+ # @param [Proc] &block a block to eval on this version of the uploader
96
+ #
97
+ def version(name, &block)
98
+ name = name.to_sym
99
+ klass = Class.new(self)
100
+ klass.version_name = name
101
+ klass.class_eval(&block) if block
102
+ versions[name] = klass
103
+ class_eval <<-RUBY
104
+ def #{name}
105
+ versions[:#{name}]
106
+ end
107
+ RUBY
108
+ end
109
+
110
+ ##
111
+ # @return [Hash{Symbol => Class}] a list of versions available for this uploader
112
+ #
113
+ def versions
114
+ @versions ||= {}
115
+ end
116
+
117
+ ##
118
+ # Generates a unique cache id for use in the caching system
119
+ #
120
+ # @return [String] a cache if in the format YYYYMMDD-HHMM-PID-RND
121
+ #
122
+ def generate_cache_id
123
+ Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
124
+ end
125
+
126
+ private
127
+
128
+ def get_storage_by_symbol(symbol)
129
+ CarrierWave.config[:storage_engines][symbol]
130
+ end
131
+
132
+ end # class << self
133
+
134
+ attr_reader :file, :model, :mounted_as
135
+
136
+ ##
137
+ # If a model is given as the first parameter, it will stored in the uploader, and
138
+ # available throught +#model+. Likewise, mounted_as stores the name of the column
139
+ # where this instance of the uploader is mounted. These values can then be used inside
140
+ # your uploader.
141
+ #
142
+ # If you do not wish to mount your uploaders with the ORM extensions in -more then you
143
+ # can override this method inside your uploader.
144
+ #
145
+ # @param [Object] model Any kind of model object
146
+ # @param [Symbol] mounted_as The name of the column where this uploader is mounted
147
+ # @example
148
+ # class MyUploader < CarrierWave::Uploader
149
+ # def store_dir
150
+ # File.join('public', 'files', mounted_as, model.permalink)
151
+ # end
152
+ # end
153
+ #
154
+ def initialize(model=nil, mounted_as=nil)
155
+ @model = model
156
+ @mounted_as = mounted_as
157
+ end
158
+
159
+ ##
160
+ # Apply all process callbacks added through CarrierWaveer.process
161
+ #
162
+ def process!
163
+ self.class.processors.each do |method, args|
164
+ self.send(method, *args)
165
+ end
166
+ end
167
+
168
+ ##
169
+ # @return [String] the path where the file is currently located.
170
+ #
171
+ def current_path
172
+ file.path if file.respond_to?(:path)
173
+ end
174
+
175
+ ##
176
+ # Returns a hash mapping the name of each version of the uploader to an instance of it
177
+ #
178
+ # @return [Hash{Symbol => CarrierWave::Uploader}] a list of uploader instances
179
+ #
180
+ def versions
181
+ return @versions if @versions
182
+ @versions = {}
183
+ self.class.versions.each do |name, klass|
184
+ @versions[name] = klass.new(model, mounted_as)
185
+ end
186
+ @versions
187
+ end
188
+
189
+ ##
190
+ # @return [String] the location where this file is accessible via a url
191
+ #
192
+ def url
193
+ if file.respond_to?(:url) and not file.url.blank?
194
+ file.url
195
+ elsif current_path
196
+ File.expand_path(current_path).gsub(File.expand_path(CarrierWave.config[:public]), '')
197
+ end
198
+ end
199
+
200
+ alias_method :to_s, :url
201
+
202
+ ##
203
+ # Returns a string that uniquely identifies the last stored file
204
+ #
205
+ # @return [String] uniquely identifies a file
206
+ #
207
+ def identifier
208
+ file.identifier if file.respond_to?(:identifier)
209
+ end
210
+
211
+ ##
212
+ # Override this in your Uploader to change the filename.
213
+ #
214
+ # Be careful using record ids as filenames. If the filename is stored in the database
215
+ # the record id will be nil when the filename is set. Don't use record ids unless you
216
+ # understand this limitation.
217
+ #
218
+ # Do not use the version_name in the filename, as it will prevent versions from being
219
+ # loaded correctly.
220
+ #
221
+ # @return [String] a filename
222
+ #
223
+ def filename
224
+ @filename
225
+ end
226
+
227
+ ##
228
+ # @return [String] the name of this version of the uploader
229
+ #
230
+ def version_name
231
+ self.class.version_name
232
+ end
233
+
234
+ ##
235
+ # @return [String] the directory relative to which we will upload
236
+ #
237
+ def root
238
+ CarrierWave.config[:root]
239
+ end
240
+
241
+ ####################
242
+ ## Cache
243
+ ####################
244
+
245
+ ##
246
+ # Override this in your Uploader to change the directory where files are cached.
247
+ #
248
+ # @return [String] a directory
249
+ #
250
+ def cache_dir
251
+ CarrierWave.config[:cache_dir]
252
+ end
253
+
254
+ ##
255
+ # Returns a String which uniquely identifies the currently cached file for later retrieval
256
+ #
257
+ # @return [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
258
+ #
259
+ def cache_name
260
+ File.join(cache_id, [version_name, original_filename].compact.join('_')) if cache_id and original_filename
261
+ end
262
+
263
+ ##
264
+ # Caches the given file unless a file has already been cached, stored or retrieved.
265
+ #
266
+ # @param [File, IOString, Tempfile] new_file any kind of file object
267
+ # @raise [CarrierWave::FormNotMultipart] if the assigned parameter is a string
268
+ #
269
+ def cache(new_file)
270
+ cache!(new_file) unless file
271
+ end
272
+
273
+ ##
274
+ # Caches the given file. Calls process! to trigger any process callbacks.
275
+ #
276
+ # @param [File, IOString, Tempfile] new_file any kind of file object
277
+ # @raise [CarrierWave::FormNotMultipart] if the assigned parameter is a string
278
+ #
279
+ def cache!(new_file)
280
+ self.cache_id = CarrierWave::Uploader.generate_cache_id unless cache_id
281
+ new_file = CarrierWave::SanitizedFile.new(new_file)
282
+ raise CarrierWave::FormNotMultipart, "check that your upload form is multipart encoded" if new_file.string?
283
+
284
+ @file = new_file
285
+
286
+ @filename = new_file.filename
287
+ self.original_filename = new_file.filename
288
+
289
+ @file = @file.copy_to(cache_path)
290
+ process!
291
+
292
+ versions.each do |name, v|
293
+ v.send(:cache_id=, cache_id)
294
+ v.cache!(new_file)
295
+ end
296
+ end
297
+
298
+ ##
299
+ # Retrieves the file with the given cache_name from the cache, unless a file has
300
+ # already been cached, stored or retrieved.
301
+ #
302
+ # @param [String] cache_name uniquely identifies a cache file
303
+ #
304
+ def retrieve_from_cache(cache_name)
305
+ retrieve_from_cache!(cache_name) unless file
306
+ rescue CarrierWave::InvalidParameter
307
+ end
308
+
309
+ ##
310
+ # Retrieves the file with the given cache_name from the cache.
311
+ #
312
+ # @param [String] cache_name uniquely identifies a cache file
313
+ # @raise [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
314
+ #
315
+ def retrieve_from_cache!(cache_name)
316
+ self.cache_id, self.original_filename = cache_name.split('/', 2)
317
+ @filename = original_filename
318
+ @file = CarrierWave::SanitizedFile.new(cache_path)
319
+ versions.each { |name, v| v.retrieve_from_cache!(cache_name) }
320
+ end
321
+
322
+ ####################
323
+ ## STORE
324
+ ####################
325
+
326
+ ##
327
+ # Override this in your Uploader to change the directory where the file backend stores files.
328
+ #
329
+ # Other backends may or may not use this method, depending on their specific needs.
330
+ #
331
+ # @return [String] a directory
332
+ #
333
+ def store_dir
334
+ [CarrierWave.config[:store_dir], version_name].compact.join(File::Separator)
335
+ end
336
+
337
+ ##
338
+ # Stores the file by passing it to this Uploader's storage engine, unless a file has
339
+ # already been cached, stored or retrieved.
340
+ #
341
+ # If CarrierWave.config[:use_cache] is true, it will first cache the file
342
+ # and apply any process callbacks before uploading it.
343
+ #
344
+ # @param [File, IOString, Tempfile] new_file any kind of file object
345
+ #
346
+ def store(new_file)
347
+ store!(new_file) unless file
348
+ end
349
+
350
+ ##
351
+ # Stores the file by passing it to this Uploader's storage engine.
352
+ #
353
+ # If new_file is omitted, a previously cached file will be stored.
354
+ #
355
+ # If CarrierWave.config[:use_cache] is true, it will first cache the file
356
+ # and apply any process callbacks before uploading it.
357
+ #
358
+ # @param [File, IOString, Tempfile] new_file any kind of file object
359
+ #
360
+ def store!(new_file=nil)
361
+ if CarrierWave.config[:use_cache]
362
+ cache!(new_file) if new_file
363
+ @file = storage.store!(self, @file)
364
+ @cache_id = nil
365
+ else
366
+ new_file = CarrierWave::SanitizedFile.new(new_file)
367
+
368
+ @filename = new_file.filename
369
+ self.original_filename = filename
370
+
371
+ @file = storage.store!(self, new_file)
372
+ end
373
+ versions.each { |name, v| v.store!(new_file) }
374
+ end
375
+
376
+ ##
377
+ # Retrieves the file from the storage, unless a file has
378
+ # already been cached, stored or retrieved.
379
+ #
380
+ # @param [String] filename uniquely identifies the file to retrieve
381
+ #
382
+ def retrieve_from_store(filename)
383
+ retrieve_from_store!(filename) unless file
384
+ rescue CarrierWave::InvalidParameter
385
+ end
386
+
387
+ ##
388
+ # Retrieves the file from the storage.
389
+ #
390
+ # @param [String] identifier uniquely identifies the file to retrieve
391
+ #
392
+ def retrieve_from_store!(identifier)
393
+ @file = storage.retrieve!(self, identifier)
394
+ versions.each { |name, v| v.retrieve_from_store!(identifier) }
395
+ end
396
+
397
+ private
398
+
399
+ def cache_path
400
+ File.expand_path(File.join(cache_dir, cache_name), root)
401
+ end
402
+
403
+ def storage
404
+ self.class.storage
405
+ end
406
+
407
+ attr_reader :cache_id, :original_filename
408
+
409
+ def cache_id=(cache_id)
410
+ raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /^[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}$/
411
+ @cache_id = cache_id
412
+ end
413
+
414
+ def original_filename=(filename)
415
+ raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~ /^[a-z0-9\.\-\+_]+$/i
416
+ @original_filename = filename
417
+ end
418
+
419
+ end # Uploader
420
+ end # CarrierWave
@@ -0,0 +1,63 @@
1
+ require 'fileutils'
2
+
3
+ module CarrierWave
4
+ class << self
5
+ attr_accessor :config
6
+ end
7
+
8
+ class UploadError < StandardError; end
9
+ class NoFileError < UploadError; end
10
+ class FormNotMultipart < UploadError; end
11
+ class InvalidParameter < UploadError; end
12
+ # Should be used by methods used as process callbacks.
13
+ class ProcessingError < UploadError; end
14
+ end
15
+
16
+ dir = File.join(File.dirname(__FILE__), 'carrierwave')
17
+
18
+ require File.join(dir, 'sanitized_file')
19
+ require File.join(dir, 'uploader')
20
+ require File.join(dir, 'mount')
21
+ require File.join(dir, 'storage', 'abstract')
22
+ require File.join(dir, 'storage', 'file')
23
+ require File.join(dir, 'storage', 's3')
24
+
25
+ CarrierWave.config = {
26
+ :storage => :file,
27
+ :use_cache => true,
28
+ :storage_engines => {
29
+ :file => CarrierWave::Storage::File,
30
+ :s3 => CarrierWave::Storage::S3
31
+ },
32
+ :s3 => {
33
+ :access => :public_read
34
+ },
35
+ :store_dir => 'public/uploads',
36
+ :cache_dir => 'public/uploads/tmp'
37
+ }
38
+
39
+ if defined?(Merb)
40
+ CarrierWave.config[:root] = Merb.root
41
+ CarrierWave.config[:public] = Merb.dir_for(:public)
42
+
43
+ orm_path = File.dirname(__FILE__) / 'carrierwave' / 'orm' / Merb.orm
44
+ require orm_path if File.exist?(orm_path + '.rb')
45
+
46
+ Merb.push_path(:model, Merb.root / "app" / "uploaders")
47
+
48
+ Merb.add_generators File.dirname(__FILE__) / 'generators' / 'uploader_generator'
49
+ end
50
+
51
+ if defined?(Rails)
52
+ CarrierWave.config[:root] = Rails.root
53
+ CarrierWave.config[:public] = File.join(Rails.root, 'public')
54
+
55
+ require File.join(File.dirname(__FILE__), "carrierwave", "orm", 'activerecord')
56
+
57
+ ActiveSupport::Dependencies.load_paths << File.join(Rails.root, "app", "uploaders")
58
+ end
59
+
60
+ if defined?(Sinatra)
61
+ CarrierWave.config[:root] = Sinatra::Application.root
62
+ CarrierWave.config[:public] = Sinatra::Application.public
63
+ end
@@ -0,0 +1,32 @@
1
+ class <%= class_name %>Uploader < CarrierWave::Uploader
2
+
3
+ # Include RMagick or ImageScience support
4
+ # include CarrierWave::RMagick
5
+ # include CarrierWave::ImageScience
6
+
7
+ # Choose what kind of storage to use for this uploader
8
+ storage :file
9
+
10
+ # Process files as they are uploaded.
11
+ # process :scale => [200, 300]
12
+ #
13
+ # def scale(width, height)
14
+ # # do something
15
+ # end
16
+
17
+ # Create different verions of your uploaded files
18
+ # version :thumb do
19
+ # process :scale => [50, 50]
20
+ # end
21
+
22
+ # Override the filename of the uploaded files
23
+ # def filename
24
+ # "something"
25
+ # end
26
+
27
+ # Override the directory where uploaded files will be stored
28
+ # def store_dir
29
+ # "something"
30
+ # end
31
+
32
+ end
@@ -0,0 +1,20 @@
1
+ module Merb
2
+ module Generators
3
+ class UploaderGenerator < NamedGenerator
4
+
5
+ def self.source_root
6
+ File.join(File.dirname(__FILE__), 'templates')
7
+ end
8
+
9
+ first_argument :name, :required => true, :desc => "The name of this uploader"
10
+
11
+ template :uploader do |t|
12
+ t.source = 'uploader.rbt'
13
+ t.destination = "app/uploaders/#{file_name}_uploader.rb"
14
+ end
15
+ end
16
+
17
+ add :uploader, UploaderGenerator
18
+
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ bork bork bork
@@ -0,0 +1 @@
1
+ this is stuff
@@ -0,0 +1 @@
1
+ this is stuff