activestorage-memory 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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