railties 6.1.6 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +626 -259
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.md +99 -0
  5. data/README.rdoc +5 -6
  6. data/lib/minitest/rails_plugin.rb +64 -1
  7. data/lib/rails/all.rb +0 -1
  8. data/lib/rails/api/task.rb +35 -4
  9. data/lib/rails/app_updater.rb +9 -6
  10. data/lib/rails/application/bootstrap.rb +41 -7
  11. data/lib/rails/application/configuration.rb +262 -88
  12. data/lib/rails/application/default_middleware_stack.rb +29 -4
  13. data/lib/rails/application/dummy_config.rb +19 -0
  14. data/lib/rails/application/finisher.rb +89 -124
  15. data/lib/rails/application/routes_reloader.rb +8 -0
  16. data/lib/rails/application.rb +200 -125
  17. data/lib/rails/application_controller.rb +3 -3
  18. data/lib/rails/autoloaders/inflector.rb +21 -0
  19. data/lib/rails/autoloaders.rb +42 -42
  20. data/lib/rails/backtrace_cleaner.rb +1 -1
  21. data/lib/rails/cli.rb +5 -2
  22. data/lib/rails/code_statistics.rb +2 -2
  23. data/lib/rails/code_statistics_calculator.rb +10 -1
  24. data/lib/rails/command/actions.rb +10 -12
  25. data/lib/rails/command/base.rb +61 -45
  26. data/lib/rails/command/behavior.rb +1 -1
  27. data/lib/rails/command/environment_argument.rb +33 -17
  28. data/lib/rails/command/helpers/editor.rb +17 -12
  29. data/lib/rails/command.rb +90 -36
  30. data/lib/rails/commands/about/about_command.rb +14 -0
  31. data/lib/rails/commands/application/application_command.rb +2 -0
  32. data/lib/rails/commands/console/console_command.rb +17 -13
  33. data/lib/rails/commands/credentials/USAGE +53 -53
  34. data/lib/rails/commands/credentials/credentials_command/diffing.rb +31 -19
  35. data/lib/rails/commands/credentials/credentials_command.rb +67 -69
  36. data/lib/rails/commands/db/system/change/change_command.rb +2 -1
  37. data/lib/rails/commands/dbconsole/dbconsole_command.rb +26 -117
  38. data/lib/rails/commands/destroy/destroy_command.rb +3 -2
  39. data/lib/rails/commands/dev/dev_command.rb +1 -6
  40. data/lib/rails/commands/encrypted/USAGE +15 -20
  41. data/lib/rails/commands/encrypted/encrypted_command.rb +46 -35
  42. data/lib/rails/commands/gem_help/USAGE +16 -0
  43. data/lib/rails/commands/gem_help/gem_help_command.rb +13 -0
  44. data/lib/rails/commands/generate/generate_command.rb +2 -2
  45. data/lib/rails/commands/help/USAGE +15 -14
  46. data/lib/rails/commands/help/help_command.rb +21 -2
  47. data/lib/rails/commands/initializers/initializers_command.rb +1 -4
  48. data/lib/rails/commands/middleware/middleware_command.rb +17 -0
  49. data/lib/rails/commands/new/new_command.rb +2 -0
  50. data/lib/rails/commands/notes/notes_command.rb +2 -1
  51. data/lib/rails/commands/plugin/plugin_command.rb +2 -0
  52. data/lib/rails/commands/rake/rake_command.rb +25 -22
  53. data/lib/rails/commands/restart/restart_command.rb +14 -0
  54. data/lib/rails/commands/routes/routes_command.rb +13 -1
  55. data/lib/rails/commands/runner/USAGE +14 -12
  56. data/lib/rails/commands/runner/runner_command.rb +34 -21
  57. data/lib/rails/commands/secret/secret_command.rb +13 -0
  58. data/lib/rails/commands/secrets/USAGE +44 -49
  59. data/lib/rails/commands/secrets/secrets_command.rb +20 -38
  60. data/lib/rails/commands/server/server_command.rb +33 -30
  61. data/lib/rails/commands/test/USAGE +14 -0
  62. data/lib/rails/commands/test/test_command.rb +56 -14
  63. data/lib/rails/commands/unused_routes/unused_routes_command.rb +75 -0
  64. data/lib/rails/commands/version/version_command.rb +1 -0
  65. data/lib/rails/configuration.rb +23 -28
  66. data/lib/rails/console/app.rb +1 -4
  67. data/lib/rails/console/helpers.rb +2 -2
  68. data/lib/rails/deprecator.rb +7 -0
  69. data/lib/rails/engine/configuration.rb +53 -9
  70. data/lib/rails/engine.rb +63 -48
  71. data/lib/rails/gem_version.rb +3 -3
  72. data/lib/rails/generators/actions/create_migration.rb +2 -4
  73. data/lib/rails/generators/actions.rb +231 -73
  74. data/lib/rails/generators/active_model.rb +28 -14
  75. data/lib/rails/generators/app_base.rb +476 -176
  76. data/lib/rails/generators/app_name.rb +3 -14
  77. data/lib/rails/generators/base.rb +24 -20
  78. data/lib/rails/generators/database.rb +39 -1
  79. data/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt +1 -1
  80. data/lib/rails/generators/erb/scaffold/scaffold_generator.rb +2 -0
  81. data/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +8 -8
  82. data/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt +8 -4
  83. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +11 -28
  84. data/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt +7 -3
  85. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +17 -0
  86. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +8 -17
  87. data/lib/rails/generators/erb.rb +1 -1
  88. data/lib/rails/generators/generated_attribute.rb +57 -9
  89. data/lib/rails/generators/migration.rb +2 -7
  90. data/lib/rails/generators/model_helpers.rb +3 -2
  91. data/lib/rails/generators/named_base.rb +13 -13
  92. data/lib/rails/generators/rails/app/USAGE +22 -6
  93. data/lib/rails/generators/rails/app/app_generator.rb +113 -114
  94. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +103 -0
  95. data/lib/rails/generators/rails/app/templates/Gemfile.tt +43 -53
  96. data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt +2 -2
  97. data/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt +2 -2
  98. data/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt +1 -1
  99. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +3 -9
  100. data/lib/rails/generators/rails/app/templates/app/views/layouts/mailer.html.erb.tt +1 -1
  101. data/lib/rails/generators/rails/app/templates/bin/rails.tt +1 -4
  102. data/lib/rails/generators/rails/app/templates/bin/rake.tt +0 -3
  103. data/lib/rails/generators/rails/app/templates/bin/setup.tt +16 -12
  104. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +6 -18
  105. data/lib/rails/generators/rails/app/templates/config/boot.rb.tt +1 -1
  106. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +2 -2
  107. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +3 -3
  108. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +7 -7
  109. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +5 -5
  110. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +3 -3
  111. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +2 -2
  112. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +7 -9
  113. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +4 -4
  114. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +5 -5
  115. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +59 -0
  116. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +16 -12
  117. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +38 -61
  118. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +14 -15
  119. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +3 -5
  120. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +21 -28
  121. data/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt +3 -3
  122. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +3 -1
  123. data/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt +4 -4
  124. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +283 -0
  125. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +11 -9
  126. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +13 -15
  127. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +12 -20
  128. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +8 -1
  129. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +5 -5
  130. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +6 -4
  131. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +10 -0
  132. data/lib/rails/generators/rails/app/templates/dockerignore.tt +43 -0
  133. data/lib/rails/generators/rails/app/templates/gitattributes.tt +0 -5
  134. data/lib/rails/generators/rails/app/templates/gitignore.tt +7 -9
  135. data/lib/rails/generators/rails/app/templates/node-version.tt +1 -0
  136. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +10 -8
  137. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +10 -8
  138. data/lib/rails/generators/rails/application_record/application_record_generator.rb +4 -0
  139. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +2 -1
  140. data/lib/rails/generators/rails/controller/USAGE +12 -4
  141. data/lib/rails/generators/rails/controller/controller_generator.rb +6 -2
  142. data/lib/rails/generators/rails/controller/templates/controller.rb.tt +1 -5
  143. data/lib/rails/generators/rails/credentials/credentials_generator.rb +29 -24
  144. data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +8 -0
  145. data/lib/rails/generators/rails/db/system/change/change_generator.rb +31 -1
  146. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +1 -2
  147. data/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt +1 -1
  148. data/lib/rails/generators/rails/migration/USAGE +21 -11
  149. data/lib/rails/generators/rails/model/model_generator.rb +4 -0
  150. data/lib/rails/generators/rails/plugin/USAGE +17 -6
  151. data/lib/rails/generators/rails/plugin/plugin_generator.rb +69 -30
  152. data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +7 -3
  153. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +9 -31
  154. data/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt +1 -1
  155. data/lib/rails/generators/rails/plugin/templates/README.md.tt +1 -1
  156. data/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt +2 -2
  157. data/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt +0 -3
  158. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +5 -20
  159. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +0 -7
  160. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt +2 -2
  161. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt +1 -1
  162. data/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt +2 -2
  163. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +6 -11
  164. data/lib/rails/generators/rails/resource/resource_generator.rb +6 -0
  165. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +2 -20
  166. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +1 -1
  167. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +1 -5
  168. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +6 -10
  169. data/lib/rails/generators/resource_helpers.rb +2 -2
  170. data/lib/rails/generators/test_case.rb +2 -2
  171. data/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt +1 -1
  172. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +2 -2
  173. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +4 -4
  174. data/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt +5 -5
  175. data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt +7 -7
  176. data/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt +9 -11
  177. data/lib/rails/generators/testing/{behaviour.rb → behavior.rb} +7 -5
  178. data/lib/rails/generators.rb +14 -29
  179. data/lib/rails/health_controller.rb +55 -0
  180. data/lib/rails/info.rb +2 -2
  181. data/lib/rails/info_controller.rb +31 -13
  182. data/lib/rails/initializable.rb +1 -1
  183. data/lib/rails/mailers_controller.rb +17 -9
  184. data/lib/rails/paths.rb +14 -11
  185. data/lib/rails/rack/logger.rb +16 -15
  186. data/lib/rails/rackup/server.rb +15 -0
  187. data/lib/rails/railtie/configuration.rb +15 -3
  188. data/lib/rails/railtie.rb +54 -31
  189. data/lib/rails/ruby_version_check.rb +5 -3
  190. data/lib/rails/secrets.rb +10 -8
  191. data/lib/rails/source_annotation_extractor.rb +68 -19
  192. data/lib/rails/tasks/engine.rake +8 -8
  193. data/lib/rails/tasks/framework.rake +4 -12
  194. data/lib/rails/tasks/log.rake +1 -1
  195. data/lib/rails/tasks/misc.rake +3 -14
  196. data/lib/rails/tasks/statistics.rake +8 -5
  197. data/lib/rails/tasks/tmp.rake +13 -6
  198. data/lib/rails/tasks/yarn.rake +8 -9
  199. data/lib/rails/tasks/zeitwerk.rake +14 -42
  200. data/lib/rails/tasks.rb +0 -2
  201. data/lib/rails/templates/layouts/application.html.erb +15 -0
  202. data/lib/rails/templates/rails/mailers/email.html.erb +45 -11
  203. data/lib/rails/templates/rails/mailers/index.html.erb +14 -7
  204. data/lib/rails/templates/rails/mailers/mailer.html.erb +11 -5
  205. data/lib/rails/templates/rails/welcome/index.html.erb +65 -48
  206. data/lib/rails/test_help.rb +13 -14
  207. data/lib/rails/test_unit/line_filtering.rb +1 -1
  208. data/lib/rails/test_unit/railtie.rb +0 -4
  209. data/lib/rails/test_unit/reporter.rb +6 -2
  210. data/lib/rails/test_unit/runner.rb +45 -20
  211. data/lib/rails/test_unit/test_parser.rb +88 -0
  212. data/lib/rails/test_unit/testing.rake +18 -43
  213. data/lib/rails/testing/maintain_test_schema.rb +16 -0
  214. data/lib/rails/version.rb +1 -1
  215. data/lib/rails/welcome_controller.rb +1 -0
  216. data/lib/rails/zeitwerk_checker.rb +15 -0
  217. data/lib/rails.rb +26 -15
  218. metadata +74 -42
  219. data/RDOC_MAIN.rdoc +0 -97
  220. data/lib/rails/.DS_Store +0 -0
  221. data/lib/rails/application/dummy_erb_compiler.rb +0 -18
  222. data/lib/rails/command/spellchecker.rb +0 -57
  223. data/lib/rails/generators/css/assets/assets_generator.rb +0 -15
  224. data/lib/rails/generators/css/assets/templates/stylesheet.css +0 -4
  225. data/lib/rails/generators/css/scaffold/scaffold_generator.rb +0 -18
  226. data/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js +0 -6
  227. data/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js +0 -5
  228. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +0 -23
  229. data/lib/rails/generators/rails/app/templates/bin/spring.tt +0 -13
  230. data/lib/rails/generators/rails/app/templates/bin/yarn.tt +0 -16
  231. data/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt +0 -8
  232. data/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt +0 -8
  233. data/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt +0 -5
  234. data/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt +0 -4
  235. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_1.rb.tt +0 -67
  236. data/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt +0 -16
  237. data/lib/rails/generators/rails/app/templates/config/spring.rb.tt +0 -6
  238. data/lib/rails/generators/rails/app/templates/package.json.tt +0 -11
  239. data/lib/rails/generators/rails/assets/USAGE +0 -16
  240. data/lib/rails/generators/rails/assets/assets_generator.rb +0 -26
  241. data/lib/rails/generators/rails/assets/templates/stylesheet.css +0 -4
  242. data/lib/rails/generators/rails/model/USAGE +0 -113
  243. data/lib/rails/generators/rails/scaffold/templates/scaffold.css +0 -80
  244. data/lib/rails/tasks/middleware.rake +0 -9
  245. data/lib/rails/tasks/restart.rake +0 -9
@@ -4,6 +4,7 @@ require "fileutils"
4
4
  require "digest/md5"
5
5
  require "rails/version" unless defined?(Rails::VERSION)
6
6
  require "open-uri"
7
+ require "tsort"
7
8
  require "uri"
8
9
  require "rails/generators"
9
10
  require "active_support/core_ext/array/extract_options"
@@ -14,6 +15,9 @@ module Rails
14
15
  include Database
15
16
  include AppName
16
17
 
18
+ NODE_LTS_VERSION = "18.15.0"
19
+ BUN_VERSION = "1.0.1"
20
+
17
21
  attr_accessor :rails_template
18
22
  add_shebang_option!
19
23
 
@@ -24,133 +28,114 @@ module Rails
24
28
  end
25
29
 
26
30
  def self.add_shared_options_for(name)
31
+ class_option :name, type: :string, aliases: "-n",
32
+ desc: "Name of the app"
33
+
27
34
  class_option :template, type: :string, aliases: "-m",
28
35
  desc: "Path to some #{name} template (can be a filesystem path or URL)"
29
36
 
30
37
  class_option :database, type: :string, aliases: "-d", default: "sqlite3",
31
38
  desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
32
39
 
33
- class_option :skip_gemfile, type: :boolean, default: false,
34
- desc: "Don't create a Gemfile"
40
+ class_option :skip_git, type: :boolean, aliases: "-G", default: nil,
41
+ desc: "Skip git init, .gitignore and .gitattributes"
35
42
 
36
- class_option :skip_git, type: :boolean, aliases: "-G", default: false,
37
- desc: "Skip .gitignore file"
43
+ class_option :skip_docker, type: :boolean, default: nil,
44
+ desc: "Skip Dockerfile, .dockerignore and bin/docker-entrypoint"
38
45
 
39
- class_option :skip_keeps, type: :boolean, default: false,
46
+ class_option :skip_keeps, type: :boolean, default: nil,
40
47
  desc: "Skip source control .keep files"
41
48
 
42
49
  class_option :skip_action_mailer, type: :boolean, aliases: "-M",
43
- default: false,
50
+ default: nil,
44
51
  desc: "Skip Action Mailer files"
45
52
 
46
- class_option :skip_action_mailbox, type: :boolean, default: false,
53
+ class_option :skip_action_mailbox, type: :boolean, default: nil,
47
54
  desc: "Skip Action Mailbox gem"
48
55
 
49
- class_option :skip_action_text, type: :boolean, default: false,
56
+ class_option :skip_action_text, type: :boolean, default: nil,
50
57
  desc: "Skip Action Text gem"
51
58
 
52
- class_option :skip_active_record, type: :boolean, aliases: "-O", default: false,
59
+ class_option :skip_active_record, type: :boolean, aliases: "-O", default: nil,
53
60
  desc: "Skip Active Record files"
54
61
 
55
- class_option :skip_active_job, type: :boolean, default: false,
62
+ class_option :skip_active_job, type: :boolean, default: nil,
56
63
  desc: "Skip Active Job"
57
64
 
58
- class_option :skip_active_storage, type: :boolean, default: false,
65
+ class_option :skip_active_storage, type: :boolean, default: nil,
59
66
  desc: "Skip Active Storage files"
60
67
 
61
- class_option :skip_puma, type: :boolean, aliases: "-P", default: false,
62
- desc: "Skip Puma related files"
63
-
64
- class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false,
68
+ class_option :skip_action_cable, type: :boolean, aliases: "-C", default: nil,
65
69
  desc: "Skip Action Cable files"
66
70
 
67
- class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false,
68
- desc: "Skip Sprockets files"
71
+ class_option :skip_asset_pipeline, type: :boolean, aliases: "-A", default: nil
69
72
 
70
- class_option :skip_spring, type: :boolean, default: false,
71
- desc: "Don't install Spring application preloader"
73
+ class_option :asset_pipeline, type: :string, aliases: "-a", default: "sprockets",
74
+ desc: "Choose your asset pipeline [options: sprockets (default), propshaft]"
72
75
 
73
- class_option :skip_listen, type: :boolean, default: false,
74
- desc: "Don't generate configuration that depends on the listen gem"
75
-
76
- class_option :skip_javascript, type: :boolean, aliases: "-J", default: name == "plugin",
76
+ class_option :skip_javascript, type: :boolean, aliases: ["-J", "--skip-js"], default: (true if name == "plugin"),
77
77
  desc: "Skip JavaScript files"
78
78
 
79
- class_option :skip_turbolinks, type: :boolean, default: false,
80
- desc: "Skip turbolinks gem"
79
+ class_option :skip_hotwire, type: :boolean, default: nil,
80
+ desc: "Skip Hotwire integration"
81
81
 
82
- class_option :skip_jbuilder, type: :boolean, default: false,
82
+ class_option :skip_jbuilder, type: :boolean, default: nil,
83
83
  desc: "Skip jbuilder gem"
84
84
 
85
- class_option :skip_test, type: :boolean, aliases: "-T", default: false,
85
+ class_option :skip_test, type: :boolean, aliases: "-T", default: nil,
86
86
  desc: "Skip test files"
87
87
 
88
- class_option :skip_system_test, type: :boolean, default: false,
88
+ class_option :skip_system_test, type: :boolean, default: nil,
89
89
  desc: "Skip system test files"
90
90
 
91
- class_option :skip_bootsnap, type: :boolean, default: false,
91
+ class_option :skip_bootsnap, type: :boolean, default: nil,
92
92
  desc: "Skip bootsnap gem"
93
93
 
94
- class_option :dev, type: :boolean, default: false,
94
+ class_option :skip_dev_gems, type: :boolean, default: nil,
95
+ desc: "Skip development gems (e.g., web-console)"
96
+
97
+ class_option :dev, type: :boolean, default: nil,
95
98
  desc: "Set up the #{name} with Gemfile pointing to your Rails checkout"
96
99
 
97
- class_option :edge, type: :boolean, default: false,
98
- desc: "Set up the #{name} with Gemfile pointing to Rails repository"
100
+ class_option :edge, type: :boolean, default: nil,
101
+ desc: "Set up the #{name} with a Gemfile pointing to the #{edge_branch} branch on the Rails repository"
99
102
 
100
- class_option :master, type: :boolean, default: false,
103
+ class_option :main, type: :boolean, default: nil, aliases: "--master",
101
104
  desc: "Set up the #{name} with Gemfile pointing to Rails repository main branch"
102
105
 
103
106
  class_option :rc, type: :string, default: nil,
104
107
  desc: "Path to file containing extra configuration options for rails command"
105
108
 
106
- class_option :no_rc, type: :boolean, default: false,
109
+ class_option :no_rc, type: :boolean, default: nil,
107
110
  desc: "Skip loading of extra configuration options from .railsrc file"
108
111
 
109
112
  class_option :help, type: :boolean, aliases: "-h", group: :rails,
110
113
  desc: "Show this help message and quit"
111
114
  end
112
115
 
116
+ def self.edge_branch # :nodoc:
117
+ Rails.gem_version.prerelease? ? "main" : [*Rails.gem_version.segments.first(2), "stable"].join("-")
118
+ end
119
+
113
120
  def initialize(positional_argv, option_argv, *)
114
121
  @argv = [*positional_argv, *option_argv]
115
122
  @gem_filter = lambda { |gem| true }
116
- @extra_entries = []
117
123
  super
118
124
  end
119
125
 
120
126
  private
121
- def gemfile_entry(name, *args) # :doc:
122
- options = args.extract_options!
123
- version = args.first
124
- github = options[:github]
125
- path = options[:path]
126
-
127
- if github
128
- @extra_entries << GemfileEntry.github(name, github)
129
- elsif path
130
- @extra_entries << GemfileEntry.path(name, path)
131
- else
132
- @extra_entries << GemfileEntry.version(name, version)
133
- end
134
- self
135
- end
136
-
137
127
  def gemfile_entries # :doc:
138
- [rails_gemfile_entry,
139
- database_gemfile_entry,
140
- web_server_gemfile_entry,
141
- assets_gemfile_entry,
142
- webpacker_gemfile_entry,
143
- javascript_gemfile_entry,
144
- jbuilder_gemfile_entry,
145
- psych_gemfile_entry,
146
- cable_gemfile_entry,
147
- @extra_entries].flatten.find_all(&@gem_filter)
148
- end
149
-
150
- def add_gem_entry_filter # :doc:
151
- @gem_filter = lambda { |next_filter, entry|
152
- yield(entry) && next_filter.call(entry)
153
- }.curry[@gem_filter]
128
+ [
129
+ rails_gemfile_entry,
130
+ asset_pipeline_gemfile_entry,
131
+ database_gemfile_entry,
132
+ web_server_gemfile_entry,
133
+ javascript_gemfile_entry,
134
+ hotwire_gemfile_entry,
135
+ css_gemfile_entry,
136
+ jbuilder_gemfile_entry,
137
+ cable_gemfile_entry,
138
+ ].flatten.compact.select(&@gem_filter)
154
139
  end
155
140
 
156
141
  def builder # :doc:
@@ -165,6 +150,89 @@ module Rails
165
150
  builder.public_send(meth, *args) if builder.respond_to?(meth)
166
151
  end
167
152
 
153
+ def deduce_implied_options(options, option_reasons, meta_options)
154
+ active = options.transform_values { |value| [] if value }.compact
155
+ irrevocable = (active.keys - meta_options).to_set
156
+
157
+ deduction_order = TSort.tsort(
158
+ ->(&block) { option_reasons.each_key(&block) },
159
+ ->(key, &block) { option_reasons[key]&.each(&block) }
160
+ )
161
+
162
+ deduction_order.each do |name|
163
+ active_reasons = option_reasons[name].to_a.select(&active)
164
+ active[name] ||= active_reasons if active_reasons.any?
165
+ irrevocable << name if active_reasons.any?(irrevocable)
166
+ end
167
+
168
+ revoked = options.select { |name, value| value == false }.keys.to_set - irrevocable
169
+ deduction_order.reverse_each do |name|
170
+ revoked += option_reasons[name].to_a if revoked.include?(name)
171
+ end
172
+ revoked -= meta_options
173
+
174
+ active.filter_map do |name, reasons|
175
+ unless revoked.include?(name) || reasons.all?(revoked)
176
+ [name, reasons - revoked.to_a]
177
+ end
178
+ end.to_h
179
+ end
180
+
181
+ OPTION_IMPLICATIONS = { # :nodoc:
182
+ skip_active_job: [:skip_action_mailer, :skip_active_storage],
183
+ skip_active_record: [:skip_active_storage],
184
+ skip_active_storage: [:skip_action_mailbox, :skip_action_text],
185
+ skip_javascript: [:skip_hotwire],
186
+ }
187
+
188
+ # ==== Options
189
+ #
190
+ # [+:meta_options+]
191
+ # A list of generator options which only serve to trigger other options.
192
+ # These options should have no other effects, and will be treated
193
+ # transparently when revoking other options.
194
+ #
195
+ # For example: --minimal implies both --skip-active-job and
196
+ # --skip-active-storage. Also, --skip-active-job by itself implies
197
+ # --skip-active-storage. If --skip-active-job is explicitly
198
+ # specified, --no-skip-active-storage should raise an error. But, if
199
+ # only --minimal is specified, --no-skip-active-storage should "undo"
200
+ # the implied --skip-active-job. This can be accomplished by passing
201
+ # <tt>meta_options: [:minimal]</tt>.
202
+ #
203
+ # In contrast, --api is not a meta option because it does other things
204
+ # besides implying options such as --skip-asset-pipeline. (And so --api
205
+ # with --no-skip-asset-pipeline should raise an error.)
206
+ def imply_options(option_implications = OPTION_IMPLICATIONS, meta_options: [])
207
+ option_reasons = {}
208
+ option_implications.each do |reason, implications|
209
+ implications.each do |implication|
210
+ (option_reasons[implication.to_s] ||= []) << reason.to_s
211
+ end
212
+ end
213
+
214
+ @implied_options = deduce_implied_options(options, option_reasons, meta_options.map(&:to_s))
215
+ @implied_options_conflicts = @implied_options.keys.select { |name| options[name] == false }
216
+ self.options = options.merge(@implied_options.transform_values { true }).freeze
217
+ end
218
+
219
+ def report_implied_options
220
+ return if @implied_options.blank?
221
+
222
+ say "Based on the specified options, the following options will also be activated:"
223
+ say ""
224
+ @implied_options.each do |name, reasons|
225
+ due_to = reasons.map { |reason| "--#{reason.dasherize}" }.join(", ")
226
+ say " --#{name.dasherize} [due to #{due_to}]"
227
+ if @implied_options_conflicts.include?(name)
228
+ say " ERROR: Conflicts with --no-#{name.dasherize}", :red
229
+ end
230
+ end
231
+ say ""
232
+
233
+ raise "Cannot proceed due to conflicting options" if @implied_options_conflicts.any?
234
+ end
235
+
168
236
  def create_root # :doc:
169
237
  valid_const?
170
238
 
@@ -173,56 +241,79 @@ module Rails
173
241
  end
174
242
 
175
243
  def apply_rails_template # :doc:
176
- original_argv = ARGV.dup
177
- ARGV.replace(@argv)
178
-
179
244
  apply rails_template if rails_template
180
245
  rescue Thor::Error, LoadError, Errno::ENOENT => e
181
246
  raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}"
182
- ensure
183
- ARGV.replace(original_argv)
184
247
  end
185
248
 
186
249
  def set_default_accessors! # :doc:
187
250
  self.destination_root = File.expand_path(app_path, destination_root)
188
- self.rails_template = \
189
- case options[:template]
190
- when /^https?:\/\//
191
- options[:template]
192
- when String
193
- File.expand_path(options[:template], Dir.pwd)
194
- else
195
- options[:template]
196
- end
251
+
252
+ if options[:template].is_a?(String) && !options[:template].match?(/^https?:\/\//)
253
+ interpolated = options[:template].gsub(/\$(\w+)|\$\{\g<1>\}|%\g<1>%/) { |m| ENV[$1] || m }
254
+ self.rails_template = File.expand_path(interpolated)
255
+ else
256
+ self.rails_template = options[:template]
257
+ end
197
258
  end
198
259
 
199
260
  def database_gemfile_entry # :doc:
200
- return [] if options[:skip_active_record]
261
+ return if options[:skip_active_record]
262
+
201
263
  gem_name, gem_version = gem_for_database
202
264
  GemfileEntry.version gem_name, gem_version,
203
- "Use #{options[:database]} as the database for Active Record"
265
+ "Use #{options[:database]} as the database for Active Record"
204
266
  end
205
267
 
206
268
  def web_server_gemfile_entry # :doc:
207
- return [] if options[:skip_puma]
208
- comment = "Use Puma as the app server"
209
- GemfileEntry.new("puma", "~> 5.0", comment)
269
+ GemfileEntry.new "puma", ">= 5.0", "Use the Puma web server [https://github.com/puma/puma]"
270
+ end
271
+
272
+ def asset_pipeline_gemfile_entry
273
+ return if skip_asset_pipeline?
274
+
275
+ if options[:asset_pipeline] == "sprockets"
276
+ GemfileEntry.floats "sprockets-rails",
277
+ "The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]"
278
+ elsif options[:asset_pipeline] == "propshaft"
279
+ GemfileEntry.floats "propshaft", "The modern asset pipeline for Rails [https://github.com/rails/propshaft]"
280
+ end
281
+ end
282
+
283
+ def required_railties
284
+ @required_railties ||= {
285
+ "active_model/railtie" => true,
286
+ "active_job/railtie" => !options[:skip_active_job],
287
+ "active_record/railtie" => !options[:skip_active_record],
288
+ "active_storage/engine" => !options[:skip_active_storage],
289
+ "action_controller/railtie" => true,
290
+ "action_mailer/railtie" => !options[:skip_action_mailer],
291
+ "action_mailbox/engine" => !options[:skip_action_mailbox],
292
+ "action_text/engine" => !options[:skip_action_text],
293
+ "action_view/railtie" => true,
294
+ "action_cable/engine" => !options[:skip_action_cable],
295
+ "rails/test_unit/railtie" => !options[:skip_test],
296
+ }
210
297
  end
211
298
 
212
299
  def include_all_railties? # :doc:
213
- [
214
- options.values_at(
215
- :skip_active_record,
216
- :skip_action_mailer,
217
- :skip_test,
218
- :skip_sprockets,
219
- :skip_action_cable,
220
- :skip_active_job
221
- ),
222
- skip_active_storage?,
223
- skip_action_mailbox?,
224
- skip_action_text?
225
- ].flatten.none?
300
+ required_railties.values.all?
301
+ end
302
+
303
+ def rails_require_statement
304
+ if include_all_railties?
305
+ %(require "rails/all")
306
+ else
307
+ require_statements = required_railties.map do |railtie, required|
308
+ %(#{"# " if !required}require "#{railtie}")
309
+ end
310
+
311
+ <<~RUBY.strip
312
+ require "rails"
313
+ # Pick the frameworks you want:
314
+ #{require_statements.join("\n")}
315
+ RUBY
316
+ end
226
317
  end
227
318
 
228
319
  def comment_if(value) # :doc:
@@ -243,25 +334,46 @@ module Rails
243
334
  end
244
335
 
245
336
  def sqlite3? # :doc:
246
- !options[:skip_active_record] && options[:database] == "sqlite3"
337
+ !skip_active_record? && options[:database] == "sqlite3"
338
+ end
339
+
340
+ def skip_active_record? # :doc:
341
+ options[:skip_active_record]
247
342
  end
248
343
 
249
344
  def skip_active_storage? # :doc:
250
- options[:skip_active_storage] || options[:skip_active_record]
345
+ options[:skip_active_storage]
346
+ end
347
+
348
+ def skip_action_cable? # :doc:
349
+ options[:skip_action_cable]
350
+ end
351
+
352
+ def skip_action_mailer? # :doc:
353
+ options[:skip_action_mailer]
251
354
  end
252
355
 
253
356
  def skip_action_mailbox? # :doc:
254
- options[:skip_action_mailbox] || skip_active_storage?
357
+ options[:skip_action_mailbox]
255
358
  end
256
359
 
257
360
  def skip_action_text? # :doc:
258
- options[:skip_action_text] || skip_active_storage?
361
+ options[:skip_action_text]
362
+ end
363
+
364
+ def skip_asset_pipeline? # :doc:
365
+ options[:skip_asset_pipeline]
366
+ end
367
+
368
+ def skip_sprockets?
369
+ skip_asset_pipeline? || options[:asset_pipeline] != "sprockets"
259
370
  end
260
371
 
261
- def skip_dev_gems? # :doc:
262
- options[:skip_dev_gems]
372
+ def skip_propshaft?
373
+ skip_asset_pipeline? || options[:asset_pipeline] != "propshaft"
263
374
  end
264
375
 
376
+
265
377
  class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
266
378
  def initialize(name, version, comment, options = {}, commented_out = false)
267
379
  super
@@ -279,38 +391,43 @@ module Rails
279
391
  new(name, version, comment)
280
392
  end
281
393
 
394
+ def self.floats(name, comment = nil)
395
+ new(name, nil, comment)
396
+ end
397
+
282
398
  def self.path(name, path, comment = nil)
283
399
  new(name, nil, comment, path: path)
284
400
  end
285
401
 
286
- def version
287
- version = super
288
-
289
- if version.is_a?(Array)
290
- version.join("', '")
291
- else
292
- version
293
- end
402
+ def to_s
403
+ [
404
+ (comment.gsub(/^/, "# ").chomp + "\n" if comment),
405
+ ("# " if commented_out),
406
+ "gem \"#{name}\"",
407
+ *Array(version).map { |constraint| ", \"#{constraint}\"" },
408
+ *options.map { |key, value| ", #{key}: #{value.inspect}" },
409
+ ].compact.join
294
410
  end
295
411
  end
296
412
 
413
+ def gem_ruby_version
414
+ Gem::Version.new(Gem::VERSION) >= Gem::Version.new("3.3.13") ? Gem.ruby_version : RUBY_VERSION
415
+ end
416
+
417
+ def rails_prerelease?
418
+ options.dev? || options.edge? || options.main?
419
+ end
420
+
297
421
  def rails_gemfile_entry
298
422
  if options.dev?
299
- [
300
- GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH)
301
- ]
423
+ GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH, "Use local checkout of Rails")
302
424
  elsif options.edge?
303
- [
304
- GemfileEntry.github("rails", "rails/rails", "6-1-stable")
305
- ]
306
- elsif options.master?
307
- [
308
- GemfileEntry.github("rails", "rails/rails", "main")
309
- ]
425
+ GemfileEntry.github("rails", "rails/rails", edge_branch, "Use specific branch of Rails")
426
+ elsif options.main?
427
+ GemfileEntry.github("rails", "rails/rails", "main", "Use main development branch of Rails")
310
428
  else
311
- [GemfileEntry.version("rails",
312
- rails_version_specifier,
313
- "Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'")]
429
+ GemfileEntry.version("rails", rails_version_specifier,
430
+ %(Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"))
314
431
  end
315
432
  end
316
433
 
@@ -327,47 +444,168 @@ module Rails
327
444
  end
328
445
  end
329
446
 
330
- def assets_gemfile_entry
331
- return [] if options[:skip_sprockets]
447
+ def jbuilder_gemfile_entry
448
+ return if options[:skip_jbuilder]
449
+ GemfileEntry.new "jbuilder", nil, "Build JSON APIs with ease [https://github.com/rails/jbuilder]", {}, options[:api]
450
+ end
451
+
452
+ def javascript_gemfile_entry
453
+ return if options[:skip_javascript]
332
454
 
333
- GemfileEntry.version("sass-rails", ">= 6", "Use SCSS for stylesheets")
455
+ if options[:javascript] == "importmap"
456
+ GemfileEntry.floats "importmap-rails", "Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]"
457
+ else
458
+ GemfileEntry.floats "jsbundling-rails", "Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]"
459
+ end
334
460
  end
335
461
 
336
- def webpacker_gemfile_entry
337
- return [] if options[:skip_javascript]
462
+ def hotwire_gemfile_entry
463
+ return if options[:skip_hotwire]
338
464
 
339
- GemfileEntry.version "webpacker", "~> 5.0", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
465
+ turbo_rails_entry =
466
+ GemfileEntry.floats "turbo-rails", "Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]"
467
+
468
+ stimulus_rails_entry =
469
+ GemfileEntry.floats "stimulus-rails", "Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]"
470
+
471
+ [ turbo_rails_entry, stimulus_rails_entry ]
340
472
  end
341
473
 
342
- def jbuilder_gemfile_entry
343
- return [] if options[:skip_jbuilder]
344
- comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
345
- GemfileEntry.new "jbuilder", "~> 2.7", comment, {}, options[:api]
474
+ def using_js_runtime?
475
+ (options[:javascript] && !%w[importmap].include?(options[:javascript])) ||
476
+ (options[:css] && !%w[tailwind sass].include?(options[:css]))
346
477
  end
347
478
 
348
- def javascript_gemfile_entry
349
- if options[:skip_javascript] || options[:skip_turbolinks]
350
- []
351
- else
352
- [ GemfileEntry.version("turbolinks", "~> 5",
353
- "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") ]
479
+ def using_node?
480
+ using_js_runtime? && !%w[bun].include?(options[:javascript])
481
+ end
482
+
483
+ def using_bun?
484
+ using_js_runtime? && %w[bun].include?(options[:javascript])
485
+ end
486
+
487
+ def node_version
488
+ if using_node?
489
+ ENV.fetch("NODE_VERSION") do
490
+ `node --version`[/\d+\.\d+\.\d+/]
491
+ rescue
492
+ NODE_LTS_VERSION
493
+ end
354
494
  end
355
495
  end
356
496
 
357
- def psych_gemfile_entry
358
- return [] unless defined?(Rubinius)
497
+ def dockerfile_yarn_version
498
+ using_node? and `yarn --version`[/\d+\.\d+\.\d+/]
499
+ rescue
500
+ "latest"
501
+ end
359
502
 
360
- comment = "Use Psych as the YAML engine, instead of Syck, so serialized " \
361
- "data can be read safely from different rubies (see http://git.io/uuLVag)"
362
- GemfileEntry.new("psych", "~> 2.0", comment, platforms: :rbx)
503
+ def dockerfile_bun_version
504
+ using_bun? and `bun --version`[/\d+\.\d+\.\d+/]
505
+ rescue
506
+ BUN_VERSION
507
+ end
508
+
509
+ def dockerfile_binfile_fixups
510
+ # binfiles may have OS specific paths to ruby. Normalize them.
511
+ shebangs = Dir["bin/*"].map { |file| IO.read(file).lines.first }.join
512
+ rubies = shebangs.scan(%r{#!/usr/bin/env (ruby.*)}).flatten.uniq
513
+
514
+ binfixups = (rubies - %w(ruby)).map do |ruby|
515
+ "sed -i 's/#{Regexp.quote(ruby)}$/ruby/' bin/*"
516
+ end
517
+
518
+ # Windows line endings will cause scripts to fail. If any
519
+ # or found OR this generation is run on a windows platform
520
+ # and there are other binfixups required, then convert
521
+ # line endings. This avoids adding unnecessary fixups if
522
+ # none are required, but prepares for the need to do the
523
+ # fix line endings if other fixups are required.
524
+ has_cr = Dir["bin/*"].any? { |file| IO.read(file).include? "\r" }
525
+ if has_cr || (Gem.win_platform? && !binfixups.empty?)
526
+ binfixups.unshift 'sed -i "s/\r$//g" bin/*'
527
+ end
528
+
529
+ # Windows file systems may not have the concept of executable.
530
+ # In such cases, fix up during the build.
531
+ unless Dir["bin/*"].all? { |file| File.executable? file }
532
+ binfixups.unshift "chmod +x bin/*"
533
+ end
534
+
535
+ binfixups
536
+ end
537
+
538
+ def dockerfile_build_packages
539
+ # start with the essentials
540
+ packages = %w(build-essential git pkg-config)
541
+
542
+ # add database support
543
+ packages << build_package_for_database unless skip_active_record?
544
+
545
+ # ActiveStorage preview support
546
+ packages << "libvips" unless skip_active_storage?
547
+
548
+ packages << "curl" if using_js_runtime?
549
+
550
+ packages << "unzip" if using_bun?
551
+
552
+ # node support, including support for building native modules
553
+ if using_node?
554
+ packages << "node-gyp" # pkg-config already listed above
555
+
556
+ # module build process depends on Python, and debian changed
557
+ # how python is installed with the bullseye release. Below
558
+ # is based on debian release included with the Ruby images on
559
+ # Dockerhub.
560
+ case Gem.ruby_version.to_s
561
+ when /^2\.7/
562
+ bullseye = Gem.ruby_version >= Gem::Version.new("2.7.4")
563
+ when /^3\.0/
564
+ bullseye = Gem.ruby_version >= Gem::Version.new("3.0.2")
565
+ else
566
+ bullseye = true
567
+ end
568
+
569
+ if bullseye
570
+ packages << "python-is-python3"
571
+ else
572
+ packages << "python"
573
+ end
574
+ end
575
+
576
+ packages.compact.sort
577
+ end
578
+
579
+ def dockerfile_deploy_packages
580
+ # Add curl to work with the default healthcheck strategy in Kamal
581
+ packages = ["curl"]
582
+
583
+ # ActiveRecord databases
584
+ packages << deploy_package_for_database unless skip_active_record?
585
+
586
+ # ActiveStorage preview support
587
+ packages << "libvips" unless skip_active_storage?
588
+
589
+ packages.compact.sort
590
+ end
591
+
592
+ def css_gemfile_entry
593
+ return unless options[:css]
594
+
595
+ if !using_js_runtime? && options[:css] == "tailwind"
596
+ GemfileEntry.floats "tailwindcss-rails", "Use Tailwind CSS [https://github.com/rails/tailwindcss-rails]"
597
+ elsif !using_js_runtime? && options[:css] == "sass"
598
+ GemfileEntry.floats "dartsass-rails", "Use Dart SASS [https://github.com/rails/dartsass-rails]"
599
+ else
600
+ GemfileEntry.floats "cssbundling-rails", "Bundle and process CSS [https://github.com/rails/cssbundling-rails]"
601
+ end
363
602
  end
364
603
 
365
604
  def cable_gemfile_entry
366
- return [] if options[:skip_action_cable]
605
+ return if options[:skip_action_cable]
606
+
367
607
  comment = "Use Redis adapter to run Action Cable in production"
368
- gems = []
369
- gems << GemfileEntry.new("redis", "~> 4.0", comment, {}, true)
370
- gems
608
+ GemfileEntry.new("redis", ">= 4.0.1", comment, {}, true)
371
609
  end
372
610
 
373
611
  def bundle_command(command, env = {})
@@ -397,51 +635,84 @@ module Rails
397
635
  end
398
636
 
399
637
  def bundle_install?
400
- !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
638
+ !(options[:skip_bundle] || options[:pretend])
401
639
  end
402
640
 
403
- def spring_install?
404
- !options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
405
- end
406
-
407
- def webpack_install?
408
- !(options[:skip_javascript] || options[:skip_webpack_install])
641
+ def bundler_windows_platforms
642
+ Gem.rubygems_version >= Gem::Version.new("3.3.22") ? "windows" : "mswin mswin64 mingw x64_mingw"
409
643
  end
410
644
 
411
645
  def depends_on_system_test?
412
646
  !(options[:skip_system_test] || options[:skip_test] || options[:api])
413
647
  end
414
648
 
415
- def depend_on_listen?
416
- !options[:skip_listen] && os_supports_listen_out_of_the_box?
417
- end
418
-
419
649
  def depend_on_bootsnap?
420
650
  !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION)
421
651
  end
422
652
 
423
- def os_supports_listen_out_of_the_box?
424
- /darwin|linux/.match?(RbConfig::CONFIG["host_os"])
653
+ def target_rails_prerelease(self_command = "new")
654
+ return unless rails_prerelease? && bundle_install?
655
+
656
+ if !File.exist?(File.expand_path("Gemfile", destination_root))
657
+ create_file("Gemfile", <<~GEMFILE)
658
+ source "https://rubygems.org"
659
+ git_source(:github) { |repo| "https://github.com/\#{repo}.git" }
660
+ #{rails_gemfile_entry}
661
+ GEMFILE
662
+
663
+ run_bundle
664
+
665
+ @argv.delete_at(@argv.index(app_path))
666
+ @argv.unshift(destination_root)
667
+ require "shellwords"
668
+ bundle_command("exec rails #{self_command} #{Shellwords.join(@argv)}")
669
+ exit
670
+ else
671
+ remove_file("Gemfile")
672
+ remove_file("Gemfile.lock")
673
+ end
425
674
  end
426
675
 
427
676
  def run_bundle
428
- bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1") if bundle_install?
677
+ if bundle_install?
678
+ bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1")
679
+
680
+ # The vast majority of Rails apps will be deployed on `x86_64-linux`.
681
+ platforms = ["--add-platform=x86_64-linux"]
682
+
683
+ # Users that develop on M1 mac may use docker and would need `aarch64-linux` as well.
684
+ platforms << "--add-platform=aarch64-linux" if RUBY_PLATFORM.start_with?("arm64")
685
+
686
+ platforms.each do |platform|
687
+ bundle_command("lock #{platform}", "BUNDLE_IGNORE_MESSAGES" => "1")
688
+ end
689
+ end
429
690
  end
430
691
 
431
- def run_webpack
432
- return unless webpack_install?
692
+ def run_javascript
693
+ return if options[:skip_javascript] || !bundle_install?
433
694
 
434
- unless bundle_install?
435
- say <<~EXPLAIN
436
- Skipping `rails webpacker:install` because `bundle install` was skipped.
437
- To complete setup, you must run `bundle install` followed by `rails webpacker:install`.
438
- EXPLAIN
439
- return
695
+ case options[:javascript]
696
+ when "importmap" then rails_command "importmap:install"
697
+ when "webpack", "bun", "esbuild", "rollup" then rails_command "javascript:install:#{options[:javascript]}"
440
698
  end
699
+ end
700
+
701
+ def run_hotwire
702
+ return if options[:skip_hotwire] || !bundle_install?
703
+
704
+ rails_command "turbo:install stimulus:install"
705
+ end
706
+
707
+ def run_css
708
+ return if !options[:css] || !bundle_install?
441
709
 
442
- rails_command "webpacker:install"
443
- if options[:webpack] && options[:webpack] != "webpack"
444
- rails_command "webpacker:install:#{options[:webpack]}"
710
+ if !using_js_runtime? && options[:css] == "tailwind"
711
+ rails_command "tailwindcss:install"
712
+ elsif !using_js_runtime? && options[:css] == "sass"
713
+ rails_command "dartsass:install"
714
+ else
715
+ rails_command "css:install:#{options[:css]}"
445
716
  end
446
717
  end
447
718
 
@@ -459,6 +730,35 @@ module Rails
459
730
  def keep_file(destination)
460
731
  create_file("#{destination}/.keep") if keeps?
461
732
  end
733
+
734
+ def user_default_branch
735
+ @user_default_branch ||= `git config init.defaultbranch`
736
+ end
737
+
738
+ def git_init_command
739
+ return "git init" if user_default_branch.strip.present?
740
+
741
+ git_version = `git --version`[/\d+\.\d+\.\d+/]
742
+
743
+ if Gem::Version.new(git_version) >= Gem::Version.new("2.28.0")
744
+ "git init -b main"
745
+ else
746
+ "git init && git symbolic-ref HEAD refs/heads/main"
747
+ end
748
+ end
749
+
750
+ def edge_branch
751
+ self.class.edge_branch
752
+ end
753
+
754
+ def dockerfile_chown_directories
755
+ directories = %w(log tmp)
756
+
757
+ directories << "storage" unless skip_active_storage? && !sqlite3?
758
+ directories << "db" unless skip_active_record?
759
+
760
+ directories.sort
761
+ end
462
762
  end
463
763
  end
464
764
  end