activestorage-memory 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +28 -7
- data/app/controllers/activestorage/memory/memory_controller.rb +60 -0
- data/config/routes.rb +6 -0
- data/lib/active_storage/service/memory_service.rb +102 -12
- data/lib/activestorage/memory/engine.rb +7 -0
- data/lib/{active_storage → activestorage}/memory/version.rb +2 -2
- data/lib/{active_storage → activestorage}/memory.rb +2 -1
- data/spec/active_storage/service/memory_service_spec.rb +99 -0
- data/spec/activestorage/memory_spec.rb +7 -0
- data/spec/controllers/activestorage/memory/memory_controller_spec.rb +136 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/jobs/application_job.rb +7 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +15 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +33 -0
- data/spec/dummy/config/application.rb +44 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +76 -0
- data/spec/dummy/config/environments/production.rb +97 -0
- data/spec/dummy/config/environments/test.rb +66 -0
- data/spec/dummy/config/initializers/assets.rb +12 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +8 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/permissions_policy.rb +13 -0
- data/spec/dummy/config/locales/en.yml +31 -0
- data/spec/dummy/config/puma.rb +35 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/storage.yml +37 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/db/migrate/20240413101449_create_active_storage_tables.active_storage.rb +57 -0
- data/spec/dummy/db/schema.rb +44 -0
- data/spec/dummy/log/development.log +53 -0
- data/spec/dummy/log/test.log +4820 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/storage/development.sqlite3 +0 -0
- data/spec/dummy/storage/test.sqlite3 +0 -0
- data/spec/dummy/tmp/local_secret.txt +1 -0
- data/spec/dummy/tmp/restart.txt +0 -0
- data/spec/rails_helper.rb +65 -0
- data/spec/spec_helper.rb +15 -0
- metadata +119 -19
- data/.github/workflows/main.yml +0 -18
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/Gemfile +0 -10
- data/Gemfile.lock +0 -204
- data/LICENSE.txt +0 -21
- data/activestorage-memory.gemspec +0 -36
- data/bin/console +0 -15
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b5979d939220916998f7502ffce56be3fbf14328601cdcfae7d3aeaaa181ce2
|
4
|
+
data.tar.gz: 615c9f1c2472e74058a353f1c17c79a80eee114a6fb97a770973f68dd0c1fde9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0161b44194c712db207d028801bb57174ed2ba05322ae056b6989a690e870df5d4769316313bae0f0c1df4e9bb9ccf0d434f6e0cffdc6f02147f41b95534bc5b
|
7
|
+
data.tar.gz: 2698dd5ca0974dcdc6443dbcde2065822e2b32f91a13da1d22c6f7b07f5f423b2d0c9e0ee50b70401cd38e904500c58de7206a4cac41582a9504af4f0a77eb0e
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
# ActiveStorage
|
1
|
+
# ActiveStorage-Memory
|
2
2
|
|
3
|
-
|
3
|
+
Provides an in-memory ActiveStorage service.
|
4
4
|
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
6
5
|
|
7
6
|
## Installation
|
8
7
|
|
@@ -22,13 +21,35 @@ Or install it yourself as:
|
|
22
21
|
|
23
22
|
## Usage
|
24
23
|
|
25
|
-
|
24
|
+
Declare a Memory service in config/storage.yml
|
25
|
+
|
26
|
+
```
|
27
|
+
memory:
|
28
|
+
service: Memory
|
29
|
+
```
|
30
|
+
|
31
|
+
To use the Memory service in test, you add the following to config/environments/test.rb:
|
32
|
+
|
33
|
+
```
|
34
|
+
config.active_storage.service = :memory
|
35
|
+
```
|
36
|
+
|
37
|
+
In Active Storage's analyzer feature, asynchronous jobs are executed. So you should set the queue adapter to inline at config/environments/test.
|
38
|
+
```
|
39
|
+
config.active_job.queue_adapter = :inline
|
40
|
+
```
|
41
|
+
|
42
|
+
If you are conducting file downloads and uploads during system testing or integration testing, please add the following to the routing.
|
43
|
+
|
44
|
+
``` config/routes.rb
|
45
|
+
|
46
|
+
mount ActiveStorage::Memory::Engine => "/" if Rails.env.test?
|
47
|
+
|
48
|
+
```
|
26
49
|
|
27
|
-
## Development
|
28
50
|
|
29
|
-
|
51
|
+
You can read more about Active Storage in the Active Storage Overview guide.
|
30
52
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
53
|
|
33
54
|
## Contributing
|
34
55
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Activestorage::Memory
|
4
|
+
class MemoryController < ActiveStorage::BaseController
|
5
|
+
skip_forgery_protection
|
6
|
+
|
7
|
+
def show
|
8
|
+
key = decode_verified_key
|
9
|
+
|
10
|
+
unless key
|
11
|
+
head :not_found
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
service = named_memory_service(key[:service_name])
|
16
|
+
if service.exist?(key[:key])
|
17
|
+
send_data(service.store[key[:key]], content_type: key[:content_type], disposition: key[:disposition])
|
18
|
+
else
|
19
|
+
head :not_found
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def update
|
24
|
+
token = decode_verified_token
|
25
|
+
|
26
|
+
unless token
|
27
|
+
head :not_found
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
unless acceptable_content?(token)
|
32
|
+
head :unprocessable_entity
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
named_memory_service(token[:service_name]).upload token[:key], request.body, checksum: token[:checksum]
|
37
|
+
head :no_content
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def named_memory_service(name)
|
43
|
+
ActiveStorage::Blob.services.fetch(name) do
|
44
|
+
ActiveStorage::Blob.service
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def decode_verified_key
|
49
|
+
ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)&.deep_symbolize_keys
|
50
|
+
end
|
51
|
+
|
52
|
+
def decode_verified_token
|
53
|
+
ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)&.deep_symbolize_keys
|
54
|
+
end
|
55
|
+
|
56
|
+
def acceptable_content?(token)
|
57
|
+
token[:content_type] == request.content_mime_type.to_s && token[:content_length] == request.content_length
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/config/routes.rb
ADDED
@@ -11,19 +11,26 @@ module ActiveStorage
|
|
11
11
|
@config = config
|
12
12
|
end
|
13
13
|
|
14
|
-
def upload(key, io, **)
|
14
|
+
def upload(key, io, checksum: nil, **)
|
15
15
|
instrument(:upload, key: key) do
|
16
16
|
store[key] = io.read
|
17
|
+
ensure_integrity_of(key, checksum) if checksum
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
|
-
def download(key)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
def download(key, &block)
|
22
|
+
if block_given?
|
23
|
+
instrument(:streaming_download, key: key) do
|
24
|
+
stream key, &block
|
25
|
+
end
|
26
|
+
else
|
27
|
+
instrument(:download, key: key) do
|
28
|
+
io = StringIO.new(store.fetch(key))
|
29
|
+
io.set_encoding(io.string.encoding)
|
30
|
+
io
|
31
|
+
rescue KeyError
|
32
|
+
raise ActiveStorage::FileNotFoundError
|
33
|
+
end
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -43,11 +50,94 @@ module ActiveStorage
|
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
46
|
-
def
|
47
|
-
instrument
|
48
|
-
|
49
|
-
|
53
|
+
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, **)
|
54
|
+
instrument :url, key: key do |payload|
|
55
|
+
verified_token_with_expiration = generate_verified_token(
|
56
|
+
key,
|
57
|
+
expires_in: expires_in,
|
58
|
+
content_type: content_type,
|
59
|
+
content_length: content_length,
|
60
|
+
checksum: checksum
|
61
|
+
)
|
62
|
+
url_helpers.update_rails_memory_service_url(
|
63
|
+
verified_token_with_expiration,
|
64
|
+
url_options
|
65
|
+
).tap do |generated_url|
|
66
|
+
payload[:url] = generated_url
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def headers_for_direct_upload(_key, content_type:, **)
|
72
|
+
{ 'Content-Type' => content_type }
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def stream(key)
|
78
|
+
io = StringIO.new(store.fetch(key))
|
79
|
+
while data = io.read(5.megabytes)
|
80
|
+
yield data
|
50
81
|
end
|
82
|
+
rescue KeyError
|
83
|
+
raise ActiveStorage::FileNotFoundError
|
84
|
+
end
|
85
|
+
|
86
|
+
def url_helpers
|
87
|
+
@url_helpers ||= Activestorage::Memory::Engine.routes.url_helpers
|
88
|
+
end
|
89
|
+
|
90
|
+
def generate_verified_token(key, expires_in:, content_type:, content_length:, checksum:)
|
91
|
+
ActiveStorage.verifier.generate(
|
92
|
+
{
|
93
|
+
key: key,
|
94
|
+
content_type: content_type,
|
95
|
+
content_length: content_length,
|
96
|
+
checksum: checksum,
|
97
|
+
service_name: name
|
98
|
+
},
|
99
|
+
expires_in: expires_in,
|
100
|
+
purpose: :blob_token
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
def private_url(key, expires_in:, filename:, content_type:, disposition: :inline, **)
|
105
|
+
generate_url(key, expires_in: expires_in, filename: filename, content_type: content_type, disposition: disposition)
|
106
|
+
end
|
107
|
+
|
108
|
+
def public_url(key, filename:, content_type: nil, disposition: :attachment, **)
|
109
|
+
generate_url(key, expires_in: nil, filename: filename, content_type: content_type, disposition: disposition)
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_url(key, expires_in:, filename:, content_type:, disposition:)
|
113
|
+
content_disposition = content_disposition_with(type: disposition, filename: ActiveStorage::Filename.wrap(filename))
|
114
|
+
verified_key_with_expiration = ActiveStorage.verifier.generate(
|
115
|
+
{
|
116
|
+
key: key,
|
117
|
+
disposition: content_disposition,
|
118
|
+
content_type: content_type,
|
119
|
+
service_name: name
|
120
|
+
},
|
121
|
+
expires_in: expires_in,
|
122
|
+
purpose: :blob_key
|
123
|
+
)
|
124
|
+
|
125
|
+
if url_options.blank?
|
126
|
+
raise ArgumentError, "Cannot generate URL for #{filename} using Memory service, please set ActiveStorage::Current.url_options."
|
127
|
+
end
|
128
|
+
|
129
|
+
url_helpers.rails_memory_service_url(verified_key_with_expiration, filename: filename, **url_options)
|
130
|
+
end
|
131
|
+
|
132
|
+
def url_options
|
133
|
+
ActiveStorage::Current.url_options || Rails.application.default_url_options
|
134
|
+
end
|
135
|
+
|
136
|
+
def ensure_integrity_of(key, checksum)
|
137
|
+
return if OpenSSL::Digest.new('md5', store[key]).base64digest == checksum
|
138
|
+
|
139
|
+
delete key
|
140
|
+
raise ActiveStorage::IntegrityError
|
51
141
|
end
|
52
142
|
end
|
53
143
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe ActiveStorage::Service::MemoryService do
|
4
|
+
let(:service) { ActiveStorage::Service::MemoryService.new }
|
5
|
+
let(:content) { 'content' }
|
6
|
+
let(:io) { StringIO.new(content) }
|
7
|
+
let(:key) { 'key' }
|
8
|
+
let(:host) { 'example.com' }
|
9
|
+
|
10
|
+
before do
|
11
|
+
ActiveStorage::Current.url_options = { host: host }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#upload' do
|
15
|
+
it 'stores by key' do
|
16
|
+
service.upload(key, io)
|
17
|
+
expect(service.store[key]).to eq(content)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#download' do
|
22
|
+
context 'when key does not exist' do
|
23
|
+
it 'raises ActiveStorage::FileNotFoundError' do
|
24
|
+
expect { service.download(key) }.to raise_error(ActiveStorage::FileNotFoundError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when key exists' do
|
29
|
+
before do
|
30
|
+
service.upload(key, io)
|
31
|
+
end
|
32
|
+
it 'retrieves by key' do
|
33
|
+
expect(service.download(key).read).to eq(content)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#delete' do
|
39
|
+
context 'when key does not exist' do
|
40
|
+
it 'ignores key errors' do
|
41
|
+
expect { service.delete(key) }.not_to raise_error
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when key exists' do
|
46
|
+
before do
|
47
|
+
service.upload(key, io)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'deletes by key' do
|
51
|
+
service.delete(key)
|
52
|
+
expect(service.store).not_to have_key(key)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#exist?' do
|
58
|
+
context 'when key does not exist' do
|
59
|
+
it 'returns false' do
|
60
|
+
expect(service.exist?(key)).to be false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when key exists' do
|
65
|
+
before do
|
66
|
+
service.upload(key, io)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'checks by key' do
|
70
|
+
expect(service.exist?(key)).to be true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#url_for_direct_upload' do
|
76
|
+
let(:filename) { 'filename' }
|
77
|
+
let(:content_type) { 'image/jpeg' }
|
78
|
+
let(:content_length) { content.size }
|
79
|
+
let(:checksum) { OpenSSL::Digest.new('md5', content).base64digest }
|
80
|
+
let(:service_name) { 'memory' }
|
81
|
+
let(:expires_in) { 5.minutes }
|
82
|
+
|
83
|
+
before do
|
84
|
+
service.upload(key, io)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns a memory url' do
|
88
|
+
expect(service.url(key, expires_in: expires_in, filename: filename, content_type: content_type)).to start_with("http://#{host}/")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#headers_for_direct_upload' do
|
93
|
+
let(:content_type) { 'image/jpeg' }
|
94
|
+
|
95
|
+
it 'returns content type' do
|
96
|
+
expect(service.headers_for_direct_upload(key, content_type: content_type)).to eq('Content-Type' => content_type)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
RSpec.describe Activestorage::Memory::MemoryController, type: :request do
|
6
|
+
let(:key) { 'file_key' }
|
7
|
+
let(:content) { 'content' }
|
8
|
+
let(:filename) { 'file.jpg' }
|
9
|
+
let(:content_type) { 'image/jpeg' }
|
10
|
+
let(:service_name) { :memory }
|
11
|
+
let(:disposition) { "inline; filename=\"file.jpg\"; filename*=UTF-8''file.jpg" }
|
12
|
+
let(:expires_in) { ActiveStorage.service_urls_expire_in }
|
13
|
+
let(:checksum) { OpenSSL::Digest.new('md5', content).base64digest }
|
14
|
+
let!(:blob) do
|
15
|
+
ActiveStorage::Blob.create_and_upload!(
|
16
|
+
key: key,
|
17
|
+
io: StringIO.new(content),
|
18
|
+
filename: filename,
|
19
|
+
content_type: content_type,
|
20
|
+
service_name: service_name
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
ActiveStorage::Current.url_options = { only_path: true }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "GET #show" do
|
29
|
+
let(:valid_key) do
|
30
|
+
ActiveStorage.verifier.generate(
|
31
|
+
{
|
32
|
+
key: key,
|
33
|
+
service_name: service_name,
|
34
|
+
content_type: content_type,
|
35
|
+
disposition: disposition
|
36
|
+
},
|
37
|
+
expires_in: expires_in,
|
38
|
+
purpose: :blob_key
|
39
|
+
)
|
40
|
+
end
|
41
|
+
let(:invalid_key) { 'invalid_key' }
|
42
|
+
|
43
|
+
subject { get "/rails/active_storage/memory/#{valid_key}/#{filename}" }
|
44
|
+
|
45
|
+
context "when key is valid" do
|
46
|
+
it "returns http success if key is valid and file exists" do
|
47
|
+
subject
|
48
|
+
expect(response).to have_http_status(:success)
|
49
|
+
expect(response.body).to eq(content)
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when file does not exist" do
|
53
|
+
before do
|
54
|
+
ActiveStorage::Blob.services.fetch(service_name).delete(key)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns http not_found" do
|
58
|
+
subject
|
59
|
+
expect(response).to have_http_status(:not_found)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when key is invalid" do
|
65
|
+
subject { get "/rails/active_storage/memory/#{invalid_key}/#{filename}" }
|
66
|
+
|
67
|
+
it "returns http not_found" do
|
68
|
+
subject
|
69
|
+
expect(response).to have_http_status(:not_found)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "PATCH #update" do
|
75
|
+
let(:valid_token) do
|
76
|
+
ActiveStorage.verifier.generate(
|
77
|
+
{
|
78
|
+
key: key,
|
79
|
+
service_name: service_name,
|
80
|
+
checksum: checksum,
|
81
|
+
content_type: content_type,
|
82
|
+
content_length: content.length
|
83
|
+
},
|
84
|
+
purpose: :blob_token
|
85
|
+
)
|
86
|
+
end
|
87
|
+
let(:invalid_token) { 'invalid_token' }
|
88
|
+
|
89
|
+
context "when token is valid" do
|
90
|
+
context "when content is acceptable" do
|
91
|
+
subject do
|
92
|
+
put(
|
93
|
+
"/rails/active_storage/memory/#{valid_token}",
|
94
|
+
params: content,
|
95
|
+
headers: { 'Content-Type' => content_type, 'Content-Length' => content.size }
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "returns http no_content if token is valid and content is acceptable" do
|
100
|
+
subject
|
101
|
+
expect(response).to have_http_status(:no_content)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when content is not acceptable" do
|
106
|
+
subject do
|
107
|
+
put(
|
108
|
+
"/rails/active_storage/memory/#{valid_token}",
|
109
|
+
params: content,
|
110
|
+
headers: { 'Content-Type' => "image/png", 'Content-Length' => content.size }
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns http unprocessable_entity" do
|
115
|
+
subject
|
116
|
+
expect(response).to have_http_status(:unprocessable_entity)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "when token is invalid" do
|
122
|
+
subject do
|
123
|
+
put(
|
124
|
+
"/rails/active_storage/memory/#{invalid_token}",
|
125
|
+
params: content,
|
126
|
+
headers: { 'Content-Type' => content_type, 'Content-Length' => content.size }
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "returns http not_found" do
|
131
|
+
subject
|
132
|
+
expect(response).to have_http_status(:not_found)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/spec/dummy/Rakefile
ADDED
@@ -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,7 @@
|
|
1
|
+
class ApplicationJob < ActiveJob::Base
|
2
|
+
# Automatically retry jobs that encountered a deadlock
|
3
|
+
# retry_on ActiveRecord::Deadlocked
|
4
|
+
|
5
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
6
|
+
# discard_on ActiveJob::DeserializationError
|
7
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Dummy</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<%= csrf_meta_tags %>
|
7
|
+
<%= csp_meta_tag %>
|
8
|
+
|
9
|
+
<%= stylesheet_link_tag "application" %>
|
10
|
+
</head>
|
11
|
+
|
12
|
+
<body>
|
13
|
+
<%= yield %>
|
14
|
+
</body>
|
15
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= yield %>
|
data/spec/dummy/bin/rake
ADDED