railties 8.0.2.1 → 8.1.0.beta1

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -224
  3. data/README.rdoc +1 -1
  4. data/lib/minitest/rails_plugin.rb +48 -12
  5. data/lib/rails/application/bootstrap.rb +6 -3
  6. data/lib/rails/application/configuration.rb +26 -0
  7. data/lib/rails/application/default_middleware_stack.rb +1 -1
  8. data/lib/rails/application/finisher.rb +2 -1
  9. data/lib/rails/application/routes_reloader.rb +1 -2
  10. data/lib/rails/application.rb +6 -4
  11. data/lib/rails/application_controller.rb +2 -0
  12. data/lib/rails/command/base.rb +0 -2
  13. data/lib/rails/command/environment_argument.rb +0 -1
  14. data/lib/rails/command.rb +1 -1
  15. data/lib/rails/commands/console/irb_console.rb +6 -5
  16. data/lib/rails/commands/credentials/credentials_command.rb +25 -5
  17. data/lib/rails/commands/encrypted/encrypted_command.rb +0 -1
  18. data/lib/rails/engine/lazy_route_set.rb +8 -11
  19. data/lib/rails/engine.rb +0 -1
  20. data/lib/rails/gem_version.rb +3 -3
  21. data/lib/rails/generators/actions.rb +2 -3
  22. data/lib/rails/generators/app_base.rb +43 -54
  23. data/lib/rails/generators/bundle_helper.rb +34 -0
  24. data/lib/rails/generators/database.rb +1 -1
  25. data/lib/rails/generators/erb/authentication/authentication_generator.rb +2 -0
  26. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +2 -2
  27. data/lib/rails/generators/generated_attribute.rb +1 -1
  28. data/lib/rails/generators/migration.rb +0 -1
  29. data/lib/rails/generators/rails/app/app_generator.rb +14 -4
  30. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +15 -13
  31. data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -0
  32. data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +5 -0
  33. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +1 -0
  34. data/lib/rails/generators/rails/app/templates/bin/bundler-audit.tt +5 -0
  35. data/lib/rails/generators/rails/app/templates/bin/ci.tt +5 -0
  36. data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +1 -1
  37. data/lib/rails/generators/rails/app/templates/bin/setup.tt +1 -0
  38. data/lib/rails/generators/rails/app/templates/config/bundler-audit.yml.tt +5 -0
  39. data/lib/rails/generators/rails/app/templates/config/ci.rb.tt +34 -0
  40. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +9 -1
  41. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +10 -2
  42. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +1 -1
  43. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +9 -1
  44. data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +5 -5
  45. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +5 -0
  46. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +16 -4
  47. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
  48. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_1.rb.tt +66 -0
  49. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
  50. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +0 -7
  51. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +0 -6
  52. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +101 -19
  53. data/lib/rails/generators/rails/app/templates/github/dependabot.yml +2 -2
  54. data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +3 -0
  55. data/lib/rails/generators/rails/app/templates/public/400.html +1 -1
  56. data/lib/rails/generators/rails/app/templates/public/404.html +2 -2
  57. data/lib/rails/generators/rails/app/templates/public/422.html +1 -1
  58. data/lib/rails/generators/rails/app/templates/public/500.html +2 -2
  59. data/lib/rails/generators/rails/authentication/authentication_generator.rb +22 -9
  60. data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt +6 -0
  61. data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt +2 -2
  62. data/lib/rails/generators/rails/authentication/templates/test/test_helpers/session_test_helper.rb.tt +15 -0
  63. data/lib/rails/generators/rails/benchmark/USAGE +1 -1
  64. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +0 -2
  65. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +4 -0
  66. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +2 -2
  67. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +17 -5
  68. data/lib/rails/generators/rails/master_key/master_key_generator.rb +0 -12
  69. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +20 -9
  70. data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +2 -2
  71. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +2 -2
  72. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +2 -2
  73. data/lib/rails/generators/rails/script/USAGE +1 -1
  74. data/lib/rails/generators/test_unit/authentication/authentication_generator.rb +5 -0
  75. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/passwords_controller_test.rb.tt +67 -0
  76. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/sessions_controller_test.rb +33 -0
  77. data/lib/rails/generators/test_unit/authentication/templates/test/models/user_test.rb.tt +4 -3
  78. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +1 -1
  79. data/lib/rails/generators/testing/behavior.rb +0 -3
  80. data/lib/rails/generators.rb +3 -1
  81. data/lib/rails/health_controller.rb +10 -2
  82. data/lib/rails/info.rb +4 -4
  83. data/lib/rails/info_controller.rb +2 -3
  84. data/lib/rails/initializable.rb +63 -19
  85. data/lib/rails/rack/silence_request.rb +5 -2
  86. data/lib/rails/railtie/configurable.rb +0 -1
  87. data/lib/rails/railtie.rb +0 -1
  88. data/lib/rails/templates/rails/info/notes.html.erb +23 -0
  89. data/lib/rails/templates/rails/mailers/email.html.erb +2 -1
  90. data/lib/rails/templates/rails/welcome/index.html.erb +17 -1
  91. data/lib/rails/test_unit/reporter.rb +5 -4
  92. data/lib/rails/test_unit/runner.rb +8 -5
  93. data/lib/rails.rb +9 -2
  94. metadata +33 -14
  95. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +0 -30
  96. data/lib/rails/generators/test_unit/plugin/plugin_generator.rb +0 -15
  97. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +0 -7
  98. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +0 -2
@@ -4,7 +4,7 @@
4
4
 
5
5
  <head>
6
6
 
7
- <title>Were sorry, but something went wrong (500 Internal Server Error)</title>
7
+ <title>We're sorry, but something went wrong (500 Internal Server Error)</title>
8
8
 
9
9
  <meta charset="utf-8">
10
10
  <meta name="viewport" content="initial-scale=1, width=device-width">
@@ -105,7 +105,7 @@
105
105
  <svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m101.23 93.8427c-8.1103 0-15.4098 3.7849-19.7354 8.3813h-36.2269v-99.21891h103.8143v37.03791h-68.3984v24.8722c5.1366-2.7035 15.1396-5.9477 24.6014-5.9477 35.146 0 56.233 22.7094 56.233 55.4215 0 34.605-23.791 57.315-60.558 57.315-37.8492 0-61.64-22.169-63.8028-55.963h42.9857c1.0814 10.814 9.1919 19.195 21.6281 19.195 11.355 0 19.465-8.381 19.465-20.547 0-11.625-7.299-20.5463-20.006-20.5463zm138.833 77.8613c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm140.456 133.2831c-40.823 0-64.884-35.146-64.884-85.7015 0-50.5554 24.061-85.700907 64.884-85.700907 40.822 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.062 85.7015-64.884 85.7015zm0-133.2831c-17.573 0-22.71 21.8984-22.71 47.5816 0 25.6835 5.137 47.5815 22.71 47.5815 17.302 0 22.709-21.898 22.709-47.5815 0-25.6832-5.407-47.5816-22.709-47.5816z" fill="#f0eff0"/><path d="m23.1377 68.9967v34.0033h-8.9162v-34.0033zm4.3157 34.0033v-24.921h8.6947v2.1598c1.3845-1.5506 3.8212-2.7136 6.701-2.7136 5.538 0 8.8054 3.5997 8.8054 9.1377v16.3371h-8.6393v-14.2327c0-2.049-1.0522-3.5443-3.2674-3.5443-1.7168 0-3.1567.9969-3.5997 2.7136v15.0634zm29.9913-8.5839v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.5839v6.8671h5.2058v6.7564h-5.2058v8.307c0 1.9383.9415 2.769 2.6583 2.769.9414 0 1.9937-.2216 2.769-.5538v7.3654c-.9969.443-2.8798.775-4.8181.775-5.8703 0-9.1931-2.769-9.1931-9.0819zm32.3666-.1108h8.0301c-.8861 5.7597-5.2057 9.2487-11.6852 9.2487-7.6424 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.3165-13.0143 12.5159-13.0143 7.6424 0 11.9621 5.095 11.9621 12.5159v2.1598h-16.1156c.2769 2.9905 1.8275 4.5965 4.3196 4.5965 1.7722 0 3.1567-.7753 3.6551-2.4921zm-3.8212-10.0237c-2.0491 0-3.4336 1.2737-3.9874 3.5997h7.5317c-.1107-2.0491-1.3845-3.5997-3.5443-3.5997zm31.4299-6.3134v8.3624c-1.052-.5538-2.215-.7753-3.599-.7753-2.382 0-3.988 1.0522-4.431 2.8244v14.6203h-8.694v-24.921h8.694v2.2152c1.219-1.6614 3.157-2.769 5.649-2.769 1.108 0 1.994.2215 2.381.443zm2.949 25.0318v-24.921h8.694v2.1598c1.385-1.5506 3.821-2.7136 6.701-2.7136 5.538 0 8.806 3.5997 8.806 9.1377v16.3371h-8.64v-14.2327c0-2.049-1.052-3.5443-3.267-3.5443-1.717 0-3.157.9969-3.6 2.7136v15.0634zm50.371 0h-8.363v-1.274c-.83.831-3.323 1.717-5.981 1.717-4.929 0-9.082-2.769-9.082-8.0301 0-4.818 4.153-7.9193 9.581-7.9193 2.049 0 4.485.6646 5.482 1.3845v-1.606c0-1.606-.941-2.9905-3.046-2.9905-1.606 0-2.547.7199-2.935 1.8275h-8.196c.72-4.8181 4.984-8.6393 11.408-8.6393 7.089 0 11.132 3.7659 11.132 10.2453zm-8.363-6.9779v-1.4399c-.554-1.0522-2.049-1.7167-3.655-1.7167-1.717 0-3.433.7199-3.433 2.3813 0 1.7168 1.716 2.4367 3.433 2.4367 1.606 0 3.101-.6645 3.655-1.6614zm20.742-29.0191v35.997h-8.694v-35.997zm13.036 25.9178h9.248c.72 2.326 2.714 3.489 5.483 3.489 2.713 0 4.596-1.163 4.596-3.2674 0-1.6061-1.052-2.326-3.212-2.8244l-6.534-1.3845c-4.985-1.1076-8.751-3.7105-8.751-9.47 0-6.6456 5.538-11.0206 13.07-11.0206 8.307 0 13.014 4.5411 13.956 10.4114h-8.695c-.72-1.8829-2.27-3.3228-5.205-3.3228-2.548 0-4.265 1.1076-4.265 2.9905 0 1.4953 1.052 2.326 2.825 2.7137l6.645 1.5506c5.815 1.3845 9.027 4.5412 9.027 9.8023 0 6.9778-5.87 10.9654-13.291 10.9654-8.141 0-13.679-3.9322-14.897-10.6332zm46.509 1.3845h8.031c-.887 5.7597-5.206 9.2487-11.686 9.2487-7.642 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.317-13.0143 12.516-13.0143 7.643 0 11.962 5.095 11.962 12.5159v2.1598h-16.115c.277 2.9905 1.827 4.5965 4.319 4.5965 1.773 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm31.431-6.3134v8.3624c-1.053-.5538-2.216-.7753-3.6-.7753-2.381 0-3.988 1.0522-4.431 2.8244v14.6203h-8.694v-24.921h8.694v2.2152c1.219-1.6614 3.157-2.769 5.649-2.769 1.108 0 1.994.2215 2.382.443zm18.288 25.0318h-7.809l-9.47-24.921h8.861l4.763 14.288 4.652-14.288h8.528zm25.614-8.6947h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.642 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.516-13.0143 7.642 0 11.962 5.095 11.962 12.5159v2.1598h-16.116c.277 2.9905 1.828 4.5965 4.32 4.5965 1.772 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.434 1.2737-3.988 3.5997h7.532c-.111-2.0491-1.384-3.5997-3.544-3.5997zm31.43-6.3134v8.3624c-1.052-.5538-2.215-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.157-2.769 5.649-2.769 1.107 0 1.993.2215 2.381.443zm13.703-8.9715h24.312v7.6424h-15.562v5.3165h14.232v7.4763h-14.232v5.8703h15.562v7.6978h-24.312zm44.667 8.9715v8.3624c-1.052-.5538-2.215-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.156-2.769 5.648-2.769 1.108 0 1.994.2215 2.382.443zm19.673 0v8.3624c-1.053-.5538-2.216-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.156-2.769 5.648-2.769 1.108 0 1.994.2215 2.382.443zm26.769 12.5713c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.529 0c0-3.4336-1.495-5.8703-4.208-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.208-2.3813 4.208-5.8149zm28.082-12.5713v8.3624c-1.052-.5538-2.215-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.157-2.769 5.649-2.769 1.107 0 1.993.2215 2.381.443z" fill="#d30001"/></svg>
106
106
  </header>
107
107
  <article>
108
- <p><strong>Were sorry, but something went wrong.</strong><br> If youre the application owner check the logs for more information.</p>
108
+ <p><strong>We're sorry, but something went wrong.</strong><br> If you're the application owner check the logs for more information.</p>
109
109
  </article>
110
110
  </main>
111
111
 
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/generators/bundle_helper"
4
+
3
5
  module Rails
4
6
  module Generators
5
7
  class AuthenticationGenerator < Base # :nodoc:
8
+ include BundleHelper
9
+
6
10
  class_option :api, type: :boolean,
7
11
  desc: "Generate API-only controllers and models, with no view templates"
8
12
 
@@ -21,12 +25,16 @@ module Rails
21
25
 
22
26
  template "app/channels/application_cable/connection.rb" if defined?(ActionCable::Engine)
23
27
 
24
- template "app/mailers/passwords_mailer.rb"
28
+ if defined?(ActionMailer::Railtie)
29
+ template "app/mailers/passwords_mailer.rb"
30
+
31
+ template "app/views/passwords_mailer/reset.html.erb"
32
+ template "app/views/passwords_mailer/reset.text.erb"
25
33
 
26
- template "app/views/passwords_mailer/reset.html.erb"
27
- template "app/views/passwords_mailer/reset.text.erb"
34
+ template "test/mailers/previews/passwords_mailer_preview.rb"
35
+ end
28
36
 
29
- template "test/mailers/previews/passwords_mailer_preview.rb"
37
+ template "test/test_helpers/session_test_helper.rb"
30
38
  end
31
39
 
32
40
  def configure_application_controller
@@ -39,17 +47,22 @@ module Rails
39
47
  end
40
48
 
41
49
  def enable_bcrypt
42
- if File.read("Gemfile").include?('gem "bcrypt"')
50
+ if File.read(File.expand_path("Gemfile", destination_root)).include?('gem "bcrypt"')
43
51
  uncomment_lines "Gemfile", /gem "bcrypt"/
44
- Bundler.with_original_env { execute_command :bundle, "install --quiet" }
52
+ bundle_command("install --quiet")
45
53
  else
46
- Bundler.with_original_env { execute_command :bundle, "add bcrypt", capture: true }
54
+ bundle_command("add bcrypt", {}, quiet: true)
47
55
  end
48
56
  end
49
57
 
50
58
  def add_migrations
51
- generate "migration CreateUsers email_address:string!:uniq password_digest:string! --force"
52
- generate "migration CreateSessions user:references ip_address:string user_agent:string --force"
59
+ generate "migration", "CreateUsers", "email_address:string!:uniq password_digest:string!", "--force"
60
+ generate "migration", "CreateSessions", "user:references ip_address:string user_agent:string", "--force"
61
+ end
62
+
63
+ def configure_test_helper
64
+ inject_into_file "test/test_helper.rb", "require_relative \"test_helpers/session_test_helper\"\n", after: "require \"rails/test_help\"\n"
65
+ inject_into_class "test/test_helper.rb", "TestCase", " include SessionTestHelper\n"
53
66
  end
54
67
 
55
68
  hook_for :test_framework
@@ -1,9 +1,13 @@
1
1
  class PasswordsController < ApplicationController
2
2
  allow_unauthenticated_access
3
3
  before_action :set_user_by_token, only: %i[ edit update ]
4
+ <%- if defined?(ActionMailer::Railtie) -%>
5
+ rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." }
6
+ <%- end -%>
4
7
 
5
8
  def new
6
9
  end
10
+ <%- if defined?(ActionMailer::Railtie) -%>
7
11
 
8
12
  def create
9
13
  if user = User.find_by(email_address: params[:email_address])
@@ -12,12 +16,14 @@ class PasswordsController < ApplicationController
12
16
 
13
17
  redirect_to new_session_path, notice: "Password reset instructions sent (if user with that email address exists)."
14
18
  end
19
+ <%- end -%>
15
20
 
16
21
  def edit
17
22
  end
18
23
 
19
24
  def update
20
25
  if @user.update(params.permit(:password, :password_confirmation))
26
+ @user.sessions.destroy_all
21
27
  redirect_to new_session_path, notice: "Password has been reset."
22
28
  else
23
29
  redirect_to edit_password_path(params[:token]), alert: "Passwords did not match."
@@ -1,6 +1,6 @@
1
1
  class SessionsController < ApplicationController
2
2
  allow_unauthenticated_access only: %i[ new create ]
3
- rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_url, alert: "Try again later." }
3
+ rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." }
4
4
 
5
5
  def new
6
6
  end
@@ -16,6 +16,6 @@ class SessionsController < ApplicationController
16
16
 
17
17
  def destroy
18
18
  terminate_session
19
- redirect_to new_session_path
19
+ redirect_to new_session_path, status: :see_other
20
20
  end
21
21
  end
@@ -0,0 +1,15 @@
1
+ module SessionTestHelper
2
+ def sign_in_as(user)
3
+ Current.session = user.sessions.create!
4
+
5
+ ActionDispatch::TestRequest.create.cookie_jar.tap do |cookie_jar|
6
+ cookie_jar.signed[:session_id] = Current.session.id
7
+ cookies[:session_id] = cookie_jar[:session_id]
8
+ end
9
+ end
10
+
11
+ def sign_out
12
+ Current.session&.destroy!
13
+ cookies.delete(:session_id)
14
+ end
15
+ end
@@ -13,7 +13,7 @@ Example:
13
13
  script/benchmarks/opt_compare.rb
14
14
 
15
15
  You can run the generated benchmark file using:
16
- `ruby script/benchmarks/opt_compare.rb`
16
+ `bin/rails runner script/benchmarks/opt_compare.rb`
17
17
 
18
18
  You can specify different reports:
19
19
  `bin/rails generate benchmark opt_compare patch1 patch2 patch3`
@@ -4,8 +4,6 @@ require_relative "../../config/environment"
4
4
 
5
5
  # Any benchmarking setup goes here...
6
6
 
7
-
8
-
9
7
  Benchmark.ips do |x|
10
8
  <%- reports.each do |report| -%>
11
9
  x.report("<%= report %>") { }
@@ -1,3 +1,7 @@
1
1
  # Make sure RUBY_VERSION matches the Ruby version in .ruby-version
2
2
  ARG RUBY_VERSION=<%= Gem.ruby_version %>
3
3
  FROM ghcr.io/rails/devcontainer/images/ruby:$RUBY_VERSION
4
+
5
+ # Ensure binding is always 0.0.0.0
6
+ # Binds the server to all IP addresses of the container, so it can be accessed from outside the container.
7
+ ENV BINDING="0.0.0.0"
@@ -7,7 +7,7 @@ services:
7
7
  dockerfile: .devcontainer/Dockerfile
8
8
 
9
9
  volumes:
10
- - ../..:/workspaces:cached
10
+ - ../../<%= options[:app_name] %>:/workspaces/<%= options[:app_name] %>:cached
11
11
 
12
12
  # Overrides default command so things don't shut down after the process ends.
13
13
  command: sleep infinity
@@ -32,7 +32,7 @@ services:
32
32
 
33
33
  <%- if options[:redis] -%>
34
34
  redis:
35
- image: redis:7.2
35
+ image: valkey/valkey:8
36
36
  restart: unless-stopped
37
37
  volumes:
38
38
  - redis-data:/data
@@ -21,18 +21,20 @@ module Rails
21
21
 
22
22
  log ""
23
23
  add_key_file_silently(key_path, key)
24
+ ensure_key_files_are_ignored(key_path)
24
25
  log ""
25
26
  end
26
27
  end
27
28
 
28
29
  def add_key_file_silently(key_path, key = nil)
29
30
  create_file key_path, key || ActiveSupport::EncryptedFile.generate_key, perm: 0600
31
+ ensure_key_files_are_ignored_silently(key_path)
30
32
  end
31
33
 
32
- def ignore_key_file(key_path, ignore: key_ignore(key_path))
34
+ def ensure_key_files_are_ignored(key_path, ignore: key_ignore(key_path))
33
35
  if File.exist?(".gitignore")
34
36
  unless File.read(".gitignore").include?(ignore)
35
- log "Ignoring #{key_path} so it won't end up in Git history:"
37
+ log "Ignoring #{ignore} so it won't end up in Git history:"
36
38
  log ""
37
39
  append_to_file ".gitignore", ignore
38
40
  log ""
@@ -44,13 +46,23 @@ module Rails
44
46
  end
45
47
  end
46
48
 
47
- def ignore_key_file_silently(key_path, ignore: key_ignore(key_path))
48
- append_to_file ".gitignore", ignore if File.exist?(".gitignore")
49
+ def ensure_key_files_are_ignored_silently(key_path, ignore: key_ignore(key_path))
50
+ if File.exist?(".gitignore")
51
+ unless File.read(".gitignore").include?(ignore)
52
+ append_to_file ".gitignore", ignore
53
+ end
54
+ end
49
55
  end
50
56
 
51
57
  private
52
58
  def key_ignore(key_path)
53
- [ "", "/#{key_path}", "" ].join("\n")
59
+ key_path = Pathname.new(key_path) unless key_path.is_a?(Pathname)
60
+ <<~IGNORE
61
+
62
+ # Ignore key files for decrypting credentials and more.
63
+ /#{key_path.dirname.join("*.key")}
64
+
65
+ IGNORE
54
66
  end
55
67
  end
56
68
  end
@@ -32,22 +32,10 @@ module Rails
32
32
  end
33
33
  end
34
34
 
35
- def ignore_master_key_file
36
- key_file_generator.ignore_key_file(MASTER_KEY_PATH, ignore: key_ignore)
37
- end
38
-
39
- def ignore_master_key_file_silently
40
- key_file_generator.ignore_key_file_silently(MASTER_KEY_PATH, ignore: key_ignore)
41
- end
42
-
43
35
  private
44
36
  def key_file_generator
45
37
  EncryptionKeyFileGenerator.new([], options)
46
38
  end
47
-
48
- def key_ignore
49
- [ "", "# Ignore master key for decrypting credentials and more.", "/#{MASTER_KEY_PATH}", "" ].join("\n")
50
- end
51
39
  end
52
40
  end
53
41
  end
@@ -3,22 +3,35 @@ name: CI
3
3
  on:
4
4
  pull_request:
5
5
  push:
6
- branches: [ main ]
6
+ branches: [ <%= user_default_branch %> ]
7
7
 
8
8
  jobs:
9
9
  <%- unless skip_rubocop? -%>
10
10
  lint:
11
11
  runs-on: ubuntu-latest
12
+ env:
13
+ RUBY_VERSION: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
14
+ RUBOCOP_CACHE_ROOT: tmp/rubocop
12
15
  steps:
13
16
  - name: Checkout code
14
- uses: actions/checkout@v4
17
+ uses: actions/checkout@v5
15
18
 
16
19
  - name: Set up Ruby
17
20
  uses: ruby/setup-ruby@v1
18
21
  with:
19
- ruby-version: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
22
+ ruby-version: ${{ env.RUBY_VERSION }}
20
23
  bundler-cache: true
21
24
 
25
+ - name: Prepare RuboCop cache
26
+ uses: actions/cache@v4
27
+ env:
28
+ DEPENDENCIES_HASH: ${{ hashFiles('**/.rubocop.yml', '**/.rubocop_todo.yml', 'Gemfile.lock') }}
29
+ with:
30
+ path: ${{ env.RUBOCOP_CACHE_ROOT }}
31
+ key: rubocop-${{ runner.os }}-${{ env.RUBY_VERSION }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }}
32
+ restore-keys: |
33
+ rubocop-${{ runner.os }}-${{ env.RUBY_VERSION }}-${{ env.DEPENDENCIES_HASH }}-
34
+
22
35
  - name: Lint code for consistent style
23
36
  run: bin/rubocop -f github
24
37
 
@@ -30,7 +43,7 @@ jobs:
30
43
  <%- if options[:database] == "sqlite3" -%>
31
44
  # services:
32
45
  # redis:
33
- # image: redis
46
+ # image: valkey/valkey:8
34
47
  # ports:
35
48
  # - 6379:6379
36
49
  # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
@@ -56,18 +69,15 @@ jobs:
56
69
  <%- end -%>
57
70
 
58
71
  # redis:
59
- # image: redis
72
+ # image: valkey/valkey:8
60
73
  # ports:
61
74
  # - 6379:6379
62
75
  # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
63
76
 
64
77
  <%- end -%>
65
78
  steps:
66
- - name: Install packages
67
- run: sudo apt-get update && sudo apt-get install --no-install-recommends -y <%= ci_packages.join(" ") %>
68
-
69
79
  - name: Checkout code
70
- uses: actions/checkout@v4
80
+ uses: actions/checkout@v5
71
81
 
72
82
  - name: Set up Ruby
73
83
  uses: ruby/setup-ruby@v1
@@ -91,6 +101,7 @@ jobs:
91
101
  <%- elsif options[:database] == "postgresql" -%>
92
102
  DATABASE_URL: postgres://postgres:postgres@localhost:5432
93
103
  <%- end -%>
104
+ # RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
94
105
  # REDIS_URL: redis://localhost:6379/0
95
106
  run: <%= test_command %>
96
107
 
@@ -3,10 +3,10 @@ updates:
3
3
  - package-ecosystem: bundler
4
4
  directory: "/"
5
5
  schedule:
6
- interval: daily
6
+ interval: weekly
7
7
  open-pull-requests-limit: 10
8
8
  - package-ecosystem: github-actions
9
9
  directory: "/"
10
10
  schedule:
11
- interval: daily
11
+ interval: weekly
12
12
  open-pull-requests-limit: 10
@@ -21,7 +21,7 @@ class <%= controller_class_name %>Controller < ApplicationController
21
21
  if @<%= orm_instance.save %>
22
22
  render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %>
23
23
  else
24
- render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
24
+ render json: <%= "@#{orm_instance.errors}" %>, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
25
25
  end
26
26
  end
27
27
 
@@ -30,7 +30,7 @@ class <%= controller_class_name %>Controller < ApplicationController
30
30
  if @<%= orm_instance.update("#{singular_table_name}_params") %>
31
31
  render json: <%= "@#{singular_table_name}" %>
32
32
  else
33
- render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
33
+ render json: <%= "@#{orm_instance.errors}" %>, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
34
34
  end
35
35
  end
36
36
 
@@ -27,7 +27,7 @@ class <%= controller_class_name %>Controller < ApplicationController
27
27
  if @<%= orm_instance.save %>
28
28
  redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully created.") %>
29
29
  else
30
- render :new, status: :unprocessable_entity
30
+ render :new, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
31
31
  end
32
32
  end
33
33
 
@@ -36,7 +36,7 @@ class <%= controller_class_name %>Controller < ApplicationController
36
36
  if @<%= orm_instance.update("#{singular_table_name}_params") %>
37
37
  redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully updated.") %>, status: :see_other
38
38
  else
39
- render :edit, status: :unprocessable_entity
39
+ render :edit, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
40
40
  end
41
41
  end
42
42
 
@@ -9,7 +9,7 @@ Example:
9
9
  script/my_script.rb
10
10
 
11
11
  You can run the script using:
12
- `ruby script/my_script.rb`
12
+ `bin/rails runner script/my_script.rb`
13
13
 
14
14
  You can specify a folder:
15
15
  `bin/rails generate script cleanup/my_script`
@@ -9,6 +9,11 @@ module TestUnit # :nodoc:
9
9
  template "test/fixtures/users.yml"
10
10
  template "test/models/user_test.rb"
11
11
  end
12
+
13
+ def create_controller_test_files
14
+ template "test/controllers/sessions_controller_test.rb"
15
+ template "test/controllers/passwords_controller_test.rb"
16
+ end
12
17
  end
13
18
  end
14
19
  end
@@ -0,0 +1,67 @@
1
+ require "test_helper"
2
+
3
+ class PasswordsControllerTest < ActionDispatch::IntegrationTest
4
+ setup { @user = User.take }
5
+
6
+ test "new" do
7
+ get new_password_path
8
+ assert_response :success
9
+ end
10
+
11
+ test "create" do
12
+ post passwords_path, params: { email_address: @user.email_address }
13
+ assert_enqueued_email_with PasswordsMailer, :reset, args: [ @user ]
14
+ assert_redirected_to new_session_path
15
+
16
+ follow_redirect!
17
+ assert_notice "reset instructions sent"
18
+ end
19
+
20
+ test "create for an unknown user redirects but sends no mail" do
21
+ post passwords_path, params: { email_address: "missing-user@example.com" }
22
+ assert_enqueued_emails 0
23
+ assert_redirected_to new_session_path
24
+
25
+ follow_redirect!
26
+ assert_notice "reset instructions sent"
27
+ end
28
+
29
+ test "edit" do
30
+ get edit_password_path(@user.password_reset_token)
31
+ assert_response :success
32
+ end
33
+
34
+ test "edit with invalid password reset token" do
35
+ get edit_password_path("invalid token")
36
+ assert_redirected_to new_password_path
37
+
38
+ follow_redirect!
39
+ assert_notice "reset link is invalid"
40
+ end
41
+
42
+ test "update" do
43
+ assert_changes -> { @user.reload.password_digest } do
44
+ put password_path(@user.password_reset_token), params: { password: "new", password_confirmation: "new" }
45
+ assert_redirected_to new_session_path
46
+ end
47
+
48
+ follow_redirect!
49
+ assert_notice "Password has been reset"
50
+ end
51
+
52
+ test "update with non matching passwords" do
53
+ token = @user.password_reset_token
54
+ assert_no_changes -> { @user.reload.password_digest } do
55
+ put password_path(token), params: { password: "no", password_confirmation: "match" }
56
+ assert_redirected_to edit_password_path(token)
57
+ end
58
+
59
+ follow_redirect!
60
+ assert_notice "Passwords did not match"
61
+ end
62
+
63
+ private
64
+ def assert_notice(text)
65
+ assert_select "div", /#{text}/
66
+ end
67
+ end
@@ -0,0 +1,33 @@
1
+ require "test_helper"
2
+
3
+ class SessionsControllerTest < ActionDispatch::IntegrationTest
4
+ setup { @user = User.take }
5
+
6
+ test "new" do
7
+ get new_session_path
8
+ assert_response :success
9
+ end
10
+
11
+ test "create with valid credentials" do
12
+ post session_path, params: { email_address: @user.email_address, password: "password" }
13
+
14
+ assert_redirected_to root_path
15
+ assert cookies[:session_id]
16
+ end
17
+
18
+ test "create with invalid credentials" do
19
+ post session_path, params: { email_address: @user.email_address, password: "wrong" }
20
+
21
+ assert_redirected_to new_session_path
22
+ assert_nil cookies[:session_id]
23
+ end
24
+
25
+ test "destroy" do
26
+ sign_in_as(User.take)
27
+
28
+ delete session_path
29
+
30
+ assert_redirected_to new_session_path
31
+ assert_empty cookies[:session_id]
32
+ end
33
+ end
@@ -1,7 +1,8 @@
1
1
  require "test_helper"
2
2
 
3
3
  class UserTest < ActiveSupport::TestCase
4
- # test "the truth" do
5
- # assert true
6
- # end
4
+ test "downcases and strips email_address" do
5
+ user = User.new(email_address: " DOWNCASED@EXAMPLE.COM ")
6
+ assert_equal("downcased@example.com", user.email_address)
7
+ end
7
8
  end
@@ -4,7 +4,7 @@
4
4
  <%= name %>:
5
5
  <% attributes.each do |attribute| -%>
6
6
  <%- if attribute.password_digest? -%>
7
- password_digest: <%%= BCrypt::Password.create("secret") %>
7
+ password_digest: <%= BCrypt::Password.create("secret") %> # Generated with BCrypt::Password.create("secret")
8
8
  <%- elsif attribute.reference? -%>
9
9
  <%= yaml_key_value(attribute.column_name.delete_suffix("_id"), attribute.default || name) %>
10
10
  <%- elsif !attribute.virtual? -%>
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/class/attribute"
4
- require "active_support/core_ext/module/delegation"
5
3
  require "active_support/core_ext/hash/reverse_merge"
6
4
  require "active_support/core_ext/kernel/reporting"
7
5
  require "active_support/testing/stream"
8
- require "active_support/concern"
9
6
  require "rails/generators"
10
7
 
11
8
  module Rails
@@ -157,7 +157,9 @@ module Rails
157
157
  "action_text:install",
158
158
  "action_mailbox:install",
159
159
  "devcontainer"
160
- ]
160
+ ].tap do |h|
161
+ h << "test_unit" if test.to_s != "test_unit"
162
+ end
161
163
  end
162
164
  end
163
165
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "action_controller"
4
+
3
5
  module Rails
4
6
  # Built-in Health Check Endpoint
5
7
  #
@@ -41,11 +43,17 @@ module Rails
41
43
 
42
44
  private
43
45
  def render_up
44
- render html: html_status(color: "green")
46
+ respond_to do |format|
47
+ format.html { render html: html_status(color: "green") }
48
+ format.json { render json: { status: "up", timestamp: Time.current.iso8601 } }
49
+ end
45
50
  end
46
51
 
47
52
  def render_down
48
- render html: html_status(color: "red"), status: 500
53
+ respond_to do |format|
54
+ format.html { render html: html_status(color: "red"), status: 500 }
55
+ format.json { render json: { status: "down", timestamp: Time.current.iso8601 }, status: 500 }
56
+ end
49
57
  end
50
58
 
51
59
  def html_status(color:)
data/lib/rails/info.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "cgi"
3
+ require "active_support/core_ext/erb/util"
4
4
 
5
5
  module Rails
6
6
  # This module helps build the runtime properties that are displayed in
@@ -43,11 +43,11 @@ module Rails
43
43
  def to_html
44
44
  (+"<table>").tap do |table|
45
45
  properties.each do |(name, value)|
46
- table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
46
+ table << %(<tr><td class="name">#{ERB::Util.html_escape(name.to_s)}</td>)
47
47
  formatted_value = if value.kind_of?(Array)
48
- "<ul>" + value.map { |v| "<li>#{CGI.escapeHTML(v.to_s)}</li>" }.join + "</ul>"
48
+ "<ul>" + value.map { |v| "<li>#{ERB::Util.html_escape(v.to_s)}</li>" }.join + "</ul>"
49
49
  else
50
- CGI.escapeHTML(value.to_s)
50
+ ERB::Util.html_escape(value.to_s)
51
51
  end
52
52
  table << %(<td class="value">#{formatted_value}</td></tr>)
53
53
  end
@@ -33,9 +33,8 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
33
33
  end
34
34
 
35
35
  def notes
36
- @annotations = Rails::SourceAnnotationExtractor.new(
37
- Rails::SourceAnnotationExtractor::Annotation.tags.join("|")
38
- ).find(
36
+ tags = params[:tag].presence || Rails::SourceAnnotationExtractor::Annotation.tags.join("|")
37
+ @annotations = Rails::SourceAnnotationExtractor.new(tags).find(
39
38
  Rails::SourceAnnotationExtractor::Annotation.directories
40
39
  )
41
40
  end