railties 7.0.8 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +188 -268
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.md +99 -0
  5. data/README.rdoc +4 -4
  6. data/lib/minitest/rails_plugin.rb +67 -1
  7. data/lib/rails/all.rb +1 -3
  8. data/lib/rails/api/task.rb +39 -6
  9. data/lib/rails/application/bootstrap.rb +28 -10
  10. data/lib/rails/application/configuration.rb +228 -72
  11. data/lib/rails/application/default_middleware_stack.rb +8 -2
  12. data/lib/rails/application/dummy_config.rb +19 -0
  13. data/lib/rails/application/finisher.rb +50 -33
  14. data/lib/rails/application.rb +110 -76
  15. data/lib/rails/backtrace_cleaner.rb +19 -4
  16. data/lib/rails/cli.rb +5 -3
  17. data/lib/rails/command/actions.rb +10 -12
  18. data/lib/rails/command/base.rb +55 -53
  19. data/lib/rails/command/environment_argument.rb +32 -16
  20. data/lib/rails/command/helpers/editor.rb +17 -12
  21. data/lib/rails/command.rb +84 -33
  22. data/lib/rails/commands/about/about_command.rb +14 -0
  23. data/lib/rails/commands/app/update_command.rb +93 -0
  24. data/lib/rails/commands/application/application_command.rb +2 -0
  25. data/lib/rails/commands/boot/boot_command.rb +14 -0
  26. data/lib/rails/commands/console/console_command.rb +11 -30
  27. data/lib/rails/commands/console/irb_console.rb +137 -0
  28. data/lib/rails/commands/credentials/USAGE +53 -55
  29. data/lib/rails/commands/credentials/credentials_command/diffing.rb +5 -3
  30. data/lib/rails/commands/credentials/credentials_command.rb +64 -70
  31. data/lib/rails/commands/db/system/change/change_command.rb +2 -1
  32. data/lib/rails/commands/dbconsole/dbconsole_command.rb +32 -131
  33. data/lib/rails/commands/destroy/destroy_command.rb +3 -2
  34. data/lib/rails/commands/dev/dev_command.rb +1 -6
  35. data/lib/rails/commands/devcontainer/devcontainer_command.rb +39 -0
  36. data/lib/rails/commands/encrypted/USAGE +15 -20
  37. data/lib/rails/commands/encrypted/encrypted_command.rb +46 -35
  38. data/lib/rails/commands/gem_help/USAGE +16 -0
  39. data/lib/rails/commands/gem_help/gem_help_command.rb +13 -0
  40. data/lib/rails/commands/generate/generate_command.rb +2 -2
  41. data/lib/rails/commands/help/USAGE +13 -13
  42. data/lib/rails/commands/help/help_command.rb +21 -2
  43. data/lib/rails/commands/initializers/initializers_command.rb +1 -4
  44. data/lib/rails/commands/middleware/middleware_command.rb +17 -0
  45. data/lib/rails/commands/new/new_command.rb +2 -0
  46. data/lib/rails/commands/notes/notes_command.rb +2 -1
  47. data/lib/rails/commands/plugin/plugin_command.rb +2 -0
  48. data/lib/rails/commands/rake/rake_command.rb +25 -22
  49. data/lib/rails/commands/restart/restart_command.rb +14 -0
  50. data/lib/rails/commands/routes/routes_command.rb +13 -1
  51. data/lib/rails/commands/runner/USAGE +14 -12
  52. data/lib/rails/commands/runner/runner_command.rb +42 -19
  53. data/lib/rails/commands/secret/secret_command.rb +13 -0
  54. data/lib/rails/commands/server/server_command.rb +37 -34
  55. data/lib/rails/commands/test/USAGE +14 -0
  56. data/lib/rails/commands/test/test_command.rb +58 -14
  57. data/lib/rails/commands/unused_routes/unused_routes_command.rb +75 -0
  58. data/lib/rails/commands/version/version_command.rb +1 -0
  59. data/lib/rails/configuration.rb +15 -6
  60. data/lib/rails/console/app.rb +5 -35
  61. data/lib/rails/console/helpers.rb +5 -16
  62. data/lib/rails/console/methods.rb +23 -0
  63. data/lib/rails/deprecator.rb +7 -0
  64. data/lib/rails/engine/configuration.rb +50 -6
  65. data/lib/rails/engine.rb +51 -23
  66. data/lib/rails/gem_version.rb +3 -3
  67. data/lib/rails/generators/actions.rb +6 -15
  68. data/lib/rails/generators/active_model.rb +28 -14
  69. data/lib/rails/generators/app_base.rb +382 -88
  70. data/lib/rails/generators/app_name.rb +3 -14
  71. data/lib/rails/generators/base.rb +21 -9
  72. data/lib/rails/generators/database.rb +231 -35
  73. data/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt +1 -1
  74. data/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt +2 -0
  75. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +2 -0
  76. data/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt +2 -0
  77. data/lib/rails/generators/generated_attribute.rb +38 -1
  78. data/lib/rails/generators/migration.rb +4 -5
  79. data/lib/rails/generators/model_helpers.rb +2 -1
  80. data/lib/rails/generators/rails/app/USAGE +22 -6
  81. data/lib/rails/generators/rails/app/app_generator.rb +132 -82
  82. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +110 -0
  83. data/lib/rails/generators/rails/app/templates/Gemfile.tt +19 -21
  84. data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +4 -0
  85. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +8 -1
  86. data/lib/rails/generators/rails/app/templates/app/views/layouts/mailer.html.erb.tt +1 -1
  87. data/lib/rails/generators/rails/app/templates/app/views/pwa/manifest.json.erb.tt +22 -0
  88. data/lib/rails/generators/rails/app/templates/app/views/pwa/service-worker.js +26 -0
  89. data/lib/rails/generators/rails/app/templates/bin/brakeman.tt +6 -0
  90. data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +7 -0
  91. data/lib/rails/generators/rails/app/templates/bin/setup.tt +15 -2
  92. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +6 -17
  93. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +3 -3
  94. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +11 -6
  95. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +10 -3
  96. data/lib/rails/generators/rails/app/templates/config/databases/{jdbcmysql.yml.tt → trilogy.yml.tt} +12 -7
  97. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +25 -8
  98. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +37 -28
  99. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +20 -13
  100. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +2 -0
  101. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +2 -2
  102. data/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt +1 -1
  103. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +4 -4
  104. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_2.rb.tt +70 -0
  105. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +11 -9
  106. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +11 -13
  107. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +24 -34
  108. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +9 -1
  109. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +6 -4
  110. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +15 -0
  111. data/lib/rails/generators/rails/app/templates/dockerignore.tt +56 -0
  112. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +138 -0
  113. data/lib/rails/generators/rails/app/templates/github/dependabot.yml +12 -0
  114. data/lib/rails/generators/rails/app/templates/gitignore.tt +7 -11
  115. data/lib/rails/generators/rails/app/templates/node-version.tt +1 -0
  116. data/lib/rails/generators/rails/app/templates/public/406-unsupported-browser.html +66 -0
  117. data/lib/rails/generators/rails/app/templates/public/icon.png +0 -0
  118. data/lib/rails/generators/rails/app/templates/public/icon.svg +3 -0
  119. data/lib/rails/generators/rails/app/templates/rubocop.yml.tt +8 -0
  120. data/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt +1 -1
  121. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +10 -8
  122. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +9 -7
  123. data/lib/rails/generators/rails/application_record/application_record_generator.rb +4 -0
  124. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +2 -1
  125. data/lib/rails/generators/rails/controller/USAGE +12 -4
  126. data/lib/rails/generators/rails/controller/controller_generator.rb +6 -1
  127. data/lib/rails/generators/rails/controller/templates/controller.rb.tt +1 -1
  128. data/lib/rails/generators/rails/credentials/credentials_generator.rb +29 -24
  129. data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +8 -0
  130. data/lib/rails/generators/rails/db/system/change/change_generator.rb +146 -5
  131. data/lib/rails/generators/rails/devcontainer/devcontainer_generator.rb +166 -0
  132. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +3 -0
  133. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +47 -0
  134. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/devcontainer.json.tt +37 -0
  135. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +1 -2
  136. data/lib/rails/generators/rails/migration/USAGE +21 -11
  137. data/lib/rails/generators/rails/migration/migration_generator.rb +4 -0
  138. data/lib/rails/generators/rails/model/model_generator.rb +4 -0
  139. data/lib/rails/generators/rails/plugin/USAGE +17 -6
  140. data/lib/rails/generators/rails/plugin/plugin_generator.rb +43 -22
  141. data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +2 -2
  142. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +7 -3
  143. data/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt +1 -1
  144. data/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt +2 -0
  145. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +1 -17
  146. data/lib/rails/generators/rails/plugin/templates/bin/rubocop.tt +7 -0
  147. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +103 -0
  148. data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +12 -0
  149. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +0 -2
  150. data/lib/rails/generators/rails/plugin/templates/rubocop.yml.tt +8 -0
  151. data/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt +1 -1
  152. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +4 -4
  153. data/lib/rails/generators/rails/resource/resource_generator.rb +6 -0
  154. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +2 -1
  155. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +1 -1
  156. data/lib/rails/generators/test_case.rb +2 -2
  157. data/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt +6 -4
  158. data/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt +3 -2
  159. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +16 -2
  160. data/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt +2 -2
  161. data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt +2 -2
  162. data/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt +2 -0
  163. data/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt +1 -1
  164. data/lib/rails/generators/testing/assertions.rb +20 -0
  165. data/lib/rails/generators/testing/{behaviour.rb → behavior.rb} +8 -4
  166. data/lib/rails/generators.rb +7 -15
  167. data/lib/rails/health_controller.rb +55 -0
  168. data/lib/rails/info.rb +3 -3
  169. data/lib/rails/info_controller.rb +31 -11
  170. data/lib/rails/mailers_controller.rb +29 -6
  171. data/lib/rails/paths.rb +15 -12
  172. data/lib/rails/pwa_controller.rb +15 -0
  173. data/lib/rails/rack/logger.rb +27 -16
  174. data/lib/rails/rackup/server.rb +15 -0
  175. data/lib/rails/railtie/configurable.rb +2 -2
  176. data/lib/rails/railtie/configuration.rb +14 -1
  177. data/lib/rails/railtie.rb +20 -21
  178. data/lib/rails/source_annotation_extractor.rb +67 -18
  179. data/lib/rails/tasks/engine.rake +8 -8
  180. data/lib/rails/tasks/framework.rake +2 -34
  181. data/lib/rails/tasks/log.rake +1 -1
  182. data/lib/rails/tasks/misc.rake +3 -14
  183. data/lib/rails/tasks/statistics.rake +5 -4
  184. data/lib/rails/tasks/tmp.rake +6 -6
  185. data/lib/rails/tasks/zeitwerk.rake +15 -35
  186. data/lib/rails/tasks.rb +0 -2
  187. data/lib/rails/templates/layouts/application.html.erb +1 -1
  188. data/lib/rails/templates/rails/mailers/email.html.erb +44 -8
  189. data/lib/rails/templates/rails/mailers/index.html.erb +14 -7
  190. data/lib/rails/templates/rails/mailers/mailer.html.erb +11 -5
  191. data/lib/rails/templates/rails/welcome/index.html.erb +5 -2
  192. data/lib/rails/test_help.rb +11 -18
  193. data/lib/rails/test_unit/line_filtering.rb +1 -1
  194. data/lib/rails/test_unit/reporter.rb +14 -4
  195. data/lib/rails/test_unit/runner.rb +62 -20
  196. data/lib/rails/test_unit/test_parser.rb +133 -0
  197. data/lib/rails/test_unit/testing.rake +13 -33
  198. data/lib/rails/testing/maintain_test_schema.rb +16 -0
  199. data/lib/rails/version.rb +1 -1
  200. data/lib/rails/zeitwerk_checker.rb +15 -0
  201. data/lib/rails.rb +20 -17
  202. metadata +89 -42
  203. data/RDOC_MAIN.rdoc +0 -97
  204. data/lib/rails/app_updater.rb +0 -40
  205. data/lib/rails/application/dummy_erb_compiler.rb +0 -18
  206. data/lib/rails/commands/secrets/USAGE +0 -66
  207. data/lib/rails/commands/secrets/secrets_command.rb +0 -65
  208. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +0 -68
  209. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +0 -70
  210. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +0 -24
  211. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +0 -62
  212. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +0 -53
  213. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_0.rb.tt +0 -143
  214. data/lib/rails/generators/rails/app/templates/public/apple-touch-icon-precomposed.png +0 -0
  215. data/lib/rails/generators/rails/app/templates/public/apple-touch-icon.png +0 -0
  216. data/lib/rails/generators/rails/app/templates/public/favicon.ico +0 -0
  217. data/lib/rails/generators/rails/model/USAGE +0 -113
  218. data/lib/rails/ruby_version_check.rb +0 -15
  219. data/lib/rails/secrets.rb +0 -110
  220. data/lib/rails/tasks/middleware.rake +0 -9
  221. data/lib/rails/tasks/restart.rake +0 -9
@@ -3,7 +3,8 @@
3
3
  require "thor"
4
4
  require "erb"
5
5
 
6
- require "active_support/core_ext/string/filters"
6
+ require "active_support/core_ext/class/attribute"
7
+ require "active_support/core_ext/module/delegation"
7
8
  require "active_support/core_ext/string/inflections"
8
9
 
9
10
  require "rails/command/actions"
@@ -14,32 +15,16 @@ module Rails
14
15
  class Error < Thor::Error # :nodoc:
15
16
  end
16
17
 
17
- class CorrectableError < Error # :nodoc:
18
- attr_reader :key, :options
19
-
20
- def initialize(message, key, options)
21
- @key = key
22
- @options = options
23
- super(message)
24
- end
25
-
26
- if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable)
27
- include DidYouMean::Correctable
28
-
29
- def corrections
30
- @corrections ||= DidYouMean::SpellChecker.new(dictionary: options).correct(key)
31
- end
32
- end
33
- end
34
-
35
18
  include Actions
36
19
 
20
+ class_attribute :bin, instance_accessor: false, default: "bin/rails"
21
+
37
22
  class << self
38
23
  def exit_on_failure? # :nodoc:
39
24
  false
40
25
  end
41
26
 
42
- # Returns true when the app is a Rails engine.
27
+ # Returns true when the app is a \Rails engine.
43
28
  def engine?
44
29
  defined?(ENGINE_ROOT)
45
30
  end
@@ -50,7 +35,7 @@ module Rails
50
35
  if usage
51
36
  super
52
37
  else
53
- @desc ||= ERB.new(File.read(usage_path), trim_mode: "-").result(binding) if usage_path
38
+ class_usage
54
39
  end
55
40
  end
56
41
 
@@ -81,23 +66,38 @@ module Rails
81
66
 
82
67
  def perform(command, args, config) # :nodoc:
83
68
  if Rails::Command::HELP_MAPPINGS.include?(args.first)
84
- command, args = "help", []
69
+ command, args = "help", [command]
70
+ args.clear if instance_method(:help).arity.zero?
85
71
  end
86
72
 
87
73
  dispatch(command, args.dup, nil, config)
88
74
  end
89
75
 
90
76
  def printing_commands
91
- namespaced_commands
77
+ commands.filter_map do |name, command|
78
+ [namespaced_name(name), command.description] unless command.hidden?
79
+ end
92
80
  end
93
81
 
94
- def executable
95
- "rails #{command_name}"
82
+ def executable(command_name = self.command_name)
83
+ "#{bin} #{namespaced_name(command_name)}"
96
84
  end
97
85
 
98
- # Use Rails' default banner.
99
- def banner(*)
100
- "#{executable} #{arguments.map(&:usage).join(' ')} [options]".squish
86
+ def banner(command = nil, *)
87
+ if command
88
+ # Similar to Thor's banner, but show the namespace (minus the
89
+ # "rails:" prefix), and show the command's declared bin instead of
90
+ # the command runner.
91
+ command.formatted_usage(self).gsub(/^#{namespace}:(\w+)/) { executable($1) }
92
+ else
93
+ executable
94
+ end
95
+ end
96
+
97
+ # Override Thor's class-level help to also show the USAGE.
98
+ def help(shell, *) # :nodoc:
99
+ super
100
+ shell.say class_usage if class_usage
101
101
  end
102
102
 
103
103
  # Sets the base_name taking into account the current class namespace.
@@ -119,12 +119,16 @@ module Rails
119
119
  end
120
120
  end
121
121
 
122
+ def class_usage # :nodoc:
123
+ if usage_path
124
+ @class_usage ||= ERB.new(File.read(usage_path), trim_mode: "-").result(binding)
125
+ end
126
+ end
127
+
122
128
  # Path to lookup a USAGE description in a file.
123
129
  def usage_path
124
- if default_command_root
125
- path = File.join(default_command_root, "USAGE")
126
- path if File.exist?(path)
127
- end
130
+ @usage_path = resolve_path("USAGE") unless defined?(@usage_path)
131
+ @usage_path
128
132
  end
129
133
 
130
134
  # Default file root to place extra files a command might need, placed
@@ -133,8 +137,8 @@ module Rails
133
137
  # For a Rails::Command::TestCommand placed in <tt>rails/command/test_command.rb</tt>
134
138
  # would return <tt>rails/test</tt>.
135
139
  def default_command_root
136
- path = File.expand_path(relative_command_path, __dir__)
137
- path if File.exist?(path)
140
+ @default_command_root = resolve_path(".") unless defined?(@default_command_root)
141
+ @default_command_root
138
142
  end
139
143
 
140
144
  private
@@ -145,37 +149,35 @@ module Rails
145
149
  else
146
150
  # Prevent exception about command without usage.
147
151
  # Some commands define their documentation differently.
148
- @usage ||= ""
152
+ @usage ||= meth
149
153
  @desc ||= ""
150
154
 
151
155
  super
152
156
  end
153
157
  end
154
158
 
155
- def command_root_namespace
156
- (namespace.split(":") - %w(rails)).join(":")
157
- end
158
-
159
- def relative_command_path
160
- File.join("../commands", *command_root_namespace.split(":"))
159
+ def namespaced_name(name)
160
+ *prefix, basename = namespace.delete_prefix("rails:").split(":")
161
+ prefix.concat([basename, name.to_s].uniq).join(":")
161
162
  end
162
163
 
163
- def namespaced_commands
164
- commands.keys.map do |key|
165
- if command_root_namespace.match?(/(\A|:)#{key}\z/)
166
- command_root_namespace
167
- else
168
- "#{command_root_namespace}:#{key}"
169
- end
170
- end
164
+ def resolve_path(path)
165
+ path = File.join("../commands", *namespace.delete_prefix("rails:").split(":"), path)
166
+ path = File.expand_path(path, __dir__)
167
+ path if File.exist?(path)
171
168
  end
172
169
  end
173
170
 
174
- def help
175
- if command_name = self.class.command_name
176
- self.class.command_help(shell, command_name)
177
- else
171
+ no_commands do
172
+ delegate :executable, to: :class
173
+ attr_reader :current_subcommand
174
+
175
+ def invoke_command(command, *) # :nodoc:
176
+ @current_subcommand ||= nil
177
+ original_subcommand, @current_subcommand = @current_subcommand, command.name
178
178
  super
179
+ ensure
180
+ @current_subcommand = original_subcommand
179
181
  end
180
182
  end
181
183
  end
@@ -9,31 +9,47 @@ module Rails
9
9
  extend ActiveSupport::Concern
10
10
 
11
11
  included do
12
- no_commands do
13
- class_attribute :environment_desc, default: "Specifies the environment to run this #{self.command_name} under (test/development/production)."
12
+ class_option :environment, aliases: "-e", type: :string,
13
+ desc: "The environment to run `#{self.command_name}` in (e.g. test / development / production)."
14
+ end
15
+
16
+ def initialize(...)
17
+ super
18
+
19
+ @environment_specified = options[:environment].present?
20
+
21
+ if !@environment_specified
22
+ self.options = options.merge(environment: Rails::Command.environment)
23
+ elsif !available_environments.include?(options[:environment])
24
+ self.options = options.merge(environment: expand_environment_name(options[:environment]))
14
25
  end
15
- class_option :environment, aliases: "-e", type: :string, desc: environment_desc
16
26
  end
17
27
 
18
28
  private
19
- def extract_environment_option_from_argument(default_environment: Rails::Command.environment)
20
- if options[:environment]
21
- self.options = options.merge(environment: acceptable_environment(options[:environment]))
22
- else
23
- self.options = options.merge(environment: default_environment)
24
- end
29
+ def require_application!
30
+ ENV["RAILS_ENV"] = environment
31
+ super
32
+ end
33
+
34
+ def environment
35
+ @environment ||= options[:environment]
36
+ end
37
+
38
+ def environment=(environment)
39
+ @environment = environment
25
40
  end
26
41
 
27
- def acceptable_environment(env = nil)
28
- if available_environments.include? env
29
- env
30
- else
31
- %w( production development test ).detect { |e| /^#{env}/.match?(e) } || env
32
- end
42
+ def environment_specified?
43
+ @environment_specified
33
44
  end
34
45
 
35
46
  def available_environments
36
- Dir["config/environments/*.rb"].map { |fname| File.basename(fname, ".*") }
47
+ @available_environments ||=
48
+ Dir["config/environments/*.rb"].map { |filename| File.basename(filename, ".*") }
49
+ end
50
+
51
+ def expand_environment_name(name)
52
+ %w[production development test].find { |full_name| full_name.start_with?(name) } || name
37
53
  end
38
54
  end
39
55
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "shellwords"
3
4
  require "active_support/encrypted_file"
4
5
 
5
6
  module Rails
@@ -7,27 +8,31 @@ module Rails
7
8
  module Helpers
8
9
  module Editor
9
10
  private
10
- def ensure_editor_available(command:)
11
- if ENV["EDITOR"].to_s.empty?
12
- say "No $EDITOR to open file in. Assign one like this:"
11
+ def editor
12
+ ENV["VISUAL"].to_s.empty? ? ENV["EDITOR"] : ENV["VISUAL"]
13
+ end
14
+
15
+ def display_hint_if_system_editor_not_specified
16
+ if editor.to_s.empty?
17
+ say "No $VISUAL or $EDITOR to open file in. Assign one like this:"
13
18
  say ""
14
- say %(EDITOR="mate --wait" #{command})
19
+ say %(VISUAL="mate --wait" #{executable(current_subcommand)})
15
20
  say ""
16
- say "For editors that fork and exit immediately, it's important to pass a wait flag,"
17
- say "otherwise the credentials will be saved immediately with no chance to edit."
21
+ say "For editors that fork and exit immediately, it's important to pass a wait flag;"
22
+ say "otherwise, the file will be saved immediately with no chance to edit."
18
23
 
19
- false
20
- else
21
24
  true
22
25
  end
23
26
  end
24
27
 
25
- def catch_editing_exceptions
26
- yield
28
+ def system_editor(file_path)
29
+ system(*Shellwords.split(editor), file_path.to_s)
30
+ end
31
+
32
+ def using_system_editor
33
+ display_hint_if_system_editor_not_specified || yield
27
34
  rescue Interrupt
28
35
  say "Aborted changing file: nothing saved."
29
- rescue ActiveSupport::EncryptedFile::MissingKeyError => error
30
- say error.message
31
36
  end
32
37
  end
33
38
  end
data/lib/rails/command.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "active_support"
4
4
  require "active_support/core_ext/enumerable"
5
5
  require "active_support/core_ext/object/blank"
6
+ require "rails/deprecator"
6
7
 
7
8
  require "thor"
8
9
 
@@ -13,9 +14,40 @@ module Rails
13
14
  autoload :Behavior
14
15
  autoload :Base
15
16
 
17
+ class CorrectableNameError < StandardError # :nodoc:
18
+ attr_reader :name
19
+
20
+ def initialize(message, name, alternatives)
21
+ @name = name
22
+ @alternatives = alternatives
23
+ super(message)
24
+ end
25
+
26
+ if !Exception.method_defined?(:detailed_message) # Ruby 3.2+
27
+ def detailed_message(...)
28
+ message
29
+ end
30
+ end
31
+
32
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
33
+ include DidYouMean::Correctable
34
+
35
+ def corrections
36
+ @corrections ||= DidYouMean::SpellChecker.new(dictionary: @alternatives).correct(name)
37
+ end
38
+ end
39
+ end
40
+
41
+ class UnrecognizedCommandError < CorrectableNameError # :nodoc:
42
+ def initialize(name)
43
+ super("Unrecognized command #{name.inspect}", name, Command.printing_commands.map(&:first))
44
+ end
45
+ end
46
+
16
47
  include Behavior
17
48
 
18
- HELP_MAPPINGS = %w(-h -? --help)
49
+ HELP_MAPPINGS = %w(-h -? --help).to_set
50
+ VERSION_MAPPINGS = %w(-v --version).to_set
19
51
 
20
52
  class << self
21
53
  def hidden_commands # :nodoc:
@@ -28,30 +60,26 @@ module Rails
28
60
 
29
61
  # Receives a namespace, arguments, and the behavior to invoke the command.
30
62
  def invoke(full_namespace, args = [], **config)
31
- namespace = full_namespace = full_namespace.to_s
32
-
33
- if char = namespace =~ /:(\w+)$/
34
- command_name, namespace = $1, namespace.slice(0, char)
35
- else
36
- command_name = namespace
37
- end
38
-
39
- command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)
40
- command_name, namespace, args = "application", "application", ["--help"] if rails_new_with_no_path?(args)
41
- command_name, namespace = "version", "version" if %w( -v --version ).include?(command_name)
42
-
43
- original_argv = ARGV.dup
44
- ARGV.replace(args)
63
+ args = ["--help"] if rails_new_with_no_path?(args)
45
64
 
65
+ full_namespace = full_namespace.to_s
66
+ namespace, command_name = split_namespace(full_namespace)
46
67
  command = find_by_namespace(namespace, command_name)
47
- if command && command.all_commands[command_name]
48
- command.perform(command_name, args, config)
68
+
69
+ with_argv(args) do
70
+ if command && command.all_commands[command_name]
71
+ command.perform(command_name, args, config)
72
+ else
73
+ invoke_rake(full_namespace, args, config)
74
+ end
75
+ end
76
+ rescue UnrecognizedCommandError => error
77
+ if error.name == full_namespace && command && command_name == full_namespace
78
+ command.perform("help", [], config)
49
79
  else
50
- args = ["--describe", full_namespace] if HELP_MAPPINGS.include?(args[0])
51
- find_by_namespace("rake").perform(full_namespace, args, config)
80
+ puts error.detailed_message
52
81
  end
53
- ensure
54
- ARGV.replace(original_argv)
82
+ exit(1)
55
83
  end
56
84
 
57
85
  # Rails finds namespaces similar to Thor, it only adds one rule:
@@ -76,33 +104,56 @@ module Rails
76
104
  namespaces[(lookups & namespaces.keys).first]
77
105
  end
78
106
 
79
- # Returns the root of the Rails engine or app running the command.
107
+ # Returns the root of the \Rails engine or app running the command.
80
108
  def root
81
109
  if defined?(ENGINE_ROOT)
82
110
  Pathname.new(ENGINE_ROOT)
83
- elsif defined?(APP_PATH)
84
- Pathname.new(File.expand_path("../..", APP_PATH))
111
+ else
112
+ application_root
85
113
  end
86
114
  end
87
115
 
88
- def print_commands # :nodoc:
89
- commands.each { |command| puts(" #{command}") }
116
+ def application_root # :nodoc:
117
+ Pathname.new(File.expand_path("../..", APP_PATH)) if defined?(APP_PATH)
90
118
  end
91
119
 
92
- private
93
- COMMANDS_IN_USAGE = %w(generate console server test test:system dbconsole new)
94
- private_constant :COMMANDS_IN_USAGE
120
+ def printing_commands # :nodoc:
121
+ lookup!
122
+
123
+ (subclasses - hidden_commands).flat_map(&:printing_commands)
124
+ end
95
125
 
126
+ private
96
127
  def rails_new_with_no_path?(args)
97
128
  args == ["new"]
98
129
  end
99
130
 
100
- def commands
101
- lookup!
131
+ def split_namespace(namespace)
132
+ case namespace
133
+ when /^(.+):(\w+)$/
134
+ [$1, $2]
135
+ when ""
136
+ ["help", "help"]
137
+ when HELP_MAPPINGS, "help"
138
+ ["help", "help_extended"]
139
+ when VERSION_MAPPINGS
140
+ ["version", "version"]
141
+ else
142
+ [namespace, namespace]
143
+ end
144
+ end
102
145
 
103
- visible_commands = (subclasses - hidden_commands).flat_map(&:printing_commands)
146
+ def with_argv(argv)
147
+ original_argv = ARGV.dup
148
+ ARGV.replace(argv)
149
+ yield
150
+ ensure
151
+ ARGV.replace(original_argv)
152
+ end
104
153
 
105
- (visible_commands - COMMANDS_IN_USAGE).sort
154
+ def invoke_rake(task, args, config)
155
+ args = ["--describe", task] if HELP_MAPPINGS.include?(args[0])
156
+ find_by_namespace("rake").perform(task, args, config)
106
157
  end
107
158
 
108
159
  def command_type # :doc:
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Command
5
+ class AboutCommand < Base # :nodoc:
6
+ desc "about", "List versions of all Rails frameworks and the environment"
7
+ def perform
8
+ boot_application!
9
+
10
+ say Rails::Info
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/rails/app/app_generator"
5
+
6
+ module Rails
7
+ module Command
8
+ module App
9
+ class UpdateCommand < Base # :nodoc:
10
+ include Thor::Actions
11
+ add_runtime_options!
12
+
13
+ desc "update", "Update configs and some other initially generated files (or use just update:configs or update:bin)"
14
+ def perform
15
+ configs
16
+ bin
17
+ public_directory
18
+ active_storage
19
+ display_upgrade_guide_info
20
+ end
21
+
22
+ desc "configs", "Update config files in the application config/ directory", hide: true
23
+ def configs
24
+ require_application!
25
+ app_generator.create_boot_file
26
+ app_generator.update_config_files
27
+ end
28
+
29
+ desc "bin", "Add or update executables in the application bin/ directory", hide: true
30
+ def bin
31
+ require_application!
32
+ app_generator.update_bin_files
33
+ end
34
+
35
+ desc "public_directory", "Add or update files in the application public/ directory", hide: true
36
+ def public_directory
37
+ require_application!
38
+ app_generator.create_public_files
39
+ end
40
+
41
+ desc "active_storage", "Run the active_storage:update command", hide: true
42
+ def active_storage
43
+ require_application!
44
+ app_generator.update_active_storage
45
+ end
46
+
47
+ private
48
+ def display_upgrade_guide_info
49
+ say "\nAfter this, check Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html for more details about upgrading your app."
50
+ end
51
+
52
+ def app_generator
53
+ @app_generator ||= begin
54
+ gen = Rails::Generators::AppGenerator.new(["rails"], generator_options, destination_root: Rails.root)
55
+ gen.send(:valid_const?) unless File.exist?(Rails.root.join("config", "application.rb"))
56
+ gen
57
+ end
58
+ end
59
+
60
+ def generator_options
61
+ {
62
+ api: !!Rails.application.config.api_only,
63
+ update: true,
64
+ name: Rails.application.class.name.chomp("::Application").underscore,
65
+ skip_active_job: !defined?(ActiveJob::Railtie),
66
+ skip_active_record: !defined?(ActiveRecord::Railtie),
67
+ skip_active_storage: !defined?(ActiveStorage::Engine),
68
+ skip_action_mailer: !defined?(ActionMailer::Railtie),
69
+ skip_action_mailbox: !defined?(ActionMailbox::Engine),
70
+ skip_action_text: !defined?(ActionText::Engine),
71
+ skip_action_cable: !defined?(ActionCable::Engine),
72
+ skip_test: !defined?(Rails::TestUnitRailtie),
73
+ skip_system_test: Rails.application.config.generators.system_tests.nil?,
74
+ asset_pipeline: asset_pipeline,
75
+ skip_asset_pipeline: asset_pipeline.nil?,
76
+ skip_bootsnap: !defined?(Bootsnap),
77
+ }.merge(options)
78
+ end
79
+
80
+ def asset_pipeline
81
+ case
82
+ when defined?(Sprockets::Railtie)
83
+ "sprockets"
84
+ when defined?(Propshaft::Railtie)
85
+ "propshaft"
86
+ else
87
+ nil
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -18,6 +18,8 @@ module Rails
18
18
  class ApplicationCommand < Base # :nodoc:
19
19
  hide_command!
20
20
 
21
+ self.bin = "rails"
22
+
21
23
  def help
22
24
  perform # Punt help output to the generator.
23
25
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/command/environment_argument"
4
+
5
+ module Rails
6
+ module Command
7
+ class BootCommand < Base # :nodoc:
8
+ include EnvironmentArgument
9
+
10
+ desc "boot", "Boot the application and exit"
11
+ def perform(*) = boot_application!
12
+ end
13
+ end
14
+ end
@@ -1,20 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "irb"
4
- require "irb/completion"
5
-
6
3
  require "rails/command/environment_argument"
7
4
 
8
5
  module Rails
9
6
  class Console
10
- module BacktraceCleaner
11
- def filter_backtrace(bt)
12
- if result = super
13
- Rails.backtrace_cleaner.filter([result]).first
14
- end
15
- end
16
- end
17
-
18
7
  def self.start(*args)
19
8
  new(*args).start
20
9
  end
@@ -34,19 +23,18 @@ module Rails
34
23
 
35
24
  app.load_console
36
25
 
37
- @console = app.config.console || IRB
38
-
39
- if @console == IRB
40
- IRB::WorkSpace.prepend(BacktraceCleaner)
41
-
42
- if Rails.env.production?
43
- ENV["IRB_USE_AUTOCOMPLETE"] ||= "false"
44
- end
26
+ @console = app.config.console || begin
27
+ require "rails/commands/console/irb_console"
28
+ IRBConsole.new(app)
45
29
  end
46
30
  end
47
31
 
48
32
  def sandbox?
49
- options[:sandbox]
33
+ return options[:sandbox] if !options[:sandbox].nil?
34
+
35
+ return false if Rails.env.local?
36
+
37
+ app.config.sandbox_by_default
50
38
  end
51
39
 
52
40
  def environment
@@ -68,9 +56,6 @@ module Rails
68
56
  puts "Loading #{Rails.env} environment (Rails #{Rails.version})"
69
57
  end
70
58
 
71
- if defined?(console::ExtendCommandBundle)
72
- console::ExtendCommandBundle.include(Rails::ConsoleMethods)
73
- end
74
59
  console.start
75
60
  end
76
61
  end
@@ -79,7 +64,7 @@ module Rails
79
64
  class ConsoleCommand < Base # :nodoc:
80
65
  include EnvironmentArgument
81
66
 
82
- class_option :sandbox, aliases: "-s", type: :boolean, default: false,
67
+ class_option :sandbox, aliases: "-s", type: :boolean, default: nil,
83
68
  desc: "Rollback database modifications on exit."
84
69
 
85
70
  def initialize(args = [], local_options = {}, config = {})
@@ -96,13 +81,9 @@ module Rails
96
81
  super(args, local_options, config)
97
82
  end
98
83
 
84
+ desc "console", "Start the Rails console"
99
85
  def perform
100
- extract_environment_option_from_argument
101
-
102
- # RAILS_ENV needs to be set before config/application is required.
103
- ENV["RAILS_ENV"] = options[:environment]
104
-
105
- require_application_and_environment!
86
+ boot_application!
106
87
  Rails::Console.start(Rails.application, options)
107
88
  end
108
89
  end