cloudfuji_paperclip 2.4.6 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +9 -6
  3. data/Appraisals +6 -6
  4. data/CONTRIBUTING.md +34 -2
  5. data/Gemfile +2 -0
  6. data/NEWS +90 -0
  7. data/README.md +62 -29
  8. data/RUNNING_TESTS.md +4 -0
  9. data/Rakefile +7 -2
  10. data/UPGRADING +14 -0
  11. data/features/basic_integration.feature +11 -7
  12. data/features/rake_tasks.feature +1 -1
  13. data/features/step_definitions/attachment_steps.rb +11 -2
  14. data/features/step_definitions/rails_steps.rb +17 -79
  15. data/features/support/env.rb +3 -0
  16. data/features/support/fakeweb.rb +7 -0
  17. data/features/support/file_helpers.rb +24 -0
  18. data/features/support/rails.rb +3 -3
  19. data/gemfiles/{rails3_1.gemfile → 3.0.gemfile} +3 -1
  20. data/gemfiles/{rails2.gemfile → 3.1.gemfile} +3 -1
  21. data/gemfiles/{rails3.gemfile → 3.2.gemfile} +3 -1
  22. data/images.rake +21 -0
  23. data/lib/cloudfuji_paperclip.rb +1 -0
  24. data/lib/generators/paperclip/paperclip_generator.rb +1 -2
  25. data/lib/paperclip.rb +54 -319
  26. data/lib/paperclip/attachment.rb +86 -107
  27. data/lib/paperclip/attachment_options.rb +9 -0
  28. data/lib/paperclip/callbacks.rb +30 -0
  29. data/lib/paperclip/errors.rb +27 -0
  30. data/lib/paperclip/geometry.rb +6 -4
  31. data/lib/paperclip/glue.rb +23 -0
  32. data/lib/paperclip/helpers.rb +71 -0
  33. data/lib/paperclip/instance_methods.rb +35 -0
  34. data/lib/paperclip/interpolations.rb +4 -4
  35. data/lib/paperclip/io_adapters/attachment_adapter.rb +69 -0
  36. data/lib/paperclip/io_adapters/file_adapter.rb +81 -0
  37. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  38. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  39. data/lib/paperclip/io_adapters/registry.rb +32 -0
  40. data/lib/paperclip/io_adapters/stringio_adapter.rb +64 -0
  41. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +63 -0
  42. data/lib/paperclip/locales/en.yml +17 -0
  43. data/lib/paperclip/logger.rb +21 -0
  44. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +5 -5
  45. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +7 -7
  46. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +11 -11
  47. data/lib/paperclip/missing_attachment_styles.rb +6 -9
  48. data/lib/paperclip/processor.rb +32 -17
  49. data/lib/paperclip/railtie.rb +13 -17
  50. data/lib/paperclip/storage/filesystem.rb +4 -13
  51. data/lib/paperclip/storage/fog.rb +33 -24
  52. data/lib/paperclip/storage/s3.rb +36 -28
  53. data/lib/paperclip/tempfile.rb +41 -0
  54. data/lib/paperclip/thumbnail.rb +2 -3
  55. data/lib/paperclip/validators.rb +45 -0
  56. data/lib/paperclip/validators/attachment_content_type_validator.rb +54 -0
  57. data/lib/paperclip/validators/attachment_presence_validator.rb +26 -0
  58. data/lib/paperclip/validators/attachment_size_validator.rb +102 -0
  59. data/lib/paperclip/version.rb +1 -1
  60. data/lib/tasks/paperclip.rake +4 -12
  61. data/paperclip.gemspec +15 -5
  62. data/test/adapter_registry_test.rb +32 -0
  63. data/test/attachment_adapter_test.rb +51 -0
  64. data/test/attachment_options_test.rb +27 -0
  65. data/test/attachment_test.rb +130 -46
  66. data/test/file_adapter_test.rb +88 -0
  67. data/test/generator_test.rb +78 -0
  68. data/test/geometry_test.rb +5 -5
  69. data/test/helper.rb +21 -22
  70. data/test/identity_adapter_test.rb +8 -0
  71. data/test/integration_test.rb +55 -102
  72. data/test/interpolations_test.rb +15 -5
  73. data/test/matchers/validate_attachment_content_type_matcher_test.rb +23 -0
  74. data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
  75. data/test/matchers/validate_attachment_size_matcher_test.rb +37 -2
  76. data/test/nil_adapter_test.rb +25 -0
  77. data/test/paperclip_missing_attachment_styles_test.rb +16 -0
  78. data/test/paperclip_test.rb +34 -183
  79. data/test/storage/filesystem_test.rb +27 -27
  80. data/test/storage/fog_test.rb +68 -12
  81. data/test/storage/s3_live_test.rb +79 -38
  82. data/test/storage/s3_test.rb +204 -34
  83. data/test/stringio_adapter_test.rb +42 -0
  84. data/test/thumbnail_test.rb +29 -8
  85. data/test/uploaded_file_adapter_test.rb +98 -0
  86. data/test/url_generator_test.rb +8 -8
  87. data/test/validators/attachment_content_type_validator_test.rb +192 -0
  88. data/test/validators/attachment_presence_validator_test.rb +85 -0
  89. data/test/validators/attachment_size_validator_test.rb +207 -0
  90. data/test/validators_test.rb +25 -0
  91. metadata +166 -59
  92. data/generators/paperclip/USAGE +0 -5
  93. data/generators/paperclip/paperclip_generator.rb +0 -27
  94. data/generators/paperclip/templates/paperclip_migration.rb.erb +0 -19
  95. data/init.rb +0 -4
  96. data/lib/paperclip/callback_compatibility.rb +0 -61
  97. data/lib/paperclip/iostream.rb +0 -45
  98. data/lib/paperclip/upfile.rb +0 -62
  99. data/rails/init.rb +0 -2
  100. data/test/.gitignore +0 -1
  101. data/test/fixtures/question?mark.png +0 -0
  102. data/test/iostream_test.rb +0 -71
  103. data/test/upfile_test.rb +0 -53
@@ -2,33 +2,30 @@ require './test/helper'
2
2
 
3
3
  class FileSystemTest < Test::Unit::TestCase
4
4
  context "Filesystem" do
5
- setup do
6
- rebuild_model :styles => { :thumbnail => "25x25#" }
7
- @dummy = Dummy.create!
8
-
9
- @dummy.avatar = File.open(fixture_file('5k.png'))
10
- end
5
+ context "normal file" do
6
+ setup do
7
+ rebuild_model :styles => { :thumbnail => "25x25#" }
8
+ @dummy = Dummy.create!
11
9
 
12
- should "allow file assignment" do
13
- assert @dummy.save
14
- end
10
+ @file = File.open(fixture_file('5k.png'))
11
+ @dummy.avatar = @file
12
+ end
15
13
 
16
- should "store the original" do
17
- @dummy.save
18
- assert File.exists?(@dummy.avatar.path)
19
- end
14
+ teardown { @file.close }
20
15
 
21
- should "store the thumbnail" do
22
- @dummy.save
23
- assert File.exists?(@dummy.avatar.path(:thumbnail))
24
- end
16
+ should "allow file assignment" do
17
+ assert @dummy.save
18
+ end
25
19
 
26
- should "clean up file objects" do
27
- File.stubs(:exist?).returns(true)
28
- Paperclip::Tempfile.any_instance.expects(:close).at_least_once()
29
- Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once()
20
+ should "store the original" do
21
+ @dummy.save
22
+ assert File.exists?(@dummy.avatar.path)
23
+ end
30
24
 
31
- @dummy.save!
25
+ should "store the thumbnail" do
26
+ @dummy.save
27
+ assert File.exists?(@dummy.avatar.path(:thumbnail))
28
+ end
32
29
  end
33
30
 
34
31
  context "with file that has space in file name" do
@@ -36,20 +33,23 @@ class FileSystemTest < Test::Unit::TestCase
36
33
  rebuild_model :styles => { :thumbnail => "25x25#" }
37
34
  @dummy = Dummy.create!
38
35
 
39
- @dummy.avatar = File.open(fixture_file('spaced file.png'))
36
+ @file = File.open(fixture_file('spaced file.png'))
37
+ @dummy.avatar = @file
40
38
  @dummy.save
41
39
  end
42
40
 
41
+ teardown { @file.close }
42
+
43
43
  should "store the file" do
44
44
  assert File.exists?(@dummy.avatar.path)
45
45
  end
46
46
 
47
- should "store the path unescaped" do
48
- assert_match /\/spaced file\.png/, @dummy.avatar.path
47
+ should "return a replaced version for path" do
48
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.path
49
49
  end
50
50
 
51
- should "return an escaped version of URL" do
52
- assert_match /\/spaced%20file\.png/, @dummy.avatar.url
51
+ should "return a replaced version for url" do
52
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.url
53
53
  end
54
54
  end
55
55
  end
@@ -5,7 +5,6 @@ Fog.mock!
5
5
 
6
6
  class FogTest < Test::Unit::TestCase
7
7
  context "" do
8
-
9
8
  context "with credentials provided in a path string" do
10
9
  setup do
11
10
  rebuild_model :styles => { :medium => "300x300>", :thumb => "100x100>" },
@@ -13,10 +12,13 @@ class FogTest < Test::Unit::TestCase
13
12
  :url => '/:attachment/:filename',
14
13
  :fog_directory => "paperclip",
15
14
  :fog_credentials => fixture_file('fog.yml')
15
+ @file = File.new(fixture_file('5k.png'), 'rb')
16
16
  @dummy = Dummy.new
17
- @dummy.avatar = File.new(fixture_file('5k.png'), 'rb')
17
+ @dummy.avatar = @file
18
18
  end
19
19
 
20
+ teardown { @file.close }
21
+
20
22
  should "have the proper information loading credentials from a file" do
21
23
  assert_equal @dummy.avatar.fog_credentials[:provider], 'AWS'
22
24
  end
@@ -29,10 +31,13 @@ class FogTest < Test::Unit::TestCase
29
31
  :url => '/:attachment/:filename',
30
32
  :fog_directory => "paperclip",
31
33
  :fog_credentials => File.open(fixture_file('fog.yml'))
34
+ @file = File.new(fixture_file('5k.png'), 'rb')
32
35
  @dummy = Dummy.new
33
- @dummy.avatar = File.new(fixture_file('5k.png'), 'rb')
36
+ @dummy.avatar = @file
34
37
  end
35
38
 
39
+ teardown { @file.close }
40
+
36
41
  should "have the proper information loading credentials from a file" do
37
42
  assert_equal @dummy.avatar.fog_credentials[:provider], 'AWS'
38
43
  end
@@ -49,21 +54,17 @@ class FogTest < Test::Unit::TestCase
49
54
  :aws_access_key_id => 'AWS_ID',
50
55
  :aws_secret_access_key => 'AWS_SECRET'
51
56
  }
57
+ @file = File.new(fixture_file('5k.png'), 'rb')
52
58
  @dummy = Dummy.new
53
- @dummy.avatar = File.new(fixture_file('5k.png'), 'rb')
59
+ @dummy.avatar = @file
54
60
  end
61
+
62
+ teardown { @file.close }
63
+
55
64
  should "be able to interpolate the path without blowing up" do
56
65
  assert_equal File.expand_path(File.join(File.dirname(__FILE__), "../../public/avatars/5k.png")),
57
66
  @dummy.avatar.path
58
67
  end
59
-
60
- should "clean up file objects" do
61
- File.stubs(:exist?).returns(true)
62
- Paperclip::Tempfile.any_instance.expects(:close).at_least_once()
63
- Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once()
64
-
65
- @dummy.save!
66
- end
67
68
  end
68
69
 
69
70
  setup do
@@ -110,6 +111,13 @@ class FogTest < Test::Unit::TestCase
110
111
  directory.destroy
111
112
  end
112
113
 
114
+ should "pass the content type to the Fog::Storage::AWS::Files instance" do
115
+ Fog::Storage::AWS::Files.any_instance.expects(:create).with do |hash|
116
+ hash[:content_type]
117
+ end
118
+ @dummy.save
119
+ end
120
+
113
121
  context "without a bucket" do
114
122
  setup do
115
123
  @connection.directories.get(@fog_directory).destroy
@@ -213,6 +221,54 @@ class FogTest < Test::Unit::TestCase
213
221
 
214
222
  end
215
223
 
224
+ context "with a proc for a bucket name evaluating a model method" do
225
+ setup do
226
+ @dynamic_fog_directory = 'dynamicpaperclip'
227
+ rebuild_model(@options.merge(:fog_directory => lambda { |attachment| attachment.instance.bucket_name }))
228
+ @dummy = Dummy.new
229
+ @dummy.stubs(:bucket_name).returns(@dynamic_fog_directory)
230
+ @dummy.avatar = @file
231
+ @dummy.save
232
+ end
233
+
234
+ should "have created the bucket" do
235
+ assert @connection.directories.get(@dynamic_fog_directory).inspect
236
+ end
237
+
238
+ end
239
+
240
+ context "with a proc for the fog_host evaluating a model method" do
241
+ setup do
242
+ rebuild_model(@options.merge(:fog_host => lambda { |attachment| attachment.instance.fog_host }))
243
+ @dummy = Dummy.new
244
+ @dummy.stubs(:fog_host).returns('http://dynamicfoghost.com')
245
+ @dummy.avatar = @file
246
+ @dummy.save
247
+ end
248
+
249
+ should "provide a public url" do
250
+ assert_match /http:\/\/dynamicfoghost\.com/, @dummy.avatar.url
251
+ end
252
+ end
253
+
254
+ context "with a proc for the fog_credentials evaluating a model method" do
255
+ setup do
256
+ @dynamic_fog_credentials = {
257
+ :provider => 'AWS',
258
+ :aws_access_key_id => 'DYNAMIC_ID',
259
+ :aws_secret_access_key => 'DYNAMIC_SECRET'
260
+ }
261
+ rebuild_model(@options.merge(:fog_credentials => lambda { |attachment| attachment.instance.fog_credentials }))
262
+ @dummy = Dummy.new
263
+ @dummy.stubs(:fog_credentials).returns(@dynamic_fog_credentials)
264
+ @dummy.avatar = @file
265
+ @dummy.save
266
+ end
267
+
268
+ should "provide a public url" do
269
+ assert_equal @dummy.avatar.fog_credentials, @dynamic_fog_credentials
270
+ end
271
+ end
216
272
  end
217
273
 
218
274
  end
@@ -3,17 +3,53 @@ require 'aws'
3
3
 
4
4
  unless ENV["S3_BUCKET"].blank?
5
5
  class S3LiveTest < Test::Unit::TestCase
6
+ context "when assigning an S3 attachment directly to another model" do
7
+ setup do
8
+ @s3_credentials = File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
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 => @s3_credentials
14
+
15
+ @file = File.new(fixture_file("5k.png"))
16
+ end
17
+
18
+ should "not raise any error" do
19
+ @attachment = Dummy.new.avatar
20
+ @attachment.assign(@file)
21
+ @attachment.save
22
+
23
+ @attachment2 = Dummy.new.avatar
24
+ @attachment2.assign(@file)
25
+ @attachment2.save
26
+ end
27
+
28
+ should "allow assignment from another S3 object" do
29
+ @attachment = Dummy.new.avatar
30
+ @attachment.assign(@file)
31
+ @attachment.save
32
+
33
+ @attachment2 = Dummy.new.avatar
34
+ @attachment2.assign(@attachment)
35
+ @attachment2.save
36
+ end
37
+
38
+ teardown { [@s3_credentials, @file].each(&:close) }
39
+ end
6
40
 
7
41
  context "Generating an expiring url on a nonexistant attachment" do
8
42
  setup do
43
+ @s3_credentials = File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
9
44
  rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
10
45
  :storage => :s3,
11
46
  :bucket => ENV["S3_BUCKET"],
12
47
  :path => ":class/:attachment/:id/:style.:extension",
13
- :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
48
+ :s3_credentials => @s3_credentials
14
49
 
15
50
  @dummy = Dummy.new
16
51
  end
52
+
17
53
  should "return nil" do
18
54
  assert_nil @dummy.avatar.expiring_url
19
55
  end
@@ -21,16 +57,19 @@ unless ENV["S3_BUCKET"].blank?
21
57
 
22
58
  context "Using S3 for real, an attachment with S3 storage" do
23
59
  setup do
60
+ @s3_credentials = File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
24
61
  rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
25
62
  :storage => :s3,
26
63
  :bucket => ENV["S3_BUCKET"],
27
64
  :path => ":class/:attachment/:id/:style.:extension",
28
- :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
65
+ :s3_credentials => @s3_credentials
29
66
 
30
67
  Dummy.delete_all
31
68
  @dummy = Dummy.new
32
69
  end
33
70
 
71
+ teardown { @s3_credentials.close }
72
+
34
73
  should "be extended by the S3 module" do
35
74
  assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
36
75
  end
@@ -46,10 +85,6 @@ unless ENV["S3_BUCKET"].blank?
46
85
  @dummy.destroy
47
86
  end
48
87
 
49
- should "still return a Tempfile when sent #to_file" do
50
- assert_equal Paperclip::Tempfile, @dummy.avatar.to_file.class
51
- end
52
-
53
88
  context "and saved" do
54
89
  setup do
55
90
  @dummy.save
@@ -58,80 +93,86 @@ unless ENV["S3_BUCKET"].blank?
58
93
  should "be on S3" do
59
94
  assert true
60
95
  end
61
-
62
- should "generate a tempfile with the right name" do
63
- file = @dummy.avatar.to_file
64
- assert_match /^original.*\.png$/, File.basename(file.path)
65
- end
66
96
  end
67
97
  end
68
98
  end
69
99
 
70
100
  context "An attachment that uses S3 for storage and has spaces in file name" do
71
101
  setup do
102
+ @s3_credentials = File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
72
103
  rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
73
104
  :storage => :s3,
74
105
  :bucket => ENV["S3_BUCKET"],
75
- :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
106
+ :s3_credentials => @s3_credentials
76
107
 
77
108
  Dummy.delete_all
109
+ @file = File.new(fixture_file('spaced file.png'), 'rb')
78
110
  @dummy = Dummy.new
79
- @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
111
+ @dummy.avatar = @file
80
112
  @dummy.save
81
113
  end
82
114
 
83
- should "return an unescaped version for path" do
84
- assert_match /.+\/spaced file\.png/, @dummy.avatar.path
115
+ teardown { @s3_credentials.close }
116
+
117
+ should "return a replaced version for path" do
118
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.path
85
119
  end
86
120
 
87
- should "return an escaped version for url" do
88
- assert_match /.+\/spaced%20file\.png/, @dummy.avatar.url
121
+ should "return a replaced version for url" do
122
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.url
89
123
  end
90
124
 
91
125
  should "be accessible" do
92
126
  assert_success_response @dummy.avatar.url
93
127
  end
94
128
 
95
- should "be destoryable" do
129
+ should "be reprocessable" do
130
+ assert @dummy.avatar.reprocess!
131
+ end
132
+
133
+ should "be destroyable" do
96
134
  url = @dummy.avatar.url
97
135
  @dummy.destroy
98
136
  assert_not_found_response url
99
137
  end
100
138
  end
101
139
 
102
- context "An attachment that uses S3 for storage and has a question mark in file name" do
140
+ context "An attachment that uses S3 for storage and uses AES256 encryption" do
103
141
  setup do
142
+ @s3_credentials = File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
104
143
  rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
105
144
  :storage => :s3,
106
145
  :bucket => ENV["S3_BUCKET"],
107
- :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
146
+ :path => ":class/:attachment/:id/:style.:extension",
147
+ :s3_credentials => @s3_credentials,
148
+ :s3_server_side_encryption => :aes256
108
149
 
109
150
  Dummy.delete_all
110
151
  @dummy = Dummy.new
111
- @dummy.avatar = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', 'question?mark.png'), 'rb')
112
- @dummy.save
113
152
  end
114
153
 
115
- should "return an unescaped version for path" do
116
- assert_match /.+\/question\?mark\.png/, @dummy.avatar.path
117
- end
154
+ teardown { @s3_credentials.close }
118
155
 
119
- should "return an escaped version for url" do
120
- assert_match /.+\/question%3Fmark\.png/, @dummy.avatar.url
121
- end
156
+ context "when assigned" do
157
+ setup do
158
+ @file = File.new(fixture_file('5k.png'), 'rb')
159
+ @dummy.avatar = @file
160
+ end
122
161
 
123
- should "be accessible" do
124
- assert_success_response @dummy.avatar.url
125
- end
162
+ teardown do
163
+ @file.close
164
+ @dummy.destroy
165
+ end
126
166
 
127
- should "be accessible with an expiring url" do
128
- assert_success_response @dummy.avatar.expiring_url
129
- end
167
+ context "and saved" do
168
+ setup do
169
+ @dummy.save
170
+ end
130
171
 
131
- should "be destroyable" do
132
- url = @dummy.avatar.url
133
- @dummy.destroy
134
- assert_not_found_response url
172
+ should "be encrypted on S3" do
173
+ assert @dummy.avatar.s3_object.server_side_encryption == :aes256
174
+ end
175
+ end
135
176
  end
136
177
  end
137
178
  end
@@ -88,7 +88,6 @@ class S3Test < Test::Unit::TestCase
88
88
 
89
89
  setup do
90
90
  rebuild_model :storage => :s3,
91
- #:bucket => "testing", # intentionally left out
92
91
  :http_proxy => @proxy_settings,
93
92
  :s3_credentials => {:not => :important}
94
93
 
@@ -187,8 +186,10 @@ class S3Test < Test::Unit::TestCase
187
186
  'secret_access_key' => "54321"
188
187
  }
189
188
 
190
- @dummy = Dummy.new
191
- @dummy.avatar = File.new(fixture_file('5k.png'), 'rb')
189
+ File.open(fixture_file('5k.png'), 'rb') do |file|
190
+ @dummy = Dummy.new
191
+ @dummy.avatar = file
192
+ end
192
193
  end
193
194
 
194
195
  should "return a url containing the correct original file mime type" do
@@ -218,16 +219,44 @@ class S3Test < Test::Unit::TestCase
218
219
  'secret_access_key' => "54321"
219
220
  }
220
221
 
222
+ File.open(fixture_file('spaced file.png'), 'rb') do |file|
223
+ @dummy = Dummy.new
224
+ @dummy.avatar = file
225
+ end
226
+ end
227
+
228
+ should "return a replaced version for path" do
229
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.path
230
+ end
231
+
232
+ should "return a replaced version for url" do
233
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.url
234
+ end
235
+ end
236
+
237
+ context "An attachment that uses S3 for storage and has a question mark in file name" do
238
+ setup do
239
+ rebuild_model :styles => { :large => ['500x500#', :jpg] },
240
+ :storage => :s3,
241
+ :bucket => "bucket",
242
+ :s3_credentials => {
243
+ 'access_key_id' => "12345",
244
+ 'secret_access_key' => "54321"
245
+ }
246
+
247
+ file = Paperclip.io_adapters.for(StringIO.new("."))
248
+ file.original_filename = "question?mark.png"
221
249
  @dummy = Dummy.new
222
- @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
250
+ @dummy.avatar = file
251
+ @dummy.save
223
252
  end
224
253
 
225
- should "return an unescaped version for path" do
226
- assert_match /.+\/spaced file\.png/, @dummy.avatar.path
254
+ should "return a replaced version for path" do
255
+ assert_match /.+\/question_mark\.png/, @dummy.avatar.path
227
256
  end
228
257
 
229
- should "return an escaped version for url" do
230
- assert_match /.+\/spaced%20file\.png/, @dummy.avatar.url
258
+ should "return a replaced version for url" do
259
+ assert_match /.+\/question_mark\.png/, @dummy.avatar.url
231
260
  end
232
261
  end
233
262
 
@@ -309,19 +338,30 @@ class S3Test < Test::Unit::TestCase
309
338
  should "return a relative URL for Rails to calculate assets host" do
310
339
  assert_match %r{^avatars/stringio\.txt}, @dummy.avatar.url
311
340
  end
341
+
312
342
  end
313
343
 
314
344
  context "Generating a secure url with an expiration" do
315
345
  setup do
316
- rebuild_model :storage => :s3,
317
- :s3_credentials => {
318
- :production => { :bucket => "prod_bucket" },
319
- :development => { :bucket => "dev_bucket" }
320
- },
321
- :s3_host_alias => "something.something.com",
322
- :s3_permissions => "private",
323
- :path => ":attachment/:basename.:extension",
324
- :url => ":s3_alias_url"
346
+ @build_model_with_options = lambda {|options|
347
+ base_options = {
348
+ :storage => :s3,
349
+ :s3_credentials => {
350
+ :production => { :bucket => "prod_bucket" },
351
+ :development => { :bucket => "dev_bucket" }
352
+ },
353
+ :s3_host_alias => "something.something.com",
354
+ :s3_permissions => "private",
355
+ :path => ":attachment/:basename.:extension",
356
+ :url => ":s3_alias_url"
357
+ }
358
+
359
+ rebuild_model base_options.merge(options)
360
+ }
361
+ end
362
+
363
+ should "use default options" do
364
+ @build_model_with_options[{}]
325
365
 
326
366
  rails_env("production")
327
367
 
@@ -335,8 +375,40 @@ class S3Test < Test::Unit::TestCase
335
375
  @dummy.avatar.expiring_url
336
376
  end
337
377
 
338
- should "should succeed" do
339
- assert true
378
+ should "allow overriding s3_url_options" do
379
+ @build_model_with_options[:s3_url_options => { :response_content_disposition => "inline" }]
380
+
381
+ rails_env("production")
382
+
383
+ @dummy = Dummy.new
384
+ @dummy.avatar = StringIO.new(".")
385
+
386
+ object = stub
387
+ @dummy.avatar.stubs(:s3_object).returns(object)
388
+ object.expects(:url_for).with(:read, :expires => 3600, :secure => true, :response_content_disposition => "inline")
389
+
390
+ @dummy.avatar.expiring_url
391
+ end
392
+
393
+ should "allow overriding s3_object options with a proc" do
394
+ @build_model_with_options[:s3_url_options => lambda {|attachment| { :response_content_type => attachment.avatar_content_type } }]
395
+
396
+ rails_env("production")
397
+
398
+ @dummy = Dummy.new
399
+
400
+ @file = StringIO.new(".")
401
+ @file.stubs(:original_filename).returns("5k.png\n\n")
402
+ @file.stubs(:content_type).returns("image/png\n\n")
403
+ @file.stubs(:to_tempfile).returns(@file)
404
+
405
+ @dummy.avatar = @file
406
+
407
+ object = stub
408
+ @dummy.avatar.stubs(:s3_object).returns(object)
409
+ object.expects(:url_for).with(:read, :expires => 3600, :secure => true, :response_content_type => "image/png")
410
+
411
+ @dummy.avatar.expiring_url
340
412
  end
341
413
  end
342
414
 
@@ -475,14 +547,6 @@ class S3Test < Test::Unit::TestCase
475
547
  end
476
548
  end
477
549
 
478
- should "delete tempfiles" do
479
- File.stubs(:exist?).returns(true)
480
- Paperclip::Tempfile.any_instance.expects(:close).at_least_once()
481
- Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once()
482
-
483
- @dummy.save!
484
- end
485
-
486
550
  context "and saved without a bucket" do
487
551
  setup do
488
552
  AWS::S3::BucketCollection.any_instance.expects(:create).with("testing")
@@ -510,6 +574,16 @@ class S3Test < Test::Unit::TestCase
510
574
  assert true
511
575
  end
512
576
  end
577
+
578
+ context 'that the file were missing' do
579
+ setup do
580
+ AWS::S3::S3Object.any_instance.stubs(:exists?).raises(AWS::Errors::Base)
581
+ end
582
+
583
+ should 'return false on exists?' do
584
+ assert !@dummy.avatar.exists?
585
+ end
586
+ end
513
587
  end
514
588
  end
515
589
 
@@ -528,6 +602,21 @@ class S3Test < Test::Unit::TestCase
528
602
  end
529
603
  end
530
604
 
605
+ context "An attachment with S3 storage and S3 credentials defined as a Proc" do
606
+ setup do
607
+ rebuild_model :storage => :s3,
608
+ :bucket => {:not => :important},
609
+ :s3_credentials => lambda { |attachment|
610
+ Hash['access_key_id' => "access#{attachment.instance.other}", 'secret_access_key' => "secret#{attachment.instance.other}"]
611
+ }
612
+ end
613
+
614
+ should "get the right credentials" do
615
+ assert "access1234", Dummy.new(:other => '1234').avatar.s3_credentials[:access_key_id]
616
+ assert "secret1234", Dummy.new(:other => '1234').avatar.s3_credentials[:secret_access_key]
617
+ end
618
+ end
619
+
531
620
  context "An attachment with S3 storage and specific s3 headers set" do
532
621
  setup do
533
622
  rebuild_model :storage => :s3,
@@ -684,6 +773,45 @@ class S3Test < Test::Unit::TestCase
684
773
  end
685
774
  end
686
775
 
776
+ context "An attachment with S3 storage and using AES256 encryption" do
777
+ setup do
778
+ rebuild_model :storage => :s3,
779
+ :bucket => "testing",
780
+ :path => ":attachment/:style/:basename.:extension",
781
+ :s3_credentials => {
782
+ 'access_key_id' => "12345",
783
+ 'secret_access_key' => "54321"
784
+ },
785
+ :s3_server_side_encryption => :aes256
786
+ end
787
+
788
+ context "when assigned" do
789
+ setup do
790
+ @file = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', '5k.png'), 'rb')
791
+ @dummy = Dummy.new
792
+ @dummy.avatar = @file
793
+ end
794
+
795
+ teardown { @file.close }
796
+
797
+ context "and saved" do
798
+ setup do
799
+ object = stub
800
+ @dummy.avatar.stubs(:s3_object).returns(object)
801
+ object.expects(:write).with(anything,
802
+ :content_type => "image/png",
803
+ :acl => :public_read,
804
+ :server_side_encryption => :aes256)
805
+ @dummy.save
806
+ end
807
+
808
+ should "succeed" do
809
+ assert true
810
+ end
811
+ end
812
+ end
813
+ end
814
+
687
815
  context "An attachment with S3 storage and storage class set using the :storage_class option" do
688
816
  setup do
689
817
  rebuild_model :storage => :s3,
@@ -921,13 +1049,6 @@ class S3Test < Test::Unit::TestCase
921
1049
 
922
1050
  context "and saved" do
923
1051
  setup do
924
- [:thumb, :original].each do |style|
925
- object = stub
926
- @dummy.avatar.stubs(:s3_object).with(style).returns(object)
927
- object.expects(:write).with(anything,
928
- :content_type => "image/png",
929
- :acl => style == :thumb ? :public_read : :private)
930
- end
931
1052
  @dummy.save
932
1053
  end
933
1054
 
@@ -940,4 +1061,53 @@ class S3Test < Test::Unit::TestCase
940
1061
 
941
1062
  end
942
1063
  end
1064
+
1065
+ context "An attachment with S3 storage and metadata set using a proc as headers" do
1066
+ setup do
1067
+ rebuild_model(
1068
+ :storage => :s3,
1069
+ :bucket => "testing",
1070
+ :path => ":attachment/:style/:basename.:extension",
1071
+ :styles => {
1072
+ :thumb => "80x80>"
1073
+ },
1074
+ :s3_credentials => {
1075
+ 'access_key_id' => "12345",
1076
+ 'secret_access_key' => "54321"
1077
+ },
1078
+ :s3_headers => lambda {|attachment|
1079
+ {'Content-Disposition' => "attachment; filename=\"#{attachment.name}\""}
1080
+ }
1081
+ )
1082
+ end
1083
+
1084
+ context "when assigned" do
1085
+ setup do
1086
+ @file = File.new(fixture_file('5k.png'), 'rb')
1087
+ @dummy = Dummy.new
1088
+ @dummy.stubs(:name => 'Custom Avatar Name.png')
1089
+ @dummy.avatar = @file
1090
+ end
1091
+
1092
+ teardown { @file.close }
1093
+
1094
+ context "and saved" do
1095
+ setup do
1096
+ [:thumb, :original].each do |style|
1097
+ object = stub
1098
+ @dummy.avatar.stubs(:s3_object).with(style).returns(object)
1099
+ object.expects(:write).with(anything,
1100
+ :content_type => "image/png",
1101
+ :acl => :public_read,
1102
+ :content_disposition => 'attachment; filename="Custom Avatar Name.png"')
1103
+ end
1104
+ @dummy.save
1105
+ end
1106
+
1107
+ should "succeed" do
1108
+ assert true
1109
+ end
1110
+ end
1111
+ end
1112
+ end
943
1113
  end