activestorage-memory 0.1.1 → 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.
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 +78 -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: 1b5979d939220916998f7502ffce56be3fbf14328601cdcfae7d3aeaaa181ce2
4
+ data.tar.gz: 615c9f1c2472e74058a353f1c17c79a80eee114a6fb97a770973f68dd0c1fde9
5
5
  SHA512:
6
- metadata.gz: 0bc9b5759bf76f16ecb8d52f29b8a1daacdb32464030d1e9529d49629f6ef7a1ba177172608738d578df792a99027ac24cf657994e44c2b45df557f8fd4451e3
7
- data.tar.gz: afdccf65c826b80d12b322aafc364b602afd702e5527d6aa14be40628f81fc109bbf676dd8293bdcc16309e83366b723fb3fc1e7d2b70b580a545af522493063
6
+ metadata.gz: 0161b44194c712db207d028801bb57174ed2ba05322ae056b6989a690e870df5d4769316313bae0f0c1df4e9bb9ccf0d434f6e0cffdc6f02147f41b95534bc5b
7
+ data.tar.gz: 2698dd5ca0974dcdc6443dbcde2065822e2b32f91a13da1d22c6f7b07f5f423b2d0c9e0ee50b70401cd38e904500c58de7206a4cac41582a9504af4f0a77eb0e
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
 
@@ -49,13 +50,28 @@ module ActiveStorage
49
50
  end
50
51
  end
51
52
 
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}"
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
56
68
  end
57
69
  end
58
70
 
71
+ def headers_for_direct_upload(_key, content_type:, **)
72
+ { 'Content-Type' => content_type }
73
+ end
74
+
59
75
  private
60
76
 
61
77
  def stream(key)
@@ -66,5 +82,62 @@ module ActiveStorage
66
82
  rescue KeyError
67
83
  raise ActiveStorage::FileNotFoundError
68
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
141
+ end
69
142
  end
70
143
  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.0'
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
@@ -0,0 +1,44 @@
1
+ require_relative "boot"
2
+
3
+ require "rails"
4
+ # Pick the frameworks you want:
5
+ require "active_model/railtie"
6
+ require "active_job/railtie"
7
+ require "active_record/railtie"
8
+ require "active_storage/engine"
9
+ require "action_controller/railtie"
10
+ require "action_mailer/railtie"
11
+ require "action_mailbox/engine"
12
+ require "action_text/engine"
13
+ require "action_view/railtie"
14
+ require "action_cable/engine"
15
+ # require "rails/test_unit/railtie"
16
+
17
+ # Require the gems listed in Gemfile, including any gems
18
+ # you've limited to :test, :development, or :production.
19
+ Bundler.require(*Rails.groups)
20
+
21
+ module Dummy
22
+ class Application < Rails::Application
23
+ config.load_defaults Rails::VERSION::STRING.to_f
24
+
25
+ # For compatibility with applications that use this config
26
+ config.action_controller.include_all_helpers = false
27
+
28
+ # Please, add to the `ignore` list any other `lib` subdirectories that do
29
+ # not contain `.rb` files, or that should not be reloaded or eager loaded.
30
+ # Common ones are `templates`, `generators`, or `middleware`, for example.
31
+ config.autoload_lib(ignore: %w(assets tasks))
32
+
33
+ # Configuration for the application, engines, and railties goes here.
34
+ #
35
+ # These settings can be overridden in specific environments using the files
36
+ # in config/environments, which are processed later.
37
+ #
38
+ # config.time_zone = "Central Time (US & Canada)"
39
+ # config.eager_load_paths << Rails.root.join("extras")
40
+
41
+ # Don't generate system test files.
42
+ config.generators.system_tests = nil
43
+ end
44
+ end