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.
- data/README.rdoc +35 -20
- data/Rakefile +1 -1
- data/lib/carrierwave.rb +55 -7
- data/lib/carrierwave/compatibility/paperclip.rb +91 -0
- data/lib/carrierwave/core_ext/inheritable_attributes.rb +102 -0
- data/lib/carrierwave/core_ext/module_setup.rb +49 -0
- data/lib/carrierwave/mount.rb +119 -103
- data/lib/carrierwave/orm/activerecord.rb +6 -1
- data/lib/carrierwave/orm/sequel.rb +15 -2
- data/lib/carrierwave/processing/rmagick.rb +8 -7
- data/lib/carrierwave/storage/abstract.rb +16 -1
- data/lib/carrierwave/storage/file.rb +20 -1
- data/lib/carrierwave/uploader.rb +31 -593
- data/lib/carrierwave/uploader/cache.rb +114 -0
- data/lib/carrierwave/uploader/callbacks.rb +40 -0
- data/lib/carrierwave/uploader/default_path.rb +21 -0
- data/lib/carrierwave/uploader/extension_whitelist.rb +35 -0
- data/lib/carrierwave/uploader/mountable.rb +37 -0
- data/lib/carrierwave/uploader/paths.rb +25 -0
- data/lib/carrierwave/uploader/processing.rb +79 -0
- data/lib/carrierwave/uploader/proxy.rb +60 -0
- data/lib/carrierwave/uploader/remove.rb +21 -0
- data/lib/carrierwave/uploader/store.rb +154 -0
- data/lib/carrierwave/uploader/url.rb +22 -0
- data/lib/carrierwave/uploader/versions.rb +145 -0
- data/lib/generators/uploader_generator.rb +1 -1
- data/rails_generators/uploader/templates/uploader.rb +12 -8
- data/spec/compatibility/paperclip_spec.rb +41 -0
- data/spec/mount_spec.rb +88 -25
- data/spec/orm/activerecord_spec.rb +7 -9
- data/spec/orm/datamapper_spec.rb +7 -9
- data/spec/orm/sequel_spec.rb +47 -32
- data/spec/spec_helper.rb +13 -0
- data/spec/test.log +1717 -0
- data/spec/uploader/cache_spec.rb +194 -0
- data/spec/uploader/default_path_spec.rb +66 -0
- data/spec/uploader/extension_whitelist_spec.rb +42 -0
- data/spec/uploader/mountable_spec.rb +31 -0
- data/spec/uploader/paths_spec.rb +20 -0
- data/spec/uploader/processing_spec.rb +60 -0
- data/spec/uploader/proxy_spec.rb +52 -0
- data/spec/uploader/remove_spec.rb +65 -0
- data/spec/uploader/store_spec.rb +260 -0
- data/spec/uploader/url_spec.rb +85 -0
- data/spec/uploader/versions_spec.rb +275 -0
- metadata +30 -3
- data/spec/uploader_spec.rb +0 -887
data/lib/carrierwave/mount.rb
CHANGED
@@ -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
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
199
|
-
@
|
200
|
-
@
|
207
|
+
def _mounter(column)
|
208
|
+
@_mounters ||= {}
|
209
|
+
@_mounters[column] ||= Mounter.new(self, column)
|
201
210
|
end
|
202
211
|
|
203
|
-
|
204
|
-
@_uploaders ||= {}
|
205
|
-
@_uploaders[column] = uploader
|
206
|
-
end
|
212
|
+
end # Extension
|
207
213
|
|
208
|
-
|
209
|
-
|
210
|
-
|
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
|
-
|
213
|
-
return _uploader_get(column) unless _uploader_get(column).blank?
|
220
|
+
attr_accessor :uploader, :integrity_error, :processing_error, :remove
|
214
221
|
|
215
|
-
|
222
|
+
def initialize(record, column, options={})
|
223
|
+
@record = record
|
224
|
+
@column = column
|
225
|
+
@options = record.class.uploader_options[column]
|
226
|
+
end
|
216
227
|
|
217
|
-
|
218
|
-
|
219
|
-
|
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
|
224
|
-
|
225
|
-
|
226
|
-
|
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
|
-
|
229
|
-
raise e unless
|
242
|
+
self.integrity_error = e
|
243
|
+
raise e unless options[:ignore_integrity_errors]
|
230
244
|
rescue CarrierWave::ProcessingError => e
|
231
|
-
|
232
|
-
raise e unless
|
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
|
241
|
-
|
249
|
+
def cache_name
|
250
|
+
uploader.cache_name
|
242
251
|
end
|
243
252
|
|
244
|
-
def
|
245
|
-
|
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
|
249
|
-
unless
|
250
|
-
if
|
251
|
-
|
252
|
-
write_uploader(
|
258
|
+
def store!
|
259
|
+
unless uploader.blank?
|
260
|
+
if remove?
|
261
|
+
uploader.remove!
|
262
|
+
record.write_uploader(serialization_column, '')
|
253
263
|
else
|
254
|
-
|
255
|
-
write_uploader(
|
264
|
+
uploader.store!
|
265
|
+
record.write_uploader(serialization_column, uploader.identifier)
|
256
266
|
end
|
257
267
|
end
|
258
268
|
end
|
259
269
|
|
260
|
-
def
|
261
|
-
|
270
|
+
def url(*args)
|
271
|
+
uploader.url(*args)
|
262
272
|
end
|
263
273
|
|
264
|
-
def
|
265
|
-
|
274
|
+
def blank?
|
275
|
+
uploader.blank?
|
266
276
|
end
|
267
277
|
|
268
|
-
def
|
269
|
-
|
278
|
+
def remove?
|
279
|
+
!remove.blank? and remove !~ /\A0|false$\z/
|
270
280
|
end
|
271
281
|
|
272
|
-
def
|
273
|
-
|
282
|
+
def remove!
|
283
|
+
uploader.remove!
|
274
284
|
end
|
275
285
|
|
276
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/carrierwave/uploader.rb
CHANGED
@@ -1,603 +1,41 @@
|
|
1
1
|
module CarrierWave
|
2
2
|
|
3
3
|
##
|
4
|
-
#
|
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
|
-
#
|
279
|
-
#
|
280
|
-
#
|
281
|
-
#
|
282
|
-
#
|
283
|
-
#
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
#
|
290
|
-
#
|
291
|
-
#
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
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
|