mongoid-direct-s3-upload 0.1.0

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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +250 -0
  4. data/Rakefile +31 -0
  5. data/app/assets/javascripts/s3_relay.coffee +118 -0
  6. data/app/assets/stylesheets/s3_relay.css +31 -0
  7. data/app/controllers/s3_relay/uploads_controller.rb +71 -0
  8. data/app/helpers/s3_relay/uploads_helper.rb +24 -0
  9. data/app/models/s3_relay/upload.rb +73 -0
  10. data/config/routes.rb +5 -0
  11. data/lib/s3_relay.rb +4 -0
  12. data/lib/s3_relay/base.rb +35 -0
  13. data/lib/s3_relay/engine.rb +16 -0
  14. data/lib/s3_relay/model.rb +51 -0
  15. data/lib/s3_relay/private_url.rb +37 -0
  16. data/lib/s3_relay/s3_relay.rb +4 -0
  17. data/lib/s3_relay/upload_presigner.rb +61 -0
  18. data/lib/s3_relay/version.rb +3 -0
  19. data/test/controllers/s3_relay/uploads_controller_test.rb +144 -0
  20. data/test/dummy/README.md +24 -0
  21. data/test/dummy/Rakefile +6 -0
  22. data/test/dummy/app/assets/config/manifest.js +3 -0
  23. data/test/dummy/app/assets/javascripts/application.js +15 -0
  24. data/test/dummy/app/assets/javascripts/cable.js +13 -0
  25. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  26. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  27. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  28. data/test/dummy/app/controllers/application_controller.rb +3 -0
  29. data/test/dummy/app/helpers/application_helper.rb +2 -0
  30. data/test/dummy/app/jobs/application_job.rb +2 -0
  31. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  32. data/test/dummy/app/models/application_record.rb +3 -0
  33. data/test/dummy/app/models/product.rb +6 -0
  34. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  35. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  36. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  37. data/test/dummy/bin/bundle +3 -0
  38. data/test/dummy/bin/rails +9 -0
  39. data/test/dummy/bin/rake +9 -0
  40. data/test/dummy/bin/setup +38 -0
  41. data/test/dummy/bin/spring +17 -0
  42. data/test/dummy/bin/update +29 -0
  43. data/test/dummy/bin/yarn +11 -0
  44. data/test/dummy/config.ru +5 -0
  45. data/test/dummy/config/application.rb +19 -0
  46. data/test/dummy/config/boot.rb +3 -0
  47. data/test/dummy/config/cable.yml +10 -0
  48. data/test/dummy/config/database.yml +22 -0
  49. data/test/dummy/config/environment.rb +5 -0
  50. data/test/dummy/config/environments/development.rb +54 -0
  51. data/test/dummy/config/environments/production.rb +91 -0
  52. data/test/dummy/config/environments/test.rb +42 -0
  53. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  54. data/test/dummy/config/initializers/assets.rb +14 -0
  55. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  57. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  58. data/test/dummy/config/initializers/inflections.rb +16 -0
  59. data/test/dummy/config/initializers/mime_types.rb +4 -0
  60. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  61. data/test/dummy/config/locales/en.yml +33 -0
  62. data/test/dummy/config/puma.rb +56 -0
  63. data/test/dummy/config/routes.rb +7 -0
  64. data/test/dummy/config/secrets.yml +32 -0
  65. data/test/dummy/config/spring.rb +6 -0
  66. data/test/dummy/db/migrate/20141021002149_create_products.rb +9 -0
  67. data/test/dummy/db/schema.rb +41 -0
  68. data/test/dummy/db/seeds.rb +7 -0
  69. data/test/dummy/package.json +5 -0
  70. data/test/dummy/public/404.html +67 -0
  71. data/test/dummy/public/422.html +67 -0
  72. data/test/dummy/public/500.html +66 -0
  73. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  74. data/test/dummy/public/apple-touch-icon.png +0 -0
  75. data/test/dummy/public/favicon.ico +0 -0
  76. data/test/dummy/public/robots.txt +1 -0
  77. data/test/dummy/test/application_system_test_case.rb +5 -0
  78. data/test/dummy/test/test_helper.rb +9 -0
  79. data/test/factories/products.rb +5 -0
  80. data/test/factories/uploads.rb +23 -0
  81. data/test/helpers/s3_relay/uploads_helper_test.rb +29 -0
  82. data/test/lib/s3_relay/model_test.rb +66 -0
  83. data/test/lib/s3_relay/private_url_test.rb +28 -0
  84. data/test/lib/s3_relay/upload_presigner_test.rb +38 -0
  85. data/test/models/s3_relay/upload_test.rb +142 -0
  86. data/test/support/database_cleaner.rb +14 -0
  87. data/test/test_helper.rb +29 -0
  88. metadata +356 -0
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
File without changes
@@ -0,0 +1 @@
1
+ # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
@@ -0,0 +1,5 @@
1
+ require "test_helper"
2
+
3
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4
+ driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5
+ end
@@ -0,0 +1,9 @@
1
+ require File.expand_path('../../config/environment', __FILE__)
2
+ require 'rails/test_help'
3
+
4
+ class ActiveSupport::TestCase
5
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
6
+ fixtures :all
7
+
8
+ # Add more helper methods to be used by all tests here...
9
+ end
@@ -0,0 +1,5 @@
1
+ FactoryGirl.define do
2
+ factory :product do
3
+ name "Flux Capacitor"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ FactoryGirl.define do
2
+ factory :upload, class: "S3Relay::Upload" do
3
+ uuid { SecureRandom.uuid }
4
+ end
5
+
6
+ factory :file_upload, parent: :upload do
7
+ filename "cat.png"
8
+ content_type "image/png"
9
+ upload_type "FileUpload"
10
+ end
11
+
12
+ factory :icon_upload, parent: :upload do
13
+ filename "cat.png"
14
+ content_type "image/png"
15
+ upload_type "IconUpload"
16
+ end
17
+
18
+ factory :photo_upload, parent: :upload do
19
+ filename "cat.png"
20
+ content_type "image/png"
21
+ upload_type "PhotoUpload"
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ require "test_helper"
2
+
3
+ describe S3Relay::UploadsHelper do
4
+ before { @product = FactoryGirl.create(:product) }
5
+
6
+ describe "#s3_relay_field" do
7
+ describe "without options" do
8
+ it do
9
+ s3_relay_field(@product, :photo_uploads)
10
+ .must_equal %Q{<div class="s3r-container" data-parent-type="product" data-parent-id="#{@product.id}" data-association="photo_uploads" data-disposition=\"inline\"><input type="file" name="file" id="file" class="s3r-field" /><table class="s3r-upload-list"></table></div>}
11
+ end
12
+ end
13
+
14
+ describe "with multiple: true" do
15
+ it do
16
+ s3_relay_field(@product, :photo_uploads, multiple: true)
17
+ .must_equal %Q{<div class="s3r-container" data-parent-type="product" data-parent-id="#{@product.id}" data-association="photo_uploads" data-disposition=\"inline\"><input type="file" name="file" id="file" multiple="multiple" class="s3r-field" /><table class="s3r-upload-list"></table></div>}
18
+ end
19
+ end
20
+
21
+ describe "with disposition: attachment" do
22
+ it do
23
+ s3_relay_field(@product, :photo_uploads, disposition: :attachment)
24
+ .must_equal %Q{<div class="s3r-container" data-parent-type="product" data-parent-id="#{@product.id}" data-association="photo_uploads" data-disposition=\"attachment\"><input type="file" name="file" id="file" disposition=\"attachment\" class="s3r-field" /><table class="s3r-upload-list"></table></div>}
25
+ end
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,66 @@
1
+ require "test_helper"
2
+
3
+ describe S3Relay::Model do
4
+ before do
5
+ @product = FactoryGirl.build(:product)
6
+ end
7
+
8
+ describe "#s3_relay" do
9
+
10
+ describe "associations" do
11
+ it "has_one" do
12
+ Product.reflect_on_association(:icon_upload).macro
13
+ .must_equal(:has_one)
14
+ end
15
+
16
+ it "has_many" do
17
+ Product.reflect_on_association(:photo_uploads).macro
18
+ .must_equal(:has_many)
19
+ end
20
+ end
21
+
22
+ describe "scopes" do
23
+ before do
24
+ @product.save
25
+ @icon = FactoryGirl.create(:icon_upload, parent: @product)
26
+ @photo_1 = FactoryGirl.create(:photo_upload, parent: @product)
27
+ @photo_2 = FactoryGirl.create(:photo_upload, parent: @product)
28
+ end
29
+
30
+ describe "has_one" do
31
+ it { @product.icon_upload.must_equal @icon }
32
+ end
33
+
34
+ describe "has_many" do
35
+ it do
36
+ @product.photo_uploads.pluck(:id).sort
37
+ .must_equal [@photo_1.id, @photo_2.id]
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "virtual attribute for UUID assignment" do
43
+ it { @product.must_respond_to :new_photo_uploads_uuids= }
44
+ end
45
+
46
+ describe "association method" do
47
+ before do
48
+ @product = FactoryGirl.create(:product)
49
+ @icon = FactoryGirl.create(:icon_upload)
50
+ @photo_1 = FactoryGirl.create(:photo_upload)
51
+ @photo_2 = FactoryGirl.create(:photo_upload)
52
+ end
53
+
54
+ describe "has_many" do
55
+ it do
56
+ @product.photo_uploads.must_equal []
57
+ @product.new_photo_uploads_uuids = [@photo_1.uuid, @photo_2.uuid]
58
+ @product.associate_photo_uploads
59
+ @product.photo_uploads.must_equal [@photo_1, @photo_2]
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,28 @@
1
+ require "test_helper"
2
+
3
+ describe S3Relay::PrivateUrl do
4
+ before do
5
+ S3Relay::PrivateUrl.any_instance.stubs(:access_key_id)
6
+ .returns("access-key-id")
7
+ S3Relay::PrivateUrl.any_instance.stubs(:secret_access_key)
8
+ .returns("secret-access-key")
9
+ S3Relay::PrivateUrl.any_instance.stubs(:region)
10
+ .returns("region")
11
+ S3Relay::PrivateUrl.any_instance.stubs(:bucket)
12
+ .returns("bucket")
13
+ S3Relay::PrivateUrl.any_instance.stubs(:acl)
14
+ .returns("acl")
15
+ end
16
+
17
+ describe "#generate" do
18
+ it do
19
+ uuid = "123-456-789"
20
+ file = "Crazy + c@t picture.png"
21
+ time = Time.parse("2014-01-01 12:00am")
22
+ url = S3Relay::PrivateUrl.new(uuid, file, expires: time).generate
23
+
24
+ url.must_equal "https://bucket.s3-region.amazonaws.com/123-456-789/Crazy%20%2B%20c@t%20picture.png?AWSAccessKeyId=access-key-id&Expires=1388563200&Signature=8Kp5NL77iycg4CFFwxboo905t%2Fs%3D"
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,38 @@
1
+ require "test_helper"
2
+
3
+ describe S3Relay::UploadPresigner do
4
+ before do
5
+ S3Relay::UploadPresigner.any_instance.stubs(:access_key_id)
6
+ .returns("access-key-id")
7
+ S3Relay::UploadPresigner.any_instance.stubs(:secret_access_key)
8
+ .returns("secret-access-key")
9
+ S3Relay::UploadPresigner.any_instance.stubs(:region)
10
+ .returns("region")
11
+ S3Relay::UploadPresigner.any_instance.stubs(:bucket)
12
+ .returns("bucket")
13
+ S3Relay::UploadPresigner.any_instance.stubs(:acl)
14
+ .returns("acl")
15
+ end
16
+
17
+ describe "#form_data" do
18
+ it do
19
+ uuid = SecureRandom.uuid
20
+ time = Time.parse("2014-12-01 12:00am")
21
+
22
+ S3Relay::UploadPresigner.any_instance.stubs(:uuid).returns(uuid)
23
+
24
+ data = S3Relay::UploadPresigner.new(expires: time).form_data
25
+
26
+ data["awsaccesskeyid"].must_equal "access-key-id"
27
+ data["x_amz_server_side_encryption"].must_equal "AES256"
28
+ data["key"].must_equal "#{uuid}/${filename}"
29
+ data["success_action_status"].must_equal "201"
30
+ data["acl"].must_equal "acl"
31
+ data["endpoint"].must_equal "https://bucket.s3-region.amazonaws.com"
32
+ data["policy"].length.must_equal 400 # TODO: Improve this
33
+ data["signature"].length.must_equal 28 # TODO: Improve this
34
+ data["uuid"].must_equal uuid
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,142 @@
1
+ require "test_helper"
2
+
3
+ describe S3Relay::Upload do
4
+ before do
5
+ @upload = FactoryGirl.build(:upload)
6
+ end
7
+
8
+ describe "associations" do
9
+ describe "parent" do
10
+ it do
11
+ S3Relay::Upload.reflect_on_association(:parent).macro
12
+ .must_equal :belongs_to
13
+ end
14
+ end
15
+ end
16
+
17
+ describe "validations" do
18
+ describe "uuid" do
19
+ it "validates presence" do
20
+ @upload.uuid = nil
21
+ @upload.valid?
22
+ @upload.errors[:uuid].must_include("can't be blank")
23
+ end
24
+
25
+ it "validates uniqueness" do
26
+ FactoryGirl.create(:file_upload, uuid: @upload.uuid)
27
+ @upload.valid?
28
+ @upload.errors[:uuid].must_include("has already been taken")
29
+ end
30
+ end
31
+
32
+ describe "upload_type" do
33
+ it "validates presence" do
34
+ @upload.valid?
35
+ @upload.errors[:upload_type].must_include("can't be blank")
36
+ end
37
+ end
38
+
39
+ describe "filename" do
40
+ it "validates presence" do
41
+ @upload.valid?
42
+ @upload.errors[:filename].must_include("can't be blank")
43
+ end
44
+ end
45
+
46
+ describe "content_type" do
47
+ it "validates presence" do
48
+ @upload.valid?
49
+ @upload.errors[:content_type].must_include("can't be blank")
50
+ end
51
+ end
52
+
53
+ describe "pending_at" do
54
+ it "validates presence" do
55
+ @upload.pending_at = nil
56
+ @upload.valid?
57
+ @upload.errors[:pending_at].must_include("can't be blank")
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "scopes" do
63
+ before do
64
+ @pending = FactoryGirl.build(:file_upload)
65
+ .tap { |u| u.save(validate: false) }
66
+ @imported = FactoryGirl.build(:upload, state: "imported")
67
+ .tap { |u| u.save(validate: false) }
68
+ end
69
+
70
+ describe "pending" do
71
+ it do
72
+ results = S3Relay::Upload.pending.all
73
+ results.must_include @pending
74
+ results.wont_include @imported
75
+ end
76
+ end
77
+
78
+ describe "imported" do
79
+ it do
80
+ results = S3Relay::Upload.imported.all
81
+ results.wont_include @pending
82
+ results.must_include @imported
83
+ end
84
+ end
85
+ end
86
+
87
+ describe "upon finalization" do
88
+ it do
89
+ @upload.state.must_equal "pending"
90
+ @upload.pending_at.wont_equal nil
91
+ end
92
+ end
93
+
94
+ describe "#pending?" do
95
+ it { @upload.pending?.must_equal true }
96
+ end
97
+
98
+ describe "#imported" do
99
+ it do
100
+ @upload.state = "imported"
101
+ @upload.imported?.must_equal true
102
+ end
103
+ end
104
+
105
+ describe "#mark_imported!" do
106
+ it do
107
+ @upload.mark_imported!
108
+ @upload.state.must_equal "imported"
109
+ @upload.imported_at.wont_equal nil
110
+ end
111
+ end
112
+
113
+ describe "#notify_parent" do
114
+ before { @upload = FactoryGirl.build(:file_upload) }
115
+
116
+ describe "when the parent is associated" do
117
+ it do
118
+ @product = FactoryGirl.create(:product)
119
+ @upload.parent = @product
120
+ @product.expects(:import_upload)
121
+ @upload.save
122
+ end
123
+ end
124
+ end
125
+
126
+ describe "#private_url" do
127
+ it do
128
+ uuid = SecureRandom.uuid
129
+ filename = "cat.png"
130
+ @upload.uuid = uuid
131
+ @upload.filename = filename
132
+
133
+ url = "url"
134
+ klass = stub
135
+ S3Relay::PrivateUrl.expects(:new).with(uuid, filename).returns(klass)
136
+ klass.expects(:generate).returns(url)
137
+
138
+ @upload.private_url.must_equal url
139
+ end
140
+ end
141
+
142
+ end
@@ -0,0 +1,14 @@
1
+ require "database_cleaner"
2
+
3
+ DatabaseCleaner.clean_with :truncation
4
+ DatabaseCleaner.strategy = :transaction
5
+
6
+ class MiniTest::Spec
7
+ before :each do
8
+ DatabaseCleaner.start
9
+ end
10
+
11
+ after :each do
12
+ DatabaseCleaner.clean
13
+ end
14
+ end