aboutsummaryrefslogtreecommitdiffstats
path: root/railties
diff options
context:
space:
mode:
Diffstat (limited to 'railties')
-rw-r--r--railties/.gitignore1
-rw-r--r--railties/CHANGELOG.md297
-rw-r--r--railties/MIT-LICENSE2
-rw-r--r--railties/RDOC_MAIN.rdoc18
-rw-r--r--railties/README.rdoc2
-rw-r--r--railties/Rakefile61
-rw-r--r--railties/lib/minitest/rails_plugin.rb2
-rw-r--r--railties/lib/rails.rb5
-rw-r--r--railties/lib/rails/all.rb4
-rw-r--r--railties/lib/rails/api/generator.rb3
-rw-r--r--railties/lib/rails/api/task.rb16
-rw-r--r--railties/lib/rails/app_loader.rb4
-rw-r--r--railties/lib/rails/app_updater.rb2
-rw-r--r--railties/lib/rails/application.rb117
-rw-r--r--railties/lib/rails/application/configuration.rb105
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb2
-rw-r--r--railties/lib/rails/application/dummy_erb_compiler.rb19
-rw-r--r--railties/lib/rails/application/finisher.rb11
-rw-r--r--railties/lib/rails/autoloaders.rb44
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb22
-rw-r--r--railties/lib/rails/code_statistics.rb6
-rw-r--r--railties/lib/rails/command.rb19
-rw-r--r--railties/lib/rails/command/actions.rb10
-rw-r--r--railties/lib/rails/command/base.rb16
-rw-r--r--railties/lib/rails/command/behavior.rb15
-rw-r--r--railties/lib/rails/command/environment_argument.rb21
-rw-r--r--railties/lib/rails/command/spellchecker.rb4
-rw-r--r--railties/lib/rails/commands/console/console_command.rb6
-rw-r--r--railties/lib/rails/commands/credentials/USAGE20
-rw-r--r--railties/lib/rails/commands/credentials/credentials_command.rb75
-rw-r--r--railties/lib/rails/commands/db/system/change/change_command.rb20
-rw-r--r--railties/lib/rails/commands/dbconsole/dbconsole_command.rb28
-rw-r--r--railties/lib/rails/commands/dev/dev_command.rb17
-rw-r--r--railties/lib/rails/commands/encrypted/USAGE28
-rw-r--r--railties/lib/rails/commands/encrypted/encrypted_command.rb5
-rw-r--r--railties/lib/rails/commands/help/help_command.rb2
-rw-r--r--railties/lib/rails/commands/initializers/initializers_command.rb23
-rw-r--r--railties/lib/rails/commands/new/new_command.rb4
-rw-r--r--railties/lib/rails/commands/plugin/plugin_command.rb2
-rw-r--r--railties/lib/rails/commands/runner/runner_command.rb22
-rw-r--r--railties/lib/rails/commands/secrets/USAGE6
-rw-r--r--railties/lib/rails/commands/secrets/secrets_command.rb6
-rw-r--r--railties/lib/rails/commands/server/server_command.rb34
-rw-r--r--railties/lib/rails/configuration.rb8
-rw-r--r--railties/lib/rails/engine.rb49
-rw-r--r--railties/lib/rails/engine/configuration.rb4
-rw-r--r--railties/lib/rails/gem_version.rb2
-rw-r--r--railties/lib/rails/generators.rb13
-rw-r--r--railties/lib/rails/generators/actions.rb89
-rw-r--r--railties/lib/rails/generators/app_base.rb151
-rw-r--r--railties/lib/rails/generators/app_name.rb50
-rw-r--r--railties/lib/rails/generators/database.rb58
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt9
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt10
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb70
-rw-r--r--railties/lib/rails/generators/js/assets/assets_generator.rb15
-rw-r--r--railties/lib/rails/generators/js/assets/templates/javascript.js2
-rw-r--r--railties/lib/rails/generators/migration.rb3
-rw-r--r--railties/lib/rails/generators/model_helpers.rb9
-rw-r--r--railties/lib/rails/generators/named_base.rb1
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb103
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile.tt15
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt22
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js (renamed from railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt)11
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt23
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/bundle.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup.tt9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/update.tt13
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt30
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt25
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/locales/en.yml2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/gitignore.tt9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/package.json.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/ruby-version.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt11
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt6
-rw-r--r--railties/lib/rails/generators/rails/assets/USAGE5
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/javascript.js2
-rw-r--r--railties/lib/rails/generators/rails/credentials/credentials_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/db/system/change/change_generator.rb65
-rw-r--r--railties/lib/rails/generators/rails/helper/helper_generator.rb5
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb27
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt33
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/gitignore.tt2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt2
-rw-r--r--railties/lib/rails/generators/test_unit/integration/integration_generator.rb6
-rw-r--r--railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt4
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb5
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt12
-rw-r--r--railties/lib/rails/generators/test_unit/system/system_generator.rb5
-rw-r--r--railties/lib/rails/generators/testing/behaviour.rb3
-rw-r--r--railties/lib/rails/info.rb4
-rw-r--r--railties/lib/rails/info_controller.rb2
-rw-r--r--railties/lib/rails/mailers_controller.rb6
-rw-r--r--railties/lib/rails/paths.rb28
-rw-r--r--railties/lib/rails/rack/logger.rb2
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/ruby_version_check.rb6
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb2
-rw-r--r--railties/lib/rails/tasks.rb1
-rw-r--r--railties/lib/rails/tasks/annotations.rake6
-rw-r--r--railties/lib/rails/tasks/dev.rake9
-rw-r--r--railties/lib/rails/tasks/framework.rake6
-rw-r--r--railties/lib/rails/tasks/initializers.rake9
-rw-r--r--railties/lib/rails/tasks/routes.rake9
-rw-r--r--railties/lib/rails/tasks/statistics.rake4
-rw-r--r--railties/lib/rails/tasks/yarn.rake5
-rw-r--r--railties/lib/rails/templates/rails/welcome/index.html.erb2
-rw-r--r--railties/lib/rails/test_unit/reporter.rb2
-rw-r--r--railties/lib/rails/test_unit/runner.rb10
-rw-r--r--railties/lib/rails/test_unit/testing.rake2
-rw-r--r--railties/railties.gemspec9
-rw-r--r--railties/test/abstract_unit.rb20
-rw-r--r--railties/test/app_loader_test.rb10
-rw-r--r--railties/test/application/asset_debugging_test.rb2
-rw-r--r--railties/test/application/assets_test.rb24
-rw-r--r--railties/test/application/bin_setup_test.rb20
-rw-r--r--railties/test/application/configuration_test.rb702
-rw-r--r--railties/test/application/console_test.rb43
-rw-r--r--railties/test/application/credentials_test.rb56
-rw-r--r--railties/test/application/dbconsole_test.rb20
-rw-r--r--railties/test/application/initializers/frameworks_test.rb52
-rw-r--r--railties/test/application/loading_test.rb90
-rw-r--r--railties/test/application/mailer_previews_test.rb3
-rw-r--r--railties/test/application/middleware/cookies_test.rb10
-rw-r--r--railties/test/application/middleware/exceptions_test.rb2
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb4
-rw-r--r--railties/test/application/middleware/session_test.rb134
-rw-r--r--railties/test/application/middleware_test.rb19
-rw-r--r--railties/test/application/multiple_applications_test.rb28
-rw-r--r--railties/test/application/rack/logger_test.rb6
-rw-r--r--railties/test/application/rake/dbs_test.rb238
-rw-r--r--railties/test/application/rake/dev_test.rb44
-rw-r--r--railties/test/application/rake/initializers_test.rb44
-rw-r--r--railties/test/application/rake/multi_dbs_test.rb145
-rw-r--r--railties/test/application/rake/notes_test.rb1
-rw-r--r--railties/test/application/rake/routes_test.rb59
-rw-r--r--railties/test/application/rake_test.rb16
-rw-r--r--railties/test/application/runner_test.rb8
-rw-r--r--railties/test/application/server_test.rb26
-rw-r--r--railties/test/application/test_runner_test.rb138
-rw-r--r--railties/test/application/test_test.rb2
-rw-r--r--railties/test/application/url_generation_test.rb2
-rw-r--r--railties/test/application/zeitwerk_integration_test.rb371
-rw-r--r--railties/test/backtrace_cleaner_test.rb30
-rw-r--r--railties/test/code_statistics_calculator_test.rb2
-rw-r--r--railties/test/command/base_test.rb2
-rw-r--r--railties/test/commands/console_test.rb34
-rw-r--r--railties/test/commands/credentials_test.rb63
-rw-r--r--railties/test/commands/db_system_change_test.rb62
-rw-r--r--railties/test/commands/dbconsole_test.rb56
-rw-r--r--railties/test/commands/dev_test.rb65
-rw-r--r--railties/test/commands/encrypted_test.rb2
-rw-r--r--railties/test/commands/initializers_test.rb48
-rw-r--r--railties/test/commands/routes_test.rb330
-rw-r--r--railties/test/commands/server_test.rb20
-rw-r--r--railties/test/console_helpers.rb2
-rw-r--r--railties/test/engine/commands_test.rb25
-rw-r--r--railties/test/generators/actions_test.rb79
-rw-r--r--railties/test/generators/api_app_generator_test.rb6
-rw-r--r--railties/test/generators/app_generator_test.rb241
-rw-r--r--railties/test/generators/assets_generator_test.rb4
-rw-r--r--railties/test/generators/channel_generator_test.rb41
-rw-r--r--railties/test/generators/controller_generator_test.rb5
-rw-r--r--railties/test/generators/db_system_change_generator_test.rb93
-rw-r--r--railties/test/generators/generated_attribute_test.rb12
-rw-r--r--railties/test/generators/generators_test_helper.rb55
-rw-r--r--railties/test/generators/helper_generator_test.rb17
-rw-r--r--railties/test/generators/integration_test_generator_test.rb7
-rw-r--r--railties/test/generators/mailer_generator_test.rb2
-rw-r--r--railties/test/generators/migration_generator_test.rb64
-rw-r--r--railties/test/generators/model_generator_test.rb80
-rw-r--r--railties/test/generators/plugin_generator_test.rb87
-rw-r--r--railties/test/generators/resource_generator_test.rb10
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb18
-rw-r--r--railties/test/generators/scaffold_generator_test.rb79
-rw-r--r--railties/test/generators/shared_generator_tests.rb51
-rw-r--r--railties/test/generators/system_test_generator_test.rb14
-rw-r--r--railties/test/generators/test_runner_in_engine_test.rb2
-rw-r--r--railties/test/generators_test.rb1
-rw-r--r--railties/test/isolation/abstract_unit.rb82
-rw-r--r--railties/test/isolation/assets/config/webpack/development.js3
-rw-r--r--railties/test/isolation/assets/config/webpack/production.js3
-rw-r--r--railties/test/isolation/assets/config/webpack/test.js3
-rw-r--r--railties/test/isolation/assets/config/webpacker.yml8
-rw-r--r--railties/test/isolation/assets/package.json11
-rw-r--r--railties/test/path_generation_test.rb2
-rw-r--r--railties/test/rack_logger_test.rb2
-rw-r--r--railties/test/rails_info_controller_test.rb1
-rw-r--r--railties/test/railties/engine_test.rb83
-rw-r--r--railties/test/railties/mounted_engine_test.rb6
-rw-r--r--railties/test/railties/railtie_test.rb24
-rw-r--r--railties/test/secrets_test.rb16
-rw-r--r--railties/test/test_unit/reporter_test.rb10
220 files changed, 4968 insertions, 1709 deletions
diff --git a/railties/.gitignore b/railties/.gitignore
index c08562e016..17a49da08c 100644
--- a/railties/.gitignore
+++ b/railties/.gitignore
@@ -2,4 +2,5 @@
/test/500.html
/test/fixtures/tmp/
/test/initializer/root/log/
+/test/isolation/assets/yarn.lock
/tmp/
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 81cb5a6c9f..ab400e5982 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,10 +1,283 @@
+* Only force `:async` ActiveJob adapter to `:inline` during seeding.
+
+ *BatedUrGonnaDie*
+
+* The `connection` option of `rails dbconsole` command is deprecated in
+ favor of `database` option.
+
+ *Yuji Yaginuma*
+
+* Replace `chromedriver-helper` gem with `webdrivers` in default Gemfile.
+ `chromedriver-helper` is deprecated as of March 31, 2019 and won't
+ receive any further updates.
+
+ *Guillermo Iguaran‮*
+
+* Applications running in `:zeitwerk` mode that use `bootsnap` need
+ to upgrade `bootsnap` to at least 1.4.2.
+
+ *Xavier Noria*
+
+* Add `config.disable_sandbox` option to Rails console.
+
+ This setting will disable `rails console --sandbox` mode, preventing
+ developer from accidentally starting a sandbox console,
+ which when left inactive, can cause the database server to run out of memory.
+
+ *Prem Sichanugrist*
+
+* Add `-e/--environment` option to `rails initializers`.
+
+ *Yuji Yaginuma*
+
+
+## Rails 6.0.0.beta3 (March 11, 2019) ##
+
+* Generate random development secrets
+
+ A random development secret is now generated to tmp/development_secret.txt
+
+ This avoids an issue where development mode servers were vulnerable to
+ remote code execution.
+
+ Fixes CVE-2019-5420
+
+ *Eileen M. Uchitelle*, *Aaron Patterson*, *John Hawthorn*
+
+
+## Rails 6.0.0.beta2 (February 25, 2019) ##
+
+* Fix non-symbol access to nested hashes returned from `Rails::Application.config_for`
+ being broken by allowing non-symbol access with a deprecation notice.
+
+ *Ufuk Kayserilioglu*
+
+* Fix deeply nested namespace command printing.
+
+ *Gannon McGibbon*
+
+
+## Rails 6.0.0.beta1 (January 18, 2019) ##
+
+* Remove deprecated `after_bundle` helper inside plugins templates.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated support to old `config.ru` that use the application class as argument of `run`.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `environment` argument from the rails commands.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `capify!`.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `config.secret_token`.
+
+ *Rafael Mendonça França*
+
+* Seed database with inline ActiveJob job adapter.
+
+ *Gannon McGibbon*
+
+* Add `rails db:system:change` command for changing databases.
+
+ ```
+ bin/rails db:system:change --to=postgresql
+ force config/database.yml
+ gsub Gemfile
+ ```
+
+ The change command copies a template `config/database.yml` with
+ the target database adapter into your app, and replaces your database gem
+ with the target database gem.
+
+ *Gannon McGibbon*
+
+* Add `rails test:channels`.
+
+ *bogdanvlviv*
+
+* Use original `bundler` environment variables during the process of generating a new rails project.
+
+ *Marco Costa*
+
+* Send Active Storage analysis and purge jobs to dedicated queues by default.
+
+ Analysis jobs now use the `:active_storage_analysis` queue, and purge jobs
+ now use the `:active_storage_purge` queue. This matches Action Mailbox,
+ which sends its jobs to dedicated queues by default.
+
+ *George Claghorn*
+
+* Add `rails test:mailboxes`.
+
+ *George Claghorn*
+
+* Introduce guard against DNS rebinding attacks.
+
+ The `ActionDispatch::HostAuthorization` is a new middleware that prevents
+ against DNS rebinding and other `Host` header attacks. It is included in
+ the development environment by default with the following configuration:
+
+ Rails.application.config.hosts = [
+ IPAddr.new("0.0.0.0/0"), # All IPv4 addresses.
+ IPAddr.new("::/0"), # All IPv6 addresses.
+ "localhost" # The localhost reserved domain.
+ ]
+
+ In other environments `Rails.application.config.hosts` is empty and no
+ `Host` header checks will be done. If you want to guard against header
+ attacks on production, you have to manually permit the allowed hosts
+ with:
+
+ Rails.application.config.hosts << "product.com"
+
+ The host of a request is checked against the `hosts` entries with the case
+ operator (`#===`), which lets `hosts` support entries of type `RegExp`,
+ `Proc` and `IPAddr` to name a few. Here is an example with a regexp.
+
+ # Allow requests from subdomains like `www.product.com` and
+ # `beta1.product.com`.
+ Rails.application.config.hosts << /.*\.product\.com/
+
+ A special case is supported that allows you to permit all sub-domains:
+
+ # Allow requests from subdomains like `www.product.com` and
+ # `beta1.product.com`.
+ Rails.application.config.hosts << ".product.com"
+
+ *Genadi Samokovarov*
+
+* Remove redundant suffixes on generated helpers.
+
+ *Gannon McGibbon*
+
+* Remove redundant suffixes on generated integration tests.
+
+ *Gannon McGibbon*
+
+* Fix boolean interaction in scaffold system tests.
+
+ *Gannon McGibbon*
+
+* Remove redundant suffixes on generated system tests.
+
+ *Gannon McGibbon*
+
+* Add an `abort_on_failure` boolean option to the generator method that shell
+ out (`generate`, `rake`, `rails_command`) to abort the generator if the
+ command fails.
+
+ *David Rodríguez*
+
+* Remove `app/assets` and `app/javascript` from `eager_load_paths` and `autoload_paths`.
+
+ *Gannon McGibbon*
+
+* Use Ids instead of memory addresses when displaying references in scaffold views.
+
+ Fixes #29200.
+
+ *Rasesh Patel*
+
+* Adds support for multiple databases to `rails db:migrate:status`.
+ Subtasks are also added to get the status of individual databases (eg. `rails db:migrate:status:animals`).
+
+ *Gannon McGibbon*
+
+* Use Webpacker by default to manage app-level JavaScript through the new app/javascript directory.
+ Sprockets is now solely in charge, by default, of compiling CSS and other static assets.
+ Action Cable channel generators will create ES6 stubs rather than use CoffeeScript.
+ Active Storage, Action Cable, Turbolinks, and Rails-UJS are loaded by a new application.js pack.
+ Generators no longer generate JavaScript stubs.
+
+ *DHH*, *Lachlan Sylvester*
+
+* Add `database` (aliased as `db`) option to model generator to allow
+ setting the database. This is useful for applications that use
+ multiple databases and put migrations per database in their own directories.
+
+ ```
+ bin/rails g model Room capacity:integer --database=kingston
+ invoke active_record
+ create db/kingston_migrate/20180830151055_create_rooms.rb
+ ```
+
+ Because rails scaffolding uses the model generator, you can
+ also specify a database with the scaffold generator.
+
+ *Gannon McGibbon*
+
+* Raise an error when "recyclable cache keys" are being used by a cache store
+ that does not explicitly support it. Custom cache keys that do support this feature
+ can bypass this error by implementing the `supports_cache_versioning?` method on their
+ class and returning a truthy value.
+
+ *Richard Schneeman*
+
+* Support environment specific credentials overrides.
+
+ So any environment will look for `config/credentials/#{Rails.env}.yml.enc` and fall back
+ to `config/credentials.yml.enc`.
+
+ The encryption key can be in `ENV["RAILS_MASTER_KEY"]` or `config/credentials/production.key`.
+
+ Environment credentials overrides can be edited with `rails credentials:edit --environment production`.
+ If no override is set up for the passed environment, it will be created.
+
+ Additionally, the default lookup paths can be overwritten with these configs:
+
+ - `config.credentials.content_path`
+ - `config.credentials.key_path`
+
+ *Wojciech Wnętrzak*
+
+* Make `ActiveSupport::Cache::NullStore` the default cache store in the test environment.
+
+ *Michael C. Nelson*
+
+* Emit warning for unknown inflection rule when generating model.
+
+ *Yoshiyuki Kinjo*
+
+* Add `database` (aliased as `db`) option to migration generator.
+
+ If you're using multiple databases and have a folder for each database
+ for migrations (ex db/migrate and db/new_db_migrate) you can now pass the
+ `--database` option to the generator to make sure the the migration
+ is inserted into the correct folder.
+
+ ```
+ rails g migration CreateHouses --database=kingston
+ invoke active_record
+ create db/kingston_migrate/20180830151055_create_houses.rb
+ ```
+
+ *Eileen M. Uchitelle*
+
+* Deprecate `rake routes` in favor of `rails routes`.
+
+ *Yuji Yaginuma*
+
+* Deprecate `rake initializers` in favor of `rails initializers`.
+
+ *Annie-Claude Côté*
+
+* Deprecate `rake dev:cache` in favor of `rails dev:cache`.
+
+ *Annie-Claude Côté*
+
* Deprecate `rails notes` subcommands in favor of passing an `annotations` argument to `rails notes`.
The following subcommands are replaced by passing `--annotations` or `-a` to `rails notes`:
- - `rails notes:custom ANNOTATION=custom` is deprecated in favor of using `rails notes -a custom`.
- - `rails notes:optimize` is deprecated in favor of using `rails notes -a OPTIMIZE`.
- - `rails notes:todo` is deprecated in favor of using`rails notes -a TODO`.
- - `rails notes:fixme` is deprecated in favor of using `rails notes -a FIXME`.
+ - `rails notes:custom ANNOTATION=custom` is deprecated in favor of using `rails notes -a custom`.
+ - `rails notes:optimize` is deprecated in favor of using `rails notes -a OPTIMIZE`.
+ - `rails notes:todo` is deprecated in favor of using`rails notes -a TODO`.
+ - `rails notes:fixme` is deprecated in favor of using `rails notes -a FIXME`.
*Annie-Claude Côté*
@@ -17,13 +290,13 @@
*Annie-Claude Côté*
-* Don't generate unused files in `app:update` task
+* Don't generate unused files in `app:update` task.
- Skip the assets' initializer when sprockets isn't loaded.
+ Skip the assets' initializer when sprockets isn't loaded.
- Skip `config/spring.rb` when spring isn't loaded.
+ Skip `config/spring.rb` when spring isn't loaded.
- Skip yarn's contents when yarn integration isn't used.
+ Skip yarn's contents when yarn integration isn't used.
*Tsukuru Tanimichi*
@@ -44,9 +317,9 @@
*Jose Luis Duran*
-* Deprecate support for using the `HOST` environment to specify the server IP.
+* Deprecate support for using the `HOST` environment variable to specify the server IP.
- The `BINDING` environment should be used instead.
+ The `BINDING` environment variable should be used instead.
Fixes #29516.
@@ -89,9 +362,9 @@
*Benoit Tigeot*
-* Rails 6 requires Ruby 2.4.1 or newer.
+* Rails 6 requires Ruby 2.5.0 or newer.
- *Jeremy Daer*
+ *Jeremy Daer*, *Kasper Timm Hansen*
Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/railties/CHANGELOG.md) for previous changes.
diff --git a/railties/MIT-LICENSE b/railties/MIT-LICENSE
index cce00cbc3a..ea8823d7ef 100644
--- a/railties/MIT-LICENSE
+++ b/railties/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2018 David Heinemeier Hansson
+Copyright (c) 2004-2019 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc
index a4a4b6b235..f1783a4737 100644
--- a/railties/RDOC_MAIN.rdoc
+++ b/railties/RDOC_MAIN.rdoc
@@ -4,7 +4,7 @@
\Rails is a web-application framework that includes everything needed to
create database-backed web applications according to the
-{Model-View-Controller (MVC)}[http://en.wikipedia.org/wiki/Model-view-controller]
+{Model-View-Controller (MVC)}[https://en.wikipedia.org/wiki/Model-view-controller]
pattern.
Understanding the MVC pattern is key to understanding \Rails. MVC divides your
@@ -44,11 +44,11 @@ or to generate the body of an email. In \Rails, View generation is handled by {A
{Active Record}[link:files/activerecord/README_rdoc.html], {Active Model}[link:files/activemodel/README_rdoc.html],
{Action Pack}[link:files/actionpack/README_rdoc.html], and {Action View}[link:files/actionview/README_rdoc.html] can each be used independently outside \Rails.
In addition to that, \Rails also comes with {Action Mailer}[link:files/actionmailer/README_rdoc.html], a library
-to generate and send emails; {Active Job}[link:files/activejob/README_md.html], a
-framework for declaring jobs and making them run on a variety of queueing
+to generate and send emails; {Action Mailbox}[link:files/actionmailbox/README_md.html], a library to receive emails within a Rails application;
+{Active Job}[link:files/activejob/README_md.html], a framework for declaring jobs and making them run on a variety of queueing
backends; {Action Cable}[link:files/actioncable/README_md.html], a framework to
integrate WebSockets with a \Rails application; {Active Storage}[link:files/activestorage/README_md.html],
-a library to attach cloud and local files to \Rails applications;
+a library to attach cloud and local files to \Rails applications; {Action Text}[link:files/actiontext/README_md.html], a library to handle rich text content;
and {Active Support}[link:files/activesupport/README_rdoc.html], a collection
of utility classes and standard library extensions that are useful for \Rails,
and may also be used independently outside \Rails.
@@ -77,18 +77,18 @@ and may also be used independently outside \Rails.
5. Follow the guidelines to start developing your application. You may find the following resources handy:
* The \README file created within your application.
- * {Getting Started with \Rails}[http://guides.rubyonrails.org/getting_started.html].
- * {Ruby on \Rails Guides}[http://guides.rubyonrails.org].
- * {The API Documentation}[http://api.rubyonrails.org].
+ * {Getting Started with \Rails}[https://guides.rubyonrails.org/getting_started.html].
+ * {Ruby on \Rails Guides}[https://guides.rubyonrails.org].
+ * {The API Documentation}[https://api.rubyonrails.org].
* {Ruby on \Rails Tutorial}[https://www.railstutorial.org/book].
== Contributing
We encourage you to contribute to Ruby on \Rails! Please check out the
-{Contributing to Ruby on \Rails guide}[http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how to proceed. {Join us!}[http://contributors.rubyonrails.org]
+{Contributing to Ruby on \Rails guide}[https://guides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how to proceed. {Join us!}[http://contributors.rubyonrails.org]
Trying to report a possible security vulnerability in \Rails? Please
-check out our {security policy}[http://rubyonrails.org/security/] for
+check out our {security policy}[https://rubyonrails.org/security/] for
guidelines about how to proceed.
Everyone interacting in \Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the \Rails {code of conduct}[http://rubyonrails.org/conduct/].
diff --git a/railties/README.rdoc b/railties/README.rdoc
index 2715ccac3f..51437e3147 100644
--- a/railties/README.rdoc
+++ b/railties/README.rdoc
@@ -29,7 +29,7 @@ Railties is released under the MIT license:
API documentation is at
-* http://api.rubyonrails.org
+* https://api.rubyonrails.org
Bug reports can be filed for the Ruby on Rails project here:
diff --git a/railties/Rakefile b/railties/Rakefile
index e1be0ceb40..0f305ea332 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -11,6 +11,33 @@ task test: "test:isolated"
namespace :test do
task :isolated do
+ estimated_duration = {
+ "test/application/test_runner_test.rb" => 201,
+ "test/application/assets_test.rb" => 131,
+ "test/application/rake/migrations_test.rb" => 65,
+ "test/generators/scaffold_generator_test.rb" => 57,
+ "test/generators/plugin_test_runner_test.rb" => 57,
+ "test/application/test_test.rb" => 52,
+ "test/application/configuration_test.rb" => 49,
+ "test/generators/app_generator_test.rb" => 43,
+ "test/application/rake/dbs_test.rb" => 43,
+ "test/application/rake_test.rb" => 33,
+ "test/generators/plugin_generator_test.rb" => 30,
+ "test/railties/engine_test.rb" => 27,
+ "test/generators/scaffold_controller_generator_test.rb" => 23,
+ "test/railties/generators_test.rb" => 19,
+ "test/application/console_test.rb" => 16,
+ "test/engine/commands_test.rb" => 15,
+ "test/application/routing_test.rb" => 15,
+ "test/application/mailer_previews_test.rb" => 15,
+ "test/application/rake/multi_dbs_test.rb" => 13,
+ "test/application/asset_debugging_test.rb" => 12,
+ "test/application/bin_setup_test.rb" => 11,
+ "test/engine/test_test.rb" => 10,
+ "test/application/runner_test.rb" => 10,
+ }
+ estimated_duration.default = 1
+
dash_i = [
"test",
"lib",
@@ -28,15 +55,38 @@ namespace :test do
require "bundler/setup" unless defined?(Bundler)
require "active_support"
+ # Only generate the template app once.
+ require_relative "test/isolation/abstract_unit"
+
failing_files = []
dirs = (ENV["TEST_DIR"] || ENV["TEST_DIRS"] || "**").split(",")
test_options = ENV["TESTOPTS"].to_s.split(/[\s]+/)
- test_files = dirs.map { |dir| "test/#{dir}/*_test.rb" }
- Dir[*test_files].each do |file|
- next true if file.start_with?("test/fixtures/")
+ test_patterns = dirs.map { |dir| "test/#{dir}/*_test.rb" }
+ test_files = Dir[*test_patterns].select do |file|
+ !file.start_with?("test/fixtures/") && !file.start_with?("test/isolation/assets/")
+ end
+
+ if ENV["BUILDKITE_PARALLEL_JOB_COUNT"]
+ n = ENV["BUILDKITE_PARALLEL_JOB"].to_i
+ m = ENV["BUILDKITE_PARALLEL_JOB_COUNT"].to_i
+
+ buckets = Array.new(m) { [] }
+ allocations = Array.new(m) { 0 }
+ test_files.sort_by { |file| [-estimated_duration[file], file] }.each do |file|
+ idx = allocations.index(allocations.min)
+ buckets[idx] << file
+ allocations[idx] += estimated_duration[file]
+ end
+
+ puts "Running #{buckets[n].size} of #{test_files.size} test files, estimated duration #{allocations[n]}s"
+
+ test_files = buckets[n]
+ end
+ test_files.each do |file|
+ puts "--- #{file}"
fake_command = Shellwords.join([
FileUtils::RUBY,
"-w",
@@ -56,10 +106,13 @@ namespace :test do
unless $?.success?
failing_files << file
+ puts "^^^ +++"
end
end
+ puts "--- All tests completed"
unless failing_files.empty?
+ puts "^^^ +++"
puts
puts "Failed in:"
failing_files.each do |file|
@@ -67,7 +120,7 @@ namespace :test do
end
puts
- raise "Failure in isolated test runner"
+ exit 1
end
end
end
diff --git a/railties/lib/minitest/rails_plugin.rb b/railties/lib/minitest/rails_plugin.rb
index 6486fa1798..4b7df6360a 100644
--- a/railties/lib/minitest/rails_plugin.rb
+++ b/railties/lib/minitest/rails_plugin.rb
@@ -54,6 +54,6 @@ module Minitest
end
end
- # Backwardscompatibility with Rails 5.0 generated plugin test scripts
+ # Backwards compatibility with Rails 5.0 generated plugin test scripts
mattr_reader :run_via, default: {}
end
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 092105d502..1f533a8c04 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -13,6 +13,7 @@ require "active_support/core_ext/object/blank"
require "rails/application"
require "rails/version"
+require "rails/autoloaders"
require "active_support/railtie"
require "action_dispatch/railtie"
@@ -110,5 +111,9 @@ module Rails
def public_path
application && Pathname.new(application.paths["public"].first)
end
+
+ def autoloaders
+ Autoloaders
+ end
end
end
diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb
index f5dccd2381..da810f1eed 100644
--- a/railties/lib/rails/all.rb
+++ b/railties/lib/rails/all.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+# rubocop:disable Style/RedundantBegin
+
require "rails"
%w(
@@ -10,6 +12,8 @@ require "rails"
action_mailer/railtie
active_job/railtie
action_cable/engine
+ action_mailbox/engine
+ action_text/engine
rails/test_unit/railtie
sprockets/railtie
).each do |railtie|
diff --git a/railties/lib/rails/api/generator.rb b/railties/lib/rails/api/generator.rb
index 3405560b74..126d4d0438 100644
--- a/railties/lib/rails/api/generator.rb
+++ b/railties/lib/rails/api/generator.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "sdoc"
+require "active_support/core_ext/array/extract"
class RDoc::Generator::API < RDoc::Generator::SDoc # :nodoc:
RDoc::RDoc.add_generator self
@@ -11,7 +12,7 @@ class RDoc::Generator::API < RDoc::Generator::SDoc # :nodoc:
# since they aren't nested under a definition of the `ActiveStorage` module.
if visited.empty?
classes = classes.reject { |klass| active_storage?(klass) }
- core_exts, classes = classes.partition { |klass| core_extension?(klass) }
+ core_exts = classes.extract! { |klass| core_extension?(klass) }
super.unshift([ "Core extensions", "", "", build_core_ext_subtree(core_exts, visited) ])
else
diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb
index e7f0557584..d5312843e4 100644
--- a/railties/lib/rails/api/task.rb
+++ b/railties/lib/rails/api/task.rb
@@ -74,6 +74,22 @@ module Rails
)
},
+ "actionmailbox" => {
+ include: %w(
+ README.md
+ app/**/action_mailbox/**/*.rb
+ lib/action_mailbox/**/*.rb
+ )
+ },
+
+ "actiontext" => {
+ include: %w(
+ README.md
+ app/**/action_text/**/*.rb
+ lib/action_text/**/*.rb
+ )
+ },
+
"railties" => {
include: %w(
README.rdoc
diff --git a/railties/lib/rails/app_loader.rb b/railties/lib/rails/app_loader.rb
index 20eb75d95c..cc057a407d 100644
--- a/railties/lib/rails/app_loader.rb
+++ b/railties/lib/rails/app_loader.rb
@@ -23,7 +23,7 @@ control:
# too that you may or may not want (like yarn)
If you already have Rails binstubs in source control, you might be
-inadverently overwriting them during deployment by using bundle install
+inadvertently overwriting them during deployment by using bundle install
with the --binstubs option.
If your application was created prior to Rails 4, here's how to upgrade:
@@ -49,7 +49,7 @@ EOS
if exe = find_executable
contents = File.read(exe)
- if contents =~ /(APP|ENGINE)_PATH/
+ if /(APP|ENGINE)_PATH/.match?(contents)
exec RUBY, exe, *ARGV
break # non reachable, hack to be able to stub exec in the test suite
elsif exe.end_with?("bin/rails") && contents.include?("This file was generated by Bundler")
diff --git a/railties/lib/rails/app_updater.rb b/railties/lib/rails/app_updater.rb
index a243968a39..19d136e041 100644
--- a/railties/lib/rails/app_updater.rb
+++ b/railties/lib/rails/app_updater.rb
@@ -21,7 +21,7 @@ module Rails
private
def generator_options
options = { api: !!Rails.application.config.api_only, update: true }
- options[:skip_yarn] = !File.exist?(Rails.root.join("bin", "yarn"))
+ options[:skip_javascript] = !File.exist?(Rails.root.join("bin", "yarn"))
options[:skip_active_record] = !defined?(ActiveRecord::Railtie)
options[:skip_active_storage] = !defined?(ActiveStorage::Engine) || !defined?(ActiveRecord::Railtie)
options[:skip_action_mailer] = !defined?(ActionMailer::Railtie)
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index e346d5cc3a..038284ebdd 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -7,6 +7,7 @@ require "active_support/key_generator"
require "active_support/message_verifier"
require "active_support/encrypted_configuration"
require "active_support/deprecation"
+require "active_support/hash_with_indifferent_access"
require "rails/engine"
require "rails/secrets"
@@ -172,14 +173,9 @@ module Rails
def key_generator
# number of iterations selected based on consultation with the google security
# team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
- @caching_key_generator ||=
- if secret_key_base
- ActiveSupport::CachingKeyGenerator.new(
- ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
- )
- else
- ActiveSupport::LegacyKeyGenerator.new(secrets.secret_token)
- end
+ @caching_key_generator ||= ActiveSupport::CachingKeyGenerator.new(
+ ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
+ )
end
# Returns a message verifier object.
@@ -232,7 +228,12 @@ module Rails
if yaml.exist?
require "erb"
- (YAML.load(ERB.new(yaml.read).result) || {})[env] || {}
+ config = YAML.load(ERB.new(yaml.read).result) || {}
+ config = (config["shared"] || {}).merge(config[env] || {})
+
+ ActiveSupport::OrderedOptions.new.tap do |options|
+ options.update(NonSymbolAccessDeprecatedHash.new(config))
+ end
else
raise "Could not load configuration. No such file - #{yaml}"
end
@@ -249,7 +250,6 @@ module Rails
super.merge(
"action_dispatch.parameter_filter" => config.filter_parameters,
"action_dispatch.redirect_filter" => config.filter_redirect,
- "action_dispatch.secret_token" => secrets.secret_token,
"action_dispatch.secret_key_base" => secret_key_base,
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
@@ -267,6 +267,7 @@ module Rails
"action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer,
"action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest,
"action_dispatch.cookies_rotations" => config.action_dispatch.cookies_rotations,
+ "action_dispatch.use_cookies_with_metadata" => config.action_dispatch.use_cookies_with_metadata,
"action_dispatch.content_security_policy" => config.content_security_policy,
"action_dispatch.content_security_policy_report_only" => config.content_security_policy_report_only,
"action_dispatch.content_security_policy_nonce_generator" => config.content_security_policy_nonce_generator
@@ -373,9 +374,7 @@ module Rails
@config ||= Application::Configuration.new(self.class.find_root(self.class.called_from))
end
- def config=(configuration) #:nodoc:
- @config = configuration
- end
+ attr_writer :config
# Returns secrets added to config/secrets.yml.
#
@@ -400,34 +399,25 @@ module Rails
# Fallback to config.secret_key_base if secrets.secret_key_base isn't set
secrets.secret_key_base ||= config.secret_key_base
- # Fallback to config.secret_token if secrets.secret_token isn't set
- secrets.secret_token ||= config.secret_token
-
- if secrets.secret_token.present?
- ActiveSupport::Deprecation.warn(
- "`secrets.secret_token` is deprecated in favor of `secret_key_base` and will be removed in Rails 6.0."
- )
- end
secrets
end
end
- def secrets=(secrets) #:nodoc:
- @secrets = secrets
- end
+ attr_writer :secrets
# The secret_key_base is used as the input secret to the application's key generator, which in turn
# is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
#
- # In test and development, this is simply derived as a MD5 hash of the application's name.
+ # In development and test, this is randomly generated and stored in a
+ # temporary file in <tt>tmp/development_secret.txt</tt>.
#
# In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
# then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
# the correct place to store it is in the encrypted credentials file.
def secret_key_base
- if Rails.env.test? || Rails.env.development?
- secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
+ if Rails.env.development? || Rails.env.test?
+ secrets.secret_key_base ||= generate_development_secret
else
validate_secret_key_base(
ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
@@ -438,13 +428,17 @@ module Rails
# Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with
# the Rails master key, which is either taken from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading
# +config/master.key+.
+ # If specific credentials file exists for current environment, it takes precedence, thus for +production+
+ # environment look first for +config/credentials/production.yml.enc+ with master key taken
+ # from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading +config/credentials/production.key+.
+ # Default behavior can be overwritten by setting +config.credentials.content_path+ and +config.credentials.key_path+.
def credentials
- @credentials ||= encrypted("config/credentials.yml.enc")
+ @credentials ||= encrypted(config.credentials.content_path, key_path: config.credentials.key_path)
end
# Shorthand to decrypt any encrypted configurations or files.
#
- # For any file added with <tt>bin/rails encrypted:edit</tt> call +read+ to decrypt
+ # For any file added with <tt>rails encrypted:edit</tt> call +read+ to decrypt
# the file with the master key.
# The master key is either stored in +config/master.key+ or <tt>ENV["RAILS_MASTER_KEY"]</tt>.
#
@@ -581,13 +575,29 @@ module Rails
secret_key_base
elsif secret_key_base
raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String`"
- elsif secrets.secret_token.blank?
+ else
raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `rails credentials:edit`"
end
end
private
+ def generate_development_secret
+ if secrets.secret_key_base.nil?
+ key_file = Rails.root.join("tmp/development_secret.txt")
+
+ if !File.exist?(key_file)
+ random_key = SecureRandom.hex(64)
+ FileUtils.mkdir_p(key_file.dirname)
+ File.binwrite(key_file, random_key)
+ end
+
+ secrets.secret_key_base = File.binread(key_file)
+ end
+
+ secrets.secret_key_base
+ end
+
def build_request(env)
req = super
env["ORIGINAL_FULLPATH"] = req.fullpath
@@ -598,5 +608,52 @@ module Rails
def build_middleware
config.app_middleware + super
end
+
+ class NonSymbolAccessDeprecatedHash < HashWithIndifferentAccess # :nodoc:
+ def initialize(value = nil)
+ if value.is_a?(Hash)
+ value.each_pair { |k, v| self[k] = v }
+ else
+ super
+ end
+ end
+
+ def []=(key, value)
+ regular_writer(key.to_sym, convert_value(value, for: :assignment))
+ end
+
+ private
+
+ def convert_key(key)
+ unless key.kind_of?(Symbol)
+ ActiveSupport::Deprecation.warn(<<~MESSAGE.squish)
+ Accessing hashes returned from config_for by non-symbol keys
+ is deprecated and will be removed in Rails 6.1.
+ Use symbols for access instead.
+ MESSAGE
+
+ key = key.to_sym
+ end
+
+ key
+ end
+
+ def convert_value(value, options = {}) # :doc:
+ if value.is_a? Hash
+ if options[:for] == :to_hash
+ value.to_hash
+ else
+ self.class.new(value)
+ end
+ elsif value.is_a?(Array)
+ if options[:for] != :assignment || value.frozen?
+ value = value.dup
+ end
+ value.map! { |e| convert_value(e, options) }
+ else
+ value
+ end
+ end
+ end
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index bba573499d..b79dbdbc6f 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+require "ipaddr"
require "active_support/core_ext/kernel/reporting"
require "active_support/file_update_checker"
require "rails/engine/configuration"
@@ -11,15 +12,16 @@ module Rails
attr_accessor :allow_concurrency, :asset_host, :autoflush_log,
:cache_classes, :cache_store, :consider_all_requests_local, :console,
:eager_load, :exceptions_app, :file_watcher, :filter_parameters,
- :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
- :railties_order, :relative_url_root, :secret_key_base, :secret_token,
+ :force_ssl, :helpers_paths, :hosts, :logger, :log_formatter, :log_tags,
+ :railties_order, :relative_url_root, :secret_key_base,
:ssl_options, :public_file_server,
:session_options, :time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x, :enable_dependency_loading,
:read_encrypted_secrets, :log_level, :content_security_policy_report_only,
- :content_security_policy_nonce_generator, :require_master_key
+ :content_security_policy_nonce_generator, :require_master_key, :credentials,
+ :disable_sandbox
- attr_reader :encoding, :api_only, :loaded_config_version
+ attr_reader :encoding, :api_only, :loaded_config_version, :autoloader
def initialize(*)
super
@@ -29,6 +31,7 @@ module Rails
@filter_parameters = []
@filter_redirect = []
@helpers_paths = []
+ @hosts = Array(([IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0"), ".localhost"] if Rails.env.development?))
@public_file_server = ActiveSupport::OrderedOptions.new
@public_file_server.enabled = true
@public_file_server.index_name = "index"
@@ -48,7 +51,6 @@ module Rails
@autoflush_log = true
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
@eager_load = nil
- @secret_token = nil
@secret_key_base = nil
@api_only = false
@debug_exception_response_format = nil
@@ -60,6 +62,11 @@ module Rails
@content_security_policy_nonce_generator = nil
@require_master_key = false
@loaded_config_version = nil
+ @credentials = ActiveSupport::OrderedOptions.new
+ @credentials.content_path = default_credentials_content_path
+ @credentials.key_path = default_credentials_key_path
+ @autoloader = :classic
+ @disable_sandbox = false
end
def load_defaults(target_version)
@@ -92,10 +99,6 @@ module Rails
if respond_to?(:active_record)
active_record.cache_versioning = true
- # Remove the temporary load hook from SQLite3Adapter when this is removed
- ActiveSupport.on_load(:active_record_sqlite3adapter) do
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = true
- end
end
if respond_to?(:action_dispatch)
@@ -117,9 +120,28 @@ module Rails
when "6.0"
load_defaults "5.2"
+ self.autoloader = :zeitwerk if RUBY_ENGINE == "ruby"
+
if respond_to?(:action_view)
action_view.default_enforce_utf8 = false
end
+
+ if respond_to?(:action_dispatch)
+ action_dispatch.use_cookies_with_metadata = true
+ end
+
+ if respond_to?(:action_mailer)
+ action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
+ end
+
+ if respond_to?(:active_job)
+ active_job.return_false_on_aborted_enqueue = true
+ end
+
+ if respond_to?(:active_storage)
+ active_storage.queues.analysis = :active_storage_analysis
+ active_storage.queues.purge = :active_storage_purge
+ end
else
raise "Unknown version #{target_version.to_s.inspect}"
end
@@ -146,9 +168,7 @@ module Rails
@debug_exception_response_format || :default
end
- def debug_exception_response_format=(value)
- @debug_exception_response_format = value
- end
+ attr_writer :debug_exception_response_format
def paths
@paths ||= begin
@@ -166,16 +186,24 @@ module Rails
end
end
- # Loads the database YAML without evaluating ERB. People seem to
- # write ERB that makes the database configuration depend on
- # Rails configuration. But we want Rails configuration (specifically
- # `rake` and `rails` tasks) to be generated based on information in
- # the database yaml, so we need a method that loads the database
- # yaml *without* the context of the Rails application.
+ # Load the database YAML without evaluating ERB. This allows us to
+ # create the rake tasks for multiple databases without filling in the
+ # configuration values or loading the environment. Do not use this
+ # method.
+ #
+ # This uses a DummyERB custom compiler so YAML can ignore the ERB
+ # tags and load the database.yml for the rake tasks.
def load_database_yaml # :nodoc:
- path = paths["config/database"].existent.first
- return {} unless path
- YAML.load_file(path.to_s)
+ if path = paths["config/database"].existent.first
+ require "rails/application/dummy_erb_compiler"
+
+ yaml = Pathname.new(path)
+ erb = DummyERB.new(yaml.read)
+
+ YAML.load(erb.result)
+ else
+ {}
+ end
end
# Loads and returns the entire raw configuration of database from
@@ -209,7 +237,7 @@ module Rails
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
"Error: #{e.message}"
rescue => e
- raise e, "Cannot load `Rails.application.database_configuration`:\n#{e.message}", e.backtrace
+ raise e, "Cannot load database configuration:\n#{e.message}", e.backtrace
end
def colorize_logging
@@ -264,6 +292,18 @@ module Rails
end
end
+ def autoloader=(autoloader)
+ case autoloader
+ when :classic
+ @autoloader = autoloader
+ when :zeitwerk
+ require "zeitwerk"
+ @autoloader = autoloader
+ else
+ raise ArgumentError, "config.autoloader may be :classic or :zeitwerk, got #{autoloader.inspect} instead"
+ end
+ end
+
class Custom #:nodoc:
def initialize
@configurations = Hash.new
@@ -283,6 +323,27 @@ module Rails
true
end
end
+
+ private
+ def default_credentials_content_path
+ if credentials_available_for_current_env?
+ root.join("config", "credentials", "#{Rails.env}.yml.enc")
+ else
+ root.join("config", "credentials.yml.enc")
+ end
+ end
+
+ def default_credentials_key_path
+ if credentials_available_for_current_env?
+ root.join("config", "credentials", "#{Rails.env}.key")
+ else
+ root.join("config", "master.key")
+ end
+ end
+
+ def credentials_available_for_current_env?
+ File.exist?(root.join("config", "credentials", "#{Rails.env}.yml.enc"))
+ end
end
end
end
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index 433a7ab41f..193cc59f3a 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -13,6 +13,8 @@ module Rails
def build_stack
ActionDispatch::MiddlewareStack.new do |middleware|
+ middleware.use ::ActionDispatch::HostAuthorization, config.hosts, config.action_dispatch.hosts_response_app
+
if config.force_ssl
middleware.use ::ActionDispatch::SSL, config.ssl_options
end
diff --git a/railties/lib/rails/application/dummy_erb_compiler.rb b/railties/lib/rails/application/dummy_erb_compiler.rb
new file mode 100644
index 0000000000..c4659123bb
--- /dev/null
+++ b/railties/lib/rails/application/dummy_erb_compiler.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# These classes are used to strip out the ERB configuration
+# values so we can evaluate the database.yml without evaluating
+# the ERB values.
+class DummyERB < ERB # :nodoc:
+ def make_compiler(trim_mode)
+ DummyCompiler.new trim_mode
+ end
+end
+
+class DummyCompiler < ERB::Compiler # :nodoc:
+ def compile_content(stag, out)
+ case stag
+ when "<%="
+ out.push "_erbout << 'dummy_compiler'"
+ end
+ end
+end
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index 04aaf6dd9a..1cf44a480c 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -21,6 +21,13 @@ module Rails
end
end
+ initializer :let_zeitwerk_take_over do
+ if config.autoloader == :zeitwerk
+ require "active_support/dependencies/zeitwerk_integration"
+ ActiveSupport::Dependencies::ZeitwerkIntegration.take_over(enable_reloading: !config.cache_classes)
+ end
+ end
+
initializer :add_builtin_route do |app|
if Rails.env.development?
app.routes.prepend do
@@ -66,6 +73,10 @@ module Rails
initializer :eager_load! do
if config.eager_load
ActiveSupport.run_load_hooks(:before_eager_load, self)
+ # Checks defined?(Zeitwerk) instead of zeitwerk_enabled? because we
+ # want to eager load any dependency managed by Zeitwerk regardless of
+ # the autoloading mode of the application.
+ Zeitwerk::Loader.eager_load_all if defined?(Zeitwerk)
config.eager_load_namespaces.each(&:eager_load!)
end
end
diff --git a/railties/lib/rails/autoloaders.rb b/railties/lib/rails/autoloaders.rb
new file mode 100644
index 0000000000..1bd3f18a74
--- /dev/null
+++ b/railties/lib/rails/autoloaders.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require "active_support/dependencies/zeitwerk_integration"
+
+module Rails
+ module Autoloaders # :nodoc:
+ class << self
+ include Enumerable
+
+ def main
+ if zeitwerk_enabled?
+ @main ||= Zeitwerk::Loader.new.tap do |loader|
+ loader.tag = "rails.main"
+ loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
+ end
+ end
+ end
+
+ def once
+ if zeitwerk_enabled?
+ @once ||= Zeitwerk::Loader.new.tap do |loader|
+ loader.tag = "rails.once"
+ loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
+ end
+ end
+ end
+
+ def each
+ if zeitwerk_enabled?
+ yield main
+ yield once
+ end
+ end
+
+ def logger=(logger)
+ each { |loader| loader.logger = logger }
+ end
+
+ def zeitwerk_enabled?
+ Rails.configuration.autoloader == :zeitwerk
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
index ae8db0f8ef..7c2eb1dc42 100644
--- a/railties/lib/rails/backtrace_cleaner.rb
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -5,30 +5,18 @@ require "active_support/backtrace_cleaner"
module Rails
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
APP_DIRS_PATTERN = /^\/?(app|config|lib|test|\(\w*\))/
- RENDER_TEMPLATE_PATTERN = /:in `_render_template_\w*'/
- EMPTY_STRING = "".freeze
- SLASH = "/".freeze
- DOT_SLASH = "./".freeze
+ RENDER_TEMPLATE_PATTERN = /:in `.*_\w+_{2,3}\d+_\d+'/
+ EMPTY_STRING = ""
+ SLASH = "/"
+ DOT_SLASH = "./"
def initialize
super
- @root = "#{Rails.root}/".freeze
+ @root = "#{Rails.root}/"
add_filter { |line| line.sub(@root, EMPTY_STRING) }
add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, EMPTY_STRING) }
add_filter { |line| line.sub(DOT_SLASH, SLASH) } # for tests
-
- add_gem_filters
add_silencer { |line| !APP_DIRS_PATTERN.match?(line) }
end
-
- private
- def add_gem_filters
- gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
- return if gems_paths.empty?
-
- gems_regexp = %r{(#{gems_paths.join('|')})/gems/([^/]+)-([\w.]+)/(.*)}
- gems_result = '\2 (\3) \4'.freeze
- add_filter { |line| line.sub(gems_regexp, gems_result) }
- end
end
end
diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb
index 9c447c366f..09082282f3 100644
--- a/railties/lib/rails/code_statistics.rb
+++ b/railties/lib/rails/code_statistics.rb
@@ -46,7 +46,7 @@ class CodeStatistics #:nodoc:
if File.directory?(path) && (/^\./ !~ file_name)
stats.add(calculate_directory_statistics(path, pattern))
- elsif file_name =~ pattern
+ elsif file_name&.match?(pattern)
stats.add_by_file_path(path)
end
end
@@ -95,8 +95,8 @@ class CodeStatistics #:nodoc:
end
def print_line(name, statistics)
- m_over_c = (statistics.methods / statistics.classes) rescue m_over_c = 0
- loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue loc_over_m = 0
+ m_over_c = (statistics.methods / statistics.classes) rescue 0
+ loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue 0
print "| #{name.ljust(20)} "
HEADERS.each_key do |k|
diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb
index 6d99ac9936..f09aa3ae0d 100644
--- a/railties/lib/rails/command.rb
+++ b/railties/lib/rails/command.rb
@@ -83,20 +83,21 @@ module Rails
end
def print_commands # :nodoc:
- sorted_groups.each { |b, n| print_list(b, n) }
+ commands.each { |command| puts(" #{command}") }
end
- def sorted_groups # :nodoc:
- lookup!
+ private
+ COMMANDS_IN_USAGE = %w(generate console server test test:system dbconsole new)
+ private_constant :COMMANDS_IN_USAGE
- groups = (subclasses - hidden_commands).group_by { |c| c.namespace.split(":").first }
- groups.transform_values! { |commands| commands.flat_map(&:printing_commands).sort }
+ def commands
+ lookup!
- rails = groups.delete("rails")
- [[ "rails", rails ]] + groups.sort.to_a
- end
+ visible_commands = (subclasses - hidden_commands).flat_map(&:printing_commands)
+
+ (visible_commands - COMMANDS_IN_USAGE).sort
+ end
- private
def command_type # :doc:
@command_type ||= "command"
end
diff --git a/railties/lib/rails/command/actions.rb b/railties/lib/rails/command/actions.rb
index cbb743346b..50651ad61a 100644
--- a/railties/lib/rails/command/actions.rb
+++ b/railties/lib/rails/command/actions.rb
@@ -11,10 +11,20 @@ module Rails
end
def require_application_and_environment!
+ require_application!
+ require_environment!
+ end
+
+ def require_application!
require ENGINE_PATH if defined?(ENGINE_PATH)
if defined?(APP_PATH)
require APP_PATH
+ end
+ end
+
+ def require_environment!
+ if defined?(APP_PATH)
Rails.application.require_environment!
end
end
diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb
index fa462ef7e9..a22b198c66 100644
--- a/railties/lib/rails/command/base.rb
+++ b/railties/lib/rails/command/base.rb
@@ -70,7 +70,7 @@ module Rails
end
def executable
- "bin/rails #{command_name}"
+ "rails #{command_name}"
end
# Use Rails' default banner.
@@ -115,7 +115,7 @@ module Rails
# For a Rails::Command::TestCommand placed in <tt>rails/command/test_command.rb</tt>
# would return <tt>rails/test</tt>.
def default_command_root
- path = File.expand_path(File.join("../commands", command_root_namespace), __dir__)
+ path = File.expand_path(relative_command_path, __dir__)
path if File.exist?(path)
end
@@ -135,12 +135,20 @@ module Rails
end
def command_root_namespace
- (namespace.split(":") - %w( rails )).first
+ (namespace.split(":") - %w(rails)).join(":")
+ end
+
+ def relative_command_path
+ File.join("../commands", *command_root_namespace.split(":"))
end
def namespaced_commands
commands.keys.map do |key|
- key == command_root_namespace ? key : "#{command_root_namespace}:#{key}"
+ if command_root_namespace.match?(/(\A|\:)#{key}\z/)
+ command_root_namespace
+ else
+ "#{command_root_namespace}:#{key}"
+ end
end
end
end
diff --git a/railties/lib/rails/command/behavior.rb b/railties/lib/rails/command/behavior.rb
index 718e2d9ab2..7fb2a99e67 100644
--- a/railties/lib/rails/command/behavior.rb
+++ b/railties/lib/rails/command/behavior.rb
@@ -56,12 +56,10 @@ module Rails
def lookup!
$LOAD_PATH.each do |base|
Dir[File.join(base, *file_lookup_paths)].each do |path|
- begin
- path = path.sub("#{base}/", "")
- require path
- rescue Exception
- # No problem
- end
+ path = path.sub("#{base}/", "")
+ require path
+ rescue Exception
+ # No problem
end
end
end
@@ -73,8 +71,9 @@ module Rails
paths = []
namespaces.each do |namespace|
pieces = namespace.split(":")
- paths << pieces.dup.push(pieces.last).join("/")
- paths << pieces.join("/")
+ path = pieces.join("/")
+ paths << "#{path}/#{pieces.last}"
+ paths << path
end
paths.uniq!
paths
diff --git a/railties/lib/rails/command/environment_argument.rb b/railties/lib/rails/command/environment_argument.rb
index 5dc98b113d..0cb3f1ce1e 100644
--- a/railties/lib/rails/command/environment_argument.rb
+++ b/railties/lib/rails/command/environment_argument.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support"
+require "active_support/core_ext/class/attribute"
module Rails
module Command
@@ -8,26 +9,16 @@ module Rails
extend ActiveSupport::Concern
included do
- argument :environment, optional: true, banner: "environment"
-
- class_option :environment, aliases: "-e", type: :string,
- desc: "Specifies the environment to run this console under (test/development/production)."
+ class_attribute :environment_desc, default: "Specifies the environment to run this #{self.command_name} under (test/development/production)."
+ class_option :environment, aliases: "-e", type: :string, desc: environment_desc
end
private
- def extract_environment_option_from_argument
- if environment
- self.options = options.merge(environment: acceptable_environment(environment))
-
- ActiveSupport::Deprecation.warn "Passing the environment's name as a " \
- "regular argument is deprecated and " \
- "will be removed in the next Rails " \
- "version. Please, use the -e option " \
- "instead."
- elsif options[:environment]
+ def extract_environment_option_from_argument(default_environment: Rails::Command.environment)
+ if options[:environment]
self.options = options.merge(environment: acceptable_environment(options[:environment]))
else
- self.options = options.merge(environment: Rails::Command.environment)
+ self.options = options.merge(environment: default_environment)
end
end
diff --git a/railties/lib/rails/command/spellchecker.rb b/railties/lib/rails/command/spellchecker.rb
index 04485097fa..085d5b16df 100644
--- a/railties/lib/rails/command/spellchecker.rb
+++ b/railties/lib/rails/command/spellchecker.rb
@@ -24,8 +24,8 @@ module Rails
n = s.length
m = t.length
- return m if (0 == n)
- return n if (0 == m)
+ return m if 0 == n
+ return n if 0 == m
d = (0..m).to_a
x = nil
diff --git a/railties/lib/rails/commands/console/console_command.rb b/railties/lib/rails/commands/console/console_command.rb
index e35faa5b01..7a9eaefea1 100644
--- a/railties/lib/rails/commands/console/console_command.rb
+++ b/railties/lib/rails/commands/console/console_command.rb
@@ -26,6 +26,12 @@ module Rails
@options = options
app.sandbox = sandbox?
+
+ if sandbox? && app.config.disable_sandbox
+ puts "Error: Unable to start console in sandbox mode as sandbox mode is disabled (config.disable_sandbox is true)."
+ exit 1
+ end
+
app.load_console
@console = app.config.console || IRB
diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE
index 85877c71b7..c8d3fb9eda 100644
--- a/railties/lib/rails/commands/credentials/USAGE
+++ b/railties/lib/rails/commands/credentials/USAGE
@@ -14,7 +14,7 @@ that just contains the secret_key_base used by MessageVerifiers/MessageEncryptor
signing and encrypting cookies.
For applications created prior to Rails 5.2, we'll automatically generate a new
-credentials file in `config/credentials.yml.enc` the first time you run `bin/rails credentials:edit`.
+credentials file in `config/credentials.yml.enc` the first time you run `rails credentials:edit`.
If you didn't have a master key saved in `config/master.key`, that'll be created too.
Don't lose this master key! Put it in a password manager your team can access.
@@ -38,3 +38,21 @@ the encrypted credentials.
When the temporary file is next saved the contents are encrypted and written to
`config/credentials.yml.enc` while the file itself is destroyed to prevent credentials
from leaking.
+
+=== Environment Specific Credentials
+
+The `credentials` command supports passing an `--environment` option to create an
+environment specific override. That override will take precedence over the
+global `config/credentials.yml.enc` file when running in that environment. So:
+
+ rails credentials:edit --environment development
+
+will create `config/credentials/development.yml.enc` with the corresponding
+encryption key in `config/credentials/development.key` if the credentials file
+doesn't exist.
+
+The encryption key can also be put in `ENV["RAILS_MASTER_KEY"]`, which takes
+precedence over the file encryption key.
+
+In addition to that, the default credentials lookup paths can be overridden through
+`config.credentials.content_path` and `config.credentials.key_path`.
diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb
index fa54c0362a..e23a1b3008 100644
--- a/railties/lib/rails/commands/credentials/credentials_command.rb
+++ b/railties/lib/rails/commands/credentials/credentials_command.rb
@@ -2,11 +2,15 @@
require "active_support"
require "rails/command/helpers/editor"
+require "rails/command/environment_argument"
module Rails
module Command
class CredentialsCommand < Rails::Command::Base # :nodoc:
include Helpers::Editor
+ include EnvironmentArgument
+
+ self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
no_commands do
def help
@@ -17,47 +21,84 @@ module Rails
end
def edit
- require_application_and_environment!
+ extract_environment_option_from_argument(default_environment: nil)
+ require_application!
ensure_editor_available(command: "bin/rails credentials:edit") || (return)
- ensure_master_key_has_been_added if Rails.application.credentials.key.nil?
+
+ ensure_encryption_key_has_been_added if credentials.key.nil?
ensure_credentials_have_been_added
catch_editing_exceptions do
change_credentials_in_system_editor
end
- say "New credentials encrypted and saved."
+ say "File encrypted and saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
end
def show
- require_application_and_environment!
+ extract_environment_option_from_argument(default_environment: nil)
+ require_application!
- say Rails.application.credentials.read.presence || missing_credentials_message
+ say credentials.read.presence || missing_credentials_message
end
private
- def ensure_master_key_has_been_added
- master_key_generator.add_master_key_file
- master_key_generator.ignore_master_key_file
+ def credentials
+ Rails.application.encrypted(content_path, key_path: key_path)
+ end
+
+ def ensure_encryption_key_has_been_added
+ encryption_key_file_generator.add_key_file(key_path)
+ encryption_key_file_generator.ignore_key_file(key_path)
end
def ensure_credentials_have_been_added
- credentials_generator.add_credentials_file_silently
+ if options[:environment]
+ encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
+ else
+ credentials_generator.add_credentials_file_silently
+ end
end
def change_credentials_in_system_editor
- Rails.application.credentials.change do |tmp_path|
+ credentials.change do |tmp_path|
system("#{ENV["EDITOR"]} #{tmp_path}")
end
end
+ def missing_credentials_message
+ if credentials.key.nil?
+ "Missing '#{key_path}' to decrypt credentials. See `rails credentials:help`"
+ else
+ "File '#{content_path}' does not exist. Use `rails credentials:edit` to change that."
+ end
+ end
+
+
+ def content_path
+ options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
+ end
- def master_key_generator
+ def key_path
+ options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
+ end
+
+
+ def encryption_key_file_generator
+ require "rails/generators"
+ require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
+
+ Rails::Generators::EncryptionKeyFileGenerator.new
+ end
+
+ def encrypted_file_generator
require "rails/generators"
- require "rails/generators/rails/master_key/master_key_generator"
+ require "rails/generators/rails/encrypted_file/encrypted_file_generator"
- Rails::Generators::MasterKeyGenerator.new
+ Rails::Generators::EncryptedFileGenerator.new
end
def credentials_generator
@@ -66,14 +107,6 @@ module Rails
Rails::Generators::CredentialsGenerator.new
end
-
- def missing_credentials_message
- if Rails.application.credentials.key.nil?
- "Missing master key to decrypt credentials. See bin/rails credentials:help"
- else
- "No credentials have been added yet. Use bin/rails credentials:edit to change that."
- end
- end
end
end
end
diff --git a/railties/lib/rails/commands/db/system/change/change_command.rb b/railties/lib/rails/commands/db/system/change/change_command.rb
new file mode 100644
index 0000000000..760c229c07
--- /dev/null
+++ b/railties/lib/rails/commands/db/system/change/change_command.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require "rails/generators"
+require "rails/generators/rails/db/system/change/change_generator"
+
+module Rails
+ module Command
+ module Db
+ module System
+ class ChangeCommand < Base # :nodoc:
+ class_option :to, desc: "The database system to switch to."
+
+ def perform
+ Rails::Generators::Db::System::ChangeGenerator.start
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
index 806b7de6d6..72f3235ce3 100644
--- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
+++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/deprecation"
+require "active_support/core_ext/string/filters"
require "rails/command/environment_argument"
module Rails
@@ -75,7 +77,7 @@ module Rails
args += ["-P", "#{config['password']}"] if config["password"]
if config["host"]
- host_arg = "#{config['host']}".dup
+ host_arg = +"#{config['host']}"
host_arg << ":#{config['port']}" if config["port"]
args += ["-S", host_arg]
end
@@ -89,15 +91,15 @@ module Rails
def config
@config ||= begin
- # We need to check whether the user passed the connection the
+ # We need to check whether the user passed the database the
# first time around to show a consistent error message to people
# relying on 2-level database configuration.
- if @options["connection"] && configurations[connection].blank?
- raise ActiveRecord::AdapterNotSpecified, "'#{connection}' connection is not configured. Available configuration: #{configurations.inspect}"
- elsif configurations[environment].blank? && configurations[connection].blank?
+ if @options["database"] && configurations[database].blank?
+ raise ActiveRecord::AdapterNotSpecified, "'#{database}' database is not configured. Available configuration: #{configurations.inspect}"
+ elsif configurations[environment].blank? && configurations[database].blank?
raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
else
- configurations[connection] || configurations[environment].presence
+ configurations[database] || configurations[environment].presence
end
end
end
@@ -106,8 +108,8 @@ module Rails
Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment
end
- def connection
- @options.fetch(:connection, "primary")
+ def database
+ @options.fetch(:database, "primary")
end
private
@@ -156,12 +158,22 @@ module Rails
class_option :connection, aliases: "-c", type: :string,
desc: "Specifies the connection to use."
+ class_option :database, aliases: "--db", type: :string,
+ desc: "Specifies the database to use."
+
def perform
extract_environment_option_from_argument
# RAILS_ENV needs to be set before config/application is required.
ENV["RAILS_ENV"] = options[:environment]
+ if options["connection"]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `connection` option is deprecated and will be removed in Rails 6.1. Please use `database` option instead.
+ MSG
+ options["database"] = options["connection"]
+ end
+
require_application_and_environment!
Rails::DBConsole.start(options)
end
diff --git a/railties/lib/rails/commands/dev/dev_command.rb b/railties/lib/rails/commands/dev/dev_command.rb
new file mode 100644
index 0000000000..a3f02f3172
--- /dev/null
+++ b/railties/lib/rails/commands/dev/dev_command.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "rails/dev_caching"
+
+module Rails
+ module Command
+ class DevCommand < Base # :nodoc:
+ def help
+ say "rails dev:cache # Toggle development mode caching on/off."
+ end
+
+ def cache
+ Rails::DevCaching.enable_by_file
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/encrypted/USAGE b/railties/lib/rails/commands/encrypted/USAGE
new file mode 100644
index 0000000000..253eec2378
--- /dev/null
+++ b/railties/lib/rails/commands/encrypted/USAGE
@@ -0,0 +1,28 @@
+=== Storing Encrypted Files in Source Control
+
+The Rails `encrypted` commands provide access to encrypted files or configurations.
+See the `Rails.application.encrypted` documentation for using them in your app.
+
+=== Encryption Keys
+
+By default, Rails looks for the encryption key in `config/master.key` or
+`ENV["RAILS_MASTER_KEY"]`, but that lookup can be overridden with `--key`:
+
+ rails encrypted:edit config/encrypted_file.yml.enc --key config/encrypted_file.key
+
+Don't commit the key! Add it to your source control's ignore file. If you use
+Git, Rails handles this for you.
+
+=== Editing Files
+
+To edit or create an encrypted file use:
+
+ rails encrypted:edit config/encrypted_file.yml.enc
+
+This opens a temporary file in `$EDITOR` with the decrypted contents for editing.
+
+=== Viewing Files
+
+To print the decrypted contents of an encrypted file use:
+
+ rails encrypted:show config/encrypted_file.yml.enc
diff --git a/railties/lib/rails/commands/encrypted/encrypted_command.rb b/railties/lib/rails/commands/encrypted/encrypted_command.rb
index 3bc8f76ce4..f10a07cdf8 100644
--- a/railties/lib/rails/commands/encrypted/encrypted_command.rb
+++ b/railties/lib/rails/commands/encrypted/encrypted_command.rb
@@ -16,6 +16,7 @@ module Rails
def help
say "Usage:\n #{self.class.banner}"
say ""
+ say self.class.desc
end
end
@@ -76,9 +77,9 @@ module Rails
def missing_encrypted_message(key:, key_path:, file_path:)
if key.nil?
- "Missing '#{key_path}' to decrypt data. See bin/rails encrypted:help"
+ "Missing '#{key_path}' to decrypt data. See `rails encrypted:help`"
else
- "File '#{file_path}' does not exist. Use bin/rails encrypted:edit #{file_path} to change that."
+ "File '#{file_path}' does not exist. Use `rails encrypted:edit #{file_path}` to change that."
end
end
end
diff --git a/railties/lib/rails/commands/help/help_command.rb b/railties/lib/rails/commands/help/help_command.rb
index 8e5b4d68d3..9df34e9b79 100644
--- a/railties/lib/rails/commands/help/help_command.rb
+++ b/railties/lib/rails/commands/help/help_command.rb
@@ -6,7 +6,7 @@ module Rails
hide_command!
def help(*)
- puts self.class.desc
+ say self.class.desc
Rails::Command.print_commands
end
diff --git a/railties/lib/rails/commands/initializers/initializers_command.rb b/railties/lib/rails/commands/initializers/initializers_command.rb
new file mode 100644
index 0000000000..bd2f3bed67
--- /dev/null
+++ b/railties/lib/rails/commands/initializers/initializers_command.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require "rails/command/environment_argument"
+
+module Rails
+ module Command
+ class InitializersCommand < Base # :nodoc:
+ include EnvironmentArgument
+
+ desc "initializers", "Print out all defined initializers in the order they are invoked by Rails."
+ def perform
+ extract_environment_option_from_argument
+ ENV["RAILS_ENV"] = options[:environment]
+
+ require_application_and_environment!
+
+ Rails.application.initializers.tsort_each do |initializer|
+ say "#{initializer.context_class}.#{initializer.name}"
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/new/new_command.rb b/railties/lib/rails/commands/new/new_command.rb
index d73d64d899..a4f2081510 100644
--- a/railties/lib/rails/commands/new/new_command.rb
+++ b/railties/lib/rails/commands/new/new_command.rb
@@ -10,8 +10,8 @@ module Rails
end
def perform(*)
- puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
- puts "Type 'rails' for help."
+ say "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
+ say "Type 'rails' for help."
exit 1
end
end
diff --git a/railties/lib/rails/commands/plugin/plugin_command.rb b/railties/lib/rails/commands/plugin/plugin_command.rb
index 2b192abf9b..96187aa952 100644
--- a/railties/lib/rails/commands/plugin/plugin_command.rb
+++ b/railties/lib/rails/commands/plugin/plugin_command.rb
@@ -26,7 +26,7 @@ module Rails
if File.exist?(railsrc)
extra_args = File.read(railsrc).split(/\n+/).flat_map(&:split)
- puts "Using #{extra_args.join(" ")} from #{railsrc}"
+ say "Using #{extra_args.join(" ")} from #{railsrc}"
plugin_args.insert(1, *extra_args)
end
end
diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb
index 30fbf04982..40fb5e4d89 100644
--- a/railties/lib/rails/commands/runner/runner_command.rb
+++ b/railties/lib/rails/commands/runner/runner_command.rb
@@ -1,16 +1,18 @@
# frozen_string_literal: true
+require "rails/command/environment_argument"
+
module Rails
module Command
class RunnerCommand < Base # :nodoc:
- class_option :environment, aliases: "-e", type: :string,
- default: Rails::Command.environment.dup,
- desc: "The environment for the runner to operate under (test/development/production)"
+ include EnvironmentArgument
+
+ self.environment_desc = "The environment for the runner to operate under (test/development/production)"
no_commands do
def help
super
- puts self.class.desc
+ say self.class.desc
end
end
@@ -19,6 +21,8 @@ module Rails
end
def perform(code_or_file = nil, *command_argv)
+ extract_environment_option_from_argument
+
unless code_or_file
help
exit 1
@@ -39,11 +43,11 @@ module Rails
else
begin
eval(code_or_file, TOPLEVEL_BINDING, __FILE__, __LINE__)
- rescue SyntaxError, NameError => error
- $stderr.puts "Please specify a valid ruby command or the path of a script to run."
- $stderr.puts "Run '#{self.class.executable} -h' for help."
- $stderr.puts
- $stderr.puts error
+ rescue SyntaxError, NameError => e
+ error "Please specify a valid ruby command or the path of a script to run."
+ error "Run '#{self.class.executable} -h' for help."
+ error ""
+ error e
exit 1
end
end
diff --git a/railties/lib/rails/commands/secrets/USAGE b/railties/lib/rails/commands/secrets/USAGE
index 96e322fe91..e205cdc001 100644
--- a/railties/lib/rails/commands/secrets/USAGE
+++ b/railties/lib/rails/commands/secrets/USAGE
@@ -7,7 +7,7 @@ with the code.
=== Setup
-Run `bin/rails secrets:setup` to opt in and generate the `config/secrets.yml.key`
+Run `rails secrets:setup` to opt in and generate the `config/secrets.yml.key`
and `config/secrets.yml.enc` files.
The latter contains all the keys to be encrypted while the former holds the
@@ -45,12 +45,12 @@ the key. Add this:
config.read_encrypted_secrets = true
-to the environment you'd like to read encrypted secrets. `bin/rails secrets:setup`
+to the environment you'd like to read encrypted secrets. `rails secrets:setup`
inserts this into the production environment by default.
=== Editing Secrets
-After `bin/rails secrets:setup`, run `bin/rails secrets:edit`.
+After `rails secrets:setup`, run `rails secrets:edit`.
That command opens a temporary file in `$EDITOR` with the decrypted contents of
`config/secrets.yml.enc` to edit the encrypted secrets.
diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb
index a36ccf314c..2eebc0f35f 100644
--- a/railties/lib/rails/commands/secrets/secrets_command.rb
+++ b/railties/lib/rails/commands/secrets/secrets_command.rb
@@ -22,7 +22,7 @@ module Rails
if ENV["EDITOR"].to_s.empty?
say "No $EDITOR to open decrypted secrets in. Assign one like this:"
say ""
- say %(EDITOR="mate --wait" bin/rails secrets:edit)
+ say %(EDITOR="mate --wait" rails secrets:edit)
say ""
say "For editors that fork and exit immediately, it's important to pass a wait flag,"
say "otherwise the secrets will be saved immediately with no chance to edit."
@@ -42,7 +42,7 @@ module Rails
rescue Rails::Secrets::MissingKeyError => error
say error.message
rescue Errno::ENOENT => error
- if error.message =~ /secrets\.yml\.enc/
+ if /secrets\.yml\.enc/.match?(error.message)
deprecate_in_favor_of_credentials_and_exit
else
raise
@@ -56,7 +56,7 @@ module Rails
private
def deprecate_in_favor_of_credentials_and_exit
say "Encrypted secrets is deprecated in favor of credentials. Run:"
- say "bin/rails credentials:help"
+ say "rails credentials:help"
exit 1
end
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 2c5440d9ec..982b83ead5 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -6,6 +6,7 @@ require "rails"
require "active_support/deprecation"
require "active_support/core_ext/string/filters"
require "rails/dev_caching"
+require "rails/command/environment_argument"
module Rails
class Server < ::Rack::Server
@@ -21,19 +22,6 @@ module Rails
set_environment
end
- def app
- @app ||= begin
- app = super
- if app.is_a?(Class)
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Using `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0.
- Please change `run #{app}` to `run Rails.application` in config.ru.
- MSG
- end
- app.respond_to?(:to_app) ? app.to_app : app
- end
- end
-
def opt_parser
Options.new
end
@@ -104,12 +92,14 @@ module Rails
module Command
class ServerCommand < Base # :nodoc:
+ include EnvironmentArgument
+
# Hard-coding a bunch of handlers here as we don't have a public way of
# querying them from the Rack::Handler registry.
RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn)
DEFAULT_PORT = 3000
- DEFAULT_PID_PATH = "tmp/pids/server.pid".freeze
+ DEFAULT_PID_PATH = "tmp/pids/server.pid"
argument :using, optional: true
@@ -122,8 +112,6 @@ module Rails
desc: "Uses a custom rackup configuration.", banner: :file
class_option :daemon, aliases: "-d", type: :boolean, default: false,
desc: "Runs server as a Daemon."
- class_option :environment, aliases: "-e", type: :string,
- desc: "Specifies the environment to run this server under (development/test/production).", banner: :name
class_option :using, aliases: "-u", type: :string,
desc: "Specifies the Rack server used to run the application (thin/puma/webrick).", banner: :name
class_option :pid, aliases: "-P", type: :string, default: DEFAULT_PID_PATH,
@@ -143,6 +131,7 @@ module Rails
end
def perform
+ extract_environment_option_from_argument
set_application_directory!
prepare_restart
@@ -234,8 +223,8 @@ module Rails
if ENV["HOST"] && !ENV["BINDING"]
ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Using the `HOST` environment to specify the IP is deprecated and will be removed in Rails 6.1.
- Please use `BINDING` environment instead.
+ Using the `HOST` environment variable to specify the IP is deprecated and will be removed in Rails 6.1.
+ Please use `BINDING` environment variable instead.
MSG
return ENV["HOST"]
@@ -268,7 +257,7 @@ module Rails
end
def self.banner(*)
- "rails server [thin/puma/webrick] [options]"
+ "rails server -u [thin/puma/webrick] [options]"
end
def prepare_restart
@@ -277,7 +266,7 @@ module Rails
def deprecate_positional_rack_server_and_rewrite_to_option(original_options)
if using
- ActiveSupport::Deprecation.warn(<<~MSG)
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
Passing the Rack server name as a regular argument is deprecated
and will be removed in the next Rails version. Please, use the -u
option instead.
@@ -286,7 +275,7 @@ module Rails
original_options.concat [ "-u", using ]
else
# Use positional internally to get around Thor's immutable options.
- # TODO: Replace `using` occurences with `options[:using]` after deprecation removal.
+ # TODO: Replace `using` occurrences with `options[:using]` after deprecation removal.
@using = options[:using]
end
end
@@ -302,9 +291,10 @@ module Rails
MSG
else
suggestion = Rails::Command::Spellchecker.suggest(server, from: RACK_SERVERS)
+ suggestion_msg = "Maybe you meant #{suggestion.inspect}?" if suggestion
<<~MSG
- Could not find server "#{server}". Maybe you meant #{suggestion.inspect}?
+ Could not find server "#{server}". #{suggestion_msg}
Run `rails server --help` for more options.
MSG
end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index d3a54d9364..e8741a50ba 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -79,13 +79,7 @@ module Rails
end
protected
- def operations
- @operations
- end
-
- def delete_operations
- @delete_operations
- end
+ attr_reader :operations, :delete_operations
end
class Generators #:nodoc:
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 6a13a84108..eb2f0e8fca 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -230,7 +230,7 @@ module Rails
#
# If +MyEngine+ is isolated, The routes above will point to
# <tt>MyEngine::ArticlesController</tt>. You also don't need to use longer
- # url helpers like +my_engine_articles_path+. Instead, you should simply use
+ # URL helpers like +my_engine_articles_path+. Instead, you should simply use
# +articles_path+, like you would do with your main application.
#
# To make this behavior consistent with other parts of the framework,
@@ -238,7 +238,7 @@ module Rails
# normal Rails app, when you use a namespaced model such as
# <tt>Namespace::Article</tt>, <tt>ActiveModel::Naming</tt> will generate
# names with the prefix "namespace". In an isolated engine, the prefix will
- # be omitted in url helpers and form fields, for convenience.
+ # be omitted in URL helpers and form fields, for convenience.
#
# polymorphic_url(MyEngine::Article.new)
# # => "articles_path" # not "my_engine_articles_path"
@@ -286,11 +286,11 @@ module Rails
# Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tt> as default, so most of the time
# you can simply omit it.
#
- # Finally, if you want to generate a url to an engine's route using
+ # Finally, if you want to generate a URL to an engine's route using
# <tt>polymorphic_url</tt>, you also need to pass the engine helper. Let's
# say that you want to create a form pointing to one of the engine's routes.
# All you need to do is pass the helper as the first element in array with
- # attributes for url:
+ # attributes for URL:
#
# form_for([my_engine, @user])
#
@@ -469,13 +469,15 @@ module Rails
self
end
- # Eager load the application by loading all ruby
- # files inside eager_load paths.
def eager_load!
+ # Already done by Zeitwerk::Loader.eager_load_all in the finisher.
+ return if Rails.autoloaders.zeitwerk_enabled?
+
config.eager_load_paths.each do |load_path|
- matcher = /\A#{Regexp.escape(load_path.to_s)}\/(.*)\.rb\Z/
+ # Starts after load_path plus a slash, ends before ".rb".
+ relname_range = (load_path.to_s.length + 1)...-3
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
- require_dependency file.sub(matcher, '\1')
+ require_dependency file[relname_range]
end
end
end
@@ -531,9 +533,9 @@ module Rails
# Defines the routes for this engine. If a block is given to
# routes, it is appended to the engine.
- def routes
+ def routes(&block)
@routes ||= ActionDispatch::Routing::RouteSet.new_with_config(config)
- @routes.append(&Proc.new) if block_given?
+ @routes.append(&block) if block_given?
@routes
end
@@ -548,7 +550,13 @@ module Rails
# Blog::Engine.load_seed
def load_seed
seed_file = paths["db/seeds.rb"].existent.first
- load(seed_file) if seed_file
+ return unless seed_file
+
+ if config.active_job.queue_adapter == :async
+ with_inline_jobs { load(seed_file) }
+ else
+ load(seed_file)
+ end
end
# Add configured load paths to Ruby's load path, and remove duplicate entries.
@@ -568,12 +576,15 @@ module Rails
ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
- # Freeze so future modifications will fail rather than do nothing mysteriously
config.autoload_paths.freeze
- config.eager_load_paths.freeze
config.autoload_once_paths.freeze
end
+ initializer :set_eager_load_paths, before: :bootstrap_hook do
+ ActiveSupport::Dependencies._eager_load_paths.merge(config.eager_load_paths)
+ config.eager_load_paths.freeze
+ end
+
initializer :add_routing_paths do |app|
routing_paths = paths["config/routes.rb"].existent
@@ -658,6 +669,18 @@ module Rails
end
end
+ def with_inline_jobs
+ queue_adapter = config.active_job.queue_adapter
+ ActiveSupport.on_load(:active_job) do
+ self.queue_adapter = :inline
+ end
+ yield
+ ensure
+ ActiveSupport.on_load(:active_job) do
+ self.queue_adapter = queue_adapter
+ end
+ end
+
def has_migrations?
paths["db/migrate"].existent.any?
end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 6bf0406b21..4143b3c881 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -38,7 +38,9 @@ module Rails
@paths ||= begin
paths = Rails::Paths::Root.new(@root)
- paths.add "app", eager_load: true, glob: "{*,*/concerns}"
+ paths.add "app", eager_load: true,
+ glob: "{*,*/concerns}",
+ exclude: %w(assets javascript)
paths.add "app/assets", glob: "*"
paths.add "app/controllers", eager_load: true
paths.add "app/channels", eager_load: true, glob: "**/*_channel.rb"
diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb
index 54bfbdd516..fea24810f5 100644
--- a/railties/lib/rails/gem_version.rb
+++ b/railties/lib/rails/gem_version.rb
@@ -10,7 +10,7 @@ module Rails
MAJOR = 6
MINOR = 0
TINY = 0
- PRE = "alpha"
+ PRE = "beta3"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 2a41403557..b835b3f3fd 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -23,6 +23,8 @@ module Rails
autoload :ActiveModel, "rails/generators/active_model"
autoload :Base, "rails/generators/base"
autoload :Migration, "rails/generators/migration"
+ autoload :Database, "rails/generators/database"
+ autoload :AppName, "rails/generators/app_name"
autoload :NamedBase, "rails/generators/named_base"
autoload :ResourceHelpers, "rails/generators/resource_helpers"
autoload :TestCase, "rails/generators/test_case"
@@ -33,8 +35,6 @@ module Rails
rails: {
actions: "-a",
orm: "-o",
- javascripts: "-j",
- javascript_engine: "-je",
resource_controller: "-c",
scaffold_controller: "-c",
stylesheets: "-y",
@@ -56,8 +56,6 @@ module Rails
force_plural: false,
helper: true,
integration_tool: nil,
- javascripts: true,
- javascript_engine: :js,
orm: false,
resource_controller: :controller,
resource_route: true,
@@ -126,7 +124,7 @@ module Rails
)
if ARGV.first == "mailer"
- options[:rails].merge!(template_engine: :erb)
+ options[:rails][:template_engine] = :erb
end
end
@@ -222,6 +220,7 @@ module Rails
rails.delete("encryption_key_file")
rails.delete("master_key")
rails.delete("credentials")
+ rails.delete("db:system:change")
hidden_namespaces.each { |n| groups.delete(n.to_s) }
@@ -276,8 +275,10 @@ module Rails
else
options = sorted_groups.flat_map(&:last)
suggestion = Rails::Command::Spellchecker.suggest(namespace.to_s, from: options)
+ suggestion_msg = "Maybe you meant #{suggestion.inspect}?" if suggestion
+
puts <<~MSG
- Could not find generator '#{namespace}'. Maybe you meant #{suggestion.inspect}?
+ Could not find generator '#{namespace}'. #{suggestion_msg}
Run `rails generate --help` for more options.
MSG
end
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index d85bbfb03e..1a5f2ff203 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -7,8 +7,7 @@ module Rails
module Actions
def initialize(*) # :nodoc:
super
- @in_group = nil
- @after_bundle_callbacks = []
+ @indentation = 0
end
# Adds an entry into +Gemfile+ for the supplied gem.
@@ -36,13 +35,11 @@ module Rails
log :gemfile, message
- options.each do |option, value|
- parts << "#{option}: #{quote(value)}"
- end
+ parts << quote(options) unless options.empty?
in_root do
str = "gem #{parts.join(", ")}"
- str = " " + str if @in_group
+ str = indentation + str
str = "\n" + str
append_file "Gemfile", str, verbose: false
end
@@ -54,17 +51,29 @@ module Rails
# gem "rspec-rails"
# end
def gem_group(*names, &block)
- name = names.map(&:inspect).join(", ")
- log :gemfile, "group #{name}"
+ options = names.extract_options!
+ str = names.map(&:inspect)
+ str << quote(options) unless options.empty?
+ str = str.join(", ")
+ log :gemfile, "group #{str}"
in_root do
- append_file "Gemfile", "\ngroup #{name} do", force: true
+ append_file "Gemfile", "\ngroup #{str} do", force: true
+ with_indentation(&block)
+ append_file "Gemfile", "\nend\n", force: true
+ end
+ end
- @in_group = true
- instance_eval(&block)
- @in_group = false
+ def github(repo, options = {}, &block)
+ str = [quote(repo)]
+ str << quote(options) unless options.empty?
+ str = str.join(", ")
+ log :github, "github #{str}"
- append_file "Gemfile", "\nend\n", force: true
+ in_root do
+ append_file "Gemfile", "\n#{indentation}github #{str} do", force: true
+ with_indentation(&block)
+ append_file "Gemfile", "\n#{indentation}end", force: true
end
end
@@ -83,9 +92,7 @@ module Rails
in_root do
if block
append_file "Gemfile", "\nsource #{quote(source)} do", force: true
- @in_group = true
- instance_eval(&block)
- @in_group = false
+ with_indentation(&block)
append_file "Gemfile", "\nend\n", force: true
else
prepend_file "Gemfile", "source #{quote(source)}\n", verbose: false
@@ -213,9 +220,12 @@ module Rails
# generate(:authenticated, "user session")
def generate(what, *args)
log :generate, what
+
+ options = args.extract_options!
+ options[:without_rails_env] = true
argument = args.flat_map(&:to_s).join(" ")
- in_root { run_ruby_script("bin/rails generate #{what} #{argument}", verbose: false) }
+ execute_command :rails, "generate #{what} #{argument}", options
end
# Runs the supplied rake task (invoked with 'rake ...')
@@ -238,15 +248,6 @@ module Rails
execute_command :rails, command, options
end
- # Just run the capify command in root
- #
- # capify!
- def capify!
- ActiveSupport::Deprecation.warn("`capify!` is deprecated and will be removed in the next version of Rails.")
- log :capify, ""
- in_root { run("#{extify(:capify)} .", verbose: false) }
- end
-
# Make an entry in Rails routing file <tt>config/routes.rb</tt>
#
# route "root 'welcome#index'"
@@ -266,16 +267,6 @@ module Rails
log File.read(find_in_source_paths(path))
end
- # Registers a callback to be executed after bundle and spring binstubs
- # have run.
- #
- # after_bundle do
- # git add: '.'
- # end
- def after_bundle(&block)
- @after_bundle_callbacks << block
- end
-
private
# Define log for backwards compatibility. If just one argument is sent,
@@ -294,13 +285,15 @@ module Rails
# based on the executor parameter provided.
def execute_command(executor, command, options = {}) # :doc:
log executor, command
- env = options[:env] || ENV["RAILS_ENV"] || "development"
+ env = options[:env] || ENV["RAILS_ENV"] || "development"
+ rails_env = " RAILS_ENV=#{env}" unless options[:without_rails_env]
sudo = options[:sudo] && !Gem.win_platform? ? "sudo " : ""
config = { verbose: false }
- config.merge!(capture: options[:capture]) if options[:capture]
+ config[:capture] = options[:capture] if options[:capture]
+ config[:abort_on_failure] = options[:abort_on_failure] if options[:abort_on_failure]
- in_root { run("#{sudo}#{extify(executor)} #{command} RAILS_ENV=#{env}", config) }
+ in_root { run("#{sudo}#{extify(executor)} #{command}#{rails_env}", config) }
end
# Add an extension to the given name based on the platform.
@@ -315,6 +308,11 @@ module Rails
# Surround string with single quotes if there is no quotes.
# Otherwise fall back to double quotes
def quote(value) # :doc:
+ if value.respond_to? :each_pair
+ return value.map do |k, v|
+ "#{k}: #{quote(v)}"
+ end.join(", ")
+ end
return value.inspect unless value.is_a? String
if value.include?("'")
@@ -334,6 +332,19 @@ module Rails
"#{value.strip.indent(amount)}\n"
end
end
+
+ # Indent the +Gemfile+ to the depth of @indentation
+ def indentation # :doc:
+ " " * @indentation
+ end
+
+ # Manage +Gemfile+ indentation for a DSL action block
+ def with_indentation(&block) # :doc:
+ @indentation += 1
+ instance_eval(&block)
+ ensure
+ @indentation -= 1
+ end
end
end
end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index f51542f3ec..66f6b57379 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -11,9 +11,8 @@ require "active_support/core_ext/array/extract_options"
module Rails
module Generators
class AppBase < Base # :nodoc:
- DATABASES = %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver )
- JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
- DATABASES.concat(JDBC_DATABASES)
+ include Database
+ include AppName
attr_accessor :rails_template
add_shebang_option!
@@ -31,9 +30,6 @@ module Rails
class_option :database, type: :string, aliases: "-d", default: "sqlite3",
desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
- class_option :skip_yarn, type: :boolean, default: false,
- desc: "Don't use Yarn for managing JavaScript dependencies"
-
class_option :skip_gemfile, type: :boolean, default: false,
desc: "Don't create a Gemfile"
@@ -47,6 +43,12 @@ module Rails
default: false,
desc: "Skip Action Mailer files"
+ class_option :skip_action_mailbox, type: :boolean, default: false,
+ desc: "Skip Action Mailbox gem"
+
+ class_option :skip_action_text, type: :boolean, default: false,
+ desc: "Skip Action Text gem"
+
class_option :skip_active_record, type: :boolean, aliases: "-O", default: false,
desc: "Skip Active Record files"
@@ -68,10 +70,7 @@ module Rails
class_option :skip_listen, type: :boolean, default: false,
desc: "Don't generate configuration that depends on the listen gem"
- class_option :skip_coffee, type: :boolean, default: false,
- desc: "Don't use CoffeeScript"
-
- class_option :skip_javascript, type: :boolean, aliases: "-J", default: false,
+ class_option :skip_javascript, type: :boolean, aliases: "-J", default: name == "plugin",
desc: "Skip JavaScript files"
class_option :skip_turbolinks, type: :boolean, default: false,
@@ -106,7 +105,6 @@ module Rails
@gem_filter = lambda { |gem| true }
@extra_entries = []
super
- convert_database_option_for_jruby
end
private
@@ -130,7 +128,7 @@ module Rails
def gemfile_entries # :doc:
[rails_gemfile_entry,
database_gemfile_entry,
- webserver_gemfile_entry,
+ web_server_gemfile_entry,
assets_gemfile_entry,
webpacker_gemfile_entry,
javascript_gemfile_entry,
@@ -191,7 +189,7 @@ module Rails
"Use #{options[:database]} as the database for Active Record"
end
- def webserver_gemfile_entry # :doc:
+ def web_server_gemfile_entry # :doc:
return [] if options[:skip_puma]
comment = "Use Puma as the app server"
GemfileEntry.new("puma", "~> 3.11", comment)
@@ -206,7 +204,9 @@ module Rails
:skip_sprockets,
:skip_action_cable
),
- skip_active_storage?
+ skip_active_storage?,
+ skip_action_mailbox?,
+ skip_action_text?
].flatten.none?
end
@@ -235,6 +235,14 @@ module Rails
options[:skip_active_storage] || options[:skip_active_record]
end
+ def skip_action_mailbox? # :doc:
+ options[:skip_action_mailbox] || skip_active_storage?
+ end
+
+ def skip_action_text? # :doc:
+ options[:skip_action_text] || skip_active_storage?
+ end
+
class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
def initialize(name, version, comment, options = {}, commented_out = false)
super
@@ -296,55 +304,20 @@ module Rails
end
end
- def gem_for_database
- # %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql )
- case options[:database]
- when "mysql" then ["mysql2", [">= 0.4.4", "< 0.6.0"]]
- when "postgresql" then ["pg", [">= 0.18", "< 2.0"]]
- when "oracle" then ["activerecord-oracle_enhanced-adapter", nil]
- when "frontbase" then ["ruby-frontbase", nil]
- when "sqlserver" then ["activerecord-sqlserver-adapter", nil]
- when "jdbcmysql" then ["activerecord-jdbcmysql-adapter", nil]
- when "jdbcsqlite3" then ["activerecord-jdbcsqlite3-adapter", nil]
- when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil]
- when "jdbc" then ["activerecord-jdbc-adapter", nil]
- else [options[:database], nil]
- end
- end
-
- def convert_database_option_for_jruby
- if defined?(JRUBY_VERSION)
- opt = options.dup
- case opt[:database]
- when "postgresql" then opt[:database] = "jdbcpostgresql"
- when "mysql" then opt[:database] = "jdbcmysql"
- when "sqlite3" then opt[:database] = "jdbcsqlite3"
- end
- self.options = opt.freeze
- end
- end
-
def assets_gemfile_entry
return [] if options[:skip_sprockets]
- gems = []
- gems << GemfileEntry.version("sass-rails", "~> 5.0",
- "Use SCSS for stylesheets")
-
- if !options[:skip_javascript]
- gems << GemfileEntry.version("uglifier",
- ">= 1.3.0",
- "Use Uglifier as compressor for JavaScript assets")
- end
-
- gems
+ GemfileEntry.version("sass-rails", "~> 5.0", "Use SCSS for stylesheets")
end
def webpacker_gemfile_entry
- return [] unless options[:webpack]
+ return [] if options[:skip_javascript]
- comment = "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
- GemfileEntry.new "webpacker", nil, comment
+ if options.dev? || options.edge?
+ GemfileEntry.github "webpacker", "rails/webpacker", nil, "Use development version of Webpacker"
+ else
+ GemfileEntry.version "webpacker", "~> 4.0", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
+ end
end
def jbuilder_gemfile_entry
@@ -352,34 +325,12 @@ module Rails
GemfileEntry.new "jbuilder", "~> 2.5", comment, {}, options[:api]
end
- def coffee_gemfile_entry
- GemfileEntry.version "coffee-rails", "~> 4.2", "Use CoffeeScript for .coffee assets and views"
- end
-
def javascript_gemfile_entry
- if options[:skip_javascript] || options[:skip_sprockets]
+ if options[:skip_javascript] || options[:skip_turbolinks]
[]
else
- gems = [javascript_runtime_gemfile_entry]
- gems << coffee_gemfile_entry unless options[:skip_coffee]
-
- unless options[:skip_turbolinks]
- gems << GemfileEntry.version("turbolinks", "~> 5",
- "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks")
- end
-
- gems
- end
- end
-
- def javascript_runtime_gemfile_entry
- comment = "See https://github.com/rails/execjs#readme for more supported runtimes"
- if defined?(JRUBY_VERSION)
- GemfileEntry.version "therubyrhino", nil, comment
- elsif RUBY_PLATFORM =~ /mingw|mswin/
- GemfileEntry.version "duktape", nil, comment
- else
- GemfileEntry.new "mini_racer", nil, comment, { platforms: :ruby }, true
+ [ GemfileEntry.version("turbolinks", "~> 5",
+ "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") ]
end
end
@@ -399,7 +350,7 @@ module Rails
gems
end
- def bundle_command(command)
+ def bundle_command(command, env = {})
say_status :run, "bundle #{command}"
# We are going to shell out rather than invoking Bundler::CLI.new(command)
@@ -407,19 +358,21 @@ module Rails
# its own vendored Thor, which could be a different version. Running both
# things in the same process is a recipe for a night with paracetamol.
#
- # We unset temporary bundler variables to load proper bundler and Gemfile.
- #
# Thanks to James Tucker for the Gem tricks involved in this call.
_bundle_command = Gem.bin_path("bundler", "bundle")
require "bundler"
- Bundler.with_clean_env do
- full_command = %Q["#{Gem.ruby}" "#{_bundle_command}" #{command}]
- if options[:quiet]
- system(full_command, out: File::NULL)
- else
- system(full_command)
- end
+ Bundler.with_original_env do
+ exec_bundle_command(_bundle_command, command, env)
+ end
+ end
+
+ def exec_bundle_command(bundle_command, command, env)
+ full_command = %Q["#{Gem.ruby}" "#{bundle_command}" #{command}]
+ if options[:quiet]
+ system(env, full_command, out: File::NULL)
+ else
+ system(env, full_command)
end
end
@@ -431,6 +384,10 @@ module Rails
!options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
end
+ def webpack_install?
+ !(options[:skip_javascript] || options[:skip_webpack_install])
+ end
+
def depends_on_system_test?
!(options[:skip_system_test] || options[:skip_test] || options[:api])
end
@@ -448,13 +405,19 @@ module Rails
end
def run_bundle
- bundle_command("install") if bundle_install?
+ bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1") if bundle_install?
end
def run_webpack
- if !(webpack = options[:webpack]).nil?
+ if webpack_install?
rails_command "webpacker:install"
- rails_command "webpacker:install:#{webpack}" unless webpack == "webpack"
+ rails_command "webpacker:install:#{options[:webpack]}" if options[:webpack] && options[:webpack] != "webpack"
+ end
+ end
+
+ def generate_bundler_binstub
+ if bundle_install?
+ bundle_command("binstubs bundler")
end
end
diff --git a/railties/lib/rails/generators/app_name.rb b/railties/lib/rails/generators/app_name.rb
new file mode 100644
index 0000000000..5bb735c4e8
--- /dev/null
+++ b/railties/lib/rails/generators/app_name.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Rails
+ module Generators
+ module AppName # :nodoc:
+ RESERVED_NAMES = %w(application destroy plugin runner test)
+
+ private
+ def app_name
+ @app_name ||= original_app_name.tr('\\', "").tr("-. ", "_")
+ end
+
+ def original_app_name
+ @original_app_name ||= defined_app_const_base? ? defined_app_name : File.basename(destination_root)
+ end
+
+ def defined_app_name
+ defined_app_const_base.underscore
+ end
+
+ def defined_app_const_base
+ Rails.respond_to?(:application) && defined?(Rails::Application) &&
+ Rails.application.is_a?(Rails::Application) && Rails.application.class.name.chomp("::Application")
+ end
+
+ alias :defined_app_const_base? :defined_app_const_base
+
+ def app_const_base
+ @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, "_").squeeze("_").camelize
+ end
+ alias :camelized :app_const_base
+
+ def app_const
+ @app_const ||= "#{app_const_base}::Application"
+ end
+
+ def valid_const?
+ if /^\d/.match?(app_const)
+ raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers."
+ elsif RESERVED_NAMES.include?(original_app_name)
+ raise Error, "Invalid application name #{original_app_name}. Please give a " \
+ "name which does not match one of the reserved rails " \
+ "words: #{RESERVED_NAMES.join(", ")}"
+ elsif Object.const_defined?(app_const_base)
+ raise Error, "Invalid application name #{original_app_name}, constant #{app_const_base} is already in use. Please choose another application name."
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/database.rb b/railties/lib/rails/generators/database.rb
new file mode 100644
index 0000000000..cc6e7b50e5
--- /dev/null
+++ b/railties/lib/rails/generators/database.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Rails
+ module Generators
+ module Database # :nodoc:
+ JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
+ DATABASES = %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver ) + JDBC_DATABASES
+
+ def initialize(*)
+ super
+ convert_database_option_for_jruby
+ end
+
+ def gem_for_database(database = options[:database])
+ case database
+ when "mysql" then ["mysql2", [">= 0.4.4"]]
+ when "postgresql" then ["pg", [">= 0.18", "< 2.0"]]
+ when "sqlite3" then ["sqlite3", ["~> 1.4"]]
+ when "oracle" then ["activerecord-oracle_enhanced-adapter", nil]
+ when "frontbase" then ["ruby-frontbase", nil]
+ when "sqlserver" then ["activerecord-sqlserver-adapter", nil]
+ when "jdbcmysql" then ["activerecord-jdbcmysql-adapter", nil]
+ when "jdbcsqlite3" then ["activerecord-jdbcsqlite3-adapter", nil]
+ when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil]
+ when "jdbc" then ["activerecord-jdbc-adapter", nil]
+ else [database, nil]
+ end
+ end
+
+ def convert_database_option_for_jruby
+ if defined?(JRUBY_VERSION)
+ opt = options.dup
+ case opt[:database]
+ when "postgresql" then opt[:database] = "jdbcpostgresql"
+ when "mysql" then opt[:database] = "jdbcmysql"
+ when "sqlite3" then opt[:database] = "jdbcsqlite3"
+ end
+ self.options = opt.freeze
+ end
+ end
+
+ private
+ def mysql_socket
+ @mysql_socket ||= [
+ "/tmp/mysql.sock", # default
+ "/var/run/mysqld/mysqld.sock", # debian/gentoo
+ "/var/tmp/mysql.sock", # freebsd
+ "/var/lib/mysql/mysql.sock", # fedora
+ "/opt/local/lib/mysql/mysql.sock", # fedora
+ "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
+ "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
+ "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
+ "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
+ ].find { |f| File.exist?(f) } unless Gem.win_platform?
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt
index 518cb1121e..1dddc3d698 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt
+++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt
@@ -4,9 +4,9 @@
<h2><%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2>
<ul>
- <%% <%= singular_table_name %>.errors.full_messages.each do |message| %>
- <li><%%= message %></li>
- <%% end %>
+ <%% <%= singular_table_name %>.errors.full_messages.each do |message| %>
+ <li><%%= message %></li>
+ <%% end %>
</ul>
</div>
<%% end %>
@@ -21,6 +21,9 @@
<div class="field">
<%%= form.label :password_confirmation %>
<%%= form.password_field :password_confirmation %>
+<% elsif attribute.attachments? -%>
+ <%%= form.label :<%= attribute.column_name %> %>
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true %>
<% else -%>
<%%= form.label :<%= attribute.column_name %> %>
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %> %>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt
index e1ede7c713..2cf4e5c9d0 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt
@@ -16,7 +16,7 @@
<%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
<tr>
<% attributes.reject(&:password_digest?).each do |attribute| -%>
- <td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td>
+ <td><%%= <%= singular_table_name %>.<%= attribute.column_name %> %></td>
<% end -%>
<td><%%= link_to 'Show', <%= model_resource_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %></td>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
index 5e634153be..6b216001d2 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
+++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
@@ -3,7 +3,15 @@
<% attributes.reject(&:password_digest?).each do |attribute| -%>
<p>
<strong><%= attribute.human_name %>:</strong>
- <%%= @<%= singular_table_name %>.<%= attribute.name %> %>
+<% if attribute.attachment? -%>
+ <%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %> %>
+<% elsif attribute.attachments? -%>
+ <%% @<%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
+ <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div>
+ <%% end %>
+<% else -%>
+ <%%= @<%= singular_table_name %>.<%= attribute.column_name %> %>
+<% end -%>
</p>
<% end -%>
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index f7fd30a5fb..99c1bc4269 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -39,23 +39,23 @@ module Rails
private
- # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
- # when declaring options curly brackets should be used
- def parse_type_and_options(type)
- case type
- when /(string|text|binary|integer)\{(\d+)\}/
- return $1, limit: $2.to_i
- when /decimal\{(\d+)[,.-](\d+)\}/
- return :decimal, precision: $1.to_i, scale: $2.to_i
- when /(references|belongs_to)\{(.+)\}/
- type = $1
- provided_options = $2.split(/[,.-]/)
- options = Hash[provided_options.map { |opt| [opt.to_sym, true] }]
- return type, options
- else
- return type, {}
+ # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
+ # when declaring options curly brackets should be used
+ def parse_type_and_options(type)
+ case type
+ when /(string|text|binary|integer)\{(\d+)\}/
+ return $1, limit: $2.to_i
+ when /decimal\{(\d+)[,.-](\d+)\}/
+ return :decimal, precision: $1.to_i, scale: $2.to_i
+ when /(references|belongs_to)\{(.+)\}/
+ type = $1
+ provided_options = $2.split(/[,.-]/)
+ options = Hash[provided_options.map { |opt| [opt.to_sym, true] }]
+ return type, options
+ else
+ return type, {}
+ end
end
- end
end
def initialize(name, type = nil, index_type = false, attr_options = {})
@@ -68,13 +68,15 @@ module Rails
def field_type
@field_type ||= case type
- when :integer then :number_field
- when :float, :decimal then :text_field
- when :time then :time_select
- when :datetime, :timestamp then :datetime_select
- when :date then :date_select
- when :text then :text_area
- when :boolean then :check_box
+ when :integer then :number_field
+ when :float, :decimal then :text_field
+ when :time then :time_select
+ when :datetime, :timestamp then :datetime_select
+ when :date then :date_select
+ when :text then :text_area
+ when :rich_text then :rich_text_area
+ when :boolean then :check_box
+ when :attachment, :attachments then :file_field
else
:text_field
end
@@ -90,7 +92,9 @@ module Rails
when :string then name == "type" ? "" : "MyString"
when :text then "MyText"
when :boolean then false
- when :references, :belongs_to then nil
+ when :references, :belongs_to,
+ :attachment, :attachments,
+ :rich_text then nil
else
""
end
@@ -152,8 +156,24 @@ module Rails
type == :token
end
+ def rich_text?
+ type == :rich_text
+ end
+
+ def attachment?
+ type == :attachment
+ end
+
+ def attachments?
+ type == :attachments
+ end
+
+ def virtual?
+ rich_text? || attachment? || attachments?
+ end
+
def inject_options
- "".dup.tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
+ (+"").tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
end
def inject_index_options
diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb
deleted file mode 100644
index 9d32c666dc..0000000000
--- a/railties/lib/rails/generators/js/assets/assets_generator.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-require "rails/generators/named_base"
-
-module Js # :nodoc:
- module Generators # :nodoc:
- class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
- source_root File.expand_path("templates", __dir__)
-
- def copy_javascript
- copy_file "javascript.js", File.join("app/assets/javascripts", class_path, "#{file_name}.js")
- end
- end
- end
-end
diff --git a/railties/lib/rails/generators/js/assets/templates/javascript.js b/railties/lib/rails/generators/js/assets/templates/javascript.js
deleted file mode 100644
index dee720facd..0000000000
--- a/railties/lib/rails/generators/js/assets/templates/javascript.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place all the behaviors and hooks related to the matching controller here.
-// All this logic will automatically be available in application.js.
diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb
index 5081060895..b6ec0160cf 100644
--- a/railties/lib/rails/generators/migration.rb
+++ b/railties/lib/rails/generators/migration.rb
@@ -63,8 +63,7 @@ module Rails
numbered_destination = File.join(dir, ["%migration_number%", base].join("_"))
create_migration numbered_destination, nil, config do
- match = ERB.version.match(/\Aerb\.rb \[(?<version>[^ ]+) /)
- if match && match[:version] >= "2.2.0" # Ruby 2.6+
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
ERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer").result(context)
else
ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
diff --git a/railties/lib/rails/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb
index 50078404b3..3676432d5c 100644
--- a/railties/lib/rails/generators/model_helpers.rb
+++ b/railties/lib/rails/generators/model_helpers.rb
@@ -7,6 +7,10 @@ module Rails
module ModelHelpers # :nodoc:
PLURAL_MODEL_NAME_WARN_MESSAGE = "[WARNING] The model name '%s' was recognized as a plural, using the singular '%s' instead. " \
"Override with --force-plural or setup custom inflection rules for this noun before running the generator."
+ IRREGULAR_MODEL_NAME_WARN_MESSAGE = <<~WARNING
+ [WARNING] Rails cannot recover singular form from its plural form '%s'.
+ Please setup custom inflection rules for this noun before running the generator in config/initializers/inflections.rb.
+ WARNING
mattr_accessor :skip_warn
def self.included(base) #:nodoc:
@@ -19,11 +23,14 @@ module Rails
singular = name.singularize
unless ModelHelpers.skip_warn
say PLURAL_MODEL_NAME_WARN_MESSAGE % [name, singular]
- ModelHelpers.skip_warn = true
end
name.replace singular
assign_names!(name)
end
+ if name.singularize != name.pluralize.singularize && ! ModelHelpers.skip_warn
+ say IRREGULAR_MODEL_NAME_WARN_MESSAGE % [name.pluralize]
+ end
+ ModelHelpers.skip_warn = true
end
end
end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index d6732f8ff1..42e64cd11f 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -187,6 +187,7 @@ module Rails
def attributes_names # :doc:
@attributes_names ||= attributes.each_with_object([]) do |a, names|
+ next if a.attachments?
names << a.column_name
names << "password_confirmation" if a.password_digest?
names << "#{a.name}_type" if a.polymorphic?
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 76dbfadc0e..f2f46d6e25 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -80,7 +80,6 @@ module Rails
directory "app"
keep_file "app/assets/images"
- empty_directory_with_keep_file "app/assets/javascripts/channels" unless options[:skip_action_cable]
keep_file "app/controllers/concerns"
keep_file "app/models/concerns"
@@ -96,7 +95,7 @@ module Rails
def bin_when_updating
bin
- if options[:skip_yarn]
+ if options[:skip_javascript]
remove_file "bin/yarn"
end
end
@@ -214,6 +213,7 @@ module Rails
empty_directory_with_keep_file "test/helpers"
empty_directory_with_keep_file "test/integration"
+ template "test/channels/application_cable/connection_test.rb"
template "test/test_helper.rb"
end
@@ -242,14 +242,13 @@ module Rails
# We need to store the RAILS_DEV_PATH in a constant, otherwise the path
# can change in Ruby 1.8.7 when we FileUtils.cd.
RAILS_DEV_PATH = File.expand_path("../../../../../..", __dir__)
- RESERVED_NAMES = %w[application destroy plugin runner test]
class AppGenerator < AppBase # :nodoc:
WEBPACKS = %w( react vue angular elm stimulus )
add_shared_options_for "application"
- # Add bin/rails options
+ # Add rails command options
class_option :version, type: :boolean, aliases: "-v", group: :rails,
desc: "Show Rails version number and quit"
@@ -259,21 +258,26 @@ module Rails
class_option :skip_bundle, type: :boolean, aliases: "-B", default: false,
desc: "Don't run bundle install"
- class_option :webpack, type: :string, default: nil,
- desc: "Preconfigure for app-like JavaScript with Webpack (options: #{WEBPACKS.join('/')})"
+ class_option :webpack, type: :string, aliases: "--webpacker", default: nil,
+ desc: "Preconfigure Webpack with a particular framework (options: #{WEBPACKS.join(", ")})"
+
+ class_option :skip_webpack_install, type: :boolean, default: false,
+ desc: "Don't run Webpack install"
def initialize(*args)
super
if !options[:skip_active_record] && !DATABASES.include?(options[:database])
- raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
+ raise Error, "Invalid value for --database option. Supported preconfigurations are: #{DATABASES.join(", ")}."
end
# Force sprockets and yarn to be skipped when generating API only apps.
# Can't modify options hash as it's frozen by default.
if options[:api]
- self.options = options.merge(skip_sprockets: true, skip_javascript: true, skip_yarn: true).freeze
+ self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze
end
+
+ @after_bundle_callbacks = []
end
public_task :set_default_accessors!
@@ -287,7 +291,7 @@ module Rails
build(:gitignore) unless options[:skip_git]
build(:gemfile) unless options[:skip_gemfile]
build(:version_control)
- build(:package_json) unless options[:skip_yarn]
+ build(:package_json) unless options[:skip_javascript]
end
def create_app_files
@@ -303,6 +307,13 @@ module Rails
end
remove_task :update_bin_files
+ def update_active_storage
+ unless skip_active_storage?
+ rails_command "active_storage:update"
+ end
+ end
+ remove_task :update_active_storage
+
def create_config_files
build(:config)
end
@@ -321,7 +332,7 @@ module Rails
end
def display_upgrade_guide_info
- say "\nAfter this, check Rails upgrade guide at http://guides.rubyonrails.org/upgrading_ruby_on_rails.html for more details about upgrading your app."
+ say "\nAfter this, check Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html for more details about upgrading your app."
end
remove_task :display_upgrade_guide_info
@@ -409,7 +420,7 @@ module Rails
def delete_js_folder_skipping_javascript
if options[:skip_javascript]
- remove_dir "app/assets/javascripts"
+ remove_dir "app/javascript"
end
end
@@ -436,8 +447,9 @@ module Rails
def delete_action_cable_files_skipping_action_cable
if options[:skip_action_cable]
- remove_file "app/assets/javascripts/cable.js"
+ remove_dir "app/javascript/channels"
remove_dir "app/channels"
+ remove_dir "test/channels"
end
end
@@ -460,8 +472,8 @@ module Rails
end
end
- def delete_bin_yarn_if_skip_yarn_option
- remove_file "bin/yarn" if options[:skip_yarn]
+ def delete_bin_yarn
+ remove_file "bin/yarn" if options[:skip_javascript]
end
def finish_template
@@ -469,7 +481,8 @@ module Rails
end
public_task :apply_rails_template, :run_bundle
- public_task :run_webpack, :generate_spring_binstubs
+ public_task :generate_bundler_binstub, :generate_spring_binstubs
+ public_task :run_webpack
def run_after_bundle_callbacks
@after_bundle_callbacks.each(&:call)
@@ -486,58 +499,14 @@ module Rails
create_file(*args, &block)
end
- def app_name
- @app_name ||= original_app_name.tr("-", "_")
- end
-
- def original_app_name
- @original_app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr('\\', "").tr(". ", "_")
- end
-
- def defined_app_name
- defined_app_const_base.underscore
- end
-
- def defined_app_const_base
- Rails.respond_to?(:application) && defined?(Rails::Application) &&
- Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "")
- end
-
- alias :defined_app_const_base? :defined_app_const_base
-
- def app_const_base
- @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, "_").squeeze("_").camelize
- end
- alias :camelized :app_const_base
-
- def app_const
- @app_const ||= "#{app_const_base}::Application"
- end
-
- def valid_const?
- if app_const =~ /^\d/
- raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers."
- elsif RESERVED_NAMES.include?(original_app_name)
- raise Error, "Invalid application name #{original_app_name}. Please give a " \
- "name which does not match one of the reserved rails " \
- "words: #{RESERVED_NAMES.join(", ")}"
- elsif Object.const_defined?(app_const_base)
- raise Error, "Invalid application name #{original_app_name}, constant #{app_const_base} is already in use. Please choose another application name."
- end
- end
-
- def mysql_socket
- @mysql_socket ||= [
- "/tmp/mysql.sock", # default
- "/var/run/mysqld/mysqld.sock", # debian/gentoo
- "/var/tmp/mysql.sock", # freebsd
- "/var/lib/mysql/mysql.sock", # fedora
- "/opt/local/lib/mysql/mysql.sock", # fedora
- "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
- "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
- "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
- "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
- ].find { |f| File.exist?(f) } unless Gem.win_platform?
+ # Registers a callback to be executed after bundle and spring binstubs
+ # have run.
+ #
+ # after_bundle do
+ # git add: '.'
+ # end
+ def after_bundle(&block) # :doc:
+ @after_bundle_callbacks << block
end
def get_builder_class
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
index 1567333023..d7221453e7 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
@@ -18,20 +18,17 @@ ruby <%= "'#{RUBY_VERSION}'" -%>
<% end -%>
<% end -%>
-# Use ActiveModel has_secure_password
+# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
<% unless skip_active_storage? -%>
-# Use ActiveStorage variant
+# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
<% end -%>
-# Use Capistrano for deployment
-# gem 'capistrano-rails', group: :development
-
<% if depend_on_bootsnap? -%>
# Reduces boot times through caching; required in config/boot.rb
-gem 'bootsnap', '>= 1.1.0', require: false
+gem 'bootsnap', '>= 1.4.2', require: false
<%- end -%>
<%- if options.api? -%>
@@ -45,6 +42,7 @@ group :development, :test do
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
+<% end -%>
group :development do
<%- unless options.api? -%>
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
@@ -71,11 +69,10 @@ group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
- # Easy installation and use of chromedriver to run system tests with Chrome
- gem 'chromedriver-helper'
+ # Easy installation and use of web drivers to run system tests with browsers
+ gem 'webdrivers'
end
<%- end -%>
-<% end -%>
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
index 70b579d10e..591819335f 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
@@ -1,5 +1,2 @@
//= link_tree ../images
-<% unless options.skip_javascript -%>
-//= link_directory ../javascripts .js
-<% end -%>
//= link_directory ../stylesheets .css
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
deleted file mode 100644
index 5183bcd256..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
+++ /dev/null
@@ -1,22 +0,0 @@
-// This is a manifest file that'll be compiled into application.js, which will include all the files
-// listed below.
-//
-// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
-// vendor/assets/javascripts directory can be referenced here using a relative path.
-//
-// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// compiled file. JavaScript code in this file should be added after the last require_* statement.
-//
-// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
-// about supported directives.
-//
-<% unless options[:skip_javascript] -%>
-//= require rails-ujs
-<% unless skip_active_storage? -%>
-//= require activestorage
-<% end -%>
-<% unless options[:skip_turbolinks] -%>
-//= require turbolinks
-<% end -%>
-<% end -%>
-//= require_tree .
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js
index 739aa5f022..0eceb59b18 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js
@@ -1,13 +1,6 @@
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
-//
-//= require action_cable
-//= require_self
-//= require_tree ./channels
-(function() {
- this.App || (this.App = {});
+import { createConsumer } from "@rails/actioncable"
- App.cable = ActionCable.createConsumer();
-
-}).call(this);
+export default createConsumer()
diff --git a/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js
new file mode 100644
index 0000000000..0cfcf74919
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js
@@ -0,0 +1,5 @@
+// Load all the channels within this directory and all subdirectories.
+// Channel files must be named *_channel.js.
+
+const channels = require.context('.', true, /_channel\.js$/)
+channels.keys().forEach(channels)
diff --git a/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt
new file mode 100644
index 0000000000..e67e742263
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt
@@ -0,0 +1,23 @@
+// This file is automatically compiled by Webpack, along with any other files
+// present in this directory. You're encouraged to place your actual application logic in
+// a relevant structure within app/javascript and only use these pack files to reference
+// that code so it'll be compiled.
+
+require("@rails/ujs").start()
+<%- unless options[:skip_turbolinks] -%>
+require("turbolinks").start()
+<%- end -%>
+<%- unless skip_active_storage? -%>
+require("@rails/activestorage").start()
+<%- end -%>
+<%- unless options[:skip_action_cable] -%>
+require("channels")
+<%- end -%>
+
+
+// Uncomment to copy all static images under ../images to the output folder and reference
+// them with the image_pack_tag helper in views (e.g <%%= image_pack_tag 'rails.png' %>)
+// or the `imagePath` JavaScript helper below.
+//
+// const images = require.context('../images', true)
+// const imagePath = (name) => images(name, true)
diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt
index a009ace51c..d394c3d106 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt
@@ -1,2 +1,7 @@
class ApplicationJob < ActiveJob::Base
+ # Automatically retry jobs that encountered a deadlock
+ # retry_on ActiveRecord::Deadlocked
+
+ # Most jobs are safe to ignore if the underlying records are no longer available
+ # discard_on ActiveJob::DeserializationError
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
index ef715f1368..9a7267c783 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -9,11 +9,11 @@
<%%= stylesheet_link_tag 'application', media: 'all' %>
<%- else -%>
<%- unless options[:skip_turbolinks] -%>
- <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
- <%%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
+ <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
+ <%%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%- else -%>
- <%%= stylesheet_link_tag 'application', media: 'all' %>
- <%%= javascript_include_tag 'application' %>
+ <%%= stylesheet_link_tag 'application', media: 'all' %>
+ <%%= javascript_pack_tag 'application' %>
<%- end -%>
<%- end -%>
</head>
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt b/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt
deleted file mode 100644
index a84f0afe47..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt
+++ /dev/null
@@ -1,2 +0,0 @@
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
-load Gem.bin_path('bundler', 'bundle')
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
index 233b5a1d95..3f73bae3da 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
@@ -1,5 +1,4 @@
require 'fileutils'
-include FileUtils
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
@@ -8,23 +7,23 @@ def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
-chdir APP_ROOT do
+FileUtils.chdir APP_ROOT do
# This script is a starting point to setup your application.
# Add necessary setup steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
-<% unless options.skip_yarn? -%>
+<% unless options.skip_javascript? -%>
- # Install JavaScript dependencies if using Yarn
+ # Install JavaScript dependencies
# system('bin/yarn')
<% end -%>
<% unless options.skip_active_record? -%>
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
- # cp 'config/database.yml.sample', 'config/database.yml'
+ # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
# end
puts "\n== Preparing database =="
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
index 70cc71d83b..03b77d0d46 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
@@ -1,5 +1,4 @@
require 'fileutils'
-include FileUtils
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
@@ -8,27 +7,27 @@ def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
-chdir APP_ROOT do
+FileUtils.chdir APP_ROOT do
# This script is a way to update your development environment automatically.
# Add necessary update steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
-<% unless options.skip_yarn? -%>
+<% unless options.skip_javascript? -%>
- # Install JavaScript dependencies if using Yarn
+ # Install JavaScript dependencies
# system('bin/yarn')
<% end -%>
<% unless options.skip_active_record? -%>
puts "\n== Updating database =="
- system! 'bin/rails db:migrate'
+ system! 'rails db:migrate'
<% end -%>
puts "\n== Removing old logs and tempfiles =="
- system! 'bin/rails log:clear tmp:clear'
+ system! 'rails log:clear tmp:clear'
puts "\n== Restarting application server =="
- system! 'bin/rails restart'
+ system! 'rails restart'
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt
index 9a427113c7..1b0ee54071 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt
@@ -11,6 +11,8 @@ require "active_job/railtie"
<%= comment_if :skip_active_storage %>require "active_storage/engine"
require "action_controller/railtie"
<%= comment_if :skip_action_mailer %>require "action_mailer/railtie"
+<%= comment_if :skip_action_mailbox %>require "action_mailbox/engine"
+<%= comment_if :skip_action_text %>require "action_text/engine"
require "action_view/railtie"
<%= comment_if :skip_action_cable %>require "action_cable/engine"
<%= comment_if :skip_sprockets %>require "sprockets/railtie"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt
index 8e53156c71..f69dc91b92 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt
@@ -2,7 +2,7 @@ development:
adapter: async
test:
- adapter: async
+ adapter: test
production:
adapter: redis
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt
index 917b52e535..6ab4a26084 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt
@@ -24,12 +24,12 @@ test:
<<: *default
database: <%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt
index d40117a27f..e422aa31fc 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt
@@ -60,12 +60,12 @@ test:
<<: *default
database: <%= app_name[0,4] %>_tst
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt
index 563be77710..678455c622 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt
@@ -54,12 +54,12 @@ test:
<<: *default
url: jdbc:db://localhost/<%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt
index 2a67bdca25..b5a0efef47 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt
@@ -1,4 +1,4 @@
-# MySQL. Versions 5.1.10 and up are supported.
+# MySQL. Versions 5.5.8 and up are supported.
#
# Install the MySQL driver:
# gem install activerecord-jdbcmysql-adapter
@@ -27,12 +27,12 @@ test:
<<: *default
database: <%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt
index 70df04079d..009a81a6b8 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt
@@ -1,4 +1,4 @@
-# PostgreSQL. Versions 9.1 and up are supported.
+# PostgreSQL. Versions 9.3 and up are supported.
#
# Configure Using Gemfile
# gem 'activerecord-jdbcpostgresql-adapter'
@@ -43,12 +43,12 @@ test:
<<: *default
database: <%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt
index 04afaa0596..386eb511e5 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt
@@ -1,4 +1,4 @@
-# MySQL. Versions 5.1.10 and up are supported.
+# MySQL. Versions 5.5.8 and up are supported.
#
# Install the MySQL driver
# gem install mysql2
@@ -11,7 +11,7 @@
#
default: &default
adapter: mysql2
- encoding: utf8
+ encoding: utf8mb4
pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
@@ -32,12 +32,12 @@ test:
<<: *default
database: <%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt
index 6da0601b24..f7b6dfafab 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt
@@ -33,12 +33,12 @@ test:
<<: *default
database: <%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt
index 4ce4d276b7..44dafbd0c0 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt
@@ -1,4 +1,4 @@
-# PostgreSQL. Versions 9.1 and up are supported.
+# PostgreSQL. Versions 9.3 and up are supported.
#
# Install the pg driver:
# gem install pg
@@ -18,7 +18,7 @@ default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
- # http://guides.rubyonrails.org/configuring.html#database-pooling
+ # https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
@@ -59,12 +59,12 @@ test:
<<: *default
database: <%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt
index 049de65f22..27e8f2f35d 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt
@@ -26,12 +26,12 @@ test:
<<: *default
database: <%= app_name %>_test
-# As with config/secrets.yml, you never want to store sensitive information,
+# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 3807c8a9aa..2887bc2d67 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -16,6 +16,7 @@ Rails.application.configure do
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist?
config.action_controller.perform_caching = true
+ config.action_controller.enable_fragment_cache_logging = true
config.cache_store = :memory_store
config.public_file_server.headers = {
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index d646694477..ed1cf09eeb 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -23,12 +23,7 @@ Rails.application.configure do
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
<%- unless options.skip_sprockets? -%>
- <%- if options.skip_javascript? -%>
- # Compress CSS.
- <%- else -%>
- # Compress JavaScripts and CSS.
- config.assets.js_compressor = :uglifier
- <%- end -%>
+ # Compress CSS using a preprocessor.
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
@@ -69,7 +64,7 @@ Rails.application.configure do
# Use a real queuing backend for Active Job (and separate queues per environment).
# config.active_job.queue_adapter = :resque
- # config.active_job.queue_name_prefix = "<%= app_name %>_#{Rails.env}"
+ # config.active_job.queue_name_prefix = "<%= app_name %>_production"
<%- unless options.skip_action_mailer? -%>
config.action_mailer.perform_caching = false
@@ -103,4 +98,25 @@ Rails.application.configure do
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
<%- end -%>
+
+ # Inserts middleware to perform automatic connection switching.
+ # The `database_selector` hash is used to pass options to the DatabaseSelector
+ # middleware. The `delay` is used to determine how long to wait after a write
+ # to send a subsequent read to the primary.
+ #
+ # The `database_resolver` class is used by the middleware to determine which
+ # database is appropriate to use based on the time delay.
+ #
+ # The `database_resolver_context` class is used by the middleware to set
+ # timestamps for the last write to the primary. The resolver uses the context
+ # class timestamps to determine how long to wait before reading from the
+ # replica.
+ #
+ # By default Rails will store a last write timestamp in the session. The
+ # DatabaseSelector middleware is designed as such you can define your own
+ # strategy for connection switching and pass that into the middleware through
+ # these configuration options.
+ # config.active_record.database_selector = { delay: 2.seconds }
+ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index 82f2a8aebe..63ed3fa952 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -21,6 +21,7 @@ Rails.application.configure do
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
+ config.cache_store = :null_store
# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
@@ -47,7 +48,4 @@ Rails.application.configure do
# Raises error for missing translations.
# config.action_view.raise_on_missing_translations = true
-
- # Prevent expensive template finalization at end of test suite runs.
- config.action_view.finalize_compiled_template_methods = false
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
index 51196ae743..e92382f2d9 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
@@ -5,7 +5,7 @@ Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
-<%- unless options[:skip_yarn] -%>
+<%- unless options[:skip_javascript] -%>
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
<%- end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt
index d3bcaa5ec8..c517b0f96b 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt
@@ -11,6 +11,10 @@
# policy.object_src :none
# policy.script_src :self, :https
# policy.style_src :self, :https
+<%- unless options[:skip_javascript] -%>
+# # If you are using webpack-dev-server then specify webpack-dev-server host
+# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
+<%- end -%>
# # Specify URI for violation reports
# # policy.report_uri "/csp-violation-report-endpoint"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt
index 179b97de4a..d25552e923 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt
@@ -6,5 +6,28 @@
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
-# Don't force requests from old versions of IE to be UTF-8 encoded
+# Don't force requests from old versions of IE to be UTF-8 encoded.
# Rails.application.config.action_view.default_enforce_utf8 = false
+
+# Embed purpose and expiry metadata inside signed and encrypted
+# cookies for increased security.
+#
+# This option is not backwards compatible with earlier Rails versions.
+# It's best enabled when your entire app is migrated and stable on 6.0.
+# Rails.application.config.action_dispatch.use_cookies_with_metadata = true
+
+# Return false instead of self when enqueuing is aborted from a callback.
+# Rails.application.config.active_job.return_false_on_aborted_enqueue = true
+
+# Send Active Storage analysis and purge jobs to dedicated queues.
+# Rails.application.config.active_storage.queues.analysis = :active_storage_analysis
+# Rails.application.config.active_storage.queues.purge = :active_storage_purge
+
+# Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail.
+#
+# The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob),
+# will be removed in Rails 6.1. This setting is not backwards compatible with earlier Rails versions.
+# If you send mail in the background, job workers need to have a copy of
+# MailDeliveryJob to ensure all delivery jobs are processed properly.
+# Make sure your entire app is migrated and stable on 6.0 before using this setting.
+# Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
index decc5a8573..cf9b342d0a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
@@ -27,7 +27,7 @@
# 'true': 'foo'
#
# To learn more, please read the Rails Internationalization guide
-# available at http://guides.rubyonrails.org/i18n.html.
+# available at https://guides.rubyonrails.org/i18n.html.
en:
hello: "Hello world"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt
index f6146e7259..649253aeca 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt
@@ -17,7 +17,7 @@ port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the number of `workers` to boot in clustered mode.
-# Workers are forked webserver processes. If using threads and workers together
+# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt
index 787824f888..c06383a172 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt
@@ -1,3 +1,3 @@
Rails.application.routes.draw do
- # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
+ # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore.tt b/railties/lib/rails/generators/rails/app/templates/gitignore.tt
index 4e114fb1d9..860baa1595 100644
--- a/railties/lib/rails/generators/rails/app/templates/gitignore.tt
+++ b/railties/lib/rails/generators/rails/app/templates/gitignore.tt
@@ -22,19 +22,14 @@
<% end -%>
<% unless skip_active_storage? -%>
-# Ignore uploaded files in development
+# Ignore uploaded files in development.
/storage/*
<% if keeps? -%>
!/storage/.keep
<% end -%>
<% end -%>
-
-<% unless options.skip_yarn? -%>
-/node_modules
-/yarn-error.log
-
-<% end -%>
<% unless options.api? -%>
+
/public/assets
<% end -%>
.byebug_history
diff --git a/railties/lib/rails/generators/rails/app/templates/package.json.tt b/railties/lib/rails/generators/rails/app/templates/package.json.tt
index 46db57dcbe..07207e1747 100644
--- a/railties/lib/rails/generators/rails/app/templates/package.json.tt
+++ b/railties/lib/rails/generators/rails/app/templates/package.json.tt
@@ -1,5 +1,11 @@
{
"name": "<%= app_name %>",
"private": true,
- "dependencies": {}
+ "dependencies": {
+ "@rails/ujs": "^6.0.0-alpha"<% unless options[:skip_turbolinks] %>,
+ "turbolinks": "^5.2.0"<% end -%><% unless skip_active_storage? %>,
+ "@rails/activestorage": "^6.0.0-alpha"<% end -%><% unless options[:skip_action_cable] %>,
+ "@rails/actioncable": "^6.0.0-alpha"<% end %>
+ },
+ "version": "0.1.0"
}
diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt
index bac1339923..096cfd36a8 100644
--- a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt
+++ b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt
@@ -1 +1 @@
-<%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" -%>
+<%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
diff --git a/railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt
new file mode 100644
index 0000000000..800405f15e
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt
@@ -0,0 +1,11 @@
+require "test_helper"
+
+class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase
+ # test "connects with cookies" do
+ # cookies.signed[:user_id] = 42
+ #
+ # connect
+ #
+ # assert_equal connection.user_id, "42"
+ # end
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
index c918b57eca..47b4cf745c 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
@@ -4,10 +4,10 @@ require 'rails/test_help'
class ActiveSupport::TestCase
# Run tests in parallel with specified workers
-<% if defined?(JRUBY_VERSION) -%>
- parallelize(workers: 2, with: :threads)
+<% if defined?(JRUBY_VERSION) || Gem.win_platform? -%>
+ parallelize(workers: :number_of_processors, with: :threads)
<%- else -%>
- parallelize(workers: 2)
+ parallelize(workers: :number_of_processors)
<% end -%>
<% unless options[:skip_active_record] -%>
diff --git a/railties/lib/rails/generators/rails/assets/USAGE b/railties/lib/rails/generators/rails/assets/USAGE
index d2e5ed4482..ee73d05808 100644
--- a/railties/lib/rails/generators/rails/assets/USAGE
+++ b/railties/lib/rails/generators/rails/assets/USAGE
@@ -5,16 +5,13 @@ Description:
To create an asset within a folder, specify the asset's name as a
path like 'parent/name'.
- This generates a JavaScript stub in app/assets/javascripts and a stylesheet
- stub in app/assets/stylesheets.
+ This generates a stylesheet stub in app/assets/stylesheets.
- If CoffeeScript is available, JavaScripts will be generated with the .coffee extension.
If Sass 3 is available, stylesheets will be generated with the .scss extension.
Example:
`rails generate assets posts`
Posts assets.
- JavaScript: app/assets/javascripts/posts.js
Stylesheet: app/assets/stylesheets/posts.css
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
index ffb695a1f3..9ce8570172 100644
--- a/railties/lib/rails/generators/rails/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -3,22 +3,14 @@
module Rails
module Generators
class AssetsGenerator < NamedBase # :nodoc:
- class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
-
- class_option :javascript_engine, desc: "Engine for JavaScripts"
class_option :stylesheet_engine, desc: "Engine for Stylesheets"
private
-
def asset_name
file_name
end
- hook_for :javascript_engine do |javascript_engine|
- invoke javascript_engine, [name] if options[:javascripts]
- end
-
hook_for :stylesheet_engine do |stylesheet_engine|
invoke stylesheet_engine, [name] if options[:stylesheets]
end
diff --git a/railties/lib/rails/generators/rails/assets/templates/javascript.js b/railties/lib/rails/generators/rails/assets/templates/javascript.js
deleted file mode 100644
index dee720facd..0000000000
--- a/railties/lib/rails/generators/rails/assets/templates/javascript.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place all the behaviors and hooks related to the matching controller here.
-// All this logic will automatically be available in application.js.
diff --git a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb
index 719e0c1e4c..99b935aa6a 100644
--- a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb
+++ b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb
@@ -20,7 +20,7 @@ module Rails
add_credentials_file_silently(template)
- say "You can edit encrypted credentials with `bin/rails credentials:edit`."
+ say "You can edit encrypted credentials with `rails credentials:edit`."
say ""
end
end
diff --git a/railties/lib/rails/generators/rails/db/system/change/change_generator.rb b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb
new file mode 100644
index 0000000000..24db92fad7
--- /dev/null
+++ b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require "rails/generators/base"
+
+module Rails
+ module Generators
+ module Db
+ module System
+ class ChangeGenerator < Base # :nodoc:
+ include Database
+ include AppName
+
+ class_option :to, required: true,
+ desc: "The database system to switch to."
+
+ def self.default_generator_root
+ path = File.expand_path(File.join(base_name, "app"), base_root)
+ path if File.exist?(path)
+ end
+
+ def initialize(*)
+ super
+
+ unless DATABASES.include?(options[:to])
+ raise Error, "Invalid value for --to option. Supported preconfigurations are: #{DATABASES.join(", ")}."
+ end
+
+ opt = options.dup
+ opt[:database] ||= opt[:to]
+ self.options = opt.freeze
+ end
+
+ def edit_database_config
+ template("config/databases/#{options[:database]}.yml", "config/database.yml")
+ end
+
+ def edit_gemfile
+ name, version = gem_for_database
+ gsub_file("Gemfile", all_database_gems_regex, name)
+ gsub_file("Gemfile", gem_entry_regex_for(name), gem_entry_for(name, *version))
+ end
+
+ private
+ def all_database_gems
+ DATABASES.map { |database| gem_for_database(database) }
+ end
+
+ def all_database_gems_regex
+ all_database_gem_names = all_database_gems.map(&:first)
+ /(\b#{all_database_gem_names.join('\b|\b')}\b)/
+ end
+
+ def gem_entry_regex_for(gem_name)
+ /^gem.*\b#{gem_name}\b.*/
+ end
+
+ def gem_entry_for(*gem_name_and_version)
+ gem_name_and_version.map! { |segment| "'#{segment}'" }
+ "gem #{gem_name_and_version.join(", ")}"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/helper/helper_generator.rb b/railties/lib/rails/generators/rails/helper/helper_generator.rb
index 3837c10ca0..542eb4c9e8 100644
--- a/railties/lib/rails/generators/rails/helper/helper_generator.rb
+++ b/railties/lib/rails/generators/rails/helper/helper_generator.rb
@@ -10,6 +10,11 @@ module Rails
end
hook_for :test_framework
+
+ private
+ def file_name
+ @_file_name ||= super.sub(/_helper\z/i, "")
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index a83c911806..79a06648b5 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -88,7 +88,7 @@ task default: :test
PASSTHROUGH_OPTIONS = [
:skip_active_record, :skip_active_storage, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database,
- :javascript, :skip_yarn, :api, :quiet, :pretend, :skip
+ :api, :quiet, :pretend, :skip
]
def generate_test_dummy(force = false)
@@ -98,6 +98,7 @@ task default: :test
opts[:skip_listen] = true
opts[:skip_git] = true
opts[:skip_turbolinks] = true
+ opts[:skip_webpack_install] = true
opts[:dummy_app] = true
invoke Rails::Generators::AppGenerator,
@@ -113,7 +114,7 @@ task default: :test
end
def test_dummy_assets
- template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true
+ template "rails/javascripts.js", "#{dummy_path}/app/javascript/packs/application.js", force: true
template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/config/manifest.js", force: true
end
@@ -262,16 +263,6 @@ task default: :test
public_task :apply_rails_template
- def run_after_bundle_callbacks
- unless @after_bundle_callbacks.empty?
- ActiveSupport::Deprecation.warn("`after_bundle` is deprecated and will be removed in the next version of Rails. ")
- end
-
- @after_bundle_callbacks.each do |callback|
- callback.call
- end
- end
-
def name
@name ||= begin
# same as ActiveSupport::Inflector#underscore except not replacing '-'
@@ -348,9 +339,9 @@ task default: :test
def wrap_in_modules(unwrapped_code)
unwrapped_code = "#{unwrapped_code}".strip.gsub(/\s$\n/, "")
modules.reverse.inject(unwrapped_code) do |content, mod|
- str = "module #{mod}\n"
- str += content.lines.map { |line| " #{line}" }.join
- str += content.present? ? "\nend" : "end"
+ str = +"module #{mod}\n"
+ str << content.lines.map { |line| " #{line}" }.join
+ str << (content.present? ? "\nend" : "end")
end
end
@@ -385,11 +376,11 @@ task default: :test
end
def valid_const?
- if original_name =~ /-\d/
+ if /-\d/.match?(original_name)
raise Error, "Invalid plugin name #{original_name}. Please give a name which does not contain a namespace starting with numeric characters."
- elsif original_name =~ /[^\w-]+/
+ elsif /[^\w-]+/.match?(original_name)
raise Error, "Invalid plugin name #{original_name}. Please give a name which uses only alphabetic, numeric, \"_\" or \"-\" characters."
- elsif camelized =~ /^\d/
+ elsif /^\d/.match?(camelized)
raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers."
elsif RESERVED_NAMES.include?(name)
raise Error, "Invalid plugin name #{original_name}. Please give a " \
diff --git a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt
index 9a8c4bf098..405642c850 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt
+++ b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt
@@ -4,21 +4,30 @@ $:.push File.expand_path("lib", __dir__)
require "<%= namespaced_name %>/version"
# Describe your gem and declare its dependencies:
-Gem::Specification.new do |s|
- s.name = "<%= name %>"
- s.version = <%= camelized_modules %>::VERSION
- s.authors = ["<%= author %>"]
- s.email = ["<%= email %>"]
- s.homepage = "TODO"
- s.summary = "TODO: Summary of <%= camelized_modules %>."
- s.description = "TODO: Description of <%= camelized_modules %>."
- s.license = "MIT"
+Gem::Specification.new do |spec|
+ spec.name = "<%= name %>"
+ spec.version = <%= camelized_modules %>::VERSION
+ spec.authors = ["<%= author %>"]
+ spec.email = ["<%= email %>"]
+ spec.homepage = "TODO"
+ spec.summary = "TODO: Summary of <%= camelized_modules %>."
+ spec.description = "TODO: Description of <%= camelized_modules %>."
+ spec.license = "MIT"
- s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
+ if spec.respond_to?(:metadata)
+ spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
+ else
+ raise "RubyGems 2.0 or newer is required to protect against " \
+ "public gem pushes."
+ end
- <%= '# ' if options.dev? || options.edge? -%>s.add_dependency "rails", "<%= Array(rails_version_specifier).join('", "') %>"
+ spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
+
+ <%= '# ' if options.dev? || options.edge? -%>spec.add_dependency "rails", "<%= Array(rails_version_specifier).join('", "') %>"
<% unless options[:skip_active_record] -%>
- s.add_development_dependency "<%= gem_for_database[0] %>"
+ spec.add_development_dependency "<%= gem_for_database[0] %>"
<% end -%>
end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt b/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt
index 7a68da5c4b..0aabf09252 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt
+++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt
@@ -7,7 +7,7 @@ pkg/
<%= dummy_path %>/db/*.sqlite3-journal
<% end -%>
<%= dummy_path %>/log/*.log
-<% unless options[:skip_yarn] -%>
+<% unless options[:skip_javascript] -%>
<%= dummy_path %>/node_modules/
<%= dummy_path %>/yarn-error.log
<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt
index 755d19ef5d..4f7a8d3d6e 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt
@@ -10,8 +10,7 @@ ActiveRecord::Migrator.migrations_paths << File.expand_path('../db/migrate', __d
<% end -%>
require "rails/test_help"
-# Filter out Minitest backtrace while allowing backtrace from other libraries
-# to be shown.
+# Filter out the backtrace from minitest while preserving the one from other libraries.
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
<% unless engine? -%>
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
index 7030561a33..8b46eb88ae 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
@@ -32,6 +32,14 @@ module Rails
hook_for :helper, as: :scaffold do |invoked|
invoke invoked, [ controller_name ]
end
+
+ private
+
+ def permitted_params
+ params = attributes_names.map { |name| ":#{name}" }.join(", ")
+ params += attributes.select(&:attachments?).map { |a| ", #{a.name}: []" }.join
+ params
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt
index 400afec6dc..bb26370276 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt
@@ -54,7 +54,7 @@ class <%= controller_class_name %>Controller < ApplicationController
<%- if attributes_names.empty? -%>
params.fetch(:<%= singular_table_name %>, {})
<%- else -%>
- params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
+ params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
<%- end -%>
end
end
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt
index 05f1c2b2d3..82b43987b4 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt
@@ -61,7 +61,7 @@ class <%= controller_class_name %>Controller < ApplicationController
<%- if attributes_names.empty? -%>
params.fetch(:<%= singular_table_name %>, {})
<%- else -%>
- params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
+ params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
<%- end -%>
end
end
diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
index ae307c5cd9..ba27ed329b 100644
--- a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
+++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
@@ -10,6 +10,12 @@ module TestUnit # :nodoc:
def create_test_files
template "integration_test.rb", File.join("test/integration", class_path, "#{file_name}_test.rb")
end
+
+ private
+
+ def file_name
+ @_file_name ||= super.sub(/_test\z/i, "")
+ end
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt
index 0681780c97..0fd9f305d7 100644
--- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt
+++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt
@@ -1,4 +1,4 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
<% unless attributes.empty? -%>
<% %w(one two).each do |name| %>
<%= name %>:
@@ -7,7 +7,7 @@
password_digest: <%%= BCrypt::Password.create('secret') %>
<%- elsif attribute.reference? -%>
<%= yaml_key_value(attribute.column_name.sub(/_id$/, ''), attribute.default || name) %>
- <%- else -%>
+ <%- elsif !attribute.virtual? -%>
<%= yaml_key_value(attribute.column_name, attribute.default) %>
<%- end -%>
<%- if attribute.polymorphic? -%>
diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
index e2e8b18eab..6df50c3217 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -54,6 +54,11 @@ module TestUnit # :nodoc:
end
end.sort.to_h
end
+
+ def boolean?(name)
+ attribute = attributes.find { |attr| attr.name == name }
+ attribute&.type == :boolean
+ end
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt
index f83f5a5c62..4f5bbf1108 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt
@@ -16,7 +16,11 @@ class <%= class_name.pluralize %>Test < ApplicationSystemTestCase
click_on "New <%= class_name.titleize %>"
<%- attributes_hash.each do |attr, value| -%>
- fill_in "<%= attr.humanize.titleize %>", with: <%= value %>
+ <%- if boolean?(attr) -%>
+ check "<%= attr.humanize %>" if <%= value %>
+ <%- else -%>
+ fill_in "<%= attr.humanize %>", with: <%= value %>
+ <%- end -%>
<%- end -%>
click_on "Create <%= human_name %>"
@@ -29,7 +33,11 @@ class <%= class_name.pluralize %>Test < ApplicationSystemTestCase
click_on "Edit", match: :first
<%- attributes_hash.each do |attr, value| -%>
- fill_in "<%= attr.humanize.titleize %>", with: <%= value %>
+ <%- if boolean?(attr) -%>
+ check "<%= attr.humanize %>" if <%= value %>
+ <%- else -%>
+ fill_in "<%= attr.humanize %>", with: <%= value %>
+ <%- end -%>
<%- end -%>
click_on "Update <%= human_name %>"
diff --git a/railties/lib/rails/generators/test_unit/system/system_generator.rb b/railties/lib/rails/generators/test_unit/system/system_generator.rb
index 08504d4124..adecf74b70 100644
--- a/railties/lib/rails/generators/test_unit/system/system_generator.rb
+++ b/railties/lib/rails/generators/test_unit/system/system_generator.rb
@@ -14,6 +14,11 @@ module TestUnit # :nodoc:
template "system_test.rb", File.join("test/system", class_path, "#{file_name.pluralize}_test.rb")
end
+
+ private
+ def file_name
+ @_file_name ||= super.sub(/_test\z/i, "")
+ end
end
end
end
diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb
index 6ab88bd59f..ec29ad12ba 100644
--- a/railties/lib/rails/generators/testing/behaviour.rb
+++ b/railties/lib/rails/generators/testing/behaviour.rb
@@ -67,6 +67,9 @@ module Rails
def run_generator(args = default_arguments, config = {})
capture(:stdout) do
args += ["--skip-bundle"] unless args.include? "--dev"
+ args |= ["--skip-bootsnap"] unless args.include? "--no-skip-bootsnap"
+ args |= ["--skip-webpack-install"] unless args.include? "--no-skip-webpack-install"
+
generator_class.start(args, config.reverse_merge(destination_root: destination_root))
end
end
diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
index d5c9973c6b..72b555ec19 100644
--- a/railties/lib/rails/info.rb
+++ b/railties/lib/rails/info.rb
@@ -41,7 +41,7 @@ module Rails
alias inspect to_s
def to_html
- "<table>".dup.tap do |table|
+ (+"<table>").tap do |table|
properties.each do |(name, value)|
table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
formatted_value = if value.kind_of?(Array)
@@ -63,7 +63,7 @@ module Rails
# The Ruby version and platform, e.g. "2.0.0-p247 (x86_64-darwin12.4.0)".
property "Ruby version" do
- "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
+ RUBY_DESCRIPTION
end
# The RubyGems version, if it's installed.
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
index b4f4a5922a..f74d979721 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -4,7 +4,7 @@ require "rails/application_controller"
require "action_dispatch/routing/inspector"
class Rails::InfoController < Rails::ApplicationController # :nodoc:
- prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
+ prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
layout -> { request.xhr? ? false : "application" }
before_action :require_local!
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index 0b0e802358..5cffa52860 100644
--- a/railties/lib/rails/mailers_controller.rb
+++ b/railties/lib/rails/mailers_controller.rb
@@ -3,13 +3,15 @@
require "rails/application_controller"
class Rails::MailersController < Rails::ApplicationController # :nodoc:
- prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
+ prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
before_action :require_local!, unless: :show_previews?
before_action :find_preview, :set_locale, only: :preview
helper_method :part_query, :locale_query
+ content_security_policy(false)
+
def index
@previews = ActionMailer::Preview.all
@page_title = "Mailer Previews"
@@ -36,7 +38,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
end
else
@part = find_preferred_part(request.format, Mime[:html], Mime[:text])
- render action: "email", layout: false, formats: %w[html]
+ render action: "email", layout: false, formats: [:html]
end
else
raise AbstractController::ActionNotFound, "Email '#{@email_action}' not found in #{@preview.name}"
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 87222563fd..8367ac8980 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -113,10 +113,11 @@ module Rails
attr_accessor :glob
def initialize(root, current, paths, options = {})
- @paths = paths
- @current = current
- @root = root
- @glob = options[:glob]
+ @paths = paths
+ @current = current
+ @root = root
+ @glob = options[:glob]
+ @exclude = options[:exclude]
options[:autoload_once] ? autoload_once! : skip_autoload_once!
options[:eager_load] ? eager_load! : skip_eager_load!
@@ -189,13 +190,11 @@ module Rails
raise "You need to set a path root" unless @root.path
result = []
- each do |p|
- path = File.expand_path(p, @root.path)
+ each do |path|
+ path = File.expand_path(path, @root.path)
if @glob && File.directory?(path)
- Dir.chdir(path) do
- result.concat(Dir.glob(@glob).map { |file| File.join path, file }.sort)
- end
+ result.concat files_in(path)
else
result << path
end
@@ -222,6 +221,17 @@ module Rails
end
alias to_a expanded
+
+ private
+
+ def files_in(path)
+ Dir.chdir(path) do
+ files = Dir.glob(@glob)
+ files -= @exclude if @exclude
+ files.map! { |file| File.join(path, file) }
+ files.sort
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index 4ea7e40319..3a95b55811 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -50,7 +50,7 @@ module Rails
'Started %s "%s" for %s at %s' % [
request.request_method,
request.filtered_path,
- request.ip,
+ request.remote_ip,
Time.now.to_default_s ]
end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 88dd932370..a67b90e285 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -224,7 +224,7 @@ module Rails
end
def railtie_namespace #:nodoc:
- @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
+ @railtie_namespace ||= self.class.module_parents.detect { |n| n.respond_to?(:railtie_namespace) }
end
protected
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
index b2d44d9b8e..ab5339bf24 100644
--- a/railties/lib/rails/ruby_version_check.rb
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -1,15 +1,15 @@
# frozen_string_literal: true
-if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4.1") && RUBY_ENGINE == "ruby"
+if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5.0") && RUBY_ENGINE == "ruby"
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
- Rails 6 requires Ruby 2.4.1 or newer.
+ Rails 6 requires Ruby 2.5.0 or newer.
You're running
#{desc}
- Please upgrade to Ruby 2.4.1 or newer to continue.
+ Please upgrade to Ruby 2.5.0 or newer to continue.
end_message
end
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 2d66a4dc7d..d7170e6282 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -50,7 +50,7 @@ module Rails
# If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
# Otherwise the string contains just line and text.
def to_s(options = {})
- s = "[#{line.to_s.rjust(options[:indent])}] ".dup
+ s = +"[#{line.to_s.rjust(options[:indent])}] "
s << "[#{tag}] " if options[:tag]
s << text
end
diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb
index 56f2eba312..2f644a20c9 100644
--- a/railties/lib/rails/tasks.rb
+++ b/railties/lib/rails/tasks.rb
@@ -12,6 +12,7 @@ require "rake"
middleware
misc
restart
+ routes
tmp
yarn
).tap { |arr|
diff --git a/railties/lib/rails/tasks/annotations.rake b/railties/lib/rails/tasks/annotations.rake
index 65af778a15..3a78de418a 100644
--- a/railties/lib/rails/tasks/annotations.rake
+++ b/railties/lib/rails/tasks/annotations.rake
@@ -2,20 +2,20 @@
require "rails/source_annotation_extractor"
-task :notes do
+task notes: :environment do
Rails::SourceAnnotationExtractor::Annotation.notes_task_deprecation_warning
Rails::Command.invoke :notes
end
namespace :notes do
["OPTIMIZE", "FIXME", "TODO"].each do |annotation|
- task annotation.downcase.intern do
+ task annotation.downcase.intern => :environment do
Rails::SourceAnnotationExtractor::Annotation.notes_task_deprecation_warning
Rails::Command.invoke :notes, ["--annotations", annotation]
end
end
- task :custom do
+ task custom: :environment do
Rails::SourceAnnotationExtractor::Annotation.notes_task_deprecation_warning
Rails::Command.invoke :notes, ["--annotations", ENV["ANNOTATION"]]
end
diff --git a/railties/lib/rails/tasks/dev.rake b/railties/lib/rails/tasks/dev.rake
index 5aea6f7dc5..716fb6a331 100644
--- a/railties/lib/rails/tasks/dev.rake
+++ b/railties/lib/rails/tasks/dev.rake
@@ -1,10 +1,11 @@
# frozen_string_literal: true
-require "rails/dev_caching"
+require "rails/command"
+require "active_support/deprecation"
namespace :dev do
- desc "Toggle development mode caching on/off"
- task :cache do
- Rails::DevCaching.enable_by_file
+ task cache: :environment do
+ ActiveSupport::Deprecation.warn("Using `bin/rake dev:cache` is deprecated and will be removed in Rails 6.1. Use `bin/rails dev:cache` instead.\n")
+ Rails::Command.invoke "dev:cache"
end
end
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index 1a3711c446..2886986865 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -2,7 +2,7 @@
namespace :app do
desc "Update configs and some other initially generated files (or use just update:configs or update:bin)"
- task update: [ "update:configs", "update:bin", "update:upgrade_guide_info" ]
+ task update: [ "update:configs", "update:bin", "update:active_storage", "update:upgrade_guide_info" ]
desc "Applies the template supplied by LOCATION=(/path/to/template) or URL"
task template: :environment do
@@ -51,6 +51,10 @@ namespace :app do
Rails::AppUpdater.invoke_from_app_generator :update_bin_files
end
+ task :active_storage do
+ Rails::AppUpdater.invoke_from_app_generator :update_active_storage
+ end
+
task :upgrade_guide_info do
Rails::AppUpdater.invoke_from_app_generator :display_upgrade_guide_info
end
diff --git a/railties/lib/rails/tasks/initializers.rake b/railties/lib/rails/tasks/initializers.rake
index ae85cb0f86..f108517d1d 100644
--- a/railties/lib/rails/tasks/initializers.rake
+++ b/railties/lib/rails/tasks/initializers.rake
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-desc "Print out all defined initializers in the order they are invoked by Rails."
+require "rails/command"
+require "active_support/deprecation"
+
task initializers: :environment do
- Rails.application.initializers.tsort_each do |initializer|
- puts "#{initializer.context_class}.#{initializer.name}"
- end
+ ActiveSupport::Deprecation.warn("Using `bin/rake initializers` is deprecated and will be removed in Rails 6.1. Use `bin/rails initializers` instead.\n")
+ Rails::Command.invoke "initializers"
end
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
new file mode 100644
index 0000000000..21ce900a8c
--- /dev/null
+++ b/railties/lib/rails/tasks/routes.rake
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require "rails/command"
+require "active_support/deprecation"
+
+task routes: :environment do
+ ActiveSupport::Deprecation.warn("Using `bin/rake routes` is deprecated and will be removed in Rails 6.1. Use `bin/rails routes` instead.\n")
+ Rails::Command.invoke "routes"
+end
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index 594db91eec..5abba7d3b4 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -9,14 +9,18 @@ STATS_DIRECTORIES = [
%w(Jobs app/jobs),
%w(Models app/models),
%w(Mailers app/mailers),
+ %w(Mailboxes app/mailboxes),
%w(Channels app/channels),
%w(JavaScripts app/assets/javascripts),
+ %w(JavaScript app/javascript),
%w(Libraries lib/),
%w(APIs app/apis),
%w(Controller\ tests test/controllers),
%w(Helper\ tests test/helpers),
%w(Model\ tests test/models),
%w(Mailer\ tests test/mailers),
+ %w(Mailbox\ tests test/mailboxes),
+ %w(Channel\ tests test/channels),
%w(Job\ tests test/jobs),
%w(Integration\ tests test/integration),
%w(System\ tests test/system),
diff --git a/railties/lib/rails/tasks/yarn.rake b/railties/lib/rails/tasks/yarn.rake
index cf45a392e8..48a8d8e143 100644
--- a/railties/lib/rails/tasks/yarn.rake
+++ b/railties/lib/rails/tasks/yarn.rake
@@ -6,10 +6,9 @@ namespace :yarn do
# Install only production deps when for not usual envs.
valid_node_envs = %w[test development production]
node_env = ENV.fetch("NODE_ENV") do
- rails_env = ENV["RAILS_ENV"]
- valid_node_envs.include?(rails_env) ? rails_env : "production"
+ valid_node_envs.include?(Rails.env) ? Rails.env : "production"
end
- system({ "NODE_ENV" => node_env }, "./bin/yarn install --no-progress --frozen-lockfile")
+ system({ "NODE_ENV" => node_env }, "#{Rails.root}/bin/yarn install --no-progress --frozen-lockfile")
end
end
diff --git a/railties/lib/rails/templates/rails/welcome/index.html.erb b/railties/lib/rails/templates/rails/welcome/index.html.erb
index b6823457c0..6750e01029 100644
--- a/railties/lib/rails/templates/rails/welcome/index.html.erb
+++ b/railties/lib/rails/templates/rails/welcome/index.html.erb
@@ -66,7 +66,7 @@
<p class="version">
<strong>Rails version:</strong> <%= Rails.version %><br />
- <strong>Ruby version:</strong> <%= RUBY_VERSION %> (<%= RUBY_PLATFORM %>)
+ <strong>Ruby version:</strong> <%= RUBY_DESCRIPTION %>
</p>
</section>
</div>
diff --git a/railties/lib/rails/test_unit/reporter.rb b/railties/lib/rails/test_unit/reporter.rb
index 28b93cee5a..417933836b 100644
--- a/railties/lib/rails/test_unit/reporter.rb
+++ b/railties/lib/rails/test_unit/reporter.rb
@@ -5,7 +5,7 @@ require "minitest"
module Rails
class TestUnitReporter < Minitest::StatisticsReporter
- class_attribute :executable, default: "bin/rails test"
+ class_attribute :executable, default: "rails test"
def record(result)
super
diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb
index de5744c662..d38952bb30 100644
--- a/railties/lib/rails/test_unit/runner.rb
+++ b/railties/lib/rails/test_unit/runner.rb
@@ -12,8 +12,8 @@ module Rails
class << self
def attach_before_load_options(opts)
- opts.on("--warnings", "-w", "Run with Ruby warnings enabled") {}
- opts.on("-e", "--environment ENV", "Run tests in the ENV environment") {}
+ opts.on("--warnings", "-w", "Run with Ruby warnings enabled") { }
+ opts.on("-e", "--environment ENV", "Run tests in the ENV environment") { }
end
def parse_options(argv)
@@ -63,7 +63,7 @@ module Rails
# Extract absolute and relative paths but skip -n /.*/ regexp filters.
argv.select { |arg| arg =~ %r%^/?\w+/% && !arg.end_with?("/") }.map do |path|
case
- when path =~ /(:\d+)+$/
+ when /(:\d+)+$/.match?(path)
file, *lines = path.split(":")
filters << [ file, lines ]
file
@@ -87,7 +87,7 @@ module Rails
@filters = [ @named_filter, *derive_line_filters(patterns) ].compact
end
- # Minitest uses === to find matching filters.
+ # minitest uses === to find matching filters.
def ===(method)
@filters.any? { |filter| filter === method }
end
@@ -96,7 +96,7 @@ module Rails
def derive_named_filter(filter)
if filter.respond_to?(:named_filter)
filter.named_filter
- elsif filter =~ %r%/(.*)/% # Regexp filtering copied from Minitest.
+ elsif filter =~ %r%/(.*)/% # Regexp filtering copied from minitest.
Regexp.new $1
elsif filter.is_a?(String)
filter
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 32ac27a135..3a1b62d9d1 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -28,7 +28,7 @@ namespace :test do
desc "Run tests quickly, but also reset db"
task db: %w[db:test:prepare test]
- ["models", "helpers", "controllers", "mailers", "integration", "jobs"].each do |name|
+ ["models", "helpers", "channels", "controllers", "mailers", "integration", "jobs", "mailboxes"].each do |name|
task name => "test:prepare" do
$: << "test"
Rails::TestUnit::Runner.rake_run(["test/#{name}"])
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index 6fdb4648c2..519b08746a 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -9,13 +9,13 @@ Gem::Specification.new do |s|
s.summary = "Tools for creating, working with, and running Rails applications."
s.description = "Rails internals: application bootup, plugins, generators, and rake tasks."
- s.required_ruby_version = ">= 2.4.1"
+ s.required_ruby_version = ">= 2.5.0"
s.license = "MIT"
s.author = "David Heinemeier Hansson"
s.email = "david@loudthinking.com"
- s.homepage = "http://rubyonrails.org"
+ s.homepage = "https://rubyonrails.org"
s.files = Dir["CHANGELOG.md", "README.rdoc", "MIT-LICENSE", "RDOC_MAIN.rdoc", "exe/**/*", "lib/**/{*,.[a-z]*}"]
s.require_path = "lib"
@@ -30,11 +30,14 @@ Gem::Specification.new do |s|
"changelog_uri" => "https://github.com/rails/rails/blob/v#{version}/railties/CHANGELOG.md"
}
+ # NOTE: Please read our dependency guidelines before updating versions:
+ # https://edgeguides.rubyonrails.org/security.html#dependency-management-and-cves
+
s.add_dependency "activesupport", version
s.add_dependency "actionpack", version
s.add_dependency "rake", ">= 0.8.7"
- s.add_dependency "thor", ">= 0.19.0", "< 2.0"
+ s.add_dependency "thor", ">= 0.20.3", "< 2.0"
s.add_dependency "method_source"
s.add_development_dependency "actionview", version
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
index b42f37d6b9..9600194ed6 100644
--- a/railties/test/abstract_unit.rb
+++ b/railties/test/abstract_unit.rb
@@ -21,12 +21,16 @@ end
class ActiveSupport::TestCase
include ActiveSupport::Testing::Stream
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
end
+
+require_relative "../../tools/test_common"
diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb
index 93ed68fabb..0deb1a76df 100644
--- a/railties/test/app_loader_test.rb
+++ b/railties/test/app_loader_test.rb
@@ -9,12 +9,12 @@ class AppLoaderTest < ActiveSupport::TestCase
@loader ||= Class.new do
extend Rails::AppLoader
- def self.exec_arguments
- @exec_arguments
- end
+ class << self
+ attr_accessor :exec_arguments
- def self.exec(*args)
- @exec_arguments = args
+ def exec(*args)
+ self.exec_arguments = args
+ end
end
end
end
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index 3e0f31860b..7623e8e352 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -95,7 +95,7 @@ module ApplicationTests
end
end
- test "public url methods are not over-written by the asset pipeline" do
+ test "public URL methods are not over-written by the asset pipeline" do
contents = "doesnotexist"
cases = {
asset_url: %r{http://example.org/#{contents}},
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 4ca6d02b85..a80581211b 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -68,20 +68,6 @@ module ApplicationTests
assert_equal 'a = "/assets/rails.png";', last_response.body.strip
end
- test "assets do not require compressors until it is used" do
- app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
- add_to_env_config "production", "config.assets.compile = true"
- add_to_env_config "production", "config.assets.precompile = []"
-
- # Load app env
- app "production"
-
- assert_not defined?(Uglifier)
- get "/assets/demo.js"
- assert_match "alert()", last_response.body
- assert defined?(Uglifier)
- end
-
test "precompile creates the file, gives it the original asset's content and run in production as default" do
app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
app_file "app/assets/javascripts/application.js", "alert();"
@@ -443,13 +429,13 @@ module ApplicationTests
end
test "digested assets are not mistakenly removed" do
- app_file "app/assets/application.js", "alert();"
+ app_file "app/assets/application.css", "div { font-weight: bold }"
add_to_config "config.assets.compile = true"
precompile!
- files = Dir["#{app_path}/public/assets/application-*.js"]
- assert_equal 1, files.length, "Expected digested application.js asset to be generated, but none found"
+ files = Dir["#{app_path}/public/assets/application-*.css"]
+ assert_equal 1, files.length, "Expected digested application.css asset to be generated, but none found"
end
test "digested assets are removed from configured path" do
@@ -464,7 +450,7 @@ module ApplicationTests
assert_equal 0, files.length, "Expected application.js asset to be removed, but still exists"
end
- test "asset urls should use the request's protocol by default" do
+ test "asset URLs should use the request's protocol by default" do
app_with_assets_in_view
add_to_config "config.asset_host = 'example.com'"
add_to_env_config "development", "config.assets.digest = false"
@@ -480,7 +466,7 @@ module ApplicationTests
assert_match('src="https://example.com/assets/application.self.js', last_response.body)
end
- test "asset urls should be protocol-relative if no request is in scope" do
+ test "asset URLs should be protocol-relative if no request is in scope" do
app_file "app/assets/images/rails.png", "notreallyapng"
app_file "app/assets/javascripts/image_loader.js.erb", "var src='<%= image_path('rails.png') %>';"
add_to_config "config.assets.precompile = %w{rails.png image_loader.js}"
diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb
index 54934dbe24..a952d2466b 100644
--- a/railties/test/application/bin_setup_test.rb
+++ b/railties/test/application/bin_setup_test.rb
@@ -43,18 +43,22 @@ module ApplicationTests
# Ignore line that's only output by Bundler < 1.14
output.sub!(/^Resolving dependencies\.\.\.\n/, "")
+ # Suppress Bundler platform warnings from output
+ output.gsub!(/^The dependency .* will be unused .*\.\n/, "")
+ # Ignore warnings such as `Psych.safe_load is deprecated`
+ output.gsub!(/^warning:\s.*\n/, "")
- assert_equal(<<-OUTPUT, output)
-== Installing dependencies ==
-The Gemfile's dependencies are satisfied
+ assert_equal(<<~OUTPUT, output)
+ == Installing dependencies ==
+ The Gemfile's dependencies are satisfied
-== Preparing database ==
-Created database 'db/development.sqlite3'
-Created database 'db/test.sqlite3'
+ == Preparing database ==
+ Created database 'db/development.sqlite3'
+ Created database 'db/test.sqlite3'
-== Removing old logs and tempfiles ==
+ == Removing old logs and tempfiles ==
-== Restarting application server ==
+ == Restarting application server ==
OUTPUT
end
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index c2699006f6..b8e167b488 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -3,6 +3,7 @@
require "isolation/abstract_unit"
require "rack/test"
require "env_helpers"
+require "set"
class ::MyMailInterceptor
def self.delivering_email(email); email; end
@@ -123,6 +124,18 @@ module ApplicationTests
assert_equal "MyLogger", Rails.application.config.logger.class.name
end
+ test "raises an error if cache does not support recyclable cache keys" do
+ build_app(initializers: true)
+ add_to_env_config "production", "config.cache_store = Class.new {}.new"
+ add_to_env_config "production", "config.active_record.cache_versioning = true"
+
+ error = assert_raise(RuntimeError) do
+ app "production"
+ end
+
+ assert_match(/You're using a cache/, error.message)
+ end
+
test "a renders exception on pending migration" do
add_to_config <<-RUBY
config.active_record.migration_error = :page_load
@@ -297,6 +310,115 @@ module ApplicationTests
assert_equal %w(noop_email).to_set, PostsMailer.instance_variable_get(:@action_methods)
end
+ test "does not eager load attribute methods in development" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ app "development"
+
+ assert_not_includes Post.instance_methods, :title
+ end
+
+ test "does not eager load attribute methods in production when the schema cache is empty" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app "production"
+
+ assert_not_includes Post.instance_methods, :title
+ end
+
+ test "eager loads attribute methods in production when the schema cache is populated" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app_file "config/initializers/schema_cache.rb", <<-RUBY
+ ActiveRecord::Base.connection.schema_cache.add("posts")
+ RUBY
+
+ app "production"
+
+ assert_includes Post.instance_methods, :title
+ end
+
+ test "does not attempt to eager load attribute methods for models that aren't connected" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app_file "app/models/comment.rb", <<-RUBY
+ class Comment < ActiveRecord::Base
+ establish_connection(adapter: "mysql2", database: "does_not_exist")
+ end
+ RUBY
+
+ assert_nothing_raised do
+ app "production"
+ end
+ end
+
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true
@@ -474,45 +596,30 @@ module ApplicationTests
assert_equal "some_value", verifier.verify(message)
end
- test "application message verifier can be used when the key_generator is ActiveSupport::LegacyKeyGenerator" do
+ test "application will generate secret_key_base in tmp file if blank in development" do
app_file "config/initializers/secret_token.rb", <<-RUBY
Rails.application.credentials.secret_key_base = nil
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
RUBY
- app "production"
+ # For test that works even if tmp dir does not exist.
+ Dir.chdir(app_path) { FileUtils.remove_dir("tmp") }
- assert_kind_of ActiveSupport::LegacyKeyGenerator, Rails.application.key_generator
- message = app.message_verifier(:sensitive_value).generate("some_value")
- assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message)
+ app "development"
+
+ assert_not_nil app.secrets.secret_key_base
+ assert File.exist?(app_path("tmp/development_secret.txt"))
end
- test "config.secret_token is deprecated" do
+ test "application will not generate secret_key_base in tmp file if blank in production" do
app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
+ Rails.application.credentials.secret_key_base = nil
RUBY
- app "production"
-
- assert_deprecated(/secret_token/) do
- app.secrets
- end
- end
-
- test "secrets.secret_token is deprecated" do
- app_file "config/secrets.yml", <<-YAML
- production:
- secret_token: "b3c631c314c0bbca50c1b2843150fe33"
- YAML
-
- app "production"
-
- assert_deprecated(/secret_token/) do
- app.secrets
+ assert_raises ArgumentError do
+ app "production"
end
end
-
test "raises when secret_key_base is blank" do
app_file "config/initializers/secret_token.rb", <<-RUBY
Rails.application.credentials.secret_key_base = nil
@@ -534,23 +641,8 @@ module ApplicationTests
end
end
- test "prefer secrets.secret_token over config.secret_token" do
- app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.config.secret_token = ""
- RUBY
- app_file "config/secrets.yml", <<-YAML
- development:
- secret_token: 3b7cd727ee24e8444053437c36cc66c3
- YAML
-
- app "development"
-
- assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_token
- end
-
test "application verifier can build different verifiers" do
make_basic_app do |application|
- application.credentials.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
application.config.session_store :disabled
end
@@ -589,22 +681,6 @@ module ApplicationTests
assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_key_base
end
- test "config.secret_token over-writes a blank secrets.secret_token" do
- app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
- RUBY
- app_file "config/secrets.yml", <<-YAML
- development:
- secret_key_base:
- secret_token:
- YAML
-
- app "development"
-
- assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.secrets.secret_token
- assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token
- end
-
test "custom secrets saved in config/secrets.yml are loaded in app secrets" do
app_file "config/secrets.yml", <<-YAML
development:
@@ -667,19 +743,6 @@ module ApplicationTests
assert_equal "iaminallyoursecretkeybase", app.secrets.secret_key_base
end
- test "uses ActiveSupport::LegacyKeyGenerator as app.key_generator when secrets.secret_key_base is blank" do
- app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.credentials.secret_key_base = nil
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
- RUBY
-
- app "production"
-
- assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token
- assert_nil app.credentials.secret_key_base
- assert_kind_of ActiveSupport::LegacyKeyGenerator, app.key_generator
- end
-
test "that nested keys are symbolized the same as parents for hashes more than one level deep" do
app_file "config/secrets.yml", <<-YAML
development:
@@ -1117,6 +1180,38 @@ module ApplicationTests
end
end
+ test "autoloaders" do
+ app "development"
+
+ config = Rails.application.config
+ assert Rails.autoloaders.zeitwerk_enabled?
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.main
+ assert_equal "rails.main", Rails.autoloaders.main.tag
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.once
+ assert_equal "rails.once", Rails.autoloaders.once.tag
+ assert_equal [Rails.autoloaders.main, Rails.autoloaders.once], Rails.autoloaders.to_a
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.main.inflector
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.once.inflector
+
+ config.autoloader = :classic
+ assert_not Rails.autoloaders.zeitwerk_enabled?
+ assert_nil Rails.autoloaders.main
+ assert_nil Rails.autoloaders.once
+ assert_equal 0, Rails.autoloaders.count
+
+ config.autoloader = :zeitwerk
+ assert Rails.autoloaders.zeitwerk_enabled?
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.main
+ assert_equal "rails.main", Rails.autoloaders.main.tag
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.once
+ assert_equal "rails.once", Rails.autoloaders.once.tag
+ assert_equal [Rails.autoloaders.main, Rails.autoloaders.once], Rails.autoloaders.to_a
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.main.inflector
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.once.inflector
+
+ assert_raises(ArgumentError) { config.autoloader = :unknown }
+ end
+
test "config.action_view.cache_template_loading with cache_classes default" do
add_to_config "config.cache_classes = true"
@@ -1433,14 +1528,12 @@ module ApplicationTests
end
test "config.session_store with :active_record_store with activerecord-session_store gem" do
- begin
- make_basic_app do |application|
- ActionDispatch::Session::ActiveRecordStore = Class.new(ActionDispatch::Session::CookieStore)
- application.config.session_store :active_record_store
- end
- ensure
- ActionDispatch::Session.send :remove_const, :ActiveRecordStore
+ make_basic_app do |application|
+ ActionDispatch::Session::ActiveRecordStore = Class.new(ActionDispatch::Session::CookieStore)
+ application.config.session_store :active_record_store
end
+ ensure
+ ActionDispatch::Session.send :remove_const, :ActiveRecordStore
end
test "config.session_store with :active_record_store without activerecord-session_store gem" do
@@ -1603,6 +1696,14 @@ module ApplicationTests
assert_kind_of Hash, Rails.application.config.database_configuration
end
+ test "autoload paths do not include asset paths" do
+ app "development"
+ ActiveSupport::Dependencies.autoload_paths.each do |path|
+ assert_not_operator path, :ends_with?, "app/assets"
+ assert_not_operator path, :ends_with?, "app/javascript"
+ end
+ end
+
test "raises with proper error message if no database configuration found" do
FileUtils.rm("#{app_path}/config/database.yml")
err = assert_raises RuntimeError do
@@ -1668,10 +1769,10 @@ module ApplicationTests
assert_equal true, Rails.application.config.action_mailer.show_previews
end
- test "config_for loads custom configuration from yaml files" do
+ test "config_for loads custom configuration from yaml accessible as symbol or string" do
app_file "config/custom.yml", <<-RUBY
development:
- key: 'custom key'
+ foo: 'bar'
RUBY
add_to_config <<-RUBY
@@ -1680,7 +1781,171 @@ module ApplicationTests
app "development"
- assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ assert_equal "bar", Rails.application.config.my_custom_config[:foo]
+ assert_equal "bar", Rails.application.config.my_custom_config["foo"]
+ end
+
+ test "config_for loads nested custom configuration from yaml as symbol keys" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar:
+ baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_equal 1, Rails.application.config.my_custom_config[:foo][:bar][:baz]
+ end
+
+ test "config_for loads nested custom configuration from yaml with deprecated non-symbol access" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar:
+ baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_deprecated do
+ assert_equal 1, Rails.application.config.my_custom_config["foo"]["bar"]["baz"]
+ end
+ end
+
+ test "config_for loads nested custom configuration inside array from yaml with deprecated non-symbol access" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar:
+ - baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ config = Rails.application.config.my_custom_config
+ assert_instance_of Rails::Application::NonSymbolAccessDeprecatedHash, config[:foo][:bar].first
+
+ assert_deprecated do
+ assert_equal 1, config[:foo][:bar].first["baz"]
+ end
+ end
+
+ test "config_for makes all hash methods available" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo: 0
+ bar:
+ baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ actual = Rails.application.config.my_custom_config
+
+ assert_equal({ foo: 0, bar: { baz: 1 } }, actual)
+ assert_equal([ :foo, :bar ], actual.keys)
+ assert_equal([ 0, baz: 1], actual.values)
+ assert_equal({ foo: 0, bar: { baz: 1 } }, actual.to_h)
+ assert_equal(0, actual[:foo])
+ assert_equal({ baz: 1 }, actual[:bar])
+ end
+
+ test "config_for generates deprecation notice when nested hash methods are called with non-symbols" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar: 1
+ baz: 2
+ qux:
+ boo: 3
+ RUBY
+
+ app "development"
+
+ actual = Rails.application.config_for("custom")[:foo]
+
+ # slice
+ assert_deprecated do
+ assert_equal({ bar: 1, baz: 2 }, actual.slice("bar", "baz"))
+ end
+
+ # except
+ assert_deprecated do
+ assert_equal({ qux: { boo: 3 } }, actual.except("bar", "baz"))
+ end
+
+ # dig
+ assert_deprecated do
+ assert_equal(3, actual.dig("qux", "boo"))
+ end
+
+ # fetch - hit
+ assert_deprecated do
+ assert_equal(1, actual.fetch("bar", 0))
+ end
+
+ # fetch - miss
+ assert_deprecated do
+ assert_equal(0, actual.fetch("does-not-exist", 0))
+ end
+
+ # fetch_values
+ assert_deprecated do
+ assert_equal([1, 2], actual.fetch_values("bar", "baz"))
+ end
+
+ # key? - hit
+ assert_deprecated do
+ assert(actual.key?("bar"))
+ end
+
+ # key? - miss
+ assert_deprecated do
+ assert_not(actual.key?("does-not-exist"))
+ end
+
+ # slice!
+ actual = Rails.application.config_for("custom")[:foo]
+
+ assert_deprecated do
+ slice = actual.slice!("bar", "baz")
+ assert_equal({ bar: 1, baz: 2 }, actual)
+ assert_equal({ qux: { boo: 3 } }, slice)
+ end
+
+ # extract!
+ actual = Rails.application.config_for("custom")[:foo]
+
+ assert_deprecated do
+ extracted = actual.extract!("bar", "baz")
+ assert_equal({ bar: 1, baz: 2 }, extracted)
+ assert_equal({ qux: { boo: 3 } }, actual)
+ end
+
+ # except!
+ actual = Rails.application.config_for("custom")[:foo]
+
+ assert_deprecated do
+ actual.except!("bar", "baz")
+ assert_equal({ qux: { boo: 3 } }, actual)
+ end
end
test "config_for uses the Pathname object if it is provided" do
@@ -1695,7 +1960,7 @@ module ApplicationTests
app "development"
- assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ assert_equal "custom key", Rails.application.config.my_custom_config[:key]
end
test "config_for raises an exception if the file does not exist" do
@@ -1725,8 +1990,29 @@ module ApplicationTests
assert_equal({}, Rails.application.config.my_custom_config)
end
- test "config_for with empty file returns an empty hash" do
+ test "config_for implements shared configuration as secrets case found" do
app_file "config/custom.yml", <<-RUBY
+ shared:
+ foo: :bar
+ test:
+ foo: :baz
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "test"
+
+ assert_equal(:baz, Rails.application.config.my_custom_config[:foo])
+ end
+
+ test "config_for implements shared configuration as secrets case not found" do
+ app_file "config/custom.yml", <<-RUBY
+ shared:
+ foo: :bar
+ test:
+ foo: :baz
RUBY
add_to_config <<-RUBY
@@ -1735,40 +2021,45 @@ module ApplicationTests
app "development"
- assert_equal({}, Rails.application.config.my_custom_config)
+ assert_equal(:bar, Rails.application.config.my_custom_config[:foo])
end
- test "default SQLite3Adapter.represent_boolean_as_integer for 5.1 is false" do
- remove_from_config '.*config\.load_defaults.*\n'
+ test "config_for with empty file returns an empty hash" do
+ app_file "config/custom.yml", <<-RUBY
+ RUBY
- app_file "app/models/post.rb", <<-RUBY
- class Post < ActiveRecord::Base
- end
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
RUBY
app "development"
- force_lazy_load_hooks { Post }
- assert_not ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ assert_equal({}, Rails.application.config.my_custom_config)
end
- test "default SQLite3Adapter.represent_boolean_as_integer for new installs is true" do
+ test "represent_boolean_as_integer is deprecated" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
+ Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
+ RUBY
+
app_file "app/models/post.rb", <<-RUBY
class Post < ActiveRecord::Base
end
RUBY
app "development"
- force_lazy_load_hooks { Post }
-
- assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ assert_deprecated do
+ force_lazy_load_hooks { Post }
+ end
end
- test "represent_boolean_as_integer should be able to set via config.active_record.sqlite3.represent_boolean_as_integer" do
+ test "represent_boolean_as_integer raises when the value is false" do
remove_from_config '.*config\.load_defaults.*\n'
app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
- Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
+ Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = false
RUBY
app_file "app/models/post.rb", <<-RUBY
@@ -1777,9 +2068,9 @@ module ApplicationTests
RUBY
app "development"
- force_lazy_load_hooks { Post }
-
- assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ assert_raises(RuntimeError) do
+ force_lazy_load_hooks { Post }
+ end
end
test "config_for containing ERB tags should evaluate" do
@@ -1794,7 +2085,7 @@ module ApplicationTests
app "development"
- assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ assert_equal "custom key", Rails.application.config.my_custom_config[:key]
end
test "config_for with syntax error show a more descriptive exception" do
@@ -1827,7 +2118,7 @@ module ApplicationTests
RUBY
require "#{app_path}/config/environment"
- assert_equal "unicorn", Rails.application.config.my_custom_config["key"]
+ assert_equal "unicorn", Rails.application.config.my_custom_config[:key]
end
test "api_only is false by default" do
@@ -1981,7 +2272,9 @@ module ApplicationTests
test "ActionView::Template.finalize_compiled_template_methods is true by default" do
app "test"
- assert_equal true, ActionView::Template.finalize_compiled_template_methods
+ assert_deprecated do
+ ActionView::Template.finalize_compiled_template_methods
+ end
end
test "ActionView::Template.finalize_compiled_template_methods can be configured via config.action_view.finalize_compiled_template_methods" do
@@ -1993,7 +2286,210 @@ module ApplicationTests
app "test"
- assert_equal false, ActionView::Template.finalize_compiled_template_methods
+ assert_deprecated do
+ ActionView::Template.finalize_compiled_template_methods
+ end
+ end
+
+ test "ActiveJob::Base.return_false_on_aborted_enqueue is true by default" do
+ app "development"
+
+ assert_equal true, ActiveJob::Base.return_false_on_aborted_enqueue
+ end
+
+ test "ActiveJob::Base.return_false_on_aborted_enqueue is false in the 5.x defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+ add_to_config 'config.load_defaults "5.2"'
+
+ app "development"
+
+ assert_equal false, ActiveJob::Base.return_false_on_aborted_enqueue
+ end
+
+ test "ActiveJob::Base.return_false_on_aborted_enqueue can be configured in the new framework defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
+ Rails.application.config.active_job.return_false_on_aborted_enqueue = true
+ RUBY
+
+ app "development"
+
+ assert_equal true, ActiveJob::Base.return_false_on_aborted_enqueue
+ end
+
+ test "ActiveStorage.queues[:analysis] is :active_storage_analysis by default" do
+ app "development"
+
+ assert_equal :active_storage_analysis, ActiveStorage.queues[:analysis]
+ end
+
+ test "ActiveStorage.queues[:analysis] is nil without Rails 6 defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app "development"
+
+ assert_nil ActiveStorage.queues[:analysis]
+ end
+
+ test "ActiveStorage.queues[:purge] is :active_storage_purge by default" do
+ app "development"
+
+ assert_equal :active_storage_purge, ActiveStorage.queues[:purge]
+ end
+
+ test "ActiveStorage.queues[:purge] is nil without Rails 6 defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app "development"
+
+ assert_nil ActiveStorage.queues[:purge]
+ end
+
+ test "ActionMailbox.logger is Rails.logger by default" do
+ app "development"
+
+ assert_equal Rails.logger, ActionMailbox.logger
+ end
+
+ test "ActionMailbox.logger can be configured" do
+ app_file "lib/my_logger.rb", <<-RUBY
+ require "logger"
+ class MyLogger < ::Logger
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ require "my_logger"
+ config.action_mailbox.logger = MyLogger.new(STDOUT)
+ RUBY
+
+ app "development"
+
+ assert_equal "MyLogger", ActionMailbox.logger.class.name
+ end
+
+ test "ActionMailbox.incinerate_after is 30.days by default" do
+ app "development"
+
+ assert_equal 30.days, ActionMailbox.incinerate_after
+ end
+
+ test "ActionMailbox.incinerate_after can be configured" do
+ add_to_config <<-RUBY
+ config.action_mailbox.incinerate_after = 14.days
+ RUBY
+
+ app "development"
+
+ assert_equal 14.days, ActionMailbox.incinerate_after
+ end
+
+ test "ActionMailbox.queues[:incineration] is :action_mailbox_incineration by default" do
+ app "development"
+
+ assert_equal :action_mailbox_incineration, ActionMailbox.queues[:incineration]
+ end
+
+ test "ActionMailbox.queues[:incineration] can be configured" do
+ add_to_config <<-RUBY
+ config.action_mailbox.queues.incineration = :another_queue
+ RUBY
+
+ app "development"
+
+ assert_equal :another_queue, ActionMailbox.queues[:incineration]
+ end
+
+ test "ActionMailbox.queues[:routing] is :action_mailbox_routing by default" do
+ app "development"
+
+ assert_equal :action_mailbox_routing, ActionMailbox.queues[:routing]
+ end
+
+ test "ActionMailbox.queues[:routing] can be configured" do
+ add_to_config <<-RUBY
+ config.action_mailbox.queues.routing = :another_queue
+ RUBY
+
+ app "development"
+
+ assert_equal :another_queue, ActionMailbox.queues[:routing]
+ end
+
+ test "ActionMailer::Base.delivery_job is ActionMailer::MailDeliveryJob by default" do
+ app "development"
+
+ assert_equal ActionMailer::MailDeliveryJob, ActionMailer::Base.delivery_job
+ end
+
+ test "ActionMailer::Base.delivery_job is ActionMailer::DeliveryJob in the 5.x defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+ add_to_config 'config.load_defaults "5.2"'
+
+ app "development"
+
+ assert_equal ActionMailer::DeliveryJob, ActionMailer::Base.delivery_job
+ end
+
+ test "ActionMailer::Base.delivery_job can be configured in the new framework defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
+ Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
+ RUBY
+
+ app "development"
+
+ assert_equal ActionMailer::MailDeliveryJob, ActionMailer::Base.delivery_job
+ end
+
+ test "ActiveRecord::Base.filter_attributes should equal to filter_parameters" do
+ app_file "config/initializers/filter_parameters_logging.rb", <<-RUBY
+ Rails.application.config.filter_parameters += [ :password, :credit_card_number ]
+ RUBY
+ app "development"
+ assert_equal [ :password, :credit_card_number ], Rails.application.config.filter_parameters
+ assert_equal [ :password, :credit_card_number ], ActiveRecord::Base.filter_attributes
+ end
+
+ test "ActiveStorage.routes_prefix can be configured via config.active_storage.routes_prefix" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.active_storage.routes_prefix = '/files'
+ end
+ RUBY
+
+ output = rails("routes", "-g", "active_storage")
+ assert_equal <<~MESSAGE, output
+ Prefix Verb URI Pattern Controller#Action
+ rails_service_blob GET /files/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /files/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /files/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /files/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /files/direct_uploads(.:format) active_storage/direct_uploads#create
+ MESSAGE
+ end
+
+ test "hosts include .localhost in development" do
+ app "development"
+ assert_includes Rails.application.config.hosts, ".localhost"
+ end
+
+ test "disable_sandbox is false by default" do
+ app "development"
+
+ assert_equal false, Rails.configuration.disable_sandbox
+ end
+
+ test "disable_sandbox can be overridden" do
+ add_to_config <<-RUBY
+ config.disable_sandbox = true
+ RUBY
+
+ app "development"
+
+ assert Rails.configuration.disable_sandbox
end
private
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 4a14042cd3..db16f4cc56 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -25,7 +25,7 @@ class ConsoleTest < ActiveSupport::TestCase
end
def test_app_method_should_return_integration_session
- TestHelpers::Rack.send :remove_method, :app
+ TestHelpers::Rack.remove_method :app
load_environment
console_session = irb_context.app
assert_instance_of ActionDispatch::Integration::Session, console_session
@@ -109,7 +109,7 @@ class FullStackConsoleTest < ActiveSupport::TestCase
CODE
system "#{app_path}/bin/rails runner 'Post.connection.create_table :posts'"
- @master, @slave = PTY.open
+ @primary, @replica = PTY.open
end
def teardown
@@ -117,19 +117,23 @@ class FullStackConsoleTest < ActiveSupport::TestCase
end
def write_prompt(command, expected_output = nil)
- @master.puts command
- assert_output command, @master
- assert_output expected_output, @master if expected_output
- assert_output "> ", @master
+ @primary.puts command
+ assert_output command, @primary
+ assert_output expected_output, @primary if expected_output
+ assert_output "> ", @primary
end
- def spawn_console(options)
- Process.spawn(
+ def spawn_console(options, wait_for_prompt: true)
+ pid = Process.spawn(
"#{app_path}/bin/rails console #{options}",
- in: @slave, out: @slave, err: @slave
+ in: @replica, out: @replica, err: @replica
)
- assert_output "> ", @master, 30
+ if wait_for_prompt
+ assert_output "> ", @primary, 30
+ end
+
+ pid
end
def test_sandbox
@@ -138,21 +142,32 @@ class FullStackConsoleTest < ActiveSupport::TestCase
write_prompt "Post.count", "=> 0"
write_prompt "Post.create"
write_prompt "Post.count", "=> 1"
- @master.puts "quit"
+ @primary.puts "quit"
spawn_console("--sandbox")
write_prompt "Post.count", "=> 0"
write_prompt "Post.transaction { Post.create; raise }"
write_prompt "Post.count", "=> 0"
- @master.puts "quit"
+ @primary.puts "quit"
+ end
+
+ def test_sandbox_when_sandbox_is_disabled
+ add_to_config <<-RUBY
+ config.disable_sandbox = true
+ RUBY
+
+ output = `#{app_path}/bin/rails console --sandbox`
+
+ assert_includes output, "sandbox mode is disabled"
+ assert_equal 1, $?.exitstatus
end
def test_environment_option_and_irb_option
- spawn_console("test -- --verbose")
+ spawn_console("-e test -- --verbose")
write_prompt "a = 1", "a = 1"
write_prompt "puts Rails.env", "puts Rails.env\r\ntest"
- @master.puts "quit"
+ @primary.puts "quit"
end
end
diff --git a/railties/test/application/credentials_test.rb b/railties/test/application/credentials_test.rb
new file mode 100644
index 0000000000..2f6b109b50
--- /dev/null
+++ b/railties/test/application/credentials_test.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "env_helpers"
+
+class Rails::CredentialsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation, EnvHelpers
+
+ setup :build_app
+ teardown :teardown_app
+
+ test "reads credentials from environment specific path" do
+ write_credentials_override(:production)
+
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+
+ test "reads credentials from customized path and key" do
+ write_credentials_override(:staging)
+ add_to_env_config("production", "config.credentials.content_path = config.root.join('config/credentials/staging.yml.enc')")
+ add_to_env_config("production", "config.credentials.key_path = config.root.join('config/credentials/staging.key')")
+
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+
+ test "reads credentials using environment variable key" do
+ write_credentials_override(:production, with_key: false)
+
+ switch_env("RAILS_MASTER_KEY", credentials_key) do
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+ end
+
+ private
+ def write_credentials_override(name, with_key: true)
+ Dir.chdir(app_path) do
+ Dir.mkdir "config/credentials"
+ File.write "config/credentials/#{name}.key", credentials_key if with_key
+
+ # secret_key_base: secret
+ # mystery: revealed
+ File.write "config/credentials/#{name}.yml.enc",
+ "vgvKu4MBepIgZ5VHQMMPwnQNsLlWD9LKmJHu3UA/8yj6x+3fNhz3DwL9brX7UA==--qLdxHP6e34xeTAiI--nrcAsleXuo9NqiEuhntAhw=="
+ end
+ end
+
+ def credentials_key
+ "2117e775dc2024d4f49ddf3aeb585919"
+ end
+end
diff --git a/railties/test/application/dbconsole_test.rb b/railties/test/application/dbconsole_test.rb
index 8eb293c179..8c03fe4ac6 100644
--- a/railties/test/application/dbconsole_test.rb
+++ b/railties/test/application/dbconsole_test.rb
@@ -33,11 +33,11 @@ module ApplicationTests
end
RUBY
- master, slave = PTY.open
- spawn_dbconsole(slave)
- assert_output("sqlite>", master)
+ primary, replica = PTY.open
+ spawn_dbconsole(replica)
+ assert_output("sqlite>", primary)
ensure
- master.puts ".exit"
+ primary.puts ".exit"
end
def test_respect_environment_option
@@ -56,14 +56,14 @@ module ApplicationTests
database: db/production.sqlite3
YAML
- master, slave = PTY.open
- spawn_dbconsole(slave, "-e production")
- assert_output("sqlite>", master)
+ primary, replica = PTY.open
+ spawn_dbconsole(replica, "-e production")
+ assert_output("sqlite>", primary)
- master.puts "pragma database_list;"
- assert_output("production.sqlite3", master)
+ primary.puts "pragma database_list;"
+ assert_output("production.sqlite3", primary)
ensure
- master.puts ".exit"
+ primary.puts ".exit"
end
private
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 1530ea82d6..a35247fc43 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -39,7 +39,7 @@ module ApplicationTests
assert_equal expanded_path, ActionMailer::Base.view_paths[0].to_s
end
- test "allows me to configure default url options for ActionMailer" do
+ test "allows me to configure default URL options for ActionMailer" do
app_file "config/environments/development.rb", <<-RUBY
Rails.application.configure do
config.action_mailer.default_url_options = { :host => "test.rails" }
@@ -61,7 +61,7 @@ module ApplicationTests
assert_equal "https", ActionMailer::Base.default_url_options[:protocol]
end
- test "includes url helpers as action methods" do
+ test "includes URL helpers as action methods" do
app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
get "/foo", :to => lambda { |env| [200, {}, []] }, :as => :foo
@@ -230,35 +230,31 @@ module ApplicationTests
end
test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do
- begin
- require "#{app_path}/config/environment"
- orig_database_url = ENV.delete("DATABASE_URL")
- orig_rails_env, Rails.env = Rails.env, "development"
- ActiveRecord::Base.establish_connection
- assert ActiveRecord::Base.connection
- assert_match(/#{ActiveRecord::Base.configurations[Rails.env]['database']}/, ActiveRecord::Base.connection_config[:database])
- ensure
- ActiveRecord::Base.remove_connection
- ENV["DATABASE_URL"] = orig_database_url if orig_database_url
- Rails.env = orig_rails_env if orig_rails_env
- end
+ require "#{app_path}/config/environment"
+ orig_database_url = ENV.delete("DATABASE_URL")
+ orig_rails_env, Rails.env = Rails.env, "development"
+ ActiveRecord::Base.establish_connection
+ assert ActiveRecord::Base.connection
+ assert_match(/#{ActiveRecord::Base.configurations[Rails.env]['database']}/, ActiveRecord::Base.connection_config[:database])
+ ensure
+ ActiveRecord::Base.remove_connection
+ ENV["DATABASE_URL"] = orig_database_url if orig_database_url
+ Rails.env = orig_rails_env if orig_rails_env
end
test "active record establish_connection uses DATABASE_URL even if Rails.env is set" do
- begin
- require "#{app_path}/config/environment"
- orig_database_url = ENV.delete("DATABASE_URL")
- orig_rails_env, Rails.env = Rails.env, "development"
- database_url_db_name = "db/database_url_db.sqlite3"
- ENV["DATABASE_URL"] = "sqlite3:#{database_url_db_name}"
- ActiveRecord::Base.establish_connection
- assert ActiveRecord::Base.connection
- assert_match(/#{database_url_db_name}/, ActiveRecord::Base.connection_config[:database])
- ensure
- ActiveRecord::Base.remove_connection
- ENV["DATABASE_URL"] = orig_database_url if orig_database_url
- Rails.env = orig_rails_env if orig_rails_env
- end
+ require "#{app_path}/config/environment"
+ orig_database_url = ENV.delete("DATABASE_URL")
+ orig_rails_env, Rails.env = Rails.env, "development"
+ database_url_db_name = "db/database_url_db.sqlite3"
+ ENV["DATABASE_URL"] = "sqlite3:#{database_url_db_name}"
+ ActiveRecord::Base.establish_connection
+ assert ActiveRecord::Base.connection
+ assert_match(/#{database_url_db_name}/, ActiveRecord::Base.connection_config[:database])
+ ensure
+ ActiveRecord::Base.remove_connection
+ ENV["DATABASE_URL"] = orig_database_url if orig_database_url
+ Rails.env = orig_rails_env if orig_rails_env
end
test "connections checked out during initialization are returned to the pool" do
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index 889ad16fb8..9c98489590 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -34,6 +34,22 @@ class LoadingTest < ActiveSupport::TestCase
assert_equal "omg", p.title
end
+ test "constants without a matching file raise NameError" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post
+ NON_EXISTING_CONSTANT
+ end
+ RUBY
+
+ boot_app
+
+ e = assert_raise(NameError) { User }
+ assert_equal "uninitialized constant #{self.class}::User", e.message
+
+ e = assert_raise(NameError) { Post }
+ assert_equal "uninitialized constant Post::NON_EXISTING_CONSTANT", e.message
+ end
+
test "concerns in app are autoloaded" do
app_file "app/controllers/concerns/trackable.rb", <<-CONCERN
module Trackable
@@ -371,6 +387,72 @@ class LoadingTest < ActiveSupport::TestCase
end
end
+ test "active record query cache hooks are installed before first request in production" do
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ begin
+ class OmgController < ActionController::Metal
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ def show
+ if ActiveRecord::Base.connection.query_cache_enabled
+ self.response_body = ["Query cache is enabled."]
+ else
+ self.response_body = ["Expected ActiveRecord::Base.connection.query_cache_enabled to be true"]
+ end
+ end
+ end
+ rescue => e
+ puts "Error loading metal: \#{e.class} \#{e.message}"
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ boot_app "production"
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "/omg/show"
+ assert_equal "Query cache is enabled.", last_response.body
+ end
+
+ test "active record query cache hooks are installed before first request in development" do
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ begin
+ class OmgController < ActionController::Metal
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ def show
+ if ActiveRecord::Base.connection.query_cache_enabled
+ self.response_body = ["Query cache is enabled."]
+ else
+ self.response_body = ["Expected ActiveRecord::Base.connection.query_cache_enabled to be true"]
+ end
+ end
+ end
+ rescue => e
+ puts "Error loading metal: \#{e.class} \#{e.message}"
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ boot_app "development"
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "/omg/show"
+ assert_equal "Query cache is enabled.", last_response.body
+ end
+
private
def setup_ar!
@@ -382,4 +464,12 @@ class LoadingTest < ActiveSupport::TestCase
end
end
end
+
+ def boot_app(env = "development")
+ ENV["RAILS_ENV"] = env
+
+ require "#{app_path}/config/environment"
+ ensure
+ ENV.delete "RAILS_ENV"
+ end
end
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index ba186bda44..fb84276b8a 100644
--- a/railties/test/application/mailer_previews_test.rb
+++ b/railties/test/application/mailer_previews_test.rb
@@ -85,6 +85,7 @@ module ApplicationTests
end
test "mailer previews are loaded from a custom preview_path" do
+ app_dir "lib/mailer_previews"
add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
mailer "notifier", <<-RUBY
@@ -254,6 +255,7 @@ module ApplicationTests
end
test "mailer previews are reloaded from a custom preview_path" do
+ app_dir "lib/mailer_previews"
add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
app("development")
@@ -818,6 +820,7 @@ module ApplicationTests
def build_app
super
app_file "config/routes.rb", "Rails.application.routes.draw do; end"
+ app_dir "test/mailers/previews"
end
def mailer(name, contents)
diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb
index ecb4ee3446..fe48ef3f03 100644
--- a/railties/test/application/middleware/cookies_test.rb
+++ b/railties/test/application/middleware/cookies_test.rb
@@ -110,14 +110,14 @@ module ApplicationTests
assert_equal "signed cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "signed cookie", verifier_sha512.verify(last_response.body)
+ assert_equal "signed cookie", verifier_sha512.verify(last_response.body, purpose: "cookie.signed_cookie")
get "/foo/write_raw_cookie_sha256"
get "/foo/read_signed"
assert_equal "signed cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "signed cookie", verifier_sha512.verify(last_response.body)
+ assert_equal "signed cookie", verifier_sha512.verify(last_response.body, purpose: "cookie.signed_cookie")
end
test "encrypted cookies rotating multiple encryption keys" do
@@ -180,14 +180,14 @@ module ApplicationTests
assert_equal "encrypted cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body)
+ assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body, purpose: "cookie.encrypted_cookie")
- get "/foo/write_raw_cookie_sha256"
+ get "/foo/write_raw_cookie_two"
get "/foo/read_encrypted"
assert_equal "encrypted cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body)
+ assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body, purpose: "cookie.encrypted_cookie")
end
end
end
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
index 2d659ade8d..17df78ed4e 100644
--- a/railties/test/application/middleware/exceptions_test.rb
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -60,7 +60,7 @@ module ApplicationTests
assert_equal "YOU FAILED", last_response.body
end
- test "url generation error when action_dispatch.show_exceptions is set raises an exception" do
+ test "URL generation error when action_dispatch.show_exceptions is set raises an exception" do
controller :foo, <<-RUBY
class FooController < ActionController::Base
def index
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
index 83cf8a27f7..515b32080e 100644
--- a/railties/test/application/middleware/remote_ip_test.rb
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -12,7 +12,9 @@ module ApplicationTests
remote_ip = nil
env = Rack::MockRequest.env_for("/").merge(env).merge!(
"action_dispatch.show_exceptions" => false,
- "action_dispatch.key_generator" => ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33")
+ "action_dispatch.key_generator" => ActiveSupport::CachingKeyGenerator.new(
+ ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 1000)
+ )
)
endpoint = Proc.new do |e|
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index 9182a63ab7..479615c133 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -183,7 +183,7 @@ module ApplicationTests
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
get "/foo/read_raw_cookie"
- assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"]
+ assert_equal 1, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"]
end
test "session upgrading signature to encryption cookie store works the same way as encrypted cookie store" do
@@ -215,8 +215,6 @@ module ApplicationTests
RUBY
add_to_config <<-RUBY
- secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
-
# Enable AEAD cookies
config.action_dispatch.use_authenticated_cookie_encryption = true
RUBY
@@ -235,69 +233,7 @@ module ApplicationTests
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
get "/foo/read_raw_cookie"
- assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"]
- end
-
- test "session upgrading signature to encryption cookie store upgrades session to encrypted mode" do
- app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- get ':controller(/:action)'
- end
- RUBY
-
- controller :foo, <<-RUBY
- class FooController < ActionController::Base
- def write_raw_session
- # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
- cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
- head :ok
- end
-
- def write_session
- session[:foo] = session[:foo] + 1
- head :ok
- end
-
- def read_session
- render plain: session[:foo]
- end
-
- def read_encrypted_cookie
- render plain: cookies.encrypted[:_myapp_session]['foo']
- end
-
- def read_raw_cookie
- render plain: cookies[:_myapp_session]
- end
- end
- RUBY
-
- add_to_config <<-RUBY
- secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
-
- # Enable AEAD cookies
- config.action_dispatch.use_authenticated_cookie_encryption = true
- RUBY
-
- require "#{app_path}/config/environment"
-
- get "/foo/write_raw_session"
- get "/foo/read_session"
- assert_equal "1", last_response.body
-
- get "/foo/write_session"
- get "/foo/read_session"
- assert_equal "2", last_response.body
-
- get "/foo/read_encrypted_cookie"
- assert_equal "2", last_response.body
-
- cipher = "aes-256-gcm"
- secret = app.key_generator.generate_key("authenticated encrypted cookie")
- encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
-
- get "/foo/read_raw_cookie"
- assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"]
+ assert_equal 1, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"]
end
test "session upgrading from AES-CBC-HMAC encryption to AES-GCM encryption" do
@@ -364,71 +300,7 @@ module ApplicationTests
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
get "/foo/read_raw_cookie"
- assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"]
- ensure
- ENV["RAILS_ENV"] = old_rails_env
- end
- end
-
- test "session upgrading legacy signed cookies to new signed cookies" do
- app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- get ':controller(/:action)'
- end
- RUBY
-
- controller :foo, <<-RUBY
- class FooController < ActionController::Base
- def write_raw_session
- # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
- cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
- head :ok
- end
-
- def write_session
- session[:foo] = session[:foo] + 1
- head :ok
- end
-
- def read_session
- render plain: session[:foo]
- end
-
- def read_signed_cookie
- render plain: cookies.signed[:_myapp_session]['foo']
- end
-
- def read_raw_cookie
- render plain: cookies[:_myapp_session]
- end
- end
- RUBY
-
- add_to_config <<-RUBY
- secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
- Rails.application.credentials.secret_key_base = nil
- RUBY
-
- begin
- old_rails_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production"
-
- require "#{app_path}/config/environment"
-
- get "/foo/write_raw_session"
- get "/foo/read_session"
- assert_equal "1", last_response.body
-
- get "/foo/write_session"
- get "/foo/read_session"
- assert_equal "2", last_response.body
-
- get "/foo/read_signed_cookie"
- assert_equal "2", last_response.body
-
- verifier = ActiveSupport::MessageVerifier.new(app.secrets.secret_token)
-
- get "/foo/read_raw_cookie"
- assert_equal 2, verifier.verify(last_response.body)["foo"]
+ assert_equal 2, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"]
ensure
ENV["RAILS_ENV"] = old_rails_env
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 5efaf841d4..4242daf39a 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -25,6 +25,8 @@ module ApplicationTests
boot!
assert_equal [
+ "Webpacker::DevServerProxy",
+ "ActionDispatch::HostAuthorization",
"Rack::Sendfile",
"ActionDispatch::Static",
"ActionDispatch::Executor",
@@ -56,6 +58,8 @@ module ApplicationTests
boot!
assert_equal [
+ "Webpacker::DevServerProxy",
+ "ActionDispatch::HostAuthorization",
"Rack::Sendfile",
"ActionDispatch::Static",
"ActionDispatch::Executor",
@@ -138,7 +142,7 @@ module ApplicationTests
add_to_config "config.ssl_options = { redirect: { host: 'example.com' } }"
boot!
- assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware.first.args
+ assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware[2].args
end
test "removing Active Record omits its middleware" do
@@ -222,35 +226,36 @@ module ApplicationTests
test "insert middleware after" do
add_to_config "config.middleware.insert_after Rack::Sendfile, Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.second
+ assert_equal "Rack::Config", middleware.fourth
end
test "unshift middleware" do
add_to_config "config.middleware.unshift Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.first
+ assert_equal "Rack::Config", middleware.second
end
test "Rails.cache does not respond to middleware" do
add_to_config "config.cache_store = :memory_store"
boot!
- assert_equal "Rack::Runtime", middleware.fourth
+ assert_equal "Rack::Runtime", middleware[5]
end
test "Rails.cache does respond to middleware" do
boot!
- assert_equal "Rack::Runtime", middleware.fifth
+ assert_equal "ActiveSupport::Cache::Strategy::LocalCache", middleware[5]
+ assert_equal "Rack::Runtime", middleware[6]
end
test "insert middleware before" do
add_to_config "config.middleware.insert_before Rack::Sendfile, Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.first
+ assert_equal "Rack::Config", middleware.third
end
test "can't change middleware after it's built" do
boot!
- assert_raise frozen_error_class do
+ assert_raise FrozenError do
app.config.middleware.use Rack::Config
end
end
diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb
index d6c81c1fe2..f0f1112f6b 100644
--- a/railties/test/application/multiple_applications_test.rb
+++ b/railties/test/application/multiple_applications_test.rb
@@ -100,30 +100,6 @@ module ApplicationTests
assert_nothing_raised { AppTemplate::Application.new }
end
- def test_initializers_run_on_different_applications_go_to_the_same_class
- application1 = AppTemplate::Application.new
- run_count = 0
-
- AppTemplate::Application.initializer :init0 do
- run_count += 1
- end
-
- application1.initializer :init1 do
- run_count += 1
- end
-
- AppTemplate::Application.new.initializer :init2 do
- run_count += 1
- end
-
- assert_equal 0, run_count, "Without loading the initializers, the count should be 0"
-
- # Set config.eager_load to false so that an eager_load warning doesn't pop up
- AppTemplate::Application.create { config.eager_load = false }.initialize!
-
- assert_equal 3, run_count, "There should have been three initializers that incremented the count"
- end
-
def test_consoles_run_on_different_applications_go_to_the_same_class
run_count = 0
AppTemplate::Application.console { run_count += 1 }
@@ -165,12 +141,12 @@ module ApplicationTests
app.config.some_setting = "a_different_setting"
assert_equal "a_different_setting", app.config.some_setting, "The configuration's some_setting should be set."
- new_config = Rails::Application::Configuration.new("root_of_application")
+ new_config = Rails::Application::Configuration.new(Pathname.new("root_of_application"))
new_config.some_setting = "some_setting_dude"
app.config = new_config
assert_equal "some_setting_dude", app.config.some_setting, "The configuration's some_setting should have changed."
- assert_equal "root_of_application", app.config.root, "The root should have changed to the new config's root."
+ assert_equal "root_of_application", app.config.root.to_s, "The root should have changed to the new config's root."
assert_equal new_config, app.config, "The application's config should have changed to the new config."
end
end
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
index d949a48366..ea425d5fa5 100644
--- a/railties/test/application/rack/logger_test.rb
+++ b/railties/test/application/rack/logger_test.rb
@@ -53,6 +53,12 @@ module ApplicationTests
wait
assert_match 'Started HEAD "/"', logs
end
+
+ test "logger logs correct remote IP address" do
+ get "/", {}, { "REMOTE_ADDR" => "127.0.0.1", "HTTP_X_FORWARDED_FOR" => "1.2.3.4" }
+ wait
+ assert_match 'Started GET "/" for 1.2.3.4', logs
+ end
end
end
end
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 0594236b1f..258066a7e6 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
require "isolation/abstract_unit"
+require "env_helpers"
module ApplicationTests
module RakeTests
class RakeDbsTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::Isolation
+ include ActiveSupport::Testing::Isolation, EnvHelpers
def setup
build_app
@@ -31,6 +32,7 @@ module ApplicationTests
output = rails("db:create")
assert_match(/Created database/, output)
assert File.exist?(expected_database)
+ yield if block_given?
assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded
output = rails("db:drop")
assert_match(/Dropped database/, output)
@@ -38,12 +40,12 @@ module ApplicationTests
end
end
- test "db:create and db:drop without database url" do
+ test "db:create and db:drop without database URL" do
require "#{app_path}/config/environment"
db_create_and_drop ActiveRecord::Base.configurations[Rails.env]["database"]
end
- test "db:create and db:drop with database url" do
+ test "db:create and db:drop with database URL" do
require "#{app_path}/config/environment"
set_database_url
db_create_and_drop database_url_db_name
@@ -51,6 +53,7 @@ module ApplicationTests
test "db:create and db:drop respect environment setting" do
app_file "config/database.yml", <<-YAML
+ <% 1 %>
development:
database: <%= Rails.application.config.database %>
adapter: sqlite3
@@ -62,7 +65,61 @@ module ApplicationTests
end
RUBY
- db_create_and_drop "db/development.sqlite3", environment_loaded: false
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
+ test "db:create and db:drop don't raise errors when loading YAML with multiline ERB" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ database: <%=
+ Rails.application.config.database
+ %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
+ test "db:create and db:drop don't raise errors when loading YAML containing conditional statements in ERB" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ <% if Rails.application.config.database %>
+ database: <%= Rails.application.config.database %>
+ <% else %>
+ database: db/default.sqlite3
+ <% end %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
+ test "db:create and db:drop don't raise errors when loading YAML containing multiple ERB statements on the same line" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ database: <% if Rails.application.config.database %><%= Rails.application.config.database %><% else %>db/default.sqlite3<% end %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
end
def with_database_existing
@@ -83,6 +140,8 @@ module ApplicationTests
def with_bad_permissions
Dir.chdir(app_path) do
+ skip "Can't avoid permissions as root" if Process.uid.zero?
+
set_database_url
FileUtils.chmod("-w", "db")
yield
@@ -93,7 +152,7 @@ module ApplicationTests
test "db:create failure because bad permissions" do
with_bad_permissions do
output = rails("db:create", allow_failure: true)
- assert_match(/Couldn't create database/, output)
+ assert_match("Couldn't create '#{database_url_db_name}' database. Please check your configuration.", output)
assert_equal 1, $?.exitstatus
end
end
@@ -127,6 +186,59 @@ module ApplicationTests
end
end
+ test "db:truncate_all truncates all non-internal tables" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+
+ rails "db:truncate_all"
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 0, Book.count
+ end
+ end
+
+ test "db:truncate_all does not truncate any tables when environment is protected" do
+ with_rails_env "production" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ books = ActiveRecord::Base.connection.execute("SELECT * from \"books\"")
+
+ output = rails("db:truncate_all", allow_failure: true)
+ assert_match(/ActiveRecord::ProtectedEnvironmentError/, output)
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 1, Book.count
+ assert_equal(books, ActiveRecord::Base.connection.execute("SELECT * from \"books\""))
+ end
+ end
+ end
+
def db_migrate_and_status(expected_database)
rails "generate", "model", "book", "title:string"
rails "db:migrate"
@@ -167,9 +279,10 @@ module ApplicationTests
def db_fixtures_load(expected_database)
Dir.chdir(app_path) do
rails "generate", "model", "book", "title:string"
+ reload
rails "db:migrate", "db:fixtures:load"
+
assert_match expected_database, ActiveRecord::Base.connection_config[:database]
- require "#{app_path}/app/models/book"
assert_equal 2, Book.count
end
end
@@ -189,8 +302,9 @@ module ApplicationTests
require "#{app_path}/config/environment"
rails "generate", "model", "admin::book", "title:string"
+ reload
rails "db:migrate", "db:fixtures:load"
- require "#{app_path}/app/models/admin/book"
+
assert_equal 2, Admin::Book.count
end
@@ -311,13 +425,12 @@ module ApplicationTests
end
test "db:setup loads schema and seeds database" do
- begin
- @old_rails_env = ENV["RAILS_ENV"]
- @old_rack_env = ENV["RACK_ENV"]
- ENV.delete "RAILS_ENV"
- ENV.delete "RACK_ENV"
+ @old_rails_env = ENV["RAILS_ENV"]
+ @old_rack_env = ENV["RACK_ENV"]
+ ENV.delete "RAILS_ENV"
+ ENV.delete "RACK_ENV"
- app_file "db/schema.rb", <<-RUBY
+ app_file "db/schema.rb", <<-RUBY
ActiveRecord::Schema.define(version: "1") do
create_table :users do |t|
t.string :name
@@ -325,16 +438,15 @@ module ApplicationTests
end
RUBY
- app_file "db/seeds.rb", <<-RUBY
- puts ActiveRecord::Base.connection_config[:database]
- RUBY
+ app_file "db/seeds.rb", <<-RUBY
+ puts ActiveRecord::Base.connection_config[:database]
+ RUBY
- database_path = rails("db:setup")
- assert_equal "development.sqlite3", File.basename(database_path.strip)
- ensure
- ENV["RAILS_ENV"] = @old_rails_env
- ENV["RACK_ENV"] = @old_rack_env
- end
+ database_path = rails("db:setup")
+ assert_equal "development.sqlite3", File.basename(database_path.strip)
+ ensure
+ ENV["RAILS_ENV"] = @old_rails_env
+ ENV["RACK_ENV"] = @old_rack_env
end
test "db:setup sets ar_internal_metadata" do
@@ -375,6 +487,88 @@ module ApplicationTests
assert_equal "test", test_environment.call
end
+
+ test "db:seed:replant truncates all non-internal tables and loads the seeds" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+
+ app_file "db/seeds.rb", <<-RUBY
+ Book.create!(title: "Rework")
+ Book.create!(title: "Ruby Under a Microscope")
+ RUBY
+
+ rails "db:seed:replant"
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 2, Book.count
+ assert_not_predicate Book.where(title: "Remote"), :exists?
+ assert_predicate Book.where(title: "Rework"), :exists?
+ assert_predicate Book.where(title: "Ruby Under a Microscope"), :exists?
+ end
+ end
+
+ test "db:seed:replant does not truncate any tables and does not load the seeds when environment is protected" do
+ with_rails_env "production" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ books = ActiveRecord::Base.connection.execute("SELECT * from \"books\"")
+
+ app_file "db/seeds.rb", <<-RUBY
+ Book.create!(title: "Rework")
+ RUBY
+
+ output = rails("db:seed:replant", allow_failure: true)
+ assert_match(/ActiveRecord::ProtectedEnvironmentError/, output)
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 1, Book.count
+ assert_equal(books, ActiveRecord::Base.connection.execute("SELECT * from \"books\""))
+ assert_not_predicate Book.where(title: "Rework"), :exists?
+ end
+ end
+ end
+
+ test "db:prepare setup the database" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ output = rails("db:prepare")
+ assert_match(/CreateBooks: migrated/, output)
+
+ output = rails("db:drop")
+ assert_match(/Dropped database/, output)
+
+ rails "generate", "model", "recipe", "title:string"
+ output = rails("db:prepare")
+ assert_match(/CreateBooks: migrated/, output)
+ assert_match(/CreateRecipes: migrated/, output)
+ end
+ end
end
end
end
diff --git a/railties/test/application/rake/dev_test.rb b/railties/test/application/rake/dev_test.rb
index 66e1ac9d99..a87f453075 100644
--- a/railties/test/application/rake/dev_test.rb
+++ b/railties/test/application/rake/dev_test.rb
@@ -9,6 +9,7 @@ module ApplicationTests
def setup
build_app
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
end
def teardown
@@ -17,33 +18,46 @@ module ApplicationTests
test "dev:cache creates file and outputs message" do
Dir.chdir(app_path) do
- output = rails("dev:cache")
- assert File.exist?("tmp/caching-dev.txt")
- assert_match(/Development mode is now being cached/, output)
+ stderr = capture(:stderr) do
+ output = run_rake_dev_cache
+ assert File.exist?("tmp/caching-dev.txt")
+ assert_match(/Development mode is now being cached/, output)
+ end
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake dev:cache` is deprecated and will be removed in Rails 6.1/, stderr)
end
end
test "dev:cache deletes file and outputs message" do
Dir.chdir(app_path) do
- rails "dev:cache" # Create caching file.
- output = rails("dev:cache") # Delete caching file.
- assert_not File.exist?("tmp/caching-dev.txt")
- assert_match(/Development mode is no longer being cached/, output)
+ stderr = capture(:stderr) do
+ run_rake_dev_cache # Create caching file.
+ output = run_rake_dev_cache # Delete caching file.
+ assert_not File.exist?("tmp/caching-dev.txt")
+ assert_match(/Development mode is no longer being cached/, output)
+ end
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake dev:cache` is deprecated and will be removed in Rails 6.1/, stderr)
end
end
test "dev:cache touches tmp/restart.txt" do
Dir.chdir(app_path) do
- rails "dev:cache"
- assert File.exist?("tmp/restart.txt")
-
- prev_mtime = File.mtime("tmp/restart.txt")
- sleep(1)
- rails "dev:cache"
- curr_mtime = File.mtime("tmp/restart.txt")
- assert_not_equal prev_mtime, curr_mtime
+ stderr = capture(:stderr) do
+ run_rake_dev_cache
+ assert File.exist?("tmp/restart.txt")
+
+ prev_mtime = File.mtime("tmp/restart.txt")
+ run_rake_dev_cache
+ curr_mtime = File.mtime("tmp/restart.txt")
+ assert_not_equal prev_mtime, curr_mtime
+ end
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake dev:cache` is deprecated and will be removed in Rails 6.1/, stderr)
end
end
+
+ private
+ def run_rake_dev_cache
+ `bin/rake dev:cache`
+ end
end
end
end
diff --git a/railties/test/application/rake/initializers_test.rb b/railties/test/application/rake/initializers_test.rb
new file mode 100644
index 0000000000..8de4967021
--- /dev/null
+++ b/railties/test/application/rake/initializers_test.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeInitializersTest < ActiveSupport::TestCase
+ setup :build_app
+ teardown :teardown_app
+
+ test "`rake initializers` prints out defined initializers invoked by Rails" do
+ capture(:stderr) do
+ initial_output = run_rake_initializers
+ initial_output_length = initial_output.split("\n").length
+
+ assert_operator initial_output_length, :>, 0
+ assert_not initial_output.include?("set_added_test_module")
+
+ add_to_config <<-RUBY
+ initializer(:set_added_test_module) { }
+ RUBY
+
+ final_output = run_rake_initializers
+ final_output_length = final_output.split("\n").length
+
+ assert_equal 1, (final_output_length - initial_output_length)
+ assert final_output.include?("set_added_test_module")
+ end
+ end
+
+ test "`rake initializers` outputs a deprecation warning" do
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
+
+ stderr = capture(:stderr) { run_rake_initializers }
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake initializers` is deprecated and will be removed in Rails 6.1/, stderr)
+ end
+
+ private
+ def run_rake_initializers
+ Dir.chdir(app_path) { `bin/rake initializers` }
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb
index 07d96fcb56..147b8f94e1 100644
--- a/railties/test/application/rake/multi_dbs_test.rb
+++ b/railties/test/application/rake/multi_dbs_test.rb
@@ -16,21 +16,24 @@ module ApplicationTests
teardown_app
end
- def db_create_and_drop(namespace, expected_database, environment_loaded: true)
+ def db_create_and_drop(namespace, expected_database)
Dir.chdir(app_path) do
output = rails("db:create")
assert_match(/Created database/, output)
assert_match_namespace(namespace, output)
+ assert_no_match(/already exists/, output)
assert File.exist?(expected_database)
+
output = rails("db:drop")
assert_match(/Dropped database/, output)
assert_match_namespace(namespace, output)
+ assert_no_match(/does not exist/, output)
assert_not File.exist?(expected_database)
end
end
- def db_create_and_drop_namespace(namespace, expected_database, environment_loaded: true)
+ def db_create_and_drop_namespace(namespace, expected_database)
Dir.chdir(app_path) do
output = rails("db:create:#{namespace}")
assert_match(/Created database/, output)
@@ -52,11 +55,40 @@ module ApplicationTests
end
end
- def db_migrate_and_schema_dump_and_load(namespace, expected_database, format)
+ def db_migrate_and_migrate_status
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ rails "db:migrate"
+ output = rails "db:migrate:status"
+ assert_match(/up \d+ Create books/, output)
+ assert_match(/up \d+ Create dogs/, output)
+ end
+ end
+
+ def db_migrate_and_schema_cache_dump
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ rails "db:migrate"
+ rails "db:schema:cache:dump"
+ assert File.exist?("db/schema_cache.yml")
+ assert File.exist?("db/animals_schema_cache.yml")
+ end
+ end
+
+ def db_migrate_and_schema_cache_dump_and_schema_cache_clear
Dir.chdir(app_path) do
- rails "generate", "model", "book", "title:string"
- rails "generate", "model", "dog", "name:string"
- write_models_for_animals
+ generate_models_for_animals
+ rails "db:migrate"
+ rails "db:schema:cache:dump"
+ rails "db:schema:cache:clear"
+ assert_not File.exist?("db/schema_cache.yml")
+ assert_not File.exist?("db/animals_schema_cache.yml")
+ end
+ end
+
+ def db_migrate_and_schema_dump_and_load(format)
+ Dir.chdir(app_path) do
+ generate_models_for_animals
rails "db:migrate", "db:#{format}:dump"
if format == "schema"
@@ -81,11 +113,9 @@ module ApplicationTests
end
end
- def db_migrate_namespaced(namespace, expected_database)
+ def db_migrate_namespaced(namespace)
Dir.chdir(app_path) do
- rails "generate", "model", "book", "title:string"
- rails "generate", "model", "dog", "name:string"
- write_models_for_animals
+ generate_models_for_animals
output = rails("db:migrate:#{namespace}")
if namespace == "primary"
assert_match(/CreateBooks: migrated/, output)
@@ -95,6 +125,33 @@ module ApplicationTests
end
end
+ def db_migrate_status_namespaced(namespace)
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ output = rails("db:migrate:status:#{namespace}")
+ if namespace == "primary"
+ assert_match(/up \d+ Create books/, output)
+ else
+ assert_match(/up \d+ Create dogs/, output)
+ end
+ end
+ end
+
+ def db_prepare
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ output = rails("db:prepare")
+
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ if db_config.spec_name == "primary"
+ assert_match(/CreateBooks: migrated/, output)
+ else
+ assert_match(/CreateDogs: migrated/, output)
+ end
+ end
+ end
+ end
+
def write_models_for_animals
# make a directory for the animals migration
FileUtils.mkdir_p("#{app_path}/db/animals_migrate")
@@ -114,51 +171,81 @@ module ApplicationTests
# create the base model for dog to inherit from
File.open("#{app_path}/app/models/animals_base.rb", "w") do |file|
- file.write(<<-EOS
-class AnimalsBase < ActiveRecord::Base
- self.abstract_class = true
+ file.write(<<~EOS)
+ class AnimalsBase < ActiveRecord::Base
+ self.abstract_class = true
- establish_connection :animals
-end
-EOS
-)
+ establish_connection :animals
+ end
+ EOS
end
end
+ def generate_models_for_animals
+ rails "generate", "model", "book", "title:string"
+ rails "generate", "model", "dog", "name:string"
+ write_models_for_animals
+ reload
+ end
+
test "db:create and db:drop works on all databases for env" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_create_and_drop namespace, config["database"]
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_create_and_drop db_config.spec_name, db_config.config["database"]
end
end
test "db:create:namespace and db:drop:namespace works on specified databases" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_create_and_drop_namespace namespace, config["database"]
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_create_and_drop_namespace db_config.spec_name, db_config.config["database"]
end
end
test "db:migrate and db:schema:dump and db:schema:load works on all databases" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_migrate_and_schema_dump_and_load namespace, config["database"], "schema"
- end
+ db_migrate_and_schema_dump_and_load "schema"
end
test "db:migrate and db:structure:dump and db:structure:load works on all databases" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_migrate_and_schema_dump_and_load namespace, config["database"], "structure"
- end
+ db_migrate_and_schema_dump_and_load "structure"
end
test "db:migrate:namespace works" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_migrate_namespaced namespace, config["database"]
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_migrate_namespaced db_config.spec_name
end
end
+
+ test "db:migrate:status works on all databases" do
+ require "#{app_path}/config/environment"
+ db_migrate_and_migrate_status
+ end
+
+ test "db:migrate:status:namespace works" do
+ require "#{app_path}/config/environment"
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_migrate_namespaced db_config.spec_name
+ db_migrate_status_namespaced db_config.spec_name
+ end
+ end
+
+ test "db:schema:cache:dump works on all databases" do
+ require "#{app_path}/config/environment"
+ db_migrate_and_schema_cache_dump
+ end
+
+ test "db:schema:cache:clear works on all databases" do
+ require "#{app_path}/config/environment"
+ db_migrate_and_schema_cache_dump_and_schema_cache_clear
+ end
+
+ test "db:prepare works on all databases" do
+ require "#{app_path}/config/environment"
+ db_prepare
+ end
end
end
end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 9e22ba84b5..60802ef7c4 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -10,6 +10,7 @@ module ApplicationTests
def setup
build_app
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
require "rails/all"
super
end
diff --git a/railties/test/application/rake/routes_test.rb b/railties/test/application/rake/routes_test.rb
new file mode 100644
index 0000000000..9879d1f047
--- /dev/null
+++ b/railties/test/application/rake/routes_test.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeRoutesTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup :build_app
+ teardown :teardown_app
+
+ test "`rake routes` outputs routes" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ end
+ RUBY
+
+ assert_equal <<~MESSAGE, run_rake_routes
+ Prefix Verb URI Pattern Controller#Action
+ cart GET /cart(.:format) cart#show
+ rails_amazon_inbound_emails POST /rails/action_mailbox/amazon/inbound_emails(.:format) action_mailbox/ingresses/amazon/inbound_emails#create
+ rails_mandrill_inbound_emails POST /rails/action_mailbox/mandrill/inbound_emails(.:format) action_mailbox/ingresses/mandrill/inbound_emails#create
+ rails_postmark_inbound_emails POST /rails/action_mailbox/postmark/inbound_emails(.:format) action_mailbox/ingresses/postmark/inbound_emails#create
+ rails_relay_inbound_emails POST /rails/action_mailbox/relay/inbound_emails(.:format) action_mailbox/ingresses/relay/inbound_emails#create
+ rails_sendgrid_inbound_emails POST /rails/action_mailbox/sendgrid/inbound_emails(.:format) action_mailbox/ingresses/sendgrid/inbound_emails#create
+ rails_mailgun_inbound_emails POST /rails/action_mailbox/mailgun/inbound_emails/mime(.:format) action_mailbox/ingresses/mailgun/inbound_emails#create
+ rails_conductor_inbound_emails GET /rails/conductor/action_mailbox/inbound_emails(.:format) rails/conductor/action_mailbox/inbound_emails#index
+ POST /rails/conductor/action_mailbox/inbound_emails(.:format) rails/conductor/action_mailbox/inbound_emails#create
+ new_rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/new(.:format) rails/conductor/action_mailbox/inbound_emails#new
+ edit_rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format) rails/conductor/action_mailbox/inbound_emails#edit
+ rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#show
+ PATCH /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#update
+ PUT /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#update
+ DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#destroy
+ rails_conductor_inbound_email_reroute POST /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format) rails/conductor/action_mailbox/reroutes#create
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ MESSAGE
+ end
+
+ test "`rake routes` outputs a deprecation warning" do
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
+
+ stderr = capture(:stderr) { run_rake_routes }
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake routes` is deprecated and will be removed in Rails 6.1/, stderr)
+ end
+
+ private
+ def run_rake_routes
+ Dir.chdir(app_path) { `bin/rake routes` }
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 1522a2bbc5..fe56e3d076 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -118,7 +118,7 @@ module ApplicationTests
end
def test_code_statistics_sanity
- assert_match "Code LOC: 25 Test LOC: 0 Code to Test Ratio: 1:0.0",
+ assert_match "Code LOC: 32 Test LOC: 0 Code to Test Ratio: 1:0.0",
rails("stats")
end
@@ -145,8 +145,8 @@ module ApplicationTests
# loading a specific fixture
rails "db:fixtures:load", "FIXTURES=products"
- assert_equal 2, ::AppTemplate::Application::Product.count
- assert_equal 0, ::AppTemplate::Application::User.count
+ assert_equal 2, Product.count
+ assert_equal 0, User.count
end
def test_loading_only_yml_fixtures
@@ -160,7 +160,10 @@ module ApplicationTests
def test_scaffold_tests_pass_by_default
rails "generate", "scaffold", "user", "username:string", "password:string"
- with_rails_env("test") { rails("db:migrate") }
+ with_rails_env("test") do
+ rails("db:migrate")
+ rails("webpacker:compile")
+ end
output = rails("test")
assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
@@ -189,7 +192,10 @@ module ApplicationTests
rails "generate", "model", "Product"
rails "generate", "model", "Cart"
rails "generate", "scaffold", "LineItems", "product:references", "cart:belongs_to"
- with_rails_env("test") { rails("db:migrate") }
+ with_rails_env("test") do
+ rails("db:migrate")
+ rails("webpacker:compile")
+ end
output = rails("test")
assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index 8f5f48c281..dfb9540093 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -105,6 +105,14 @@ module ApplicationTests
assert_match "development", rails("runner", "puts Rails.env")
end
+ def test_environment_option
+ assert_match "production", rails("runner", "-e", "production", "puts Rails.env")
+ end
+
+ def test_environment_option_is_properly_expanded
+ assert_match "production", rails("runner", "-e", "prod", "puts Rails.env")
+ end
+
def test_runner_detects_syntax_errors
output = rails("runner", "puts 'hello world", allow_failure: true)
assert_not_predicate $?, :success?
diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb
index 92b991dd05..5fe1b4e6e7 100644
--- a/railties/test/application/server_test.rb
+++ b/railties/test/application/server_test.rb
@@ -18,20 +18,6 @@ module ApplicationTests
teardown_app
end
- test "deprecate support of older `config.ru`" do
- remove_file "config.ru"
- app_file "config.ru", <<-RUBY
- require_relative 'config/environment'
- run AppTemplate::Application
- RUBY
-
- server = Rails::Server.new(config: "#{app_path}/config.ru")
- server.app
-
- log = File.read(Rails.application.config.paths["log"].first)
- assert_match(/DEPRECATION WARNING: Using `Rails::Application` subclass to start the server is deprecated/, log)
- end
-
test "restart rails server with custom pid file path" do
skip "PTY unavailable" unless available_pty?
@@ -40,17 +26,17 @@ module ApplicationTests
f.puts "require 'bundler/setup'"
end
- master, slave = PTY.open
+ primary, replica = PTY.open
pid = nil
- begin
- pid = Process.spawn("#{app_path}/bin/rails server -P tmp/dummy.pid", in: slave, out: slave, err: slave)
- assert_output("Listening", master)
+ Bundler.with_original_env do
+ pid = Process.spawn("bin/rails server -b localhost -P tmp/dummy.pid", chdir: app_path, in: replica, out: replica, err: replica)
+ assert_output("Listening", primary)
rails("restart")
- assert_output("Restarting", master)
- assert_output("Inherited", master)
+ assert_output("Restarting", primary)
+ assert_output("tcp://localhost:3000", primary)
ensure
kill(pid) if pid
end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index 8e5ccf94cc..0bdd2b314d 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -16,7 +16,7 @@ module ApplicationTests
teardown_app
end
- def test_run_via_backwardscompatibility
+ def test_run_via_backwards_compatibility
require "minitest/rails_plugin"
assert_nothing_raised do
@@ -98,6 +98,17 @@ module ApplicationTests
end
end
+ def test_run_channels
+ create_test_file :channels, "foo_channel"
+ create_test_file :channels, "bar_channel"
+
+ rails("test:channels").tap do |output|
+ assert_match "FooChannelTest", output
+ assert_match "BarChannelTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
def test_run_controllers
create_test_file :controllers, "foo_controller"
create_test_file :controllers, "bar_controller"
@@ -131,6 +142,18 @@ module ApplicationTests
end
end
+ def test_run_mailboxes
+ create_test_file :mailboxes, "foo_mailbox"
+ create_test_file :mailboxes, "bar_mailbox"
+ create_test_file :models, "foo"
+
+ rails("test:mailboxes").tap do |output|
+ assert_match "FooMailboxTest", output
+ assert_match "BarMailboxTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
def test_run_functionals
create_test_file :mailers, "foo_mailer"
create_test_file :controllers, "bar_controller"
@@ -155,11 +178,11 @@ module ApplicationTests
end
def test_run_all_suites
- suites = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration, :jobs]
+ suites = [:models, :helpers, :unit, :channels, :controllers, :mailers, :functional, :integration, :jobs, :mailboxes]
suites.each { |suite| create_test_file suite, "foo_#{suite}" }
run_test_command("") .tap do |output|
suites.each { |suite| assert_match "Foo#{suite.to_s.camelize}Test", output }
- assert_match "8 runs, 8 assertions, 0 failures", output
+ assert_match "10 runs, 10 assertions, 0 failures", output
end
end
@@ -504,7 +527,7 @@ module ApplicationTests
create_test_file :models, "post", pass: false, print: false
output = run_test_command("test/models/post_test.rb")
- expect = %r{Running:\n\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/models/post_test.rb:6\]:\nwups!\n\nbin/rails test test/models/post_test.rb:4\n\n\n\n}
+ expect = %r{Running:\n\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/models/post_test.rb:6\]:\nwups!\n\nrails test test/models/post_test.rb:4\n\n\n\n}
assert_match expect, output
end
@@ -523,25 +546,84 @@ module ApplicationTests
end
def test_run_in_parallel_with_processes
+ exercise_parallelization_regardless_of_machine_core_count(with: :processes)
+
file_name = create_parallel_processes_test_file
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
output = run_test_command(file_name)
assert_match %r{Finished in.*\n2 runs, 2 assertions}, output
+ assert_no_match "create_table(:users)", output
end
def test_run_in_parallel_with_threads
- app_path("/test/test_helper.rb") do |file_name|
- file = File.read(file_name)
- file.sub!(/parallelize\(([^\)]*)\)/, "parallelize(\\1, with: :threads)")
- File.write(file_name, file)
- end
+ exercise_parallelization_regardless_of_machine_core_count(with: :threads)
file_name = create_parallel_threads_test_file
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
output = run_test_command(file_name)
assert_match %r{Finished in.*\n2 runs, 2 assertions}, output
+ assert_no_match "create_table(:users)", output
+ end
+
+ def test_run_in_parallel_with_unmarshable_exception
+ exercise_parallelization_regardless_of_machine_core_count(with: :processes)
+
+ file = app_file "test/fail_test.rb", <<-RUBY
+ require "test_helper"
+ class FailTest < ActiveSupport::TestCase
+ class BadError < StandardError
+ def initialize
+ super
+ @proc = ->{ }
+ end
+ end
+
+ test "fail" do
+ raise BadError
+ assert true
+ end
+ end
+ RUBY
+
+ output = run_test_command(file)
+
+ assert_match "DRb::DRbRemoteError: FailTest::BadError", output
+ assert_match "1 runs, 0 assertions, 0 failures, 1 errors", output
+ end
+
+ def test_run_in_parallel_with_unknown_object
+ exercise_parallelization_regardless_of_machine_core_count(with: :processes)
+
+ create_scaffold
+
+ app_file "config/environments/test.rb", <<-RUBY
+ Rails.application.configure do
+ config.action_controller.allow_forgery_protection = true
+ config.action_dispatch.show_exceptions = false
+ end
+ RUBY
+
+ output = run_test_command("-n test_should_create_user")
+
+ assert_match "ActionController::InvalidAuthenticityToken", output
end
def test_raise_error_when_specified_file_does_not_exist
@@ -553,7 +635,7 @@ module ApplicationTests
create_test_file :models, "account"
create_test_file :models, "post", pass: false
# This specifically verifies TEST for backwards compatibility with rake test
- # as bin/rails test already supports running tests from a single file more cleanly.
+ # as `rails test` already supports running tests from a single file more cleanly.
output = Dir.chdir(app_path) { `bin/rake test TEST=test/models/post_test.rb` }
assert_match "PostTest", output, "passing TEST= should run selected test"
@@ -660,6 +742,7 @@ module ApplicationTests
def test_reset_sessions_before_rollback_on_system_tests
app_file "test/system/reset_session_before_rollback_test.rb", <<-RUBY
require "application_system_test_case"
+ require "selenium/webdriver"
class ResetSessionBeforeRollbackTest < ApplicationSystemTestCase
def teardown_fixtures
@@ -685,6 +768,32 @@ module ApplicationTests
end
end
+ def test_reset_sessions_on_failed_system_test_screenshot
+ app_file "test/system/reset_sessions_on_failed_system_test_screenshot_test.rb", <<~RUBY
+ require "application_system_test_case"
+ require "selenium/webdriver"
+
+ class ResetSessionsOnFailedSystemTestScreenshotTest < ApplicationSystemTestCase
+ ActionDispatch::SystemTestCase.class_eval do
+ def take_failed_screenshot
+ raise Capybara::CapybaraError
+ end
+ end
+
+ Capybara.instance_eval do
+ def reset_sessions!
+ puts "Capybara.reset_sessions! called"
+ end
+ end
+
+ test "dummy" do
+ end
+ end
+ RUBY
+ output = run_test_command("test/system/reset_sessions_on_failed_system_test_screenshot_test.rb")
+ assert_match "Capybara.reset_sessions! called", output
+ end
+
def test_system_tests_are_not_run_with_the_default_test_command
app_file "test/system/dummy_test.rb", <<-RUBY
require "application_system_test_case"
@@ -719,6 +828,7 @@ module ApplicationTests
def test_system_tests_are_run_through_rake_test_when_given_in_TEST
app_file "test/system/dummy_test.rb", <<-RUBY
require "application_system_test_case"
+ require "selenium/webdriver"
class DummyTest < ApplicationSystemTestCase
test "something" do
@@ -885,6 +995,14 @@ module ApplicationTests
RUBY
end
+ def exercise_parallelization_regardless_of_machine_core_count(with:)
+ app_path("test/test_helper.rb") do |file_name|
+ file = File.read(file_name)
+ file.sub!(/parallelize\(([^\)]*)\)/, "parallelize(workers: 2, with: :#{with})")
+ File.write(file_name, file)
+ end
+ end
+
def create_env_test
app_file "test/unit/env_test.rb", <<-RUBY
require 'test_helper'
diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb
index fb43bebfbe..83e63718df 100644
--- a/railties/test/application/test_test.rb
+++ b/railties/test/application/test_test.rb
@@ -234,7 +234,7 @@ module ApplicationTests
# TODO: would be nice if we could detect the schema change automatically.
# For now, the user has to synchronize the schema manually.
- # This test-case serves as a reminder for this use-case.
+ # This test case serves as a reminder for this use case.
test "manually synchronize test schema after rollback" do
output = rails("generate", "model", "user", "name:string")
version = output.match(/(\d+)_create_users\.rb/)[1]
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
index f22b9fda3d..dc737089f6 100644
--- a/railties/test/application/url_generation_test.rb
+++ b/railties/test/application/url_generation_test.rb
@@ -19,6 +19,8 @@ module ApplicationTests
config.session_store :cookie_store, key: "_myapp_session"
config.active_support.deprecation = :log
config.eager_load = false
+ config.hosts << proc { true }
+ config.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
end
Rails.application.initialize!
diff --git a/railties/test/application/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb
new file mode 100644
index 0000000000..b248459e47
--- /dev/null
+++ b/railties/test/application/zeitwerk_integration_test.rb
@@ -0,0 +1,371 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "active_support/dependencies/zeitwerk_integration"
+
+class ZeitwerkIntegrationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def boot(env = "development")
+ app(env)
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def deps
+ ActiveSupport::Dependencies
+ end
+
+ def decorated?
+ deps.singleton_class < deps::ZeitwerkIntegration::Decorations
+ end
+
+ test "ActiveSupport::Dependencies is decorated by default" do
+ boot
+
+ assert decorated?
+ assert Rails.autoloaders.zeitwerk_enabled?
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.main
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.once
+ assert_equal [Rails.autoloaders.main, Rails.autoloaders.once], Rails.autoloaders.to_a
+ end
+
+ test "ActiveSupport::Dependencies is not decorated in classic mode" do
+ add_to_config "config.autoloader = :classic"
+ boot
+
+ assert_not decorated?
+ assert_not Rails.autoloaders.zeitwerk_enabled?
+ assert_nil Rails.autoloaders.main
+ assert_nil Rails.autoloaders.once
+ assert_equal 0, Rails.autoloaders.count
+ end
+
+ test "autoloaders inflect with Active Support" do
+ app_file "config/initializers/inflections.rb", <<-RUBY
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
+ inflect.acronym 'RESTful'
+ end
+ RUBY
+
+ app_file "app/controllers/restful_controller.rb", <<-RUBY
+ class RESTfulController < ApplicationController
+ end
+ RUBY
+
+ boot
+
+ basename = "restful_controller"
+ abspath = "#{Rails.root}/app/controllers/#{basename}.rb"
+ camelized = "RESTfulController"
+
+ Rails.autoloaders.each do |autoloader|
+ assert_equal camelized, autoloader.inflector.camelize(basename, abspath)
+ end
+
+ assert RESTfulController
+ end
+
+ test "constantize returns the value stored in the constant" do
+ app_file "app/models/admin/user.rb", "class Admin::User; end"
+ boot
+
+ assert_same Admin::User, deps.constantize("Admin::User")
+ end
+
+ test "constantize raises if the constant is unknown" do
+ boot
+
+ assert_raises(NameError) { deps.constantize("Admin") }
+ end
+
+ test "safe_constantize returns the value stored in the constant" do
+ app_file "app/models/admin/user.rb", "class Admin::User; end"
+ boot
+
+ assert_same Admin::User, deps.safe_constantize("Admin::User")
+ end
+
+ test "safe_constantize returns nil for unknown constants" do
+ boot
+
+ assert_nil deps.safe_constantize("Admin")
+ end
+
+ test "to_unload? says if a constant would be unloaded (main)" do
+ app_file "app/models/user.rb", "class User; end"
+ app_file "app/models/post.rb", "class Post; end"
+ boot
+
+ assert Post
+ assert deps.to_unload?("Post")
+ assert_not deps.to_unload?("User")
+ end
+
+ test "to_unload? says if a constant would be unloaded (once)" do
+ add_to_config 'config.autoload_once_paths << "#{Rails.root}/extras"'
+ app_file "extras/foo.rb", "class Foo; end"
+ app_file "extras/bar.rb", "class Bar; end"
+ boot
+
+ assert Foo
+ assert_not deps.to_unload?("Foo")
+ assert_not deps.to_unload?("Bar")
+ end
+
+ test "to_unload? says if a constant would be unloaded (reloading disabled)" do
+ app_file "app/models/user.rb", "class User; end"
+ app_file "app/models/post.rb", "class Post; end"
+ boot("production")
+
+ assert Post
+ assert_not deps.to_unload?("Post")
+ assert_not deps.to_unload?("User")
+ end
+
+ test "eager loading loads the application code" do
+ $zeitwerk_integration_test_user = false
+ $zeitwerk_integration_test_post = false
+
+ app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
+ app_file "app/models/post.rb", "class Post; end; $zeitwerk_integration_test_post = true"
+
+ boot("production")
+
+ assert $zeitwerk_integration_test_user
+ assert $zeitwerk_integration_test_post
+ end
+
+ test "reloading is enabled if config.cache_classes is false" do
+ boot
+
+ assert Rails.autoloaders.main.reloading_enabled?
+ assert_not Rails.autoloaders.once.reloading_enabled?
+ end
+
+ test "reloading is disabled if config.cache_classes is true" do
+ boot("production")
+
+ assert_not Rails.autoloaders.main.reloading_enabled?
+ assert_not Rails.autoloaders.once.reloading_enabled?
+ end
+
+ test "eager loading loads code in engines" do
+ $test_blog_engine_eager_loaded = false
+
+ engine("blog") do |bukkit|
+ bukkit.write("lib/blog.rb", "class BlogEngine < Rails::Engine; end")
+ bukkit.write("app/models/post.rb", "Post = $test_blog_engine_eager_loaded = true")
+ end
+
+ boot("production")
+
+ assert $test_blog_engine_eager_loaded
+ end
+
+ test "eager loading loads anything managed by Zeitwerk" do
+ $zeitwerk_integration_test_user = false
+ app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
+
+ $zeitwerk_integration_test_extras = false
+ app_dir "extras"
+ app_file "extras/webhook_hacks.rb", "WebhookHacks = 1; $zeitwerk_integration_test_extras = true"
+
+ require "zeitwerk"
+ autoloader = Zeitwerk::Loader.new
+ autoloader.push_dir("#{app_path}/extras")
+ autoloader.setup
+
+ boot("production")
+
+ assert $zeitwerk_integration_test_user
+ assert $zeitwerk_integration_test_extras
+ end
+
+ test "autoload directories not present in eager load paths are not eager loaded" do
+ $zeitwerk_integration_test_user = false
+ app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
+
+ $zeitwerk_integration_test_lib = false
+ app_dir "lib"
+ app_file "lib/webhook_hacks.rb", "WebhookHacks = 1; $zeitwerk_integration_test_lib = true"
+
+ $zeitwerk_integration_test_extras = false
+ app_dir "extras"
+ app_file "extras/websocket_hacks.rb", "WebsocketHacks = 1; $zeitwerk_integration_test_extras = true"
+
+ add_to_config "config.autoload_paths << '#{app_path}/lib'"
+ add_to_config "config.autoload_once_paths << '#{app_path}/extras'"
+
+ boot("production")
+
+ assert $zeitwerk_integration_test_user
+ assert_not $zeitwerk_integration_test_lib
+ assert_not $zeitwerk_integration_test_extras
+
+ assert WebhookHacks
+ assert WebsocketHacks
+
+ assert $zeitwerk_integration_test_lib
+ assert $zeitwerk_integration_test_extras
+ end
+
+ test "autoload_paths are set as root dirs of main, and in the same order" do
+ boot
+
+ existing_autoload_paths = deps.autoload_paths.select { |dir| File.directory?(dir) }
+ assert_equal existing_autoload_paths, Rails.autoloaders.main.dirs
+ end
+
+ test "autoload_once_paths go to the once autoloader, and in the same order" do
+ extras = %w(e1 e2 e3)
+ extras.each do |extra|
+ app_dir extra
+ add_to_config %(config.autoload_once_paths << "\#{Rails.root}/#{extra}")
+ end
+
+ boot
+
+ extras = extras.map { |extra| "#{app_path}/#{extra}" }
+ extras.each do |extra|
+ assert_not_includes Rails.autoloaders.main.dirs, extra
+ end
+ assert_equal extras, Rails.autoloaders.once.dirs
+ end
+
+ test "clear reloads the main autoloader, and does not reload the once one" do
+ boot
+
+ $zeitwerk_integration_reload_test = []
+
+ main_autoloader = Rails.autoloaders.main
+ def main_autoloader.reload
+ $zeitwerk_integration_reload_test << :main_autoloader
+ super
+ end
+
+ once_autoloader = Rails.autoloaders.once
+ def once_autoloader.reload
+ $zeitwerk_integration_reload_test << :once_autoloader
+ super
+ end
+
+ ActiveSupport::Dependencies.clear
+
+ assert_equal %i(main_autoloader), $zeitwerk_integration_reload_test
+ end
+
+ test "verbose = true sets the dependencies logger if present" do
+ boot
+
+ logger = Logger.new(File::NULL)
+ ActiveSupport::Dependencies.logger = logger
+ ActiveSupport::Dependencies.verbose = true
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same logger, autoloader.logger
+ end
+ end
+
+ test "verbose = true sets the Rails logger as fallback" do
+ boot
+
+ ActiveSupport::Dependencies.verbose = true
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same Rails.logger, autoloader.logger
+ end
+ end
+
+ test "verbose = false sets loggers to nil" do
+ boot
+
+ ActiveSupport::Dependencies.verbose = true
+ Rails.autoloaders.each do |autoloader|
+ assert autoloader.logger
+ end
+
+ ActiveSupport::Dependencies.verbose = false
+ Rails.autoloaders.each do |autoloader|
+ assert_nil autoloader.logger
+ end
+ end
+
+ test "unhooks" do
+ boot
+
+ assert_equal Module, Module.method(:const_missing).owner
+ assert_equal :no_op, deps.unhook!
+ end
+
+ test "autoloaders.logger=" do
+ boot
+
+ logger = ->(_msg) { }
+ Rails.autoloaders.logger = logger
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same logger, autoloader.logger
+ end
+
+ Rails.autoloaders.logger = Rails.logger
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same Rails.logger, autoloader.logger
+ end
+
+ Rails.autoloaders.logger = nil
+
+ Rails.autoloaders.each do |autoloader|
+ assert_nil autoloader.logger
+ end
+ end
+
+ # This is here because to guarantee classic mode works as always, Zeitwerk
+ # integration does not touch anything in classic. The descendants tracker is a
+ # very small one-liner exception. We leave its main test suite untouched, and
+ # add some minimal safety net here.
+ #
+ # When time passes, things are going to be reorganized (famous last words).
+ test "descendants tracker" do
+ class ::ZeitwerkDTIntegrationTestRoot
+ extend ActiveSupport::DescendantsTracker
+ end
+ class ::ZeitwerkDTIntegrationTestChild < ::ZeitwerkDTIntegrationTestRoot; end
+ class ::ZeitwerkDTIntegrationTestGrandchild < ::ZeitwerkDTIntegrationTestChild; end
+
+ begin
+ app_file "app/models/user.rb", "class User < ZeitwerkDTIntegrationTestRoot; end"
+ app_file "app/models/post.rb", "class Post < ZeitwerkDTIntegrationTestRoot; end"
+ app_file "app/models/tutorial.rb", "class Tutorial < Post; end"
+ boot
+
+ assert User
+ assert Tutorial
+
+ direct_descendants = [ZeitwerkDTIntegrationTestChild, User, Post].to_set
+ assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
+
+ descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild, Tutorial])
+ assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
+
+ ActiveSupport::DescendantsTracker.clear
+
+ direct_descendants = [ZeitwerkDTIntegrationTestChild].to_set
+ assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
+
+ descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild])
+ assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
+ ensure
+ Object.send(:remove_const, :ZeitwerkDTIntegrationTestRoot)
+ Object.send(:remove_const, :ZeitwerkDTIntegrationTestChild)
+ Object.send(:remove_const, :ZeitwerkDTIntegrationTestGrandchild)
+ end
+ end
+end
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index 70917ba20b..ec512b6b64 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -8,27 +8,19 @@ class BacktraceCleanerTest < ActiveSupport::TestCase
@cleaner = Rails::BacktraceCleaner.new
end
- test "should format installed gems correctly" do
- backtrace = [ "#{Gem.path[0]}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
- result = @cleaner.clean(backtrace, :all)
- assert_equal "nosuchgem (1.2.3) lib/foo.rb", result[0]
- end
-
- test "should format installed gems not in Gem.default_dir correctly" do
- target_dir = Gem.path.detect { |p| p != Gem.default_dir }
- # skip this test if default_dir is the only directory on Gem.path
- if target_dir
- backtrace = [ "#{target_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
- result = @cleaner.clean(backtrace, :all)
- assert_equal "nosuchgem (1.2.3) lib/foo.rb", result[0]
- end
+ test "should consider traces from irb lines as User code" do
+ backtrace = [ "(irb):1",
+ "/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'",
+ "bin/rails:4:in `<main>'" ]
+ result = @cleaner.clean(backtrace)
+ assert_equal "(irb):1", result[0]
+ assert_equal 1, result.length
end
- test "should consider traces from irb lines as User code" do
- backtrace = [ "from (irb):1",
- "from /Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'",
- "from bin/rails:4:in `<main>'" ]
+ test "should omit ActionView template methods names" do
+ method_name = ActionView::Template.new(nil, "app/views/application/index.html.erb", nil, locals: []).send :method_name
+ backtrace = [ "app/views/application/index.html.erb:4:in `block in #{method_name}'"]
result = @cleaner.clean(backtrace, :all)
- assert_equal "from (irb):1", result[0]
+ assert_equal "app/views/application/index.html.erb:4", result[0]
end
end
diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb
index 51917de2e0..e763cfb376 100644
--- a/railties/test/code_statistics_calculator_test.rb
+++ b/railties/test/code_statistics_calculator_test.rb
@@ -26,7 +26,7 @@ class CodeStatisticsCalculatorTest < ActiveSupport::TestCase
end
end
- test "count number of methods in Minitest file" do
+ test "count number of methods in minitest file" do
code = <<-RUBY
class FooTest < ActionController::TestCase
test 'expectation' do
diff --git a/railties/test/command/base_test.rb b/railties/test/command/base_test.rb
index a49ae8aae7..9132c8b4af 100644
--- a/railties/test/command/base_test.rb
+++ b/railties/test/command/base_test.rb
@@ -4,10 +4,12 @@ require "abstract_unit"
require "rails/command"
require "rails/commands/generate/generate_command"
require "rails/commands/secrets/secrets_command"
+require "rails/commands/db/system/change/change_command"
class Rails::Command::BaseTest < ActiveSupport::TestCase
test "printing commands" do
assert_equal %w(generate), Rails::Command::GenerateCommand.printing_commands
assert_equal %w(secrets:setup secrets:edit secrets:show), Rails::Command::SecretsCommand.printing_commands
+ assert_equal %w(db:system:change), Rails::Command::Db::System::ChangeCommand.printing_commands
end
end
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index b7cdb8229e..f6df2b694a 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -94,28 +94,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
assert_match(/\sspecial-production\s/, output)
end
- def test_rails_env_is_production_when_first_argument_is_p
- assert_deprecated do
- start ["p"]
- assert_match(/\sproduction\s/, output)
- end
- end
-
- def test_rails_env_is_test_when_first_argument_is_t
- assert_deprecated do
- start ["t"]
- assert_match(/\stest\s/, output)
- end
- end
-
- def test_rails_env_is_development_when_argument_is_d
- assert_deprecated do
- start ["d"]
- assert_match(/\sdevelopment\s/, output)
- end
- end
-
- def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
+ def test_rails_env_is_dev_when_environment_option_is_dev_and_dev_env_is_present
Rails::Command::ConsoleCommand.class_eval do
alias_method :old_environments, :available_environments
@@ -124,9 +103,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
end
end
- assert_deprecated do
- assert_match("dev", parse_arguments(["dev"])[:environment])
- end
+ assert_match("dev", parse_arguments(["-e", "dev"])[:environment])
ensure
Rails::Command::ConsoleCommand.class_eval do
undef_method :available_environments
@@ -151,7 +128,8 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
def build_app(console)
mocked_console = Class.new do
- attr_reader :sandbox, :console
+ attr_accessor :sandbox
+ attr_reader :console, :disable_sandbox
def initialize(console)
@console = console
@@ -161,10 +139,6 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
self
end
- def sandbox=(arg)
- @sandbox = arg
- end
-
def load_console
end
end
diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb
index fc16b8194f..0ee36081c0 100644
--- a/railties/test/commands/credentials_test.rb
+++ b/railties/test/commands/credentials_test.rb
@@ -15,7 +15,7 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
test "edit without editor gives hint" do
run_edit_command(editor: "").tap do |output|
assert_match "No $EDITOR to open file in", output
- assert_match "bin/rails credentials:edit", output
+ assert_match "rails credentials:edit", output
end
end
@@ -55,11 +55,44 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
end
end
+ test "edit command modifies file specified by environment option" do
+ assert_match(/access_key_id: 123/, run_edit_command(environment: "production"))
+ Dir.chdir(app_path) do
+ assert File.exist?("config/credentials/production.key")
+ assert File.exist?("config/credentials/production.yml.enc")
+ end
+ end
+
+ test "edit command properly expands environment option" do
+ assert_match(/access_key_id: 123/, run_edit_command(environment: "prod"))
+ Dir.chdir(app_path) do
+ assert File.exist?("config/credentials/production.key")
+ assert File.exist?("config/credentials/production.yml.enc")
+ end
+ end
+
+ test "edit command does not raise when an initializer tries to access non-existent credentials" do
+ app_file "config/initializers/raise_when_loaded.rb", <<-RUBY
+ Rails.application.credentials.missing_key!
+ RUBY
+
+ assert_match(/access_key_id: 123/, run_edit_command(environment: "qa"))
+ end
+
+ test "edit command generates template file when the file does not exist" do
+ FileUtils.rm("#{app_path}/config/credentials.yml.enc")
+ run_edit_command
+
+ output = run_show_command
+ assert_match(/access_key_id: 123/, output)
+ assert_match(/secret_key_base/, output)
+ end
+
test "show credentials" do
assert_match(/access_key_id: 123/, run_show_command)
end
- test "show command raise error when require_master_key is specified and key does not exist" do
+ test "show command raises error when require_master_key is specified and key does not exist" do
remove_file "config/master.key"
add_to_config "config.require_master_key = true"
@@ -70,17 +103,33 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
remove_file "config/master.key"
add_to_config "config.require_master_key = false"
- assert_match(/Missing master key to decrypt credentials/, run_show_command)
+ assert_match(/Missing 'config\/master\.key' to decrypt credentials/, run_show_command)
+ end
+
+ test "show command displays content specified by environment option" do
+ run_edit_command(environment: "production")
+
+ assert_match(/access_key_id: 123/, run_show_command(environment: "production"))
+ end
+
+ test "show command properly expands environment option" do
+ run_edit_command(environment: "production")
+
+ output = run_show_command(environment: "prod")
+ assert_match(/access_key_id: 123/, output)
+ assert_no_match(/secret_key_base/, output)
end
private
- def run_edit_command(editor: "cat")
+ def run_edit_command(editor: "cat", environment: nil, **options)
switch_env("EDITOR", editor) do
- rails "credentials:edit"
+ args = environment ? ["--environment", environment] : []
+ rails "credentials:edit", args, **options
end
end
- def run_show_command(**options)
- rails "credentials:show", **options
+ def run_show_command(environment: nil, **options)
+ args = environment ? ["--environment", environment] : []
+ rails "credentials:show", args, **options
end
end
diff --git a/railties/test/commands/db_system_change_test.rb b/railties/test/commands/db_system_change_test.rb
new file mode 100644
index 0000000000..2ff45a7878
--- /dev/null
+++ b/railties/test/commands/db_system_change_test.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "rails/command"
+require "rails/commands/db/system/change/change_command"
+
+class Rails::Command::Db::System::ChangeCommandTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup { build_app }
+
+ teardown { teardown_app }
+
+ test "change to existing database" do
+ change_database(to: "sqlite3")
+
+ output = change_database(to: "sqlite3")
+
+ assert_match "identical config/database.yml", output
+ assert_match "gsub Gemfile", output
+ end
+
+ test "change to invalid database" do
+ output = change_database(to: "invalid-db")
+
+ assert_match <<~MSG.squish, output
+ Invalid value for --to option.
+ Supported preconfigurations are:
+ mysql, postgresql, sqlite3, oracle, frontbase,
+ ibm_db, sqlserver, jdbcmysql, jdbcsqlite3,
+ jdbcpostgresql, jdbc.
+ MSG
+ end
+
+ test "change to postgresql" do
+ output = change_database(to: "postgresql")
+
+ assert_match "force config/database.yml", output
+ assert_match "gsub Gemfile", output
+ end
+
+ test "change to mysql" do
+ output = change_database(to: "mysql")
+
+ assert_match "force config/database.yml", output
+ assert_match "gsub Gemfile", output
+ end
+
+ test "change to sqlite3" do
+ change_database(to: "postgresql")
+ output = change_database(to: "sqlite3")
+
+ assert_match "force config/database.yml", output
+ assert_match "gsub Gemfile", output
+ end
+
+ private
+ def change_database(to:, **options)
+ args = ["--to", to]
+ rails "db:system:change", args, **options
+ end
+end
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 0aea21051a..76a7cd055f 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -99,28 +99,12 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
ENV["RACK_ENV"] = nil
end
- def test_rails_env_is_development_when_argument_is_dev
- assert_deprecated do
- stub_available_environments([ "development", "test" ]) do
- assert_match("development", parse_arguments([ "dev" ])[:environment])
- end
- end
- end
-
def test_rails_env_is_development_when_environment_option_is_dev
stub_available_environments([ "development", "test" ]) do
assert_match("development", parse_arguments([ "-e", "dev" ])[:environment])
end
end
- def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
- assert_deprecated do
- stub_available_environments([ "dev" ]) do
- assert_match("dev", parse_arguments([ "dev" ])[:environment])
- end
- end
- end
-
def test_mysql
start(adapter: "mysql2", database: "db")
assert_not aborted
@@ -128,9 +112,9 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
end
def test_mysql_full
- start(adapter: "mysql2", database: "db", host: "locahost", port: 1234, socket: "socket", username: "user", password: "qwerty", encoding: "UTF-8")
+ start(adapter: "mysql2", database: "db", host: "localhost", port: 1234, socket: "socket", username: "user", password: "qwerty", encoding: "UTF-8")
assert_not aborted
- assert_equal [%w[mysql mysql5], "--host=locahost", "--port=1234", "--socket=socket", "--user=user", "--default-character-set=UTF-8", "-p", "db"], dbconsole.find_cmd_and_exec_args
+ assert_equal [%w[mysql mysql5], "--host=localhost", "--port=1234", "--socket=socket", "--user=user", "--default-character-set=UTF-8", "-p", "db"], dbconsole.find_cmd_and_exec_args
end
def test_mysql_include_password
@@ -232,22 +216,22 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
end
end
- def test_specifying_a_custom_connection_and_environment
+ def test_specifying_a_custom_database_and_environment
stub_available_environments(["development"]) do
- dbconsole = parse_arguments(["-c", "custom", "-e", "development"])
+ dbconsole = parse_arguments(["--db", "custom", "-e", "development"])
assert_equal "development", dbconsole[:environment]
- assert_equal "custom", dbconsole.connection
+ assert_equal "custom", dbconsole.database
end
end
- def test_specifying_a_missing_connection
+ def test_specifying_a_missing_database
app_db_config({}) do
e = assert_raises(ActiveRecord::AdapterNotSpecified) do
- Rails::Command.invoke(:dbconsole, ["-c", "i_do_not_exist"])
+ Rails::Command.invoke(:dbconsole, ["--db", "i_do_not_exist"])
end
- assert_includes e.message, "'i_do_not_exist' connection is not configured."
+ assert_includes e.message, "'i_do_not_exist' database is not configured."
end
end
@@ -261,18 +245,30 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
end
end
+ def test_connection_options_is_deprecate
+ command = Rails::Command::DbconsoleCommand.new([], ["-c", "custom"])
+ Rails::DBConsole.stub(:start, nil) do
+ assert_deprecated("`connection` option is deprecated") do
+ command.perform
+ end
+ end
+
+ assert_equal "custom", command.options["connection"]
+ assert_equal "custom", command.options["database"]
+ end
+
def test_print_help_short
stdout = capture(:stdout) do
Rails::Command.invoke(:dbconsole, ["-h"])
end
- assert_match(/bin\/rails dbconsole \[environment\]/, stdout)
+ assert_match(/rails dbconsole \[options\]/, stdout)
end
def test_print_help_long
stdout = capture(:stdout) do
Rails::Command.invoke(:dbconsole, ["--help"])
end
- assert_match(/bin\/rails dbconsole \[environment\]/, stdout)
+ assert_match(/rails dbconsole \[options\]/, stdout)
end
attr_reader :aborted, :output
@@ -308,11 +304,9 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def capture_abort
@aborted = false
@output = capture(:stderr) do
- begin
- yield
- rescue SystemExit
- @aborted = true
- end
+ yield
+ rescue SystemExit
+ @aborted = true
end
end
diff --git a/railties/test/commands/dev_test.rb b/railties/test/commands/dev_test.rb
new file mode 100644
index 0000000000..ae8516fe9a
--- /dev/null
+++ b/railties/test/commands/dev_test.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "rails/command"
+
+class Rails::Command::DevTest < ActiveSupport::TestCase
+ setup :build_app
+ teardown :teardown_app
+
+ test "`rails dev:cache` creates both caching and restart file when restart file doesn't exist and dev caching is currently off" do
+ Dir.chdir(app_path) do
+ assert_not File.exist?("tmp/caching-dev.txt")
+ assert_not File.exist?("tmp/restart.txt")
+
+ assert_equal <<~OUTPUT, run_dev_cache_command
+ Development mode is now being cached.
+ OUTPUT
+
+ assert File.exist?("tmp/caching-dev.txt")
+ assert File.exist?("tmp/restart.txt")
+ end
+ end
+
+ test "`rails dev:cache` creates caching file and touches restart file when dev caching is currently off" do
+ Dir.chdir(app_path) do
+ app_file("tmp/restart.txt", "")
+
+ assert_not File.exist?("tmp/caching-dev.txt")
+ assert File.exist?("tmp/restart.txt")
+ restart_file_time_before = File.mtime("tmp/restart.txt")
+
+ assert_equal <<~OUTPUT, run_dev_cache_command
+ Development mode is now being cached.
+ OUTPUT
+
+ assert File.exist?("tmp/caching-dev.txt")
+ restart_file_time_after = File.mtime("tmp/restart.txt")
+ assert_operator restart_file_time_before, :<, restart_file_time_after
+ end
+ end
+
+ test "`rails dev:cache` removes caching file and touches restart file when dev caching is currently on" do
+ Dir.chdir(app_path) do
+ app_file("tmp/caching-dev.txt", "")
+ app_file("tmp/restart.txt", "")
+
+ assert File.exist?("tmp/caching-dev.txt")
+ assert File.exist?("tmp/restart.txt")
+ restart_file_time_before = File.mtime("tmp/restart.txt")
+
+ assert_equal <<~OUTPUT, run_dev_cache_command
+ Development mode is no longer being cached.
+ OUTPUT
+
+ assert_not File.exist?("tmp/caching-dev.txt")
+ restart_file_time_after = File.mtime("tmp/restart.txt")
+ assert_operator restart_file_time_before, :<, restart_file_time_after
+ end
+ end
+
+ private
+ def run_dev_cache_command
+ rails "dev:cache"
+ end
+end
diff --git a/railties/test/commands/encrypted_test.rb b/railties/test/commands/encrypted_test.rb
index 9fc73d5f18..8b608fe8c0 100644
--- a/railties/test/commands/encrypted_test.rb
+++ b/railties/test/commands/encrypted_test.rb
@@ -14,7 +14,7 @@ class Rails::Command::EncryptedCommandTest < ActiveSupport::TestCase
test "edit without editor gives hint" do
run_edit_command("config/tokens.yml.enc", editor: "").tap do |output|
assert_match "No $EDITOR to open file in", output
- assert_match "bin/rails encrypted:edit", output
+ assert_match "rails encrypted:edit", output
end
end
diff --git a/railties/test/commands/initializers_test.rb b/railties/test/commands/initializers_test.rb
new file mode 100644
index 0000000000..793365ef3d
--- /dev/null
+++ b/railties/test/commands/initializers_test.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "rails/command"
+
+class Rails::Command::InitializersTest < ActiveSupport::TestCase
+ setup :build_app
+ teardown :teardown_app
+
+ test "`rails initializers` prints out defined initializers invoked by Rails" do
+ initial_output = run_initializers_command
+ initial_output_length = initial_output.split("\n").length
+
+ assert_operator initial_output_length, :>, 0
+ assert_not initial_output.include?("set_added_test_module")
+
+ add_to_config <<-RUBY
+ initializer(:set_added_test_module) { }
+ RUBY
+
+ final_output = run_initializers_command
+ final_output_length = final_output.split("\n").length
+
+ assert_equal 1, (final_output_length - initial_output_length)
+ assert final_output.include?("set_added_test_module")
+ end
+
+
+ test "prints out initializers only specified in environment option" do
+ add_to_config <<-RUBY
+ initializer(:set_added_development_module) { } if Rails.env.development?
+ initializer(:set_added_production_module) { } if Rails.env.production?
+ RUBY
+
+ output = run_initializers_command.split("\n")
+ assert_includes output, "AppTemplate::Application.set_added_development_module"
+ assert_not_includes output, "AppTemplate::Application.set_added_production_module"
+
+ output = run_initializers_command(["-e", "production"]).split("\n")
+ assert_not_includes output, "AppTemplate::Application.set_added_development_module"
+ assert_includes output, "AppTemplate::Application.set_added_production_module"
+ end
+
+ private
+ def run_initializers_command(args = [])
+ rails "initializers", args
+ end
+end
diff --git a/railties/test/commands/routes_test.rb b/railties/test/commands/routes_test.rb
index 77ed2bda61..b4f927060e 100644
--- a/railties/test/commands/routes_test.rb
+++ b/railties/test/commands/routes_test.rb
@@ -13,20 +13,32 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
resource :post
+ resource :user_permission
end
RUBY
- expected_output = [" Prefix Verb URI Pattern Controller#Action",
- " new_post GET /post/new(.:format) posts#new",
- "edit_post GET /post/edit(.:format) posts#edit",
- " post GET /post(.:format) posts#show",
- " PATCH /post(.:format) posts#update",
- " PUT /post(.:format) posts#update",
- " DELETE /post(.:format) posts#destroy",
- " POST /post(.:format) posts#create\n"].join("\n")
-
- output = run_routes_command(["-c", "PostController"])
- assert_equal expected_output, output
+ assert_equal <<~OUTPUT, run_routes_command([ "-c", "PostController" ])
+ Prefix Verb URI Pattern Controller#Action
+ new_post GET /post/new(.:format) posts#new
+ edit_post GET /post/edit(.:format) posts#edit
+ post GET /post(.:format) posts#show
+ PATCH /post(.:format) posts#update
+ PUT /post(.:format) posts#update
+ DELETE /post(.:format) posts#destroy
+ POST /post(.:format) posts#create
+ rails_postmark_inbound_emails POST /rails/action_mailbox/postmark/inbound_emails(.:format) action_mailbox/ingresses/postmark/inbound_emails#create
+ OUTPUT
+
+ assert_equal <<~OUTPUT, run_routes_command([ "-c", "UserPermissionController" ])
+ Prefix Verb URI Pattern Controller#Action
+ new_user_permission GET /user_permission/new(.:format) user_permissions#new
+ edit_user_permission GET /user_permission/edit(.:format) user_permissions#edit
+ user_permission GET /user_permission(.:format) user_permissions#show
+ PATCH /user_permission(.:format) user_permissions#update
+ PUT /user_permission(.:format) user_permissions#update
+ DELETE /user_permission(.:format) user_permissions#destroy
+ POST /user_permission(.:format) user_permissions#create
+ OUTPUT
end
test "rails routes with global search key" do
@@ -38,25 +50,33 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
end
RUBY
- output = run_routes_command(["-g", "show"])
- assert_equal <<~MESSAGE, output
- Prefix Verb URI Pattern Controller#Action
- cart GET /cart(.:format) cart#show
- rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
- rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
- rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ assert_equal <<~MESSAGE, run_routes_command([ "-g", "show" ])
+ Prefix Verb URI Pattern Controller#Action
+ cart GET /cart(.:format) cart#show
+ rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#show
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
MESSAGE
- output = run_routes_command(["-g", "POST"])
- assert_equal <<~MESSAGE, output
- Prefix Verb URI Pattern Controller#Action
- POST /cart(.:format) cart#create
- rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ assert_equal <<~MESSAGE, run_routes_command([ "-g", "POST" ])
+ Prefix Verb URI Pattern Controller#Action
+ POST /cart(.:format) cart#create
+ rails_amazon_inbound_emails POST /rails/action_mailbox/amazon/inbound_emails(.:format) action_mailbox/ingresses/amazon/inbound_emails#create
+ rails_mandrill_inbound_emails POST /rails/action_mailbox/mandrill/inbound_emails(.:format) action_mailbox/ingresses/mandrill/inbound_emails#create
+ rails_postmark_inbound_emails POST /rails/action_mailbox/postmark/inbound_emails(.:format) action_mailbox/ingresses/postmark/inbound_emails#create
+ rails_relay_inbound_emails POST /rails/action_mailbox/relay/inbound_emails(.:format) action_mailbox/ingresses/relay/inbound_emails#create
+ rails_sendgrid_inbound_emails POST /rails/action_mailbox/sendgrid/inbound_emails(.:format) action_mailbox/ingresses/sendgrid/inbound_emails#create
+ rails_mailgun_inbound_emails POST /rails/action_mailbox/mailgun/inbound_emails/mime(.:format) action_mailbox/ingresses/mailgun/inbound_emails#create
+ POST /rails/conductor/action_mailbox/inbound_emails(.:format) rails/conductor/action_mailbox/inbound_emails#create
+ rails_conductor_inbound_email_reroute POST /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format) rails/conductor/action_mailbox/reroutes#create
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
MESSAGE
- output = run_routes_command(["-g", "basketballs"])
- assert_equal " Prefix Verb URI Pattern Controller#Action\n" \
- "basketballs GET /basketballs(.:format) basketball#index\n", output
+ assert_equal <<~MESSAGE, run_routes_command([ "-g", "basketballs" ])
+ Prefix Verb URI Pattern Controller#Action
+ basketballs GET /basketballs(.:format) basketball#index
+ MESSAGE
end
test "rails routes with controller search key" do
@@ -64,17 +84,30 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
Rails.application.routes.draw do
get '/cart', to: 'cart#show'
get '/basketball', to: 'basketball#index'
+ get '/user_permission', to: 'user_permission#index'
end
RUBY
+ expected_cart_output = "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n"
output = run_routes_command(["-c", "cart"])
- assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ assert_equal expected_cart_output, output
output = run_routes_command(["-c", "Cart"])
- assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ assert_equal expected_cart_output, output
output = run_routes_command(["-c", "CartController"])
- assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ assert_equal expected_cart_output, output
+
+ expected_perm_output = [" Prefix Verb URI Pattern Controller#Action",
+ "user_permission GET /user_permission(.:format) user_permission#index\n"].join("\n")
+ output = run_routes_command(["-c", "user_permission"])
+ assert_equal expected_perm_output, output
+
+ output = run_routes_command(["-c", "UserPermission"])
+ assert_equal expected_perm_output, output
+
+ output = run_routes_command(["-c", "UserPermissionController"])
+ assert_equal expected_perm_output, output
end
test "rails routes with namespaced controller search key" do
@@ -82,24 +115,47 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
Rails.application.routes.draw do
namespace :admin do
resource :post
+ resource :user_permission
end
end
RUBY
- expected_output = [" Prefix Verb URI Pattern Controller#Action",
- " new_admin_post GET /admin/post/new(.:format) admin/posts#new",
- "edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit",
- " admin_post GET /admin/post(.:format) admin/posts#show",
- " PATCH /admin/post(.:format) admin/posts#update",
- " PUT /admin/post(.:format) admin/posts#update",
- " DELETE /admin/post(.:format) admin/posts#destroy",
- " POST /admin/post(.:format) admin/posts#create\n"].join("\n")
+ assert_equal <<~OUTPUT, run_routes_command([ "-c", "Admin::PostController" ])
+ Prefix Verb URI Pattern Controller#Action
+ new_admin_post GET /admin/post/new(.:format) admin/posts#new
+ edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit
+ admin_post GET /admin/post(.:format) admin/posts#show
+ PATCH /admin/post(.:format) admin/posts#update
+ PUT /admin/post(.:format) admin/posts#update
+ DELETE /admin/post(.:format) admin/posts#destroy
+ POST /admin/post(.:format) admin/posts#create
+ OUTPUT
+
+ assert_equal <<~OUTPUT, run_routes_command([ "-c", "PostController" ])
+ Prefix Verb URI Pattern Controller#Action
+ new_admin_post GET /admin/post/new(.:format) admin/posts#new
+ edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit
+ admin_post GET /admin/post(.:format) admin/posts#show
+ PATCH /admin/post(.:format) admin/posts#update
+ PUT /admin/post(.:format) admin/posts#update
+ DELETE /admin/post(.:format) admin/posts#destroy
+ POST /admin/post(.:format) admin/posts#create
+ rails_postmark_inbound_emails POST /rails/action_mailbox/postmark/inbound_emails(.:format) action_mailbox/ingresses/postmark/inbound_emails#create
+ OUTPUT
- output = run_routes_command(["-c", "Admin::PostController"])
- assert_equal expected_output, output
+ expected_permission_output = <<~OUTPUT
+ Prefix Verb URI Pattern Controller#Action
+ new_admin_user_permission GET /admin/user_permission/new(.:format) admin/user_permissions#new
+ edit_admin_user_permission GET /admin/user_permission/edit(.:format) admin/user_permissions#edit
+ admin_user_permission GET /admin/user_permission(.:format) admin/user_permissions#show
+ PATCH /admin/user_permission(.:format) admin/user_permissions#update
+ PUT /admin/user_permission(.:format) admin/user_permissions#update
+ DELETE /admin/user_permission(.:format) admin/user_permissions#destroy
+ POST /admin/user_permission(.:format) admin/user_permissions#create
+ OUTPUT
- output = run_routes_command(["-c", "PostController"])
- assert_equal expected_output, output
+ assert_equal expected_permission_output, run_routes_command([ "-c", "Admin::UserPermissionController" ])
+ assert_equal expected_permission_output, run_routes_command([ "-c", "UserPermissionController" ])
end
test "rails routes displays message when no routes are defined" do
@@ -109,63 +165,151 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
RUBY
assert_equal <<~MESSAGE, run_routes_command
- Prefix Verb URI Pattern Controller#Action
- rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
- rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
- rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
- update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
- rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ Prefix Verb URI Pattern Controller#Action
+ rails_amazon_inbound_emails POST /rails/action_mailbox/amazon/inbound_emails(.:format) action_mailbox/ingresses/amazon/inbound_emails#create
+ rails_mandrill_inbound_emails POST /rails/action_mailbox/mandrill/inbound_emails(.:format) action_mailbox/ingresses/mandrill/inbound_emails#create
+ rails_postmark_inbound_emails POST /rails/action_mailbox/postmark/inbound_emails(.:format) action_mailbox/ingresses/postmark/inbound_emails#create
+ rails_relay_inbound_emails POST /rails/action_mailbox/relay/inbound_emails(.:format) action_mailbox/ingresses/relay/inbound_emails#create
+ rails_sendgrid_inbound_emails POST /rails/action_mailbox/sendgrid/inbound_emails(.:format) action_mailbox/ingresses/sendgrid/inbound_emails#create
+ rails_mailgun_inbound_emails POST /rails/action_mailbox/mailgun/inbound_emails/mime(.:format) action_mailbox/ingresses/mailgun/inbound_emails#create
+ rails_conductor_inbound_emails GET /rails/conductor/action_mailbox/inbound_emails(.:format) rails/conductor/action_mailbox/inbound_emails#index
+ POST /rails/conductor/action_mailbox/inbound_emails(.:format) rails/conductor/action_mailbox/inbound_emails#create
+ new_rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/new(.:format) rails/conductor/action_mailbox/inbound_emails#new
+ edit_rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format) rails/conductor/action_mailbox/inbound_emails#edit
+ rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#show
+ PATCH /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#update
+ PUT /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#update
+ DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#destroy
+ rails_conductor_inbound_email_reroute POST /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format) rails/conductor/action_mailbox/reroutes#create
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
MESSAGE
end
test "rails routes with expanded option" do
- begin
- previous_console_winsize = IO.console.winsize
- IO.console.winsize = [0, 27]
+ previous_console_winsize = IO.console.winsize
+ IO.console.winsize = [0, 27]
- app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- get '/cart', to: 'cart#show'
- end
- RUBY
-
- output = run_routes_command(["--expanded"])
-
- assert_equal <<~MESSAGE, output
- --[ Route 1 ]--------------
- Prefix | cart
- Verb | GET
- URI | /cart(.:format)
- Controller#Action | cart#show
- --[ Route 2 ]--------------
- Prefix | rails_service_blob
- Verb | GET
- URI | /rails/active_storage/blobs/:signed_id/*filename(.:format)
- Controller#Action | active_storage/blobs#show
- --[ Route 3 ]--------------
- Prefix | rails_blob_representation
- Verb | GET
- URI | /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format)
- Controller#Action | active_storage/representations#show
- --[ Route 4 ]--------------
- Prefix | rails_disk_service
- Verb | GET
- URI | /rails/active_storage/disk/:encoded_key/*filename(.:format)
- Controller#Action | active_storage/disk#show
- --[ Route 5 ]--------------
- Prefix | update_rails_disk_service
- Verb | PUT
- URI | /rails/active_storage/disk/:encoded_token(.:format)
- Controller#Action | active_storage/disk#update
- --[ Route 6 ]--------------
- Prefix | rails_direct_uploads
- Verb | POST
- URI | /rails/active_storage/direct_uploads(.:format)
- Controller#Action | active_storage/direct_uploads#create
- MESSAGE
- ensure
- IO.console.winsize = previous_console_winsize
- end
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ end
+ RUBY
+
+ # rubocop:disable Layout/TrailingWhitespace
+ assert_equal <<~MESSAGE, run_routes_command([ "--expanded" ])
+ --[ Route 1 ]--------------
+ Prefix | cart
+ Verb | GET
+ URI | /cart(.:format)
+ Controller#Action | cart#show
+ --[ Route 2 ]--------------
+ Prefix | rails_amazon_inbound_emails
+ Verb | POST
+ URI | /rails/action_mailbox/amazon/inbound_emails(.:format)
+ Controller#Action | action_mailbox/ingresses/amazon/inbound_emails#create
+ --[ Route 3 ]--------------
+ Prefix | rails_mandrill_inbound_emails
+ Verb | POST
+ URI | /rails/action_mailbox/mandrill/inbound_emails(.:format)
+ Controller#Action | action_mailbox/ingresses/mandrill/inbound_emails#create
+ --[ Route 4 ]--------------
+ Prefix | rails_postmark_inbound_emails
+ Verb | POST
+ URI | /rails/action_mailbox/postmark/inbound_emails(.:format)
+ Controller#Action | action_mailbox/ingresses/postmark/inbound_emails#create
+ --[ Route 5 ]--------------
+ Prefix | rails_relay_inbound_emails
+ Verb | POST
+ URI | /rails/action_mailbox/relay/inbound_emails(.:format)
+ Controller#Action | action_mailbox/ingresses/relay/inbound_emails#create
+ --[ Route 6 ]--------------
+ Prefix | rails_sendgrid_inbound_emails
+ Verb | POST
+ URI | /rails/action_mailbox/sendgrid/inbound_emails(.:format)
+ Controller#Action | action_mailbox/ingresses/sendgrid/inbound_emails#create
+ --[ Route 7 ]--------------
+ Prefix | rails_mailgun_inbound_emails
+ Verb | POST
+ URI | /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)
+ Controller#Action | action_mailbox/ingresses/mailgun/inbound_emails#create
+ --[ Route 8 ]--------------
+ Prefix | rails_conductor_inbound_emails
+ Verb | GET
+ URI | /rails/conductor/action_mailbox/inbound_emails(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#index
+ --[ Route 9 ]--------------
+ Prefix |
+ Verb | POST
+ URI | /rails/conductor/action_mailbox/inbound_emails(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#create
+ --[ Route 10 ]-------------
+ Prefix | new_rails_conductor_inbound_email
+ Verb | GET
+ URI | /rails/conductor/action_mailbox/inbound_emails/new(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#new
+ --[ Route 11 ]-------------
+ Prefix | edit_rails_conductor_inbound_email
+ Verb | GET
+ URI | /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#edit
+ --[ Route 12 ]-------------
+ Prefix | rails_conductor_inbound_email
+ Verb | GET
+ URI | /rails/conductor/action_mailbox/inbound_emails/:id(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#show
+ --[ Route 13 ]-------------
+ Prefix |
+ Verb | PATCH
+ URI | /rails/conductor/action_mailbox/inbound_emails/:id(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#update
+ --[ Route 14 ]-------------
+ Prefix |
+ Verb | PUT
+ URI | /rails/conductor/action_mailbox/inbound_emails/:id(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#update
+ --[ Route 15 ]-------------
+ Prefix |
+ Verb | DELETE
+ URI | /rails/conductor/action_mailbox/inbound_emails/:id(.:format)
+ Controller#Action | rails/conductor/action_mailbox/inbound_emails#destroy
+ --[ Route 16 ]-------------
+ Prefix | rails_conductor_inbound_email_reroute
+ Verb | POST
+ URI | /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)
+ Controller#Action | rails/conductor/action_mailbox/reroutes#create
+ --[ Route 17 ]-------------
+ Prefix | rails_service_blob
+ Verb | GET
+ URI | /rails/active_storage/blobs/:signed_id/*filename(.:format)
+ Controller#Action | active_storage/blobs#show
+ --[ Route 18 ]-------------
+ Prefix | rails_blob_representation
+ Verb | GET
+ URI | /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format)
+ Controller#Action | active_storage/representations#show
+ --[ Route 19 ]-------------
+ Prefix | rails_disk_service
+ Verb | GET
+ URI | /rails/active_storage/disk/:encoded_key/*filename(.:format)
+ Controller#Action | active_storage/disk#show
+ --[ Route 20 ]-------------
+ Prefix | update_rails_disk_service
+ Verb | PUT
+ URI | /rails/active_storage/disk/:encoded_token(.:format)
+ Controller#Action | active_storage/disk#update
+ --[ Route 21 ]-------------
+ Prefix | rails_direct_uploads
+ Verb | POST
+ URI | /rails/active_storage/direct_uploads(.:format)
+ Controller#Action | active_storage/direct_uploads#create
+ MESSAGE
+ # rubocop:enable Layout/TrailingWhitespace
+ ensure
+ IO.console.winsize = previous_console_winsize
end
private
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index e5b1da6ea4..b78370a233 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -22,6 +22,12 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase
assert_nil options[:server]
end
+ def test_environment_option_is_properly_expanded
+ args = ["-e", "prod"]
+ options = parse_arguments(args)
+ assert_equal "production", options[:environment]
+ end
+
def test_explicit_using_option
args = ["-u", "thin"]
options = parse_arguments(args)
@@ -32,6 +38,12 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase
assert_match(/Could not find server "tin". Maybe you meant "thin"?/, run_command("--using", "tin"))
end
+ def test_using_server_mistype_without_suggestion
+ output = run_command("--using", "t")
+ assert_match(/Could not find server "t"/, output)
+ assert_no_match(/Maybe you meant/, output)
+ end
+
def test_using_positional_argument_deprecation
assert_match(/DEPRECATION WARNING/, run_command("tin"))
end
@@ -225,10 +237,10 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase
end
def test_records_user_supplied_options
- server_options = parse_arguments(["-p", 3001])
+ server_options = parse_arguments(["-p", "3001"])
assert_equal [:Port], server_options[:user_supplied_options]
- server_options = parse_arguments(["--port", 3001])
+ server_options = parse_arguments(["--port", "3001"])
assert_equal [:Port], server_options[:user_supplied_options]
server_options = parse_arguments(["-p3001", "-C", "--binding", "127.0.0.1"])
@@ -279,6 +291,8 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase
end
def parse_arguments(args = [])
- Rails::Command::ServerCommand.new([], args).server_options
+ command = Rails::Command::ServerCommand.new([], args)
+ command.send(:extract_environment_option_from_argument)
+ command.server_options
end
end
diff --git a/railties/test/console_helpers.rb b/railties/test/console_helpers.rb
index 8350fce5ee..67f55fdc45 100644
--- a/railties/test/console_helpers.rb
+++ b/railties/test/console_helpers.rb
@@ -9,7 +9,7 @@ module ConsoleHelpers
def assert_output(expected, io, timeout = 10)
timeout = Time.now + timeout
- output = "".dup
+ output = +""
until output.include?(expected) || Time.now > timeout
if IO.select([io], [], [], 0.1)
output << io.read(1)
diff --git a/railties/test/engine/commands_test.rb b/railties/test/engine/commands_test.rb
index aeb64d445b..0e5167578d 100644
--- a/railties/test/engine/commands_test.rb
+++ b/railties/test/engine/commands_test.rb
@@ -24,7 +24,7 @@ class Rails::Engine::CommandsTest < ActiveSupport::TestCase
def test_runner_command_work_inside_engine
output = capture(:stdout) do
- Dir.chdir(plugin_path) { system("bin/rails runner 'puts Rails.env'") }
+ Dir.chdir(plugin_path) { system({ "SKIP_REQUIRE_WEBPACKER" => "true" }, "bin/rails runner 'puts Rails.env'") }
end
assert_equal "test", output.strip
@@ -33,29 +33,29 @@ class Rails::Engine::CommandsTest < ActiveSupport::TestCase
def test_console_command_work_inside_engine
skip "PTY unavailable" unless available_pty?
- master, slave = PTY.open
- spawn_command("console", slave)
- assert_output(">", master)
+ primary, replica = PTY.open
+ spawn_command("console", replica)
+ assert_output(">", primary)
ensure
- master.puts "quit"
+ primary.puts "quit"
end
def test_dbconsole_command_work_inside_engine
skip "PTY unavailable" unless available_pty?
- master, slave = PTY.open
- spawn_command("dbconsole", slave)
- assert_output("sqlite>", master)
+ primary, replica = PTY.open
+ spawn_command("dbconsole", replica)
+ assert_output("sqlite>", primary)
ensure
- master.puts ".exit"
+ primary.puts ".exit"
end
def test_server_command_work_inside_engine
skip "PTY unavailable" unless available_pty?
- master, slave = PTY.open
- pid = spawn_command("server", slave)
- assert_output("Listening on", master)
+ primary, replica = PTY.open
+ pid = spawn_command("server", replica)
+ assert_output("Listening on", primary)
ensure
kill(pid)
end
@@ -67,6 +67,7 @@ class Rails::Engine::CommandsTest < ActiveSupport::TestCase
def spawn_command(command, fd)
Process.spawn(
+ { "SKIP_REQUIRE_WEBPACKER" => "true" },
"#{plugin_path}/bin/rails #{command}",
in: fd, out: fd, err: fd
)
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index a54a6dbc28..44d4e92256 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -125,7 +125,7 @@ class ActionsTest < Rails::Generators::TestCase
def test_gem_works_even_if_frozen_string_is_passed_as_argument
run_generator
- action :gem, "frozen_gem".freeze, "1.0.0".freeze
+ action :gem, -"frozen_gem", -"1.0.0"
assert_file "Gemfile", /^gem 'frozen_gem', '1.0.0'$/
end
@@ -144,6 +144,44 @@ class ActionsTest < Rails::Generators::TestCase
assert_file "Gemfile", /\ngroup :development, :test do\n gem 'rspec-rails'\nend\n\ngroup :test do\n gem 'fakeweb'\nend/
end
+ def test_github_should_create_an_indented_block
+ run_generator
+
+ action :github, "user/repo" do
+ gem "foo"
+ gem "bar"
+ gem "baz"
+ end
+
+ assert_file "Gemfile", /\ngithub 'user\/repo' do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend/
+ end
+
+ def test_github_should_create_an_indented_block_with_options
+ run_generator
+
+ action :github, "user/repo", a: "correct", other: true do
+ gem "foo"
+ gem "bar"
+ gem "baz"
+ end
+
+ assert_file "Gemfile", /\ngithub 'user\/repo', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend/
+ end
+
+ def test_github_should_create_an_indented_block_within_a_group
+ run_generator
+
+ action :gem_group, :magic do
+ github "user/repo", a: "correct", other: true do
+ gem "foo"
+ gem "bar"
+ gem "baz"
+ end
+ end
+
+ assert_file "Gemfile", /\ngroup :magic do\n github 'user\/repo', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\n end\nend\n/
+ end
+
def test_environment_should_include_data_in_environment_initializer_block
run_generator
autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
@@ -265,12 +303,30 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_generate_should_run_script_generate_with_argument_and_options
- assert_called_with(generator, :run_ruby_script, ["bin/rails generate model MyModel", verbose: false]) do
- action :generate, "model", "MyModel"
+ run_generator
+ action :generate, "model", "MyModel"
+ assert_file "app/models/my_model.rb", /MyModel/
+ end
+
+ def test_generate_aborts_when_subprocess_fails_if_requested
+ run_generator
+ content = capture(:stderr) do
+ assert_raises SystemExit do
+ action :generate, "model", "MyModel:ADsad", abort_on_failure: true
+ action :generate, "model", "MyModel"
+ end
end
+ assert_match(/wrong constant name MyModel:aDsad/, content)
+ assert_no_file "app/models/my_model.rb"
end
- def test_rails_should_run_rake_command_with_default_env
+ def test_generate_should_run_command_without_env
+ assert_called_with(generator, :run, ["rails generate model MyModel name:string", verbose: false]) do
+ action :generate, "model", "MyModel", "name:string"
+ end
+ end
+
+ def test_rake_should_run_rake_command_with_default_env
assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=development", verbose: false]) do
with_rails_env nil do
action :rake, "log:clear"
@@ -278,13 +334,13 @@ class ActionsTest < Rails::Generators::TestCase
end
end
- def test_rails_with_env_option_should_run_rake_command_in_env
+ def test_rake_with_env_option_should_run_rake_command_in_env
assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=production", verbose: false]) do
action :rake, "log:clear", env: "production"
end
end
- test "rails command with RAILS_ENV variable should run rake command in env" do
+ test "rake with RAILS_ENV variable should run rake command in env" do
assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=production", verbose: false]) do
with_rails_env "production" do
action :rake, "log:clear"
@@ -300,7 +356,7 @@ class ActionsTest < Rails::Generators::TestCase
end
end
- test "rails command with sudo option should run rake command with sudo" do
+ test "rake with sudo option should run rake command with sudo" do
assert_called_with(generator, :run, ["sudo rake log:clear RAILS_ENV=development", verbose: false]) do
with_rails_env nil do
action :rake, "log:clear", sudo: true
@@ -362,15 +418,6 @@ class ActionsTest < Rails::Generators::TestCase
end
end
- def test_capify_should_run_the_capify_command
- content = capture(:stderr) do
- assert_called_with(generator, :run, ["capify .", verbose: false]) do
- action :capify!
- end
- end
- assert_match(/DEPRECATION WARNING: `capify!` is deprecated/, content)
- end
-
def test_route_should_add_data_to_the_routes_block_in_config_routes
run_generator
route_command = "route '/login', controller: 'sessions', action: 'new'"
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index 9c523ad372..4b9878187b 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -14,9 +14,9 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
super
Kernel.silence_warnings do
- Thor::Base.shell.send(:attr_accessor, :always_force)
+ Thor::Base.shell.attr_accessor :always_force
@shell = Thor::Base.shell.new
- @shell.send(:always_force=, true)
+ @shell.always_force = true
end
end
@@ -40,7 +40,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
end
assert_file "Gemfile" do |content|
- assert_no_match(/gem 'coffee-rails'/, content)
assert_no_match(/gem 'sass-rails'/, content)
assert_no_match(/gem 'web-console'/, content)
assert_no_match(/gem 'capybara'/, content)
@@ -118,7 +117,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
app/views/layouts
app/views/layouts/mailer.html.erb
app/views/layouts/mailer.text.erb
- bin/bundle
bin/rails
bin/rake
bin/setup
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index b0f958091c..3bdbc70990 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -13,10 +13,11 @@ DEFAULT_APP_FILES = %w(
config.ru
app/assets/config/manifest.js
app/assets/images
- app/assets/javascripts
- app/assets/javascripts/application.js
- app/assets/javascripts/cable.js
- app/assets/javascripts/channels
+ app/javascript
+ app/javascript/channels
+ app/javascript/channels/consumer.js
+ app/javascript/channels/index.js
+ app/javascript/packs/application.js
app/assets/stylesheets
app/assets/stylesheets/application.css
app/channels/application_cable/channel.rb
@@ -37,7 +38,6 @@ DEFAULT_APP_FILES = %w(
app/views/layouts/application.html.erb
app/views/layouts/mailer.html.erb
app/views/layouts/mailer.text.erb
- bin/bundle
bin/rails
bin/rake
bin/setup
@@ -81,6 +81,7 @@ DEFAULT_APP_FILES = %w(
test/test_helper.rb
test/fixtures
test/fixtures/files
+ test/channels/application_cable/connection_test.rb
test/controllers
test/models
test/helpers
@@ -106,7 +107,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_skip_bundle
- assert_not_called(generator([destination_root], skip_bundle: true), :bundle_command) do
+ assert_not_called(generator([destination_root], skip_bundle: true, skip_webpack_install: true), :bundle_command) do
quietly { generator.invoke_all }
# skip_bundle is only about running bundle install, ensure the Gemfile is still
# generated.
@@ -118,9 +119,9 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator
assert_file("app/views/layouts/application.html.erb", /stylesheet_link_tag\s+'application', media: 'all', 'data-turbolinks-track': 'reload'/)
- assert_file("app/views/layouts/application.html.erb", /javascript_include_tag\s+'application', 'data-turbolinks-track': 'reload'/)
+ assert_file("app/views/layouts/application.html.erb", /javascript_pack_tag\s+'application', 'data-turbolinks-track': 'reload'/)
assert_file("app/assets/stylesheets/application.css")
- assert_file("app/assets/javascripts/application.js")
+ assert_file("app/javascript/packs/application.js")
end
def test_application_job_file_present
@@ -198,7 +199,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_new_application_use_json_serialzier
+ def test_new_application_use_json_serializer
run_generator
assert_file("config/initializers/cookies_serializer.rb", /Rails\.application\.config\.action_dispatch\.cookies_serializer = :json/)
@@ -211,23 +212,33 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_new_application_doesnt_need_defaults
+ run_generator
assert_no_file "config/initializers/new_framework_defaults_6_0.rb"
end
def test_new_application_load_defaults
app_root = File.join(destination_root, "myfirstapp")
run_generator [app_root]
+
output = nil
assert_file "#{app_root}/config/application.rb", /\s+config\.load_defaults #{Rails::VERSION::STRING.to_f}/
Dir.chdir(app_root) do
- output = `./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"`
+ output = `SKIP_REQUIRE_WEBPACKER=true ./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"`
end
assert_equal "false\n", output
end
+ def test_csp_initializer_include_connect_src_example
+ run_generator
+
+ assert_file "config/initializers/content_security_policy.rb" do |content|
+ assert_match(/# policy\.connect_src/, content)
+ end
+ end
+
def test_app_update_keep_the_cookie_serializer_if_it_is_already_configured
app_root = File.join(destination_root, "myapp")
run_generator [app_root]
@@ -298,10 +309,10 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_app_update_does_not_generate_yarn_contents_when_bin_yarn_is_not_used
app_root = File.join(destination_root, "myapp")
- run_generator [app_root, "--skip-yarn"]
+ run_generator [app_root, "--skip-javascript"]
stub_rails_application(app_root) do
- generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_yarn: true }, { destination_root: app_root, shell: @shell }
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_javascript: true }, { destination_root: app_root, shell: @shell }
generator.send(:app_const)
quietly { generator.send(:update_bin_files) }
@@ -353,6 +364,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "#{app_root}/config/environments/production.rb" do |content|
assert_no_match(/config\.action_cable/, content)
end
+
+ assert_no_file "#{app_root}/test/channels/application_cable/connection_test.rb"
end
def test_app_update_does_not_generate_bootsnap_contents_when_skip_bootsnap_is_given
@@ -425,6 +438,36 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_file "#{app_root}/config/storage.yml"
end
+ def test_generator_skips_action_mailbox_when_skip_action_mailbox_is_given
+ run_generator [destination_root, "--skip-action-mailbox"]
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_mailbox\/engine["']/
+ end
+
+ def test_generator_skips_action_mailbox_when_skip_active_record_is_given
+ run_generator [destination_root, "--skip-active-record"]
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_mailbox\/engine["']/
+ end
+
+ def test_generator_skips_action_mailbox_when_skip_active_storage_is_given
+ run_generator [destination_root, "--skip-active-storage"]
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_mailbox\/engine["']/
+ end
+
+ def test_generator_skips_action_text_when_skip_action_text_is_given
+ run_generator [destination_root, "--skip-action-text"]
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_text\/engine["']/
+ end
+
+ def test_generator_skips_action_text_when_skip_active_record_is_given
+ run_generator [destination_root, "--skip-active-record"]
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_text\/engine["']/
+ end
+
+ def test_generator_skips_action_text_when_skip_active_storage_is_given
+ run_generator [destination_root, "--skip-active-storage"]
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_text\/engine["']/
+ end
+
def test_app_update_does_not_change_config_target_version
run_generator
@@ -483,7 +526,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
if defined?(JRUBY_VERSION)
assert_gem "activerecord-jdbcsqlite3-adapter"
else
- assert_gem "sqlite3"
+ assert_gem "sqlite3", "'~> 1.4'"
end
end
@@ -493,7 +536,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
if defined?(JRUBY_VERSION)
assert_gem "activerecord-jdbcmysql-adapter"
else
- assert_gem "mysql2", "'>= 0.4.4', '< 0.6.0'"
+ assert_gem "mysql2", "'>= 0.4.4'"
end
end
@@ -560,7 +603,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator
assert_gem "sass-rails"
- assert_gem "uglifier"
end
def test_action_cable_redis_gems
@@ -575,7 +617,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_gem "capybara"
assert_no_gem "selenium-webdriver"
- assert_no_gem "chromedriver-helper"
+ assert_no_gem "webdrivers"
assert_no_directory("test")
end
@@ -584,7 +626,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator [destination_root, "--skip-system-test"]
assert_no_gem "capybara"
assert_no_gem "selenium-webdriver"
- assert_no_gem "chromedriver-helper"
+ assert_no_gem "webdrivers"
assert_directory("test")
@@ -602,48 +644,14 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_inclusion_of_javascript_runtime
- run_generator
- if defined?(JRUBY_VERSION)
- assert_gem "therubyrhino"
- elsif RUBY_PLATFORM =~ /mingw|mswin/
- assert_gem "duktape"
- else
- assert_file "Gemfile", /# gem 'mini_racer', platforms: :ruby/
- end
- end
-
- def test_rails_ujs_is_the_default_ujs_library
- run_generator
- assert_file "app/assets/javascripts/application.js" do |contents|
- assert_match %r{^//= require rails-ujs}, contents
- end
- end
-
def test_javascript_is_skipped_if_required
run_generator [destination_root, "--skip-javascript"]
- assert_no_file "app/assets/javascripts"
+ assert_no_file "app/javascript"
assert_file "app/views/layouts/application.html.erb" do |contents|
assert_match(/stylesheet_link_tag\s+'application', media: 'all' %>/, contents)
- assert_no_match(/javascript_include_tag\s+'application' \%>/, contents)
- end
-
- assert_no_gem "coffee-rails"
- assert_no_gem "uglifier"
-
- assert_file "config/environments/production.rb" do |content|
- assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
- end
- end
-
- def test_coffeescript_is_skipped_if_required
- run_generator [destination_root, "--skip-coffee"]
-
- assert_file "Gemfile" do |content|
- assert_no_match(/coffee-rails/, content)
- assert_match(/uglifier/, content)
+ assert_no_match(/javascript_pack_tag\s+'application'/, contents)
end
end
@@ -670,6 +678,21 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_inclusion_of_listen_related_configuration_on_other_rubies
+ ruby_engine = Object.send(:remove_const, :RUBY_ENGINE)
+ Object.const_set(:RUBY_ENGINE, "MyRuby")
+
+ run_generator
+ if RbConfig::CONFIG["host_os"] =~ /darwin|linux/
+ assert_listen_related_configuration
+ else
+ assert_no_listen_related_configuration
+ end
+ ensure
+ Object.send(:remove_const, :RUBY_ENGINE)
+ Object.const_set(:RUBY_ENGINE, ruby_engine)
+ end
+
def test_non_inclusion_of_listen_related_configuration_if_skip_listen
run_generator [destination_root, "--skip-listen"]
assert_no_listen_related_configuration
@@ -745,7 +768,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_web_console_with_dev_option
- run_generator [destination_root, "--dev"]
+ run_generator [destination_root, "--dev", "--skip-bundle"]
assert_file "Gemfile" do |content|
assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content)
@@ -763,17 +786,41 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_generation_runs_bundle_install
- assert_generates_with_bundler
+ generator([destination_root], skip_webpack_install: true)
+
+ assert_bundler_command_called("install")
+ end
+
+ def test_generation_use_original_bundle_environment
+ generator([destination_root], skip_webpack_install: true)
+
+ mock_original_env = -> do
+ { "BUNDLE_RUBYONRAILS__ORG" => "user:pass" }
+ end
+
+ ensure_environment_is_set = -> *_args do
+ assert_equal "user:pass", ENV["BUNDLE_RUBYONRAILS__ORG"]
+ end
+
+ Bundler.stub :original_env, mock_original_env do
+ generator.stub :exec_bundle_command, ensure_environment_is_set do
+ quietly { generator.invoke_all }
+ end
+ end
end
def test_dev_option
- assert_generates_with_bundler dev: true
+ generator([destination_root], dev: true, skip_webpack_install: true)
+
+ assert_bundler_command_called("install")
rails_path = File.expand_path("../../..", Rails.root)
assert_file "Gemfile", /^gem\s+["']rails["'],\s+path:\s+["']#{Regexp.escape(rails_path)}["']$/
end
def test_edge_option
- assert_generates_with_bundler edge: true
+ generator([destination_root], edge: true, skip_webpack_install: true)
+
+ assert_bundler_command_called("install")
assert_file "Gemfile", %r{^gem\s+["']rails["'],\s+github:\s+["']#{Regexp.escape("rails/rails")}["']$}
end
@@ -782,23 +829,18 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_gem "spring"
end
+ def test_bundler_binstub
+ generator([destination_root], skip_webpack_install: true)
+
+ assert_bundler_command_called("binstubs bundler")
+ end
+
def test_spring_binstubs
jruby_skip "spring doesn't run on JRuby"
- command_check = -> command do
- @binstub_called ||= 0
- case command
- when "install"
- # Called when running bundle, we just want to stub it so nothing to do here.
- when "exec spring binstub --all"
- @binstub_called += 1
- assert_equal 1, @binstub_called, "exec spring binstub --all expected to be called once, but was called #{@install_called} times."
- end
- end
+ generator([destination_root], skip_webpack_install: true)
- generator.stub :bundle_command, command_check do
- quietly { generator.invoke_all }
- end
+ assert_bundler_command_called("exec spring binstub --all")
end
def test_spring_no_fork
@@ -818,27 +860,30 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_spring_with_dev_option
- run_generator [destination_root, "--dev"]
+ run_generator [destination_root, "--dev", "--skip-bundle"]
assert_no_gem "spring"
end
- def test_webpack_option
+ def test_skip_javascript_option
command_check = -> command, *_ do
@called ||= 0
if command == "webpacker:install"
@called += 1
- assert_equal 1, @called, "webpacker:install expected to be called once, but was called #{@called} times."
+ assert_equal 0, @called, "webpacker:install expected not to be called, but was called #{@called} times."
end
end
- generator([destination_root], webpack: "webpack").stub(:rails_command, command_check) do
+ generator([destination_root], skip_javascript: true).stub(:rails_command, command_check) do
generator.stub :bundle_command, nil do
quietly { generator.invoke_all }
end
end
- assert_gem "webpacker"
+ assert_no_gem "webpacker"
+ assert_file "config/initializers/content_security_policy.rb" do |content|
+ assert_no_match(/policy\.connect_src/, content)
+ end
end
def test_webpack_option_with_js_framework
@@ -860,6 +905,22 @@ class AppGeneratorTest < Rails::Generators::TestCase
quietly { generator.invoke_all }
end
end
+
+ assert_gem "webpacker"
+ end
+
+ def test_skip_webpack_install
+ command_check = -> command do
+ if command == "webpacker:install"
+ assert false, "webpacker:install expected not to be called."
+ end
+ end
+
+ generator([destination_root], skip_webpack_install: true).stub(:rails_command, command_check) do
+ quietly { generator.invoke_all }
+ end
+
+ assert_gem "webpacker"
end
def test_generator_if_skip_turbolinks_is_given
@@ -869,13 +930,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "app/views/layouts/application.html.erb" do |content|
assert_no_match(/data-turbolinks-track/, content)
end
- assert_file "app/assets/javascripts/application.js" do |content|
+ assert_file "app/javascript/packs/application.js" do |content|
assert_no_match(/turbolinks/, content)
end
end
def test_bootsnap
- run_generator
+ run_generator [destination_root, "--no-skip-bootsnap"]
unless defined?(JRUBY_VERSION)
assert_gem "bootsnap"
@@ -900,7 +961,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_bootsnap_with_dev_option
- run_generator [destination_root, "--dev"]
+ run_generator [destination_root, "--dev", "--skip-bundle"]
assert_no_gem "bootsnap"
assert_file "config/boot.rb" do |content|
@@ -922,6 +983,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
else
assert_match(/#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}/, content)
end
+
+ assert content.end_with?("\n"), "expected .ruby-version to end with newline"
end
end
@@ -968,7 +1031,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_after_bundle_callback
path = "http://example.org/rails_template"
- template = %{ after_bundle { run 'echo ran after_bundle' } }.dup
+ template = +%{ after_bundle { run 'echo ran after_bundle' } }
template.instance_eval "def read; self; end" # Make the string respond to read
check_open = -> *args do
@@ -976,7 +1039,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
template
end
- sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"]
+ sequence = ["git init", "install", "binstubs bundler", "exec spring binstub --all", "webpacker:install", "echo ran after_bundle"]
@sequence_step ||= 0
ensure_bundler_first = -> command, options = nil do
assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}"
@@ -993,7 +1056,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- assert_equal 4, @sequence_step
+ assert_equal 6, @sequence_step
end
def test_gitignore
@@ -1063,18 +1126,14 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def assert_generates_with_bundler(options = {})
- generator([destination_root], options)
-
- command_check = -> command do
- @install_called ||= 0
+ def assert_bundler_command_called(target_command)
+ command_check = -> (command, env = {}) do
+ @command_called ||= 0
case command
- when "install"
- @install_called += 1
- assert_equal 1, @install_called, "install expected to be called once, but was called #{@install_called} times"
- when "exec spring binstub --all"
- # Called when running tests with spring, let through unscathed.
+ when target_command
+ @command_called += 1
+ assert_equal 1, @command_called, "#{command} expected to be called once, but was called #{@command_called} times."
end
end
diff --git a/railties/test/generators/assets_generator_test.rb b/railties/test/generators/assets_generator_test.rb
index 3cec41dbf8..83d2429acf 100644
--- a/railties/test/generators/assets_generator_test.rb
+++ b/railties/test/generators/assets_generator_test.rb
@@ -9,13 +9,11 @@ class AssetsGeneratorTest < Rails::Generators::TestCase
def test_assets
run_generator
- assert_file "app/assets/javascripts/posts.js"
assert_file "app/assets/stylesheets/posts.css"
end
def test_skipping_assets
- run_generator ["posts", "--no-stylesheets", "--no-javascripts"]
- assert_no_file "app/assets/javascripts/posts.js"
+ run_generator ["posts", "--no-stylesheets"]
assert_no_file "app/assets/stylesheets/posts.css"
end
end
diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb
index e543cc11b8..1a25422c3c 100644
--- a/railties/test/generators/channel_generator_test.rb
+++ b/railties/test/generators/channel_generator_test.rb
@@ -26,8 +26,8 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_file "app/assets/javascripts/channels/chat.js" do |channel|
- assert_match(/App\.chat = App\.cable\.subscriptions\.create\("ChatChannel/, channel)
+ assert_file "app/javascript/channels/chat_channel.js" do |channel|
+ assert_match(/import consumer from "\.\/consumer"\s+consumer\.subscriptions\.create\("ChatChannel/, channel)
end
end
@@ -40,8 +40,8 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/def mute/, channel)
end
- assert_file "app/assets/javascripts/channels/chat.js" do |channel|
- assert_match(/App\.chat = App\.cable\.subscriptions\.create\("ChatChannel/, channel)
+ assert_file "app/javascript/channels/chat_channel.js" do |channel|
+ assert_match(/import consumer from "\.\/consumer"\s+consumer\.subscriptions\.create\("ChatChannel/, channel)
assert_match(/,\n\n speak/, channel)
assert_match(/,\n\n mute: function\(\) \{\n return this\.perform\('mute'\);\n \}\n\}\);/, channel)
end
@@ -54,15 +54,27 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_no_file "app/assets/javascripts/channels/chat.js"
+ assert_no_file "app/javascript/channels/chat_channel.js"
end
- def test_cable_js_is_created_if_not_present_already
+ def test_consumer_js_is_created_if_not_present_already
run_generator ["chat"]
- FileUtils.rm("#{destination_root}/app/assets/javascripts/cable.js")
+ FileUtils.rm("#{destination_root}/app/javascript/channels/index.js")
+ FileUtils.rm("#{destination_root}/app/javascript/channels/consumer.js")
run_generator ["camp"]
- assert_file "app/assets/javascripts/cable.js"
+ assert_file "app/javascript/channels/index.js"
+ assert_file "app/javascript/channels/consumer.js"
+ end
+
+ def test_invokes_default_test_framework
+ run_generator %w(chat -t=test_unit)
+
+ assert_file "test/channels/chat_channel_test.rb" do |test|
+ assert_match(/class ChatChannelTest < ActionCable::Channel::TestCase/, test)
+ assert_match(/# test "subscribes" do/, test)
+ assert_match(/# assert subscription.confirmed\?/, test)
+ end
end
def test_channel_on_revoke
@@ -70,11 +82,13 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
run_generator ["chat"], behavior: :revoke
assert_no_file "app/channels/chat_channel.rb"
- assert_no_file "app/assets/javascripts/channels/chat.js"
+ assert_no_file "app/javascript/channels/chat_channel.js"
+ assert_no_file "test/channels/chat_channel_test.rb"
assert_file "app/channels/application_cable/channel.rb"
assert_file "app/channels/application_cable/connection.rb"
- assert_file "app/assets/javascripts/cable.js"
+ assert_file "app/javascript/channels/index.js"
+ assert_file "app/javascript/channels/consumer.js"
end
def test_channel_suffix_is_not_duplicated
@@ -83,7 +97,10 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/channels/chat_channel_channel.rb"
assert_file "app/channels/chat_channel.rb"
- assert_no_file "app/assets/javascripts/channels/chat_channel.js"
- assert_file "app/assets/javascripts/channels/chat.js"
+ assert_no_file "app/javascript/channels/chat_channel_channel.js"
+ assert_file "app/javascript/channels/chat_channel.js"
+
+ assert_no_file "test/channels/chat_channel_channel_test.rb"
+ assert_file "test/channels/chat_channel_test.rb"
end
end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index 021004c9b8..8786756c68 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -39,13 +39,11 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
def test_invokes_assets
run_generator
- assert_file "app/assets/javascripts/account.js"
assert_file "app/assets/stylesheets/account.css"
end
def test_does_not_invoke_assets_if_required
run_generator ["account", "--skip-assets"]
- assert_no_file "app/assets/javascripts/account.js"
assert_no_file "app/assets/stylesheets/account.css"
end
@@ -132,9 +130,6 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/helpers/account_controller_helper.rb"
assert_file "app/helpers/account_helper.rb"
- assert_no_file "app/assets/javascripts/account_controller.js"
- assert_file "app/assets/javascripts/account.js"
-
assert_no_file "app/assets/stylesheets/account_controller.css"
assert_file "app/assets/stylesheets/account.css"
end
diff --git a/railties/test/generators/db_system_change_generator_test.rb b/railties/test/generators/db_system_change_generator_test.rb
new file mode 100644
index 0000000000..607db96906
--- /dev/null
+++ b/railties/test/generators/db_system_change_generator_test.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require "generators/generators_test_helper"
+require "rails/generators/rails/db/system/change/change_generator"
+
+module Rails
+ module Generators
+ module Db
+ module System
+ class ChangeGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ setup do
+ copy_gemfile(
+ GemfileEntry.new("sqlite3", nil, "Use sqlite3 as the database for Active Record")
+ )
+ end
+
+ test "change to invalid database" do
+ output = capture(:stderr) do
+ run_generator ["--to", "invalid-db"]
+ end
+
+ assert_match <<~MSG.squish, output
+ Invalid value for --to option.
+ Supported preconfigurations are:
+ mysql, postgresql, sqlite3, oracle, frontbase,
+ ibm_db, sqlserver, jdbcmysql, jdbcsqlite3,
+ jdbcpostgresql, jdbc.
+ MSG
+ end
+
+ test "change to postgresql" do
+ run_generator ["--to", "postgresql"]
+
+ assert_file("config/database.yml") do |content|
+ assert_match "adapter: postgresql", content
+ assert_match "database: test_app", content
+ end
+
+ assert_file("Gemfile") do |content|
+ assert_match "# Use pg as the database for Active Record", content
+ assert_match "gem 'pg', '>= 0.18', '< 2.0'", content
+ end
+ end
+
+ test "change to mysql" do
+ run_generator ["--to", "mysql"]
+
+ assert_file("config/database.yml") do |content|
+ assert_match "adapter: mysql2", content
+ assert_match "database: test_app", content
+ end
+
+ assert_file("Gemfile") do |content|
+ assert_match "# Use mysql2 as the database for Active Record", content
+ assert_match "gem 'mysql2', '>= 0.4.4'", content
+ end
+ end
+
+ test "change to sqlite3" do
+ run_generator ["--to", "sqlite3"]
+
+ assert_file("config/database.yml") do |content|
+ assert_match "adapter: sqlite3", content
+ assert_match "db/development.sqlite3", content
+ end
+
+ assert_file("Gemfile") do |content|
+ assert_match "# Use sqlite3 as the database for Active Record", content
+ assert_match "gem 'sqlite3', '~> 1.4'", content
+ end
+ end
+
+ test "change from versioned gem to other versioned gem" do
+ run_generator ["--to", "sqlite3"]
+ run_generator ["--to", "mysql", "--force"]
+
+ assert_file("config/database.yml") do |content|
+ assert_match "adapter: mysql2", content
+ assert_match "database: test_app", content
+ end
+
+ assert_file("Gemfile") do |content|
+ assert_match "# Use mysql2 as the database for Active Record", content
+ assert_match "gem 'mysql2', '>= 0.4.4'", content
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index 772b4f6f0d..bf60d6bc22 100644
--- a/railties/test/generators/generated_attribute_test.rb
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -38,6 +38,16 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
assert_field_type :boolean, :check_box
end
+ def test_field_type_returns_rich_text_area
+ assert_field_type :rich_text, :rich_text_area
+ end
+
+ def test_field_type_returns_file_field
+ %w(attachment attachments).each do |attribute_type|
+ assert_field_type attribute_type, :file_field
+ end
+ end
+
def test_field_type_with_unknown_type_returns_text_field
%w(foo bar baz).each do |attribute_type|
assert_field_type attribute_type, :text_field
@@ -84,7 +94,7 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
end
def test_default_value_is_nil
- %w(references belongs_to).each do |attribute_type|
+ %w(references belongs_to rich_text attachment attachments).each do |attribute_type|
assert_field_default_value attribute_type, nil
end
end
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index ad2a55f496..8b42cb83db 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -30,6 +30,12 @@ module GeneratorsTestHelper
include ActiveSupport::Testing::Stream
include ActiveSupport::Testing::MethodCallAssertions
+ GemfileEntry = Struct.new(:name, :version, :comment, :options, :commented_out) do
+ def initialize(name, version, comment, options = {}, commented_out = false)
+ super
+ end
+ end
+
def self.included(base)
base.class_eval do
destination File.join(Rails.root, "tmp")
@@ -42,10 +48,59 @@ module GeneratorsTestHelper
end
end
+ def with_secondary_database_configuration
+ original_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = {
+ test: {
+ secondary: {
+ database: "db/secondary.sqlite3",
+ migrations_paths: "db/secondary_migrate",
+ },
+ },
+ }
+ yield
+ ensure
+ ActiveRecord::Base.configurations = original_configurations
+ end
+
def copy_routes
routes = File.expand_path("../../lib/rails/generators/rails/app/templates/config/routes.rb.tt", __dir__)
destination = File.join(destination_root, "config")
FileUtils.mkdir_p(destination)
FileUtils.cp routes, File.join(destination, "routes.rb")
end
+
+ def copy_gemfile(*gemfile_entries)
+ locals = gemfile_locals.merge(gemfile_entries: gemfile_entries)
+ gemfile = File.expand_path("../../lib/rails/generators/rails/app/templates/Gemfile.tt", __dir__)
+ gemfile = evaluate_template(gemfile, locals)
+ destination = File.join(destination_root)
+ File.write File.join(destination, "Gemfile"), gemfile
+ end
+
+ def evaluate_template(file, locals = {})
+ erb = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ ERB.new(File.read(file), trim_mode: "-", eoutvar: "@output_buffer")
+ else
+ ERB.new(File.read(file), nil, "-", "@output_buffer")
+ end
+ context = Class.new do
+ locals.each do |local, value|
+ class_attribute local, default: value
+ end
+ end
+ erb.result(context.new.instance_eval("binding"))
+ end
+
+ private
+ def gemfile_locals
+ {
+ skip_active_storage: true,
+ depend_on_bootsnap: false,
+ depend_on_listen: false,
+ spring_install: false,
+ depends_on_system_test: false,
+ options: ActiveSupport::OrderedOptions.new,
+ }
+ end
end
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
index 4cdb6adf82..192839799e 100644
--- a/railties/test/generators/helper_generator_test.rb
+++ b/railties/test/generators/helper_generator_test.rb
@@ -30,12 +30,17 @@ class HelperGeneratorTest < Rails::Generators::TestCase
require "#{destination_root}/app/helpers/products_helper"
assert_nothing_raised do
- begin
- run_generator ["admin::products"]
- ensure
- # cleanup
- Object.send(:remove_const, :ProductsHelper)
- end
+ run_generator ["admin::products"]
+ ensure
+ # cleanup
+ Object.send(:remove_const, :ProductsHelper)
end
end
+
+ def test_helper_suffix_is_not_duplicated
+ run_generator %w(products_helper)
+
+ assert_no_file "app/helpers/products_helper_helper.rb"
+ assert_file "app/helpers/products_helper.rb"
+ end
end
diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb
index 82791f1a27..2ec4895096 100644
--- a/railties/test/generators/integration_test_generator_test.rb
+++ b/railties/test/generators/integration_test_generator_test.rb
@@ -15,4 +15,11 @@ class IntegrationTestGeneratorTest < Rails::Generators::TestCase
run_generator %w(iguchi/integration)
assert_file "test/integration/iguchi/integration_test.rb", /class Iguchi::IntegrationTest < ActionDispatch::IntegrationTest/
end
+
+ def test_test_suffix_is_not_duplicated
+ run_generator %w(integration_test)
+
+ assert_no_file "test/integration/integration_test_test.rb"
+ assert_file "test/integration/integration_test.rb"
+ end
end
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index ddac6e1a1e..099e06c4d3 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -119,7 +119,7 @@ class MailerGeneratorTest < Rails::Generators::TestCase
assert_match(/haml \[not found\]/, content)
end
- def test_mailer_with_namedspaced_mailer
+ def test_mailer_with_namespaced_mailer
run_generator ["Farm::Animal", "moos"]
assert_file "app/mailers/farm/animal_mailer.rb" do |mailer|
assert_match(/class Farm::AnimalMailer < ApplicationMailer/, mailer)
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 88a939a55a..540bed551b 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -51,12 +51,12 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
def test_add_migration_with_table_having_from_in_title
- migration = "add_email_address_to_blacklisted_from_campaign"
+ migration = "add_email_address_to_excluded_from_campaign"
run_generator [migration, "email_address:string"]
assert_migration "db/migrate/#{migration}.rb" do |content|
assert_method :change, content do |change|
- assert_match(/add_column :blacklisted_from_campaigns, :email_address, :string/, change)
+ assert_match(/add_column :excluded_from_campaigns, :email_address, :string/, change)
end
end
end
@@ -254,6 +254,28 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_database_puts_migrations_in_configured_folder
+ with_secondary_database_configuration do
+ run_generator ["create_books", "--database=secondary"]
+ assert_migration "db/secondary_migrate/create_books.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :books/, change)
+ end
+ end
+ end
+ end
+
+ def test_database_puts_migrations_in_configured_folder_with_aliases
+ with_secondary_database_configuration do
+ run_generator ["create_books", "--db=secondary"]
+ assert_migration "db/secondary_migrate/create_books.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :books/, change)
+ end
+ end
+ end
+ end
+
def test_should_create_empty_migrations_if_name_not_start_with_add_or_remove_or_create
migration = "delete_books"
run_generator [migration, "title:string", "content:text"]
@@ -344,6 +366,44 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
Rails.application.config.paths["db/migrate"] = old_paths
end
+ def test_add_migration_ignores_virtual_attributes
+ migration = "add_rich_text_content_to_messages"
+ run_generator [migration, "content:rich_text", "video:attachment", "photos:attachments"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_no_match(/add_column :messages, :content, :rich_text/, change)
+ assert_no_match(/add_column :messages, :video, :attachment/, change)
+ assert_no_match(/add_column :messages, :photos, :attachments/, change)
+ end
+ end
+ end
+
+ def test_create_table_migration_ignores_virtual_attributes
+ run_generator ["create_messages", "content:rich_text", "video:attachment", "photos:attachments"]
+ assert_migration "db/migrate/create_messages.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :messages/, change)
+ assert_no_match(/ t\.rich_text :content/, change)
+ assert_no_match(/ t\.attachment :video/, change)
+ assert_no_match(/ t\.attachments :photos/, change)
+ end
+ end
+ end
+
+ def test_remove_migration_with_virtual_attributes
+ migration = "remove_content_from_messages"
+ run_generator [migration, "content:rich_text", "video:attachment", "photos:attachments"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_no_match(/remove_column :messages, :content, :rich_text/, change)
+ assert_no_match(/remove_column :messages, :video, :attachment/, change)
+ assert_no_match(/remove_column :messages, :photos, :attachments/, change)
+ end
+ end
+ end
+
private
def with_singular_table_name
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 8d933e82c3..c97cd17ec6 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -7,6 +7,11 @@ class ModelGeneratorTest < Rails::Generators::TestCase
include GeneratorsTestHelper
arguments %w(Account name:string age:integer)
+ def setup
+ super
+ Rails::Generators::ModelHelpers.skip_warn = false
+ end
+
def test_help_shows_invoked_generators_options
content = run_generator ["--help"]
assert_match(/ActiveRecord options:/, content)
@@ -37,12 +42,24 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
def test_plural_names_are_singularized
- content = run_generator ["accounts".freeze]
+ content = run_generator ["accounts"]
assert_file "app/models/account.rb", /class Account < ApplicationRecord/
assert_file "test/models/account_test.rb", /class AccountTest/
assert_match(/\[WARNING\] The model name 'accounts' was recognized as a plural, using the singular 'account' instead\. Override with --force-plural or setup custom inflection rules for this noun before running the generator\./, content)
end
+ def test_unknown_inflection_rule_are_warned
+ content = run_generator ["porsche"]
+ assert_match("[WARNING] Rails cannot recover singular form from its plural form 'porsches'.\nPlease setup custom inflection rules for this noun before running the generator in config/initializers/inflections.rb.", content)
+ assert_file "app/models/porsche.rb", /class Porsche < ApplicationRecord/
+
+ uncountable_content = run_generator ["sheep"]
+ assert_no_match("[WARNING] Rails cannot recover singular form from its plural form", uncountable_content)
+
+ regular_content = run_generator ["account"]
+ assert_no_match("[WARNING] Rails cannot recover singular form from its plural form", regular_content)
+ end
+
def test_model_with_underscored_parent_option
run_generator ["account", "--parent", "admin/account"]
assert_file "app/models/account.rb", /class Account < Admin::Account/
@@ -87,7 +104,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
ActiveRecord::Base.pluralize_table_names = true
end
- def test_migration_with_namespaces_in_model_name_without_plurization
+ def test_migration_with_namespaces_in_model_name_without_pluralization
ActiveRecord::Base.pluralize_table_names = false
run_generator ["Gallery::Image"]
assert_migration "db/migrate/create_gallery_image", /class CreateGalleryImage < ActiveRecord::Migration\[[0-9.]+\]/
@@ -375,6 +392,28 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_database_puts_migrations_in_configured_folder
+ with_secondary_database_configuration do
+ run_generator ["account", "--database=secondary"]
+ assert_migration "db/secondary_migrate/create_accounts.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :accounts/, change)
+ end
+ end
+ end
+ end
+
+ def test_database_puts_migrations_in_configured_folder_with_aliases
+ with_secondary_database_configuration do
+ run_generator ["account", "--db=secondary"]
+ assert_migration "db/secondary_migrate/create_accounts.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :accounts/, change)
+ end
+ end
+ end
+ end
+
def test_required_belongs_to_adds_required_association
run_generator ["account", "supplier:references{required}"]
@@ -460,6 +499,43 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_file "app/models/user.rb", expected_file
end
+ def test_model_with_rich_text_attribute_adds_has_rich_text
+ run_generator ["message", "content:rich_text"]
+ expected_file = <<~FILE
+ class Message < ApplicationRecord
+ has_rich_text :content
+ end
+ FILE
+ assert_file "app/models/message.rb", expected_file
+ end
+
+ def test_model_with_attachment_attribute_adds_has_one_attached
+ run_generator ["message", "video:attachment"]
+ expected_file = <<~FILE
+ class Message < ApplicationRecord
+ has_one_attached :video
+ end
+ FILE
+ assert_file "app/models/message.rb", expected_file
+ end
+
+ def test_model_with_attachments_attribute_adds_has_many_attached
+ run_generator ["message", "photos:attachments"]
+ expected_file = <<~FILE
+ class Message < ApplicationRecord
+ has_many_attached :photos
+ end
+ FILE
+ assert_file "app/models/message.rb", expected_file
+ end
+
+ def test_skip_virtual_fields_in_fixtures
+ run_generator ["message", "content:rich_text", "video:attachment", "photos:attachments"]
+
+ assert_generated_fixture("test/fixtures/messages.yml",
+ "one" => nil, "two" => nil)
+ end
+
private
def assert_generated_fixture(path, parsed_contents)
fixture_file = File.new File.expand_path(path, destination_root)
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 28ac3611b7..f45464f8d0 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -140,10 +140,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
run_generator
assert_file "test/dummy/app/assets/stylesheets/application.css"
-
- assert_file "test/dummy/app/assets/javascripts/application.js" do |contents|
- assert_no_match(/jquery/, contents)
- end
end
def test_ensure_that_plugin_options_are_not_passed_to_app_generator
@@ -210,28 +206,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_no_file "#{destination_root}/Gemfile.lock"
end
- def test_skipping_javascripts_without_mountable_option
- run_generator
- assert_no_file "app/assets/javascripts/bukkits/application.js"
- end
-
- def test_javascripts_generation
- run_generator [destination_root, "--mountable"]
- assert_file "app/assets/javascripts/bukkits/application.js" do |content|
- assert_match "//= require rails-ujs", content
- assert_match "//= require activestorage", content
- assert_match "//= require_tree .", content
- end
- assert_file "app/views/layouts/bukkits/application.html.erb" do |content|
- assert_match "javascript_include_tag", content
- end
- end
-
- def test_skip_javascripts
+ def test_skip_javascript
run_generator [destination_root, "--skip-javascript", "--mountable"]
- assert_no_file "app/assets/javascripts/bukkits/application.js"
assert_file "app/views/layouts/bukkits/application.html.erb" do |content|
- assert_no_match "javascript_include_tag", content
+ assert_no_match "javascript_pack_tag", content
end
end
@@ -264,7 +242,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_creating_engine_in_full_mode
run_generator [destination_root, "--full"]
- assert_file "app/assets/javascripts/bukkits"
assert_file "app/assets/stylesheets/bukkits"
assert_file "app/assets/images/bukkits"
assert_file "app/models"
@@ -280,7 +257,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_creating_engine_with_hyphenated_name_in_full_mode
run_generator [File.join(destination_root, "hyphenated-name"), "--full"]
- assert_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
+ assert_no_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
assert_file "hyphenated-name/app/assets/stylesheets/hyphenated/name"
assert_file "hyphenated-name/app/assets/images/hyphenated/name"
assert_file "hyphenated-name/app/models"
@@ -297,7 +274,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_creating_engine_with_hyphenated_and_underscored_name_in_full_mode
run_generator [File.join(destination_root, "my_hyphenated-name"), "--full"]
- assert_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
+ assert_no_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/stylesheets/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/images/my_hyphenated/name"
assert_file "my_hyphenated-name/app/models"
@@ -318,7 +295,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_create_mountable_application_with_mountable_option
run_generator [destination_root, "--mountable"]
- assert_file "app/assets/javascripts/bukkits"
+ assert_no_file "app/assets/javascripts/bukkits"
assert_file "app/assets/stylesheets/bukkits"
assert_file "app/assets/images/bukkits"
assert_file "config/routes.rb", /Bukkits::Engine\.routes\.draw do/
@@ -334,7 +311,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_match "<%= csrf_meta_tags %>", contents
assert_match "<%= csp_meta_tag %>", contents
assert_match(/stylesheet_link_tag\s+['"]bukkits\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]bukkits\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]bukkits\/application['"]/, contents)
assert_match "<%= yield %>", contents
end
assert_file "test/test_helper.rb" do |content|
@@ -348,7 +325,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_create_mountable_application_with_mountable_option_and_hypenated_name
run_generator [File.join(destination_root, "hyphenated-name"), "--mountable"]
- assert_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
+ assert_no_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
assert_file "hyphenated-name/app/assets/stylesheets/hyphenated/name"
assert_file "hyphenated-name/app/assets/images/hyphenated/name"
assert_file "hyphenated-name/config/routes.rb", /Hyphenated::Name::Engine\.routes\.draw do/
@@ -364,13 +341,13 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "hyphenated-name/app/views/layouts/hyphenated/name/application.html.erb" do |contents|
assert_match "<title>Hyphenated name</title>", contents
assert_match(/stylesheet_link_tag\s+['"]hyphenated\/name\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]hyphenated\/name\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]hyphenated\/name\/application['"]/, contents)
end
end
def test_create_mountable_application_with_mountable_option_and_hypenated_and_underscored_name
run_generator [File.join(destination_root, "my_hyphenated-name"), "--mountable"]
- assert_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
+ assert_no_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/stylesheets/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/images/my_hyphenated/name"
assert_file "my_hyphenated-name/config/routes.rb", /MyHyphenated::Name::Engine\.routes\.draw do/
@@ -386,13 +363,13 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "my_hyphenated-name/app/views/layouts/my_hyphenated/name/application.html.erb" do |contents|
assert_match "<title>My hyphenated name</title>", contents
assert_match(/stylesheet_link_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
end
end
def test_create_mountable_application_with_mountable_option_and_multiple_hypenates_in_name
run_generator [File.join(destination_root, "deep-hyphenated-name"), "--mountable"]
- assert_file "deep-hyphenated-name/app/assets/javascripts/deep/hyphenated/name"
+ assert_no_file "deep-hyphenated-name/app/assets/javascripts/deep/hyphenated/name"
assert_file "deep-hyphenated-name/app/assets/stylesheets/deep/hyphenated/name"
assert_file "deep-hyphenated-name/app/assets/images/deep/hyphenated/name"
assert_file "deep-hyphenated-name/config/routes.rb", /Deep::Hyphenated::Name::Engine\.routes\.draw do/
@@ -408,15 +385,15 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "deep-hyphenated-name/app/views/layouts/deep/hyphenated/name/application.html.erb" do |contents|
assert_match "<title>Deep hyphenated name</title>", contents
assert_match(/stylesheet_link_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
end
end
def test_creating_gemspec
run_generator
- assert_file "bukkits.gemspec", /s\.name\s+= "bukkits"/
- assert_file "bukkits.gemspec", /s\.files = Dir\["\{app,config,db,lib\}\/\*\*\/\*", "MIT-LICENSE", "Rakefile", "README\.md"\]/
- assert_file "bukkits.gemspec", /s\.version\s+ = Bukkits::VERSION/
+ assert_file "bukkits.gemspec", /spec\.name\s+= "bukkits"/
+ assert_file "bukkits.gemspec", /spec\.files = Dir\["\{app,config,db,lib\}\/\*\*\/\*", "MIT-LICENSE", "Rakefile", "README\.md"\]/
+ assert_file "bukkits.gemspec", /spec\.version\s+ = Bukkits::VERSION/
end
def test_usage_of_engine_commands
@@ -465,7 +442,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
end
- def test_dummy_appplication_skip_listen_by_default
+ def test_dummy_application_skip_listen_by_default
run_generator
assert_file "test/dummy/config/environments/development.rb" do |contents|
@@ -735,38 +712,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
Object.send(:remove_const, "ENGINE_ROOT")
end
- def test_after_bundle_callback
- path = "http://example.org/rails_template"
- template = %{ after_bundle { run "echo ran after_bundle" } }.dup
- template.instance_eval "def read; self; end" # Make the string respond to read
-
- check_open = -> *args do
- assert_equal [ path, "Accept" => "application/x-thor-template" ], args
- template
- end
-
- sequence = ["echo ran after_bundle"]
- @sequence_step ||= 0
- ensure_bundler_first = -> command do
- assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}"
- @sequence_step += 1
- end
-
- content = nil
- generator([destination_root], template: path).stub(:open, check_open, template) do
- generator.stub(:bundle_command, ensure_bundler_first) do
- generator.stub(:run, ensure_bundler_first) do
- silence_stream($stdout) do
- content = capture(:stderr) { generator.invoke_all }
- end
- end
- end
- end
-
- assert_equal 1, @sequence_step
- assert_match(/DEPRECATION WARNING: `after_bundle` is deprecated/, content)
- end
-
private
def action(*args, &block)
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
index 63a2cd3869..b99b4baf6b 100644
--- a/railties/test/generators/resource_generator_test.rb
+++ b/railties/test/generators/resource_generator_test.rb
@@ -7,7 +7,11 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
include GeneratorsTestHelper
arguments %w(account)
- setup :copy_routes
+ def setup
+ super
+ copy_routes
+ Rails::Generators::ModelHelpers.skip_warn = false
+ end
def test_help_with_inherited_options
content = run_generator ["--help"]
@@ -61,7 +65,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
end
def test_plural_names_are_singularized
- content = run_generator ["accounts".freeze]
+ content = run_generator ["accounts"]
assert_file "app/models/account.rb", /class Account < ApplicationRecord/
assert_file "test/models/account_test.rb", /class AccountTest/
assert_match(/\[WARNING\] The model name 'accounts' was recognized as a plural, using the singular 'account' instead\. Override with --force-plural or setup custom inflection rules for this noun before running the generator\./, content)
@@ -75,7 +79,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
end
def test_mass_nouns_do_not_throw_warnings
- content = run_generator ["sheep".freeze]
+ content = run_generator ["sheep"]
assert_no_match(/\[WARNING\]/, content)
end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index fd5aa817b4..1348744b0b 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -80,6 +80,15 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_controller_permit_attachment_attributes
+ run_generator ["Message", "video:attachment", "photos:attachments"]
+
+ assert_file "app/controllers/messages_controller.rb" do |content|
+ assert_match(/def message_params/, content)
+ assert_match(/params\.require\(:message\)\.permit\(:video, photos: \[\]\)/, content)
+ end
+ end
+
def test_helper_are_invoked_with_a_pluralized_name
run_generator
assert_file "app/helpers/users_helper.rb", /module UsersHelper/
@@ -276,4 +285,13 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
assert_no_match(/assert_redirected_to/, content)
end
end
+
+ def test_api_only_generates_params_for_attachments
+ run_generator ["Message", "video:attachment", "photos:attachments", "--api"]
+
+ assert_file "app/controllers/messages_controller.rb" do |content|
+ assert_match(/def message_params/, content)
+ assert_match(/params\.require\(:message\)\.permit\(:video, photos: \[\]\)/, content)
+ end
+ end
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 3e631f6021..bfa52a1beb 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -5,7 +5,7 @@ require "rails/generators/rails/scaffold/scaffold_generator"
class ScaffoldGeneratorTest < Rails::Generators::TestCase
include GeneratorsTestHelper
- arguments %w(product_line title:string product:belongs_to user:references)
+ arguments %w(product_line title:string approved:boolean product:belongs_to user:references)
setup :copy_routes
@@ -17,6 +17,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "test/models/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
assert_file "test/fixtures/product_lines.yml"
assert_migration "db/migrate/create_product_lines.rb", /belongs_to :product/
+ assert_migration "db/migrate/create_product_lines.rb", /boolean :approved/
assert_migration "db/migrate/create_product_lines.rb", /references :user/
# Route
@@ -60,8 +61,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "test/controllers/product_lines_controller_test.rb" do |test|
assert_match(/class ProductLinesControllerTest < ActionDispatch::IntegrationTest/, test)
- assert_match(/post product_lines_url, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
- assert_match(/patch product_line_url\(@product_line\), params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_match(/post product_lines_url, params: \{ product_line: \{ approved: @product_line\.approved, product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_match(/patch product_line_url\(@product_line\), params: \{ product_line: \{ approved: @product_line\.approved, product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
end
# System tests
@@ -69,6 +70,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_match(/class ProductLinesTest < ApplicationSystemTestCase/, test)
assert_match(/visit product_lines_url/, test)
assert_match(/fill_in "Title", with: @product_line\.title/, test)
+ assert_match(/check "Approved" if @product_line\.approved/, test)
assert_match(/assert_text "Product line was successfully updated"/, test)
end
@@ -93,7 +95,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/product_lines.js"
assert_file "app/assets/stylesheets/product_lines.css"
end
@@ -166,7 +167,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/product_lines.js"
assert_no_file "app/assets/stylesheets/product_lines.css"
end
@@ -222,7 +222,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
- assert_no_file "app/assets/javascripts/product_lines.js"
assert_no_file "app/assets/stylesheets/product_lines.css"
end
@@ -299,7 +298,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
- assert_file "app/assets/javascripts/admin/roles.js"
assert_file "app/assets/stylesheets/admin/roles.css"
end
@@ -335,7 +333,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/admin/roles.js"
assert_no_file "app/assets/stylesheets/admin/roles.css"
end
@@ -380,28 +377,24 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
def test_scaffold_generator_no_assets_with_switch_no_assets
run_generator [ "posts", "--no-assets" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/posts.js"
assert_no_file "app/assets/stylesheets/posts.css"
end
def test_scaffold_generator_no_assets_with_switch_assets_false
run_generator [ "posts", "--assets=false" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/posts.js"
assert_no_file "app/assets/stylesheets/posts.css"
end
def test_scaffold_generator_no_scaffold_stylesheet_with_switch_no_scaffold_stylesheet
run_generator [ "posts", "--no-scaffold-stylesheet" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/posts.js"
assert_file "app/assets/stylesheets/posts.css"
end
def test_scaffold_generator_no_scaffold_stylesheet_with_switch_scaffold_stylesheet_false
run_generator [ "posts", "--scaffold-stylesheet=false" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/posts.js"
assert_file "app/assets/stylesheets/posts.css"
end
@@ -429,17 +422,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
def test_scaffold_generator_no_stylesheets
run_generator [ "posts", "--no-stylesheets" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/posts.js"
assert_no_file "app/assets/stylesheets/posts.css"
end
- def test_scaffold_generator_no_javascripts
- run_generator [ "posts", "--no-javascripts" ]
- assert_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/posts.js"
- assert_file "app/assets/stylesheets/posts.css"
- end
-
def test_scaffold_generator_outputs_error_message_on_missing_attribute_type
run_generator ["post", "title", "body:text", "author"]
@@ -452,8 +437,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
end
- def test_scaffold_generator_belongs_to
- run_generator ["account", "name", "currency:belongs_to"]
+ def test_scaffold_generator_belongs_to_and_references
+ run_generator ["account", "name", "currency:belongs_to", "user:references"]
assert_file "app/models/account.rb", /belongs_to :currency/
@@ -466,7 +451,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "app/controllers/accounts_controller.rb" do |content|
assert_instance_method :account_params, content do |m|
- assert_match(/permit\(:name, :currency_id\)/, m)
+ assert_match(/permit\(:name, :currency_id, :user_id\)/, m)
end
end
@@ -474,6 +459,50 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_match(/^\W{4}<%= form\.text_field :name %>/, content)
assert_match(/^\W{4}<%= form\.text_field :currency_id %>/, content)
end
+
+ assert_file "app/views/accounts/index.html.erb" do |content|
+ assert_match(/^\W{8}<td><%= account\.name %><\/td>/, content)
+ assert_match(/^\W{8}<td><%= account\.user_id %><\/td>/, content)
+ end
+
+ assert_file "app/views/accounts/show.html.erb" do |content|
+ assert_match(/^\W{2}<%= @account\.name %>/, content)
+ assert_match(/^\W{2}<%= @account\.user_id %>/, content)
+ end
+ end
+
+ def test_scaffold_generator_attachments
+ run_generator ["message", "video:attachment", "photos:attachments", "images:attachments"]
+
+ assert_file "app/models/message.rb", /has_one_attached :video/
+ assert_file "app/models/message.rb", /has_many_attached :photos/
+
+ assert_file "app/controllers/messages_controller.rb" do |content|
+ assert_instance_method :message_params, content do |m|
+ assert_match(/permit\(:video, photos: \[\], images: \[\]\)/, m)
+ end
+ end
+
+ assert_file "app/views/messages/_form.html.erb" do |content|
+ assert_match(/^\W{4}<%= form\.file_field :video %>/, content)
+ assert_match(/^\W{4}<%= form\.file_field :photos, multiple: true %>/, content)
+ end
+ end
+
+ def test_scaffold_generator_database
+ with_secondary_database_configuration do
+ run_generator ["posts", "--database=secondary"]
+
+ assert_migration "db/secondary_migrate/create_posts.rb"
+ end
+ end
+
+ def test_scaffold_generator_database_with_aliases
+ with_secondary_database_configuration do
+ run_generator ["posts", "--db=secondary"]
+
+ assert_migration "db/secondary_migrate/create_posts.rb"
+ end
end
def test_scaffold_generator_password_digest
@@ -514,7 +543,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "test/system/users_test.rb" do |content|
assert_match(/fill_in "Password", with: 'secret'/, content)
- assert_match(/fill_in "Password Confirmation", with: 'secret'/, content)
+ assert_match(/fill_in "Password confirmation", with: 'secret'/, content)
end
assert_file "test/fixtures/users.yml" do |content|
@@ -622,7 +651,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert File.exist?("app/helpers/bukkits/users_helper.rb")
- assert File.exist?("app/assets/javascripts/bukkits/users.js")
assert File.exist?("app/assets/stylesheets/bukkits/users.css")
end
end
@@ -652,7 +680,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_not File.exist?("app/helpers/bukkits/users_helper.rb")
- assert_not File.exist?("app/assets/javascripts/bukkits/users.js")
assert_not File.exist?("app/assets/stylesheets/bukkits/users.css")
end
end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index aa577e4234..26ce487c5f 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -10,9 +10,9 @@ module SharedGeneratorTests
Rails::Generators::AppGenerator.instance_variable_set("@desc", nil)
Kernel.silence_warnings do
- Thor::Base.shell.send(:attr_accessor, :always_force)
+ Thor::Base.shell.attr_accessor :always_force
@shell = Thor::Base.shell.new
- @shell.send(:always_force=, true)
+ @shell.always_force = true
end
end
@@ -83,7 +83,7 @@ module SharedGeneratorTests
def test_template_is_executed_when_supplied_an_https_path
path = "https://gist.github.com/josevalim/103208/raw/"
- template = %{ say "It works!" }.dup
+ template = +%{ say "It works!" }
template.instance_eval "def read; self; end" # Make the string respond to read
check_open = -> *args do
@@ -91,7 +91,7 @@ module SharedGeneratorTests
template
end
- generator([destination_root], template: path).stub(:open, check_open, template) do
+ generator([destination_root], template: path, skip_webpack_install: true).stub(:open, check_open, template) do
generator.stub :bundle_command, nil do
quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) }
end
@@ -99,7 +99,7 @@ module SharedGeneratorTests
end
def test_skip_gemfile
- assert_not_called(generator([destination_root], skip_gemfile: true), :bundle_command) do
+ assert_not_called(generator([destination_root], skip_gemfile: true, skip_webpack_install: true), :bundle_command) do
quietly { generator.invoke_all }
assert_no_file "Gemfile"
end
@@ -127,6 +127,8 @@ module SharedGeneratorTests
"--skip-active-record",
"--skip-active-storage",
"--skip-action-mailer",
+ "--skip-action-mailbox",
+ "--skip-action-text",
"--skip-action-cable",
"--skip-sprockets"
]
@@ -138,6 +140,10 @@ module SharedGeneratorTests
assert_file "#{application_path}/config/application.rb", /^# require\s+["']active_storage\/engine["']/
assert_file "#{application_path}/config/application.rb", /^require\s+["']action_controller\/railtie["']/
assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_mailer\/railtie["']/
+ unless generator_class.name == "Rails::Generators::PluginGenerator"
+ assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_mailbox\/engine["']/
+ assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_text\/engine["']/
+ end
assert_file "#{application_path}/config/application.rb", /^require\s+["']action_view\/railtie["']/
assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_cable\/engine["']/
assert_file "#{application_path}/config/application.rb", /^# require\s+["']sprockets\/railtie["']/
@@ -198,8 +204,10 @@ module SharedGeneratorTests
def test_generator_for_active_storage
run_generator
- assert_file "#{application_path}/app/assets/javascripts/application.js" do |content|
- assert_match(/^\/\/= require activestorage/, content)
+ unless generator_class.name == "Rails::Generators::PluginGenerator"
+ assert_file "#{application_path}/app/javascript/packs/application.js" do |content|
+ assert_match(/^require\("@rails\/activestorage"\)\.start\(\)/, content)
+ end
end
assert_file "#{application_path}/config/environments/development.rb" do |content|
@@ -228,8 +236,8 @@ module SharedGeneratorTests
assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/
- assert_file "#{application_path}/app/assets/javascripts/application.js" do |content|
- assert_no_match(/^\/\/= require activestorage/, content)
+ assert_file "#{application_path}/app/javascript/packs/application.js" do |content|
+ assert_no_match(/activestorage/, content)
end
assert_file "#{application_path}/config/environments/development.rb" do |content|
@@ -258,8 +266,8 @@ module SharedGeneratorTests
assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/
- assert_file "#{application_path}/app/assets/javascripts/application.js" do |content|
- assert_no_match(/^\/\/= require activestorage/, content)
+ assert_file "#{application_path}/app/javascript/packs/application.js" do |content|
+ assert_no_match(/^require\("@rails\/activestorage"\)\.start\(\)/, content)
end
assert_file "#{application_path}/config/environments/development.rb" do |content|
@@ -303,8 +311,8 @@ module SharedGeneratorTests
run_generator [destination_root, "--skip-action-cable"]
assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
assert_no_file "#{application_path}/config/cable.yml"
- assert_no_file "#{application_path}/app/assets/javascripts/cable.js"
- assert_no_directory "#{application_path}/app/assets/javascripts/channels"
+ assert_no_file "#{application_path}/app/javascript/consumer.js"
+ assert_no_directory "#{application_path}/app/javascript/channels"
assert_no_directory "#{application_path}/app/channels"
assert_file "Gemfile" do |content|
assert_no_match(/redis/, content)
@@ -320,8 +328,6 @@ module SharedGeneratorTests
assert_file "Gemfile" do |content|
assert_no_match(/sass-rails/, content)
- assert_no_match(/uglifier/, content)
- assert_no_match(/coffee-rails/, content)
end
assert_file "#{application_path}/config/environments/development.rb" do |content|
@@ -330,35 +336,26 @@ module SharedGeneratorTests
assert_file "#{application_path}/config/environments/production.rb" do |content|
assert_no_match(/config\.assets\.digest/, content)
- assert_no_match(/config\.assets\.js_compressor/, content)
assert_no_match(/config\.assets\.css_compressor/, content)
assert_no_match(/config\.assets\.compile/, content)
end
end
def test_generator_for_yarn
+ skip "#34009 disabled JS by default for plugins" if generator_class.name == "Rails::Generators::PluginGenerator"
run_generator
assert_file "#{application_path}/package.json", /dependencies/
+ assert_file "#{application_path}/bin/yarn"
assert_file "#{application_path}/config/initializers/assets.rb", /node_modules/
-
- assert_file ".gitignore" do |content|
- assert_match(/node_modules/, content)
- assert_match(/yarn-error\.log/, content)
- end
end
def test_generator_for_yarn_skipped
- run_generator([destination_root, "--skip-yarn"])
+ run_generator([destination_root, "--skip-javascript"])
assert_no_file "#{application_path}/package.json"
assert_no_file "#{application_path}/bin/yarn"
assert_file "#{application_path}/config/initializers/assets.rb" do |content|
assert_no_match(/node_modules/, content)
end
-
- assert_file ".gitignore" do |content|
- assert_no_match(/node_modules/, content)
- assert_no_match(/yarn-error\.log/, content)
- end
end
end
diff --git a/railties/test/generators/system_test_generator_test.rb b/railties/test/generators/system_test_generator_test.rb
index efa70a050b..5742ba444d 100644
--- a/railties/test/generators/system_test_generator_test.rb
+++ b/railties/test/generators/system_test_generator_test.rb
@@ -16,4 +16,18 @@ class SystemTestGeneratorTest < Rails::Generators::TestCase
run_generator %w(admin/user)
assert_file "test/system/admin/users_test.rb", /class Admin::UsersTest < ApplicationSystemTestCase/
end
+
+ def test_test_name_is_pluralized
+ run_generator %w(user)
+
+ assert_no_file "test/system/user_test.rb"
+ assert_file "test/system/users_test.rb"
+ end
+
+ def test_test_suffix_is_not_duplicated
+ run_generator %w(users_test)
+
+ assert_no_file "test/system/users_test_test.rb"
+ assert_file "test/system/users_test.rb"
+ end
end
diff --git a/railties/test/generators/test_runner_in_engine_test.rb b/railties/test/generators/test_runner_in_engine_test.rb
index 0e15b5e388..bd102a32b5 100644
--- a/railties/test/generators/test_runner_in_engine_test.rb
+++ b/railties/test/generators/test_runner_in_engine_test.rb
@@ -19,7 +19,7 @@ class TestRunnerInEngineTest < ActiveSupport::TestCase
create_test_file "post", pass: false
output = run_test_command("test/post_test.rb")
- expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/post_test\.rb:6\]:\nwups!\n\nbin/rails test test/post_test\.rb:4}
+ expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/post_test\.rb:6\]:\nwups!\n\nrails test test/post_test\.rb:4}
assert_match expect, output
end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index f98c1f78f7..abdc04a8d3 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -28,6 +28,7 @@ class GeneratorsTest < Rails::Generators::TestCase
output = capture(:stdout) { Rails::Generators.invoke name }
assert_match "Could not find generator '#{name}'", output
assert_match "`rails generate --help`", output
+ assert_no_match "Maybe you meant", output
end
def test_generator_suggestions
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 516c457e48..3fcfaa9623 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -16,6 +16,9 @@ require "active_support/testing/autorun"
require "active_support/testing/stream"
require "active_support/testing/method_call_assertions"
require "active_support/test_case"
+require "minitest/retry"
+
+Minitest::Retry.use!(verbose: false, retry_count: 1)
RAILS_FRAMEWORK_ROOT = File.expand_path("../../..", __dir__)
@@ -30,11 +33,11 @@ require "rails/secrets"
module TestHelpers
module Paths
def app_template_path
- File.join Dir.tmpdir, "app_template"
+ File.join RAILS_FRAMEWORK_ROOT, "tmp/templates/app_template"
end
def tmp_path(*args)
- @tmp_path ||= File.realpath(Dir.mktmpdir)
+ @tmp_path ||= File.realpath(Dir.mktmpdir(nil, File.join(RAILS_FRAMEWORK_ROOT, "tmp")))
File.join(@tmp_path, *args)
end
@@ -71,7 +74,7 @@ module TestHelpers
end
def extract_body(response)
- "".dup.tap do |body|
+ (+"").tap do |body|
response[2].each { |chunk| body << chunk }
end
end
@@ -120,30 +123,59 @@ module TestHelpers
adapter: sqlite3
pool: 5
timeout: 5000
+ variables:
+ statement_timeout: 1000
development:
primary:
<<: *default
database: db/development.sqlite3
+ primary_readonly:
+ <<: *default
+ database: db/development.sqlite3
+ replica: true
animals:
<<: *default
database: db/development_animals.sqlite3
migrations_paths: db/animals_migrate
+ animals_readonly:
+ <<: *default
+ database: db/development_animals.sqlite3
+ migrations_paths: db/animals_migrate
+ replica: true
test:
primary:
<<: *default
database: db/test.sqlite3
+ primary_readonly:
+ <<: *default
+ database: db/test.sqlite3
+ replica: true
animals:
<<: *default
database: db/test_animals.sqlite3
migrations_paths: db/animals_migrate
+ animals_readonly:
+ <<: *default
+ database: db/test_animals.sqlite3
+ migrations_paths: db/animals_migrate
+ replica: true
production:
primary:
<<: *default
database: db/production.sqlite3
+ primary_readonly:
+ <<: *default
+ database: db/production.sqlite3
+ replica: true
animals:
<<: *default
database: db/production_animals.sqlite3
migrations_paths: db/animals_migrate
+ animals_readonly:
+ <<: *default
+ database: db/production_animals.sqlite3
+ migrations_paths: db/animals_migrate
+ readonly: true
YAML
end
else
@@ -167,10 +199,10 @@ module TestHelpers
end
add_to_config <<-RUBY
+ config.hosts << proc { true }
config.eager_load = false
config.session_store :cookie_store, key: "_myapp_session"
config.active_support.deprecation = :log
- config.active_support.test_order = :random
config.action_controller.allow_forgery_protection = false
config.log_level = :info
RUBY
@@ -191,11 +223,12 @@ module TestHelpers
@app = Class.new(Rails::Application) do
def self.name; "RailtiesTestApp"; end
end
+ @app.config.hosts << proc { true }
@app.config.eager_load = false
@app.config.session_store :cookie_store, key: "_myapp_session"
@app.config.active_support.deprecation = :log
- @app.config.active_support.test_order = :random
@app.config.log_level = :info
+ @app.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
yield @app if block_given?
@app.initialize!
@@ -391,6 +424,10 @@ module TestHelpers
file_name
end
+ def app_dir(path)
+ FileUtils.mkdir_p("#{app_path}/#{path}")
+ end
+
def remove_file(path)
FileUtils.rm_rf "#{app_path}/#{path}"
end
@@ -400,7 +437,7 @@ module TestHelpers
end
def use_frameworks(arr)
- to_remove = [:actionmailer, :activerecord, :activestorage, :activejob] - arr
+ to_remove = [:actionmailer, :activerecord, :activestorage, :activejob, :actionmailbox] - arr
if to_remove.include?(:activerecord)
remove_from_config "config.active_record.*"
@@ -424,18 +461,21 @@ module TestHelpers
end
end
end
+
+ module Reload
+ def reload
+ ActiveSupport::Dependencies.clear
+ end
+ end
end
class ActiveSupport::TestCase
include TestHelpers::Paths
include TestHelpers::Rack
include TestHelpers::Generation
+ include TestHelpers::Reload
include ActiveSupport::Testing::Stream
include ActiveSupport::Testing::MethodCallAssertions
-
- def frozen_error_class
- Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError
- end
end
# Create a scope and build a fixture rails app
@@ -444,17 +484,31 @@ Module.new do
# Build a rails app
FileUtils.rm_rf(app_template_path)
- FileUtils.mkdir(app_template_path)
+ FileUtils.mkdir_p(app_template_path)
- `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-gemfile --skip-listen --no-rc`
+ `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-bundle --skip-listen --no-rc --skip-webpack-install`
File.open("#{app_template_path}/config/boot.rb", "w") do |f|
f.puts "require 'rails/all'"
end
+ unless File.exist?("#{RAILS_FRAMEWORK_ROOT}/actionview/lib/assets/compiled/rails-ujs.js")
+ Dir.chdir("#{RAILS_FRAMEWORK_ROOT}/actionview") { `yarn build` }
+ end
+
+ assets_path = "#{RAILS_FRAMEWORK_ROOT}/railties/test/isolation/assets"
+ unless Dir.exist?("#{assets_path}/node_modules")
+ Dir.chdir(assets_path) { `yarn install` }
+ end
+ FileUtils.cp("#{assets_path}/package.json", "#{app_template_path}/package.json")
+ FileUtils.cp("#{assets_path}/config/webpacker.yml", "#{app_template_path}/config/webpacker.yml")
+ FileUtils.cp_r("#{assets_path}/config/webpack", "#{app_template_path}/config/webpack")
+ FileUtils.ln_s("#{assets_path}/node_modules", "#{app_template_path}/node_modules")
+ FileUtils.chdir(app_template_path) { `bin/rails webpacker:binstubs` }
+
# Fake 'Bundler.require' -- we run using the repo's Gemfile, not an
# app-specific one: we don't want to require every gem that lists.
contents = File.read("#{app_template_path}/config/application.rb")
- contents.sub!(/^Bundler\.require.*/, "%w(turbolinks).each { |r| require r }")
+ contents.sub!(/^Bundler\.require.*/, "%w(turbolinks webpacker).each { |r| require r }")
File.write("#{app_template_path}/config/application.rb", contents)
require "rails"
@@ -467,6 +521,8 @@ Module.new do
require "action_view"
require "active_storage"
require "action_cable"
+ require "action_mailbox"
+ require "action_text"
require "sprockets"
require "action_view/helpers"
diff --git a/railties/test/isolation/assets/config/webpack/development.js b/railties/test/isolation/assets/config/webpack/development.js
new file mode 100644
index 0000000000..395290f431
--- /dev/null
+++ b/railties/test/isolation/assets/config/webpack/development.js
@@ -0,0 +1,3 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
+const { environment } = require('@rails/webpacker')
+module.exports = environment.toWebpackConfig()
diff --git a/railties/test/isolation/assets/config/webpack/production.js b/railties/test/isolation/assets/config/webpack/production.js
new file mode 100644
index 0000000000..d064a6a7fb
--- /dev/null
+++ b/railties/test/isolation/assets/config/webpack/production.js
@@ -0,0 +1,3 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'production'
+const { environment } = require('@rails/webpacker')
+module.exports = environment.toWebpackConfig()
diff --git a/railties/test/isolation/assets/config/webpack/test.js b/railties/test/isolation/assets/config/webpack/test.js
new file mode 100644
index 0000000000..395290f431
--- /dev/null
+++ b/railties/test/isolation/assets/config/webpack/test.js
@@ -0,0 +1,3 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
+const { environment } = require('@rails/webpacker')
+module.exports = environment.toWebpackConfig()
diff --git a/railties/test/isolation/assets/config/webpacker.yml b/railties/test/isolation/assets/config/webpacker.yml
new file mode 100644
index 0000000000..0b1f43a407
--- /dev/null
+++ b/railties/test/isolation/assets/config/webpacker.yml
@@ -0,0 +1,8 @@
+default: &default
+ check_yarn_integrity: false
+development:
+ <<: *default
+test:
+ <<: *default
+production:
+ <<: *default
diff --git a/railties/test/isolation/assets/package.json b/railties/test/isolation/assets/package.json
new file mode 100644
index 0000000000..7c34450fe0
--- /dev/null
+++ b/railties/test/isolation/assets/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "dummy",
+ "private": true,
+ "dependencies": {
+ "@rails/actioncable": "file:../../../../actioncable",
+ "@rails/activestorage": "file:../../../../activestorage",
+ "@rails/ujs": "file:../../../../actionview",
+ "@rails/webpacker": "https://github.com/rails/webpacker.git",
+ "turbolinks": "^5.2.0"
+ }
+}
diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb
index 849b183b37..0c1ee259b0 100644
--- a/railties/test/path_generation_test.rb
+++ b/railties/test/path_generation_test.rb
@@ -66,7 +66,7 @@ class PathGenerationTest < ActiveSupport::TestCase
super
app = self
@routes = TestSet.new ->(c) { app.controller = c }
- secrets.secret_token = "foo"
+ secrets.secret_key_base = "foo"
end
def app; routes; end
}
diff --git a/railties/test/rack_logger_test.rb b/railties/test/rack_logger_test.rb
index 6e8f333e1d..ac37062e6d 100644
--- a/railties/test/rack_logger_test.rb
+++ b/railties/test/rack_logger_test.rb
@@ -56,7 +56,7 @@ module Rails
end
def test_notification
- logger = TestLogger.new {}
+ logger = TestLogger.new { }
assert_difference("subscriber.starts.length") do
assert_difference("subscriber.finishes.length") do
diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb
index 878a238f8d..185a2f9f09 100644
--- a/railties/test/rails_info_controller_test.rb
+++ b/railties/test/rails_info_controller_test.rb
@@ -10,6 +10,7 @@ end
class InfoControllerTest < ActionController::TestCase
tests Rails::InfoController
+ Rails.application.config.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
def setup
Rails.application.routes.draw do
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 4ac8f8d741..fe5c62c07d 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -87,11 +87,10 @@ module RailtiesTest
end
RUBY
+ restrict_frameworks
boot_rails
Dir.chdir(app_path) do
- # Install Active Storage migration file first so as not to affect test.
- `bundle exec rake active_storage:install`
output = `bundle exec rake bukkits:install:migrations`
["CreateUsers", "AddLastNameToUsers", "CreateSessions"].each do |migration_name|
@@ -174,11 +173,10 @@ module RailtiesTest
class CreateKeys < ActiveRecord::Migration::Current; end
RUBY
+ restrict_frameworks
boot_rails
Dir.chdir(app_path) do
- # Install Active Storage migration file first so as not to affect test.
- `bundle exec rake active_storage:install`
output = `bundle exec rake railties:install:migrations`.split("\n")
assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.first)
@@ -706,25 +704,27 @@ YAML
RUBY
@plugin.write "app/controllers/bukkits/foo_controller.rb", <<-RUBY
- class Bukkits::FooController < ActionController::Base
- def index
- render inline: "<%= help_the_engine %>"
- end
+ module Bukkits
+ class FooController < ActionController::Base
+ def index
+ render inline: "<%= help_the_engine %>"
+ end
- def show
- render plain: foo_path
- end
+ def show
+ render plain: foo_path
+ end
- def from_app
- render inline: "<%= (self.respond_to?(:bar_path) || self.respond_to?(:something)) %>"
- end
+ def from_app
+ render inline: "<%= (self.respond_to?(:bar_path) || self.respond_to?(:something)) %>"
+ end
- def routes_helpers_in_view
- render inline: "<%= foo_path %>, <%= main_app.bar_path %>"
- end
+ def routes_helpers_in_view
+ render inline: "<%= foo_path %>, <%= main_app.bar_path %>"
+ end
- def polymorphic_path_without_namespace
- render plain: polymorphic_path(Post.new)
+ def polymorphic_path_without_namespace
+ render plain: polymorphic_path(Post.new)
+ end
end
end
RUBY
@@ -879,6 +879,31 @@ YAML
assert Bukkits::Engine.config.bukkits_seeds_loaded
end
+ test "jobs are ran inline while loading seeds with async adapter configured" do
+ app_file "db/seeds.rb", <<-RUBY
+ Rails.application.config.seed_queue_adapter = ActiveJob::Base.queue_adapter
+ RUBY
+
+ boot_rails
+ Rails.application.load_seed
+
+ assert_instance_of ActiveJob::QueueAdapters::InlineAdapter, Rails.application.config.seed_queue_adapter
+ assert_instance_of ActiveJob::QueueAdapters::AsyncAdapter, ActiveJob::Base.queue_adapter
+ end
+
+ test "jobs are ran with original adapter while loading seeds with custom adapter configured" do
+ app_file "db/seeds.rb", <<-RUBY
+ Rails.application.config.seed_queue_adapter = ActiveJob::Base.queue_adapter
+ RUBY
+
+ boot_rails
+ Rails.application.config.active_job.queue_adapter = :delayed_job
+ Rails.application.load_seed
+
+ assert_instance_of ActiveJob::QueueAdapters::DelayedJobAdapter, Rails.application.config.seed_queue_adapter
+ assert_instance_of ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter
+ end
+
test "skips nonexistent seed data" do
FileUtils.rm "#{app_path}/db/seeds.rb"
boot_rails
@@ -1497,5 +1522,25 @@ YAML
def app
Rails.application
end
+
+ # Restrict frameworks to load in order to avoid engine frameworks affect tests.
+ def restrict_frameworks
+ remove_from_config("require 'rails/all'")
+ remove_from_config("require_relative 'boot'")
+ remove_from_env_config("development", "config.active_storage.*")
+ frameworks = <<~RUBY
+ require "rails"
+ require "active_model/railtie"
+ require "active_job/railtie"
+ require "active_record/railtie"
+ require "action_controller/railtie"
+ require "action_mailer/railtie"
+ require "action_view/railtie"
+ require "sprockets/railtie"
+ require "rails/test_unit/railtie"
+ RUBY
+ environment = File.read("#{app_path}/config/application.rb")
+ File.open("#{app_path}/config/application.rb", "w") { |f| f.puts frameworks + "\n" + environment }
+ end
end
end
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index 48f0fbc80f..9755823e51 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -232,7 +232,7 @@ module ApplicationTests
get "/someone/blog/generate_application_route"
assert_equal "/", last_response.body
- get "/somone/blog/application_route_in_view"
+ get "/someone/blog/application_route_in_view"
assert_equal "/", last_response.body
# test generating engine's route from other engine
@@ -258,8 +258,8 @@ module ApplicationTests
assert_equal "http://example.org/anonymous/blog/posts/44", last_response.body
# test that correct path is generated for the same polymorphic_path call in an engine
- get "/somone/blog/engine_polymorphic_path"
- assert_equal "/somone/blog/posts/44", last_response.body
+ get "/someone/blog/engine_polymorphic_path"
+ assert_equal "/someone/blog/posts/44", last_response.body
# and in an application
get "/application_polymorphic_path"
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index 7c3d1e3759..b9725ca0ad 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -25,12 +25,10 @@ module RailtiesTest
end
test "Railtie provides railtie_name" do
- begin
- class ::FooBarBaz < Rails::Railtie ; end
- assert_equal "foo_bar_baz", FooBarBaz.railtie_name
- ensure
- Object.send(:remove_const, :"FooBarBaz")
- end
+ class ::FooBarBaz < Rails::Railtie ; end
+ assert_equal "foo_bar_baz", FooBarBaz.railtie_name
+ ensure
+ Object.send(:remove_const, :"FooBarBaz")
end
test "railtie_name can be set manually" do
@@ -203,14 +201,12 @@ module RailtiesTest
end
test "we can change our environment if we want to" do
- begin
- original_env = Rails.env
- Rails.env = "foo"
- assert_equal("foo", Rails.env)
- ensure
- Rails.env = original_env
- assert_equal(original_env, Rails.env)
- end
+ original_env = Rails.env
+ Rails.env = "foo"
+ assert_equal("foo", Rails.env)
+ ensure
+ Rails.env = original_env
+ assert_equal(original_env, Rails.env)
end
end
end
diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb
index 06877bc76a..133d851819 100644
--- a/railties/test/secrets_test.rb
+++ b/railties/test/secrets_test.rb
@@ -35,15 +35,13 @@ class Rails::SecretsTest < ActiveSupport::TestCase
test "reading with ENV variable" do
run_secrets_generator do
- begin
- old_key = ENV["RAILS_MASTER_KEY"]
- ENV["RAILS_MASTER_KEY"] = IO.binread("config/secrets.yml.key").strip
- FileUtils.rm("config/secrets.yml.key")
-
- assert_match "# production:\n# external_api_key:", Rails::Secrets.read
- ensure
- ENV["RAILS_MASTER_KEY"] = old_key
- end
+ old_key = ENV["RAILS_MASTER_KEY"]
+ ENV["RAILS_MASTER_KEY"] = IO.binread("config/secrets.yml.key").strip
+ FileUtils.rm("config/secrets.yml.key")
+
+ assert_match "# production:\n# external_api_key:", Rails::Secrets.read
+ ensure
+ ENV["RAILS_MASTER_KEY"] = old_key
end
end
diff --git a/railties/test/test_unit/reporter_test.rb b/railties/test/test_unit/reporter_test.rb
index 91cb47779b..81b7ab19a1 100644
--- a/railties/test/test_unit/reporter_test.rb
+++ b/railties/test/test_unit/reporter_test.rb
@@ -18,7 +18,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
@reporter.record(failed_test)
@reporter.report
- assert_match %r{^bin/rails test .*test/test_unit/reporter_test\.rb:\d+$}, @output.string
+ assert_match %r{^rails test .*test/test_unit/reporter_test\.rb:\d+$}, @output.string
assert_rerun_snippet_count 1
end
@@ -64,7 +64,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
@reporter.record(failed_test)
@reporter.report
- expect = %r{\AF\n\nFailure:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nboo\n\nbin/rails test test/test_unit/reporter_test\.rb:\d+\n\n\z}
+ expect = %r{\AF\n\nFailure:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nboo\n\nrails test test/test_unit/reporter_test\.rb:\d+\n\n\z}
assert_match expect, @output.string
end
@@ -72,7 +72,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
@reporter.record(errored_test)
@reporter.report
- expect = %r{\AE\n\nError:\nTestUnitReporterTest::ExampleTest#woot:\nArgumentError: wups\n \n\nbin/rails test .*test/test_unit/reporter_test\.rb:\d+\n\n\z}
+ expect = %r{\AE\n\nError:\nTestUnitReporterTest::ExampleTest#woot:\nArgumentError: wups\n \n\nrails test .*test/test_unit/reporter_test\.rb:\d+\n\n\z}
assert_match expect, @output.string
end
@@ -81,7 +81,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
verbose.record(skipped_test)
verbose.report
- expect = %r{\ATestUnitReporterTest::ExampleTest#woot = 10\.00 s = S\n\n\nSkipped:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nskipchurches, misstemples\n\nbin/rails test test/test_unit/reporter_test\.rb:\d+\n\n\z}
+ expect = %r{\ATestUnitReporterTest::ExampleTest#woot = 10\.00 s = S\n\n\nSkipped:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nskipchurches, misstemples\n\nrails test test/test_unit/reporter_test\.rb:\d+\n\n\z}
assert_match expect, @output.string
end
@@ -159,7 +159,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
private
def assert_rerun_snippet_count(snippet_count)
- assert_equal snippet_count, @output.string.scan(%r{^bin/rails test }).size
+ assert_equal snippet_count, @output.string.scan(%r{^rails test }).size
end
def failed_test