paperclip 2.4.5 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of paperclip might be problematic. Click here for more details.

Files changed (63) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +13 -0
  3. data/Appraisals +14 -0
  4. data/CONTRIBUTING.md +38 -0
  5. data/Gemfile +5 -0
  6. data/NEWS +23 -0
  7. data/README.md +72 -42
  8. data/Rakefile +1 -46
  9. data/cucumber/paperclip_steps.rb +6 -0
  10. data/features/basic_integration.feature +46 -0
  11. data/features/rake_tasks.feature +63 -0
  12. data/features/step_definitions/attachment_steps.rb +65 -0
  13. data/features/step_definitions/html_steps.rb +15 -0
  14. data/features/step_definitions/rails_steps.rb +182 -0
  15. data/features/step_definitions/s3_steps.rb +14 -0
  16. data/features/step_definitions/web_steps.rb +209 -0
  17. data/features/support/env.rb +8 -0
  18. data/features/support/fakeweb.rb +3 -0
  19. data/features/support/fixtures/.boot_config.rb.swo +0 -0
  20. data/features/support/fixtures/boot_config.txt +15 -0
  21. data/features/support/fixtures/gemfile.txt +5 -0
  22. data/features/support/fixtures/preinitializer.txt +20 -0
  23. data/features/support/paths.rb +28 -0
  24. data/features/support/rails.rb +46 -0
  25. data/features/support/selectors.rb +19 -0
  26. data/gemfiles/rails2.gemfile +9 -0
  27. data/gemfiles/rails3.gemfile +9 -0
  28. data/gemfiles/rails3_1.gemfile +9 -0
  29. data/lib/paperclip.rb +26 -19
  30. data/lib/paperclip/attachment.rb +123 -109
  31. data/lib/paperclip/interpolations.rb +7 -4
  32. data/lib/paperclip/matchers.rb +33 -2
  33. data/lib/paperclip/missing_attachment_styles.rb +1 -1
  34. data/lib/paperclip/railtie.rb +5 -0
  35. data/lib/paperclip/schema.rb +39 -0
  36. data/lib/paperclip/storage/fog.rb +21 -10
  37. data/lib/paperclip/storage/s3.rb +107 -40
  38. data/lib/paperclip/style.rb +13 -5
  39. data/lib/paperclip/url_generator.rb +64 -0
  40. data/lib/paperclip/version.rb +1 -1
  41. data/lib/tasks/paperclip.rake +1 -1
  42. data/paperclip.gemspec +41 -0
  43. data/test/.gitignore +1 -0
  44. data/test/attachment_test.rb +155 -168
  45. data/test/fixtures/question?mark.png +0 -0
  46. data/test/helper.rb +24 -1
  47. data/test/interpolations_test.rb +16 -2
  48. data/test/paperclip_missing_attachment_styles_test.rb +16 -0
  49. data/test/paperclip_test.rb +72 -22
  50. data/test/schema_test.rb +98 -0
  51. data/test/storage/filesystem_test.rb +2 -2
  52. data/test/{fog_test.rb → storage/fog_test.rb} +35 -8
  53. data/test/storage/s3_live_test.rb +63 -13
  54. data/test/storage/s3_test.rb +394 -91
  55. data/test/style_test.rb +50 -21
  56. data/test/support/mock_attachment.rb +22 -0
  57. data/test/support/mock_interpolator.rb +24 -0
  58. data/test/support/mock_model.rb +2 -0
  59. data/test/support/mock_url_generator_builder.rb +27 -0
  60. data/test/url_generator_test.rb +187 -0
  61. metadata +307 -125
  62. data/lib/paperclip/options.rb +0 -78
  63. data/test/options_test.rb +0 -75
@@ -10,7 +10,8 @@ require 'active_record'
10
10
  require 'active_record/version'
11
11
  require 'active_support'
12
12
  require 'mime/types'
13
- require 'pry'
13
+ require 'pathname'
14
+
14
15
  require 'pathname'
15
16
 
16
17
  puts "Testing against version #{ActiveRecord::VERSION::STRING}"
@@ -53,6 +54,10 @@ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FIL
53
54
  ActiveRecord::Base.establish_connection(config['test'])
54
55
  Paperclip.options[:logger] = ActiveRecord::Base.logger
55
56
 
57
+ Dir[File.join(File.dirname(__FILE__), 'support','*')].each do |f|
58
+ require f
59
+ end
60
+
56
61
  def reset_class class_name
57
62
  ActiveRecord::Base.send(:include, Paperclip::Glue)
58
63
  Object.send(:remove_const, class_name) rescue nil
@@ -152,3 +157,21 @@ def with_exitstatus_returning(code)
152
157
  `ruby -e 'exit #{saved_exitstatus.to_i}'`
153
158
  end
154
159
  end
160
+
161
+ def fixture_file(filename)
162
+ File.join(File.dirname(__FILE__), 'fixtures', filename)
163
+ end
164
+
165
+ def assert_success_response(url)
166
+ Net::HTTP.get_response(URI.parse(url)) do |response|
167
+ assert_equal "200", response.code,
168
+ "Expected HTTP response code 200, got #{response.code}"
169
+ end
170
+ end
171
+
172
+ def assert_not_found_response(url)
173
+ Net::HTTP.get_response(URI.parse(url)) do |response|
174
+ assert_equal "404", response.code,
175
+ "Expected HTTP response code 404, got #{response.code}"
176
+ end
177
+ end
@@ -88,6 +88,13 @@ class InterpolationsTest < Test::Unit::TestCase
88
88
  assert_equal 23, Paperclip::Interpolations.id(attachment, :style)
89
89
  end
90
90
 
91
+ should "return nil for attachments to new records" do
92
+ attachment = mock
93
+ attachment.expects(:id).returns(nil)
94
+ attachment.expects(:instance).returns(attachment)
95
+ assert_nil Paperclip::Interpolations.id(attachment, :style)
96
+ end
97
+
91
98
  should "return the partitioned id of the attachment when the id is an integer" do
92
99
  attachment = mock
93
100
  attachment.expects(:id).returns(23)
@@ -102,6 +109,13 @@ class InterpolationsTest < Test::Unit::TestCase
102
109
  assert_equal "32f/nj2/3oi", Paperclip::Interpolations.id_partition(attachment, :style)
103
110
  end
104
111
 
112
+ should "return nil for the partitioned id of an attachment to a new record (when the id is nil)" do
113
+ attachment = mock
114
+ attachment.expects(:id).returns(nil)
115
+ attachment.expects(:instance).returns(attachment)
116
+ assert_nil Paperclip::Interpolations.id_partition(attachment, :style)
117
+ end
118
+
105
119
  should "return the name of the attachment" do
106
120
  attachment = mock
107
121
  attachment.expects(:name).returns("file")
@@ -181,14 +195,14 @@ class InterpolationsTest < Test::Unit::TestCase
181
195
  should "return attachment's hash when passing both arguments" do
182
196
  attachment = mock
183
197
  fake_hash = "a_wicked_secure_hash"
184
- attachment.expects(:hash).returns(fake_hash)
198
+ attachment.expects(:hash_key).returns(fake_hash)
185
199
  assert_equal fake_hash, Paperclip::Interpolations.hash(attachment, :style)
186
200
  end
187
201
 
188
202
  should "return Object#hash when passing no argument" do
189
203
  attachment = mock
190
204
  fake_hash = "a_wicked_secure_hash"
191
- attachment.expects(:hash).never.returns(fake_hash)
205
+ attachment.expects(:hash_key).never.returns(fake_hash)
192
206
  assert_not_equal fake_hash, Paperclip::Interpolations.hash
193
207
  end
194
208
 
@@ -68,6 +68,22 @@ class PaperclipMissingAttachmentStylesTest < Test::Unit::TestCase
68
68
  Paperclip.save_current_attachments_styles!
69
69
  assert_equal Hash.new, Paperclip.missing_attachments_styles
70
70
  end
71
+
72
+ should "be able to calculate differences when a new attachment is added to a model" do
73
+ rebuild_model :styles => {:croppable => '600x600>', :big => '1000x1000>'}
74
+ Paperclip.save_current_attachments_styles!
75
+
76
+ class ::Dummy
77
+ has_attached_file :photo, :styles => {:small => 'x100', :large => '1000x1000>'}
78
+ end
79
+
80
+ expected_hash = {
81
+ :Dummy => {:photo => [:large, :small]}
82
+ }
83
+ assert_equal expected_hash, Paperclip.missing_attachments_styles
84
+ Paperclip.save_current_attachments_styles!
85
+ assert_equal Hash.new, Paperclip.missing_attachments_styles
86
+ end
71
87
 
72
88
  # It's impossible to build styles hash without loading from database whole bunch of records
73
89
  should "skip lambda-styles" do
@@ -86,6 +86,16 @@ class PaperclipTest < Test::Unit::TestCase
86
86
  has_attached_file :blah
87
87
  end
88
88
  end
89
+
90
+ should "not generate warning if attachment is redifined with the same url string but has :class in it" do
91
+ Paperclip.expects(:log).never
92
+ Dummy.class_eval do
93
+ has_attached_file :blah, :url => "/system/:class/:attachment/:id/:style/:filename"
94
+ end
95
+ Dummy2.class_eval do
96
+ has_attached_file :blah, :url => "/system/:class/:attachment/:id/:style/:filename"
97
+ end
98
+ end
89
99
  end
90
100
 
91
101
  context "An ActiveRecord model with an 'avatar' attachment" do
@@ -170,38 +180,78 @@ class PaperclipTest < Test::Unit::TestCase
170
180
  end
171
181
 
172
182
  context "a validation with an if guard clause" do
173
- setup do
174
- Dummy.send(:"validates_attachment_presence", :avatar, :if => lambda{|i| i.foo })
175
- @dummy = Dummy.new
176
- @dummy.stubs(:avatar_file_name).returns(nil)
177
- end
183
+ context "as a lambda" do
184
+ setup do
185
+ Dummy.send(:"validates_attachment_presence", :avatar, :if => lambda{|i| i.foo })
186
+ @dummy = Dummy.new
187
+ @dummy.stubs(:avatar_file_name).returns(nil)
188
+ end
178
189
 
179
- should "attempt validation if the guard returns true" do
180
- @dummy.expects(:foo).returns(true)
181
- assert ! @dummy.valid?
190
+ should "attempt validation if the guard returns true" do
191
+ @dummy.expects(:foo).returns(true)
192
+ assert ! @dummy.valid?
193
+ end
194
+
195
+ should "not attempt validation if the guard returns false" do
196
+ @dummy.expects(:foo).returns(false)
197
+ assert @dummy.valid?
198
+ end
182
199
  end
183
200
 
184
- should "not attempt validation if the guard returns false" do
185
- @dummy.expects(:foo).returns(false)
186
- assert @dummy.valid?
201
+ context "as a method name" do
202
+ setup do
203
+ Dummy.send(:"validates_attachment_presence", :avatar, :if => :foo)
204
+ @dummy = Dummy.new
205
+ @dummy.stubs(:avatar_file_name).returns(nil)
206
+ end
207
+
208
+ should "attempt validation if the guard returns true" do
209
+ @dummy.expects(:foo).returns(true)
210
+ assert ! @dummy.valid?
211
+ end
212
+
213
+ should "not attempt validation if the guard returns false" do
214
+ @dummy.expects(:foo).returns(false)
215
+ assert @dummy.valid?
216
+ end
187
217
  end
188
218
  end
189
219
 
190
220
  context "a validation with an unless guard clause" do
191
- setup do
192
- Dummy.send(:"validates_attachment_presence", :avatar, :unless => lambda{|i| i.foo })
193
- @dummy = Dummy.new
194
- @dummy.stubs(:avatar_file_name).returns(nil)
195
- end
221
+ context "as a lambda" do
222
+ setup do
223
+ Dummy.send(:"validates_attachment_presence", :avatar, :unless => lambda{|i| i.foo })
224
+ @dummy = Dummy.new
225
+ @dummy.stubs(:avatar_file_name).returns(nil)
226
+ end
227
+
228
+ should "attempt validation if the guard returns true" do
229
+ @dummy.expects(:foo).returns(false)
230
+ assert ! @dummy.valid?
231
+ end
196
232
 
197
- should "attempt validation if the guard returns true" do
198
- @dummy.expects(:foo).returns(false)
199
- assert ! @dummy.valid?
233
+ should "not attempt validation if the guard returns false" do
234
+ @dummy.expects(:foo).returns(true)
235
+ assert @dummy.valid?
236
+ end
200
237
  end
201
238
 
202
- should "not attempt validation if the guard returns false" do
203
- @dummy.expects(:foo).returns(true)
204
- assert @dummy.valid?
239
+ context "as a method name" do
240
+ setup do
241
+ Dummy.send(:"validates_attachment_presence", :avatar, :unless => :foo)
242
+ @dummy = Dummy.new
243
+ @dummy.stubs(:avatar_file_name).returns(nil)
244
+ end
245
+
246
+ should "attempt validation if the guard returns true" do
247
+ @dummy.expects(:foo).returns(false)
248
+ assert ! @dummy.valid?
249
+ end
250
+
251
+ should "not attempt validation if the guard returns false" do
252
+ @dummy.expects(:foo).returns(true)
253
+ assert @dummy.valid?
254
+ end
205
255
  end
206
256
  end
207
257
 
@@ -0,0 +1,98 @@
1
+ require './test/helper'
2
+ require 'paperclip/schema'
3
+
4
+ class MockSchema
5
+ include Paperclip::Schema
6
+
7
+ def initialize(table_name = nil)
8
+ @table_name = table_name
9
+ @columns = {}
10
+ @deleted_columns = []
11
+ end
12
+
13
+ def column(name, type)
14
+ @columns[name] = type
15
+ end
16
+
17
+ def remove_column(table_name, column_name)
18
+ return if @table_name && @table_name != table_name
19
+ @columns.delete(column_name)
20
+ @deleted_columns.push(column_name)
21
+ end
22
+
23
+ def has_column?(column_name)
24
+ @columns.key?(column_name)
25
+ end
26
+
27
+ def deleted_column?(column_name)
28
+ @deleted_columns.include?(column_name)
29
+ end
30
+
31
+ def type_of(column_name)
32
+ @columns[column_name]
33
+ end
34
+ end
35
+
36
+ class SchemaTest < Test::Unit::TestCase
37
+ context "Migrating up" do
38
+ setup do
39
+ @schema = MockSchema.new
40
+ @schema.has_attached_file :avatar
41
+ end
42
+
43
+ should "create the file_name column" do
44
+ assert @schema.has_column?(:avatar_file_name)
45
+ end
46
+
47
+ should "create the content_type column" do
48
+ assert @schema.has_column?(:avatar_content_type)
49
+ end
50
+
51
+ should "create the file_size column" do
52
+ assert @schema.has_column?(:avatar_file_size)
53
+ end
54
+
55
+ should "create the updated_at column" do
56
+ assert @schema.has_column?(:avatar_updated_at)
57
+ end
58
+
59
+ should "make the file_name column a string" do
60
+ assert_equal :string, @schema.type_of(:avatar_file_name)
61
+ end
62
+
63
+ should "make the content_type column a string" do
64
+ assert_equal :string, @schema.type_of(:avatar_content_type)
65
+ end
66
+
67
+ should "make the file_size column an integer" do
68
+ assert_equal :integer, @schema.type_of(:avatar_file_size)
69
+ end
70
+
71
+ should "make the updated_at column a datetime" do
72
+ assert_equal :datetime, @schema.type_of(:avatar_updated_at)
73
+ end
74
+ end
75
+
76
+ context "Migrating down" do
77
+ setup do
78
+ @schema = MockSchema.new(:users)
79
+ @schema.drop_attached_file :users, :avatar
80
+ end
81
+
82
+ should "remove the file_name column" do
83
+ assert @schema.deleted_column?(:avatar_file_name)
84
+ end
85
+
86
+ should "remove the content_type column" do
87
+ assert @schema.deleted_column?(:avatar_content_type)
88
+ end
89
+
90
+ should "remove the file_size column" do
91
+ assert @schema.deleted_column?(:avatar_file_size)
92
+ end
93
+
94
+ should "remove the updated_at column" do
95
+ assert @schema.deleted_column?(:avatar_updated_at)
96
+ end
97
+ end
98
+ end
@@ -6,7 +6,7 @@ class FileSystemTest < Test::Unit::TestCase
6
6
  rebuild_model :styles => { :thumbnail => "25x25#" }
7
7
  @dummy = Dummy.create!
8
8
 
9
- @dummy.avatar = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "5k.png"))
9
+ @dummy.avatar = File.open(fixture_file('5k.png'))
10
10
  end
11
11
 
12
12
  should "allow file assignment" do
@@ -36,7 +36,7 @@ class FileSystemTest < Test::Unit::TestCase
36
36
  rebuild_model :styles => { :thumbnail => "25x25#" }
37
37
  @dummy = Dummy.create!
38
38
 
39
- @dummy.avatar = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "spaced file.png"))
39
+ @dummy.avatar = File.open(fixture_file('spaced file.png'))
40
40
  @dummy.save
41
41
  end
42
42
 
@@ -12,9 +12,9 @@ class FogTest < Test::Unit::TestCase
12
12
  :storage => :fog,
13
13
  :url => '/:attachment/:filename',
14
14
  :fog_directory => "paperclip",
15
- :fog_credentials => File.join(File.dirname(__FILE__), 'fixtures', 'fog.yml')
15
+ :fog_credentials => fixture_file('fog.yml')
16
16
  @dummy = Dummy.new
17
- @dummy.avatar = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
17
+ @dummy.avatar = File.new(fixture_file('5k.png'), 'rb')
18
18
  end
19
19
 
20
20
  should "have the proper information loading credentials from a file" do
@@ -28,9 +28,9 @@ class FogTest < Test::Unit::TestCase
28
28
  :storage => :fog,
29
29
  :url => '/:attachment/:filename',
30
30
  :fog_directory => "paperclip",
31
- :fog_credentials => File.open(File.join(File.dirname(__FILE__), 'fixtures', 'fog.yml'))
31
+ :fog_credentials => File.open(fixture_file('fog.yml'))
32
32
  @dummy = Dummy.new
33
- @dummy.avatar = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
33
+ @dummy.avatar = File.new(fixture_file('5k.png'), 'rb')
34
34
  end
35
35
 
36
36
  should "have the proper information loading credentials from a file" do
@@ -50,10 +50,10 @@ class FogTest < Test::Unit::TestCase
50
50
  :aws_secret_access_key => 'AWS_SECRET'
51
51
  }
52
52
  @dummy = Dummy.new
53
- @dummy.avatar = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
53
+ @dummy.avatar = File.new(fixture_file('5k.png'), 'rb')
54
54
  end
55
55
  should "be able to interpolate the path without blowing up" do
56
- assert_equal File.expand_path(File.join(File.dirname(__FILE__), "../public/avatars/5k.png")),
56
+ assert_equal File.expand_path(File.join(File.dirname(__FILE__), "../../public/avatars/5k.png")),
57
57
  @dummy.avatar.path
58
58
  end
59
59
 
@@ -98,7 +98,7 @@ class FogTest < Test::Unit::TestCase
98
98
 
99
99
  context "when assigned" do
100
100
  setup do
101
- @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
101
+ @file = File.new(fixture_file('5k.png'), 'rb')
102
102
  @dummy = Dummy.new
103
103
  @dummy.avatar = @file
104
104
  end
@@ -181,11 +181,38 @@ class FogTest < Test::Unit::TestCase
181
181
  end
182
182
 
183
183
  should 'set the @fog_public instance variable to false' do
184
- assert_equal false, @dummy.avatar.options.fog_public
184
+ assert_equal false, @dummy.avatar.instance_variable_get('@options')[:fog_public]
185
185
  assert_equal false, @dummy.avatar.fog_public
186
186
  end
187
187
  end
188
188
 
189
+ context "with a valid bucket name for a subdomain" do
190
+ should "provide an url in subdomain style" do
191
+ assert_match /^https:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png\?\d*$/, @dummy.avatar.url
192
+ end
193
+ end
194
+
195
+ context "with an invalid bucket name for a subdomain" do
196
+ setup do
197
+ rebuild_model(@options.merge(:fog_directory => "this_is_invalid"))
198
+ @dummy = Dummy.new
199
+ @dummy.avatar = @file
200
+ @dummy.save
201
+ end
202
+
203
+ should "not match the bucket-subdomain restrictions" do
204
+ invalid_subdomains = %w(this_is_invalid in iamareallylongbucketnameiamareallylongbucketnameiamareallylongbu invalid- inval..id inval-.id inval.-id -invalid 192.168.10.2)
205
+ invalid_subdomains.each do |name|
206
+ assert_no_match Paperclip::Storage::Fog::AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX, name
207
+ end
208
+ end
209
+
210
+ should "provide an url in folder style" do
211
+ assert_match /^https:\/\/s3.amazonaws.com\/this_is_invalid\/avatars\/5k.png\?\d*$/, @dummy.avatar.url
212
+ end
213
+
214
+ end
215
+
189
216
  end
190
217
 
191
218
  end
@@ -1,15 +1,31 @@
1
1
  require './test/helper'
2
- require 'aws/s3'
2
+ require 'aws'
3
3
 
4
- unless ENV["S3_TEST_BUCKET"].blank?
4
+ unless ENV["S3_BUCKET"].blank?
5
5
  class S3LiveTest < Test::Unit::TestCase
6
+
7
+ context "Generating an expiring url on a nonexistant attachment" do
8
+ setup do
9
+ rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
10
+ :storage => :s3,
11
+ :bucket => ENV["S3_BUCKET"],
12
+ :path => ":class/:attachment/:id/:style.:extension",
13
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
14
+
15
+ @dummy = Dummy.new
16
+ end
17
+ should "return nil" do
18
+ assert_nil @dummy.avatar.expiring_url
19
+ end
20
+ end
21
+
6
22
  context "Using S3 for real, an attachment with S3 storage" do
7
23
  setup do
8
24
  rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
9
25
  :storage => :s3,
10
- :bucket => ENV["S3_TEST_BUCKET"],
26
+ :bucket => ENV["S3_BUCKET"],
11
27
  :path => ":class/:attachment/:id/:style.:extension",
12
- :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "s3.yml"))
28
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
13
29
 
14
30
  Dummy.delete_all
15
31
  @dummy = Dummy.new
@@ -21,7 +37,7 @@ unless ENV["S3_TEST_BUCKET"].blank?
21
37
 
22
38
  context "when assigned" do
23
39
  setup do
24
- @file = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', '5k.png'), 'rb')
40
+ @file = File.new(fixture_file('5k.png'), 'rb')
25
41
  @dummy.avatar = @file
26
42
  end
27
43
 
@@ -54,18 +70,16 @@ unless ENV["S3_TEST_BUCKET"].blank?
54
70
  context "An attachment that uses S3 for storage and has spaces in file name" do
55
71
  setup do
56
72
  rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
57
- :storage => :s3,
58
- :bucket => ENV["S3_TEST_BUCKET"],
59
- :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "s3.yml"))
73
+ :storage => :s3,
74
+ :bucket => ENV["S3_BUCKET"],
75
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
60
76
 
61
77
  Dummy.delete_all
62
78
  @dummy = Dummy.new
63
- @dummy.avatar = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', 'spaced file.png'), 'rb')
79
+ @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
64
80
  @dummy.save
65
81
  end
66
82
 
67
- teardown { @dummy.destroy }
68
-
69
83
  should "return an unescaped version for path" do
70
84
  assert_match /.+\/spaced file\.png/, @dummy.avatar.path
71
85
  end
@@ -75,13 +89,49 @@ unless ENV["S3_TEST_BUCKET"].blank?
75
89
  end
76
90
 
77
91
  should "be accessible" do
78
- assert_match /200 OK/, `curl -I #{@dummy.avatar.url}`
92
+ assert_success_response @dummy.avatar.url
79
93
  end
80
94
 
81
95
  should "be destoryable" do
82
96
  url = @dummy.avatar.url
83
97
  @dummy.destroy
84
- assert_match /404 Not Found/, `curl -I #{url}`
98
+ assert_not_found_response url
99
+ end
100
+ end
101
+
102
+ context "An attachment that uses S3 for storage and has a question mark in file name" do
103
+ setup do
104
+ rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
105
+ :storage => :s3,
106
+ :bucket => ENV["S3_BUCKET"],
107
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
108
+
109
+ Dummy.delete_all
110
+ @dummy = Dummy.new
111
+ @dummy.avatar = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', 'question?mark.png'), 'rb')
112
+ @dummy.save
113
+ end
114
+
115
+ should "return an unescaped version for path" do
116
+ assert_match /.+\/question\?mark\.png/, @dummy.avatar.path
117
+ end
118
+
119
+ should "return an escaped version for url" do
120
+ assert_match /.+\/question%3Fmark\.png/, @dummy.avatar.url
121
+ end
122
+
123
+ should "be accessible" do
124
+ assert_success_response @dummy.avatar.url
125
+ end
126
+
127
+ should "be accessible with an expiring url" do
128
+ assert_success_response @dummy.avatar.expiring_url
129
+ end
130
+
131
+ should "be destroyable" do
132
+ url = @dummy.avatar.url
133
+ @dummy.destroy
134
+ assert_not_found_response url
85
135
  end
86
136
  end
87
137
  end