plowdawg-carrierwave 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +674 -0
- data/lib/carrierwave.rb +109 -0
- data/lib/carrierwave/compatibility/paperclip.rb +95 -0
- data/lib/carrierwave/locale/en.yml +5 -0
- data/lib/carrierwave/mount.rb +382 -0
- data/lib/carrierwave/orm/activerecord.rb +46 -0
- data/lib/carrierwave/processing/mime_types.rb +58 -0
- data/lib/carrierwave/processing/mini_magick.rb +253 -0
- data/lib/carrierwave/processing/rmagick.rb +279 -0
- data/lib/carrierwave/sanitized_file.rb +302 -0
- data/lib/carrierwave/storage/abstract.rb +30 -0
- data/lib/carrierwave/storage/cloud_files.rb +188 -0
- data/lib/carrierwave/storage/file.rb +47 -0
- data/lib/carrierwave/storage/fog.rb +332 -0
- data/lib/carrierwave/storage/right_s3.rb +1 -0
- data/lib/carrierwave/storage/s3.rb +240 -0
- data/lib/carrierwave/test/matchers.rb +164 -0
- data/lib/carrierwave/uploader.rb +44 -0
- data/lib/carrierwave/uploader/cache.rb +160 -0
- data/lib/carrierwave/uploader/callbacks.rb +35 -0
- data/lib/carrierwave/uploader/configuration.rb +162 -0
- data/lib/carrierwave/uploader/default_url.rb +19 -0
- data/lib/carrierwave/uploader/download.rb +75 -0
- data/lib/carrierwave/uploader/extension_whitelist.rb +49 -0
- data/lib/carrierwave/uploader/mountable.rb +39 -0
- data/lib/carrierwave/uploader/processing.rb +90 -0
- data/lib/carrierwave/uploader/proxy.rb +77 -0
- data/lib/carrierwave/uploader/remove.rb +23 -0
- data/lib/carrierwave/uploader/store.rb +113 -0
- data/lib/carrierwave/uploader/url.rb +45 -0
- data/lib/carrierwave/uploader/versions.rb +237 -0
- data/lib/carrierwave/validations/active_model.rb +79 -0
- data/lib/carrierwave/version.rb +3 -0
- data/lib/generators/templates/uploader.rb +49 -0
- data/lib/generators/uploader_generator.rb +7 -0
- metadata +215 -0
@@ -0,0 +1,302 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'active_support/core_ext/string/multibyte'
|
5
|
+
|
6
|
+
module CarrierWave
|
7
|
+
|
8
|
+
##
|
9
|
+
# SanitizedFile is a base class which provides a common API around all
|
10
|
+
# the different quirky Ruby File libraries. It has support for Tempfile,
|
11
|
+
# File, StringIO, Merb-style upload Hashes, as well as paths given as
|
12
|
+
# Strings and Pathnames.
|
13
|
+
#
|
14
|
+
# It's probably needlessly comprehensive and complex. Help is appreciated.
|
15
|
+
#
|
16
|
+
class SanitizedFile
|
17
|
+
|
18
|
+
attr_accessor :file
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_writer :sanitize_regexp
|
22
|
+
|
23
|
+
def sanitize_regexp
|
24
|
+
@sanitize_regexp ||= /[^a-zA-Z0-9\.\-\+_]/
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(file)
|
29
|
+
self.file = file
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Returns the filename as is, without sanizting it.
|
34
|
+
#
|
35
|
+
# === Returns
|
36
|
+
#
|
37
|
+
# [String] the unsanitized filename
|
38
|
+
#
|
39
|
+
def original_filename
|
40
|
+
return @original_filename if @original_filename
|
41
|
+
if @file and @file.respond_to?(:original_filename)
|
42
|
+
@file.original_filename
|
43
|
+
elsif path
|
44
|
+
File.basename(path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Returns the filename, sanitized to strip out any evil characters.
|
50
|
+
#
|
51
|
+
# === Returns
|
52
|
+
#
|
53
|
+
# [String] the sanitized filename
|
54
|
+
#
|
55
|
+
def filename
|
56
|
+
sanitize(original_filename) if original_filename
|
57
|
+
end
|
58
|
+
|
59
|
+
alias_method :identifier, :filename
|
60
|
+
|
61
|
+
##
|
62
|
+
# Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
|
63
|
+
# this would return 'test'
|
64
|
+
#
|
65
|
+
# === Returns
|
66
|
+
#
|
67
|
+
# [String] the first part of the filename
|
68
|
+
#
|
69
|
+
def basename
|
70
|
+
split_extension(filename)[0] if filename
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Returns the file extension
|
75
|
+
#
|
76
|
+
# === Returns
|
77
|
+
#
|
78
|
+
# [String] the extension
|
79
|
+
#
|
80
|
+
def extension
|
81
|
+
split_extension(filename)[1] if filename
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Returns the file's size.
|
86
|
+
#
|
87
|
+
# === Returns
|
88
|
+
#
|
89
|
+
# [Integer] the file's size in bytes.
|
90
|
+
#
|
91
|
+
def size
|
92
|
+
if is_path?
|
93
|
+
exists? ? File.size(path) : 0
|
94
|
+
elsif @file.respond_to?(:size)
|
95
|
+
@file.size
|
96
|
+
elsif path
|
97
|
+
exists? ? File.size(path) : 0
|
98
|
+
else
|
99
|
+
0
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Returns the full path to the file. If the file has no path, it will return nil.
|
105
|
+
#
|
106
|
+
# === Returns
|
107
|
+
#
|
108
|
+
# [String, nil] the path where the file is located.
|
109
|
+
#
|
110
|
+
def path
|
111
|
+
unless @file.blank?
|
112
|
+
if is_path?
|
113
|
+
File.expand_path(@file)
|
114
|
+
elsif @file.respond_to?(:path) and not @file.path.blank?
|
115
|
+
File.expand_path(@file.path)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# === Returns
|
122
|
+
#
|
123
|
+
# [Boolean] whether the file is supplied as a pathname or string.
|
124
|
+
#
|
125
|
+
def is_path?
|
126
|
+
!!((@file.is_a?(String) || @file.is_a?(Pathname)) && !@file.blank?)
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# === Returns
|
131
|
+
#
|
132
|
+
# [Boolean] whether the file is valid and has a non-zero size
|
133
|
+
#
|
134
|
+
def empty?
|
135
|
+
@file.nil? || self.size.nil? || self.size.zero?
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# === Returns
|
140
|
+
#
|
141
|
+
# [Boolean] Whether the file exists
|
142
|
+
#
|
143
|
+
def exists?
|
144
|
+
return File.exists?(self.path) if self.path
|
145
|
+
return false
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Returns the contents of the file.
|
150
|
+
#
|
151
|
+
# === Returns
|
152
|
+
#
|
153
|
+
# [String] contents of the file
|
154
|
+
#
|
155
|
+
def read
|
156
|
+
if is_path?
|
157
|
+
File.open(@file, "rb") {|file| file.read}
|
158
|
+
else
|
159
|
+
@file.rewind if @file.respond_to?(:rewind)
|
160
|
+
@file.read
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Moves the file to the given path
|
166
|
+
#
|
167
|
+
# === Parameters
|
168
|
+
#
|
169
|
+
# [new_path (String)] The path where the file should be moved.
|
170
|
+
# [permissions (Integer)] permissions to set on the file in its new location.
|
171
|
+
#
|
172
|
+
def move_to(new_path, permissions=nil)
|
173
|
+
return if self.empty?
|
174
|
+
new_path = File.expand_path(new_path)
|
175
|
+
|
176
|
+
mkdir!(new_path)
|
177
|
+
if exists?
|
178
|
+
FileUtils.mv(path, new_path) unless new_path == path
|
179
|
+
else
|
180
|
+
File.open(new_path, "wb") { |f| f.write(read) }
|
181
|
+
end
|
182
|
+
chmod!(new_path, permissions)
|
183
|
+
self.file = new_path
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Creates a copy of this file and moves it to the given path. Returns the copy.
|
188
|
+
#
|
189
|
+
# === Parameters
|
190
|
+
#
|
191
|
+
# [new_path (String)] The path where the file should be copied to.
|
192
|
+
# [permissions (Integer)] permissions to set on the copy
|
193
|
+
#
|
194
|
+
# === Returns
|
195
|
+
#
|
196
|
+
# @return [CarrierWave::SanitizedFile] the location where the file will be stored.
|
197
|
+
#
|
198
|
+
def copy_to(new_path, permissions=nil)
|
199
|
+
return if self.empty?
|
200
|
+
new_path = File.expand_path(new_path)
|
201
|
+
|
202
|
+
mkdir!(new_path)
|
203
|
+
if exists?
|
204
|
+
FileUtils.cp(path, new_path) unless new_path == path
|
205
|
+
else
|
206
|
+
File.open(new_path, "wb") { |f| f.write(read) }
|
207
|
+
end
|
208
|
+
chmod!(new_path, permissions)
|
209
|
+
self.class.new({:tempfile => new_path, :content_type => content_type})
|
210
|
+
end
|
211
|
+
|
212
|
+
##
|
213
|
+
# Removes the file from the filesystem.
|
214
|
+
#
|
215
|
+
def delete
|
216
|
+
FileUtils.rm(self.path) if exists?
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# Returns the content type of the file.
|
221
|
+
#
|
222
|
+
# === Returns
|
223
|
+
#
|
224
|
+
# [String] the content type of the file
|
225
|
+
#
|
226
|
+
def content_type
|
227
|
+
return @content_type if @content_type
|
228
|
+
@file.content_type.chomp if @file.respond_to?(:content_type) and @file.content_type
|
229
|
+
end
|
230
|
+
|
231
|
+
##
|
232
|
+
# Sets the content type of the file.
|
233
|
+
#
|
234
|
+
# === Returns
|
235
|
+
#
|
236
|
+
# [String] the content type of the file
|
237
|
+
#
|
238
|
+
def content_type=(type)
|
239
|
+
@content_type = type
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# Used to sanitize the file name. Public to allow overriding for non-latin characters.
|
244
|
+
#
|
245
|
+
# === Returns
|
246
|
+
#
|
247
|
+
# [Regexp] the regexp for sanitizing the file name
|
248
|
+
#
|
249
|
+
def sanitize_regexp
|
250
|
+
CarrierWave::SanitizedFile.sanitize_regexp
|
251
|
+
end
|
252
|
+
|
253
|
+
private
|
254
|
+
|
255
|
+
def file=(file)
|
256
|
+
if file.is_a?(Hash)
|
257
|
+
@file = file["tempfile"] || file[:tempfile]
|
258
|
+
@original_filename = file["filename"] || file[:filename]
|
259
|
+
@content_type = file["content_type"] || file[:content_type]
|
260
|
+
else
|
261
|
+
@file = file
|
262
|
+
@original_filename = nil
|
263
|
+
@content_type = nil
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# create the directory if it doesn't exist
|
268
|
+
def mkdir!(path)
|
269
|
+
FileUtils.mkdir_p(File.dirname(path)) unless File.exists?(File.dirname(path))
|
270
|
+
end
|
271
|
+
|
272
|
+
def chmod!(path, permissions)
|
273
|
+
File.chmod(permissions, path) if permissions
|
274
|
+
end
|
275
|
+
|
276
|
+
# Sanitize the filename, to prevent hacking
|
277
|
+
def sanitize(name)
|
278
|
+
name = name.gsub("\\", "/") # work-around for IE
|
279
|
+
name = File.basename(name)
|
280
|
+
name = name.gsub(sanitize_regexp,"_")
|
281
|
+
name = "_#{name}" if name =~ /\A\.+\z/
|
282
|
+
name = "unnamed" if name.size == 0
|
283
|
+
return name.mb_chars.to_s
|
284
|
+
end
|
285
|
+
|
286
|
+
def split_extension(filename)
|
287
|
+
# regular expressions to try for identifying extensions
|
288
|
+
extension_matchers = [
|
289
|
+
/\A(.+)\.(tar\.gz)\z/, # matches "something.tar.gz"
|
290
|
+
/\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
|
291
|
+
]
|
292
|
+
|
293
|
+
extension_matchers.each do |regexp|
|
294
|
+
if filename =~ regexp
|
295
|
+
return $1, $2
|
296
|
+
end
|
297
|
+
end
|
298
|
+
return filename, "" # In case we weren't able to split the extension
|
299
|
+
end
|
300
|
+
|
301
|
+
end # SanitizedFile
|
302
|
+
end # CarrierWave
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Storage
|
5
|
+
|
6
|
+
##
|
7
|
+
# This file serves mostly as a specification for Storage engines. There is no requirement
|
8
|
+
# that storage engines must be a subclass of this class.
|
9
|
+
#
|
10
|
+
class Abstract
|
11
|
+
|
12
|
+
attr_reader :uploader
|
13
|
+
|
14
|
+
def initialize(uploader)
|
15
|
+
@uploader = uploader
|
16
|
+
end
|
17
|
+
|
18
|
+
def identifier
|
19
|
+
uploader.filename
|
20
|
+
end
|
21
|
+
|
22
|
+
def store!(file)
|
23
|
+
end
|
24
|
+
|
25
|
+
def retrieve!(identifier)
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Abstract
|
29
|
+
end # Storage
|
30
|
+
end # CarrierWave
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'cloudfiles'
|
3
|
+
|
4
|
+
module CarrierWave
|
5
|
+
module Storage
|
6
|
+
|
7
|
+
##
|
8
|
+
# Uploads things to Rackspace Cloud Files webservices using the Rackspace libraries (cloudfiles gem).
|
9
|
+
# In order for CarrierWave to connect to Cloud Files, you'll need to specify an username, api key
|
10
|
+
# and container. Optional arguments are config.cloud_files_snet (using the private internal
|
11
|
+
# Rackspace network for communication) and config.cloud_files_auth_url (for connecting to Rackspace's
|
12
|
+
# UK infrastructure or an OpenStack Swift installation)
|
13
|
+
#
|
14
|
+
# CarrierWave.configure do |config|
|
15
|
+
# config.cloud_files_username = "xxxxxx"
|
16
|
+
# config.cloud_files_api_key = "xxxxxx"
|
17
|
+
# config.cloud_files_container = "my_container"
|
18
|
+
# config.cloud_files_auth_url = "https://lon.auth.api.rackspacecloud.com/v1.0"
|
19
|
+
# config.cloud_files_snet = true
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# You can optionally include your CDN host name in the configuration.
|
23
|
+
# This is *highly* recommended, as without it every request requires a lookup
|
24
|
+
# of this information.
|
25
|
+
#
|
26
|
+
# config.cloud_files_cdn_host = "c000000.cdn.rackspacecloud.com"
|
27
|
+
#
|
28
|
+
#
|
29
|
+
class CloudFiles < Abstract
|
30
|
+
|
31
|
+
class File
|
32
|
+
|
33
|
+
def initialize(uploader, base, path)
|
34
|
+
@uploader = uploader
|
35
|
+
@path = path
|
36
|
+
@base = base
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Returns the current path/filename of the file on Cloud Files.
|
41
|
+
#
|
42
|
+
# === Returns
|
43
|
+
#
|
44
|
+
# [String] A path
|
45
|
+
#
|
46
|
+
def path
|
47
|
+
@path
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Reads the contents of the file from Cloud Files
|
52
|
+
#
|
53
|
+
# === Returns
|
54
|
+
#
|
55
|
+
# [String] contents of the file
|
56
|
+
#
|
57
|
+
def read
|
58
|
+
object = cf_container.object(@path)
|
59
|
+
@content_type = object.content_type
|
60
|
+
object.data
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Remove the file from Cloud Files
|
65
|
+
#
|
66
|
+
def delete
|
67
|
+
begin
|
68
|
+
cf_container.delete_object(@path)
|
69
|
+
rescue ::CloudFiles::Exception::NoSuchObject
|
70
|
+
# If the file's not there, don't panic
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Returns the url on the Cloud Files CDN. Note that the parent container must be marked as
|
77
|
+
# public for this to work.
|
78
|
+
#
|
79
|
+
# === Returns
|
80
|
+
#
|
81
|
+
# [String] file's url
|
82
|
+
#
|
83
|
+
def url
|
84
|
+
if @uploader.cloud_files_cdn_host
|
85
|
+
"http://" + @uploader.cloud_files_cdn_host + "/" + @path
|
86
|
+
else
|
87
|
+
begin
|
88
|
+
cf_container.object(@path).public_url
|
89
|
+
rescue ::CloudFiles::Exception::NoSuchObject
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def content_type
|
96
|
+
cf_container.object(@path).content_type
|
97
|
+
end
|
98
|
+
|
99
|
+
def content_type=(new_content_type)
|
100
|
+
headers["content-type"] = new_content_type
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Writes the supplied data into the object on Cloud Files.
|
105
|
+
#
|
106
|
+
# === Returns
|
107
|
+
#
|
108
|
+
# boolean
|
109
|
+
#
|
110
|
+
def store(data,headers={})
|
111
|
+
object = cf_container.create_object(@path)
|
112
|
+
object.write(data,headers)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def headers
|
118
|
+
@headers ||= { }
|
119
|
+
end
|
120
|
+
|
121
|
+
def container
|
122
|
+
@uploader.cloud_files_container
|
123
|
+
end
|
124
|
+
|
125
|
+
def connection
|
126
|
+
@base.connection
|
127
|
+
end
|
128
|
+
|
129
|
+
def cf_connection
|
130
|
+
config = {:username => @uploader.cloud_files_username, :api_key => @uploader.cloud_files_api_key}
|
131
|
+
config[:auth_url] = @uploader.cloud_files_auth_url if @uploader.respond_to?(:cloud_files_auth_url)
|
132
|
+
config[:snet] = @uploader.cloud_files_snet if @uploader.respond_to?(:cloud_files_snet)
|
133
|
+
@cf_connection ||= ::CloudFiles::Connection.new(config)
|
134
|
+
end
|
135
|
+
|
136
|
+
def cf_container
|
137
|
+
if @cf_container
|
138
|
+
@cf_container
|
139
|
+
else
|
140
|
+
begin
|
141
|
+
@cf_container = cf_connection.container(container)
|
142
|
+
rescue NoSuchContainerException
|
143
|
+
@cf_container = cf_connection.create_container(container)
|
144
|
+
@cf_container.make_public
|
145
|
+
end
|
146
|
+
@cf_container
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Store the file on Cloud Files
|
155
|
+
#
|
156
|
+
# === Parameters
|
157
|
+
#
|
158
|
+
# [file (CarrierWave::SanitizedFile)] the file to store
|
159
|
+
#
|
160
|
+
# === Returns
|
161
|
+
#
|
162
|
+
# [CarrierWave::Storage::CloudFiles::File] the stored file
|
163
|
+
#
|
164
|
+
def store!(file)
|
165
|
+
cloud_files_options = {'Content-Type' => file.content_type}
|
166
|
+
f = CarrierWave::Storage::CloudFiles::File.new(uploader, self, uploader.store_path)
|
167
|
+
f.store(file.read,cloud_files_options)
|
168
|
+
f
|
169
|
+
end
|
170
|
+
|
171
|
+
# Do something to retrieve the file
|
172
|
+
#
|
173
|
+
# @param [String] identifier uniquely identifies the file
|
174
|
+
#
|
175
|
+
# [identifier (String)] uniquely identifies the file
|
176
|
+
#
|
177
|
+
# === Returns
|
178
|
+
#
|
179
|
+
# [CarrierWave::Storage::CloudFiles::File] the stored file
|
180
|
+
#
|
181
|
+
def retrieve!(identifier)
|
182
|
+
CarrierWave::Storage::CloudFiles::File.new(uploader, self, uploader.store_path(identifier))
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
end # CloudFiles
|
187
|
+
end # Storage
|
188
|
+
end # CarrierWave
|