railties 6.1.7.8 → 7.0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +219 -320
  3. data/RDOC_MAIN.rdoc +16 -16
  4. data/README.rdoc +1 -2
  5. data/lib/minitest/rails_plugin.rb +1 -1
  6. data/lib/rails/all.rb +0 -1
  7. data/lib/rails/app_updater.rb +9 -6
  8. data/lib/rails/application/bootstrap.rb +24 -5
  9. data/lib/rails/application/configuration.rb +95 -33
  10. data/lib/rails/application/default_middleware_stack.rb +22 -3
  11. data/lib/rails/application/finisher.rb +53 -95
  12. data/lib/rails/application/routes_reloader.rb +8 -0
  13. data/lib/rails/application.rb +74 -101
  14. data/lib/rails/application_controller.rb +3 -3
  15. data/lib/rails/autoloaders/inflector.rb +21 -0
  16. data/lib/rails/autoloaders.rb +42 -42
  17. data/lib/rails/code_statistics.rb +2 -2
  18. data/lib/rails/code_statistics_calculator.rb +10 -1
  19. data/lib/rails/command/base.rb +26 -12
  20. data/lib/rails/command/behavior.rb +1 -1
  21. data/lib/rails/command/environment_argument.rb +1 -1
  22. data/lib/rails/command.rb +9 -6
  23. data/lib/rails/commands/console/console_command.rb +4 -0
  24. data/lib/rails/commands/credentials/USAGE +4 -2
  25. data/lib/rails/commands/credentials/credentials_command/diffing.rb +26 -16
  26. data/lib/rails/commands/credentials/credentials_command.rb +7 -3
  27. data/lib/rails/commands/dbconsole/dbconsole_command.rb +10 -11
  28. data/lib/rails/commands/help/USAGE +3 -2
  29. data/lib/rails/commands/runner/runner_command.rb +3 -2
  30. data/lib/rails/commands/server/server_command.rb +7 -5
  31. data/lib/rails/configuration.rb +18 -23
  32. data/lib/rails/console/helpers.rb +2 -2
  33. data/lib/rails/engine/configuration.rb +3 -3
  34. data/lib/rails/engine.rb +31 -37
  35. data/lib/rails/gem_version.rb +5 -5
  36. data/lib/rails/generators/actions/create_migration.rb +2 -4
  37. data/lib/rails/generators/actions.rb +229 -62
  38. data/lib/rails/generators/app_base.rb +155 -130
  39. data/lib/rails/generators/app_name.rb +1 -1
  40. data/lib/rails/generators/base.rb +12 -16
  41. data/lib/rails/generators/erb/scaffold/scaffold_generator.rb +2 -0
  42. data/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +8 -8
  43. data/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt +8 -4
  44. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +11 -28
  45. data/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt +7 -3
  46. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +17 -0
  47. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +8 -17
  48. data/lib/rails/generators/erb.rb +1 -1
  49. data/lib/rails/generators/generated_attribute.rb +45 -9
  50. data/lib/rails/generators/migration.rb +2 -6
  51. data/lib/rails/generators/model_helpers.rb +1 -1
  52. data/lib/rails/generators/named_base.rb +13 -13
  53. data/lib/rails/generators/rails/app/app_generator.rb +70 -91
  54. data/lib/rails/generators/rails/app/templates/Gemfile.tt +44 -52
  55. data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt +2 -2
  56. data/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt +2 -2
  57. data/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt +1 -1
  58. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +3 -9
  59. data/lib/rails/generators/rails/app/templates/bin/rails.tt +1 -4
  60. data/lib/rails/generators/rails/app/templates/bin/rake.tt +0 -3
  61. data/lib/rails/generators/rails/app/templates/bin/setup.tt +9 -14
  62. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +0 -1
  63. data/lib/rails/generators/rails/app/templates/config/boot.rb.tt +1 -1
  64. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +2 -2
  65. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +3 -3
  66. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +3 -3
  67. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +2 -2
  68. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +3 -3
  69. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +2 -2
  70. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +3 -3
  71. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +1 -1
  72. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +5 -5
  73. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +7 -13
  74. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +12 -39
  75. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +7 -12
  76. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +1 -5
  77. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +21 -28
  78. data/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt +2 -2
  79. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +3 -1
  80. data/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt +4 -4
  81. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_0.rb.tt +143 -0
  82. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +3 -3
  83. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +1 -1
  84. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +4 -1
  85. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +5 -5
  86. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +2 -2
  87. data/lib/rails/generators/rails/app/templates/gitattributes.tt +0 -5
  88. data/lib/rails/generators/rails/app/templates/gitignore.tt +3 -1
  89. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +1 -1
  90. data/lib/rails/generators/rails/controller/controller_generator.rb +1 -2
  91. data/lib/rails/generators/rails/controller/templates/controller.rb.tt +0 -4
  92. data/lib/rails/generators/rails/db/system/change/change_generator.rb +1 -1
  93. data/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt +1 -1
  94. data/lib/rails/generators/rails/plugin/plugin_generator.rb +76 -27
  95. data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +7 -3
  96. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +9 -31
  97. data/lib/rails/generators/rails/plugin/templates/README.md.tt +1 -1
  98. data/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt +2 -2
  99. data/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt +0 -3
  100. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +7 -6
  101. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +0 -5
  102. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt +2 -2
  103. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt +1 -1
  104. data/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt +2 -2
  105. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +2 -7
  106. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +0 -19
  107. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +1 -5
  108. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +6 -10
  109. data/lib/rails/generators/resource_helpers.rb +2 -2
  110. data/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt +1 -1
  111. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +2 -2
  112. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +3 -3
  113. data/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt +5 -5
  114. data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt +7 -7
  115. data/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt +9 -11
  116. data/lib/rails/generators/testing/behaviour.rb +3 -4
  117. data/lib/rails/generators.rb +17 -24
  118. data/lib/rails/info.rb +1 -1
  119. data/lib/rails/info_controller.rb +1 -3
  120. data/lib/rails/initializable.rb +1 -1
  121. data/lib/rails/mailers_controller.rb +2 -4
  122. data/lib/rails/paths.rb +1 -1
  123. data/lib/rails/rack/logger.rb +7 -9
  124. data/lib/rails/railtie/configuration.rb +1 -2
  125. data/lib/rails/railtie.rb +36 -13
  126. data/lib/rails/ruby_version_check.rb +3 -3
  127. data/lib/rails/source_annotation_extractor.rb +1 -1
  128. data/lib/rails/tasks/framework.rake +7 -9
  129. data/lib/rails/tasks/statistics.rake +3 -1
  130. data/lib/rails/tasks/tmp.rake +8 -1
  131. data/lib/rails/tasks/yarn.rake +8 -9
  132. data/lib/rails/tasks/zeitwerk.rake +2 -10
  133. data/lib/rails/templates/layouts/application.html.erb +15 -0
  134. data/lib/rails/templates/rails/mailers/email.html.erb +13 -11
  135. data/lib/rails/templates/rails/welcome/index.html.erb +64 -48
  136. data/lib/rails/test_help.rb +4 -0
  137. data/lib/rails/test_unit/railtie.rb +0 -4
  138. data/lib/rails/test_unit/runner.rb +16 -9
  139. data/lib/rails/test_unit/testing.rake +4 -9
  140. data/lib/rails/version.rb +1 -1
  141. data/lib/rails/welcome_controller.rb +1 -0
  142. data/lib/rails.rb +13 -2
  143. metadata +33 -37
  144. data/lib/rails/command/spellchecker.rb +0 -57
  145. data/lib/rails/generators/css/assets/assets_generator.rb +0 -15
  146. data/lib/rails/generators/css/assets/templates/stylesheet.css +0 -4
  147. data/lib/rails/generators/css/scaffold/scaffold_generator.rb +0 -18
  148. data/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js +0 -6
  149. data/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js +0 -5
  150. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +0 -23
  151. data/lib/rails/generators/rails/app/templates/bin/spring.tt +0 -13
  152. data/lib/rails/generators/rails/app/templates/bin/yarn.tt +0 -16
  153. data/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt +0 -8
  154. data/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt +0 -8
  155. data/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt +0 -5
  156. data/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt +0 -4
  157. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_1.rb.tt +0 -67
  158. data/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt +0 -16
  159. data/lib/rails/generators/rails/app/templates/config/spring.rb.tt +0 -6
  160. data/lib/rails/generators/rails/app/templates/package.json.tt +0 -11
  161. data/lib/rails/generators/rails/assets/USAGE +0 -16
  162. data/lib/rails/generators/rails/assets/assets_generator.rb +0 -26
  163. data/lib/rails/generators/rails/assets/templates/stylesheet.css +0 -4
  164. data/lib/rails/generators/rails/scaffold/templates/scaffold.css +0 -80
@@ -30,9 +30,6 @@ module Rails
30
30
  class_option :database, type: :string, aliases: "-d", default: "sqlite3",
31
31
  desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
32
32
 
33
- class_option :skip_gemfile, type: :boolean, default: false,
34
- desc: "Don't create a Gemfile"
35
-
36
33
  class_option :skip_git, type: :boolean, aliases: "-G", default: false,
37
34
  desc: "Skip .gitignore file"
38
35
 
@@ -58,26 +55,19 @@ module Rails
58
55
  class_option :skip_active_storage, type: :boolean, default: false,
59
56
  desc: "Skip Active Storage files"
60
57
 
61
- class_option :skip_puma, type: :boolean, aliases: "-P", default: false,
62
- desc: "Skip Puma related files"
63
-
64
58
  class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false,
65
59
  desc: "Skip Action Cable files"
66
60
 
67
- class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false,
68
- desc: "Skip Sprockets files"
69
-
70
- class_option :skip_spring, type: :boolean, default: false,
71
- desc: "Don't install Spring application preloader"
61
+ class_option :skip_asset_pipeline, type: :boolean, aliases: "-A", default: false
72
62
 
73
- class_option :skip_listen, type: :boolean, default: false,
74
- desc: "Don't generate configuration that depends on the listen gem"
63
+ class_option :asset_pipeline, type: :string, aliases: "-a", default: "sprockets",
64
+ desc: "Choose your asset pipeline [options: sprockets (default), propshaft]"
75
65
 
76
66
  class_option :skip_javascript, type: :boolean, aliases: "-J", default: name == "plugin",
77
67
  desc: "Skip JavaScript files"
78
68
 
79
- class_option :skip_turbolinks, type: :boolean, default: false,
80
- desc: "Skip turbolinks gem"
69
+ class_option :skip_hotwire, type: :boolean, default: false,
70
+ desc: "Skip Hotwire integration"
81
71
 
82
72
  class_option :skip_jbuilder, type: :boolean, default: false,
83
73
  desc: "Skip jbuilder gem"
@@ -97,7 +87,7 @@ module Rails
97
87
  class_option :edge, type: :boolean, default: false,
98
88
  desc: "Set up the #{name} with Gemfile pointing to Rails repository"
99
89
 
100
- class_option :master, type: :boolean, default: false,
90
+ class_option :main, type: :boolean, default: false, aliases: "--master",
101
91
  desc: "Set up the #{name} with Gemfile pointing to Rails repository main branch"
102
92
 
103
93
  class_option :rc, type: :string, default: nil,
@@ -113,44 +103,23 @@ module Rails
113
103
  def initialize(positional_argv, option_argv, *)
114
104
  @argv = [*positional_argv, *option_argv]
115
105
  @gem_filter = lambda { |gem| true }
116
- @extra_entries = []
117
106
  super
118
107
  end
119
108
 
120
109
  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
110
  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]
111
+ [
112
+ rails_gemfile_entry,
113
+ asset_pipeline_gemfile_entry,
114
+ database_gemfile_entry,
115
+ web_server_gemfile_entry,
116
+ javascript_gemfile_entry,
117
+ hotwire_gemfile_entry,
118
+ css_gemfile_entry,
119
+ jbuilder_gemfile_entry,
120
+ psych_gemfile_entry,
121
+ cable_gemfile_entry,
122
+ ].flatten.compact.select(&@gem_filter)
154
123
  end
155
124
 
156
125
  def builder # :doc:
@@ -173,14 +142,9 @@ module Rails
173
142
  end
174
143
 
175
144
  def apply_rails_template # :doc:
176
- original_argv = ARGV.dup
177
- ARGV.replace(@argv)
178
-
179
145
  apply rails_template if rails_template
180
146
  rescue Thor::Error, LoadError, Errno::ENOENT => e
181
147
  raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}"
182
- ensure
183
- ARGV.replace(original_argv)
184
148
  end
185
149
 
186
150
  def set_default_accessors! # :doc:
@@ -190,36 +154,45 @@ module Rails
190
154
  when /^https?:\/\//
191
155
  options[:template]
192
156
  when String
193
- File.expand_path(options[:template], Dir.pwd)
157
+ File.expand_path(`echo #{options[:template]}`.strip)
194
158
  else
195
159
  options[:template]
196
160
  end
197
161
  end
198
162
 
199
163
  def database_gemfile_entry # :doc:
200
- return [] if options[:skip_active_record]
164
+ return if options[:skip_active_record]
165
+
201
166
  gem_name, gem_version = gem_for_database
202
167
  GemfileEntry.version gem_name, gem_version,
203
- "Use #{options[:database]} as the database for Active Record"
168
+ "Use #{options[:database]} as the database for Active Record"
204
169
  end
205
170
 
206
171
  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)
172
+ GemfileEntry.new "puma", "~> 5.0", "Use the Puma web server [https://github.com/puma/puma]"
173
+ end
174
+
175
+ def asset_pipeline_gemfile_entry
176
+ return if options[:skip_asset_pipeline]
177
+
178
+ if options[:asset_pipeline] == "sprockets"
179
+ GemfileEntry.floats "sprockets-rails",
180
+ "The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]"
181
+ elsif options[:asset_pipeline] == "propshaft"
182
+ GemfileEntry.floats "propshaft", "The modern asset pipeline for Rails [https://github.com/rails/propshaft]"
183
+ end
210
184
  end
211
185
 
212
186
  def include_all_railties? # :doc:
213
187
  [
214
188
  options.values_at(
215
189
  :skip_active_record,
216
- :skip_action_mailer,
217
190
  :skip_test,
218
- :skip_sprockets,
219
191
  :skip_action_cable,
220
192
  :skip_active_job
221
193
  ),
222
194
  skip_active_storage?,
195
+ skip_action_mailer?,
223
196
  skip_action_mailbox?,
224
197
  skip_action_text?
225
198
  ].flatten.none?
@@ -247,7 +220,11 @@ module Rails
247
220
  end
248
221
 
249
222
  def skip_active_storage? # :doc:
250
- options[:skip_active_storage] || options[:skip_active_record]
223
+ options[:skip_active_storage] || options[:skip_active_record] || options[:skip_active_job]
224
+ end
225
+
226
+ def skip_action_mailer? # :doc:
227
+ options[:skip_action_mailer] || options[:skip_active_job]
251
228
  end
252
229
 
253
230
  def skip_action_mailbox? # :doc:
@@ -262,6 +239,15 @@ module Rails
262
239
  options[:skip_dev_gems]
263
240
  end
264
241
 
242
+ def skip_sprockets?
243
+ options[:skip_asset_pipeline] || options[:asset_pipeline] != "sprockets"
244
+ end
245
+
246
+ def skip_propshaft?
247
+ options[:skip_asset_pipeline] || options[:asset_pipeline] != "propshaft"
248
+ end
249
+
250
+
265
251
  class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
266
252
  def initialize(name, version, comment, options = {}, commented_out = false)
267
253
  super
@@ -279,38 +265,40 @@ module Rails
279
265
  new(name, version, comment)
280
266
  end
281
267
 
268
+ def self.floats(name, comment = nil)
269
+ new(name, nil, comment)
270
+ end
271
+
282
272
  def self.path(name, path, comment = nil)
283
273
  new(name, nil, comment, path: path)
284
274
  end
285
275
 
286
- def version
287
- version = super
288
-
289
- if version.is_a?(Array)
290
- version.join("', '")
291
- else
292
- version
293
- end
276
+ def to_s
277
+ [
278
+ (comment.gsub(/^/, "# ").chomp + "\n" if comment),
279
+ ("# " if commented_out),
280
+ "gem \"#{name}\"",
281
+ *Array(version).map { |constraint| ", \"#{constraint}\"" },
282
+ *options.map { |key, value| ", #{key}: #{value.inspect}" },
283
+ ].compact.join
294
284
  end
295
285
  end
296
286
 
287
+ def rails_prerelease?
288
+ options.dev? || options.edge? || options.main?
289
+ end
290
+
297
291
  def rails_gemfile_entry
298
292
  if options.dev?
299
- [
300
- GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH)
301
- ]
293
+ GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH, "Use local checkout of Rails")
302
294
  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
- ]
295
+ edge_branch = Rails.gem_version.prerelease? ? "main" : [*Rails.gem_version.segments.first(2), "stable"].join("-")
296
+ GemfileEntry.github("rails", "rails/rails", edge_branch, "Use specific branch of Rails")
297
+ elsif options.main?
298
+ GemfileEntry.github("rails", "rails/rails", "main", "Use main development branch of Rails")
310
299
  else
311
- [GemfileEntry.version("rails",
312
- rails_version_specifier,
313
- "Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'")]
300
+ GemfileEntry.version("rails", rails_version_specifier,
301
+ %(Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"))
314
302
  end
315
303
  end
316
304
 
@@ -327,35 +315,59 @@ module Rails
327
315
  end
328
316
  end
329
317
 
330
- def assets_gemfile_entry
331
- return [] if options[:skip_sprockets]
318
+ def jbuilder_gemfile_entry
319
+ return if options[:skip_jbuilder]
320
+ GemfileEntry.new "jbuilder", nil, "Build JSON APIs with ease [https://github.com/rails/jbuilder]", {}, options[:api]
321
+ end
332
322
 
333
- GemfileEntry.version("sass-rails", ">= 6", "Use SCSS for stylesheets")
323
+ def javascript_gemfile_entry
324
+ return if options[:skip_javascript]
325
+
326
+ if adjusted_javascript_option == "importmap"
327
+ GemfileEntry.floats "importmap-rails", "Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]"
328
+ else
329
+ GemfileEntry.floats "jsbundling-rails", "Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]"
330
+ end
334
331
  end
335
332
 
336
- def webpacker_gemfile_entry
337
- return [] if options[:skip_javascript]
333
+ def hotwire_gemfile_entry
334
+ return if options[:skip_javascript] || options[:skip_hotwire]
335
+
336
+ turbo_rails_entry =
337
+ GemfileEntry.floats "turbo-rails", "Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]"
338
338
 
339
- GemfileEntry.version "webpacker", "~> 5.0", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
339
+ stimulus_rails_entry =
340
+ GemfileEntry.floats "stimulus-rails", "Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]"
341
+
342
+ [ turbo_rails_entry, stimulus_rails_entry ]
340
343
  end
341
344
 
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]
345
+ def using_node?
346
+ options[:javascript] && options[:javascript] != "importmap"
346
347
  end
347
348
 
348
- def javascript_gemfile_entry
349
- if options[:skip_javascript] || options[:skip_turbolinks]
350
- []
349
+ # CSS processors other than Tailwind require a node-based JavaScript environment. So overwrite the normal JS default
350
+ # if one such processor has been specified.
351
+ def adjusted_javascript_option
352
+ if options[:css] && options[:css] != "tailwind" && options[:javascript] == "importmap"
353
+ "esbuild"
354
+ else
355
+ options[:javascript]
356
+ end
357
+ end
358
+
359
+ def css_gemfile_entry
360
+ return unless options[:css]
361
+
362
+ if !using_node? && options[:css] == "tailwind"
363
+ GemfileEntry.floats "tailwindcss-rails", "Use Tailwind CSS [https://github.com/rails/tailwindcss-rails]"
351
364
  else
352
- [ GemfileEntry.version("turbolinks", "~> 5",
353
- "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") ]
365
+ GemfileEntry.floats "cssbundling-rails", "Bundle and process CSS [https://github.com/rails/cssbundling-rails]"
354
366
  end
355
367
  end
356
368
 
357
369
  def psych_gemfile_entry
358
- return [] unless defined?(Rubinius)
370
+ return unless defined?(Rubinius)
359
371
 
360
372
  comment = "Use Psych as the YAML engine, instead of Syck, so serialized " \
361
373
  "data can be read safely from different rubies (see http://git.io/uuLVag)"
@@ -363,11 +375,10 @@ module Rails
363
375
  end
364
376
 
365
377
  def cable_gemfile_entry
366
- return [] if options[:skip_action_cable]
378
+ return if options[:skip_action_cable]
379
+
367
380
  comment = "Use Redis adapter to run Action Cable in production"
368
- gems = []
369
- gems << GemfileEntry.new("redis", "~> 4.0", comment, {}, true)
370
- gems
381
+ GemfileEntry.new("redis", "~> 4.0", comment, {}, true)
371
382
  end
372
383
 
373
384
  def bundle_command(command, env = {})
@@ -397,51 +408,65 @@ module Rails
397
408
  end
398
409
 
399
410
  def bundle_install?
400
- !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
401
- end
402
-
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])
411
+ !(options[:skip_bundle] || options[:pretend])
409
412
  end
410
413
 
411
414
  def depends_on_system_test?
412
415
  !(options[:skip_system_test] || options[:skip_test] || options[:api])
413
416
  end
414
417
 
415
- def depend_on_listen?
416
- !options[:skip_listen] && os_supports_listen_out_of_the_box?
417
- end
418
-
419
418
  def depend_on_bootsnap?
420
419
  !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION)
421
420
  end
422
421
 
423
- def os_supports_listen_out_of_the_box?
424
- /darwin|linux/.match?(RbConfig::CONFIG["host_os"])
422
+ def target_rails_prerelease(self_command = "new")
423
+ return unless rails_prerelease? && bundle_install?
424
+
425
+ if !File.exist?(File.expand_path("Gemfile", destination_root))
426
+ create_file("Gemfile", <<~GEMFILE)
427
+ source "https://rubygems.org"
428
+ git_source(:github) { |repo| "https://github.com/\#{repo}.git" }
429
+ #{rails_gemfile_entry}
430
+ GEMFILE
431
+
432
+ run_bundle
433
+
434
+ @argv[0] = destination_root
435
+ require "shellwords"
436
+ bundle_command("exec rails #{self_command} #{Shellwords.join(@argv)}")
437
+ exit
438
+ else
439
+ remove_file("Gemfile")
440
+ remove_file("Gemfile.lock")
441
+ end
425
442
  end
426
443
 
427
444
  def run_bundle
428
445
  bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1") if bundle_install?
429
446
  end
430
447
 
431
- def run_webpack
432
- return unless webpack_install?
448
+ def run_javascript
449
+ return if options[:skip_javascript] || !bundle_install?
433
450
 
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
451
+ case adjusted_javascript_option
452
+ when "importmap" then rails_command "importmap:install"
453
+ when "webpack", "esbuild", "rollup" then rails_command "javascript:install:#{adjusted_javascript_option}"
440
454
  end
455
+ end
456
+
457
+ def run_hotwire
458
+ return if options[:skip_javascript] || options[:skip_hotwire] || !bundle_install?
441
459
 
442
- rails_command "webpacker:install"
443
- if options[:webpack] && options[:webpack] != "webpack"
444
- rails_command "webpacker:install:#{options[:webpack]}"
460
+ rails_command "turbo:install stimulus:install"
461
+ end
462
+
463
+ def run_css
464
+ return if !options[:css] || !bundle_install?
465
+
466
+ if !using_node? && options[:css] == "tailwind"
467
+ rails_command "tailwindcss:install"
468
+ else
469
+ rails_command "css:install:#{options[:css]}"
445
470
  end
446
471
  end
447
472
 
@@ -7,7 +7,7 @@ module Rails
7
7
 
8
8
  private
9
9
  def app_name
10
- @app_name ||= original_app_name.tr('\\', "").tr("-. ", "_")
10
+ @app_name ||= original_app_name.tr("\\", "").tr("-. ", "_")
11
11
  end
12
12
 
13
13
  def original_app_name
@@ -94,7 +94,7 @@ module Rails
94
94
  #
95
95
  # The first and last part used to find the generator to be invoked are
96
96
  # guessed based on class invokes hook_for, as noticed in the example above.
97
- # This can be customized with two options: :in and :as.
97
+ # This can be customized with two options: +:in+ and +:as+.
98
98
  #
99
99
  # Let's suppose you are creating a generator that needs to invoke the
100
100
  # controller generator from test unit. Your first attempt is:
@@ -108,7 +108,7 @@ module Rails
108
108
  # "test_unit:awesome", "test_unit"
109
109
  #
110
110
  # Which is not the desired lookup. You can change it by providing the
111
- # :as option:
111
+ # +:as+ option:
112
112
  #
113
113
  # class AwesomeGenerator < Rails::Generators::Base
114
114
  # hook_for :test_framework, as: :controller
@@ -119,7 +119,7 @@ module Rails
119
119
  # "test_unit:controller", "test_unit"
120
120
  #
121
121
  # Similarly, if you want it to also look up in the rails namespace, you
122
- # just need to provide the :in value:
122
+ # just need to provide the +:in+ value:
123
123
  #
124
124
  # class AwesomeGenerator < Rails::Generators::Base
125
125
  # hook_for :test_framework, in: :rails, as: :controller
@@ -206,7 +206,7 @@ module Rails
206
206
  end
207
207
 
208
208
  # Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
209
- def self.class_option(name, options = {}) #:nodoc:
209
+ def self.class_option(name, options = {}) # :nodoc:
210
210
  options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
211
211
  options[:aliases] = default_aliases_for_option(name, options)
212
212
  options[:default] = default_value_for_option(name, options)
@@ -231,7 +231,7 @@ module Rails
231
231
 
232
232
  # Cache source root and add lib/generators/base/generator/templates to
233
233
  # source paths.
234
- def self.inherited(base) #:nodoc:
234
+ def self.inherited(base) # :nodoc:
235
235
  super
236
236
 
237
237
  # Invoke source_root so the default_source_root is set.
@@ -324,21 +324,17 @@ module Rails
324
324
 
325
325
  # Sets the base_name taking into account the current class namespace.
326
326
  def self.base_name # :doc:
327
- @base_name ||= begin
328
- if base = name.to_s.split("::").first
329
- base.underscore
330
- end
327
+ @base_name ||= if base = name.to_s.split("::").first
328
+ base.underscore
331
329
  end
332
330
  end
333
331
 
334
332
  # Removes the namespaces and get the generator name. For example,
335
333
  # Rails::Generators::ModelGenerator will return "model" as generator name.
336
334
  def self.generator_name # :doc:
337
- @generator_name ||= begin
338
- if generator = name.to_s.split("::").last
339
- generator.delete_suffix!("Generator")
340
- generator.underscore
341
- end
335
+ @generator_name ||= if generator = name.to_s.split("::").last
336
+ generator.delete_suffix!("Generator")
337
+ generator.underscore
342
338
  end
343
339
  end
344
340
 
@@ -368,13 +364,13 @@ module Rails
368
364
  end
369
365
 
370
366
  # Keep hooks configuration that are used on prepare_for_invocation.
371
- def self.hooks #:nodoc:
367
+ def self.hooks # :nodoc:
372
368
  @hooks ||= from_superclass(:hooks, {})
373
369
  end
374
370
 
375
371
  # Prepare class invocation to search on Rails namespace if a previous
376
372
  # added hook is being used.
377
- def self.prepare_for_invocation(name, value) #:nodoc:
373
+ def self.prepare_for_invocation(name, value) # :nodoc:
378
374
  return super unless value.is_a?(String) || value.is_a?(Symbol)
379
375
 
380
376
  if value && constants = hooks[name]
@@ -21,6 +21,8 @@ module Erb # :nodoc:
21
21
  template filename, File.join("app/views", controller_file_path, filename)
22
22
  end
23
23
  end
24
+
25
+ template "partial.html.erb", File.join("app/views", controller_file_path, "_#{singular_name}.html.erb")
24
26
  end
25
27
 
26
28
  private
@@ -1,6 +1,6 @@
1
1
  <%%= form_with(model: <%= model_resource_name %>) do |form| %>
2
2
  <%% if <%= singular_table_name %>.errors.any? %>
3
- <div id="error_explanation">
3
+ <div style="color: red">
4
4
  <h2><%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2>
5
5
 
6
6
  <ul>
@@ -12,26 +12,26 @@
12
12
  <%% end %>
13
13
 
14
14
  <% attributes.each do |attribute| -%>
15
- <div class="field">
15
+ <div>
16
16
  <% if attribute.password_digest? -%>
17
- <%%= form.label :password %>
17
+ <%%= form.label :password, style: "display: block" %>
18
18
  <%%= form.password_field :password %>
19
19
  </div>
20
20
 
21
- <div class="field">
22
- <%%= form.label :password_confirmation %>
21
+ <div>
22
+ <%%= form.label :password_confirmation, style: "display: block" %>
23
23
  <%%= form.password_field :password_confirmation %>
24
24
  <% elsif attribute.attachments? -%>
25
- <%%= form.label :<%= attribute.column_name %> %>
25
+ <%%= form.label :<%= attribute.column_name %>, style: "display: block" %>
26
26
  <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true %>
27
27
  <% else -%>
28
- <%%= form.label :<%= attribute.column_name %> %>
28
+ <%%= form.label :<%= attribute.column_name %>, style: "display: block" %>
29
29
  <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %> %>
30
30
  <% end -%>
31
31
  </div>
32
32
 
33
33
  <% end -%>
34
- <div class="actions">
34
+ <div>
35
35
  <%%= form.submit %>
36
36
  </div>
37
37
  <%% end %>
@@ -1,6 +1,10 @@
1
- <h1>Editing <%= singular_table_name.titleize %></h1>
1
+ <h1>Editing <%= human_name.downcase %></h1>
2
2
 
3
- <%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %>
3
+ <%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
4
4
 
5
- <%%= link_to 'Show', @<%= singular_table_name %> %> |
6
- <%%= link_to 'Back', <%= index_helper %>_path %>
5
+ <br>
6
+
7
+ <div>
8
+ <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %> %> |
9
+ <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %> %>
10
+ </div>
@@ -1,31 +1,14 @@
1
- <p id="notice"><%%= notice %></p>
1
+ <p style="color: green"><%%= notice %></p>
2
2
 
3
- <h1><%= plural_table_name.titleize %></h1>
3
+ <h1><%= human_name.pluralize %></h1>
4
4
 
5
- <table>
6
- <thead>
7
- <tr>
8
- <% attributes.reject(&:password_digest?).each do |attribute| -%>
9
- <th><%= attribute.human_name %></th>
10
- <% end -%>
11
- <th colspan="3"></th>
12
- </tr>
13
- </thead>
5
+ <div id="<%= plural_table_name %>">
6
+ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
7
+ <%%= render <%= singular_table_name %> %>
8
+ <p>
9
+ <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(singular_table_name) %> %>
10
+ </p>
11
+ <%% end %>
12
+ </div>
14
13
 
15
- <tbody>
16
- <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
17
- <tr>
18
- <% attributes.reject(&:password_digest?).each do |attribute| -%>
19
- <td><%%= <%= singular_table_name %>.<%= attribute.column_name %> %></td>
20
- <% end -%>
21
- <td><%%= link_to 'Show', <%= model_resource_name %> %></td>
22
- <td><%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %></td>
23
- <td><%%= link_to 'Destroy', <%= model_resource_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
24
- </tr>
25
- <%% end %>
26
- </tbody>
27
- </table>
28
-
29
- <br>
30
-
31
- <%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_route_name %>_path %>
14
+ <%%= link_to "New <%= human_name.downcase %>", <%= new_helper(type: :path) %> %>