activestorage 0.1 → 5.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activestorage might be problematic. Click here for more details.

Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +94 -25
  4. data/app/assets/javascripts/activestorage.js +1 -0
  5. data/app/controllers/active_storage/blobs_controller.rb +16 -0
  6. data/app/controllers/active_storage/direct_uploads_controller.rb +23 -0
  7. data/app/controllers/active_storage/disk_controller.rb +51 -0
  8. data/app/controllers/active_storage/previews_controller.rb +12 -0
  9. data/app/controllers/active_storage/variants_controller.rb +16 -0
  10. data/app/javascript/activestorage/blob_record.js +54 -0
  11. data/app/javascript/activestorage/blob_upload.js +35 -0
  12. data/app/javascript/activestorage/direct_upload.js +42 -0
  13. data/app/javascript/activestorage/direct_upload_controller.js +67 -0
  14. data/app/javascript/activestorage/direct_uploads_controller.js +50 -0
  15. data/app/javascript/activestorage/file_checksum.js +53 -0
  16. data/app/javascript/activestorage/helpers.js +42 -0
  17. data/app/javascript/activestorage/index.js +11 -0
  18. data/app/javascript/activestorage/ujs.js +75 -0
  19. data/app/jobs/active_storage/analyze_job.rb +8 -0
  20. data/app/jobs/active_storage/base_job.rb +5 -0
  21. data/app/jobs/active_storage/purge_job.rb +11 -0
  22. data/app/models/active_storage/attachment.rb +35 -0
  23. data/app/models/active_storage/blob.rb +313 -0
  24. data/app/models/active_storage/filename.rb +73 -0
  25. data/app/models/active_storage/filename/parameters.rb +36 -0
  26. data/app/models/active_storage/preview.rb +90 -0
  27. data/app/models/active_storage/variant.rb +86 -0
  28. data/app/models/active_storage/variation.rb +67 -0
  29. data/config/routes.rb +43 -0
  30. data/lib/active_storage.rb +37 -2
  31. data/lib/active_storage/analyzer.rb +33 -0
  32. data/lib/active_storage/analyzer/image_analyzer.rb +36 -0
  33. data/lib/active_storage/analyzer/null_analyzer.rb +13 -0
  34. data/lib/active_storage/analyzer/video_analyzer.rb +79 -0
  35. data/lib/active_storage/attached.rb +28 -22
  36. data/lib/active_storage/attached/macros.rb +89 -16
  37. data/lib/active_storage/attached/many.rb +53 -21
  38. data/lib/active_storage/attached/one.rb +74 -20
  39. data/lib/active_storage/downloading.rb +26 -0
  40. data/lib/active_storage/engine.rb +72 -0
  41. data/lib/active_storage/gem_version.rb +17 -0
  42. data/lib/active_storage/log_subscriber.rb +52 -0
  43. data/lib/active_storage/previewer.rb +58 -0
  44. data/lib/active_storage/previewer/pdf_previewer.rb +17 -0
  45. data/lib/active_storage/previewer/video_previewer.rb +23 -0
  46. data/lib/active_storage/service.rb +112 -24
  47. data/lib/active_storage/service/azure_storage_service.rb +124 -0
  48. data/lib/active_storage/service/configurator.rb +32 -0
  49. data/lib/active_storage/service/disk_service.rb +103 -44
  50. data/lib/active_storage/service/gcs_service.rb +87 -29
  51. data/lib/active_storage/service/mirror_service.rb +38 -22
  52. data/lib/active_storage/service/s3_service.rb +83 -38
  53. data/lib/active_storage/version.rb +10 -0
  54. data/lib/tasks/activestorage.rake +4 -15
  55. metadata +64 -108
  56. data/.gitignore +0 -1
  57. data/Gemfile +0 -11
  58. data/Gemfile.lock +0 -235
  59. data/Rakefile +0 -11
  60. data/activestorage.gemspec +0 -21
  61. data/lib/active_storage/attachment.rb +0 -30
  62. data/lib/active_storage/blob.rb +0 -80
  63. data/lib/active_storage/disk_controller.rb +0 -28
  64. data/lib/active_storage/download.rb +0 -90
  65. data/lib/active_storage/filename.rb +0 -31
  66. data/lib/active_storage/migration.rb +0 -28
  67. data/lib/active_storage/purge_job.rb +0 -10
  68. data/lib/active_storage/railtie.rb +0 -56
  69. data/lib/active_storage/storage_services.yml +0 -27
  70. data/lib/active_storage/verified_key_with_expiration.rb +0 -24
  71. data/test/attachments_test.rb +0 -95
  72. data/test/blob_test.rb +0 -28
  73. data/test/database/create_users_migration.rb +0 -7
  74. data/test/database/setup.rb +0 -6
  75. data/test/disk_controller_test.rb +0 -34
  76. data/test/filename_test.rb +0 -36
  77. data/test/service/.gitignore +0 -1
  78. data/test/service/configurations-example.yml +0 -11
  79. data/test/service/disk_service_test.rb +0 -8
  80. data/test/service/gcs_service_test.rb +0 -20
  81. data/test/service/mirror_service_test.rb +0 -50
  82. data/test/service/s3_service_test.rb +0 -11
  83. data/test/service/shared_service_tests.rb +0 -68
  84. data/test/test_helper.rb +0 -28
  85. data/test/verified_key_with_expiration_test.rb +0 -19
@@ -1,95 +0,0 @@
1
- require "test_helper"
2
- require "database/setup"
3
- require "active_storage/blob"
4
-
5
- require "active_job"
6
- ActiveJob::Base.queue_adapter = :test
7
- ActiveJob::Base.logger = nil
8
-
9
- # ActiveRecord::Base.logger = Logger.new(STDOUT)
10
-
11
- class User < ActiveRecord::Base
12
- has_one_attached :avatar
13
- has_many_attached :highlights
14
- end
15
-
16
- class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
17
- include ActiveJob::TestHelper
18
-
19
- setup { @user = User.create!(name: "DHH") }
20
-
21
- teardown { ActiveStorage::Blob.all.each(&:purge) }
22
-
23
- test "attach existing blob" do
24
- @user.avatar.attach create_blob(filename: "funky.jpg")
25
- assert_equal "funky.jpg", @user.avatar.filename.to_s
26
- end
27
-
28
- test "attach new blob" do
29
- @user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
30
- assert_equal "town.jpg", @user.avatar.filename.to_s
31
- end
32
-
33
- test "purge attached blob" do
34
- @user.avatar.attach create_blob(filename: "funky.jpg")
35
- avatar_key = @user.avatar.key
36
-
37
- @user.avatar.purge
38
- assert_not @user.avatar.attached?
39
- assert_not ActiveStorage::Blob.service.exist?(avatar_key)
40
- end
41
-
42
- test "purge attached blob later when the record is destroyed" do
43
- @user.avatar.attach create_blob(filename: "funky.jpg")
44
- avatar_key = @user.avatar.key
45
-
46
- perform_enqueued_jobs do
47
- @user.destroy
48
-
49
- assert_nil ActiveStorage::Blob.find_by(key: avatar_key)
50
- assert_not ActiveStorage::Blob.service.exist?(avatar_key)
51
- end
52
- end
53
-
54
-
55
- test "attach existing blobs" do
56
- @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
57
-
58
- assert_equal "funky.jpg", @user.highlights.first.filename.to_s
59
- assert_equal "wonky.jpg", @user.highlights.second.filename.to_s
60
- end
61
-
62
- test "attach new blobs" do
63
- @user.highlights.attach(
64
- { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
65
- { io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
66
-
67
- assert_equal "town.jpg", @user.highlights.first.filename.to_s
68
- assert_equal "country.jpg", @user.highlights.second.filename.to_s
69
- end
70
-
71
- test "purge attached blobs" do
72
- @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
73
- highlight_keys = @user.highlights.collect(&:key)
74
-
75
- @user.highlights.purge
76
- assert_not @user.highlights.attached?
77
- assert_not ActiveStorage::Blob.service.exist?(highlight_keys.first)
78
- assert_not ActiveStorage::Blob.service.exist?(highlight_keys.second)
79
- end
80
-
81
- test "purge attached blobs later when the record is destroyed" do
82
- @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
83
- highlight_keys = @user.highlights.collect(&:key)
84
-
85
- perform_enqueued_jobs do
86
- @user.destroy
87
-
88
- assert_nil ActiveStorage::Blob.find_by(key: highlight_keys.first)
89
- assert_not ActiveStorage::Blob.service.exist?(highlight_keys.first)
90
-
91
- assert_nil ActiveStorage::Blob.find_by(key: highlight_keys.second)
92
- assert_not ActiveStorage::Blob.service.exist?(highlight_keys.second)
93
- end
94
- end
95
- end
data/test/blob_test.rb DELETED
@@ -1,28 +0,0 @@
1
- require "test_helper"
2
- require "database/setup"
3
- require "active_storage/blob"
4
-
5
- class ActiveStorage::BlobTest < ActiveSupport::TestCase
6
- test "create after upload sets byte size and checksum" do
7
- data = "Hello world!"
8
- blob = create_blob data: data
9
-
10
- assert_equal data, blob.download
11
- assert_equal data.length, blob.byte_size
12
- assert_equal Digest::MD5.base64digest(data), blob.checksum
13
- end
14
-
15
- test "urls expiring in 5 minutes" do
16
- blob = create_blob
17
-
18
- travel_to Time.now do
19
- assert_equal expected_url_for(blob), blob.url
20
- assert_equal expected_url_for(blob, disposition: :attachment), blob.url(disposition: :attachment)
21
- end
22
- end
23
-
24
- private
25
- def expected_url_for(blob, disposition: :inline)
26
- "/rails/blobs/#{ActiveStorage::VerifiedKeyWithExpiration.encode(blob.key, expires_in: 5.minutes)}?disposition=#{disposition}"
27
- end
28
- end
@@ -1,7 +0,0 @@
1
- class ActiveStorage::CreateUsers < ActiveRecord::Migration[5.1]
2
- def change
3
- create_table :users do |t|
4
- t.string :name
5
- end
6
- end
7
- end
@@ -1,6 +0,0 @@
1
- require "active_storage/migration"
2
- require_relative "create_users_migration"
3
-
4
- ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
5
- ActiveStorage::CreateTables.migrate(:up)
6
- ActiveStorage::CreateUsers.migrate(:up)
@@ -1,34 +0,0 @@
1
- require "test_helper"
2
- require "database/setup"
3
-
4
- require "action_controller"
5
- require "action_controller/test_case"
6
-
7
- require "active_storage/disk_controller"
8
- require "active_storage/verified_key_with_expiration"
9
-
10
- class ActiveStorage::DiskControllerTest < ActionController::TestCase
11
- Routes = ActionDispatch::Routing::RouteSet.new.tap do |routes|
12
- routes.draw do
13
- get "/rails/blobs/:encoded_key" => "active_storage/disk#show", as: :rails_disk_blob
14
- end
15
- end
16
-
17
- setup do
18
- @blob = create_blob
19
- @routes = Routes
20
- @controller = ActiveStorage::DiskController.new
21
- end
22
-
23
- test "showing blob inline" do
24
- get :show, params: { encoded_key: ActiveStorage::VerifiedKeyWithExpiration.encode(@blob.key, expires_in: 5.minutes) }
25
- assert_equal "inline; filename=\"#{@blob.filename}\"", @response.headers["Content-Disposition"]
26
- assert_equal "text/plain", @response.headers["Content-Type"]
27
- end
28
-
29
- test "sending blob as attachment" do
30
- get :show, params: { encoded_key: ActiveStorage::VerifiedKeyWithExpiration.encode(@blob.key, expires_in: 5.minutes), disposition: :attachment }
31
- assert_equal "attachment; filename=\"#{@blob.filename}\"", @response.headers["Content-Disposition"]
32
- assert_equal "text/plain", @response.headers["Content-Type"]
33
- end
34
- end
@@ -1,36 +0,0 @@
1
- require "test_helper"
2
-
3
- class ActiveStorage::FilenameTest < ActiveSupport::TestCase
4
- test "sanitize" do
5
- "%$|:;/\t\r\n\\".each_char do |character|
6
- filename = ActiveStorage::Filename.new("foo#{character}bar.pdf")
7
- assert_equal 'foo-bar.pdf', filename.sanitized
8
- assert_equal 'foo-bar.pdf', filename.to_s
9
- end
10
- end
11
-
12
- test "sanitize transcodes to valid UTF-8" do
13
- { "\xF6".force_encoding(Encoding::ISO8859_1) => "ö",
14
- "\xC3".force_encoding(Encoding::ISO8859_1) => "Ã",
15
- "\xAD" => "�",
16
- "\xCF" => "�",
17
- "\x00" => "",
18
- }.each do |actual, expected|
19
- assert_equal expected, ActiveStorage::Filename.new(actual).sanitized
20
- end
21
- end
22
-
23
- test "strips RTL override chars used to spoof unsafe executables as docs" do
24
- # Would be displayed in Windows as "evilexe.pdf" due to the right-to-left
25
- # (RTL) override char!
26
- assert_equal 'evil-fdp.exe', ActiveStorage::Filename.new("evil\u{202E}fdp.exe").sanitized
27
- end
28
-
29
- test "compare case-insensitively" do
30
- assert_operator ActiveStorage::Filename.new('foobar.pdf'), :==, ActiveStorage::Filename.new('FooBar.PDF')
31
- end
32
-
33
- test "compare sanitized" do
34
- assert_operator ActiveStorage::Filename.new('foo-bar.pdf'), :==, ActiveStorage::Filename.new("foo\tbar.pdf")
35
- end
36
- end
@@ -1 +0,0 @@
1
- configurations.yml
@@ -1,11 +0,0 @@
1
- # Copy this file to configurations.yml and edit the credentials to match your IAM test account and bucket
2
- s3:
3
- access_key_id:
4
- secret_access_key:
5
- region:
6
- bucket:
7
-
8
- gcs:
9
- project:
10
- keyfile:
11
- bucket:
@@ -1,8 +0,0 @@
1
- require "tmpdir"
2
- require "service/shared_service_tests"
3
-
4
- class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
5
- SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
6
-
7
- include ActiveStorage::Service::SharedServiceTests
8
- end
@@ -1,20 +0,0 @@
1
- require "service/shared_service_tests"
2
-
3
- if SERVICE_CONFIGURATIONS[:gcs]
4
- class ActiveStorage::Service::GCSServiceTest < ActiveSupport::TestCase
5
- SERVICE = ActiveStorage::Service.configure(:GCS, SERVICE_CONFIGURATIONS[:gcs])
6
-
7
- include ActiveStorage::Service::SharedServiceTests
8
-
9
- test "signed URL generation" do
10
- travel_to Time.now do
11
- url = SERVICE.bucket.signed_url(FIXTURE_KEY, expires: 120) +
12
- "&response-content-disposition=inline%3B+filename%3D%22test.txt%22"
13
-
14
- assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt")
15
- end
16
- end
17
- end
18
- else
19
- puts "Skipping GCS Service tests because no GCS configuration was supplied"
20
- end
@@ -1,50 +0,0 @@
1
- require "tmpdir"
2
- require "service/shared_service_tests"
3
-
4
- class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
5
- PRIMARY_DISK_SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
6
- SECONDARY_DISK_SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage_mirror"))
7
-
8
- SERVICE = ActiveStorage::Service.configure :Mirror, services: [ PRIMARY_DISK_SERVICE, SECONDARY_DISK_SERVICE ]
9
-
10
- include ActiveStorage::Service::SharedServiceTests
11
-
12
- test "uploading to all services" do
13
- begin
14
- data = "Something else entirely!"
15
- key = upload(data, to: @service)
16
-
17
- assert_equal data, PRIMARY_DISK_SERVICE.download(key)
18
- assert_equal data, SECONDARY_DISK_SERVICE.download(key)
19
- ensure
20
- @service.delete key
21
- end
22
- end
23
-
24
- test "downloading from primary service" do
25
- data = "Something else entirely!"
26
- key = upload(data, to: PRIMARY_DISK_SERVICE)
27
-
28
- assert_equal data, @service.download(key)
29
- end
30
-
31
- test "deleting from all services" do
32
- @service.delete FIXTURE_KEY
33
- assert_not PRIMARY_DISK_SERVICE.exist?(FIXTURE_KEY)
34
- assert_not SECONDARY_DISK_SERVICE.exist?(FIXTURE_KEY)
35
- end
36
-
37
- test "URL generation in primary service" do
38
- travel_to Time.now do
39
- assert_equal PRIMARY_DISK_SERVICE.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt"),
40
- @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt")
41
- end
42
- end
43
-
44
- private
45
- def upload(data, to:)
46
- SecureRandom.base58(24).tap do |key|
47
- @service.upload key, StringIO.new(data), checksum: Digest::MD5.base64digest(data)
48
- end
49
- end
50
- end
@@ -1,11 +0,0 @@
1
- require "service/shared_service_tests"
2
-
3
- if SERVICE_CONFIGURATIONS[:s3]
4
- class ActiveStorage::Service::S3ServiceTest < ActiveSupport::TestCase
5
- SERVICE = ActiveStorage::Service.configure(:S3, SERVICE_CONFIGURATIONS[:s3])
6
-
7
- include ActiveStorage::Service::SharedServiceTests
8
- end
9
- else
10
- puts "Skipping S3 Service tests because no S3 configuration was supplied"
11
- end
@@ -1,68 +0,0 @@
1
- require "test_helper"
2
- require "active_support/core_ext/securerandom"
3
- require "yaml"
4
-
5
- SERVICE_CONFIGURATIONS = begin
6
- YAML.load_file(File.expand_path("../configurations.yml", __FILE__)).deep_symbolize_keys
7
- rescue Errno::ENOENT
8
- puts "Missing service configuration file in test/services/configurations.yml"
9
- end
10
-
11
- module ActiveStorage::Service::SharedServiceTests
12
- extend ActiveSupport::Concern
13
-
14
- FIXTURE_KEY = SecureRandom.base58(24)
15
- FIXTURE_FILE = StringIO.new("Hello world!")
16
-
17
- included do
18
- setup do
19
- @service = self.class.const_get(:SERVICE)
20
- @service.upload FIXTURE_KEY, FIXTURE_FILE
21
- FIXTURE_FILE.rewind
22
- end
23
-
24
- teardown do
25
- @service.delete FIXTURE_KEY
26
- FIXTURE_FILE.rewind
27
- end
28
-
29
- test "uploading with integrity" do
30
- begin
31
- key = SecureRandom.base58(24)
32
- data = "Something else entirely!"
33
- @service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data))
34
-
35
- assert_equal data, @service.download(key)
36
- ensure
37
- @service.delete key
38
- end
39
- end
40
-
41
- test "upload without integrity" do
42
- begin
43
- key = SecureRandom.base58(24)
44
- data = "Something else entirely!"
45
-
46
- assert_raises(ActiveStorage::IntegrityError) do
47
- @service.upload(key, StringIO.new(data), checksum: "BAD_CHECKSUM")
48
- end
49
- ensure
50
- @service.delete key
51
- end
52
- end
53
-
54
- test "downloading" do
55
- assert_equal FIXTURE_FILE.read, @service.download(FIXTURE_KEY)
56
- end
57
-
58
- test "existing" do
59
- assert @service.exist?(FIXTURE_KEY)
60
- assert_not @service.exist?(FIXTURE_KEY + "nonsense")
61
- end
62
-
63
- test "deleting" do
64
- @service.delete FIXTURE_KEY
65
- assert_not @service.exist?(FIXTURE_KEY)
66
- end
67
- end
68
- end
data/test/test_helper.rb DELETED
@@ -1,28 +0,0 @@
1
- require "bundler/setup"
2
- require "active_support"
3
- require "active_support/test_case"
4
- require "active_support/testing/autorun"
5
- require "byebug"
6
-
7
- require "active_storage"
8
-
9
- require "active_storage/service"
10
- ActiveStorage::Blob.service = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
11
-
12
- require "active_storage/verified_key_with_expiration"
13
- ActiveStorage::VerifiedKeyWithExpiration.verifier = ActiveSupport::MessageVerifier.new("Testing")
14
-
15
- class ActiveSupport::TestCase
16
- private
17
- def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
18
- ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type
19
- end
20
- end
21
-
22
-
23
- require "active_storage/attached"
24
- ActiveRecord::Base.send :extend, ActiveStorage::Attached::Macros
25
-
26
- require "global_id"
27
- GlobalID.app = "ActiveStorageExampleApp"
28
- ActiveRecord::Base.send :include, GlobalID::Identification
@@ -1,19 +0,0 @@
1
- require "test_helper"
2
- require "active_support/core_ext/securerandom"
3
-
4
- class ActiveStorage::VerifiedKeyWithExpirationTest < ActiveSupport::TestCase
5
- FIXTURE_KEY = SecureRandom.base58(24)
6
-
7
- test "without expiration" do
8
- encoded_key = ActiveStorage::VerifiedKeyWithExpiration.encode(FIXTURE_KEY)
9
- assert_equal FIXTURE_KEY, ActiveStorage::VerifiedKeyWithExpiration.decode(encoded_key)
10
- end
11
-
12
- test "with expiration" do
13
- encoded_key = ActiveStorage::VerifiedKeyWithExpiration.encode(FIXTURE_KEY, expires_in: 1.minute)
14
- assert_equal FIXTURE_KEY, ActiveStorage::VerifiedKeyWithExpiration.decode(encoded_key)
15
-
16
- travel 2.minutes
17
- assert_nil ActiveStorage::VerifiedKeyWithExpiration.decode(encoded_key)
18
- end
19
- end