railties 7.1.3.4 → 7.2.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +175 -744
  3. data/lib/minitest/rails_plugin.rb +5 -2
  4. data/lib/rails/all.rb +1 -3
  5. data/lib/rails/api/task.rb +3 -2
  6. data/lib/rails/application/bootstrap.rb +5 -6
  7. data/lib/rails/application/configuration.rb +34 -39
  8. data/lib/rails/application/dummy_config.rb +2 -2
  9. data/lib/rails/application/finisher.rb +7 -0
  10. data/lib/rails/application.rb +7 -48
  11. data/lib/rails/backtrace_cleaner.rb +18 -3
  12. data/lib/rails/cli.rb +0 -1
  13. data/lib/rails/command.rb +1 -1
  14. data/lib/rails/commands/app/update_command.rb +86 -0
  15. data/lib/rails/commands/console/console_command.rb +2 -21
  16. data/lib/rails/commands/console/irb_console.rb +137 -0
  17. data/lib/rails/commands/credentials/credentials_command.rb +2 -2
  18. data/lib/rails/commands/dbconsole/dbconsole_command.rb +21 -30
  19. data/lib/rails/commands/devcontainer/devcontainer_command.rb +34 -0
  20. data/lib/rails/commands/rake/rake_command.rb +1 -1
  21. data/lib/rails/commands/runner/runner_command.rb +14 -3
  22. data/lib/rails/commands/server/server_command.rb +5 -3
  23. data/lib/rails/commands/test/test_command.rb +2 -0
  24. data/lib/rails/configuration.rb +10 -1
  25. data/lib/rails/console/app.rb +5 -32
  26. data/lib/rails/console/helpers.rb +5 -16
  27. data/lib/rails/console/methods.rb +23 -0
  28. data/lib/rails/engine.rb +5 -5
  29. data/lib/rails/gem_version.rb +3 -3
  30. data/lib/rails/generators/app_base.rb +70 -49
  31. data/lib/rails/generators/base.rb +5 -1
  32. data/lib/rails/generators/database.rb +227 -69
  33. data/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt +2 -0
  34. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +2 -0
  35. data/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt +2 -0
  36. data/lib/rails/generators/generated_attribute.rb +26 -1
  37. data/lib/rails/generators/migration.rb +3 -3
  38. data/lib/rails/generators/rails/app/app_generator.rb +52 -23
  39. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +23 -14
  40. data/lib/rails/generators/rails/app/templates/Gemfile.tt +16 -16
  41. data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +4 -0
  42. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +8 -1
  43. data/lib/rails/generators/rails/app/templates/app/views/pwa/manifest.json.erb.tt +22 -0
  44. data/lib/rails/generators/rails/app/templates/app/views/pwa/service-worker.js +26 -0
  45. data/lib/rails/generators/rails/app/templates/bin/brakeman.tt +6 -0
  46. data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +7 -0
  47. data/lib/rails/generators/rails/app/templates/bin/setup.tt +6 -2
  48. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +1 -1
  49. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +3 -3
  50. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +7 -0
  51. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +8 -1
  52. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +3 -3
  53. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +11 -6
  54. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +2 -0
  55. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +6 -5
  56. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +1 -1
  57. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_2.rb.tt +70 -0
  58. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +42 -23
  59. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +4 -0
  60. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +5 -0
  61. data/lib/rails/generators/rails/app/templates/dockerignore.tt +13 -0
  62. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +138 -0
  63. data/lib/rails/generators/rails/app/templates/github/dependabot.yml +12 -0
  64. data/lib/rails/generators/rails/app/templates/gitignore.tt +3 -3
  65. data/lib/rails/generators/rails/app/templates/public/406-unsupported-browser.html +66 -0
  66. data/lib/rails/generators/rails/app/templates/public/icon.png +0 -0
  67. data/lib/rails/generators/rails/app/templates/public/icon.svg +3 -0
  68. data/lib/rails/generators/rails/app/templates/rubocop.yml.tt +8 -0
  69. data/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt +1 -1
  70. data/lib/rails/generators/rails/controller/controller_generator.rb +1 -1
  71. data/lib/rails/generators/rails/db/system/change/change_generator.rb +131 -20
  72. data/lib/rails/generators/rails/devcontainer/devcontainer_generator.rb +166 -0
  73. data/lib/rails/generators/rails/migration/migration_generator.rb +4 -0
  74. data/lib/rails/generators/rails/plugin/plugin_generator.rb +38 -7
  75. data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +2 -2
  76. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +5 -1
  77. data/lib/rails/generators/rails/plugin/templates/bin/rubocop.tt +7 -0
  78. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +103 -0
  79. data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +12 -0
  80. data/lib/rails/generators/rails/plugin/templates/rubocop.yml.tt +8 -0
  81. data/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt +1 -1
  82. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +10 -0
  83. data/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt +2 -0
  84. data/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt +1 -1
  85. data/lib/rails/generators/testing/assertions.rb +20 -0
  86. data/lib/rails/generators/testing/behavior.rb +7 -6
  87. data/lib/rails/generators.rb +1 -1
  88. data/lib/rails/health_controller.rb +1 -1
  89. data/lib/rails/info.rb +2 -2
  90. data/lib/rails/mailers_controller.rb +14 -1
  91. data/lib/rails/paths.rb +2 -2
  92. data/lib/rails/pwa_controller.rb +15 -0
  93. data/lib/rails/rack/logger.rb +15 -7
  94. data/lib/rails/railtie/configurable.rb +2 -2
  95. data/lib/rails/railtie.rb +2 -3
  96. data/lib/rails/tasks/framework.rake +0 -26
  97. data/lib/rails/tasks/tmp.rake +1 -1
  98. data/lib/rails/templates/layouts/application.html.erb +1 -1
  99. data/lib/rails/templates/rails/mailers/email.html.erb +12 -8
  100. data/lib/rails/templates/rails/welcome/index.html.erb +3 -2
  101. data/lib/rails/test_help.rb +2 -4
  102. data/lib/rails/test_unit/reporter.rb +8 -2
  103. data/lib/rails/test_unit/runner.rb +21 -2
  104. data/lib/rails/test_unit/test_parser.rb +45 -0
  105. data/lib/rails.rb +7 -4
  106. metadata +38 -32
  107. data/lib/rails/app_updater.rb +0 -40
  108. data/lib/rails/commands/secrets/USAGE +0 -61
  109. data/lib/rails/commands/secrets/secrets_command.rb +0 -47
  110. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +0 -68
  111. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +0 -54
  112. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +0 -70
  113. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +0 -24
  114. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +0 -62
  115. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +0 -53
  116. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +0 -284
  117. data/lib/rails/generators/rails/app/templates/public/apple-touch-icon-precomposed.png +0 -0
  118. data/lib/rails/generators/rails/app/templates/public/apple-touch-icon.png +0 -0
  119. data/lib/rails/generators/rails/app/templates/public/favicon.ico +0 -0
  120. data/lib/rails/ruby_version_check.rb +0 -17
  121. data/lib/rails/secrets.rb +0 -110
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Rails
6
+ module Generators
7
+ class DevcontainerGenerator < Base # :nodoc:
8
+ class_option :app_name, type: :string, default: "rails_app",
9
+ desc: "Name of the app"
10
+
11
+ class_option :database, enum: Database::DATABASES, type: :string, default: "sqlite3",
12
+ desc: "Include configuration for selected database"
13
+
14
+ class_option :redis, type: :boolean, default: true,
15
+ desc: "Include configuration for Redis"
16
+
17
+ class_option :system_test, type: :boolean, default: true,
18
+ desc: "Include configuration for System Tests"
19
+
20
+ class_option :active_storage, type: :boolean, default: true,
21
+ desc: "Include configuration for Active Storage"
22
+
23
+ class_option :node, type: :boolean, default: false,
24
+ desc: "Include configuration for Node"
25
+
26
+ class_option :dev, type: :boolean, default: false,
27
+ desc: "For applications pointing to a local Rails checkout"
28
+
29
+ source_paths << File.expand_path(File.join(base_name, "app", "templates"), base_root)
30
+
31
+ def create_devcontainer
32
+ empty_directory ".devcontainer"
33
+
34
+ template ".devcontainer/devcontainer.json"
35
+ template ".devcontainer/Dockerfile"
36
+ template ".devcontainer/compose.yaml"
37
+ end
38
+
39
+ def update_application_system_test_case
40
+ return unless options[:system_test]
41
+ return unless File.exist?("test/application_system_test_case.rb")
42
+
43
+ gsub_file("test/application_system_test_case.rb", /^(\s*driven_by\b.*)/, system_test_configuration)
44
+ end
45
+
46
+ def update_database_yml
47
+ # Only postgresql has devcontainer specific configuration, so only update database.yml if we are using postgres
48
+ return unless options[:database] == "postgresql"
49
+
50
+ template("config/databases/#{options[:database]}.yml", "config/database.yml")
51
+ end
52
+
53
+ private
54
+ def devcontainer?
55
+ true
56
+ end
57
+
58
+ def app_name
59
+ options[:app_name]
60
+ end
61
+
62
+ def dependencies
63
+ return @dependencies if @dependencies
64
+
65
+ @dependencies = []
66
+
67
+ @dependencies << "selenium" if options[:system_test]
68
+ @dependencies << "redis" if options[:redis]
69
+ @dependencies << database.name if database.service
70
+ @dependencies
71
+ end
72
+
73
+ def container_env
74
+ return @container_env if @container_env
75
+
76
+ @container_env = {}
77
+
78
+ @container_env["CAPYBARA_SERVER_PORT"] = "45678" if options[:system_test]
79
+ @container_env["SELENIUM_HOST"] = "selenium" if options[:system_test]
80
+ @container_env["REDIS_URL"] = "redis://redis:6379/1" if options[:redis]
81
+ @container_env["DB_HOST"] = database.name if database.service
82
+
83
+ @container_env
84
+ end
85
+
86
+ def volumes
87
+ return @volumes if @volumes
88
+
89
+ @volumes = []
90
+
91
+ @volumes << "redis-data" if options[:redis]
92
+ @volumes << database.volume if database.volume
93
+
94
+ @volumes
95
+ end
96
+
97
+ def features
98
+ return @features if @features
99
+
100
+ @features = {
101
+ "ghcr.io/devcontainers/features/github-cli:1" => {}
102
+ }
103
+
104
+ @features["ghcr.io/rails/devcontainer/features/activestorage"] = {} if options[:active_storage]
105
+ @features["ghcr.io/devcontainers/features/node:1"] = {} if options[:node]
106
+
107
+ @features.merge!(database.feature) if database.feature
108
+
109
+ @features
110
+ end
111
+
112
+ def mounts
113
+ return @mounts if @mounts
114
+
115
+ @mounts = []
116
+
117
+ @mounts << local_rails_mount if options[:dev]
118
+
119
+ @mounts
120
+ end
121
+
122
+ def forward_ports
123
+ return @forward_ports if @forward_ports
124
+
125
+ @forward_ports = [3000]
126
+ @forward_ports << database.port if database.port
127
+ @forward_ports << 6379 if options[:redis]
128
+
129
+ @forward_ports
130
+ end
131
+
132
+ def database
133
+ @database ||= Database.build(options[:database])
134
+ end
135
+
136
+ def devcontainer_db_service_yaml(**options)
137
+ return unless service = database.service
138
+
139
+ { database.name => service }.to_yaml(**options)[4..-1]
140
+ end
141
+
142
+ def local_rails_mount
143
+ {
144
+ type: "bind",
145
+ source: Rails::Generators::RAILS_DEV_PATH,
146
+ target: Rails::Generators::RAILS_DEV_PATH
147
+ }
148
+ end
149
+
150
+ def system_test_configuration
151
+ <<~RUBY
152
+ if ENV["CAPYBARA_SERVER_PORT"]
153
+ served_by host: "rails-app", port: ENV["CAPYBARA_SERVER_PORT"]
154
+
155
+ driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ], options: {
156
+ browser: :remote,
157
+ url: "http://#{ENV["SELENIUM_HOST"]}:4444"
158
+ }
159
+ else
160
+ driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ]
161
+ end
162
+ RUBY
163
+ end
164
+ end
165
+ end
166
+ end
@@ -5,6 +5,10 @@ module Rails
5
5
  class MigrationGenerator < NamedBase # :nodoc:
6
6
  argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
7
7
  hook_for :orm, required: true, desc: "ORM to be invoked"
8
+
9
+ def self.exit_on_failure? # :nodoc:
10
+ true
11
+ end
8
12
  end
9
13
  end
10
14
  end
@@ -66,6 +66,16 @@ module Rails
66
66
  template "gitignore", ".gitignore"
67
67
  end
68
68
 
69
+ def cifiles
70
+ empty_directory ".github/workflows"
71
+ template "github/ci.yml", ".github/workflows/ci.yml"
72
+ template "github/dependabot.yml", ".github/dependabot.yml"
73
+ end
74
+
75
+ def rubocop
76
+ template "rubocop.yml", ".rubocop.yml"
77
+ end
78
+
69
79
  def version_control
70
80
  if !options[:skip_git] && !options[:pretend]
71
81
  run git_init_command, capture: options[:quiet], abort_on_failure: false
@@ -112,9 +122,12 @@ module Rails
112
122
  def generate_test_dummy(force = false)
113
123
  opts = options.transform_keys(&:to_sym).except(*DUMMY_IGNORE_OPTIONS)
114
124
  opts[:force] = force
125
+ opts[:skip_brakeman] = true
115
126
  opts[:skip_bundle] = true
127
+ opts[:skip_ci] = true
116
128
  opts[:skip_git] = true
117
129
  opts[:skip_hotwire] = true
130
+ opts[:skip_rubocop] = true
118
131
  opts[:dummy_app] = true
119
132
 
120
133
  invoke Rails::Generators::AppGenerator,
@@ -144,7 +157,7 @@ module Rails
144
157
  def test_dummy_clean
145
158
  inside dummy_path do
146
159
  remove_file ".ruby-version"
147
- remove_file "db/seeds.rb"
160
+ remove_dir "db"
148
161
  remove_file "Gemfile"
149
162
  remove_file "lib/tasks"
150
163
  remove_file "public/robots.txt"
@@ -167,12 +180,12 @@ module Rails
167
180
  end
168
181
  end
169
182
 
170
- def bin(force = false)
171
- bin_file = engine? ? "bin/rails.tt" : "bin/test.tt"
172
- template bin_file, force: force do |content|
183
+ def bin
184
+ exclude_pattern = Regexp.union([(engine? ? /test\.tt/ : /rails\.tt/), (/rubocop/ if skip_rubocop?)].compact)
185
+ directory "bin", { exclude_pattern: exclude_pattern } do |content|
173
186
  "#{shebang}\n" + content
174
187
  end
175
- chmod "bin", 0755, verbose: false
188
+ chmod "bin", 0755 & ~File.umask, verbose: false
176
189
  end
177
190
 
178
191
  def gemfile_entry
@@ -180,7 +193,7 @@ module Rails
180
193
 
181
194
  gemfile_in_app_path = File.join(rails_app_path, "Gemfile")
182
195
  if File.exist? gemfile_in_app_path
183
- entry = "\ngem '#{name}', path: '#{relative_path}'"
196
+ entry = %{\ngem "#{name}", path: "#{relative_path}"}
184
197
  append_file gemfile_in_app_path, entry
185
198
  end
186
199
  end
@@ -243,6 +256,16 @@ module Rails
243
256
  build(:app)
244
257
  end
245
258
 
259
+ def create_rubocop_file
260
+ return if skip_rubocop?
261
+ build(:rubocop)
262
+ end
263
+
264
+ def create_cifiles
265
+ return if skip_ci?
266
+ build(:cifiles)
267
+ end
268
+
246
269
  def create_config_files
247
270
  build(:config)
248
271
  end
@@ -341,7 +364,7 @@ module Rails
341
364
  build(:test_dummy_sprocket_assets) unless skip_sprockets?
342
365
  build(:test_dummy_clean)
343
366
  # ensure that bin/rails has proper dummy_path
344
- build(:bin, true)
367
+ build(:bin)
345
368
  end
346
369
  end
347
370
 
@@ -465,6 +488,14 @@ module Rails
465
488
  return unless inside_application?
466
489
  app_path.delete_prefix("#{rails_app_path}/")
467
490
  end
491
+
492
+ def test_command
493
+ if engine? && !options[:skip_active_record] && with_dummy_app?
494
+ "db:test:prepare test"
495
+ else
496
+ "test"
497
+ end
498
+ end
468
499
  end
469
500
  end
470
501
  end
@@ -3,8 +3,8 @@ require_relative "lib/<%= namespaced_name %>/version"
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = <%= name.inspect %>
5
5
  spec.version = <%= camelized_modules %>::VERSION
6
- spec.authors = [<%= author.inspect %>]
7
- spec.email = [<%= email.inspect %>]
6
+ spec.authors = [ <%= author.inspect %> ]
7
+ spec.email = [ <%= email.inspect %> ]
8
8
  spec.homepage = "TODO"
9
9
  spec.summary = "TODO: Summary of <%= camelized_modules %>."
10
10
  spec.description = "TODO: Description of <%= camelized_modules %>."
@@ -1,5 +1,4 @@
1
1
  source "https://rubygems.org"
2
- git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
2
  <% unless options[:skip_gemspec] -%>
4
3
 
5
4
  # Specify your gem's dependencies in <%= name %>.gemspec.
@@ -8,6 +7,11 @@ gemspec
8
7
  <% gemfile_entries.each do |gemfile_entry| %>
9
8
  <%= gemfile_entry %>
10
9
  <% end -%>
10
+ <%- unless options.skip_rubocop? -%>
11
+
12
+ # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
13
+ gem "rubocop-rails-omakase", require: false
14
+ <%- end -%>
11
15
  <% if RUBY_ENGINE == "ruby" -%>
12
16
 
13
17
  # Start debugger with binding.b [https://github.com/ruby/debug]
@@ -0,0 +1,7 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ # explicit rubocop config increases performance slightly while avoiding config confusion.
5
+ ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
6
+
7
+ load Gem.bin_path("rubocop", "rubocop")
@@ -0,0 +1,103 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [ main ]
7
+
8
+ jobs:
9
+ <%- unless skip_rubocop? -%>
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout code
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
20
+ bundler-cache: true
21
+
22
+ - name: Lint code for consistent style
23
+ run: bin/rubocop -f github
24
+
25
+ <% end -%>
26
+ <% unless options[:skip_test] -%>
27
+ test:
28
+ runs-on: ubuntu-latest
29
+
30
+ <%- if options[:database] == "sqlite3" -%>
31
+ # services:
32
+ # redis:
33
+ # image: redis
34
+ # ports:
35
+ # - 6379:6379
36
+ # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
37
+ <%- else -%>
38
+ services:
39
+ <%- if options[:database] == "mysql" || options[:database] == "trilogy" -%>
40
+ mysql:
41
+ image: mysql
42
+ env:
43
+ MYSQL_ALLOW_EMPTY_PASSWORD: true
44
+ ports:
45
+ - 3306:3306
46
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
47
+ <%- elsif options[:database] == "postgresql" -%>
48
+ postgres:
49
+ image: postgres
50
+ env:
51
+ POSTGRES_USER: postgres
52
+ POSTGRES_PASSWORD: postgres
53
+ ports:
54
+ - 5432:5432
55
+ options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3
56
+ <%- end -%>
57
+
58
+ # redis:
59
+ # image: redis
60
+ # ports:
61
+ # - 6379:6379
62
+ # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
63
+
64
+ <%- end -%>
65
+ steps:
66
+ - name: Install packages
67
+ run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable <%= (dockerfile_base_packages + [database.base_package]).join(" ") %>
68
+
69
+ - name: Checkout code
70
+ uses: actions/checkout@v4
71
+
72
+ - name: Set up Ruby
73
+ uses: ruby/setup-ruby@v1
74
+ with:
75
+ ruby-version: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
76
+ bundler-cache: true
77
+ <%- if using_bun? -%>
78
+
79
+ - uses: oven-sh/setup-bun@v1
80
+ with:
81
+ bun-version: <%= dockerfile_bun_version %>
82
+ <%- end -%>
83
+
84
+ - name: Run tests
85
+ env:
86
+ RAILS_ENV: test
87
+ <%- if options[:database] == "mysql" || options[:database] == "trilogy" -%>
88
+ DATABASE_URL: mysql2://127.0.0.1:3306
89
+ <%- elsif options[:database] == "postgresql" -%>
90
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432
91
+ <%- end -%>
92
+ # REDIS_URL: redis://localhost:6379/0
93
+ run: bin/rails <%= test_command %>
94
+
95
+ - name: Keep screenshots from failed system tests
96
+ uses: actions/upload-artifact@v4
97
+ if: failure()
98
+ with:
99
+ name: screenshots
100
+ path: ${{ github.workspace }}/tmp/screenshots
101
+ if-no-files-found: ignore
102
+ <% end -%>
103
+
@@ -0,0 +1,12 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
8
+ - package-ecosystem: github-actions
9
+ directory: "/"
10
+ schedule:
11
+ interval: daily
12
+ open-pull-requests-limit: 10
@@ -0,0 +1,8 @@
1
+ # Omakase Ruby styling for Rails
2
+ inherit_gem: { rubocop-rails-omakase: rubocop.yml }
3
+
4
+ # Overwrite or add rules to create your own house style
5
+ #
6
+ # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
7
+ # Layout/SpaceInsideArrayLiteralBrackets:
8
+ # Enabled: false
@@ -1,5 +1,5 @@
1
1
  require "test_helper"
2
2
 
3
3
  class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4
- driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
4
+ driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ]
5
5
  end
@@ -63,6 +63,16 @@ module TestUnit # :nodoc:
63
63
  attribute = attributes.find { |attr| attr.name == name }
64
64
  attribute&.virtual?
65
65
  end
66
+
67
+ def datetime?(name)
68
+ attribute = attributes.find { |attr| attr.name == name }
69
+ attribute&.type == :datetime
70
+ end
71
+
72
+ def time?(name)
73
+ attribute = attributes.find { |attr| attr.name == name }
74
+ attribute&.type == :time
75
+ end
66
76
  end
67
77
  end
68
78
  end
@@ -35,6 +35,8 @@ class <%= class_name.pluralize %>Test < ApplicationSystemTestCase
35
35
  <%- attributes_hash.each do |attr, value| -%>
36
36
  <%- if boolean?(attr) -%>
37
37
  check "<%= attr.humanize %>" if <%= value %>
38
+ <%- elsif datetime?(attr) || time?(attr) -%>
39
+ fill_in "<%= attr.humanize %>", with: <%= value %>.to_s
38
40
  <%- else -%>
39
41
  fill_in "<%= attr.humanize %>", with: <%= value %>
40
42
  <%- end -%>
@@ -1,5 +1,5 @@
1
1
  require "test_helper"
2
2
 
3
3
  class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4
- driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
4
+ driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ]
5
5
  end
@@ -121,6 +121,26 @@ module Rails
121
121
  assert_equal(value, create_generated_attribute(attribute_type).default)
122
122
  end
123
123
  end
124
+
125
+ # Asserts a given initializer exists. You need to supply a path relative
126
+ # to the `config/initializers/` directory.
127
+ #
128
+ # assert_initializer "mail_interceptors.rb"
129
+ #
130
+ # You can also give extra arguments. If the argument is a regexp, it will check if the
131
+ # regular expression matches the given file content. If it's a string, it compares the
132
+ # file with the given string:
133
+ #
134
+ # assert_initializer "mail_interceptors.rb", /SandboxEmailInterceptor/
135
+ #
136
+ # Finally, when a block is given, it yields the file content:
137
+ #
138
+ # assert_initializer "mail_interceptors.rb" do |initializer|
139
+ # assert_match(/SandboxEmailInterceptor/, initializer)
140
+ # end
141
+ def assert_initializer(name, *contents, &block)
142
+ assert_file("config/initializers/#{name}", *contents, &block)
143
+ end
124
144
  end
125
145
  end
126
146
  end
@@ -65,11 +65,15 @@ module Rails
65
65
  # You can provide a configuration hash as second argument. This method returns the output
66
66
  # printed by the generator.
67
67
  def run_generator(args = default_arguments, config = {})
68
- capture(:stdout) do
69
- args += ["--skip-bundle"] unless args.include?("--no-skip-bundle") || args.include?("--dev")
70
- args += ["--skip-bootsnap"] unless args.include?("--no-skip-bootsnap") || args.include?("--skip-bootsnap")
68
+ args += ["--skip-bundle"] unless args.include?("--no-skip-bundle") || args.include?("--dev")
69
+ args += ["--skip-bootsnap"] unless args.include?("--no-skip-bootsnap") || args.include?("--skip-bootsnap")
71
70
 
71
+ if ENV["RAILS_LOG_TO_STDOUT"] == "true"
72
72
  generator_class.start(args, config.reverse_merge(destination_root: destination_root))
73
+ else
74
+ capture(:stdout) do
75
+ generator_class.start(args, config.reverse_merge(destination_root: destination_root))
76
+ end
73
77
  end
74
78
  end
75
79
 
@@ -107,9 +111,6 @@ module Rails
107
111
  Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
108
112
  end
109
113
  end
110
-
111
- include ActiveSupport::Deprecation::DeprecatedConstantAccessor
112
- deprecate_constant "Behaviour", "Rails::Generators::Testing::Behavior", deprecator: Rails.deprecator
113
114
  end
114
115
  end
115
116
  end
@@ -23,6 +23,7 @@ module Rails
23
23
  autoload :NamedBase, "rails/generators/named_base"
24
24
  autoload :ResourceHelpers, "rails/generators/resource_helpers"
25
25
  autoload :TestCase, "rails/generators/test_case"
26
+ autoload :Devcontainer, "rails/generators/devcontainer"
26
27
 
27
28
  mattr_accessor :namespace
28
29
 
@@ -202,7 +203,6 @@ module Rails
202
203
  rails.map! { |n| n.delete_prefix("rails:") }
203
204
  rails.delete("app")
204
205
  rails.delete("plugin")
205
- rails.delete("encrypted_secrets")
206
206
  rails.delete("encrypted_file")
207
207
  rails.delete("encryption_key_file")
208
208
  rails.delete("master_key")
@@ -24,7 +24,7 @@ module Rails
24
24
  # The health check will now be accessible via the +/healthz+ path.
25
25
  #
26
26
  # NOTE: This endpoint does not reflect the status of all of your application's
27
- # dependencies, such as the database or redis cluster. Replace
27
+ # dependencies, such as the database or Redis cluster. Replace
28
28
  # <tt>"rails/health#show"</tt> with your own controller action if you have
29
29
  # application specific needs.
30
30
  #
data/lib/rails/info.rb CHANGED
@@ -95,11 +95,11 @@ module Rails
95
95
 
96
96
  # The name of the database adapter for the current environment.
97
97
  property "Database adapter" do
98
- ActiveRecord::Base.connection.pool.db_config.adapter
98
+ ActiveRecord::Base.connection_pool.db_config.adapter
99
99
  end
100
100
 
101
101
  property "Database schema version" do
102
- ActiveRecord::Base.connection.migration_context.current_version rescue nil
102
+ ActiveRecord::Base.connection_pool.migration_context.current_version rescue nil
103
103
  end
104
104
  end
105
105
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/application_controller"
4
+ require "active_support/core_ext/enumerable"
4
5
 
5
6
  class Rails::MailersController < Rails::ApplicationController # :nodoc:
6
7
  prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATHS
@@ -9,7 +10,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
9
10
  before_action :find_preview, only: [:preview, :download]
10
11
  before_action :require_local!, unless: :show_previews?
11
12
 
12
- helper_method :part_query, :locale_query
13
+ helper_method :attachment_url, :part_query, :locale_query
13
14
 
14
15
  content_security_policy(false)
15
16
 
@@ -38,6 +39,8 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
38
39
  if @preview.email_exists?(@email_action)
39
40
  @page_title = "Mailer Preview for #{@preview.preview_name}##{@email_action}"
40
41
  @email = @preview.call(@email_action, params)
42
+ @attachments = attachments_for(@email).reject { |filename, attachment| attachment.inline? }
43
+ @inline_attachments = attachments_for(@email).select { |filename, attachment| attachment.inline? }
41
44
 
42
45
  if params[:part]
43
46
  part_type = Mime::Type.lookup(params[:part])
@@ -95,6 +98,16 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
95
98
  end
96
99
  end
97
100
 
101
+ def attachments_for(email)
102
+ email.all_parts.to_a.select(&:attachment?).index_by do |attachment|
103
+ attachment.respond_to?(:original_filename) ? attachment.original_filename : attachment.filename
104
+ end
105
+ end
106
+
107
+ def attachment_url(attachment)
108
+ "data:application/octet-stream;charset=utf-8;base64,#{Base64.encode64(attachment.body.to_s)}"
109
+ end
110
+
98
111
  def part_query(mime_type)
99
112
  request.query_parameters.merge(part: mime_type).to_query
100
113
  end
data/lib/rails/paths.rb CHANGED
@@ -105,8 +105,8 @@ module Rails
105
105
  private
106
106
  def filter_by(&block)
107
107
  all_paths.find_all(&block).flat_map { |path|
108
- paths = path.existent
109
- paths - path.children.flat_map { |p| yield(p) ? [] : p.existent }
108
+ paths = path.existent_directories
109
+ paths - path.children.flat_map { |p| yield(p) ? [] : p.existent_directories }
110
110
  }.uniq
111
111
  end
112
112
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/application_controller"
4
+
5
+ class Rails::PwaController < Rails::ApplicationController # :nodoc:
6
+ skip_forgery_protection
7
+
8
+ def service_worker
9
+ render template: "pwa/service-worker", layout: false
10
+ end
11
+
12
+ def manifest
13
+ render template: "pwa/manifest", layout: false
14
+ end
15
+ end