activestorage_legacy 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/.babelrc +5 -0
  3. data/.codeclimate.yml +7 -0
  4. data/.eslintrc +19 -0
  5. data/.github/workflows/gem-push.yml +29 -0
  6. data/.github/workflows/ruby-tests.yml +37 -0
  7. data/.gitignore +9 -0
  8. data/.rubocop.yml +125 -0
  9. data/.travis.yml +25 -0
  10. data/Gemfile +33 -0
  11. data/Gemfile.lock +271 -0
  12. data/MIT-LICENSE +20 -0
  13. data/README.md +160 -0
  14. data/Rakefile +12 -0
  15. data/activestorage.gemspec +27 -0
  16. data/app/assets/javascripts/activestorage.js +1 -0
  17. data/app/controllers/active_storage/blobs_controller.rb +22 -0
  18. data/app/controllers/active_storage/direct_uploads_controller.rb +21 -0
  19. data/app/controllers/active_storage/disk_controller.rb +52 -0
  20. data/app/controllers/active_storage/variants_controller.rb +28 -0
  21. data/app/helpers/active_storage/file_field_with_direct_upload_helper.rb +18 -0
  22. data/app/javascript/activestorage/blob_record.js +54 -0
  23. data/app/javascript/activestorage/blob_upload.js +34 -0
  24. data/app/javascript/activestorage/direct_upload.js +42 -0
  25. data/app/javascript/activestorage/direct_upload_controller.js +67 -0
  26. data/app/javascript/activestorage/direct_uploads_controller.js +50 -0
  27. data/app/javascript/activestorage/file_checksum.js +53 -0
  28. data/app/javascript/activestorage/helpers.js +42 -0
  29. data/app/javascript/activestorage/index.js +11 -0
  30. data/app/javascript/activestorage/ujs.js +74 -0
  31. data/app/jobs/active_storage/purge_attachment_worker.rb +9 -0
  32. data/app/jobs/active_storage/purge_blob_worker.rb +9 -0
  33. data/app/models/active_storage/attachment.rb +33 -0
  34. data/app/models/active_storage/blob.rb +198 -0
  35. data/app/models/active_storage/filename.rb +49 -0
  36. data/app/models/active_storage/variant.rb +82 -0
  37. data/app/models/active_storage/variation.rb +53 -0
  38. data/config/routes.rb +9 -0
  39. data/config/storage_services.yml +34 -0
  40. data/lib/active_storage/attached/macros.rb +86 -0
  41. data/lib/active_storage/attached/many.rb +51 -0
  42. data/lib/active_storage/attached/one.rb +56 -0
  43. data/lib/active_storage/attached.rb +38 -0
  44. data/lib/active_storage/engine.rb +81 -0
  45. data/lib/active_storage/gem_version.rb +15 -0
  46. data/lib/active_storage/log_subscriber.rb +48 -0
  47. data/lib/active_storage/messages_metadata.rb +64 -0
  48. data/lib/active_storage/migration.rb +27 -0
  49. data/lib/active_storage/patches/active_record.rb +19 -0
  50. data/lib/active_storage/patches/delegation.rb +98 -0
  51. data/lib/active_storage/patches/secure_random.rb +26 -0
  52. data/lib/active_storage/patches.rb +4 -0
  53. data/lib/active_storage/service/azure_service.rb +115 -0
  54. data/lib/active_storage/service/configurator.rb +28 -0
  55. data/lib/active_storage/service/disk_service.rb +124 -0
  56. data/lib/active_storage/service/gcs_service.rb +79 -0
  57. data/lib/active_storage/service/mirror_service.rb +46 -0
  58. data/lib/active_storage/service/s3_service.rb +96 -0
  59. data/lib/active_storage/service.rb +113 -0
  60. data/lib/active_storage/verifier.rb +113 -0
  61. data/lib/active_storage/version.rb +8 -0
  62. data/lib/active_storage.rb +34 -0
  63. data/lib/tasks/activestorage.rake +20 -0
  64. data/package.json +33 -0
  65. data/test/controllers/direct_uploads_controller_test.rb +123 -0
  66. data/test/controllers/disk_controller_test.rb +57 -0
  67. data/test/controllers/variants_controller_test.rb +21 -0
  68. data/test/database/create_users_migration.rb +7 -0
  69. data/test/database/setup.rb +6 -0
  70. data/test/dummy/Rakefile +3 -0
  71. data/test/dummy/app/assets/config/manifest.js +5 -0
  72. data/test/dummy/app/assets/images/.keep +0 -0
  73. data/test/dummy/app/assets/javascripts/application.js +13 -0
  74. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  75. data/test/dummy/app/controllers/application_controller.rb +3 -0
  76. data/test/dummy/app/controllers/concerns/.keep +0 -0
  77. data/test/dummy/app/helpers/application_helper.rb +2 -0
  78. data/test/dummy/app/jobs/application_job.rb +2 -0
  79. data/test/dummy/app/models/application_record.rb +3 -0
  80. data/test/dummy/app/models/concerns/.keep +0 -0
  81. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  82. data/test/dummy/bin/bundle +3 -0
  83. data/test/dummy/bin/rails +4 -0
  84. data/test/dummy/bin/rake +4 -0
  85. data/test/dummy/bin/yarn +11 -0
  86. data/test/dummy/config/application.rb +22 -0
  87. data/test/dummy/config/boot.rb +5 -0
  88. data/test/dummy/config/database.yml +25 -0
  89. data/test/dummy/config/environment.rb +5 -0
  90. data/test/dummy/config/environments/development.rb +49 -0
  91. data/test/dummy/config/environments/production.rb +82 -0
  92. data/test/dummy/config/environments/test.rb +33 -0
  93. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  94. data/test/dummy/config/initializers/assets.rb +14 -0
  95. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  96. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  97. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  98. data/test/dummy/config/initializers/inflections.rb +16 -0
  99. data/test/dummy/config/initializers/mime_types.rb +4 -0
  100. data/test/dummy/config/initializers/secret_key.rb +3 -0
  101. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  102. data/test/dummy/config/routes.rb +2 -0
  103. data/test/dummy/config/secrets.yml +32 -0
  104. data/test/dummy/config/spring.rb +6 -0
  105. data/test/dummy/config/storage_services.yml +3 -0
  106. data/test/dummy/config.ru +5 -0
  107. data/test/dummy/db/.keep +0 -0
  108. data/test/dummy/lib/assets/.keep +0 -0
  109. data/test/dummy/log/.keep +0 -0
  110. data/test/dummy/package.json +5 -0
  111. data/test/dummy/public/404.html +67 -0
  112. data/test/dummy/public/422.html +67 -0
  113. data/test/dummy/public/500.html +66 -0
  114. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  115. data/test/dummy/public/apple-touch-icon.png +0 -0
  116. data/test/dummy/public/favicon.ico +0 -0
  117. data/test/filename_test.rb +36 -0
  118. data/test/fixtures/files/racecar.jpg +0 -0
  119. data/test/models/attachments_test.rb +122 -0
  120. data/test/models/blob_test.rb +47 -0
  121. data/test/models/variant_test.rb +27 -0
  122. data/test/service/.gitignore +1 -0
  123. data/test/service/azure_service_test.rb +14 -0
  124. data/test/service/configurations-example.yml +31 -0
  125. data/test/service/configurator_test.rb +14 -0
  126. data/test/service/disk_service_test.rb +12 -0
  127. data/test/service/gcs_service_test.rb +42 -0
  128. data/test/service/mirror_service_test.rb +62 -0
  129. data/test/service/s3_service_test.rb +52 -0
  130. data/test/service/shared_service_tests.rb +66 -0
  131. data/test/sidekiq/minitest_support.rb +6 -0
  132. data/test/support/assertions.rb +20 -0
  133. data/test/test_helper.rb +69 -0
  134. data/webpack.config.js +27 -0
  135. data/yarn.lock +3164 -0
  136. metadata +330 -0
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+ require 'active_storage/messages_metadata'
3
+
4
+ class ActiveStorage::Verifier
5
+ class InvalidSignature < StandardError; end
6
+
7
+ def initialize(secret, options = {})
8
+ raise ArgumentError, "Secret should not be nil." unless secret
9
+ @secret = secret
10
+ @digest = options[:digest] || "SHA1"
11
+ @serializer = options[:serializer] || Marshal
12
+ end
13
+
14
+ # Checks if a signed message could have been generated by signing an object
15
+ # with the +MessageVerifier+'s secret.
16
+ #
17
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
18
+ # signed_message = verifier.generate 'a private message'
19
+ # verifier.valid_message?(signed_message) # => true
20
+ #
21
+ # tampered_message = signed_message.chop # editing the message invalidates the signature
22
+ # verifier.valid_message?(tampered_message) # => false
23
+ def valid_message?(signed_message)
24
+ return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
25
+
26
+ data, digest = signed_message.split("--".freeze)
27
+ data.present? && digest.present? && security_class.secure_compare(digest, generate_digest(data))
28
+ end
29
+
30
+ # Decodes the signed message using the +MessageVerifier+'s secret.
31
+ #
32
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
33
+ #
34
+ # signed_message = verifier.generate 'a private message'
35
+ # verifier.verified(signed_message) # => 'a private message'
36
+ #
37
+ # Returns +nil+ if the message was not signed with the same secret.
38
+ #
39
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
40
+ # other_verifier.verified(signed_message) # => nil
41
+ #
42
+ # Returns +nil+ if the message is not Base64-encoded.
43
+ #
44
+ # invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d"
45
+ # verifier.verified(invalid_message) # => nil
46
+ #
47
+ # Raises any error raised while decoding the signed message.
48
+ #
49
+ # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
50
+ # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
51
+ def verified(signed_message, purpose: nil, **)
52
+ if valid_message?(signed_message)
53
+ begin
54
+ data = signed_message.split("--".freeze)[0]
55
+ message = ActiveSupport::MessagesMetadata.verify(decode(data), purpose)
56
+ @serializer.load(message) if message
57
+ rescue ArgumentError => argument_error
58
+ return if argument_error.message.include?("invalid base64")
59
+ raise
60
+ end
61
+ end
62
+ end
63
+
64
+ # Decodes the signed message using the +MessageVerifier+'s secret.
65
+ #
66
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
67
+ # signed_message = verifier.generate 'a private message'
68
+ #
69
+ # verifier.verify(signed_message) # => 'a private message'
70
+ #
71
+ # Raises +InvalidSignature+ if the message was not signed with the same
72
+ # secret or was not Base64-encoded.
73
+ #
74
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
75
+ # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
76
+ def verify(*args)
77
+ verified(*args) || raise(InvalidSignature)
78
+ end
79
+
80
+ # Generates a signed message for the provided value.
81
+ #
82
+ # The message is signed with the +MessageVerifier+'s secret. Without knowing
83
+ # the secret, the original value cannot be extracted from the message.
84
+ #
85
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
86
+ # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
87
+ def generate(value, expires_at: nil, expires_in: nil, purpose: nil)
88
+ data = encode(ActiveSupport::MessagesMetadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose))
89
+ "#{data}--#{generate_digest(data)}"
90
+ end
91
+
92
+ private
93
+ def encode(data)
94
+ ::Base64.strict_encode64(data)
95
+ end
96
+
97
+ def security_class
98
+ if defined?(ActiveSupport::SecurityUtils)
99
+ ActiveSupport::SecurityUtils
100
+ else
101
+ Rack::Utils
102
+ end
103
+ end
104
+
105
+ def decode(data)
106
+ ::Base64.strict_decode64(data)
107
+ end
108
+
109
+ def generate_digest(data)
110
+ require "openssl" unless defined?(OpenSSL)
111
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
112
+ end
113
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "gem_version"
2
+
3
+ module ActiveStorage
4
+ # Returns the version of the currently loaded ActiveStorage as a <tt>Gem::Version</tt>
5
+ def self.version
6
+ gem_version
7
+ end
8
+ end
@@ -0,0 +1,34 @@
1
+ #--
2
+ # Copyright (c) 2017 David Heinemeier Hansson
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require "active_record"
25
+ require "active_storage/engine"
26
+
27
+ module ActiveStorage
28
+ extend ActiveSupport::Autoload
29
+
30
+ autoload :Attached
31
+ autoload :Service
32
+
33
+ mattr_accessor :verifier
34
+ end
@@ -0,0 +1,20 @@
1
+ require "fileutils"
2
+
3
+ namespace :activestorage do
4
+ desc "Copy over the migration needed to the application"
5
+ task :install do
6
+ FileUtils.mkdir_p Rails.root.join("storage")
7
+ FileUtils.mkdir_p Rails.root.join("tmp/storage")
8
+ puts "Made storage and tmp/storage directories for development and testing"
9
+
10
+ FileUtils.cp File.expand_path("../../../config/storage_services.yml", __FILE__), Rails.root.join("config")
11
+ puts "Copied default configuration to config/storage_services.yml"
12
+
13
+ migration_file_path = "db/migrate/#{Time.now.utc.strftime("%Y%m%d%H%M%S")}_active_storage_create_tables.rb"
14
+ FileUtils.mkdir_p Rails.root.join("db/migrate")
15
+ FileUtils.cp File.expand_path("../../active_storage/migration.rb", __FILE__), Rails.root.join(migration_file_path)
16
+ puts "Copied migration to #{migration_file_path}"
17
+
18
+ puts "Now run rails db:migrate to create the tables for Active Storage"
19
+ end
20
+ end
data/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "activestorage",
3
+ "version": "0.1.1",
4
+ "description": "Attach cloud and local files in Rails applications",
5
+ "main": "app/assets/javascripts/activestorage.js",
6
+ "files": [
7
+ "app/assets/javascripts/*.js"
8
+ ],
9
+ "homepage": "https://github.com/rails/activestorage",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/rails/activestorage.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/rails/activestorage/issues"
16
+ },
17
+ "author": "Javan Makhmali <javan@javan.us>",
18
+ "license": "MIT",
19
+ "devDependencies": {
20
+ "babel-core": "^6.25.0",
21
+ "babel-loader": "^7.1.1",
22
+ "babel-preset-env": "^1.6.0",
23
+ "eslint": "^4.3.0",
24
+ "eslint-plugin-import": "^2.7.0",
25
+ "spark-md5": "^3.0.0",
26
+ "webpack": "^3.4.0"
27
+ },
28
+ "scripts": {
29
+ "prebuild": "yarn lint",
30
+ "build": "webpack -p",
31
+ "lint": "eslint app/javascript"
32
+ }
33
+ }
@@ -0,0 +1,123 @@
1
+ require "test_helper"
2
+ require "database/setup"
3
+
4
+ if SERVICE_CONFIGURATIONS[:s3]
5
+ class ActiveStorage::S3DirectUploadsControllerTest < ActionDispatch::IntegrationTest
6
+ setup do
7
+ @old_service = ActiveStorage::Blob.service
8
+ ActiveStorage::Blob.service = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
9
+ end
10
+
11
+ teardown do
12
+ ActiveStorage::Blob.service = @old_service
13
+ end
14
+
15
+ test "creating new direct upload" do
16
+ checksum = Digest::MD5.base64digest("Hello")
17
+
18
+ post rails_direct_uploads_url, { blob: {
19
+ filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
20
+
21
+ parsed_body = JSON.parse(@response.body)
22
+ parsed_body.tap do |details|
23
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
24
+ assert_equal "hello.txt", details["filename"]
25
+ assert_equal 6, details["byte_size"]
26
+ assert_equal checksum, details["checksum"]
27
+ assert_equal "text/plain", details["content_type"]
28
+ assert_match /#{SERVICE_CONFIGURATIONS[:s3][:bucket]}\.s3.(\S+)?amazonaws\.com/, details["direct_upload"]["url"]
29
+ assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum }, details["direct_upload"]["headers"])
30
+ end
31
+ end
32
+ end
33
+ else
34
+ puts "Skipping S3 Direct Upload tests because no S3 configuration was supplied"
35
+ end
36
+
37
+ if SERVICE_CONFIGURATIONS[:gcs]
38
+ class ActiveStorage::GCSDirectUploadsControllerTest < ActionDispatch::IntegrationTest
39
+ setup do
40
+ @config = SERVICE_CONFIGURATIONS[:gcs]
41
+
42
+ @old_service = ActiveStorage::Blob.service
43
+ ActiveStorage::Blob.service = ActiveStorage::Service.configure(:gcs, SERVICE_CONFIGURATIONS)
44
+ end
45
+
46
+ teardown do
47
+ ActiveStorage::Blob.service = @old_service
48
+ end
49
+
50
+ test "creating new direct upload" do
51
+ checksum = Digest::MD5.base64digest("Hello")
52
+
53
+ post rails_direct_uploads_url, { blob: {
54
+ filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
55
+
56
+ parsed_body = JSON.parse(@response.body)
57
+ parsed_body.tap do |details|
58
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
59
+ assert_equal "hello.txt", details["filename"]
60
+ assert_equal 6, details["byte_size"]
61
+ assert_equal checksum, details["checksum"]
62
+ assert_equal "text/plain", details["content_type"]
63
+ assert_match %r{storage\.googleapis\.com/#{@config[:bucket]}}, details["direct_upload"]["url"]
64
+ assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum }, details["direct_upload"]["headers"])
65
+ end
66
+ end
67
+ end
68
+ else
69
+ puts "Skipping GCS Direct Upload tests because no GCS configuration was supplied"
70
+ end
71
+
72
+ if SERVICE_CONFIGURATIONS[:azure]
73
+ class ActiveStorage::AzureDirectUploadsControllerTest < ActionDispatch::IntegrationTest
74
+ setup do
75
+ @config = SERVICE_CONFIGURATIONS[:azure]
76
+
77
+ @old_service = ActiveStorage::Blob.service
78
+ ActiveStorage::Blob.service = ActiveStorage::Service.configure(:azure, SERVICE_CONFIGURATIONS)
79
+ end
80
+
81
+ teardown do
82
+ ActiveStorage::Blob.service = @old_service
83
+ end
84
+
85
+ test "creating new direct upload" do
86
+ checksum = Digest::MD5.base64digest("Hello")
87
+
88
+ post rails_direct_uploads_url, { blob: {
89
+ filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
90
+
91
+ @response.parsed_body.tap do |details|
92
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
93
+ assert_equal "hello.txt", details["filename"]
94
+ assert_equal 6, details["byte_size"]
95
+ assert_equal checksum, details["checksum"]
96
+ assert_equal "text/plain", details["content_type"]
97
+ assert_match %r{#{@config[:storage_account_name]}\.blob\.core\.windows\.net/#{@config[:container]}}, details["direct_upload"]["url"]
98
+ assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum, "x-ms-blob-type" => "BlockBlob" }, details["direct_upload"]["headers"])
99
+ end
100
+ end
101
+ end
102
+ else
103
+ puts "Skipping Azure Direct Upload tests because no Azure configuration was supplied"
104
+ end
105
+
106
+ class ActiveStorage::DiskDirectUploadsControllerTest < ActionDispatch::IntegrationTest
107
+ test "creating new direct upload" do
108
+ checksum = Digest::MD5.base64digest("Hello")
109
+
110
+ post rails_direct_uploads_url, { blob: { filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
111
+
112
+ parsed_body = JSON.parse(@response.body)
113
+ parsed_body.tap do |details|
114
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
115
+ assert_equal "hello.txt", details["filename"]
116
+ assert_equal 6, details["byte_size"]
117
+ assert_equal checksum, details["checksum"]
118
+ assert_equal "text/plain", details["content_type"]
119
+ assert_match /rails\/active_storage\/disk/, details["direct_upload"]["url"]
120
+ assert_equal({ "Content-Type" => "text/plain" }, details["direct_upload"]["headers"])
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,57 @@
1
+ require "test_helper"
2
+ require "database/setup"
3
+
4
+ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
5
+ test "showing blob inline" do
6
+ blob = create_blob
7
+
8
+ get blob.service_url
9
+ assert_equal "inline; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
10
+ assert_equal "text/plain", @response.headers["Content-Type"]
11
+ end
12
+
13
+ test "showing blob as attachment" do
14
+ blob = create_blob
15
+
16
+ get blob.service_url(disposition: :attachment)
17
+ assert_equal "attachment; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
18
+ assert_equal "text/plain", @response.headers["Content-Type"]
19
+ end
20
+
21
+
22
+ test "directly uploading blob with integrity" do
23
+ data = "Something else entirely!"
24
+ blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest(data)
25
+
26
+ put blob.service_url_for_direct_upload, data, { 'CONTENT_TYPE' => "text/plain" }
27
+ assert_response :no_content
28
+ assert_equal data, blob.download
29
+ end
30
+
31
+ test "directly uploading blob without integrity" do
32
+ data = "Something else entirely!"
33
+ blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest("bad data")
34
+
35
+ put blob.service_url_for_direct_upload, data
36
+ assert_response :unprocessable_entity
37
+ assert_not blob.service.exist?(blob.key)
38
+ end
39
+
40
+ test "directly uploading blob with mismatched content type" do
41
+ data = "Something else entirely!"
42
+ blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest(data)
43
+
44
+ put blob.service_url_for_direct_upload, data, { 'CONTENT_TYPE' => "application/octet-stream" }
45
+ assert_response :unprocessable_entity
46
+ assert_not blob.service.exist?(blob.key)
47
+ end
48
+
49
+ test "directly uploading blob with mismatched content length" do
50
+ data = "Something else entirely!"
51
+ blob = create_blob_before_direct_upload byte_size: data.size - 1, checksum: Digest::MD5.base64digest(data)
52
+
53
+ put blob.service_url_for_direct_upload, data, { 'CONTENT_TYPE' => "text/plain" }
54
+ assert_response :unprocessable_entity
55
+ assert_not blob.service.exist?(blob.key)
56
+ end
57
+ end
@@ -0,0 +1,21 @@
1
+ require "test_helper"
2
+ require "database/setup"
3
+
4
+ class ActiveStorage::VariantsControllerTest < ActionDispatch::IntegrationTest
5
+ setup do
6
+ @blob = create_image_blob filename: "racecar.jpg"
7
+ end
8
+
9
+ test "showing variant inline" do
10
+ get rails_blob_variation_url(
11
+ filename: @blob.filename,
12
+ signed_blob_id: @blob.signed_id,
13
+ variation_key: ActiveStorage::Variation.encode(resize: "100x100"))
14
+
15
+ assert_match /racecar\.jpg\?.*disposition=inline/, @response.redirect_url
16
+
17
+ image = read_image_variant(@blob.variant(resize: "100x100"))
18
+ assert_equal 100, image.width
19
+ assert_equal 67, image.height
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ class ActiveStorageCreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :name
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ require "active_storage/migration"
2
+ require_relative "create_users_migration"
3
+
4
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
5
+ ActiveStorageCreateTables.migrate(:up)
6
+ ActiveStorageCreateUsers.migrate(:up)
@@ -0,0 +1,3 @@
1
+ require_relative 'config/application'
2
+
3
+ Rails.application.load_tasks
@@ -0,0 +1,5 @@
1
+
2
+ //= link_tree ../images
3
+ //= link_directory ../javascripts .js
4
+ //= link_directory ../stylesheets .css
5
+ //= link active_storage_manifest.js
File without changes
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
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. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
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 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
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery with: :exception
3
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
File without changes
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= csrf_meta_tags %>
6
+
7
+ <%= stylesheet_link_tag 'application', media: 'all' %>
8
+ <%= javascript_include_tag 'application' %>
9
+ </head>
10
+
11
+ <body>
12
+ <%= yield %>
13
+ </body>
14
+ </html>
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path('../config/application', __dir__)
3
+ require_relative '../config/boot'
4
+ require 'rails/commands'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../config/boot'
3
+ require 'rake'
4
+ Rake.application.run
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ VENDOR_PATH = File.expand_path('..', __dir__)
3
+ Dir.chdir(VENDOR_PATH) do
4
+ begin
5
+ exec "yarnpkg #{ARGV.join(" ")}"
6
+ rescue Errno::ENOENT
7
+ $stderr.puts "Yarn executable was not detected in the system."
8
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9
+ exit 1
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'boot'
2
+
3
+ require "rails"
4
+ require "active_model/railtie"
5
+ require "active_record/railtie"
6
+ require "action_controller/railtie"
7
+ require "action_view/railtie"
8
+ require "sprockets/railtie"
9
+ #require "action_mailer/railtie"
10
+ #require "rails/test_unit/railtie"
11
+ #require "action_cable/engine"
12
+
13
+
14
+ Bundler.require(*Rails.groups)
15
+ require "active_storage"
16
+
17
+ module Dummy
18
+ class Application < Rails::Application
19
+ # config.active_storage.service = :local
20
+ end
21
+ end
22
+
@@ -0,0 +1,5 @@
1
+ # Set up gems listed in the Gemfile.
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
3
+
4
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
@@ -0,0 +1,25 @@
1
+ # SQLite version 3.x
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem 'sqlite3'
6
+ #
7
+ default: &default
8
+ adapter: sqlite3
9
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10
+ timeout: 5000
11
+
12
+ development:
13
+ <<: *default
14
+ database: db/development.sqlite3
15
+
16
+ # Warning: The database defined as "test" will be erased and
17
+ # re-generated from your development database when you run "rake".
18
+ # Do not set this db to the same as development or production.
19
+ test:
20
+ <<: *default
21
+ database: db/test.sqlite3
22
+
23
+ production:
24
+ <<: *default
25
+ database: db/production.sqlite3
@@ -0,0 +1,5 @@
1
+ # Load the Rails application.
2
+ require_relative 'application'
3
+
4
+ # Initialize the Rails application.
5
+ Rails.application.initialize!
@@ -0,0 +1,49 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb.
3
+
4
+ # In the development environment your application's code is reloaded on
5
+ # every request. This slows down response time but is perfect for development
6
+ # since you don't have to restart the web server when you make code changes.
7
+ config.cache_classes = false
8
+
9
+ # Do not eager load code on boot.
10
+ config.eager_load = false
11
+
12
+ # Show full error reports.
13
+ config.consider_all_requests_local = true
14
+
15
+ # Enable/disable caching. By default caching is disabled.
16
+ if Rails.root.join('tmp/caching-dev.txt').exist?
17
+ config.action_controller.perform_caching = true
18
+
19
+ config.cache_store = :memory_store
20
+ config.public_file_server.headers = {
21
+ 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
22
+ }
23
+ else
24
+ config.action_controller.perform_caching = false
25
+
26
+ config.cache_store = :null_store
27
+ end
28
+
29
+ # Print deprecation notices to the Rails logger.
30
+ config.active_support.deprecation = :log
31
+
32
+ # Raise an error on page load if there are pending migrations.
33
+ # config.active_record.migration_error = :page_load
34
+
35
+ # Debug mode disables concatenation and preprocessing of assets.
36
+ # This option may cause significant delays in view rendering with a large
37
+ # number of complex assets.
38
+ config.assets.debug = true
39
+
40
+ # Suppress logger output for asset requests.
41
+ config.assets.quiet = true
42
+
43
+ # Raises error for missing translations
44
+ # config.action_view.raise_on_missing_translations = true
45
+
46
+ # Use an evented file watcher to asynchronously detect changes in source code,
47
+ # routes, locales, etc. This feature depends on the listen gem.
48
+ # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
49
+ end