jnicklas-carrierwave 0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +43 -2
- data/Rakefile +2 -2
- data/lib/carrierwave.rb +9 -4
- data/lib/carrierwave/mount.rb +106 -48
- data/lib/carrierwave/orm/activerecord.rb +3 -1
- data/lib/carrierwave/orm/datamapper.rb +3 -1
- data/lib/carrierwave/sanitized_file.rb +18 -7
- data/lib/carrierwave/storage/abstract.rb +1 -0
- data/lib/carrierwave/storage/file.rb +8 -2
- data/lib/carrierwave/storage/s3.rb +26 -2
- data/lib/carrierwave/uploader.rb +72 -33
- data/lib/generators/uploader_generator.rb +2 -2
- data/rails_generators/uploader/USAGE +2 -0
- data/{lib/generators/templates/uploader.rbt → rails_generators/uploader/templates/uploader.rb} +3 -2
- data/rails_generators/uploader/uploader_generator.rb +19 -0
- data/spec/mount_spec.rb +1 -0
- data/spec/sanitized_file_spec.rb +6 -0
- data/spec/spec_helper.rb +0 -2
- data/spec/uploader_spec.rb +69 -18
- metadata +7 -4
data/README.md
CHANGED
@@ -4,7 +4,13 @@ This plugin for Merb and Rails provides a simple and extremely flexible way to u
|
|
4
4
|
|
5
5
|
## Getting Started
|
6
6
|
|
7
|
-
|
7
|
+
Install the latest stable release:
|
8
|
+
|
9
|
+
[sudo] gem install carrierwave
|
10
|
+
|
11
|
+
Or the cutting edge development version:
|
12
|
+
|
13
|
+
[sudo] gem install jnicklas-carrierwave --source http://gems.github.com
|
8
14
|
|
9
15
|
In Merb, add it as a dependency to your config/dependencies.rb:
|
10
16
|
|
@@ -20,6 +26,10 @@ Start off by generating an uploader:
|
|
20
26
|
|
21
27
|
merb-gen uploader Avatar
|
22
28
|
|
29
|
+
or in Rails:
|
30
|
+
|
31
|
+
script/generate uploader Avatar
|
32
|
+
|
23
33
|
this should give you a file in:
|
24
34
|
|
25
35
|
app/uploaders/avatar_uploader.rb
|
@@ -127,6 +137,33 @@ When this uploader is used, an uploaded image would be scaled to be no larger th
|
|
127
137
|
|
128
138
|
One important thing to remember is that process is called *before* versions are created. This can cut down on processing cost.
|
129
139
|
|
140
|
+
## Making uploads work across form redisplays
|
141
|
+
|
142
|
+
Often you'll notice that uploaded files disappear when a validation
|
143
|
+
fails. CarrierWave has a feature that makes it easy to remember the
|
144
|
+
uploaded file even in that case. Suppose your `user` model has an uploader mounted on `avatar` file, just add a hidden field called `avatar_cache`.
|
145
|
+
In Rails, this would look like this:
|
146
|
+
|
147
|
+
<% form_for @user do |f| %>
|
148
|
+
<p>
|
149
|
+
<label>My Avatar</label>
|
150
|
+
<%= f.file_field :avatar %>
|
151
|
+
<%= f.hidden_field :avatar_cache %>
|
152
|
+
</p>
|
153
|
+
<% end %>
|
154
|
+
|
155
|
+
It might be a good idea to show th user that a file has been uploaded,
|
156
|
+
in the case of images, a small thumbnail would be a good indicator:
|
157
|
+
|
158
|
+
<% form_for @user do |f| %>
|
159
|
+
<p>
|
160
|
+
<label>My Avatar</label>
|
161
|
+
<%= image_tag(@user.avatar.url) if @user.avatar %>
|
162
|
+
<%= f.file_field :avatar %>
|
163
|
+
<%= f.hidden_field :avatar_cache %>
|
164
|
+
</p>
|
165
|
+
<% end %>
|
166
|
+
|
130
167
|
## What's in that uploader file?
|
131
168
|
|
132
169
|
The fact that uploaders are separate classes in CarrierWave is a big advantage. What this means for you is:
|
@@ -206,6 +243,10 @@ And then include it in your model:
|
|
206
243
|
process :crop_resized => [200, 200]
|
207
244
|
end
|
208
245
|
|
246
|
+
## Documentation
|
247
|
+
|
248
|
+
Full YARD documentation is [available at Rubyforge](http://carrierwave.rubyforge.org/).
|
249
|
+
|
209
250
|
## Read the source
|
210
251
|
|
211
|
-
CarrierWave is still young, but most of it is pretty well documented. Just dig in and look at the source for more in-depth explanation of what things are doing.
|
252
|
+
CarrierWave is still young, but most of it is pretty well documented. It is also extensively specced, and there are cucumber features for some common use cases. Just dig in and look at the source for more in-depth explanation of what things are doing.
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'spec/rake/spectask'
|
|
6
6
|
require 'cucumber/rake/task'
|
7
7
|
|
8
8
|
NAME = "carrierwave"
|
9
|
-
GEM_VERSION = "0.1"
|
9
|
+
GEM_VERSION = "0.1.1"
|
10
10
|
AUTHOR = "Jonas Nicklas"
|
11
11
|
EMAIL = "jonas.nicklas@gmail.com"
|
12
12
|
HOMEPAGE = "http://www.example.com"
|
@@ -25,7 +25,7 @@ spec = Gem::Specification.new do |s|
|
|
25
25
|
s.email = EMAIL
|
26
26
|
s.homepage = HOMEPAGE
|
27
27
|
s.require_path = 'lib'
|
28
|
-
s.files = %w(LICENSE Generators README.md Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
|
28
|
+
s.files = %w(LICENSE Generators README.md Rakefile TODO) + Dir.glob("{lib,spec,rails_generators}/**/*")
|
29
29
|
|
30
30
|
end
|
31
31
|
|
data/lib/carrierwave.rb
CHANGED
@@ -7,7 +7,12 @@ module CarrierWave
|
|
7
7
|
|
8
8
|
class UploadError < StandardError; end
|
9
9
|
class NoFileError < UploadError; end
|
10
|
-
class FormNotMultipart < UploadError
|
10
|
+
class FormNotMultipart < UploadError
|
11
|
+
def message
|
12
|
+
"You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.\n\n If this is a file upload, please check that your upload form is multipart encoded."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
class IntegrityError < UploadError; end
|
11
16
|
class InvalidParameter < UploadError; end
|
12
17
|
# Should be used by methods used as process callbacks.
|
13
18
|
class ProcessingError < UploadError; end
|
@@ -32,8 +37,8 @@ CarrierWave.config = {
|
|
32
37
|
:s3 => {
|
33
38
|
:access => :public_read
|
34
39
|
},
|
35
|
-
:store_dir => '
|
36
|
-
:cache_dir => '
|
40
|
+
:store_dir => 'uploads',
|
41
|
+
:cache_dir => 'uploads/tmp'
|
37
42
|
}
|
38
43
|
|
39
44
|
if defined?(Merb)
|
@@ -43,7 +48,7 @@ if defined?(Merb)
|
|
43
48
|
orm_path = File.dirname(__FILE__) / 'carrierwave' / 'orm' / Merb.orm
|
44
49
|
require orm_path if File.exist?(orm_path + '.rb')
|
45
50
|
|
46
|
-
Merb.push_path(:
|
51
|
+
Merb.push_path(:uploader, Merb.root / "app" / "uploaders")
|
47
52
|
|
48
53
|
Merb.add_generators File.dirname(__FILE__) / 'generators' / 'uploader_generator'
|
49
54
|
end
|
data/lib/carrierwave/mount.rb
CHANGED
@@ -1,23 +1,124 @@
|
|
1
1
|
module CarrierWave
|
2
2
|
|
3
|
+
##
|
4
|
+
# If a Class is extended with this module, it gains the mount_uploader
|
5
|
+
# method, which is used for mapping attributes to uploaders and allowing
|
6
|
+
# easy assignment.
|
7
|
+
#
|
8
|
+
# You can use mount_uploader with pretty much any class, however it is
|
9
|
+
# intended to be used with some kind of persistent storage, like an ORM.
|
10
|
+
# If you want to persist the uploaded files in a particular Class, it
|
11
|
+
# needs to implement a `read_uploader` and a `write_uploader` method.
|
12
|
+
#
|
3
13
|
module Mount
|
14
|
+
|
15
|
+
##
|
16
|
+
# @return [Hash{Symbol => CarrierWave}] what uploaders are mounted on which columns
|
17
|
+
#
|
18
|
+
def uploaders
|
19
|
+
@uploaders ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Mounts the given uploader on the given column. This means that assigning
|
24
|
+
# and reading from the column will upload and retrieve files. Supposing
|
25
|
+
# that a User class has an uploader mounted on image, you can assign and
|
26
|
+
# retrieve files like this:
|
27
|
+
#
|
28
|
+
# @user.image # => <Uploader>
|
29
|
+
# @user.image = some_file_object
|
30
|
+
#
|
31
|
+
# @user.store_image!
|
32
|
+
#
|
33
|
+
# @user.image.url # => '/some_url.png'
|
34
|
+
#
|
35
|
+
# It is also possible (but not recommended) to ommit the uploader, which
|
36
|
+
# will create an anonymous uploader class. Passing a block to this method
|
37
|
+
# makes it possible to customize it. This can be convenient for brevity,
|
38
|
+
# but if there is any significatnt logic in the uploader, you should do
|
39
|
+
# the right thing and have it in its own file.
|
40
|
+
#
|
41
|
+
# @param [Symbol] column the attribute to mount this uploader on
|
42
|
+
# @param [CarrierWave::Uploader] uploader the uploader class to mount
|
43
|
+
# @param [Proc] &block customize anonymous uploaders
|
44
|
+
# @example
|
45
|
+
# class Song
|
46
|
+
# mount_uploader :lyrics, LyricsUploader
|
47
|
+
# mount_uploader :file, SongUploader
|
48
|
+
# end
|
49
|
+
# @example
|
50
|
+
# class Data
|
51
|
+
# # this will add an anonymous uploader with only
|
52
|
+
# # the default settings
|
53
|
+
# mount_uploader :csv
|
54
|
+
# end
|
55
|
+
# @example
|
56
|
+
# class Product
|
57
|
+
# # this will add an anonymous uploader overriding
|
58
|
+
# # the store_dir
|
59
|
+
# mount_uploader :blueprint do
|
60
|
+
# def store_dir
|
61
|
+
# 'blueprints'
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
def mount_uploader(column, uploader=nil, &block)
|
67
|
+
unless uploader
|
68
|
+
uploader = Class.new(CarrierWave::Uploader)
|
69
|
+
uploader.class_eval(&block)
|
70
|
+
end
|
71
|
+
|
72
|
+
uploaders[column.to_sym] = uploader
|
73
|
+
|
74
|
+
include CarrierWave::Mount::Extension
|
75
|
+
|
76
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
77
|
+
def #{column} # def image
|
78
|
+
get_uploader(:#{column}) # get_uploader(:image)
|
79
|
+
end # end
|
80
|
+
#
|
81
|
+
def #{column}=(new_file) # def image=(new_file)
|
82
|
+
set_uploader(:#{column}, new_file) # set_uploader(:image, new_file)
|
83
|
+
end # end
|
84
|
+
#
|
85
|
+
def #{column}_cache # def image_cache
|
86
|
+
get_uploader_cache(:#{column}) # get_uploader_cache(:image)
|
87
|
+
end # end
|
88
|
+
#
|
89
|
+
def #{column}_cache=(cache_name) # def image_cache=(cache_name)
|
90
|
+
set_uploader_cache(:#{column}, cache_name) # set_uploader_cache(:image, cache_name)
|
91
|
+
end # end
|
92
|
+
#
|
93
|
+
def store_#{column}! # def store_image!
|
94
|
+
store_uploader!(:#{column}) # store_uploader!(:image)
|
95
|
+
end # end
|
96
|
+
EOF
|
97
|
+
end
|
98
|
+
|
4
99
|
module Extension
|
5
100
|
|
6
101
|
private
|
102
|
+
|
103
|
+
# overwrite this to read from a serialized attribute
|
104
|
+
def read_uploader(column); end
|
105
|
+
|
106
|
+
# overwrite this to write to a serialized attribute
|
107
|
+
def write_uploader(column, identifier); end
|
7
108
|
|
8
109
|
def uploaders
|
9
110
|
@uploaders ||= {}
|
10
111
|
end
|
11
112
|
|
12
113
|
def store_uploader!(column)
|
13
|
-
|
114
|
+
unless uploaders[column].blank?
|
14
115
|
uploaders[column].store!
|
15
116
|
write_uploader(column, uploaders[column].identifier)
|
16
117
|
end
|
17
118
|
end
|
18
119
|
|
19
120
|
def get_uploader(column)
|
20
|
-
return uploaders[column]
|
121
|
+
return uploaders[column] unless uploaders[column].blank?
|
21
122
|
|
22
123
|
identifier = read_uploader(column)
|
23
124
|
|
@@ -29,16 +130,12 @@ module CarrierWave
|
|
29
130
|
end
|
30
131
|
|
31
132
|
def set_uploader(column, new_file)
|
32
|
-
|
33
|
-
|
34
|
-
unless new_file.empty?
|
35
|
-
uploaders[column] ||= self.class.uploaders[column].new(self, column)
|
36
|
-
uploaders[column].cache!(new_file)
|
37
|
-
end
|
133
|
+
uploaders[column] ||= self.class.uploaders[column].new(self, column)
|
134
|
+
uploaders[column].cache!(new_file)
|
38
135
|
end
|
39
136
|
|
40
137
|
def get_uploader_cache(column)
|
41
|
-
uploaders[column].cache_name
|
138
|
+
uploaders[column].cache_name unless uploaders[column].blank?
|
42
139
|
end
|
43
140
|
|
44
141
|
def set_uploader_cache(column, cache_name)
|
@@ -50,44 +147,5 @@ module CarrierWave
|
|
50
147
|
|
51
148
|
end # Extension
|
52
149
|
|
53
|
-
def uploaders
|
54
|
-
@uploaders ||= {}
|
55
|
-
end
|
56
|
-
|
57
|
-
def mount_uploader(column, uploader=nil, &block)
|
58
|
-
unless uploader
|
59
|
-
uploader = Class.new(CarrierWave::Uploader)
|
60
|
-
uploader.class_eval(&block)
|
61
|
-
end
|
62
|
-
|
63
|
-
uploaders[column.to_sym] = uploader
|
64
|
-
|
65
|
-
include CarrierWave::Mount::Extension
|
66
|
-
|
67
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
68
|
-
def #{column} # def image
|
69
|
-
get_uploader(:#{column}) # get_uploader(:image)
|
70
|
-
end # end
|
71
|
-
#
|
72
|
-
def #{column}=(new_file) # def image=(new_file)
|
73
|
-
set_uploader(:#{column}, new_file) # set_uploader(:image, new_file)
|
74
|
-
end # end
|
75
|
-
#
|
76
|
-
def #{column}_cache # def image_cache
|
77
|
-
get_uploader_cache(:#{column}) # get_uploader_cache(:image)
|
78
|
-
end # end
|
79
|
-
#
|
80
|
-
def #{column}_cache=(cache_name) # def image_cache=(cache_name)
|
81
|
-
set_uploader_cache(:#{column}, cache_name) # set_uploader_cache(:image, cache_name)
|
82
|
-
end # end
|
83
|
-
#
|
84
|
-
def store_#{column}! # def store_image!
|
85
|
-
store_uploader!(:#{column}) # store_uploader!(:image)
|
86
|
-
end # end
|
87
|
-
EOF
|
88
|
-
|
89
|
-
after_mount(column, uploader) if respond_to?(:after_mount)
|
90
|
-
end
|
91
|
-
|
92
150
|
end # Mount
|
93
151
|
end # CarrierWave
|
@@ -1,5 +1,14 @@
|
|
1
1
|
module CarrierWave
|
2
|
-
|
2
|
+
|
3
|
+
##
|
4
|
+
# SanitizedFile is a base class which provides a common API around all
|
5
|
+
# the different quirky Ruby File libraries. It has support for Tempfile,
|
6
|
+
# File, StringIO, Merb-style upload Hashes, as well as paths given as
|
7
|
+
# Strings and Pathnames.
|
8
|
+
#
|
9
|
+
# It's probably needlessly comprehensive and complex. Help is appreciated.
|
10
|
+
#
|
11
|
+
class SanitizedFile
|
3
12
|
|
4
13
|
attr_accessor :file, :options
|
5
14
|
|
@@ -58,10 +67,12 @@ module CarrierWave
|
|
58
67
|
# @return [Integer] the file's size in bytes.
|
59
68
|
#
|
60
69
|
def size
|
61
|
-
if
|
70
|
+
if string?
|
71
|
+
exists? ? File.size(path) : 0
|
72
|
+
elsif @file.respond_to?(:size)
|
62
73
|
@file.size
|
63
74
|
elsif path
|
64
|
-
File.size(path)
|
75
|
+
exists? ? File.size(path) : 0
|
65
76
|
else
|
66
77
|
0
|
67
78
|
end
|
@@ -97,7 +108,7 @@ module CarrierWave
|
|
97
108
|
# @return [Boolean]
|
98
109
|
#
|
99
110
|
def empty?
|
100
|
-
|
111
|
+
@file.nil? || self.size.nil? || self.size.zero?
|
101
112
|
end
|
102
113
|
|
103
114
|
##
|
@@ -208,7 +219,7 @@ module CarrierWave
|
|
208
219
|
name = name.gsub("\\", "/") # work-around for IE
|
209
220
|
name = File.basename(name)
|
210
221
|
name = name.gsub(/[^a-zA-Z0-9\.\-\+_]/,"_")
|
211
|
-
name = "_#{name}" if name =~
|
222
|
+
name = "_#{name}" if name =~ /\A\.+\z/
|
212
223
|
name = "unnamed" if name.size == 0
|
213
224
|
return name.downcase
|
214
225
|
end
|
@@ -216,8 +227,8 @@ module CarrierWave
|
|
216
227
|
def split_extension(fn)
|
217
228
|
# regular expressions to try for identifying extensions
|
218
229
|
ext_regexps = [
|
219
|
-
|
220
|
-
|
230
|
+
/\A(.+)\.([^\.]{1,3}\.[^\.]{1,4})\z/, # matches "something.tar.gz"
|
231
|
+
/\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
|
221
232
|
]
|
222
233
|
ext_regexps.each do |regexp|
|
223
234
|
if fn =~ regexp
|
@@ -1,5 +1,11 @@
|
|
1
1
|
module CarrierWave
|
2
2
|
module Storage
|
3
|
+
|
4
|
+
##
|
5
|
+
# File storage stores file to the Filesystem (surprising, no?). There's really not much
|
6
|
+
# to it, it uses the store_dir defined on the uploader as the storage location. That's
|
7
|
+
# pretty much it.
|
8
|
+
#
|
3
9
|
class File < Abstract
|
4
10
|
|
5
11
|
def initialize(uploader)
|
@@ -16,7 +22,7 @@ module CarrierWave
|
|
16
22
|
#
|
17
23
|
def self.store!(uploader, file)
|
18
24
|
path = ::File.join(uploader.store_dir, uploader.filename)
|
19
|
-
path = ::File.expand_path(path, uploader.
|
25
|
+
path = ::File.expand_path(path, uploader.public)
|
20
26
|
file.move_to(path)
|
21
27
|
file
|
22
28
|
end
|
@@ -31,7 +37,7 @@ module CarrierWave
|
|
31
37
|
#
|
32
38
|
def self.retrieve!(uploader, identifier)
|
33
39
|
path = ::File.join(uploader.store_dir, identifier)
|
34
|
-
path = ::File.expand_path(path, uploader.
|
40
|
+
path = ::File.expand_path(path, uploader.public)
|
35
41
|
CarrierWave::SanitizedFile.new(path)
|
36
42
|
end
|
37
43
|
|
@@ -1,7 +1,31 @@
|
|
1
1
|
module CarrierWave
|
2
2
|
module Storage
|
3
|
+
|
3
4
|
##
|
4
|
-
# Uploads things to Amazon S3 webservices
|
5
|
+
# Uploads things to Amazon S3 webservices. It requies the aws/s3 gem. In order for
|
6
|
+
# CarrierWave to connect to Amazon S3, you'll need to specify an access key id, secret key
|
7
|
+
# and bucket
|
8
|
+
#
|
9
|
+
# CarrierWave.config[:s3][:access_key_id] = "xxxxxx"
|
10
|
+
# CarrierWave.config[:s3][:secret_access_key] = "xxxxxx"
|
11
|
+
# CarrierWave.config[:s3][:bucket] = "my_bucket_name"
|
12
|
+
#
|
13
|
+
# You can also set the access policy for the uploaded files:
|
14
|
+
#
|
15
|
+
# CarrierWave.config[:s3][:access] = :public_read
|
16
|
+
#
|
17
|
+
# Possible values are the 'canned access control policies' provided in the aws/s3 gem,
|
18
|
+
# they are:
|
19
|
+
#
|
20
|
+
# :private No one else has any access rights.
|
21
|
+
# :public_read The anonymous principal is granted READ access.
|
22
|
+
# If this policy is used on an object, it can be read from a
|
23
|
+
# browser with no authentication.
|
24
|
+
# :public_read_write The anonymous principal is granted READ and WRITE access.
|
25
|
+
# :authenticated_read Any principal authenticated as a registered Amazon S3 user
|
26
|
+
# is granted READ access.
|
27
|
+
#
|
28
|
+
# The default is :public_read, it should work in most cases.
|
5
29
|
#
|
6
30
|
class S3 < Abstract
|
7
31
|
|
@@ -75,7 +99,7 @@ module CarrierWave
|
|
75
99
|
# @return [String] file's url
|
76
100
|
#
|
77
101
|
def url
|
78
|
-
"http://s3.amazonaws.com
|
102
|
+
["http://s3.amazonaws.com", self.class.bucket, @store_dir, @identifier].compact.join('/')
|
79
103
|
end
|
80
104
|
|
81
105
|
end # S3
|
data/lib/carrierwave/uploader.rb
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
module CarrierWave
|
2
|
+
|
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.
|
17
|
+
#
|
2
18
|
class Uploader
|
3
|
-
|
19
|
+
|
4
20
|
class << self
|
5
21
|
|
6
22
|
##
|
@@ -157,7 +173,14 @@ module CarrierWave
|
|
157
173
|
end
|
158
174
|
|
159
175
|
##
|
160
|
-
#
|
176
|
+
# @return [Boolean] Whether the uploaded file is blank
|
177
|
+
#
|
178
|
+
def blank?
|
179
|
+
!file or file.empty?
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Apply all process callbacks added through CarrierWave.process
|
161
184
|
#
|
162
185
|
def process!
|
163
186
|
self.class.processors.each do |method, args|
|
@@ -193,7 +216,7 @@ module CarrierWave
|
|
193
216
|
if file.respond_to?(:url) and not file.url.blank?
|
194
217
|
file.url
|
195
218
|
elsif current_path
|
196
|
-
File.expand_path(current_path).gsub(File.expand_path(
|
219
|
+
File.expand_path(current_path).gsub(File.expand_path(public), '')
|
197
220
|
end
|
198
221
|
end
|
199
222
|
|
@@ -232,11 +255,30 @@ module CarrierWave
|
|
232
255
|
end
|
233
256
|
|
234
257
|
##
|
235
|
-
# @return [String] the directory
|
258
|
+
# @return [String] the directory that is the root of the application
|
236
259
|
#
|
237
260
|
def root
|
238
261
|
CarrierWave.config[:root]
|
239
262
|
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# @return [String] the directory where files will be publically accessible
|
266
|
+
#
|
267
|
+
def public
|
268
|
+
CarrierWave.config[:public]
|
269
|
+
end
|
270
|
+
|
271
|
+
##
|
272
|
+
# Override this method in your uploader to provide a white list of extensions which
|
273
|
+
# are allowed to be uploaded.
|
274
|
+
#
|
275
|
+
# @return [NilClass, Array[String]] a white list of extensions which are allowed to be uploaded
|
276
|
+
# @example
|
277
|
+
# def extension_white_list
|
278
|
+
# %w(jpg jpeg gif png)
|
279
|
+
# end
|
280
|
+
#
|
281
|
+
def extension_white_list; end
|
240
282
|
|
241
283
|
####################
|
242
284
|
## Cache
|
@@ -277,21 +319,28 @@ module CarrierWave
|
|
277
319
|
# @raise [CarrierWave::FormNotMultipart] if the assigned parameter is a string
|
278
320
|
#
|
279
321
|
def cache!(new_file)
|
280
|
-
self.cache_id = CarrierWave::Uploader.generate_cache_id unless cache_id
|
281
322
|
new_file = CarrierWave::SanitizedFile.new(new_file)
|
282
|
-
raise CarrierWave::FormNotMultipart
|
323
|
+
raise CarrierWave::FormNotMultipart if new_file.string?
|
283
324
|
|
284
|
-
|
325
|
+
unless new_file.empty?
|
326
|
+
if extension_white_list and not extension_white_list.include?(new_file.extension.to_s)
|
327
|
+
raise CarrierWave::IntegrityError, "You are not allowed to upload #{new_file.extension.inspect} files, allowed types: #{extension_white_list.inspect}"
|
328
|
+
end
|
285
329
|
|
286
|
-
|
287
|
-
|
330
|
+
self.cache_id = CarrierWave::Uploader.generate_cache_id unless cache_id
|
331
|
+
|
332
|
+
@file = new_file
|
333
|
+
|
334
|
+
@filename = new_file.filename
|
335
|
+
self.original_filename = new_file.filename
|
288
336
|
|
289
|
-
|
290
|
-
|
337
|
+
@file = @file.copy_to(cache_path)
|
338
|
+
process!
|
291
339
|
|
292
|
-
|
293
|
-
|
294
|
-
|
340
|
+
versions.each do |name, v|
|
341
|
+
v.send(:cache_id=, cache_id)
|
342
|
+
v.cache!(new_file)
|
343
|
+
end
|
295
344
|
end
|
296
345
|
end
|
297
346
|
|
@@ -352,35 +401,25 @@ module CarrierWave
|
|
352
401
|
#
|
353
402
|
# If new_file is omitted, a previously cached file will be stored.
|
354
403
|
#
|
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
404
|
# @param [File, IOString, Tempfile] new_file any kind of file object
|
359
405
|
#
|
360
406
|
def store!(new_file=nil)
|
361
|
-
if
|
362
|
-
|
407
|
+
cache!(new_file) if new_file
|
408
|
+
if @file
|
363
409
|
@file = storage.store!(self, @file)
|
364
410
|
@cache_id = nil
|
365
|
-
|
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)
|
411
|
+
versions.each { |name, v| v.store!(new_file) }
|
372
412
|
end
|
373
|
-
versions.each { |name, v| v.store!(new_file) }
|
374
413
|
end
|
375
414
|
|
376
415
|
##
|
377
416
|
# Retrieves the file from the storage, unless a file has
|
378
417
|
# already been cached, stored or retrieved.
|
379
418
|
#
|
380
|
-
# @param [String]
|
419
|
+
# @param [String] identifier uniquely identifies the file to retrieve
|
381
420
|
#
|
382
|
-
def retrieve_from_store(
|
383
|
-
retrieve_from_store!(
|
421
|
+
def retrieve_from_store(identifier)
|
422
|
+
retrieve_from_store!(identifier) unless file
|
384
423
|
rescue CarrierWave::InvalidParameter
|
385
424
|
end
|
386
425
|
|
@@ -397,7 +436,7 @@ module CarrierWave
|
|
397
436
|
private
|
398
437
|
|
399
438
|
def cache_path
|
400
|
-
File.expand_path(File.join(cache_dir, cache_name),
|
439
|
+
File.expand_path(File.join(cache_dir, cache_name), public)
|
401
440
|
end
|
402
441
|
|
403
442
|
def storage
|
@@ -407,12 +446,12 @@ module CarrierWave
|
|
407
446
|
attr_reader :cache_id, :original_filename
|
408
447
|
|
409
448
|
def cache_id=(cache_id)
|
410
|
-
raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~
|
449
|
+
raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
|
411
450
|
@cache_id = cache_id
|
412
451
|
end
|
413
452
|
|
414
453
|
def original_filename=(filename)
|
415
|
-
raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~
|
454
|
+
raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~ /\A[a-z0-9\.\-\+_]+\z/i
|
416
455
|
@original_filename = filename
|
417
456
|
end
|
418
457
|
|
@@ -3,13 +3,13 @@ module Merb
|
|
3
3
|
class UploaderGenerator < NamedGenerator
|
4
4
|
|
5
5
|
def self.source_root
|
6
|
-
File.join(File.dirname(__FILE__), 'templates')
|
6
|
+
File.join(File.dirname(__FILE__), '..', '..', 'rails_generators', 'uploader', 'templates')
|
7
7
|
end
|
8
8
|
|
9
9
|
first_argument :name, :required => true, :desc => "The name of this uploader"
|
10
10
|
|
11
11
|
template :uploader do |t|
|
12
|
-
t.source = 'uploader.
|
12
|
+
t.source = 'uploader.rb'
|
13
13
|
t.destination = "app/uploaders/#{file_name}_uploader.rb"
|
14
14
|
end
|
15
15
|
end
|
data/{lib/generators/templates/uploader.rbt → rails_generators/uploader/templates/uploader.rb}
RENAMED
@@ -6,6 +6,7 @@ class <%= class_name %>Uploader < CarrierWave::Uploader
|
|
6
6
|
|
7
7
|
# Choose what kind of storage to use for this uploader
|
8
8
|
storage :file
|
9
|
+
# storage :s3
|
9
10
|
|
10
11
|
# Process files as they are uploaded.
|
11
12
|
# process :scale => [200, 300]
|
@@ -14,14 +15,14 @@ class <%= class_name %>Uploader < CarrierWave::Uploader
|
|
14
15
|
# # do something
|
15
16
|
# end
|
16
17
|
|
17
|
-
# Create different
|
18
|
+
# Create different versions of your uploaded files
|
18
19
|
# version :thumb do
|
19
20
|
# process :scale => [50, 50]
|
20
21
|
# end
|
21
22
|
|
22
23
|
# Override the filename of the uploaded files
|
23
24
|
# def filename
|
24
|
-
# "something"
|
25
|
+
# "something.jpg"
|
25
26
|
# end
|
26
27
|
|
27
28
|
# Override the directory where uploaded files will be stored
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class UploaderGenerator < Rails::Generator::NamedBase
|
2
|
+
|
3
|
+
def manifest
|
4
|
+
record do |m|
|
5
|
+
m.directory 'app/uploaders'
|
6
|
+
m.template 'uploader.rb', "app/uploaders/#{name.underscore}_uploader.rb"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def class_name
|
11
|
+
name.camelize
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def banner
|
17
|
+
"Usage: #{$0} uploader UploaderName"
|
18
|
+
end
|
19
|
+
end
|
data/spec/mount_spec.rb
CHANGED
@@ -97,6 +97,7 @@ describe CarrierWave::Mount do
|
|
97
97
|
before do
|
98
98
|
@instance.stub!(:write_uploader)
|
99
99
|
@instance.stub!(:read_uploader).and_return(nil)
|
100
|
+
CarrierWave::SanitizedFile.new(file_path('test.jpg')).copy_to(public_path('uploads/tmp/19990512-1202-123-1234/test.jpg'))
|
100
101
|
end
|
101
102
|
|
102
103
|
it "should do nothing when nil is assigned" do
|
data/spec/sanitized_file_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -29,8 +29,6 @@ end
|
|
29
29
|
|
30
30
|
CarrierWave.config[:public] = public_path
|
31
31
|
CarrierWave.config[:root] = File.expand_path(File.dirname(__FILE__))
|
32
|
-
CarrierWave.config[:store_dir] = 'public/uploads'
|
33
|
-
CarrierWave.config[:cache_dir] = 'public/uploads/tmp'
|
34
32
|
|
35
33
|
module SanitizedFileSpecHelper
|
36
34
|
def stub_merb_tempfile(filename)
|
data/spec/uploader_spec.rb
CHANGED
@@ -55,7 +55,7 @@ describe CarrierWave::Uploader do
|
|
55
55
|
public_path('monkey/apache')
|
56
56
|
end
|
57
57
|
end
|
58
|
-
@uploader.store_dir.should == '
|
58
|
+
@uploader.store_dir.should == 'uploads'
|
59
59
|
@uploader.thumb.store_dir.should == public_path('monkey/apache')
|
60
60
|
end
|
61
61
|
|
@@ -133,15 +133,31 @@ describe CarrierWave::Uploader do
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
+
describe '#blank' do
|
137
|
+
it "should be true when nothing has been done" do
|
138
|
+
@uploader.should be_blank
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should not be true when the file is empty" do
|
142
|
+
@uploader.retrieve_from_cache!('20071201-1234-345-2255/test.jpeg')
|
143
|
+
@uploader.should be_blank
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should not be true when a file has been cached" do
|
147
|
+
@uploader.cache!(File.open(file_path('test.jpg')))
|
148
|
+
@uploader.should_not be_blank
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
136
152
|
describe '#store_dir' do
|
137
153
|
it "should default to the config option" do
|
138
|
-
@uploader.store_dir.should == '
|
154
|
+
@uploader.store_dir.should == 'uploads'
|
139
155
|
end
|
140
156
|
end
|
141
157
|
|
142
158
|
describe '#cache_dir' do
|
143
159
|
it "should default to the config option" do
|
144
|
-
@uploader.cache_dir.should == '
|
160
|
+
@uploader.cache_dir.should == 'uploads/tmp'
|
145
161
|
end
|
146
162
|
end
|
147
163
|
|
@@ -258,6 +274,43 @@ describe CarrierWave::Uploader do
|
|
258
274
|
@uploader.should_receive(:process!)
|
259
275
|
@uploader.cache!(File.open(file_path('test.jpg')))
|
260
276
|
end
|
277
|
+
|
278
|
+
it "should raise an error when trying to cache a string" do
|
279
|
+
running {
|
280
|
+
@uploader.cache!(file_path('test.jpg'))
|
281
|
+
}.should raise_error(CarrierWave::FormNotMultipart)
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should raise an error when trying to cache a pathname" do
|
285
|
+
running {
|
286
|
+
@uploader.cache!(Pathname.new(file_path('test.jpg')))
|
287
|
+
}.should raise_error(CarrierWave::FormNotMultipart)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should do nothing when trying to cache an empty file" do
|
291
|
+
@uploader.cache!(nil)
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should not raise an integiry error if there is no white list" do
|
295
|
+
@uploader.stub!(:extension_white_list).and_return(nil)
|
296
|
+
running {
|
297
|
+
@uploader.cache!(File.open(file_path('test.jpg')))
|
298
|
+
}.should_not raise_error(CarrierWave::IntegrityError)
|
299
|
+
end
|
300
|
+
|
301
|
+
it "should not raise an integiry error if there is a white list and the file is on it" do
|
302
|
+
@uploader.stub!(:extension_white_list).and_return(%w(jpg gif png))
|
303
|
+
running {
|
304
|
+
@uploader.cache!(File.open(file_path('test.jpg')))
|
305
|
+
}.should_not raise_error(CarrierWave::IntegrityError)
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should raise an integiry error if there is a white list and the file is not on it" do
|
309
|
+
@uploader.stub!(:extension_white_list).and_return(%w(txt doc xls))
|
310
|
+
running {
|
311
|
+
@uploader.cache!(File.open(file_path('test.jpg')))
|
312
|
+
}.should raise_error(CarrierWave::IntegrityError)
|
313
|
+
end
|
261
314
|
end
|
262
315
|
|
263
316
|
describe '#retrieve_from_cache!' do
|
@@ -375,14 +428,8 @@ describe CarrierWave::Uploader do
|
|
375
428
|
@uploader.store!(@file)
|
376
429
|
end
|
377
430
|
|
378
|
-
it "should, if a files is given as an argument and use_cache is false, not cache that file" do
|
379
|
-
CarrierWave.config[:use_cache] = false
|
380
|
-
@uploader.should_not_receive(:cache!)
|
381
|
-
@uploader.store!(@file)
|
382
|
-
CarrierWave.config[:use_cache] = true
|
383
|
-
end
|
384
|
-
|
385
431
|
it "should use a previously cached file if no argument is given" do
|
432
|
+
@uploader.cache!(File.open(file_path('test.jpg')))
|
386
433
|
@uploader.should_not_receive(:cache!)
|
387
434
|
@uploader.store!
|
388
435
|
end
|
@@ -403,6 +450,10 @@ describe CarrierWave::Uploader do
|
|
403
450
|
@uploader.store!(@file)
|
404
451
|
@uploader.file.should == @stored_file
|
405
452
|
end
|
453
|
+
|
454
|
+
it "should do nothing when trying to store an empty file" do
|
455
|
+
@uploader.store!(nil)
|
456
|
+
end
|
406
457
|
end
|
407
458
|
|
408
459
|
describe '#retrieve_from_store!' do
|
@@ -494,8 +545,8 @@ describe CarrierWave::Uploader do
|
|
494
545
|
|
495
546
|
it "should suffix the version's store_dir" do
|
496
547
|
@uploader.cache!(File.open(file_path('test.jpg')))
|
497
|
-
@uploader.store_dir.should == '
|
498
|
-
@uploader.thumb.store_dir.should == '
|
548
|
+
@uploader.store_dir.should == 'uploads'
|
549
|
+
@uploader.thumb.store_dir.should == 'uploads/thumb'
|
499
550
|
end
|
500
551
|
|
501
552
|
it "should move it to the tmp dir with the filename prefixed" do
|
@@ -516,8 +567,8 @@ describe CarrierWave::Uploader do
|
|
516
567
|
|
517
568
|
it "should suffix the version's store_dir" do
|
518
569
|
@uploader.retrieve_from_cache!('20071201-1234-345-2255/test.jpg')
|
519
|
-
@uploader.store_dir.should == '
|
520
|
-
@uploader.thumb.store_dir.should == '
|
570
|
+
@uploader.store_dir.should == 'uploads'
|
571
|
+
@uploader.thumb.store_dir.should == 'uploads/thumb'
|
521
572
|
end
|
522
573
|
end
|
523
574
|
|
@@ -551,15 +602,15 @@ describe CarrierWave::Uploader do
|
|
551
602
|
|
552
603
|
it "should, if a file is given as argument, suffix the version's store_dir" do
|
553
604
|
@uploader.store!(@file)
|
554
|
-
@uploader.store_dir.should == '
|
555
|
-
@uploader.thumb.store_dir.should == '
|
605
|
+
@uploader.store_dir.should == 'uploads'
|
606
|
+
@uploader.thumb.store_dir.should == 'uploads/thumb'
|
556
607
|
end
|
557
608
|
|
558
609
|
it "should, if a files is given as an argument and use_cache is false, suffix the version's store_dir" do
|
559
610
|
CarrierWave.config[:use_cache] = false
|
560
611
|
@uploader.store!(@file)
|
561
|
-
@uploader.store_dir.should == '
|
562
|
-
@uploader.thumb.store_dir.should == '
|
612
|
+
@uploader.store_dir.should == 'uploads'
|
613
|
+
@uploader.thumb.store_dir.should == 'uploads/thumb'
|
563
614
|
end
|
564
615
|
|
565
616
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jnicklas-carrierwave
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Nicklas
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-13 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -45,8 +45,6 @@ files:
|
|
45
45
|
- lib/carrierwave/uploader.rb
|
46
46
|
- lib/carrierwave.rb
|
47
47
|
- lib/generators
|
48
|
-
- lib/generators/templates
|
49
|
-
- lib/generators/templates/uploader.rbt
|
50
48
|
- lib/generators/uploader_generator.rb
|
51
49
|
- spec/fixtures
|
52
50
|
- spec/fixtures/bork.txt
|
@@ -59,6 +57,11 @@ files:
|
|
59
57
|
- spec/sanitized_file_spec.rb
|
60
58
|
- spec/spec_helper.rb
|
61
59
|
- spec/uploader_spec.rb
|
60
|
+
- rails_generators/uploader
|
61
|
+
- rails_generators/uploader/templates
|
62
|
+
- rails_generators/uploader/templates/uploader.rb
|
63
|
+
- rails_generators/uploader/uploader_generator.rb
|
64
|
+
- rails_generators/uploader/USAGE
|
62
65
|
has_rdoc: true
|
63
66
|
homepage: http://www.example.com
|
64
67
|
post_install_message:
|