carrierwave 0.4.3 → 0.4.4

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.

@@ -1,3 +1,25 @@
1
+ === Version 0.4.5 2010-02-20
2
+
3
+ * [added] Support for Rackspace Cloudfiles
4
+ * [added] GridFS now accepts a port
5
+ * [fixed] s3_headers is now properly initialized
6
+ * [fixed] work around DataMapper's patching of core method
7
+
8
+ === Version 0.4.4 2010-01-31
9
+
10
+ * [added] Support for downloading remote files
11
+ * [added] CarrierWave.clean_cached_files! to remove old cached files
12
+ * [added] Option to set headers for S3
13
+ * [added] GridStore now has authentication
14
+ * [fixed] Rmagick convert method now does what it says
15
+ * [fixed] Content type is stored on GridStore and Amazon S3
16
+ * [fixed] Metadata is no longer broken for S3
17
+
18
+ === Version 0.4.3 2009-12-19
19
+
20
+ * [fixed] cnamed URLs on S3 no longer have a third slash after http
21
+ * [fixed] fixed deprecation warnings on Rails 2.3.5
22
+
1
23
  === Version 0.4.2 2009-11-26
2
24
 
3
25
  * [added] RightAWS as an alternative S3 implementation
@@ -5,6 +5,7 @@ README.rdoc
5
5
  Rakefile
6
6
  cucumber.yml
7
7
  features/caching.feature
8
+ features/download.feature
8
9
  features/file_storage.feature
9
10
  features/file_storage_overridden_filename.feature
10
11
  features/file_storage_overridden_store_dir.feature
@@ -17,6 +18,7 @@ features/mount_datamapper.feature
17
18
  features/step_definitions/activerecord_steps.rb
18
19
  features/step_definitions/caching_steps.rb
19
20
  features/step_definitions/datamapper_steps.rb
21
+ features/step_definitions/download_steps.rb
20
22
  features/step_definitions/file_steps.rb
21
23
  features/step_definitions/general_steps.rb
22
24
  features/step_definitions/mount_steps.rb
@@ -54,6 +56,7 @@ lib/carrierwave/uploader/cache.rb
54
56
  lib/carrierwave/uploader/callbacks.rb
55
57
  lib/carrierwave/uploader/configuration.rb
56
58
  lib/carrierwave/uploader/default_url.rb
59
+ lib/carrierwave/uploader/download.rb
57
60
  lib/carrierwave/uploader/extension_whitelist.rb
58
61
  lib/carrierwave/uploader/mountable.rb
59
62
  lib/carrierwave/uploader/processing.rb
@@ -92,6 +95,7 @@ spec/storage/s3_spec.rb
92
95
  spec/uploader/cache_spec.rb
93
96
  spec/uploader/configuration_spec.rb
94
97
  spec/uploader/default_url_spec.rb
98
+ spec/uploader/download_spec.rb
95
99
  spec/uploader/extension_whitelist_spec.rb
96
100
  spec/uploader/mountable_spec.rb
97
101
  spec/uploader/paths_spec.rb
@@ -13,6 +13,7 @@ upload files.
13
13
  * Source code {hosted at GitHub}[http://github.com/jnicklas/carrierwave]
14
14
  * Please {report any issues}[http://github.com/jnicklas/carrierwave/issues] on GitHub
15
15
  * Please direct any questions at the {mailing list}[http://groups.google.com/group/carrierwave]
16
+ * Check out the {example app}[http://github.com/jnicklas/carrierwave-example-app]
16
17
 
17
18
  == Getting Started
18
19
 
@@ -20,8 +21,6 @@ Install the latest stable release:
20
21
 
21
22
  [sudo] gem install carrierwave
22
23
 
23
- CarrierWave is hosted *only* on {Gemcutter}[http://gemcutter.org] as of version 0.4.0.
24
-
25
24
  In Merb, add it as a dependency to your config/dependencies.rb:
26
25
 
27
26
  dependency 'carrierwave'
@@ -183,12 +182,48 @@ case of images, a small thumbnail would be a good indicator:
183
182
  <% form_for @user do |f| %>
184
183
  <p>
185
184
  <label>My Avatar</label>
186
- <%= image_tag(@user.avatar.url) if @user.avatar %>
185
+ <%= image_tag(@user.avatar_url) if @user.avatar? %>
187
186
  <%= f.file_field :avatar %>
188
187
  <%= f.hidden_field :avatar_cache %>
189
188
  </p>
190
189
  <% end %>
191
190
 
191
+ == Removing uploaded files
192
+
193
+ If you want to remove a previously uploaded file on a mounted uploader, you can
194
+ easily add a checkbox to the form which will remove the file when checked.
195
+
196
+ <% form_for @user do |f| %>
197
+ <p>
198
+ <label>My Avatar</label>
199
+ <%= image_tag(@user.avatar_url) if @user.avatar? %>
200
+ <%= f.file_field :avatar %>
201
+ </p>
202
+
203
+ <p>
204
+ <label>
205
+ <%= f.check_box :remove_avatar %>
206
+ Remove avatar
207
+ </label>
208
+ </p>
209
+ <% end %>
210
+
211
+ If you want to remove the file manually, you can call <code>remove_avatar!</code>.
212
+
213
+ == Uploading files from a remote location
214
+
215
+ Your users may find it convenient to upload a file from a location on the Internet
216
+ via a URL. CarrierWave makes this simple, just add the appropriate column to your
217
+ form and you're good to go:
218
+
219
+ <% form_for @user do |f| %>
220
+ <p>
221
+ <label>My Avatar URL:</label>
222
+ <%= image_tag(@user.avatar_url) if @user.avatar? %>
223
+ <%= f.text_field :remote_avatar_url %>
224
+ </p>
225
+ <% end %>
226
+
192
227
  == Providing a default URL
193
228
 
194
229
  In many cases, especially when working with images, it might be a good idea to
@@ -224,7 +259,7 @@ tests, it's recommended to switch off processing in your tests, and to use the
224
259
  file storage. In Rails you could do that by adding an initializer with:
225
260
 
226
261
  if Rails.env.test?
227
- Carrierwave.configure do |config|
262
+ CarrierWave.configure do |config|
228
263
  config.storage = :file
229
264
  config.enable_processing = false
230
265
  end
@@ -283,7 +318,7 @@ And then in your uploader, set the storage to :s3
283
318
  storage :s3
284
319
  end
285
320
 
286
- That's it! You can still use the +CarrierWave::Uploader#url+ method to return
321
+ That's it! You can still use the <code>CarrierWave::Uploader#url</code> method to return
287
322
  the url to the file on Amazon S3.
288
323
 
289
324
  Alternatively, and especially if your bucket is located in Europe, you can use the
@@ -308,7 +343,7 @@ You'll need to configure the database and host to use:
308
343
 
309
344
  The defaults are 'carrierwave' and 'localhost'.
310
345
 
311
- And then in your uploader, set the storage to +:grid_fs+:
346
+ And then in your uploader, set the storage to <code>:grid_fs</code>:
312
347
 
313
348
  class AvatarUploader < CarrierWave::Uploader::Base
314
349
  storage :grid_fs
@@ -335,7 +370,7 @@ include it in your Uploader:
335
370
  end
336
371
 
337
372
  The RMagick module gives you a few methods, like
338
- +CarrierWave::RMagick#resize_to_fill+ which manipulate the image file in some
373
+ <code>CarrierWave::RMagick#resize_to_fill</code> which manipulate the image file in some
339
374
  way. You can set a +process+ callback, which will call that method any time a
340
375
  file is uploaded.
341
376
 
@@ -394,7 +429,7 @@ If you are using Paperclip, you can use the provided compatibility module:
394
429
  include CarrierWave::Compatibility::Paperclip
395
430
  end
396
431
 
397
- See the documentation for +Paperclip::Compatibility::Paperclip+ for more
432
+ See the documentation for <code>Paperclip::Compatibility::Paperclip</code> for more
398
433
  detaills.
399
434
 
400
435
  Be sure to use mount_on to specify the correct column:
data/Rakefile CHANGED
@@ -26,8 +26,9 @@ $hoe = Hoe.spec 'carrierwave' do
26
26
  self.extra_dev_deps << ['rmagick', '>=2.10.0']
27
27
  self.extra_dev_deps << ['mini_magick', '>=1.2.5']
28
28
  self.extra_dev_deps << ['mongo_mapper', '>=0.6.8']
29
- self.extra_dev_deps << ['mongoid', '>=0.9.9']
29
+ self.extra_dev_deps << ['mongoid', '>=0.10.4']
30
30
  self.extra_dev_deps << ['aws-s3', '>=0.6.2']
31
+ self.extra_dev_deps << ['timecop', '>=0.3.4']
31
32
  self.extra_rdoc_files << 'README.rdoc'
32
33
  end
33
34
 
@@ -0,0 +1,20 @@
1
+ Feature: downloading files
2
+ In order to allow users to upload remote files
3
+ As a developer using CarrierWave
4
+ I want to download files to the filesystem via HTTP
5
+
6
+ Background:
7
+ Given an uploader class that uses the 'file' storage
8
+ And an instance of that class
9
+
10
+ Scenario: download a file
11
+ When I download the file 'http://s3.amazonaws.com/Monkey/testfile.txt'
12
+ Then there should be a file called 'testfile.txt' somewhere in a subdirectory of 'public/uploads/tmp'
13
+ And the file called 'testfile.txt' in a subdirectory of 'public/uploads/tmp' should contain 'S3 Remote File'
14
+
15
+ Scenario: downloading a file then storing
16
+ When I download the file 'http://s3.amazonaws.com/Monkey/testfile.txt'
17
+ And I store the file
18
+ Then there should be a file at 'public/uploads/testfile.txt'
19
+ And the file at 'public/uploads/testfile.txt' should contain 'S3 Remote File'
20
+
@@ -0,0 +1,4 @@
1
+ When /^I download the file '([^']+)'/ do |url|
2
+ @uploader.download!(url)
3
+ end
4
+
@@ -34,9 +34,20 @@ Then /^the file called '(.*?)' in a subdirectory of '(.*?)' should not be identi
34
34
  File.read(Dir.glob(File.join(file_path(directory), '**', file)).first).should_not == File.read(file_path(other))
35
35
  end
36
36
 
37
+ ###
38
+ # CONTENT
39
+
40
+ Then /^the file called '([^']+)' in a subdirectory of '([^']+)' should contain '([^']+)'$/ do |file, directory, content|
41
+ File.read(Dir.glob(File.join(file_path(directory), '**', file)).first).should include(content)
42
+ end
43
+
44
+ Then /^the file at '([^']+)' should contain '([^']+)'$/ do |path, content|
45
+ File.read(file_path(path)).should include(content)
46
+ end
47
+
37
48
  ###
38
49
  # REVERSING
39
50
 
40
51
  Then /^the file at '(.*?)' should be the reverse of the file at '(.*?)'$/ do |one, two|
41
52
  File.read(file_path(one)).should == File.read(file_path(two)).reverse
42
- end
53
+ end
@@ -7,7 +7,7 @@ require 'carrierwave/core_ext/inheritable_attributes'
7
7
 
8
8
  module CarrierWave
9
9
 
10
- VERSION = "0.4.3"
10
+ VERSION = "0.4.4"
11
11
 
12
12
  class << self
13
13
  attr_accessor :root
@@ -15,12 +15,17 @@ module CarrierWave
15
15
  def configure(&block)
16
16
  CarrierWave::Uploader::Base.configure(&block)
17
17
  end
18
+
19
+ def clean_cached_files!
20
+ CarrierWave::Uploader::Base.clean_cached_files!
21
+ end
18
22
  end
19
23
 
20
24
  class UploadError < StandardError; end
21
25
  class IntegrityError < UploadError; end
22
26
  class InvalidParameter < UploadError; end
23
27
  class ProcessingError < UploadError; end
28
+ class DownloadError < UploadError; end
24
29
 
25
30
  autoload :SanitizedFile, 'carrierwave/sanitized_file'
26
31
  autoload :Mount, 'carrierwave/mount'
@@ -40,6 +45,7 @@ module CarrierWave
40
45
  autoload :Base, 'carrierwave/uploader'
41
46
  autoload :Cache, 'carrierwave/uploader/cache'
42
47
  autoload :Store, 'carrierwave/uploader/store'
48
+ autoload :Download, 'carrierwave/uploader/download'
43
49
  autoload :Callbacks, 'carrierwave/uploader/callbacks'
44
50
  autoload :Processing, 'carrierwave/uploader/processing'
45
51
  autoload :Versions, 'carrierwave/uploader/versions'
@@ -85,6 +85,9 @@ module CarrierWave
85
85
  # [image_cache] Returns a string that identifies the cache location of the file
86
86
  # [image_cache=] Retrieves the file from the cache based on the given cache name
87
87
  #
88
+ # [remote_image_url] Returns previously cached remote url
89
+ # [remote_image_url=] Retrieve the file from the remote url
90
+ #
88
91
  # [remove_image] An attribute reader that can be used with a checkbox to mark a file for removal
89
92
  # [remove_image=] An attribute writer that can be used with a checkbox to mark a file for removal
90
93
  # [remove_image?] Whether the file should be removed when store_image! is called.
@@ -92,7 +95,7 @@ module CarrierWave
92
95
  # [store_image!] Stores a file that has been assigned with +image=+
93
96
  # [remove_image!] Removes the uploaded file from the filesystem.
94
97
  #
95
- # [image_integrity_error] Returns an error object if the last file to be assigned caused an integrty error
98
+ # [image_integrity_error] Returns an error object if the last file to be assigned caused an integrity error
96
99
  # [image_processing_error] Returns an error object if the last file to be assigned caused a processing error
97
100
  #
98
101
  # [write_image_identifier] Uses the write_uploader method to set the identifier.
@@ -185,6 +188,14 @@ module CarrierWave
185
188
  _mounter(:#{column}).cache_name = cache_name
186
189
  end
187
190
 
191
+ def remote_#{column}_url
192
+ _mounter(:#{column}).remote_url
193
+ end
194
+
195
+ def remote_#{column}_url=(url)
196
+ _mounter(:#{column}).remote_url = url
197
+ end
198
+
188
199
  def remove_#{column}
189
200
  _mounter(:#{column}).remove
190
201
  end
@@ -248,7 +259,7 @@ module CarrierWave
248
259
  # we don't pollute the model with a lot of methods.
249
260
  class Mounter #:nodoc:
250
261
 
251
- attr_reader :column, :record, :integrity_error, :processing_error
262
+ attr_reader :column, :record, :remote_url, :integrity_error, :processing_error
252
263
  attr_accessor :remove
253
264
 
254
265
  def initialize(record, column, options={})
@@ -299,6 +310,13 @@ module CarrierWave
299
310
  rescue CarrierWave::InvalidParameter
300
311
  end
301
312
 
313
+ def remote_url=(url)
314
+ unless uploader.cached?
315
+ @remote_url = url
316
+ uploader.download!(url)
317
+ end
318
+ end
319
+
302
320
  def store!
303
321
  unless uploader.blank?
304
322
  if remove?
@@ -20,4 +20,4 @@ module CarrierWave
20
20
  end # Mongoid
21
21
  end # CarrierWave
22
22
 
23
- Mongoid::Document.send(:extend, CarrierWave::Mongoid)
23
+ Mongoid::Document::ClassMethods.send(:include, CarrierWave::Mongoid)
@@ -76,7 +76,7 @@ module CarrierWave
76
76
 
77
77
  module ClassMethods
78
78
  def convert(format)
79
- process :resize_to_limit => format
79
+ process :convert => format
80
80
  end
81
81
 
82
82
  def resize_to_limit(width, height)
@@ -91,10 +91,6 @@ module CarrierWave
91
91
  process :resize_to_fill => [width, height]
92
92
  end
93
93
 
94
- def resize_and_pad(width, height)
95
- process :resize_to_fit => [width, height]
96
- end
97
-
98
94
  def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
99
95
  process :resize_and_pad => [width, height, background, gravity]
100
96
  end
@@ -118,11 +114,7 @@ module CarrierWave
118
114
  # image.convert(:png)
119
115
  #
120
116
  def convert(format)
121
- manipulate! do |img|
122
- img.format = format.to_s.upcase
123
- img = yield(img) if block_given?
124
- img
125
- end
117
+ manipulate!(:format => format)
126
118
  end
127
119
 
128
120
  ##
@@ -255,21 +247,27 @@ module CarrierWave
255
247
  #
256
248
  # [CarrierWave::ProcessingError] if manipulation failed.
257
249
  #
258
- def manipulate!
250
+ def manipulate!(options={})
259
251
  image = ::Magick::Image.read(current_path)
260
252
 
261
- if image.size > 1
253
+ frames = if image.size > 1
262
254
  list = ::Magick::ImageList.new
263
255
  image.each do |frame|
264
256
  list << yield( frame )
265
257
  end
266
- list.write(current_path)
267
- destroy_image(list)
258
+ list
268
259
  else
269
260
  frame = image.first
270
- yield( frame ).write(current_path)
271
- destroy_image(frame)
261
+ frame = yield( frame ) if block_given?
262
+ frame
263
+ end
264
+
265
+ if options[:format]
266
+ frames.write("#{options[:format]}:#{current_path}")
267
+ else
268
+ frames.write(current_path)
272
269
  end
270
+ destroy_image(frames)
273
271
  rescue ::Magick::ImageMagickError => e
274
272
  raise CarrierWave::ProcessingError.new("Failed to manipulate with rmagick, maybe it is not an image? Original Error: #{e}")
275
273
  end
@@ -38,6 +38,10 @@ module CarrierWave
38
38
  ::GridFS::GridStore.unlink(@database, @path)
39
39
  end
40
40
 
41
+ def content_type
42
+ ::GridFS::GridStore.open(@database, @path, 'r') { |f| return f.content_type }
43
+ end
44
+
41
45
  end
42
46
 
43
47
  ##
@@ -52,7 +56,7 @@ module CarrierWave
52
56
  # [CarrierWave::SanitizedFile] a sanitized file
53
57
  #
54
58
  def store!(file)
55
- ::GridFS::GridStore.open(database, uploader.store_path, 'w') do |f|
59
+ ::GridFS::GridStore.open(database, uploader.store_path, 'w', :content_type => file.content_type) do |f|
56
60
  f.write file.read
57
61
  end
58
62
  CarrierWave::Storage::GridFS::File.new(uploader, database, uploader.store_path)
@@ -79,7 +83,11 @@ module CarrierWave
79
83
  @connection ||= begin
80
84
  host = uploader.grid_fs_host
81
85
  database = uploader.grid_fs_database
82
- Mongo::Connection.new(host).db(database)
86
+ username = uploader.grid_fs_username
87
+ password = uploader.grid_fs_password
88
+ db = Mongo::Connection.new(host).db(database)
89
+ db.authenticate(username, password) if username && password
90
+ db
83
91
  end
84
92
  end
85
93
 
@@ -73,7 +73,7 @@ module CarrierWave
73
73
  #
74
74
  def read
75
75
  result = connection.get(bucket, @path)
76
- headers["content-type"] = result[:headers]["content-type"]
76
+ @headers = result[:headers]
77
77
  result[:object]
78
78
  end
79
79
 
@@ -99,47 +99,34 @@ module CarrierWave
99
99
  end
100
100
  end
101
101
 
102
- #def about
103
- # s3_object.about
104
- #end
105
-
106
- #def metadata
107
- # s3_object.metadata
108
- #end
109
-
110
102
  def content_type
111
103
  headers["content-type"]
112
104
  end
113
105
 
114
- def content_type=(new_content_type)
115
- headers["content-type"] = new_content_type
116
- end
117
-
118
106
  #def content_disposition
119
107
  # s3_object.content_disposition
120
108
  #end
121
109
 
122
- #def content_disposition=(new_disposition)
123
- # s3_object.content_disposition = new_disposition
124
- #end
125
-
126
- def store(data)
127
- connection.put(bucket, @path, data, headers)
110
+ def store(file)
111
+ connection.put(bucket, @path, file.read,
112
+ 'x-amz-acl' => @uploader.s3_access_policy,
113
+ 'content-type' => file.content_type
114
+ )
128
115
  end
129
116
 
130
- private
131
-
132
- def headers
133
- @headers ||= { 'x-amz-acl' => @uploader.s3_access_policy }
134
- end
117
+ private
118
+
119
+ def headers
120
+ @headers ||= {}
121
+ end
135
122
 
136
- def bucket
137
- @uploader.s3_bucket
138
- end
123
+ def bucket
124
+ @uploader.s3_bucket
125
+ end
139
126
 
140
- def connection
141
- @base.connection
142
- end
127
+ def connection
128
+ @base.connection
129
+ end
143
130
 
144
131
  end
145
132
 
@@ -156,7 +143,7 @@ module CarrierWave
156
143
  #
157
144
  def store!(file)
158
145
  f = CarrierWave::Storage::RightS3::File.new(uploader, self, uploader.store_path)
159
- f.store(file.read)
146
+ f.store(file)
160
147
  f
161
148
  end
162
149
 
@@ -7,7 +7,7 @@ module CarrierWave
7
7
  ##
8
8
  # Uploads things to Amazon S3 webservices. It requies the aws/s3 gem. In order for
9
9
  # CarrierWave to connect to Amazon S3, you'll need to specify an access key id, secret key
10
- # and bucket
10
+ # and bucket:
11
11
  #
12
12
  # CarrierWave.configure do |config|
13
13
  # config.s3_access_key_id = "xxxxxx"
@@ -34,6 +34,20 @@ module CarrierWave
34
34
  #
35
35
  # The default is :public_read, it should work in most cases.
36
36
  #
37
+ # You can assign HTTP headers to be used when S3 serves your files:
38
+ #
39
+ # CarrierWave.configure do |config|
40
+ # config.s3_headers = {"Content-Disposition" => "attachment; filename=foo.jpg;"}
41
+ # end
42
+ #
43
+ # You can also set the headers dynamically by overriding the s3_headers method:
44
+ #
45
+ # class MyUploader < CarrierWave::Uploader::Base
46
+ # def s3_headers
47
+ # { "Expires" => 1.year.from_how.httpdate }
48
+ # end
49
+ # end
50
+ #
37
51
  # You can change the generated url to a cnamed domain by setting the cnamed config:
38
52
  #
39
53
  # CarrierWave.configure do |config|
@@ -131,7 +145,7 @@ module CarrierWave
131
145
  end
132
146
 
133
147
  def s3_object
134
- @s3_object ||= AWS::S3::S3Object.find(@path, bucket)
148
+ @s3_object ||= AWS::S3::S3Object.find(@path, @uploader.s3_bucket)
135
149
  end
136
150
 
137
151
  end
@@ -149,7 +163,9 @@ module CarrierWave
149
163
  #
150
164
  def store!(file)
151
165
  connect!(uploader)
152
- AWS::S3::S3Object.store(uploader.store_path, file.read, uploader.s3_bucket, :access => uploader.s3_access)
166
+ s3_options = {:access => uploader.s3_access, :content_type => file.content_type}
167
+ s3_options.merge!(uploader.s3_headers)
168
+ AWS::S3::S3Object.store(uploader.store_path, file.read, uploader.s3_bucket, s3_options)
153
169
  CarrierWave::Storage::S3::File.new(uploader, uploader.store_path)
154
170
  end
155
171
 
@@ -31,6 +31,7 @@ module CarrierWave
31
31
  use CarrierWave::Uploader::Mountable
32
32
  use CarrierWave::Uploader::Cache
33
33
  use CarrierWave::Uploader::Store
34
+ use CarrierWave::Uploader::Download
34
35
  use CarrierWave::Uploader::Remove
35
36
  use CarrierWave::Uploader::ExtensionWhitelist
36
37
  use CarrierWave::Uploader::Processing
@@ -25,6 +25,32 @@ module CarrierWave
25
25
  depends_on CarrierWave::Uploader::Callbacks
26
26
  depends_on CarrierWave::Uploader::Configuration
27
27
 
28
+ module ClassMethods
29
+
30
+ ##
31
+ # Removes cached files which are older than one day. You could call this method
32
+ # from a rake task to clean out old cached files.
33
+ #
34
+ # You can call this method directly on the module like this:
35
+ #
36
+ # CarrierWave.clean_cached_files!
37
+ #
38
+ # === Note
39
+ #
40
+ # This only works as long as you haven't done anything funky with your cache_dir.
41
+ # It's recommended that you keen cache files in one place only.
42
+ #
43
+ def clean_cached_files!
44
+ Dir.glob(File.expand_path(File.join(cache_dir, '*'), CarrierWave.root)).each do |dir|
45
+ time = dir.scan(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/).first.map { |t| t.to_i }
46
+ time = Time.utc(*time)
47
+ if time < (Time.now - (60*60*24))
48
+ FileUtils.rm_rf(dir)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
28
54
  ##
29
55
  # Returns true if the uploader has been cached
30
56
  #
@@ -116,4 +142,4 @@ module CarrierWave
116
142
 
117
143
  end # Cache
118
144
  end # Uploader
119
- end # CarrierWave
145
+ end # CarrierWave
@@ -14,6 +14,8 @@ module CarrierWave
14
14
  add_config :s3_cnamed
15
15
  add_config :grid_fs_database
16
16
  add_config :grid_fs_host
17
+ add_config :grid_fs_username
18
+ add_config :grid_fs_password
17
19
  add_config :grid_fs_access_url
18
20
  add_config :store_dir
19
21
  add_config :cache_dir
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/http'
4
+
5
+ module CarrierWave
6
+ module Uploader
7
+ module Download
8
+
9
+ depends_on CarrierWave::Uploader::Callbacks
10
+ depends_on CarrierWave::Uploader::Configuration
11
+ depends_on CarrierWave::Uploader::Cache
12
+
13
+ class RemoteFile
14
+ def initialize(uri)
15
+ @uri = URI.parse(uri)
16
+ end
17
+
18
+ def original_filename
19
+ File.basename(@uri.path)
20
+ end
21
+
22
+ def respond_to?(*args)
23
+ super or file.respond_to?(*args)
24
+ end
25
+
26
+ def http?
27
+ @uri.scheme =~ /^https?$/
28
+ end
29
+
30
+ private
31
+
32
+ def file
33
+ @file ||= StringIO.new(Net::HTTP.get_response(@uri).body)
34
+ end
35
+
36
+ def method_missing(*args, &block)
37
+ file.send(*args, &block)
38
+ end
39
+ end
40
+
41
+ ##
42
+ # Caches the file by downloading it from the given URL.
43
+ #
44
+ # === Parameters
45
+ #
46
+ # [url (String)] The URL where the remote file is stored
47
+ #
48
+ def download!(uri)
49
+ unless uri.blank?
50
+ file = RemoteFile.new(uri)
51
+ raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http?
52
+ cache!(file)
53
+ end
54
+ end
55
+
56
+ end # Download
57
+ end # Uploader
58
+ end # CarrierWave
59
+
@@ -215,6 +215,52 @@ describe CarrierWave::Mount do
215
215
  end
216
216
  end
217
217
 
218
+ describe '#remote_image_url' do
219
+ before do
220
+ response = mock('HTTP Response')
221
+ response.stub!(:body).and_return('Response Body')
222
+ Net::HTTP.stub!(:get_response).and_return(response)
223
+ end
224
+
225
+ it "should return nil" do
226
+ @instance.remote_image_url.should be_nil
227
+ end
228
+
229
+ it "should return previously cached URL" do
230
+ @instance.remote_image_url = 'http://www.example.com/funky/monkey.png'
231
+ @instance.remote_image_url.should == 'http://www.example.com/funky/monkey.png'
232
+ end
233
+ end
234
+
235
+ describe '#remote_image_url=' do
236
+ before do
237
+ response = mock('HTTP Response')
238
+ response.stub!(:body).and_return('Response Body')
239
+ Net::HTTP.stub!(:get_response).and_return(response)
240
+ end
241
+
242
+ it "should do nothing when nil is assigned" do
243
+ @instance.remote_image_url = nil
244
+ @instance.image.should be_blank
245
+ end
246
+
247
+ it "should do nothing when an empty string is assigned" do
248
+ @instance.remote_image_url = ''
249
+ @instance.image.should be_blank
250
+ end
251
+
252
+ it "retrieve from cache when a cache name is assigned" do
253
+ @instance.remote_image_url = 'http://www.example.com/funky/monkey.png'
254
+ @instance.image.current_path.should =~ /monkey.png$/
255
+ end
256
+
257
+ it "should not write over a previously assigned file" do
258
+ @instance.image = stub_file('test.jpg')
259
+ @instance.remote_image_url = '19990512-1202-123-1234/monkey.jpg'
260
+ @instance.image.current_path.should =~ /test.jpg$/
261
+ end
262
+ end
263
+
218
264
  describe '#store_image!' do
219
265
 
220
266
  before do
@@ -489,4 +535,4 @@ describe CarrierWave::Mount do
489
535
  end
490
536
  end
491
537
 
492
- end
538
+ end
@@ -10,12 +10,13 @@ describe CarrierWave::Mongoid do
10
10
 
11
11
  before do
12
12
  uploader = Class.new(CarrierWave::Uploader::Base)
13
-
14
- @class = Mongoid::Document
13
+
14
+ @class = Class.new
15
15
  @class.class_eval do
16
+ include Mongoid::Document
16
17
  mount_uploader :image, uploader
17
18
  end
18
-
19
+
19
20
  @uploader = uploader
20
21
  end
21
22
 
@@ -17,6 +17,13 @@ describe CarrierWave::RMagick do
17
17
  FileUtils.rm(file_path('landscape_copy.jpg'))
18
18
  end
19
19
 
20
+ describe '#convert' do
21
+ it "should convert the image to the given format" do
22
+ # TODO: find some way to spec this
23
+ @instance.convert(:png)
24
+ end
25
+ end
26
+
20
27
  describe '#resize_to_fill' do
21
28
  it "should resize the image to exactly the given dimensions" do
22
29
  @instance.resize_to_fill(200, 200)
@@ -19,6 +19,8 @@ require 'spec'
19
19
  require 'spec/autorun'
20
20
 
21
21
  require 'carrierwave'
22
+ require 'timecop'
23
+ require 'time'
22
24
 
23
25
  require 'logger'
24
26
 
@@ -94,4 +96,4 @@ Spec::Runner.configure do |config|
94
96
  config.include CarrierWave::Test::Matchers
95
97
  config.include CarrierWave::Test::MockFiles
96
98
  config.include CarrierWave::Test::MockStorage
97
- end
99
+ end
@@ -10,9 +10,11 @@ describe CarrierWave::Storage::GridFS do
10
10
  @uploader.stub!(:grid_fs_database).and_return("carrierwave_test")
11
11
  @uploader.stub!(:grid_fs_host).and_return("localhost")
12
12
  @uploader.stub!(:grid_fs_access_url).and_return(nil)
13
+ @uploader.stub!(:grid_fs_username).and_return(nil)
14
+ @uploader.stub!(:grid_fs_password).and_return(nil)
13
15
 
14
16
  @storage = CarrierWave::Storage::GridFS.new(@uploader)
15
- @file = CarrierWave::SanitizedFile.new(file_path('test.jpg'))
17
+ @file = stub_tempfile('test.jpg', 'application/xml')
16
18
  end
17
19
 
18
20
  after do
@@ -41,6 +43,10 @@ describe CarrierWave::Storage::GridFS do
41
43
  @grid_fs_file.delete
42
44
  GridFS::GridStore.read(@database, 'uploads/bar.txt').should == ''
43
45
  end
46
+
47
+ it "should store the content type on GridFS" do
48
+ @grid_fs_file.content_type.should == 'application/xml'
49
+ end
44
50
  end
45
51
 
46
52
  describe '#retrieve!' do
@@ -2,6 +2,7 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/../spec_helper'
4
4
  require 'aws/s3'
5
+ require 'net/http'
5
6
 
6
7
  if ENV['S3_SPEC']
7
8
  describe CarrierWave::Storage::S3 do
@@ -12,13 +13,14 @@ if ENV['S3_SPEC']
12
13
  @uploader.stub!(:s3_bucket).and_return(ENV['CARRIERWAVE_TEST_BUCKET'])
13
14
  @uploader.stub!(:s3_access).and_return(:public_read)
14
15
  @uploader.stub!(:s3_cnamed).and_return(false)
16
+ @uploader.stub!(:s3_headers).and_return({'Expires' => 'Fri, 21 Jan 2021 16:51:06 GMT'})
15
17
 
16
18
  @storage = CarrierWave::Storage::S3.new(@uploader)
17
- @file = CarrierWave::SanitizedFile.new(file_path('test.jpg'))
19
+ @file = stub_tempfile('test.jpg', 'application/xml')
18
20
  end
19
21
 
20
22
  after do
21
- AWS::S3::S3Object.delete('uploads/bar.txt', 'carrierwave_test')
23
+ AWS::S3::S3Object.delete('uploads/bar.txt', @uploader.s3_bucket)
22
24
  end
23
25
 
24
26
  describe '#store!' do
@@ -28,7 +30,7 @@ if ENV['S3_SPEC']
28
30
  end
29
31
 
30
32
  it "should upload the file to s3" do
31
- AWS::S3::S3Object.value('uploads/bar.txt', 'carrierwave_test').should == 'this is stuff'
33
+ AWS::S3::S3Object.value('uploads/bar.txt', @uploader.s3_bucket).should == 'this is stuff'
32
34
  end
33
35
 
34
36
  it "should have a path" do
@@ -36,7 +38,7 @@ if ENV['S3_SPEC']
36
38
  end
37
39
 
38
40
  it "should have an Amazon URL" do
39
- @s3_file.url.should == 'http://s3.amazonaws.com/carrierwave_test/uploads/bar.txt'
41
+ @s3_file.url.should == "http://s3.amazonaws.com/#{@uploader.s3_bucket}/uploads/bar.txt"
40
42
  end
41
43
 
42
44
  context "with cnamed bucket" do
@@ -49,13 +51,23 @@ if ENV['S3_SPEC']
49
51
 
50
52
  it "should be deletable" do
51
53
  @s3_file.delete
52
- AWS::S3::S3Object.exists?('uploads/bar.txt', 'carrierwave_test').should be_false
54
+ AWS::S3::S3Object.exists?('uploads/bar.txt', @uploader.s3_bucket).should be_false
55
+ end
56
+
57
+ it "should store the content type on S3" do
58
+ @s3_file.content_type.should == 'application/xml'
59
+ end
60
+
61
+ it "should set headers" do
62
+ client = Net::HTTP.new("s3.amazonaws.com")
63
+ headers = client.request_head(URI.parse(@s3_file.url).path)
64
+ headers['Expires'].should == 'Fri, 21 Jan 2021 16:51:06 GMT'
53
65
  end
54
66
  end
55
67
 
56
68
  describe '#retrieve!' do
57
69
  before do
58
- AWS::S3::S3Object.store('uploads/bar.txt', "A test, 1234", 'carrierwave_test')
70
+ AWS::S3::S3Object.store('uploads/bar.txt', "A test, 1234", @uploader.s3_bucket)
59
71
 
60
72
  @uploader.stub!(:store_path).with('bar.txt').and_return('uploads/bar.txt')
61
73
  @s3_file = @storage.retrieve!('bar.txt')
@@ -70,12 +82,12 @@ if ENV['S3_SPEC']
70
82
  end
71
83
 
72
84
  it "should have an Amazon URL" do
73
- @s3_file.url.should == 'http://s3.amazonaws.com/carrierwave_test/uploads/bar.txt'
85
+ @s3_file.url.should == "http://s3.amazonaws.com/#{@uploader.s3_bucket}/uploads/bar.txt"
74
86
  end
75
87
 
76
88
  it "should be deletable" do
77
89
  @s3_file.delete
78
- AWS::S3::S3Object.exists?('uploads/bar.txt', 'carrierwave_test').should be_false
90
+ AWS::S3::S3Object.exists?('uploads/bar.txt', @uploader.s3_bucket).should be_false
79
91
  end
80
92
  end
81
93
 
@@ -13,6 +13,31 @@ describe CarrierWave::Uploader do
13
13
  FileUtils.rm_rf(public_path)
14
14
  end
15
15
 
16
+ describe '.clean_cached_files!' do
17
+ before do
18
+ @cache_dir = File.expand_path(@uploader_class.cache_dir, CarrierWave.root)
19
+ FileUtils.mkdir_p File.expand_path('20071201-1234-234-2213', @cache_dir)
20
+ FileUtils.mkdir_p File.expand_path('20071203-1234-234-2213', @cache_dir)
21
+ FileUtils.mkdir_p File.expand_path('20071205-1234-234-2213', @cache_dir)
22
+ end
23
+
24
+ after { FileUtils.rm_rf(@cache_dir) }
25
+
26
+ it "should clear all files older than 24 hours in the default cache directory" do
27
+ Timecop.freeze(Time.parse('2007-12-06 10:12')) do
28
+ @uploader_class.clean_cached_files!
29
+ end
30
+ Dir.glob("#{@cache_dir}/*").should have(1).element
31
+ end
32
+
33
+ it "should be aliased on the CarrierWave module" do
34
+ Timecop.freeze(Time.parse('2007-12-06 10:12')) do
35
+ CarrierWave.clean_cached_files!
36
+ end
37
+ Dir.glob("#{@cache_dir}/*").should have(1).element
38
+ end
39
+ end
40
+
16
41
  describe '#cache_dir' do
17
42
  it "should default to the config option" do
18
43
  @uploader.cache_dir.should == 'uploads/tmp'
@@ -181,4 +206,4 @@ describe CarrierWave::Uploader do
181
206
  end
182
207
  end
183
208
 
184
- end
209
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ require File.dirname(__FILE__) + '/../spec_helper'
4
+
5
+ describe CarrierWave::Uploader::Download do
6
+
7
+ before do
8
+ @uploader_class = Class.new(CarrierWave::Uploader::Base)
9
+ @uploader = @uploader_class.new
10
+ end
11
+
12
+ after do
13
+ FileUtils.rm_rf(public_path)
14
+ end
15
+
16
+ describe '#download!' do
17
+
18
+ before do
19
+ CarrierWave.stub!(:generate_cache_id).and_return('20071201-1234-345-2255')
20
+ response = mock('HTTP Response')
21
+ response.stub!(:body).and_return('Response Body')
22
+ Net::HTTP.stub!(:get_response).and_return(response)
23
+ end
24
+
25
+ it "should cache a file" do
26
+ @uploader.download!('http://www.example.com/test/file.png')
27
+ @uploader.file.should be_an_instance_of(CarrierWave::SanitizedFile)
28
+ end
29
+
30
+ it "should be cached" do
31
+ @uploader.download!('http://www.example.com/test/file.png')
32
+ @uploader.should be_cached
33
+ end
34
+
35
+ it "should store the cache name" do
36
+ @uploader.download!('http://www.example.com/test/file.png')
37
+ @uploader.cache_name.should == '20071201-1234-345-2255/file.png'
38
+ end
39
+
40
+ it "should set the filename to the file's sanitized filename" do
41
+ @uploader.download!('http://www.example.com/test/file.png')
42
+ @uploader.filename.should == 'file.png'
43
+ end
44
+
45
+ it "should move it to the tmp dir" do
46
+ @uploader.download!('http://www.example.com/test/file.png')
47
+ @uploader.file.path.should == public_path('uploads/tmp/20071201-1234-345-2255/file.png')
48
+ @uploader.file.exists?.should be_true
49
+ end
50
+
51
+ it "should set the url" do
52
+ @uploader.download!('http://www.example.com/test/file.png')
53
+ @uploader.url.should == '/uploads/tmp/20071201-1234-345-2255/file.png'
54
+ end
55
+
56
+ it "should do nothing when trying to download an empty file" do
57
+ @uploader.download!(nil)
58
+ end
59
+
60
+ it "should set permissions if options are given" do
61
+ @uploader_class.permissions = 0777
62
+
63
+ @uploader.download!('http://www.example.com/test/file.png')
64
+ @uploader.should have_permissions(0777)
65
+ end
66
+
67
+ it "should raise an error when trying to download a local file" do
68
+ running {
69
+ @uploader.download!('/etc/passwd')
70
+ }.should raise_error(CarrierWave::DownloadError)
71
+ end
72
+ end
73
+
74
+ end
75
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carrierwave
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Nicklas
@@ -9,9 +9,29 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-19 00:00:00 +01:00
12
+ date: 2010-02-20 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubyforge
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: gemcutter
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.0
34
+ version:
15
35
  - !ruby/object:Gem::Dependency
16
36
  name: newgem
17
37
  type: :development
@@ -140,7 +160,7 @@ dependencies:
140
160
  requirements:
141
161
  - - ">="
142
162
  - !ruby/object:Gem::Version
143
- version: 0.9.9
163
+ version: 0.10.4
144
164
  version:
145
165
  - !ruby/object:Gem::Dependency
146
166
  name: aws-s3
@@ -152,6 +172,16 @@ dependencies:
152
172
  - !ruby/object:Gem::Version
153
173
  version: 0.6.2
154
174
  version:
175
+ - !ruby/object:Gem::Dependency
176
+ name: timecop
177
+ type: :development
178
+ version_requirement:
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: 0.3.4
184
+ version:
155
185
  - !ruby/object:Gem::Dependency
156
186
  name: hoe
157
187
  type: :development
@@ -160,13 +190,14 @@ dependencies:
160
190
  requirements:
161
191
  - - ">="
162
192
  - !ruby/object:Gem::Version
163
- version: 2.4.0
193
+ version: 2.5.0
164
194
  version:
165
195
  description: |-
166
196
  * RDoc Documentation {available at Rubyforge}[http://carrierwave.rubyforge.org/rdoc].
167
197
  * Source code {hosted at GitHub}[http://github.com/jnicklas/carrierwave]
168
198
  * Please {report any issues}[http://github.com/jnicklas/carrierwave/issues] on GitHub
169
199
  * Please direct any questions at the {mailing list}[http://groups.google.com/group/carrierwave]
200
+ * Check out the {example app}[http://github.com/jnicklas/carrierwave-example-app]
170
201
  email:
171
202
  - jonas.nicklas@gmail.com
172
203
  executables: []
@@ -187,6 +218,7 @@ files:
187
218
  - Rakefile
188
219
  - cucumber.yml
189
220
  - features/caching.feature
221
+ - features/download.feature
190
222
  - features/file_storage.feature
191
223
  - features/file_storage_overridden_filename.feature
192
224
  - features/file_storage_overridden_store_dir.feature
@@ -199,6 +231,7 @@ files:
199
231
  - features/step_definitions/activerecord_steps.rb
200
232
  - features/step_definitions/caching_steps.rb
201
233
  - features/step_definitions/datamapper_steps.rb
234
+ - features/step_definitions/download_steps.rb
202
235
  - features/step_definitions/file_steps.rb
203
236
  - features/step_definitions/general_steps.rb
204
237
  - features/step_definitions/mount_steps.rb
@@ -236,6 +269,7 @@ files:
236
269
  - lib/carrierwave/uploader/callbacks.rb
237
270
  - lib/carrierwave/uploader/configuration.rb
238
271
  - lib/carrierwave/uploader/default_url.rb
272
+ - lib/carrierwave/uploader/download.rb
239
273
  - lib/carrierwave/uploader/extension_whitelist.rb
240
274
  - lib/carrierwave/uploader/mountable.rb
241
275
  - lib/carrierwave/uploader/processing.rb
@@ -274,6 +308,7 @@ files:
274
308
  - spec/uploader/cache_spec.rb
275
309
  - spec/uploader/configuration_spec.rb
276
310
  - spec/uploader/default_url_spec.rb
311
+ - spec/uploader/download_spec.rb
277
312
  - spec/uploader/extension_whitelist_spec.rb
278
313
  - spec/uploader/mountable_spec.rb
279
314
  - spec/uploader/paths_spec.rb