railties 8.0.3 → 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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -232
  3. data/lib/minitest/rails_plugin.rb +48 -12
  4. data/lib/rails/application/bootstrap.rb +5 -0
  5. data/lib/rails/application/configuration.rb +26 -0
  6. data/lib/rails/application/default_middleware_stack.rb +1 -1
  7. data/lib/rails/application/finisher.rb +2 -1
  8. data/lib/rails/application/routes_reloader.rb +0 -1
  9. data/lib/rails/application.rb +1 -3
  10. data/lib/rails/command/base.rb +0 -2
  11. data/lib/rails/command/environment_argument.rb +0 -1
  12. data/lib/rails/command.rb +1 -1
  13. data/lib/rails/commands/console/irb_console.rb +4 -4
  14. data/lib/rails/commands/credentials/credentials_command.rb +25 -5
  15. data/lib/rails/commands/encrypted/encrypted_command.rb +0 -1
  16. data/lib/rails/engine.rb +0 -1
  17. data/lib/rails/gem_version.rb +3 -3
  18. data/lib/rails/generators/actions.rb +2 -3
  19. data/lib/rails/generators/app_base.rb +37 -28
  20. data/lib/rails/generators/database.rb +1 -1
  21. data/lib/rails/generators/erb/authentication/authentication_generator.rb +2 -0
  22. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +2 -2
  23. data/lib/rails/generators/generated_attribute.rb +1 -1
  24. data/lib/rails/generators/migration.rb +0 -1
  25. data/lib/rails/generators/rails/app/app_generator.rb +12 -2
  26. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +14 -12
  27. data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -0
  28. data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +5 -0
  29. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +1 -0
  30. data/lib/rails/generators/rails/app/templates/bin/bundler-audit.tt +5 -0
  31. data/lib/rails/generators/rails/app/templates/bin/ci.tt +5 -0
  32. data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +1 -1
  33. data/lib/rails/generators/rails/app/templates/bin/setup.tt +1 -0
  34. data/lib/rails/generators/rails/app/templates/config/bundler-audit.yml.tt +5 -0
  35. data/lib/rails/generators/rails/app/templates/config/ci.rb.tt +34 -0
  36. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +9 -1
  37. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +10 -2
  38. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +1 -1
  39. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +9 -1
  40. data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +5 -5
  41. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +5 -0
  42. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +16 -4
  43. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
  44. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_1.rb.tt +66 -0
  45. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
  46. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +0 -7
  47. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +0 -6
  48. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +101 -19
  49. data/lib/rails/generators/rails/app/templates/github/dependabot.yml +2 -2
  50. data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +3 -0
  51. data/lib/rails/generators/rails/app/templates/public/400.html +1 -1
  52. data/lib/rails/generators/rails/app/templates/public/404.html +2 -2
  53. data/lib/rails/generators/rails/app/templates/public/422.html +1 -1
  54. data/lib/rails/generators/rails/app/templates/public/500.html +2 -2
  55. data/lib/rails/generators/rails/authentication/authentication_generator.rb +17 -6
  56. data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt +6 -0
  57. data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt +2 -2
  58. data/lib/rails/generators/rails/authentication/templates/test/test_helpers/session_test_helper.rb.tt +15 -0
  59. data/lib/rails/generators/rails/benchmark/USAGE +1 -1
  60. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +0 -2
  61. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +4 -0
  62. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +2 -2
  63. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +17 -5
  64. data/lib/rails/generators/rails/master_key/master_key_generator.rb +0 -12
  65. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +20 -9
  66. data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +2 -2
  67. data/lib/rails/generators/rails/script/USAGE +1 -1
  68. data/lib/rails/generators/test_unit/authentication/authentication_generator.rb +3 -2
  69. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/passwords_controller_test.rb.tt +67 -0
  70. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/sessions_controller_test.rb +33 -0
  71. data/lib/rails/generators/test_unit/authentication/templates/test/models/user_test.rb.tt +4 -3
  72. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +1 -1
  73. data/lib/rails/generators/testing/behavior.rb +0 -3
  74. data/lib/rails/generators.rb +3 -1
  75. data/lib/rails/health_controller.rb +8 -2
  76. data/lib/rails/info.rb +4 -5
  77. data/lib/rails/info_controller.rb +2 -3
  78. data/lib/rails/initializable.rb +63 -19
  79. data/lib/rails/rack/silence_request.rb +5 -2
  80. data/lib/rails/railtie/configurable.rb +0 -1
  81. data/lib/rails/railtie.rb +0 -1
  82. data/lib/rails/templates/rails/info/notes.html.erb +23 -0
  83. data/lib/rails/templates/rails/mailers/email.html.erb +3 -2
  84. data/lib/rails/templates/rails/welcome/index.html.erb +17 -1
  85. data/lib/rails/test_unit/reporter.rb +5 -4
  86. data/lib/rails/test_unit/runner.rb +8 -5
  87. data/lib/rails.rb +9 -2
  88. metadata +19 -15
  89. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +0 -30
  90. data/lib/rails/generators/test_unit/plugin/plugin_generator.rb +0 -15
  91. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +0 -7
  92. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +0 -2
  93. /data/lib/rails/generators/{test_unit → rails}/authentication/templates/test/mailers/previews/passwords_mailer_preview.rb.tt +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
@@ -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`
@@ -10,8 +10,9 @@ module TestUnit # :nodoc:
10
10
  template "test/models/user_test.rb"
11
11
  end
12
12
 
13
- def create_mailer_preview_files
14
- template "test/mailers/previews/passwords_mailer_preview.rb" if defined?(ActionMailer::Railtie)
13
+ def create_controller_test_files
14
+ template "test/controllers/sessions_controller_test.rb"
15
+ template "test/controllers/passwords_controller_test.rb"
15
16
  end
16
17
  end
17
18
  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
 
@@ -43,11 +43,17 @@ module Rails
43
43
 
44
44
  private
45
45
  def render_up
46
- 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
47
50
  end
48
51
 
49
52
  def render_down
50
- 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
51
57
  end
52
58
 
53
59
  def html_status(color:)
data/lib/rails/info.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "cgi/escape"
4
- require "cgi/util" if RUBY_VERSION < "3.5"
3
+ require "active_support/core_ext/erb/util"
5
4
 
6
5
  module Rails
7
6
  # This module helps build the runtime properties that are displayed in
@@ -44,11 +43,11 @@ module Rails
44
43
  def to_html
45
44
  (+"<table>").tap do |table|
46
45
  properties.each do |(name, value)|
47
- 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>)
48
47
  formatted_value = if value.kind_of?(Array)
49
- "<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>"
50
49
  else
51
- CGI.escapeHTML(value.to_s)
50
+ ERB::Util.html_escape(value.to_s)
52
51
  end
53
52
  table << %(<td class="value">#{formatted_value}</td></tr>)
54
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
@@ -9,23 +9,15 @@ module Rails
9
9
  end
10
10
 
11
11
  class Initializer
12
- attr_reader :name, :block
12
+ attr_reader :name, :block, :before, :after
13
13
 
14
- def initialize(name, context, options, &block)
15
- options[:group] ||= :default
16
- @name, @context, @options, @block = name, context, options, block
17
- end
18
-
19
- def before
20
- @options[:before]
21
- end
22
-
23
- def after
24
- @options[:after]
14
+ def initialize(name, context, before:, after:, group: nil, &block)
15
+ @group = group || :default
16
+ @name, @before, @after, @context, @block = name, before, after, context, block
25
17
  end
26
18
 
27
19
  def belongs_to?(group)
28
- @options[:group] == group || @options[:group] == :all
20
+ @group == group || @group == :all
29
21
  end
30
22
 
31
23
  def run(*args)
@@ -34,7 +26,7 @@ module Rails
34
26
 
35
27
  def bind(context)
36
28
  return self if @context
37
- Initializer.new(@name, context, @options, &block)
29
+ Initializer.new(@name, context, before:, after:, group: @group, &block)
38
30
  end
39
31
 
40
32
  def context_class
@@ -42,16 +34,66 @@ module Rails
42
34
  end
43
35
  end
44
36
 
45
- class Collection < Array
37
+ class Collection
38
+ include Enumerable
46
39
  include TSort
47
40
 
41
+ delegate_missing_to :@collection
42
+
43
+ def initialize(initializers = nil)
44
+ @order = Hash.new { |hash, key| hash[key] = Set.new }
45
+ @resolve = Hash.new { |hash, key| hash[key] = Set.new }
46
+ @collection = []
47
+ concat(initializers) if initializers
48
+ end
49
+
50
+ def to_a
51
+ @collection
52
+ end
53
+
54
+ def last
55
+ @collection.last
56
+ end
57
+
58
+ def each(&block)
59
+ @collection.each(&block)
60
+ end
61
+
48
62
  alias :tsort_each_node :each
49
63
  def tsort_each_child(initializer, &block)
50
- select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block)
64
+ @order[initializer.name].each do |name|
65
+ @resolve[name].each(&block)
66
+ end
51
67
  end
52
68
 
53
69
  def +(other)
54
- Collection.new(to_a + other.to_a)
70
+ dup.concat(other.to_a)
71
+ end
72
+
73
+ def <<(initializer)
74
+ @collection << initializer
75
+ @order[initializer.before] << initializer.name if initializer.before
76
+ @order[initializer.name] << initializer.after if initializer.after
77
+ @resolve[initializer.name] << initializer
78
+ self
79
+ end
80
+
81
+ def push(*initializers)
82
+ initializers.each(&method(:<<))
83
+ self
84
+ end
85
+
86
+ alias_method(:append, :push)
87
+
88
+ def concat(*initializer_collections)
89
+ initializer_collections.each do |initializers|
90
+ initializers.each(&method(:<<))
91
+ end
92
+ self
93
+ end
94
+
95
+ def has?(name)
96
+ @resolve.key?(name)
55
97
  end
56
98
  end
57
99
 
@@ -87,8 +129,10 @@ module Rails
87
129
 
88
130
  def initializer(name, opts = {}, &blk)
89
131
  raise ArgumentError, "A block must be passed when defining an initializer" unless blk
90
- opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
91
- initializers << Initializer.new(name, nil, opts, &blk)
132
+ opts[:after] ||= initializers.last&.name unless initializers.has?(opts[:before])
133
+ initializers << Initializer.new(
134
+ name, nil, before: opts[:before], after: opts[:after], group: opts[:group], &blk
135
+ )
92
136
  end
93
137
  end
94
138
  end
@@ -10,11 +10,14 @@ module Rails
10
10
  # This is useful for preventing recurring requests like health checks from clogging the logging.
11
11
  # This middleware is used to do just that against the path /up in production by default.
12
12
  #
13
- # Example:
13
+ # Examples:
14
14
  #
15
15
  # config.middleware.insert_before \
16
16
  # Rails::Rack::Logger, Rails::Rack::SilenceRequest, path: "/up"
17
17
  #
18
+ # config.middleware.insert_before \
19
+ # Rails::Rack::Logger, Rails::Rack::SilenceRequest, path: /test$/
20
+ #
18
21
  # This middleware can also be configured using `config.silence_healthcheck_path = "/up"` in Rails.
19
22
  class SilenceRequest
20
23
  def initialize(app, path:)
@@ -22,7 +25,7 @@ module Rails
22
25
  end
23
26
 
24
27
  def call(env)
25
- if env["PATH_INFO"] == @path
28
+ if @path === env["PATH_INFO"]
26
29
  Rails.logger.silence { @app.call(env) }
27
30
  else
28
31
  @app.call(env)
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/concern"
4
3
 
5
4
  module Rails
6
5
  class Railtie
data/lib/rails/railtie.rb CHANGED
@@ -4,7 +4,6 @@ require "rails/initializable"
4
4
  require "active_support/descendants_tracker"
5
5
  require "active_support/inflector"
6
6
  require "active_support/core_ext/module/introspection"
7
- require "active_support/core_ext/module/delegation"
8
7
 
9
8
  module Rails
10
9
  # +Rails::Railtie+ is the core of the \Rails framework and provides
@@ -33,12 +33,35 @@
33
33
  background: #282828;
34
34
  }
35
35
  }
36
+
37
+ .filter {
38
+ display: flex;
39
+ width: 100%;
40
+ justify-content: flex-end;
41
+ align-items: center;
42
+ }
43
+
44
+ .filter label[for="tag"] {
45
+ margin-right: 10px;
46
+ }
47
+
48
+ .filter select[name="tag"] {
49
+ border-radius: 8px;
50
+ padding: 0.25em;
51
+ }
36
52
  </style>
37
53
 
38
54
  <h2>
39
55
  Notes
40
56
  </h2>
41
57
 
58
+ <div class="filter">
59
+ <%= form_tag("/rails/info/notes", method: :get) do %>
60
+ <%= label_tag :tag, "Filter by Tag:" %>
61
+ <%= select_tag(:tag, options_for_select(Rails::SourceAnnotationExtractor::Annotation.tags, params[:tag]), { include_blank: "All", onchange: "this.form.submit();" }) %>
62
+ <% end %>
63
+ </div>
64
+
42
65
  <table id="route_table" class="table">
43
66
  <thead>
44
67
  <th>File Name</th>
@@ -9,6 +9,8 @@
9
9
 
10
10
  body {
11
11
  margin: 0;
12
+ display: flex;
13
+ flex-direction: column;
12
14
  }
13
15
 
14
16
  header {
@@ -18,7 +20,6 @@
18
20
  background: white;
19
21
  font: 12px "Lucida Grande", sans-serif;
20
22
  border-bottom: 1px solid #dedede;
21
- overflow: hidden;
22
23
  }
23
24
 
24
25
  dl {
@@ -155,7 +156,7 @@
155
156
  <% end %>
156
157
 
157
158
  <dt>EML File:</dt>
158
- <dd><%= link_to "Download", action: :download, locale: params[:locale] %></dd>
159
+ <dd><%= link_to "Download", action: :download %></dd>
159
160
  </dl>
160
161
  </header>
161
162