s3_relay 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +3 -19
  3. data/app/assets/javascripts/s3_relay.coffee +4 -4
  4. data/app/controllers/s3_relay/uploads_controller.rb +8 -2
  5. data/app/models/s3_relay/upload.rb +1 -1
  6. data/db/migrate/20141009000804_create_s3_relay_uploads.rb +1 -1
  7. data/lib/s3_relay/base.rb +1 -1
  8. data/lib/s3_relay/engine.rb +1 -1
  9. data/lib/s3_relay/private_url.rb +1 -1
  10. data/lib/s3_relay/version.rb +1 -1
  11. data/test/controllers/s3_relay/uploads_controller_test.rb +28 -22
  12. data/test/dummy/{README.rdoc → README.md} +1 -5
  13. data/test/dummy/Rakefile +1 -1
  14. data/test/dummy/app/assets/config/manifest.js +3 -0
  15. data/test/dummy/app/assets/javascripts/application.js +6 -4
  16. data/test/dummy/app/assets/javascripts/cable.js +13 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +5 -5
  18. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  19. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  20. data/test/dummy/app/controllers/application_controller.rb +0 -2
  21. data/test/dummy/app/jobs/application_job.rb +2 -0
  22. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  23. data/test/dummy/app/models/application_record.rb +3 -0
  24. data/test/dummy/app/models/product.rb +1 -1
  25. data/test/dummy/app/views/layouts/application.html.erb +9 -9
  26. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  27. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  28. data/test/dummy/bin/rails +6 -1
  29. data/test/dummy/bin/rake +5 -0
  30. data/test/dummy/bin/setup +38 -0
  31. data/test/dummy/bin/spring +17 -0
  32. data/test/dummy/bin/update +29 -0
  33. data/test/dummy/bin/yarn +11 -0
  34. data/test/dummy/config.ru +2 -1
  35. data/test/dummy/config/application.rb +7 -11
  36. data/test/dummy/config/boot.rb +2 -4
  37. data/test/dummy/config/cable.yml +10 -0
  38. data/test/dummy/config/environment.rb +1 -1
  39. data/test/dummy/config/environments/development.rb +24 -7
  40. data/test/dummy/config/environments/production.rb +36 -23
  41. data/test/dummy/config/environments/test.rb +6 -3
  42. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  43. data/test/dummy/config/initializers/assets.rb +8 -2
  44. data/test/dummy/config/initializers/cookies_serializer.rb +3 -1
  45. data/test/dummy/config/initializers/wrap_parameters.rb +2 -2
  46. data/test/dummy/config/locales/en.yml +10 -0
  47. data/test/dummy/config/puma.rb +56 -0
  48. data/test/dummy/config/secrets.yml +15 -5
  49. data/test/dummy/config/spring.rb +6 -0
  50. data/test/dummy/db/migrate/20141021002149_create_products.rb +1 -1
  51. data/test/dummy/db/schema.rb +13 -14
  52. data/test/dummy/db/seeds.rb +7 -0
  53. data/test/dummy/log/development.log +60 -20
  54. data/test/dummy/log/test.log +10958 -856
  55. data/test/dummy/package.json +5 -0
  56. data/test/dummy/public/404.html +6 -6
  57. data/test/dummy/public/422.html +6 -6
  58. data/test/dummy/public/500.html +6 -6
  59. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  60. data/test/dummy/public/apple-touch-icon.png +0 -0
  61. data/test/dummy/public/robots.txt +1 -0
  62. data/test/dummy/test/application_system_test_case.rb +5 -0
  63. data/test/dummy/test/test_helper.rb +9 -0
  64. data/test/factories/products.rb +2 -2
  65. data/test/factories/uploads.rb +14 -7
  66. data/test/helpers/s3_relay/uploads_helper_test.rb +7 -4
  67. data/test/lib/s3_relay/model_test.rb +15 -15
  68. data/test/lib/s3_relay/private_url_test.rb +1 -1
  69. data/test/lib/s3_relay/upload_presigner_test.rb +9 -9
  70. data/test/models/s3_relay/upload_test.rb +25 -24
  71. data/test/test_helper.rb +2 -4
  72. metadata +110 -72
  73. data/test/dummy/config/initializers/session_store.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6f7808fa908073f00317199da6e43b9821c7d827
4
- data.tar.gz: 896e7707ba3031b0a5074bf1322d8c62ffc715f0
2
+ SHA256:
3
+ metadata.gz: 10af82cfd6dbeef7aa3a3aae7938a708a09c2953a5133f03980d09e993182acc
4
+ data.tar.gz: 1aa52906c7727233f0c41563d35c5c05efc1d12ea0f7257c43d6f0ceed512911
5
5
  SHA512:
6
- metadata.gz: 65db04a3d352ff2dee8d812450eb20f2065c8cc1c727dc8c278dc914c0a08879038241d96e6371414a0211d25c0f0e4177cf227e82492b666c604a9dc5f1b57a
7
- data.tar.gz: 10239435d5ca2a5df99b4bf963ced838f2fd3561b03cfa2f5ec6941809575eab7cbcdfc93a257a9a0417d52c607738294a937ad3b9336ffdbfe10e46374a9aca
6
+ metadata.gz: ef9f94b3ad1589fa3a9b9a4f85a19bb6ed203671e0f8f5691e286cee2c2a5388fa421ccbc473f68f2ff0517972bce9f1e79c19f60d2c08d96bcdb0e3ace609ea
7
+ data.tar.gz: 546127e77ed7d20d5597ba35a2c14288bafcad9f75d66935e1fea840ad8d17923537ce3231c38d81091f144cb033234a29110cce4788e944def60f7122f8f35e
data/README.md CHANGED
@@ -6,10 +6,12 @@
6
6
  Enables direct file uploads to Amazon S3 and provides a flexible pattern for
7
7
  your Rails app to asynchronously ingest the files.
8
8
 
9
+ **Note: If you are using Rails 5.2+ you should consider migrating to Active Storage as this gem will not be suppored for Rails 6+.**
10
+
9
11
  ## Overview
10
12
 
11
13
  This Rails engine allows you to quickly implement direct uploads to Amazon S3
12
- from your Rails 3.1+ / 4.x application. It does not depend on any specific file
14
+ from your Ruby on Rails application. It does not depend on any specific file
13
15
  upload libraries, UI frameworks or AWS gems, like other solutions tend to.
14
16
 
15
17
  It works by utilizing Amazon S3's
@@ -41,24 +43,6 @@ write to disk. S3 then decrypts and streams the files as they are downloaded.
41
43
  file is permitted or if multiple are.
42
44
  * Multiple files can upload concurrently to S3.
43
45
 
44
- ## Technology & Requirements
45
-
46
- Uploads are made possible by use of the `FormData` object, defined in
47
- [XMLHttpRequest Level 2](http://dev.w3.org/2006/webapi/XMLHttpRequest-2/).
48
- Many people are broadly referring to this as being provided by HTML5, but
49
- technically it's part of the aforementioned spec that browsers have been
50
- adhering to for a couple of major versions now. Even IE, wuh?
51
-
52
- The latest versions of all of the following are ideal, but here are the gem's
53
- minimum requirements:
54
-
55
- * Ruby 1.9.3+
56
- * Rails 3.1+
57
- * Modern versions of Chrome, Safari, FireFox or IE 10+
58
- * Note: Progress bars are currently disabled in IE
59
- * Note: IE <= 9 users will be instructed to upgrade their browser upon
60
- selecting a file
61
-
62
46
  ## Demo
63
47
 
64
48
  See a demo application using `s3_relay` [here](https://github.com/kjohnston/s3_relay-demo).
@@ -46,13 +46,13 @@ uploadFiles = (container) ->
46
46
  uploadFile = (container, file) ->
47
47
  fileName = file.name
48
48
 
49
- # Assign unique id to each file to pass as get parameter so Safari doesn't
50
- # consolidate the requests
51
- $(file).uniqueId()
49
+ # Assign unique value to each request so Safari doesn't consolidate them
50
+ @s3r_upload_index ||= 0
51
+ @s3r_upload_index += 1
52
52
 
53
53
  $.ajax
54
54
  type: "GET"
55
- url: "/s3_relay/uploads/new?unique_id=#{file.id}"
55
+ url: "/s3_relay/uploads/new?s3r_upload_index=#{s3r_upload_index}"
56
56
  success: (data, status, xhr) ->
57
57
  formData = new FormData()
58
58
  xhr = new XMLHttpRequest()
@@ -11,7 +11,13 @@ class S3Relay::UploadsController < ApplicationController
11
11
  @upload = S3Relay::Upload.new(upload_attrs)
12
12
 
13
13
  if @upload.save
14
- render json: { private_url: @upload.private_url }, status: 201
14
+ data = {
15
+ private_url: @upload.private_url,
16
+ parent_type: @upload.parent_type,
17
+ parent_id: @upload.parent_id,
18
+ user_id: user_attrs[:user_id]
19
+ }
20
+ render json: data, status: 201
15
21
  else
16
22
  render json: { errors: @upload.errors }, status: 422
17
23
  end
@@ -55,7 +61,7 @@ class S3Relay::UploadsController < ApplicationController
55
61
  end
56
62
 
57
63
  def user_attrs
58
- if respond_to?(:current_user) && (id = current_user.try(:id))
64
+ if respond_to?(:current_user) && (id = current_user&.id)
59
65
  { user_id: id }
60
66
  else
61
67
  {}
@@ -1,7 +1,7 @@
1
1
  module S3Relay
2
2
  class Upload < ActiveRecord::Base
3
3
 
4
- belongs_to :parent, polymorphic: true
4
+ belongs_to :parent, polymorphic: true, optional: true
5
5
 
6
6
  validates :uuid, presence: true, uniqueness: true
7
7
  validates :upload_type, presence: true
@@ -1,4 +1,4 @@
1
- class CreateS3RelayUploads < ActiveRecord::Migration
1
+ class CreateS3RelayUploads < ActiveRecord::Migration[5.0]
2
2
  def change
3
3
  create_table :s3_relay_uploads do |t|
4
4
  t.binary :uuid, length: 16
@@ -24,7 +24,7 @@ module S3Relay
24
24
  end
25
25
 
26
26
  def endpoint
27
- "https://#{bucket}.s3-#{region}.amazonaws.com"
27
+ "https://s3.#{region}.amazonaws.com/#{bucket}"
28
28
  end
29
29
 
30
30
  def digest
@@ -4,7 +4,7 @@ module S3Relay
4
4
 
5
5
  initializer "s3_relay.action_controller" do |app|
6
6
  ActiveSupport.on_load :action_controller do
7
- helper S3Relay::UploadsHelper
7
+ helper S3Relay::UploadsHelper if respond_to?(:helper)
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module S3Relay
4
4
  attr_reader :expires, :path
5
5
 
6
6
  def initialize(uuid, file, options={})
7
- filename = URI.encode(file).gsub("+", "%2B")
7
+ filename = Addressable::URI.escape(file).gsub("+", "%2B")
8
8
  @path = [uuid, filename].join("/")
9
9
  @expires = (options[:expires] || 10.minutes.from_now).to_i
10
10
  end
@@ -1,3 +1,3 @@
1
1
  module S3Relay
2
- VERSION = "0.5.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -22,20 +22,20 @@ module S3Relay
22
22
  S3Relay::UploadPresigner.any_instance.stubs(:uuid).returns(uuid)
23
23
  S3Relay::UploadPresigner.any_instance.stubs(:expires).returns(time)
24
24
 
25
- get :new
25
+ get new_s3_relay_upload_url
26
26
  assert_response 200
27
27
 
28
28
  data = JSON.parse(response.body)
29
29
 
30
- data["awsaccesskeyid"].must_equal "access-key-id"
31
- data["x_amz_server_side_encryption"].must_equal "AES256"
32
- data["key"].must_equal "#{uuid}/${filename}"
33
- data["success_action_status"].must_equal "201"
34
- data["acl"].must_equal "acl"
35
- data["endpoint"].must_equal "https://bucket.s3-region.amazonaws.com"
36
- data["policy"].length.must_equal 380 # TODO: Improve this
37
- data["signature"].length.must_equal 28 # TODO: Improve this
38
- data["uuid"].must_equal uuid
30
+ _(data["awsaccesskeyid"]).must_equal "access-key-id"
31
+ _(data["x_amz_server_side_encryption"]).must_equal "AES256"
32
+ _(data["key"]).must_equal "#{uuid}/${filename}"
33
+ _(data["success_action_status"]).must_equal "201"
34
+ _(data["acl"]).must_equal "acl"
35
+ _(data["endpoint"]).must_equal "https://s3.region.amazonaws.com/bucket"
36
+ _(data["policy"].length).must_equal 380 # TODO: Improve this
37
+ _(data["signature"].length).must_equal 28 # TODO: Improve this
38
+ _(data["uuid"]).must_equal uuid
39
39
  end
40
40
  end
41
41
 
@@ -44,11 +44,12 @@ module S3Relay
44
44
  describe "success" do
45
45
  it do
46
46
  assert_difference "S3Relay::Upload.count", 1 do
47
- post :create,
47
+ post s3_relay_uploads_url, params: {
48
48
  association: "photo_uploads",
49
49
  uuid: SecureRandom.uuid,
50
50
  filename: "cat.png",
51
51
  content_type: "image/png"
52
+ }
52
53
  end
53
54
 
54
55
  assert_response 201
@@ -56,17 +57,18 @@ module S3Relay
56
57
 
57
58
  describe "with parent attributes" do
58
59
  describe "matching an object" do
59
- before { @product = FactoryGirl.create(:product) }
60
+ before { @product = FactoryBot.create(:product) }
60
61
 
61
62
  it do
62
63
  assert_difference "@product.photo_uploads.count", 1 do
63
- post :create,
64
+ post s3_relay_uploads_url, params: {
64
65
  association: "photo_uploads",
65
66
  uuid: SecureRandom.uuid,
66
67
  filename: "cat.png",
67
68
  content_type: "image/png",
68
69
  parent_type: @product.class.to_s,
69
70
  parent_id: @product.id.to_s
71
+ }
70
72
  end
71
73
 
72
74
  assert_response 201
@@ -76,40 +78,43 @@ module S3Relay
76
78
  describe "not matching an object" do
77
79
  it do
78
80
  assert_difference "S3Relay::Upload.count" do
79
- post :create,
81
+ post s3_relay_uploads_url, params: {
80
82
  association: "photo_uploads",
81
83
  uuid: SecureRandom.uuid,
82
84
  filename: "cat.png",
83
85
  content_type: "image/png",
84
86
  parent_type: "Product",
85
87
  parent_id: "10000000"
88
+ }
86
89
  end
87
90
 
88
91
  assert_response 201
92
+ body = JSON.parse(response.body)
89
93
 
90
- assigns[:upload].parent_type.must_equal nil
91
- assigns[:upload].parent_id.must_equal nil
94
+ assert body["parent_type"] == nil
95
+ assert body["parent_id"] == nil
92
96
  end
93
97
  end
94
98
 
95
99
  describe "with a current_user" do
96
100
  before do
97
101
  @user = OpenStruct.new(id: 123)
98
- @controller.stubs(:current_user).returns(@user)
102
+ UploadsController.any_instance.stubs(:current_user).returns(@user)
99
103
  end
100
104
 
101
105
  it "associates the upload with the user" do
102
106
  assert_difference "S3Relay::Upload.count", 1 do
103
- post :create,
107
+ post s3_relay_uploads_url, params: {
104
108
  association: "photo_uploads",
105
109
  uuid: SecureRandom.uuid,
106
110
  filename: "cat.png",
107
111
  content_type: "image/png"
112
+ }
108
113
  end
109
114
 
110
115
  assert_response 201
111
-
112
- assigns[:upload].user_id.must_equal @user.id
116
+ body = JSON.parse(response.body)
117
+ _(body["user_id"]).must_equal @user.id
113
118
  end
114
119
  end
115
120
 
@@ -119,15 +124,16 @@ module S3Relay
119
124
  describe "error" do
120
125
  it do
121
126
  assert_no_difference "S3Relay::Upload.count" do
122
- post :create,
127
+ post s3_relay_uploads_url, params: {
123
128
  uuid: SecureRandom.uuid,
124
129
  filename: "cat.png",
125
130
  content_type: "image/png"
131
+ }
126
132
  end
127
133
 
128
134
  assert_response 422
129
135
 
130
- JSON.parse(response.body)["errors"]["upload_type"]
136
+ _(JSON.parse(response.body)["errors"]["upload_type"])
131
137
  .must_include "can't be blank"
132
138
  end
133
139
  end
@@ -1,4 +1,4 @@
1
- == README
1
+ # README
2
2
 
3
3
  This README would normally document whatever steps are necessary to get the
4
4
  application up and running.
@@ -22,7 +22,3 @@ Things you may want to cover:
22
22
  * Deployment instructions
23
23
 
24
24
  * ...
25
-
26
-
27
- Please feel free to use a different markup language if you do not plan to run
28
- <tt>rake doc:app</tt>.
@@ -1,6 +1,6 @@
1
1
  # Add your own tasks in files placed in lib/tasks ending in .rake,
2
2
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
3
 
4
- require File.expand_path('../config/application', __FILE__)
4
+ require_relative 'config/application'
5
5
 
6
6
  Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
@@ -1,13 +1,15 @@
1
1
  // This is a manifest file that'll be compiled into application.js, which will include all the files
2
2
  // listed below.
3
3
  //
4
- // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
- // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5
+ // vendor/assets/javascripts directory can be referenced here using a relative path.
6
6
  //
7
7
  // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
- // compiled file.
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
9
  //
10
- // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
11
  // about supported directives.
12
12
  //
13
+ //= require rails-ujs
14
+ //= require turbolinks
13
15
  //= require_tree .
@@ -0,0 +1,13 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3
+ //
4
+ //= require action_cable
5
+ //= require_self
6
+ //= require_tree ./channels
7
+
8
+ (function() {
9
+ this.App || (this.App = {});
10
+
11
+ App.cable = ActionCable.createConsumer();
12
+
13
+ }).call(this);
@@ -2,13 +2,13 @@
2
2
  * This is a manifest file that'll be compiled into application.css, which will include all the files
3
3
  * listed below.
4
4
  *
5
- * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
- * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6
+ * vendor/assets/stylesheets directory can be referenced here using a relative path.
7
7
  *
8
8
  * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
- * compiled file so the styles you add here take precedence over styles defined in any styles
10
- * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
- * file per style scope.
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
12
  *
13
13
  *= require_tree .
14
14
  *= require_self
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Connection < ActionCable::Connection::Base
3
+ end
4
+ end
@@ -1,5 +1,3 @@
1
1
  class ApplicationController < ActionController::Base
2
- # Prevent CSRF attacks by raising an exception.
3
- # For APIs, you may want to use :null_session instead.
4
2
  protect_from_forgery with: :exception
5
3
  end
@@ -0,0 +1,2 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: 'from@example.com'
3
+ layout 'mailer'
4
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -1,4 +1,4 @@
1
- class Product < ActiveRecord::Base
1
+ class Product < ApplicationRecord
2
2
 
3
3
  s3_relay :icon_upload
4
4
  s3_relay :photo_uploads, has_many: true
@@ -1,14 +1,14 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
- <head>
4
- <title>Dummy</title>
5
- <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6
- <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7
- <%= csrf_meta_tags %>
8
- </head>
9
- <body>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= csrf_meta_tags %>
10
6
 
11
- <%= yield %>
7
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
8
+ <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
9
+ </head>
12
10
 
13
- </body>
11
+ <body>
12
+ <%= yield %>
13
+ </body>
14
14
  </html>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
+ <style>
6
+ /* Email styles need to be inline */
7
+ </style>
8
+ </head>
9
+
10
+ <body>
11
+ <%= yield %>
12
+ </body>
13
+ </html>
@@ -0,0 +1 @@
1
+ <%= yield %>