activestorage-memory 0.1.1 → 0.2.1

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -2
  3. data/app/controllers/activestorage/memory/memory_controller.rb +60 -0
  4. data/config/routes.rb +6 -0
  5. data/lib/active_storage/service/memory_service.rb +86 -5
  6. data/lib/activestorage/memory/engine.rb +7 -0
  7. data/lib/{active_storage → activestorage}/memory/version.rb +2 -2
  8. data/lib/{active_storage → activestorage}/memory.rb +2 -1
  9. data/spec/active_storage/service/memory_service_spec.rb +99 -0
  10. data/spec/activestorage/memory_spec.rb +7 -0
  11. data/spec/controllers/activestorage/memory/memory_controller_spec.rb +136 -0
  12. data/spec/dummy/Rakefile +6 -0
  13. data/spec/dummy/app/assets/config/manifest.js +3 -0
  14. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  15. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  16. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  17. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  18. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  19. data/spec/dummy/app/jobs/application_job.rb +7 -0
  20. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  21. data/spec/dummy/app/models/application_record.rb +3 -0
  22. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  23. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  24. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  25. data/spec/dummy/bin/rails +4 -0
  26. data/spec/dummy/bin/rake +4 -0
  27. data/spec/dummy/bin/setup +33 -0
  28. data/spec/dummy/config/application.rb +44 -0
  29. data/spec/dummy/config/boot.rb +5 -0
  30. data/spec/dummy/config/cable.yml +10 -0
  31. data/spec/dummy/config/database.yml +25 -0
  32. data/spec/dummy/config/environment.rb +5 -0
  33. data/spec/dummy/config/environments/development.rb +76 -0
  34. data/spec/dummy/config/environments/production.rb +97 -0
  35. data/spec/dummy/config/environments/test.rb +66 -0
  36. data/spec/dummy/config/initializers/assets.rb +12 -0
  37. data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
  38. data/spec/dummy/config/initializers/filter_parameter_logging.rb +8 -0
  39. data/spec/dummy/config/initializers/inflections.rb +16 -0
  40. data/spec/dummy/config/initializers/permissions_policy.rb +13 -0
  41. data/spec/dummy/config/locales/en.yml +31 -0
  42. data/spec/dummy/config/puma.rb +35 -0
  43. data/spec/dummy/config/routes.rb +3 -0
  44. data/spec/dummy/config/storage.yml +37 -0
  45. data/spec/dummy/config.ru +6 -0
  46. data/spec/dummy/db/migrate/20240413101449_create_active_storage_tables.active_storage.rb +57 -0
  47. data/spec/dummy/db/schema.rb +44 -0
  48. data/spec/dummy/log/development.log +53 -0
  49. data/spec/dummy/log/test.log +4820 -0
  50. data/spec/dummy/public/404.html +67 -0
  51. data/spec/dummy/public/422.html +67 -0
  52. data/spec/dummy/public/500.html +66 -0
  53. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  54. data/spec/dummy/public/apple-touch-icon.png +0 -0
  55. data/spec/dummy/public/favicon.ico +0 -0
  56. data/spec/dummy/storage/development.sqlite3 +0 -0
  57. data/spec/dummy/storage/test.sqlite3 +0 -0
  58. data/spec/dummy/tmp/local_secret.txt +1 -0
  59. data/spec/dummy/tmp/restart.txt +0 -0
  60. data/spec/rails_helper.rb +65 -0
  61. data/spec/spec_helper.rb +15 -0
  62. metadata +118 -18
  63. data/.github/workflows/main.yml +0 -18
  64. data/.gitignore +0 -11
  65. data/.rspec +0 -3
  66. data/Gemfile +0 -10
  67. data/Gemfile.lock +0 -204
  68. data/LICENSE.txt +0 -21
  69. data/activestorage-memory.gemspec +0 -36
  70. data/bin/console +0 -15
  71. data/bin/setup +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 768576d7f8ddab4a37c7d7926ff3ac799328f45030481cede0b0a58ea2e53d95
4
- data.tar.gz: 0af1f72600a0f6ba8b72448f5e2375d6439b5398235d9f9f4a0a9ec9d86cbdf6
3
+ metadata.gz: 83bd480b89220732381633492509732f10f905c5861e76078c4c6e6daba5538c
4
+ data.tar.gz: 16d37bbbe47843e3681d58145a0d994ea4aa7691084b4fba430d8dbe8dee3a98
5
5
  SHA512:
6
- metadata.gz: 0bc9b5759bf76f16ecb8d52f29b8a1daacdb32464030d1e9529d49629f6ef7a1ba177172608738d578df792a99027ac24cf657994e44c2b45df557f8fd4451e3
7
- data.tar.gz: afdccf65c826b80d12b322aafc364b602afd702e5527d6aa14be40628f81fc109bbf676dd8293bdcc16309e83366b723fb3fc1e7d2b70b580a545af522493063
6
+ metadata.gz: d1054af1c3c162d07abba56c9cfd3ff6a6ad1338873647171bd4361148563615d56780e2a76ee35d1bca8dee686c0c7f2c871310dca59c8ebfe3bdad4228e13d
7
+ data.tar.gz: fe03e7402ead1314a7666b4eb15622547cebda2a3103c774b6a3ca2d8655337d1d628c112c2b465a6ca7275088cf5d7f2a96f3c55e7326c00f794d47b4bd6ae7
data/README.md CHANGED
@@ -34,11 +34,20 @@ To use the Memory service in test, you add the following to config/environments/
34
34
  config.active_storage.service = :memory
35
35
  ```
36
36
 
37
- In Active Storage's analyzer feature, asynchronous jobs are executed. So it is recommended to set the queue adapter to async for test.
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
38
  ```
39
- config.active_job.queue_adapter = :async
39
+ config.active_job.queue_adapter = :inline
40
40
  ```
41
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
+ ```
49
+
50
+
42
51
  You can read more about Active Storage in the Active Storage Overview guide.
43
52
 
44
53
 
@@ -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
@@ -0,0 +1,6 @@
1
+ Activestorage::Memory::Engine.routes.draw do
2
+ scope ActiveStorage.routes_prefix do
3
+ get '/memory/:encoded_key/*filename' => 'memory#show', as: :rails_memory_service
4
+ put '/memory/:encoded_token' => 'memory#update', as: :update_rails_memory_service
5
+ end
6
+ end
@@ -11,9 +11,10 @@ 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
 
@@ -41,6 +42,14 @@ module ActiveStorage
41
42
  end
42
43
  end
43
44
 
45
+ def delete_prefixed(prefix)
46
+ instrument :delete_prefixed, prefix: prefix do
47
+ store.each_keys do |key|
48
+ store.delete(key) if key.start_with?(prefix)
49
+ end
50
+ end
51
+ end
52
+
44
53
  def exist?(key)
45
54
  instrument(:exist, key: key) do |payload|
46
55
  answer = store.key?(key)
@@ -49,13 +58,28 @@ module ActiveStorage
49
58
  end
50
59
  end
51
60
 
52
- def url(key)
53
- instrument(:url, key: key) do
54
- # FIXME: - this should be a URL that can be used to directly download the file
55
- "memory://#{key}"
61
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, **)
62
+ instrument :url, key: key do |payload|
63
+ verified_token_with_expiration = generate_verified_token(
64
+ key,
65
+ expires_in: expires_in,
66
+ content_type: content_type,
67
+ content_length: content_length,
68
+ checksum: checksum
69
+ )
70
+ url_helpers.update_rails_memory_service_url(
71
+ verified_token_with_expiration,
72
+ url_options
73
+ ).tap do |generated_url|
74
+ payload[:url] = generated_url
75
+ end
56
76
  end
57
77
  end
58
78
 
79
+ def headers_for_direct_upload(_key, content_type:, **)
80
+ { 'Content-Type' => content_type }
81
+ end
82
+
59
83
  private
60
84
 
61
85
  def stream(key)
@@ -66,5 +90,62 @@ module ActiveStorage
66
90
  rescue KeyError
67
91
  raise ActiveStorage::FileNotFoundError
68
92
  end
93
+
94
+ def url_helpers
95
+ @url_helpers ||= Activestorage::Memory::Engine.routes.url_helpers
96
+ end
97
+
98
+ def generate_verified_token(key, expires_in:, content_type:, content_length:, checksum:)
99
+ ActiveStorage.verifier.generate(
100
+ {
101
+ key: key,
102
+ content_type: content_type,
103
+ content_length: content_length,
104
+ checksum: checksum,
105
+ service_name: name
106
+ },
107
+ expires_in: expires_in,
108
+ purpose: :blob_token
109
+ )
110
+ end
111
+
112
+ def private_url(key, expires_in:, filename:, content_type:, disposition: :inline, **)
113
+ generate_url(key, expires_in: expires_in, filename: filename, content_type: content_type, disposition: disposition)
114
+ end
115
+
116
+ def public_url(key, filename:, content_type: nil, disposition: :attachment, **)
117
+ generate_url(key, expires_in: nil, filename: filename, content_type: content_type, disposition: disposition)
118
+ end
119
+
120
+ def generate_url(key, expires_in:, filename:, content_type:, disposition:)
121
+ content_disposition = content_disposition_with(type: disposition, filename: ActiveStorage::Filename.wrap(filename))
122
+ verified_key_with_expiration = ActiveStorage.verifier.generate(
123
+ {
124
+ key: key,
125
+ disposition: content_disposition,
126
+ content_type: content_type,
127
+ service_name: name
128
+ },
129
+ expires_in: expires_in,
130
+ purpose: :blob_key
131
+ )
132
+
133
+ if url_options.blank?
134
+ raise ArgumentError, "Cannot generate URL for #{filename} using Memory service, please set ActiveStorage::Current.url_options."
135
+ end
136
+
137
+ url_helpers.rails_memory_service_url(verified_key_with_expiration, filename: filename, **url_options)
138
+ end
139
+
140
+ def url_options
141
+ ActiveStorage::Current.url_options || Rails.application.default_url_options
142
+ end
143
+
144
+ def ensure_integrity_of(key, checksum)
145
+ return if OpenSSL::Digest.new('md5', store[key]).base64digest == checksum
146
+
147
+ delete key
148
+ raise ActiveStorage::IntegrityError
149
+ end
69
150
  end
70
151
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Activestorage::Memory
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Activestorage::Memory
6
+ end
7
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActiveStorage
3
+ module Activestorage
4
4
  module Memory
5
- VERSION = '0.1.1'
5
+ VERSION = '0.2.1'
6
6
  end
7
7
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "memory/version"
3
+ require_relative 'memory/version'
4
+ require_relative 'memory/engine'
4
5
 
5
6
  module Activestorage
6
7
  module Memory
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Activestorage::Memory do
4
+ it "has a version number" do
5
+ expect(Activestorage::Memory::VERSION).not_to be nil
6
+ end
7
+ 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
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative "config/application"
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../stylesheets .css
3
+ //= link my_engine_manifest.js
@@ -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,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
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -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,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
+ primary_abstract_class
3
+ 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,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 %>
@@ -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,33 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ # path to your application root.
5
+ APP_ROOT = File.expand_path("..", __dir__)
6
+
7
+ def system!(*args)
8
+ system(*args, exception: true)
9
+ end
10
+
11
+ FileUtils.chdir APP_ROOT do
12
+ # This script is a way to set up or update your development environment automatically.
13
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14
+ # Add necessary setup steps to this file.
15
+
16
+ puts "== Installing dependencies =="
17
+ system! "gem install bundler --conservative"
18
+ system("bundle check") || system!("bundle install")
19
+
20
+ # puts "\n== Copying sample files =="
21
+ # unless File.exist?("config/database.yml")
22
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
23
+ # end
24
+
25
+ puts "\n== Preparing database =="
26
+ system! "bin/rails db:prepare"
27
+
28
+ puts "\n== Removing old logs and tempfiles =="
29
+ system! "bin/rails log:clear tmp:clear"
30
+
31
+ puts "\n== Restarting application server =="
32
+ system! "bin/rails restart"
33
+ end