carrierwave 0.5.1 → 0.5.4

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.
@@ -5,14 +5,18 @@ module CarrierWave
5
5
  module Storage
6
6
 
7
7
  ##
8
- # Uploads things to Rackspace Cloud Files webservices using the Rackspace libraries (cloudfiles gem).
8
+ # Uploads things to Rackspace Cloud Files webservices using the Rackspace libraries (cloudfiles gem).
9
9
  # In order for CarrierWave to connect to Cloud Files, you'll need to specify an username, api key
10
- # and container
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)
11
13
  #
12
14
  # CarrierWave.configure do |config|
13
15
  # config.cloud_files_username = "xxxxxx"
14
16
  # config.cloud_files_api_key = "xxxxxx"
15
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
16
20
  # end
17
21
  #
18
22
  # You can optionally include your CDN host name in the configuration.
@@ -33,7 +37,7 @@ module CarrierWave
33
37
  end
34
38
 
35
39
  ##
36
- # Returns the current path/filename of the file on Cloud Files.
40
+ # Returns the current path/filename of the file on Cloud Files.
37
41
  #
38
42
  # === Returns
39
43
  #
@@ -60,7 +64,12 @@ module CarrierWave
60
64
  # Remove the file from Cloud Files
61
65
  #
62
66
  def delete
63
- cf_container.delete_object(@path)
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
64
73
  end
65
74
 
66
75
  ##
@@ -75,7 +84,11 @@ module CarrierWave
75
84
  if @uploader.cloud_files_cdn_host
76
85
  "http://" + @uploader.cloud_files_cdn_host + "/" + @path
77
86
  else
78
- cf_container.object(@path).public_url
87
+ begin
88
+ cf_container.object(@path).public_url
89
+ rescue ::CloudFiles::Exception::NoSuchObject
90
+ nil
91
+ end
79
92
  end
80
93
  end
81
94
 
@@ -112,21 +125,28 @@ module CarrierWave
112
125
  def connection
113
126
  @base.connection
114
127
  end
115
-
128
+
116
129
  def cf_connection
117
- @cf_connection ||= ::CloudFiles::Connection.new(@uploader.cloud_files_username, @uploader.cloud_files_api_key)
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)
118
134
  end
119
135
 
120
136
  def cf_container
121
137
  if @cf_container
122
138
  @cf_container
123
139
  else
124
- @cf_container = cf_connection.create_container(container)
125
- @cf_container.make_public
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
126
146
  @cf_container
127
147
  end
128
148
  end
129
-
149
+
130
150
 
131
151
  end
132
152
 
@@ -0,0 +1,332 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'fog'
5
+ rescue LoadError
6
+ raise "You don't have the 'fog' gem installed"
7
+ end
8
+
9
+ module CarrierWave
10
+ module Storage
11
+
12
+ ##
13
+ # Stores things using the "fog" gem.
14
+ #
15
+ # fog supports storing files with AWS, Google, Local and Rackspace
16
+ #
17
+ # You need to setup some options to configure your usage:
18
+ #
19
+ # [:fog_credentials] credentials for service
20
+ # [:fog_directory] specifies name of directory to store data in, assumed to already exist
21
+ #
22
+ # [:fog_attributes] (optional) additional attributes to set on files
23
+ # [:fog_host] (optional) non-default host to serve files from
24
+ # [:fog_public] (optional) public readability, defaults to true
25
+ # [:fog_authenticated_url_expiration] (optional) time (in seconds) that authenticated urls
26
+ # will be valid, when fog_public is false and provider is AWS or Google, defaults to 600
27
+ #
28
+ #
29
+ # AWS credentials contain the following keys:
30
+ #
31
+ # [:aws_access_key_id]
32
+ # [:aws_secret_access_key]
33
+ # [:region] (optional) defaults to 'us-east-1'
34
+ # :region should be one of ['eu-west-1', 'us-east-1', 'ap-southeast-1', 'us-west-1', 'ap-northeast-1']
35
+ #
36
+ #
37
+ # Google credentials contain the following keys:
38
+ # [:google_storage_access_key_id]
39
+ # [:google_storage_secrete_access_key]
40
+ #
41
+ #
42
+ # Local credentials contain the following keys:
43
+ #
44
+ # [:local_root] local path to files
45
+ #
46
+ #
47
+ # Rackspace credentials contain the following keys:
48
+ #
49
+ # [:rackspace_username]
50
+ # [:rackspace_api_key]
51
+ #
52
+ #
53
+ # A full example with AWS credentials:
54
+ # CarrierWave.configure do |config|
55
+ # config.fog_credentials = {
56
+ # :aws_access_key_id => 'xxxxxx',
57
+ # :aws_secret_access_key => 'yyyyyy',
58
+ # :provider => 'AWS'
59
+ # }
60
+ # config.fog_directory = 'directoryname'
61
+ # config.fog_public = true
62
+ # end
63
+ #
64
+ class Fog < Abstract
65
+
66
+ ##
67
+ # Store a file
68
+ #
69
+ # === Parameters
70
+ #
71
+ # [file (CarrierWave::SanitizedFile)] the file to store
72
+ #
73
+ # === Returns
74
+ #
75
+ # [CarrierWave::Storage::Fog::File] the stored file
76
+ #
77
+ def store!(file)
78
+ f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path)
79
+ f.store(file)
80
+ f
81
+ end
82
+
83
+ ##
84
+ # Retrieve a file
85
+ #
86
+ # === Parameters
87
+ #
88
+ # [identifier (String)] unique identifier for file
89
+ #
90
+ # === Returns
91
+ #
92
+ # [CarrierWave::Storage::Fog::File] the stored file
93
+ #
94
+ def retrieve!(identifier)
95
+ CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path(identifier))
96
+ end
97
+
98
+ def connection
99
+ @connection ||= begin
100
+ ::Fog::Storage.new(uploader.fog_credentials)
101
+ end
102
+ end
103
+
104
+ class File
105
+
106
+ ##
107
+ # Current local path to file
108
+ #
109
+ # === Returns
110
+ #
111
+ # [String] a path to file
112
+ #
113
+ attr_reader :path
114
+
115
+ ##
116
+ # Return all attributes from file
117
+ #
118
+ # === Returns
119
+ #
120
+ # [Hash] attributes from file
121
+ #
122
+ def attributes
123
+ file.attributes
124
+ end
125
+
126
+ ##
127
+ # Return a temporary authenticated url to a private file, if available
128
+ # Only supported for AWS and Google providers
129
+ #
130
+ # === Returns
131
+ #
132
+ # [String] temporary authenticated url
133
+ # or
134
+ # [NilClass] no authenticated url available
135
+ #
136
+ def authenticated_url
137
+ if ['AWS', 'Google'].include?(@uploader.fog_credentials[:provider])
138
+ # avoid a get by using local references
139
+ local_directory = connection.directories.new(:key => @uploader.fog_directory)
140
+ local_file = local_directory.files.new(:key => path)
141
+ local_file.url(::Fog::Time.now + @uploader.fog_authenticated_url_expiration)
142
+ else
143
+ nil
144
+ end
145
+ end
146
+
147
+ ##
148
+ # Lookup value for file content-type header
149
+ #
150
+ # === Returns
151
+ #
152
+ # [String] value of content-type
153
+ #
154
+ def content_type
155
+ @content_type || file.content_type
156
+ end
157
+
158
+ ##
159
+ # Set non-default content-type header (default is file.content_type)
160
+ #
161
+ # === Returns
162
+ #
163
+ # [String] returns new content type value
164
+ #
165
+ def content_type=(new_content_type)
166
+ @content_type = new_content_type
167
+ end
168
+
169
+ ##
170
+ # Remove the file from service
171
+ #
172
+ # === Returns
173
+ #
174
+ # [Boolean] true for success or raises error
175
+ #
176
+ def delete
177
+ # avoid a get by just using local reference
178
+ directory.files.new(:key => path).destroy
179
+ end
180
+
181
+ ##
182
+ # deprecated: All attributes from file (includes headers)
183
+ #
184
+ # === Returns
185
+ #
186
+ # [Hash] attributes from file
187
+ #
188
+ def headers
189
+ location = caller.first
190
+ warning = "[yellow][WARN] headers is deprecated, use attributes instead[/]"
191
+ warning << " [light_black](#{location})[/]"
192
+ Formatador.display_line(warning)
193
+ attributes
194
+ end
195
+
196
+ def initialize(uploader, base, path)
197
+ @uploader, @base, @path = uploader, base, path
198
+ end
199
+
200
+ ##
201
+ # Read content of file from service
202
+ #
203
+ # === Returns
204
+ #
205
+ # [String] contents of file
206
+ def read
207
+ file.body
208
+ end
209
+
210
+ ##
211
+ # Return size of file body
212
+ #
213
+ # === Returns
214
+ #
215
+ # [Integer] size of file body
216
+ #
217
+ def size
218
+ file.content_length
219
+ end
220
+
221
+ ##
222
+ # Write file to service
223
+ #
224
+ # === Returns
225
+ #
226
+ # [Boolean] true on success or raises error
227
+ def store(new_file)
228
+ @content_type ||= new_file.content_type
229
+ @file = directory.files.create({
230
+ :body => new_file.read,
231
+ :content_type => @content_type,
232
+ :key => path,
233
+ :public => @uploader.fog_public
234
+ }.merge(@uploader.fog_attributes))
235
+ true
236
+ end
237
+
238
+ ##
239
+ # Return a url to a public file, if available
240
+ #
241
+ # === Returns
242
+ #
243
+ # [String] public url
244
+ # or
245
+ # [NilClass] no public url available
246
+ #
247
+ def public_url
248
+ if host = @uploader.fog_host
249
+ "#{host}/#{path}"
250
+ else
251
+ # AWS/Google optimized for speed over correctness
252
+ case @uploader.fog_credentials[:provider]
253
+ when 'AWS'
254
+ # if directory is a valid subdomain, use that style for access
255
+ if @uploader.fog_directory.to_s =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\.\d{1,3}){3}$))(?:[a-z0-9]|\.(?![\.\-])|\-(?![\.])){1,61}[a-z0-9]$/
256
+ "https://#{@uploader.fog_directory}.s3.amazonaws.com/#{path}"
257
+ else
258
+ # directory is not a valid subdomain, so use path style for access
259
+ "https://s3.amazonaws.com/#{@uploader.fog_directory}/#{path}"
260
+ end
261
+ when 'Google'
262
+ "https://commondatastorage.googleapis.com/#{@uploader.fog_directory}/#{path}"
263
+ else
264
+ # avoid a get by just using local reference
265
+ directory.files.new(:key => path).public_url
266
+ end
267
+ end
268
+ end
269
+
270
+ ##
271
+ # Return url to file, if avaliable
272
+ #
273
+ # === Returns
274
+ #
275
+ # [String] url
276
+ # or
277
+ # [NilClass] no url available
278
+ #
279
+ def url
280
+ if !@uploader.fog_public
281
+ authenticated_url
282
+ else
283
+ public_url
284
+ end
285
+ end
286
+
287
+ private
288
+
289
+ ##
290
+ # connection to service
291
+ #
292
+ # === Returns
293
+ #
294
+ # [Fog::#{provider}::Storage] connection to service
295
+ #
296
+ def connection
297
+ @base.connection
298
+ end
299
+
300
+ ##
301
+ # local reference to directory containing file
302
+ #
303
+ # === Returns
304
+ #
305
+ # [Fog::#{provider}::Directory] containing directory
306
+ #
307
+ def directory
308
+ @directory ||= begin
309
+ connection.directories.new(
310
+ :key => @uploader.fog_directory,
311
+ :public => @uploader.fog_public
312
+ )
313
+ end
314
+ end
315
+
316
+ ##
317
+ # lookup file
318
+ #
319
+ # === Returns
320
+ #
321
+ # [Fog::#{provider}::File] file data from remote service
322
+ #
323
+ def file
324
+ @file ||= directory.files.get(path)
325
+ end
326
+
327
+ end
328
+
329
+ end # Fog
330
+
331
+ end # Storage
332
+ end # CarrierWave
@@ -11,7 +11,7 @@ module CarrierWave
11
11
  # connection or you reuse an existing connection.
12
12
  #
13
13
  # Creating a connection looks something like this:
14
- #
14
+ #
15
15
  # CarrierWave.configure do |config|
16
16
  # config.storage = :grid_fs
17
17
  # config.grid_fs_host = "your-host.com"
@@ -23,7 +23,7 @@ module CarrierWave
23
23
  # end
24
24
  #
25
25
  # In the above example your documents url will look like:
26
- #
26
+ #
27
27
  # http://your-app.com/images/:document-identifier-here
28
28
  #
29
29
  # When you already have a Mongo connection object (for example through Mongoid)
@@ -45,7 +45,7 @@ module CarrierWave
45
45
  end
46
46
 
47
47
  def path
48
- nil
48
+ @path
49
49
  end
50
50
 
51
51
  def url
@@ -61,7 +61,7 @@ module CarrierWave
61
61
  end
62
62
 
63
63
  def write(file)
64
- grid.open(@uploader.store_path, 'w', :content_type => file.content_type) do |f|
64
+ grid.open(@uploader.store_path, 'w', :content_type => file.content_type) do |f|
65
65
  f.write(file.read)
66
66
  end
67
67
  end
@@ -73,7 +73,7 @@ module CarrierWave
73
73
  def content_type
74
74
  grid.open(@path, 'r').content_type
75
75
  end
76
-
76
+
77
77
  def file_length
78
78
  grid.open(@path, 'r').file_length
79
79
  end
@@ -92,7 +92,7 @@ module CarrierWave
92
92
  db
93
93
  end
94
94
  end
95
-
95
+
96
96
  def grid
97
97
  @grid ||= Mongo::GridFileSystem.new(database)
98
98
  end
@@ -123,15 +123,16 @@ module CarrierWave
123
123
  end
124
124
 
125
125
  def public_url
126
+ scheme = use_ssl? ? 'https' : 'http'
126
127
  if cnamed?
127
- ["http://#{bucket}", path].compact.join('/')
128
+ ["#{scheme}://#{bucket}", path].compact.join('/')
128
129
  else
129
- ["http://#{bucket}.s3.amazonaws.com", path].compact.join('/')
130
+ ["#{scheme}://#{bucket}.s3.amazonaws.com", path].compact.join('/')
130
131
  end
131
132
  end
132
133
 
133
134
  def authenticated_url
134
- connection.get_object_url(bucket, path, Time.now + 60 * 10)
135
+ connection.get_object_url(bucket, path, Time.now + authentication_timeout)
135
136
  end
136
137
 
137
138
  def store(file)
@@ -167,6 +168,10 @@ module CarrierWave
167
168
 
168
169
  private
169
170
 
171
+ def use_ssl?
172
+ @uploader.s3_use_ssl
173
+ end
174
+
170
175
  def cnamed?
171
176
  @uploader.s3_cnamed
172
177
  end
@@ -179,6 +184,10 @@ module CarrierWave
179
184
  @uploader.s3_bucket
180
185
  end
181
186
 
187
+ def authentication_timeout
188
+ @uploader.s3_authentication_timeout
189
+ end
190
+
182
191
  def connection
183
192
  @base.connection
184
193
  end
@@ -217,10 +226,11 @@ module CarrierWave
217
226
  end
218
227
 
219
228
  def connection
220
- @connection ||= Fog::AWS::Storage.new(
221
- :aws_access_key_id => uploader.s3_access_key_id,
222
- :aws_secret_access_key => uploader.s3_secret_access_key,
223
- :region => uploader.s3_region
229
+ @connection ||= ::Fog::Storage.new(
230
+ :aws_access_key_id => uploader.s3_access_key_id,
231
+ :aws_secret_access_key => uploader.s3_secret_access_key,
232
+ :provider => 'AWS',
233
+ :region => uploader.s3_region
224
234
  )
225
235
  end
226
236
 
@@ -7,7 +7,7 @@ module CarrierWave
7
7
  "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."
8
8
  end
9
9
  end
10
-
10
+
11
11
  ##
12
12
  # Generates a unique cache id for use in the caching system
13
13
  #
@@ -37,7 +37,7 @@ module CarrierWave
37
37
  # CarrierWave.clean_cached_files!
38
38
  #
39
39
  # === Note
40
- #
40
+ #
41
41
  # This only works as long as you haven't done anything funky with your cache_dir.
42
42
  # It's recommended that you keep cache files in one place only.
43
43
  #
@@ -63,6 +63,19 @@ module CarrierWave
63
63
  @cache_id
64
64
  end
65
65
 
66
+ ##
67
+ # Caches the remotely stored file
68
+ #
69
+ # This is useful when about to process images. Most processing solutions
70
+ # require the file to be stored on the local filesystem.
71
+ #
72
+ def cache_stored_file!
73
+ sanitized = SanitizedFile.new :tempfile => StringIO.new(file.read),
74
+ :filename => File.basename(path), :content_type => file.content_type
75
+
76
+ cache! sanitized
77
+ end
78
+
66
79
  ##
67
80
  # Returns a String which uniquely identifies the currently cached file for later retrieval
68
81
  #
@@ -87,9 +100,10 @@ module CarrierWave
87
100
  #
88
101
  def cache!(new_file)
89
102
  new_file = CarrierWave::SanitizedFile.new(new_file)
90
- raise CarrierWave::FormNotMultipart if new_file.is_path? && ensure_multipart_form
91
103
 
92
104
  unless new_file.empty?
105
+ raise CarrierWave::FormNotMultipart if new_file.is_path? && ensure_multipart_form
106
+
93
107
  with_callbacks(:cache, new_file) do
94
108
  self.cache_id = CarrierWave.generate_cache_id unless cache_id
95
109
 
@@ -6,33 +6,27 @@ module CarrierWave
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- class_inheritable_accessor :_before_callbacks, :_after_callbacks
9
+ class_attribute :_before_callbacks, :_after_callbacks,
10
+ :instance_writer => false
11
+ self._before_callbacks = Hash.new []
12
+ self._after_callbacks = Hash.new []
10
13
  end
11
14
 
12
15
  def with_callbacks(kind, *args)
13
- self.class._before_callbacks_for(kind).each { |callback| self.send(callback, *args) }
16
+ self.class._before_callbacks[kind].each { |c| send c, *args }
14
17
  yield
15
- self.class._after_callbacks_for(kind).each { |callback| self.send(callback, *args) }
18
+ self.class._after_callbacks[kind].each { |c| send c, *args }
16
19
  end
17
20
 
18
21
  module ClassMethods
19
-
20
- def _before_callbacks_for(kind) #:nodoc:
21
- (self._before_callbacks || { kind => [] })[kind] || []
22
- end
23
-
24
- def _after_callbacks_for(kind) #:nodoc:
25
- (self._after_callbacks || { kind => [] })[kind] || []
26
- end
27
-
28
- def before(kind, callback)
29
- self._before_callbacks ||= {}
30
- self._before_callbacks[kind] = _before_callbacks_for(kind) + [callback]
22
+ def before(kind, callback)
23
+ self._before_callbacks = self._before_callbacks.
24
+ merge kind => _before_callbacks[kind] + [callback]
31
25
  end
32
26
 
33
27
  def after(kind, callback)
34
- self._after_callbacks ||= {}
35
- self._after_callbacks[kind] = _after_callbacks_for(kind) + [callback]
28
+ self._after_callbacks = self._after_callbacks.
29
+ merge kind => _after_callbacks[kind] + [callback]
36
30
  end
37
31
  end # ClassMethods
38
32