carrierwave_direct 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +21 -0
  4. data/README.md +356 -0
  5. data/Rakefile +12 -0
  6. data/carrierwave_direct.gemspec +31 -0
  7. data/lib/carrierwave_direct.rb +44 -0
  8. data/lib/carrierwave_direct/action_view_extensions/form_helper.rb +36 -0
  9. data/lib/carrierwave_direct/form_builder.rb +17 -0
  10. data/lib/carrierwave_direct/locale/en.rb +20 -0
  11. data/lib/carrierwave_direct/locale/en.yml +7 -0
  12. data/lib/carrierwave_direct/mount.rb +38 -0
  13. data/lib/carrierwave_direct/orm/activerecord.rb +55 -0
  14. data/lib/carrierwave_direct/test/capybara_helpers.rb +58 -0
  15. data/lib/carrierwave_direct/test/helpers.rb +32 -0
  16. data/lib/carrierwave_direct/uploader.rb +142 -0
  17. data/lib/carrierwave_direct/uploader/configuration.rb +38 -0
  18. data/lib/carrierwave_direct/validations/active_model.rb +126 -0
  19. data/lib/carrierwave_direct/version.rb +6 -0
  20. data/spec/action_view_extensions/form_helper_spec.rb +28 -0
  21. data/spec/form_builder_spec.rb +59 -0
  22. data/spec/mount_spec.rb +57 -0
  23. data/spec/orm/activerecord_spec.rb +551 -0
  24. data/spec/spec_helper.rb +5 -0
  25. data/spec/support/carrier_wave_config.rb +9 -0
  26. data/spec/support/direct_uploader.rb +4 -0
  27. data/spec/support/form_builder_helpers.rb +36 -0
  28. data/spec/support/global_helpers.rb +6 -0
  29. data/spec/support/model_helpers.rb +80 -0
  30. data/spec/support/mounted_class.rb +6 -0
  31. data/spec/support/uploader_helpers.rb +8 -0
  32. data/spec/support/view_helpers.rb +45 -0
  33. data/spec/test/capybara_helpers_spec.rb +160 -0
  34. data/spec/test/helpers_spec.rb +105 -0
  35. data/spec/uploader_spec.rb +461 -0
  36. metadata +168 -0
@@ -0,0 +1,461 @@
1
+ require 'spec_helper'
2
+
3
+ describe CarrierWaveDirect::Uploader do
4
+ include UploaderHelpers
5
+ include ModelHelpers
6
+
7
+ SAMPLE_DATA = {
8
+ :path => "upload_dir/bliind.exe",
9
+ :key => "some key",
10
+ :guid => "guid",
11
+ :store_dir => "store_dir",
12
+ :extension_regexp => "(avi)",
13
+ :url => "http://example.com/some_url",
14
+ :expiration => 60,
15
+ :max_file_size => 10485760,
16
+ :file_url => "http://anyurl.com/any_path/video_dir/filename.avi",
17
+ :mounted_model_name => "Porno",
18
+ :mounted_as => :video,
19
+ :filename => "filename",
20
+ :extension => ".avi",
21
+ :version => :thumb
22
+ }
23
+
24
+ SAMPLE_DATA.merge!(
25
+ :stored_filename_base => "#{sample(:guid)}/#{sample(:filename)}"
26
+ )
27
+
28
+ SAMPLE_DATA.merge!(
29
+ :stored_filename => "#{sample(:stored_filename_base)}#{sample(:extension)}",
30
+ :stored_version_filename => "#{sample(:stored_filename_base)}_#{sample(:version)}#{sample(:extension)}"
31
+ )
32
+
33
+ SAMPLE_DATA.merge!(
34
+ :s3_key => "#{sample(:store_dir)}/#{sample(:stored_filename)}"
35
+ )
36
+
37
+ SAMPLE_DATA.freeze
38
+
39
+ let(:subject) { DirectUploader.new }
40
+ let(:mounted_model) { mock(sample(:mounted_model_name)) }
41
+ let(:mounted_subject) { DirectUploader.new(mounted_model, sample(:mounted_as)) }
42
+ let(:direct_subject) { DirectUploader.new }
43
+
44
+ describe ".upload_expiration" do
45
+ it "should be 10 hours" do
46
+ subject.class.upload_expiration.should == 36000
47
+ end
48
+ end
49
+
50
+ describe ".max_file_size" do
51
+ it "should be 5 MB" do
52
+ subject.class.max_file_size.should == 5242880
53
+ end
54
+ end
55
+
56
+ DirectUploader.fog_credentials.keys.each do |key|
57
+ describe "##{key}" do
58
+ it "should return the #{key.to_s.capitalize}" do
59
+ subject.send(key).should == DirectUploader.fog_credentials[key]
60
+ end
61
+
62
+ it "should not be nil" do
63
+ subject.send(key).should_not be_nil
64
+ end
65
+ end
66
+ end
67
+
68
+ it_should_have_accessor(:success_action_redirect)
69
+
70
+ describe "#key=" do
71
+ before { subject.key = sample(:key) }
72
+
73
+ it "should set the key" do
74
+ subject.key.should == sample(:key)
75
+ end
76
+
77
+ context "the versions keys" do
78
+ it "should == this subject's key" do
79
+ subject.versions.each do |name, version_subject|
80
+ version_subject.key.should == subject.key
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#key" do
87
+ context "where the key is not set" do
88
+ before do
89
+ mounted_subject.key = nil
90
+ end
91
+
92
+ it "should return '*/\#\{guid\}/${filename}'" do
93
+ mounted_subject.key.should =~ /#{GUID_REGEXP}\/\$\{filename\}$/
94
+ end
95
+
96
+ context "and #store_dir returns '#{sample(:store_dir)}'" do
97
+ before do
98
+ mounted_subject.stub(:store_dir).and_return(sample(:store_dir))
99
+ end
100
+
101
+ it "should return '#{sample(:store_dir)}/\#\{guid\}/${filename}'" do
102
+ mounted_subject.key.should =~ /^#{sample(:store_dir)}\/#{GUID_REGEXP}\/\$\{filename\}$/
103
+ end
104
+ end
105
+ end
106
+
107
+ context "where the key is set to '#{sample(:key)}'" do
108
+ before { subject.key = sample(:key) }
109
+
110
+ it "should return '#{sample(:key)}'" do
111
+ subject.key.should == sample(:key)
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "#url_scheme_white_list" do
117
+ it "should return nil" do
118
+ subject.url_scheme_white_list.should be_nil
119
+ end
120
+ end
121
+
122
+ describe "#key_regexp" do
123
+ it "should return a regexp" do
124
+ subject.key_regexp.should be_a(Regexp)
125
+ end
126
+
127
+ context "where #store_dir returns '#{sample(:store_dir)}'" do
128
+ before do
129
+ subject.stub(:store_dir).and_return(sample(:store_dir))
130
+ end
131
+
132
+ context "and #extension_regexp returns '#{sample(:extension_regexp)}'" do
133
+ before do
134
+ subject.stub(:extension_regexp).and_return(sample(:extension_regexp))
135
+ end
136
+
137
+ it "should return /\\A#{sample(:store_dir)}\\/#{GUID_REGEXP}\\/.+\\.#{sample(:extension_regexp)}\\z/" do
138
+ subject.key_regexp.should == /\A#{sample(:store_dir)}\/#{GUID_REGEXP}\/.+\.#{sample(:extension_regexp)}\z/
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ describe "#extension_regexp" do
145
+ shared_examples_for "a globally allowed file extension" do
146
+ it "should return '\\w+'" do
147
+ subject.extension_regexp.should == "\\w+"
148
+ end
149
+ end
150
+
151
+ it "should return a string" do
152
+ subject.extension_regexp.should be_a(String)
153
+ end
154
+
155
+ context "where #extension_white_list returns nil" do
156
+ before do
157
+ subject.stub(:extension_white_list).and_return(nil)
158
+ end
159
+
160
+ it_should_behave_like "a globally allowed file extension"
161
+ end
162
+
163
+ context "where #extension_white_list returns []" do
164
+ before do
165
+ subject.stub(:extension_white_list).and_return([])
166
+ end
167
+
168
+ it_should_behave_like "a globally allowed file extension"
169
+ end
170
+
171
+ context "where #extension_white_list returns ['exe', 'bmp']" do
172
+
173
+ before do
174
+ subject.stub(:extension_white_list).and_return(%w{exe bmp})
175
+ end
176
+
177
+ it "should return '(exe|bmp)'" do
178
+ subject.extension_regexp.should == "(exe|bmp)"
179
+ end
180
+ end
181
+ end
182
+
183
+ describe "#has_key?" do
184
+ context "a key has not been set" do
185
+
186
+ it "should return false" do
187
+ subject.should_not have_key
188
+ end
189
+ end
190
+
191
+ context "the key has been autogenerated" do
192
+ before { subject.key }
193
+
194
+ it "should return false" do
195
+ subject.should_not have_key
196
+ end
197
+ end
198
+
199
+ context "the key has been set" do
200
+ before { subject.key = sample_key }
201
+
202
+ it "should return true" do
203
+ subject.should have_key
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "#direct_fog_url" do
209
+ it "should return the result from CarrierWave::Storage::Fog::File#public_url" do
210
+ subject.direct_fog_url.should == CarrierWave::Storage::Fog::File.new(
211
+ subject, nil, nil
212
+ ).public_url
213
+ end
214
+
215
+ context ":with_path => true" do
216
+ context "#key is set to '#{sample(:path)}'" do
217
+ before { subject.key = sample(:path) }
218
+
219
+ it "should return the full url with '/#{sample(:path)}' as the path" do
220
+ URI.parse(subject.direct_fog_url(:with_path => true)).path.should == "/#{sample(:path)}"
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ describe "#persisted?" do
227
+ it "should return false" do
228
+ subject.should_not be_persisted
229
+ end
230
+ end
231
+
232
+ describe "#filename" do
233
+ context "key is set to '#{sample(:s3_key)}'" do
234
+ before { mounted_subject.key = sample(:s3_key) }
235
+
236
+ it "should return '#{sample(:stored_filename)}'" do
237
+ mounted_subject.filename.should == sample(:stored_filename)
238
+ end
239
+ end
240
+
241
+ context "key is set to '#{sample(:key)}'" do
242
+ before { subject.key = sample(:key) }
243
+
244
+ it "should return '#{sample(:key)}'" do
245
+ subject.filename.should == sample(:key)
246
+ end
247
+ end
248
+
249
+ context "key is not set" do
250
+ context "but the model's remote #{sample(:mounted_as)} url is: '#{sample(:file_url)}'" do
251
+
252
+ before do
253
+ mounted_subject.model.stub(
254
+ "remote_#{mounted_subject.mounted_as}_url"
255
+ ).and_return(sample(:file_url))
256
+ end
257
+
258
+ it "should set the key to contain '#{File.basename(sample(:file_url))}'" do
259
+ mounted_subject.filename
260
+ mounted_subject.key.should =~ /#{Regexp.escape(File.basename(sample(:file_url)))}$/
261
+ end
262
+
263
+ it "should return a filename based off the key and remote url" do
264
+ filename = mounted_subject.filename
265
+ mounted_subject.key.should =~ /#{Regexp.escape(filename)}$/
266
+ end
267
+
268
+ # this ensures that the version subject keys are updated
269
+ # see spec for key= for more details
270
+ it "should set the key explicitly" do
271
+ mounted_subject.should_receive(:key=)
272
+ mounted_subject.filename
273
+ end
274
+ end
275
+
276
+ context "and the model's remote #{sample(:mounted_as)} url is blank" do
277
+ before do
278
+ mounted_model.stub(
279
+ "remote_#{mounted_subject.mounted_as}_url"
280
+ ).and_return nil
281
+ end
282
+
283
+ it "should return nil" do
284
+ mounted_subject.filename.should be_nil
285
+ end
286
+ end
287
+ end
288
+ end
289
+
290
+ describe "#acl" do
291
+ it "should return the sanitized s3 access policy" do
292
+ subject.acl.should == subject.s3_access_policy.to_s.gsub("_", "-")
293
+ end
294
+ end
295
+
296
+ # http://aws.amazon.com/articles/1434?_encoding=UTF8
297
+ describe "#policy" do
298
+ def decoded_policy(options = {})
299
+ instance = options.delete(:subject) || subject
300
+ JSON.parse(Base64.decode64(instance.policy(options)))
301
+ end
302
+
303
+ it "should return Base64-encoded JSON" do
304
+ decoded_policy.should be_a(Hash)
305
+ end
306
+
307
+ it "should not contain any new lines" do
308
+ subject.policy.should_not include("\n")
309
+ end
310
+
311
+ context "expiration" do
312
+ def expiration(options = {})
313
+ decoded_policy(options)["expiration"]
314
+ end
315
+
316
+ # Stolen from rails
317
+ def string_to_time(str)
318
+ d = ::Date._parse(str, false).values_at(
319
+ :year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset
320
+ ).map { |arg| arg || 0 }
321
+ d[6] *= 1000000
322
+ Time.utc(*d[0..6]) - d[7]
323
+ end
324
+
325
+
326
+ def have_expiration(expires_in = DirectUploader.upload_expiration)
327
+ eql(
328
+ string_to_time(
329
+ JSON.parse({
330
+ "expiry" => Time.now + expires_in
331
+ }.to_json)["expiry"]
332
+ )
333
+ )
334
+ end
335
+
336
+ it "should be #{DirectUploader.upload_expiration / 3600} hours from now" do
337
+ Timecop.freeze(Time.now) do
338
+ string_to_time(expiration).should have_expiration
339
+ end
340
+ end
341
+
342
+ it "should be #{sample(:expiration) / 60 } minutes from now when passing {:expiration => #{sample(:expiration)}}" do
343
+ Timecop.freeze(Time.now) do
344
+ string_to_time(expiration(:expiration => sample(:expiration))).should have_expiration(sample(:expiration))
345
+ end
346
+ end
347
+ end
348
+
349
+ context "conditions" do
350
+ def conditions(options = {})
351
+ decoded_policy(options)["conditions"]
352
+ end
353
+
354
+ def have_condition(field, value = nil)
355
+ field.is_a?(Hash) ? include(field) : include(["starts-with", "$#{field}", value.to_s])
356
+ end
357
+
358
+ context "should include" do
359
+ # Rails form builder conditions
360
+ it "'utf8'" do
361
+ conditions.should have_condition(:utf8)
362
+ end
363
+
364
+ # S3 conditions
365
+ it "'key'" do
366
+ mounted_subject.stub(:store_dir).and_return(sample(:s3_key))
367
+ mounted_subject.key
368
+ conditions(
369
+ :subject => mounted_subject
370
+ ).should have_condition(:key, sample(:s3_key))
371
+ end
372
+
373
+ it "'bucket'" do
374
+ conditions.should have_condition("bucket" => subject.fog_directory)
375
+ end
376
+
377
+ it "'acl'" do
378
+ conditions.should have_condition("acl" => subject.acl)
379
+ end
380
+
381
+ it "'success_action_redirect'" do
382
+ subject.success_action_redirect = "http://example.com/some_url"
383
+ conditions.should have_condition("success_action_redirect" => "http://example.com/some_url")
384
+ end
385
+
386
+ context "'content-length-range of'" do
387
+
388
+ def have_content_length_range(max_file_size = DirectUploader.max_file_size)
389
+ include(["content-length-range", 1, max_file_size])
390
+ end
391
+
392
+ it "#{DirectUploader.max_file_size} bytes" do
393
+ conditions.should have_content_length_range
394
+ end
395
+
396
+ it "#{sample(:max_file_size)} bytes when passing {:max_file_size => #{sample(:max_file_size)}}" do
397
+ conditions(
398
+ :max_file_size => sample(:max_file_size)
399
+ ).should have_content_length_range(sample(:max_file_size))
400
+ end
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ describe "#signature" do
407
+ it "should not contain any new lines" do
408
+ subject.signature.should_not include("\n")
409
+ end
410
+
411
+ it "should return a base64 encoded 'sha1' hash of the secret key and policy document" do
412
+ Base64.decode64(subject.signature).should == OpenSSL::HMAC.digest(
413
+ OpenSSL::Digest::Digest.new('sha1'),
414
+ subject.aws_secret_access_key, subject.policy
415
+ )
416
+ end
417
+ end
418
+
419
+ # note that 'video' is hardcoded into the MountedClass support file
420
+ # so changing the sample will cause the tests to fail
421
+ context "a class has a '#{sample(:mounted_as)}' mounted" do
422
+ describe "#{sample(:mounted_as).capitalize}Uploader" do
423
+ describe "##{sample(:mounted_as)}" do
424
+ it "should be defined" do
425
+ direct_subject.should be_respond_to(sample(:mounted_as))
426
+ end
427
+
428
+ it "should return itself" do
429
+ direct_subject.send(sample(:mounted_as)).should == direct_subject
430
+ end
431
+ end
432
+
433
+ context "has a '#{sample(:version)}' version" do
434
+ let(:video_subject) { MountedClass.new.video }
435
+
436
+ before do
437
+ DirectUploader.version(sample(:version))
438
+ end
439
+
440
+ context "and the key is '#{sample(:s3_key)}'" do
441
+ before do
442
+ video_subject.key = sample(:s3_key)
443
+ end
444
+
445
+ context "the store path" do
446
+ let(:store_path) { video_subject.send(sample(:version)).store_path }
447
+
448
+ it "should be like '#{sample(:stored_version_filename)}'" do
449
+ store_path.should =~ /#{sample(:stored_version_filename)}$/
450
+ end
451
+
452
+ it "should not be like '#{sample(:version)}_#{sample(:stored_filename_base)}'" do
453
+ store_path.should_not =~ /#{sample(:version)}_#{sample(:stored_filename_base)}/
454
+ end
455
+ end
456
+ end
457
+ end
458
+ end
459
+ end
460
+ end
461
+