carrierwave_direct 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+