railties 8.0.0 → 8.1.2

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +126 -137
  3. data/README.rdoc +1 -1
  4. data/lib/minitest/rails_plugin.rb +56 -13
  5. data/lib/rails/application/bootstrap.rb +6 -3
  6. data/lib/rails/application/configuration.rb +36 -16
  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 +18 -13
  11. data/lib/rails/application_controller.rb +2 -0
  12. data/lib/rails/code_statistics.rb +6 -3
  13. data/lib/rails/command/base.rb +0 -2
  14. data/lib/rails/command/environment_argument.rb +0 -1
  15. data/lib/rails/command.rb +1 -1
  16. data/lib/rails/commands/app/update_command.rb +5 -0
  17. data/lib/rails/commands/console/irb_console.rb +7 -16
  18. data/lib/rails/commands/credentials/credentials_command.rb +25 -5
  19. data/lib/rails/commands/devcontainer/devcontainer_command.rb +1 -0
  20. data/lib/rails/commands/encrypted/encrypted_command.rb +0 -1
  21. data/lib/rails/commands/test/test_command.rb +0 -2
  22. data/lib/rails/engine/lazy_route_set.rb +8 -11
  23. data/lib/rails/engine.rb +0 -1
  24. data/lib/rails/gem_version.rb +2 -2
  25. data/lib/rails/generators/actions.rb +9 -6
  26. data/lib/rails/generators/app_base.rb +72 -45
  27. data/lib/rails/generators/bundle_helper.rb +34 -0
  28. data/lib/rails/generators/database.rb +1 -1
  29. data/lib/rails/generators/erb/authentication/authentication_generator.rb +2 -0
  30. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +2 -2
  31. data/lib/rails/generators/generated_attribute.rb +1 -1
  32. data/lib/rails/generators/migration.rb +0 -1
  33. data/lib/rails/generators/rails/app/app_generator.rb +31 -11
  34. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +28 -16
  35. data/lib/rails/generators/rails/app/templates/Gemfile.tt +8 -3
  36. data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +5 -0
  37. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +1 -0
  38. data/lib/rails/generators/rails/app/templates/bin/bundler-audit.tt +5 -0
  39. data/lib/rails/generators/rails/app/templates/bin/ci.tt +5 -0
  40. data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +1 -1
  41. data/lib/rails/generators/rails/app/templates/bin/setup.tt +1 -0
  42. data/lib/rails/generators/rails/app/templates/config/bundler-audit.yml.tt +5 -0
  43. data/lib/rails/generators/rails/app/templates/config/ci.rb.tt +40 -0
  44. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +10 -2
  45. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +10 -2
  46. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +11 -2
  47. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +16 -6
  48. data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +25 -18
  49. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +8 -0
  50. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +10 -2
  51. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
  52. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_1.rb.tt +74 -0
  53. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
  54. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +0 -7
  55. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +0 -6
  56. data/lib/rails/generators/rails/app/templates/dockerignore.tt +6 -0
  57. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +111 -23
  58. data/lib/rails/generators/rails/app/templates/github/dependabot.yml +2 -2
  59. data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +4 -1
  60. data/lib/rails/generators/rails/app/templates/public/400.html +26 -5
  61. data/lib/rails/generators/rails/app/templates/public/404.html +27 -6
  62. data/lib/rails/generators/rails/app/templates/public/406-unsupported-browser.html +25 -4
  63. data/lib/rails/generators/rails/app/templates/public/422.html +26 -5
  64. data/lib/rails/generators/rails/app/templates/public/500.html +27 -6
  65. data/lib/rails/generators/rails/app/templates/ruby-version.tt +1 -1
  66. data/lib/rails/generators/rails/authentication/authentication_generator.rb +14 -10
  67. data/lib/rails/generators/rails/authentication/templates/app/controllers/concerns/authentication.rb.tt +1 -4
  68. data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt +6 -0
  69. data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt +2 -2
  70. data/lib/rails/generators/rails/authentication/templates/app/views/passwords_mailer/reset.html.erb.tt +3 -1
  71. data/lib/rails/generators/rails/authentication/templates/app/views/passwords_mailer/reset.text.erb.tt +3 -1
  72. data/lib/rails/generators/rails/benchmark/USAGE +1 -1
  73. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +0 -2
  74. data/lib/rails/generators/rails/db/system/change/change_generator.rb +1 -1
  75. data/lib/rails/generators/rails/devcontainer/devcontainer_generator.rb +8 -1
  76. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +4 -0
  77. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +2 -2
  78. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/devcontainer.json.tt +1 -1
  79. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +17 -5
  80. data/lib/rails/generators/rails/master_key/master_key_generator.rb +0 -12
  81. data/lib/rails/generators/rails/plugin/plugin_generator.rb +1 -0
  82. data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +0 -4
  83. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +23 -11
  84. data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +2 -2
  85. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +2 -2
  86. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +2 -2
  87. data/lib/rails/generators/rails/script/USAGE +1 -1
  88. data/lib/rails/generators/test_unit/authentication/authentication_generator.rb +17 -0
  89. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/passwords_controller_test.rb.tt +67 -0
  90. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/sessions_controller_test.rb +33 -0
  91. data/lib/rails/generators/test_unit/authentication/templates/test/models/user_test.rb.tt +4 -3
  92. data/lib/rails/generators/test_unit/authentication/templates/test/test_helpers/session_test_helper.rb.tt +19 -0
  93. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +1 -1
  94. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +8 -2
  95. data/lib/rails/generators/test_unit/scaffold/templates/application_system_test_case.rb.tt +5 -0
  96. data/lib/rails/generators/testing/behavior.rb +0 -3
  97. data/lib/rails/generators.rb +8 -6
  98. data/lib/rails/health_controller.rb +10 -2
  99. data/lib/rails/info.rb +4 -4
  100. data/lib/rails/info_controller.rb +4 -5
  101. data/lib/rails/initializable.rb +63 -19
  102. data/lib/rails/rack/silence_request.rb +5 -2
  103. data/lib/rails/railtie/configurable.rb +0 -1
  104. data/lib/rails/railtie.rb +0 -1
  105. data/lib/rails/source_annotation_extractor.rb +6 -2
  106. data/lib/rails/tasks/statistics.rake +3 -21
  107. data/lib/rails/tasks.rb +1 -3
  108. data/lib/rails/templates/rails/info/notes.html.erb +23 -0
  109. data/lib/rails/templates/rails/mailers/email.html.erb +3 -2
  110. data/lib/rails/templates/rails/welcome/index.html.erb +19 -3
  111. data/lib/rails/test_unit/line_filtering.rb +24 -3
  112. data/lib/rails/test_unit/reporter.rb +12 -6
  113. data/lib/rails/test_unit/runner.rb +15 -8
  114. data/lib/rails/test_unit/test_parser.rb +18 -15
  115. data/lib/rails.rb +9 -2
  116. metadata +37 -21
  117. data/lib/rails/console/methods.rb +0 -7
  118. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +0 -30
  119. data/lib/rails/generators/test_unit/plugin/plugin_generator.rb +0 -15
  120. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +0 -7
  121. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +0 -2
  122. /data/lib/rails/generators/{rails → test_unit}/authentication/templates/test/mailers/previews/passwords_mailer_preview.rb.tt +0 -0
@@ -2,10 +2,8 @@
2
2
 
3
3
  require "yaml"
4
4
  require "active_support/core_ext/hash/keys"
5
- require "active_support/core_ext/object/blank"
6
5
  require "active_support/key_generator"
7
6
  require "active_support/message_verifiers"
8
- require "active_support/deprecation"
9
7
  require "active_support/encrypted_configuration"
10
8
  require "active_support/hash_with_indifferent_access"
11
9
  require "active_support/configuration_file"
@@ -158,7 +156,11 @@ module Rails
158
156
 
159
157
  # Reload application routes regardless if they changed or not.
160
158
  def reload_routes!
161
- routes_reloader.reload!
159
+ if routes_reloader.execute_unless_loaded
160
+ routes_reloader.loaded = false
161
+ else
162
+ routes_reloader.reload!
163
+ end
162
164
  end
163
165
 
164
166
  def reload_routes_unless_loaded # :nodoc:
@@ -459,18 +461,21 @@ module Rails
459
461
  # is used to create all ActiveSupport::MessageVerifier and ActiveSupport::MessageEncryptor instances,
460
462
  # including the ones that sign and encrypt cookies.
461
463
  #
462
- # In development and test, this is randomly generated and stored in a
463
- # temporary file in <tt>tmp/local_secret.txt</tt>.
464
+ # We look for it first in <tt>ENV["SECRET_KEY_BASE"]</tt>, then in
465
+ # +credentials.secret_key_base+. For most applications, the correct place
466
+ # to store it is in the encrypted credentials file.
464
467
  #
465
- # You can also set <tt>ENV["SECRET_KEY_BASE_DUMMY"]</tt> to trigger the use of a randomly generated
466
- # secret_key_base that's stored in a temporary file. This is useful when precompiling assets for
467
- # production as part of a build step that otherwise does not need access to the production secrets.
468
+ # In development and test, if the secret_key_base is still empty, it is
469
+ # randomly generated and stored in a temporary file in
470
+ # <tt>tmp/local_secret.txt</tt>.
468
471
  #
469
- # Dockerfile example: <tt>RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile</tt>.
472
+ # Generating a random secret_key_base and storing it in
473
+ # <tt>tmp/local_secret.txt</tt> can also be triggered by setting
474
+ # <tt>ENV["SECRET_KEY_BASE_DUMMY"]</tt>. This is useful when precompiling
475
+ # assets for production as part of a build step that otherwise does not
476
+ # need access to the production secrets.
470
477
  #
471
- # In all other environments, we look for it first in <tt>ENV["SECRET_KEY_BASE"]</tt>,
472
- # then +credentials.secret_key_base+. For most applications, the correct place to store it is in the
473
- # encrypted credentials file.
478
+ # Dockerfile example: <tt>RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile</tt>.
474
479
  def secret_key_base
475
480
  config.secret_key_base
476
481
  end
@@ -609,7 +614,7 @@ module Rails
609
614
  end
610
615
 
611
616
  def railties_initializers(current) # :nodoc:
612
- initializers = []
617
+ initializers = Initializable::Collection.new
613
618
  ordered_railties.reverse.flatten.each do |r|
614
619
  if r == self
615
620
  initializers += current
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "action_controller"
4
+
3
5
  class Rails::ApplicationController < ActionController::Base # :nodoc:
4
6
  prepend_view_path File.expand_path("templates", __dir__)
5
7
  layout "application"
@@ -42,14 +42,17 @@ module Rails
42
42
 
43
43
  HEADERS = { lines: " Lines", code_lines: " LOC", classes: "Classes", methods: "Methods" }
44
44
 
45
+ PATTERN = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb)$/
46
+
45
47
  class_attribute :directories, default: DIRECTORIES
46
48
  class_attribute :test_types, default: TEST_TYPES
49
+ class_attribute :pattern, default: PATTERN
47
50
 
48
- # Add directories to the output of the `bin/rails stats` command.
51
+ # Add directories to the output of the <tt>bin/rails stats</tt> command.
49
52
  #
50
53
  # Rails::CodeStatistics.register_directory("My Directory", "path/to/dir")
51
54
  #
52
- # For directories that contain test code, set the `test_directory` argument to true.
55
+ # For directories that contain test code, set the <tt>test_directory</tt> argument to true.
53
56
  #
54
57
  # Rails::CodeStatistics.register_directory("Model specs", "spec/models", test_directory: true)
55
58
  def self.register_directory(label, path, test_directory: false)
@@ -81,7 +84,7 @@ module Rails
81
84
  Hash[@pairs.map { |pair| [pair.first, calculate_directory_statistics(pair.last)] }]
82
85
  end
83
86
 
84
- def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb)$/)
87
+ def calculate_directory_statistics(directory, pattern = self.class.pattern)
85
88
  stats = Rails::CodeStatisticsCalculator.new
86
89
 
87
90
  Dir.foreach(directory) do |file_name|
@@ -3,8 +3,6 @@
3
3
  require "thor"
4
4
  require "erb"
5
5
 
6
- require "active_support/core_ext/class/attribute"
7
- require "active_support/core_ext/module/delegation"
8
6
  require "active_support/core_ext/string/inflections"
9
7
 
10
8
  require "rails/command/actions"
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
- require "active_support/core_ext/class/attribute"
5
4
 
6
5
  module Rails
7
6
  module Command
data/lib/rails/command.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
+ require "active_support/rails"
4
5
  require "active_support/core_ext/enumerable"
5
- require "active_support/core_ext/object/blank"
6
6
  require "rails/deprecator"
7
7
 
8
8
  require "thor"
@@ -12,6 +12,9 @@ module Rails
12
12
 
13
13
  desc "update", "Update configs and some other initially generated files (or use just update:configs or update:bin)"
14
14
  def perform
15
+ require_application!
16
+ Rails.application.deprecators.silenced = true
17
+
15
18
  configs
16
19
  bin
17
20
  public_directory
@@ -70,7 +73,9 @@ module Rails
70
73
  skip_action_text: !defined?(ActionText::Engine),
71
74
  skip_action_cable: !defined?(ActionCable::Engine),
72
75
  skip_brakeman: skip_gem?("brakeman"),
76
+ skip_bundler_audit: skip_gem?("bundler-audit"),
73
77
  skip_rubocop: skip_gem?("rubocop"),
78
+ skip_thruster: skip_gem?("thruster"),
74
79
  skip_test: !defined?(Rails::TestUnitRailtie),
75
80
  skip_system_test: Rails.application.config.generators.system_tests.nil?,
76
81
  skip_asset_pipeline: asset_pipeline.nil?,
@@ -31,6 +31,7 @@ module Rails
31
31
 
32
32
  def execute(*)
33
33
  app = Rails.application
34
+ app.reload_routes_unless_loaded
34
35
  session = ActionDispatch::Integration::Session.new(app)
35
36
 
36
37
  # This makes app.url_for and app.foo_path available in the console
@@ -59,22 +60,11 @@ module Rails
59
60
  end
60
61
  end
61
62
 
62
- class ReloadCommand < IRB::Command::Base
63
- category "Rails console"
64
- description "Reloads the Rails application."
65
-
66
- def execute(*)
67
- puts "Reloading..."
68
- Rails.application.reloader.reload!
69
- end
70
- end
71
-
72
63
  IRB::HelperMethod.register(:helper, ControllerHelper)
73
64
  IRB::HelperMethod.register(:controller, ControllerInstance)
74
65
  IRB::HelperMethod.register(:new_session, NewSession)
75
66
  IRB::HelperMethod.register(:app, AppInstance)
76
67
  IRB::HelperMethod.register(:reload!, ReloadHelper)
77
- IRB::Command.register(:reload!, ReloadCommand)
78
68
 
79
69
  class IRBConsole
80
70
  def initialize(app)
@@ -97,12 +87,13 @@ module Rails
97
87
 
98
88
  env = colorized_env
99
89
  prompt_prefix = "%N(#{env})"
100
- IRB.conf[:IRB_NAME] = @app.name
90
+ # Respect user's configured irb name.
91
+ IRB.conf[:IRB_NAME] = @app.name if IRB.conf[:IRB_NAME] == "irb"
101
92
 
102
93
  IRB.conf[:PROMPT][:RAILS_PROMPT] = {
103
- PROMPT_I: "#{prompt_prefix}> ",
104
- PROMPT_S: "#{prompt_prefix}%l ",
105
- PROMPT_C: "#{prompt_prefix}* ",
94
+ PROMPT_I: "#{prompt_prefix}:%03n> ",
95
+ PROMPT_S: "#{prompt_prefix}:%03n%l ",
96
+ PROMPT_C: "#{prompt_prefix}:%03n* ",
106
97
  RETURN: "=> %s\n"
107
98
  }
108
99
 
@@ -131,7 +122,7 @@ module Rails
131
122
  when "production"
132
123
  IRB::Color.colorize("prod", [:RED])
133
124
  else
134
- Rails.env
125
+ IRB::Color.colorize(Rails.env, [:MAGENTA])
135
126
  end
136
127
  end
137
128
  end
@@ -35,7 +35,7 @@ module Rails
35
35
  def show
36
36
  load_environment_config!
37
37
 
38
- say credentials.read.presence || missing_credentials_message
38
+ say credentials.read.presence || missing_credentials!
39
39
  end
40
40
 
41
41
  desc "diff", "Enroll/disenroll in decrypted diffs of credentials using git"
@@ -57,6 +57,26 @@ module Rails
57
57
  say credentials.content_path.read
58
58
  end
59
59
 
60
+ desc "fetch PATH", "Fetch a value in the decrypted credentials"
61
+ def fetch(path)
62
+ load_environment_config!
63
+
64
+ if (yaml = credentials.read)
65
+ begin
66
+ value = YAML.load(yaml)
67
+ value = path.split(".").inject(value) do |doc, key|
68
+ doc.fetch(key)
69
+ end
70
+ say value.to_s
71
+ rescue KeyError, NoMethodError
72
+ say_error "Invalid or missing credential path: #{path}"
73
+ exit 1
74
+ end
75
+ else
76
+ missing_credentials!
77
+ end
78
+ end
79
+
60
80
  private
61
81
  def config
62
82
  Rails.application.config.credentials
@@ -81,7 +101,6 @@ module Rails
81
101
 
82
102
  encryption_key_file_generator = Rails::Generators::EncryptionKeyFileGenerator.new
83
103
  encryption_key_file_generator.add_key_file(key_path)
84
- encryption_key_file_generator.ignore_key_file(key_path)
85
104
  end
86
105
 
87
106
  def ensure_credentials_have_been_added
@@ -115,12 +134,13 @@ module Rails
115
134
  say "Your application will not be able to load '#{content_path}' until the error has been fixed.", :red
116
135
  end
117
136
 
118
- def missing_credentials_message
137
+ def missing_credentials!
119
138
  if !credentials.key?
120
- "Missing '#{key_path}' to decrypt credentials. See `#{executable(:help)}`."
139
+ say_error "Missing '#{key_path}' to decrypt credentials. See `#{executable(:help)}`."
121
140
  else
122
- "File '#{content_path}' does not exist. Use `#{executable(:edit)}` to change that."
141
+ say_error "File '#{content_path}' does not exist. Use `#{executable(:edit)}` to change that."
123
142
  end
143
+ exit 1
124
144
  end
125
145
 
126
146
  def relative_path(path)
@@ -22,6 +22,7 @@ module Rails
22
22
  def devcontainer_options
23
23
  @devcontainer_options ||= {
24
24
  app_name: Rails.application.railtie_name.chomp("_application"),
25
+ app_folder: File.basename(Rails.root),
25
26
  database: !!defined?(ActiveRecord) && database,
26
27
  active_storage: !!defined?(ActiveStorage),
27
28
  redis: !!((defined?(ActionCable) && !defined?(SolidCable)) || (defined?(ActiveJob) && !defined?(SolidQueue))),
@@ -45,7 +45,6 @@ module Rails
45
45
  def ensure_encryption_key_has_been_added
46
46
  return if encrypted_configuration.key?
47
47
  encryption_key_file_generator.add_key_file(key_path)
48
- encryption_key_file_generator.ignore_key_file(key_path)
49
48
  end
50
49
 
51
50
  def ensure_encrypted_configuration_has_been_added
@@ -31,8 +31,6 @@ module Rails
31
31
  Rails::TestUnit::Runner.parse_options(args)
32
32
  run_prepare_task if self.args.none?(EXACT_TEST_ARGUMENT_PATTERN)
33
33
  Rails::TestUnit::Runner.run(args)
34
- rescue Rails::TestUnit::InvalidTestError => error
35
- raise ArgumentError, error.message
36
34
  end
37
35
 
38
36
  # Define Thor tasks to avoid going through Rake and booting twice when using bin/rails test:*
@@ -4,9 +4,11 @@
4
4
 
5
5
  require "action_dispatch/routing/route_set"
6
6
 
7
+ # :enddoc:
8
+
7
9
  module Rails
8
10
  class Engine
9
- class LazyRouteSet < ActionDispatch::Routing::RouteSet # :nodoc:
11
+ class LazyRouteSet < ActionDispatch::Routing::RouteSet
10
12
  class NamedRouteCollection < ActionDispatch::Routing::RouteSet::NamedRouteCollection
11
13
  def route_defined?(name)
12
14
  Rails.application&.reload_routes_unless_loaded
@@ -34,16 +36,6 @@ module Rails
34
36
  Rails.application&.reload_routes_unless_loaded
35
37
  super
36
38
  end
37
-
38
- def polymorphic_url(record_or_hash_or_array, options = {})
39
- Rails.application&.reload_routes_unless_loaded
40
- super
41
- end
42
-
43
- def polymorphic_path(record_or_hash_or_array, options = {})
44
- Rails.application&.reload_routes_unless_loaded
45
- super
46
- end
47
39
  end
48
40
 
49
41
  def initialize(config = DEFAULT_CONFIG)
@@ -68,6 +60,11 @@ module Rails
68
60
  super
69
61
  end
70
62
 
63
+ def polymorphic_mappings
64
+ Rails.application&.reload_routes_unless_loaded
65
+ super
66
+ end
67
+
71
68
  def draw(&block)
72
69
  Rails.application&.reload_routes_unless_loaded
73
70
  super
data/lib/rails/engine.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require "rails/railtie"
4
4
  require "rails/engine/railties"
5
5
  require "active_support/callbacks"
6
- require "active_support/core_ext/module/delegation"
7
6
  require "active_support/core_ext/object/try"
8
7
  require "pathname"
9
8
 
@@ -8,8 +8,8 @@ module Rails
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 8
11
- MINOR = 0
12
- TINY = 0
11
+ MINOR = 1
12
+ TINY = 2
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -445,18 +445,17 @@ module Rails
445
445
 
446
446
  private
447
447
  # Define log for backwards compatibility. If just one argument is sent,
448
- # invoke say, otherwise invoke say_status. Differently from say and
449
- # similarly to say_status, this method respects the quiet? option given.
448
+ # invoke +say+, otherwise invoke +say_status+.
450
449
  def log(*args) # :doc:
451
450
  if args.size == 1
452
- say args.first.to_s unless options.quiet?
451
+ say args.first.to_s
453
452
  else
454
453
  args << (behavior == :invoke ? :green : :red)
455
454
  say_status(*args)
456
455
  end
457
456
  end
458
457
 
459
- # Runs the supplied command using either "rake ..." or "rails ..."
458
+ # Runs the supplied command using either +rake+ or +rails+
460
459
  # based on the executor parameter provided.
461
460
  def execute_command(executor, command, options = {}) # :doc:
462
461
  log executor, command
@@ -490,12 +489,16 @@ module Rails
490
489
  end
491
490
  alias rebase_indentation optimize_indentation
492
491
 
493
- # Indent the +Gemfile+ to the depth of @indentation
492
+ # Returns a string corresponding to the current indentation level
493
+ # (i.e. 2 * <code>@indentation</code> spaces). See also
494
+ # #with_indentation, which can be used to manage the indentation level.
494
495
  def indentation # :doc:
495
496
  " " * @indentation
496
497
  end
497
498
 
498
- # Manage +Gemfile+ indentation for a DSL action block
499
+ # Increases the current indentation indentation level for the duration
500
+ # of the given block, and decreases it after the block ends. Call
501
+ # #indentation to get an indentation string.
499
502
  def with_indentation(&block) # :doc:
500
503
  @indentation += 1
501
504
  instance_eval(&block)
@@ -7,12 +7,14 @@ require "open-uri"
7
7
  require "tsort"
8
8
  require "uri"
9
9
  require "rails/generators"
10
+ require "rails/generators/bundle_helper"
10
11
  require "active_support/core_ext/array/extract_options"
11
12
 
12
13
  module Rails
13
14
  module Generators
14
15
  class AppBase < Base # :nodoc:
15
16
  include AppName
17
+ include BundleHelper
16
18
 
17
19
  NODE_LTS_VERSION = "20.11.1"
18
20
  BUN_VERSION = "1.0.1"
@@ -71,7 +73,8 @@ module Rails
71
73
  class_option :skip_action_cable, type: :boolean, aliases: "-C", default: nil,
72
74
  desc: "Skip Action Cable files"
73
75
 
74
- class_option :skip_asset_pipeline, type: :boolean, aliases: "-A", default: nil
76
+ class_option :skip_asset_pipeline, type: :boolean, aliases: "-A", default: nil,
77
+ desc: "Skip the asset pipeline setup"
75
78
 
76
79
  class_option :skip_javascript, type: :boolean, aliases: ["-J", "--skip-js"], default: (true if name == "plugin"),
77
80
  desc: "Skip JavaScript files"
@@ -103,6 +106,9 @@ module Rails
103
106
  class_option :skip_brakeman, type: :boolean, default: nil,
104
107
  desc: "Skip brakeman setup"
105
108
 
109
+ class_option :skip_bundler_audit, type: :boolean, default: nil,
110
+ desc: "Skip bundler-audit setup"
111
+
106
112
  class_option :skip_ci, type: :boolean, default: nil,
107
113
  desc: "Skip GitHub CI files"
108
114
 
@@ -397,6 +403,10 @@ module Rails
397
403
  options[:skip_brakeman]
398
404
  end
399
405
 
406
+ def skip_bundler_audit?
407
+ options[:skip_bundler_audit]
408
+ end
409
+
400
410
  def skip_ci?
401
411
  options[:skip_ci]
402
412
  end
@@ -491,7 +501,7 @@ module Rails
491
501
  def javascript_gemfile_entry
492
502
  return if options[:skip_javascript]
493
503
 
494
- if options[:javascript] == "importmap"
504
+ if using_importmap?
495
505
  GemfileEntry.floats "importmap-rails", "Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]"
496
506
  else
497
507
  GemfileEntry.floats "jsbundling-rails", "Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]"
@@ -510,9 +520,12 @@ module Rails
510
520
  [ turbo_rails_entry, stimulus_rails_entry ]
511
521
  end
512
522
 
523
+ def using_importmap?
524
+ !options.skip_javascript? && options[:javascript] == "importmap"
525
+ end
526
+
513
527
  def using_js_runtime?
514
- (options[:javascript] && !%w[importmap].include?(options[:javascript])) ||
515
- (options[:css] && !%w[tailwind sass].include?(options[:css]))
528
+ !options.skip_javascript? && (!using_importmap? || (options[:css] && !%w[tailwind sass].include?(options[:css])))
516
529
  end
517
530
 
518
531
  def using_node?
@@ -523,26 +536,35 @@ module Rails
523
536
  using_js_runtime? && %w[bun].include?(options[:javascript])
524
537
  end
525
538
 
539
+ def capture_command(command, pattern = nil)
540
+ output = `#{command}`
541
+ if pattern
542
+ output[pattern]
543
+ else
544
+ output
545
+ end
546
+ rescue SystemCallError
547
+ nil
548
+ end
549
+
526
550
  def node_version
527
551
  if using_node?
528
552
  ENV.fetch("NODE_VERSION") do
529
- `node --version`[/\d+\.\d+\.\d+/]
530
- rescue
531
- NODE_LTS_VERSION
553
+ capture_command("node --version", /\d+\.\d+\.\d+/) || NODE_LTS_VERSION
532
554
  end
533
555
  end
534
556
  end
535
557
 
536
558
  def dockerfile_yarn_version
537
- using_node? and `yarn --version`[/\d+\.\d+\.\d+/]
538
- rescue
539
- "latest"
559
+ capture_command("yarn --version", /\d+\.\d+\.\d+/) || "latest"
560
+ end
561
+
562
+ def yarn_through_corepack?
563
+ using_node? and "#{dockerfile_yarn_version}" >= "2"
540
564
  end
541
565
 
542
566
  def dockerfile_bun_version
543
- using_bun? and `bun --version`[/\d+\.\d+\.\d+/]
544
- rescue
545
- BUN_VERSION
567
+ capture_command("bun --version", /\d+\.\d+\.\d+/) || BUN_VERSION
546
568
  end
547
569
 
548
570
  def dockerfile_binfile_fixups
@@ -592,7 +614,7 @@ module Rails
592
614
 
593
615
  def dockerfile_build_packages
594
616
  # start with the essentials
595
- packages = %w(build-essential git pkg-config)
617
+ packages = %w(build-essential git pkg-config libyaml-dev)
596
618
 
597
619
  # add database support
598
620
  packages << database.build_package unless skip_active_record?
@@ -609,6 +631,19 @@ module Rails
609
631
  packages.compact.sort
610
632
  end
611
633
 
634
+ def ci_packages
635
+ dockerfile_build_packages - [
636
+ # GitHub Actions doesn't have build-essential,
637
+ # but it's a meta-packages and all its dependencies are already installed.
638
+ "build-essential",
639
+ "git",
640
+ "pkg-config",
641
+ "libyaml-dev",
642
+ "unzip",
643
+ "python-is-python3",
644
+ ]
645
+ end
646
+
612
647
  def css_gemfile_entry
613
648
  return if options[:api]
614
649
  return unless options[:css]
@@ -629,30 +664,9 @@ module Rails
629
664
  end
630
665
  end
631
666
 
632
- def bundle_command(command, env = {})
633
- say_status :run, "bundle #{command}"
634
-
635
- # We are going to shell out rather than invoking Bundler::CLI.new(command)
636
- # because `rails new` loads the Thor gem and on the other hand bundler uses
637
- # its own vendored Thor, which could be a different version. Running both
638
- # things in the same process is a recipe for a night with paracetamol.
639
- #
640
- # Thanks to James Tucker for the Gem tricks involved in this call.
641
- _bundle_command = Gem.bin_path("bundler", "bundle")
642
-
643
- require "bundler"
644
- Bundler.with_original_env do
645
- exec_bundle_command(_bundle_command, command, env)
646
- end
647
- end
648
-
649
- def exec_bundle_command(bundle_command, command, env)
650
- full_command = %Q["#{Gem.ruby}" "#{bundle_command}" #{command}]
651
- if options[:quiet]
652
- system(env, full_command, out: File::NULL)
653
- else
654
- system(env, full_command)
655
- end
667
+ def rails_command(command, command_options = {})
668
+ command_options[:capture] = true if options[:quiet]
669
+ super
656
670
  end
657
671
 
658
672
  def bundle_install?
@@ -664,7 +678,7 @@ module Rails
664
678
  end
665
679
 
666
680
  def depend_on_bootsnap?
667
- !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION)
681
+ !options[:skip_bootsnap] && !options[:dev] && !jruby?
668
682
  end
669
683
 
670
684
  def target_rails_prerelease(self_command = "new")
@@ -740,7 +754,7 @@ module Rails
740
754
  end
741
755
 
742
756
  def add_bundler_platforms
743
- if bundle_install?
757
+ if bundle_install? && !jruby?
744
758
  # The vast majority of Rails apps will be deployed on `x86_64-linux`.
745
759
  bundle_command("lock --add-platform=x86_64-linux")
746
760
 
@@ -749,10 +763,21 @@ module Rails
749
763
  end
750
764
  end
751
765
 
752
- def generate_bundler_binstub
753
- if bundle_install?
754
- bundle_command("binstubs bundler")
766
+ def jruby?
767
+ defined?(JRUBY_VERSION)
768
+ end
769
+
770
+ def version_manager_ruby_version
771
+ return ENV["RBENV_VERSION"] if ENV["RBENV_VERSION"]
772
+ return ENV["rvm_ruby_string"] if ENV["rvm_ruby_string"]
773
+
774
+ version = if RUBY_ENGINE == "ruby"
775
+ Gem.ruby_version.to_s.sub(/\.([a-zA-Z])/, '-\1')
776
+ else
777
+ RUBY_ENGINE_VERSION
755
778
  end
779
+
780
+ "#{RUBY_ENGINE}-#{version}"
756
781
  end
757
782
 
758
783
  def empty_directory_with_keep_file(destination, config = {})
@@ -765,11 +790,13 @@ module Rails
765
790
  end
766
791
 
767
792
  def user_default_branch
768
- @user_default_branch ||= `git config init.defaultbranch`
793
+ @user_default_branch ||= capture_command("git config init.defaultbranch").strip.presence || "main"
769
794
  end
770
795
 
771
796
  def git_init_command
772
- return "git init" if user_default_branch.strip.present?
797
+ if capture_command("git config init.defaultbranch").present?
798
+ return "git init"
799
+ end
773
800
 
774
801
  git_version = `git --version`[/\d+\.\d+\.\d+/]
775
802
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Generators
5
+ module BundleHelper # :nodoc:
6
+ def bundle_command(command, env = {}, params = {})
7
+ say_status :run, "bundle #{command}"
8
+
9
+ # We are going to shell out rather than invoking Bundler::CLI.new(command)
10
+ # because `rails new` loads the Thor gem and on the other hand bundler uses
11
+ # its own vendored Thor, which could be a different version. Running both
12
+ # things in the same process is a recipe for a night with paracetamol.
13
+ #
14
+ # Thanks to James Tucker for the Gem tricks involved in this call.
15
+ _bundle_command = Gem.bin_path("bundler", "bundle")
16
+
17
+ require "bundler"
18
+ Bundler.with_original_env do
19
+ exec_bundle_command(_bundle_command, command, env, params)
20
+ end
21
+ end
22
+
23
+ private
24
+ def exec_bundle_command(bundle_command, command, env, params)
25
+ full_command = %Q["#{Gem.ruby}" "#{bundle_command}" #{command}]
26
+ if options[:quiet] || params[:quiet]
27
+ system(env, full_command, out: File::NULL)
28
+ else
29
+ system(env, full_command)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end