railties 7.1.3.2 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +175 -734
  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
@@ -2,34 +2,53 @@
2
2
  # are invoked here are part of Puma's configuration DSL. For more information
3
3
  # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
4
4
 
5
- # Puma can serve each request in a thread from an internal thread pool.
6
- # The `threads` method setting takes two numbers: a minimum and maximum.
7
- # Any libraries that use thread pools should be configured to match
8
- # the maximum value specified for Puma. Default is set to 5 threads for minimum
9
- # and maximum; this matches the default thread size of Active Record.
10
- max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
11
- min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
12
- threads min_threads_count, max_threads_count
5
+ # Puma starts a configurable number of processes (workers) and each process
6
+ # serves each request in a thread from an internal thread pool.
7
+ #
8
+ # The ideal number of threads per worker depends both on how much time the
9
+ # application spends waiting for IO operations and on how much you wish to
10
+ # to prioritize throughput over latency.
11
+ #
12
+ # As a rule of thumb, increasing the number of threads will increase how much
13
+ # traffic a given process can handle (throughput), but due to CRuby's
14
+ # Global VM Lock (GVL) it has diminishing returns and will degrade the
15
+ # response time (latency) of the application.
16
+ #
17
+ # The default is set to 3 threads as it's deemed a decent compromise between
18
+ # throughput and latency for the average Rails application.
19
+ #
20
+ # Any libraries that use a connection pool or another resource pool should
21
+ # be configured to provide at least as many connections as the number of
22
+ # threads. This includes Active Record's `pool` parameter in `database.yml`.
23
+ threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
24
+ threads threads_count, threads_count
13
25
 
14
- # Specifies that the worker count should equal the number of processors in production.
15
- if ENV["RAILS_ENV"] == "production"
26
+ # Specifies the `environment` that Puma will run in.
27
+ rails_env = ENV.fetch("RAILS_ENV", "development")
28
+ environment rails_env
29
+
30
+ case rails_env
31
+ when "production"
32
+ # If you are running more than 1 thread per process, the workers count
33
+ # should be equal to the number of processors (CPU cores) in production.
34
+ #
35
+ # Automatically detect the number of available processors in production.
16
36
  require "concurrent-ruby"
17
- worker_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count })
18
- workers worker_count if worker_count > 1
19
- end
37
+ workers_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.available_processor_count })
38
+ workers workers_count if workers_count > 1
20
39
 
21
- # Specifies the `worker_timeout` threshold that Puma will use to wait before
22
- # terminating a worker in development environments.
23
- worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
40
+ preload_app!
41
+ when "development"
42
+ # Specifies a very generous `worker_timeout` so that the worker
43
+ # isn't killed by Puma when suspended by a debugger.
44
+ worker_timeout 3600
45
+ end
24
46
 
25
47
  # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
26
- port ENV.fetch("PORT") { 3000 }
27
-
28
- # Specifies the `environment` that Puma will run in.
29
- environment ENV.fetch("RAILS_ENV") { "development" }
30
-
31
- # Specifies the `pidfile` that Puma will use.
32
- pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
48
+ port ENV.fetch("PORT", 3000)
33
49
 
34
50
  # Allow puma to be restarted by `bin/rails restart` command.
35
51
  plugin :tmp_restart
52
+
53
+ # Only use a pidfile when requested
54
+ pidfile ENV["PIDFILE"] if ENV["PIDFILE"]
@@ -5,6 +5,10 @@ Rails.application.routes.draw do
5
5
  # Can be used by load balancers and uptime monitors to verify that the app is live.
6
6
  get "up" => "rails/health#show", as: :rails_health_check
7
7
 
8
+ # Render dynamic PWA files from app/views/pwa/*
9
+ get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
10
+ get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
11
+
8
12
  # Defines the root path route ("/")
9
13
  # root "posts#index"
10
14
  end
@@ -1,5 +1,10 @@
1
1
  #!/bin/bash -e
2
2
 
3
+ # Enable jemalloc for reduced memory usage and latency.
4
+ if [ -z "${LD_PRELOAD+x}" ] && [ -f /usr/lib/*/libjemalloc.so.2 ]; then
5
+ export LD_PRELOAD="$(echo /usr/lib/*/libjemalloc.so.2)"
6
+ fi
7
+
3
8
  <% unless skip_active_record? -%>
4
9
  # If running the rails server then create or migrate existing database
5
10
  if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then
@@ -2,6 +2,7 @@
2
2
 
3
3
  # Ignore git directory.
4
4
  /.git/
5
+ /.gitignore
5
6
 
6
7
  # Ignore bundler config.
7
8
  /.bundle
@@ -41,3 +42,15 @@
41
42
  !/app/assets/builds/.keep
42
43
  /public/assets
43
44
  <% end -%>
45
+ <% unless options.skip_ci? -%>
46
+
47
+ # Ignore CI service files.
48
+ /.github
49
+ <% end -%>
50
+
51
+ # Ignore development files
52
+ /.devcontainer
53
+
54
+ # Ignore Docker-related files
55
+ /.dockerignore
56
+ /Dockerfile*
@@ -0,0 +1,138 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [ main ]
7
+
8
+ jobs:
9
+ <%- unless skip_brakeman? -%>
10
+ scan_ruby:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - name: Checkout code
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: .ruby-version
21
+ bundler-cache: true
22
+
23
+ - name: Scan for security vulnerabilities in Ruby dependencies
24
+ run: bin/brakeman --no-pager
25
+
26
+ <% end -%>
27
+ <%- if options[:javascript] == "importmap" -%>
28
+ scan_js:
29
+ runs-on: ubuntu-latest
30
+
31
+ steps:
32
+ - name: Checkout code
33
+ uses: actions/checkout@v4
34
+
35
+ - name: Set up Ruby
36
+ uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: .ruby-version
39
+ bundler-cache: true
40
+
41
+ - name: Scan for security vulnerabilities in JavaScript dependencies
42
+ run: bin/importmap audit
43
+
44
+ <% end -%>
45
+ <%- unless skip_rubocop? -%>
46
+ lint:
47
+ runs-on: ubuntu-latest
48
+ steps:
49
+ - name: Checkout code
50
+ uses: actions/checkout@v4
51
+
52
+ - name: Set up Ruby
53
+ uses: ruby/setup-ruby@v1
54
+ with:
55
+ ruby-version: .ruby-version
56
+ bundler-cache: true
57
+
58
+ - name: Lint code for consistent style
59
+ run: bin/rubocop -f github
60
+
61
+ <% end -%>
62
+ <% unless options[:skip_test] -%>
63
+ test:
64
+ runs-on: ubuntu-latest
65
+
66
+ <%- if options[:database] == "sqlite3" -%>
67
+ # services:
68
+ # redis:
69
+ # image: redis
70
+ # ports:
71
+ # - 6379:6379
72
+ # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
73
+ <%- else -%>
74
+ services:
75
+ <%- if options[:database] == "mysql" || options[:database] == "trilogy" -%>
76
+ mysql:
77
+ image: mysql
78
+ env:
79
+ MYSQL_ALLOW_EMPTY_PASSWORD: true
80
+ ports:
81
+ - 3306:3306
82
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
83
+ <%- elsif options[:database] == "postgresql" -%>
84
+ postgres:
85
+ image: postgres
86
+ env:
87
+ POSTGRES_USER: postgres
88
+ POSTGRES_PASSWORD: postgres
89
+ ports:
90
+ - 5432:5432
91
+ options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3
92
+ <%- end -%>
93
+
94
+ # redis:
95
+ # image: redis
96
+ # ports:
97
+ # - 6379:6379
98
+ # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
99
+
100
+ <%- end -%>
101
+ steps:
102
+ - name: Install packages
103
+ run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable <%= (dockerfile_base_packages + [database.base_package]).join(" ") %>
104
+
105
+ - name: Checkout code
106
+ uses: actions/checkout@v4
107
+
108
+ - name: Set up Ruby
109
+ uses: ruby/setup-ruby@v1
110
+ with:
111
+ ruby-version: .ruby-version
112
+ bundler-cache: true
113
+ <%- if using_bun? -%>
114
+
115
+ - uses: oven-sh/setup-bun@v1
116
+ with:
117
+ bun-version: <%= dockerfile_bun_version %>
118
+ <%- end -%>
119
+
120
+ - name: Run tests
121
+ env:
122
+ RAILS_ENV: test
123
+ <%- if options[:database] == "mysql" || options[:database] == "trilogy" -%>
124
+ DATABASE_URL: mysql2://127.0.0.1:3306
125
+ <%- elsif options[:database] == "postgresql" -%>
126
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432
127
+ <%- end -%>
128
+ # REDIS_URL: redis://localhost:6379/0
129
+ run: bin/rails db:test:prepare test test:system
130
+
131
+ - name: Keep screenshots from failed system tests
132
+ uses: actions/upload-artifact@v4
133
+ if: failure()
134
+ with:
135
+ name: screenshots
136
+ path: ${{ github.workspace }}/tmp/screenshots
137
+ if-no-files-found: ignore
138
+ <% end -%>
@@ -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
@@ -1,8 +1,8 @@
1
1
  # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
2
  #
3
- # If you find yourself ignoring temporary files generated by your text editor
4
- # or operating system, you probably want to add a global ignore instead:
5
- # git config --global core.excludesfile '~/.gitignore_global'
3
+ # Temporary files generated by your text editor or operating system
4
+ # belong in git's global ignore instead:
5
+ # `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore`
6
6
 
7
7
  # Ignore bundler config.
8
8
  /.bundle
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Your browser is not supported (406)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/406-unsupported-browser.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>Your browser is not supported.</h1>
62
+ <p>Please upgrade your browser to continue.</p>
63
+ </div>
64
+ </div>
65
+ </body>
66
+ </html>
@@ -0,0 +1,3 @@
1
+ <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="100%" height="100%" fill="red"/>
3
+ </svg>
@@ -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
@@ -17,7 +17,7 @@ module Rails
17
17
  def add_routes
18
18
  return if options[:skip_routes]
19
19
  return if actions.empty?
20
- routing_code = actions.map { |action| "get '#{file_name}/#{action}'" }.join("\n")
20
+ routing_code = actions.map { |action| %(get "#{file_name}/#{action}") }.join("\n")
21
21
  route routing_code, namespace: regular_class_path
22
22
  end
23
23
 
@@ -1,15 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators/base"
4
+ require "yaml"
5
+ require "json"
4
6
 
5
7
  module Rails
6
8
  module Generators
7
9
  module Db
8
10
  module System
9
11
  class ChangeGenerator < Base # :nodoc:
10
- include Database
11
12
  include AppName
12
13
 
14
+ BASE_PACKAGES = %w( curl libvips )
15
+ BUILD_PACKAGES = %w( build-essential git )
16
+
13
17
  class_option :to, required: true,
14
18
  desc: "The database system to switch to."
15
19
 
@@ -21,8 +25,8 @@ module Rails
21
25
  def initialize(*)
22
26
  super
23
27
 
24
- unless DATABASES.include?(options[:to])
25
- raise Error, "Invalid value for --to option. Supported preconfigurations are: #{DATABASES.join(", ")}."
28
+ unless Database::DATABASES.include?(options[:to])
29
+ raise Error, "Invalid value for --to option. Supported preconfigurations are: #{Database::DATABASES.join(", ")}."
26
30
  end
27
31
 
28
32
  opt = options.dup
@@ -35,7 +39,7 @@ module Rails
35
39
  end
36
40
 
37
41
  def edit_gemfile
38
- name, version = gem_for_database
42
+ name, version = database.gem
39
43
  gsub_file("Gemfile", all_database_gems_regex, name)
40
44
  gsub_file("Gemfile", gem_entry_regex_for(name), gem_entry_for(name, *version))
41
45
  end
@@ -44,27 +48,44 @@ module Rails
44
48
  dockerfile_path = File.expand_path("Dockerfile", destination_root)
45
49
  return unless File.exist?(dockerfile_path)
46
50
 
47
- build_name = docker_for_database_build
48
- deploy_name = docker_for_database_deploy
49
- if build_name
50
- gsub_file("Dockerfile", all_docker_builds_regex, build_name)
51
- end
52
- if deploy_name
53
- gsub_file("Dockerfile", all_docker_deploys_regex, deploy_name)
54
- end
51
+ gsub_file("Dockerfile", all_docker_bases_regex, docker_base_packages(database.base_package))
52
+ gsub_file("Dockerfile", all_docker_builds_regex, docker_build_packages(database.build_package))
53
+ end
54
+
55
+ def edit_devcontainer_files
56
+ return unless devcontainer?
57
+
58
+ edit_devcontainer_json
59
+ edit_compose_yaml
55
60
  end
56
61
 
57
62
  private
58
63
  def all_database_gems
59
- DATABASES.map { |database| gem_for_database(database) }
64
+ Database.all.map { |database| database.gem }
65
+ end
66
+
67
+ def all_docker_bases
68
+ Database.all.map { |database| docker_base_packages(database.base_package) }.uniq
69
+ end
70
+
71
+ def docker_base_packages(database_package)
72
+ if database_package
73
+ [database_package].concat(BASE_PACKAGES).sort
74
+ else
75
+ BASE_PACKAGES
76
+ end.join("\s")
60
77
  end
61
78
 
62
79
  def all_docker_builds
63
- DATABASES.map { |database| docker_for_database_build(database).nil? ? nil : docker_for_database_build(database) }.compact!
80
+ Database.all.map { |database| docker_build_packages(database.build_package) }.uniq
64
81
  end
65
82
 
66
- def all_docker_deploys
67
- DATABASES.map { |database| docker_for_database_deploy(database).nil? ? nil : docker_for_database_deploy(database) }.compact!
83
+ def docker_build_packages(database_package)
84
+ if database_package
85
+ [database_package].concat(BUILD_PACKAGES).sort
86
+ else
87
+ BUILD_PACKAGES
88
+ end.join("\s")
68
89
  end
69
90
 
70
91
  def all_database_gems_regex
@@ -72,12 +93,12 @@ module Rails
72
93
  /(\b#{all_database_gem_names.join('\b|\b')}\b)/
73
94
  end
74
95
 
75
- def all_docker_builds_regex
76
- /(\b#{all_docker_builds.join('\b|\b')}\b)/
96
+ def all_docker_bases_regex
97
+ /(\b#{all_docker_bases.join('\b|\b')}\b)/
77
98
  end
78
99
 
79
- def all_docker_deploys_regex
80
- /(\b#{all_docker_deploys.join('\b|\b')}\b)/
100
+ def all_docker_builds_regex
101
+ /(\b#{all_docker_builds.join('\b|\b')}\b)/
81
102
  end
82
103
 
83
104
  def gem_entry_regex_for(gem_name)
@@ -88,6 +109,96 @@ module Rails
88
109
  gem_name_and_version.map! { |segment| "\"#{segment}\"" }
89
110
  "gem #{gem_name_and_version.join(", ")}"
90
111
  end
112
+
113
+ def edit_devcontainer_json
114
+ return unless devcontainer_json
115
+
116
+ update_devcontainer_db_host
117
+ update_devcontainer_db_feature
118
+ end
119
+
120
+ def edit_compose_yaml
121
+ compose_yaml_path = File.expand_path(".devcontainer/compose.yaml", destination_root)
122
+ return unless File.exist?(compose_yaml_path)
123
+
124
+ compose_config = YAML.load_file(compose_yaml_path)
125
+
126
+ Database.all.each do |database|
127
+ compose_config["services"].delete(database.name)
128
+ compose_config["volumes"]&.delete(database.volume)
129
+ compose_config["services"]["rails-app"]["depends_on"]&.delete(database.name)
130
+ end
131
+
132
+ if database.service
133
+ compose_config["services"][database.name] = database.service
134
+ compose_config["volumes"] = { database.volume => nil }.merge(compose_config["volumes"] || {})
135
+ compose_config["services"]["rails-app"]["depends_on"] = [
136
+ database.name,
137
+ compose_config["services"]["rails-app"]["depends_on"]
138
+ ].flatten.compact
139
+ end
140
+
141
+ compose_config.delete("volumes") unless compose_config["volumes"]&.any?
142
+ compose_config["services"]["rails-app"].delete("depends_on") unless compose_config["services"]["rails-app"]["depends_on"]&.any?
143
+
144
+ File.write(compose_yaml_path, compose_config.to_yaml)
145
+ end
146
+
147
+ def update_devcontainer_db_host
148
+ container_env = devcontainer_json["containerEnv"]
149
+ db_name = database.name
150
+
151
+ if container_env["DB_HOST"]
152
+ if database.service
153
+ container_env["DB_HOST"] = db_name
154
+ else
155
+ container_env.delete("DB_HOST")
156
+ end
157
+ else
158
+ if database.service
159
+ container_env["DB_HOST"] = db_name
160
+ end
161
+ end
162
+
163
+ new_json = JSON.pretty_generate(container_env, indent: " ", object_nl: "\n ")
164
+
165
+ gsub_file(".devcontainer/devcontainer.json", /("containerEnv"\s*:\s*)(.|\n)*?(^\s{2}})/, "\\1#{new_json}")
166
+ end
167
+
168
+ def update_devcontainer_db_feature
169
+ features = devcontainer_json["features"]
170
+ db_feature = database.feature
171
+
172
+ Database.all.each do |database|
173
+ features.delete(database.feature_name)
174
+ end
175
+
176
+ features.merge!(db_feature) if db_feature
177
+
178
+ new_json = JSON.pretty_generate(features, indent: " ", object_nl: "\n ")
179
+
180
+ gsub_file(".devcontainer/devcontainer.json", /("features"\s*:\s*)(.|\n)*?(^\s{2}})/, "\\1#{new_json}")
181
+ end
182
+
183
+ def devcontainer_json
184
+ return unless File.exist?(devcontainer_json_path)
185
+
186
+ @devcontainer_json ||= JSON.parse(File.read(devcontainer_json_path))
187
+ end
188
+
189
+ def devcontainer_json_path
190
+ File.expand_path(".devcontainer/devcontainer.json", destination_root)
191
+ end
192
+
193
+ def database
194
+ @database ||= Database.build(options[:database])
195
+ end
196
+
197
+ def devcontainer?
198
+ return @devcontainer if defined?(@devcontainer)
199
+
200
+ @devcontainer = File.exist?(File.expand_path(".devcontainer", destination_root))
201
+ end
91
202
  end
92
203
  end
93
204
  end