aboutsummaryrefslogtreecommitdiffstats
path: root/railties
diff options
context:
space:
mode:
Diffstat (limited to 'railties')
-rw-r--r--railties/.gitignore1
-rw-r--r--railties/CHANGELOG.md313
-rw-r--r--railties/RDOC_MAIN.rdoc6
-rw-r--r--railties/README.rdoc2
-rw-r--r--railties/Rakefile63
-rw-r--r--railties/lib/rails.rb5
-rw-r--r--railties/lib/rails/application.rb75
-rw-r--r--railties/lib/rails/application/bootstrap.rb12
-rw-r--r--railties/lib/rails/application/configuration.rb48
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb1
-rw-r--r--railties/lib/rails/application/dummy_erb_compiler.rb19
-rw-r--r--railties/lib/rails/application/finisher.rb48
-rw-r--r--railties/lib/rails/autoloaders.rb44
-rw-r--r--railties/lib/rails/command/behavior.rb5
-rw-r--r--railties/lib/rails/command/environment_argument.rb11
-rw-r--r--railties/lib/rails/commands/console/console_command.rb6
-rw-r--r--railties/lib/rails/commands/credentials/USAGE2
-rw-r--r--railties/lib/rails/commands/credentials/credentials_command.rb20
-rw-r--r--railties/lib/rails/commands/dbconsole/dbconsole_command.rb26
-rw-r--r--railties/lib/rails/commands/dev/dev_command.rb6
-rw-r--r--railties/lib/rails/commands/encrypted/USAGE28
-rw-r--r--railties/lib/rails/commands/encrypted/encrypted_command.rb1
-rw-r--r--railties/lib/rails/commands/initializers/initializers_command.rb7
-rw-r--r--railties/lib/rails/commands/notes/notes_command.rb2
-rw-r--r--railties/lib/rails/commands/runner/runner_command.rb10
-rw-r--r--railties/lib/rails/commands/server/server_command.rb14
-rw-r--r--railties/lib/rails/engine.rb45
-rw-r--r--railties/lib/rails/gem_version.rb4
-rw-r--r--railties/lib/rails/generators.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb4
-rw-r--r--railties/lib/rails/generators/app_name.rb4
-rw-r--r--railties/lib/rails/generators/database.rb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt9
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt8
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb46
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile.tt6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/update.tt33
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt15
-rw-r--r--railties/lib/rails/generators/rails/app/templates/ruby-version.tt2
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb7
-rw-r--r--railties/lib/rails/generators/rails/db/system/change/change_generator.rb14
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb15
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb14
-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/model/templates/fixtures.yml.tt4
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb9
-rw-r--r--railties/lib/rails/mailers_controller.rb9
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb15
-rw-r--r--railties/lib/rails/tasks.rb1
-rw-r--r--railties/lib/rails/tasks/zeitwerk.rake78
-rw-r--r--railties/railties.gemspec2
-rw-r--r--railties/test/abstract_unit.rb2
-rw-r--r--railties/test/application/asset_debugging_test.rb2
-rw-r--r--railties/test/application/assets_test.rb4
-rw-r--r--railties/test/application/bin_setup_test.rb26
-rw-r--r--railties/test/application/configuration_test.rb293
-rw-r--r--railties/test/application/console_test.rb21
-rw-r--r--railties/test/application/initializers/frameworks_test.rb4
-rw-r--r--railties/test/application/loading_test.rb16
-rw-r--r--railties/test/application/mailer_previews_test.rb10
-rw-r--r--railties/test/application/middleware/exceptions_test.rb18
-rw-r--r--railties/test/application/middleware_test.rb2
-rw-r--r--railties/test/application/multiple_applications_test.rb24
-rw-r--r--railties/test/application/rake/dbs_test.rb212
-rw-r--r--railties/test/application/rake/multi_dbs_test.rb80
-rw-r--r--railties/test/application/rake/routes_test.rb1
-rw-r--r--railties/test/application/rake_test.rb4
-rw-r--r--railties/test/application/runner_test.rb8
-rw-r--r--railties/test/application/server_test.rb4
-rw-r--r--railties/test/application/test_runner_test.rb26
-rw-r--r--railties/test/application/test_test.rb2
-rw-r--r--railties/test/application/url_generation_test.rb1
-rw-r--r--railties/test/application/zeitwerk_integration_test.rb350
-rw-r--r--railties/test/backtrace_cleaner_test.rb12
-rw-r--r--railties/test/commands/console_test.rb2
-rw-r--r--railties/test/commands/credentials_test.rb27
-rw-r--r--railties/test/commands/dbconsole_test.rb24
-rw-r--r--railties/test/commands/initializers_test.rb20
-rw-r--r--railties/test/commands/notes_test.rb41
-rw-r--r--railties/test/commands/routes_test.rb43
-rw-r--r--railties/test/commands/server_test.rb10
-rw-r--r--railties/test/generators/actions_test.rb2
-rw-r--r--railties/test/generators/api_app_generator_test.rb8
-rw-r--r--railties/test/generators/app_generator_test.rb152
-rw-r--r--railties/test/generators/db_system_change_generator_test.rb21
-rw-r--r--railties/test/generators/generated_attribute_test.rb29
-rw-r--r--railties/test/generators/migration_generator_test.rb78
-rw-r--r--railties/test/generators/model_generator_test.rb108
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb27
-rw-r--r--railties/test/generators/scaffold_generator_test.rb56
-rw-r--r--railties/test/generators/shared_generator_tests.rb5
-rw-r--r--railties/test/isolation/abstract_unit.rb41
-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/rails_info_controller_test.rb1
-rw-r--r--railties/test/railties/engine_test.rb77
104 files changed, 2354 insertions, 712 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 19f4de8a1d..7bc7391f9e 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,314 +1,3 @@
-* 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 prevent
- 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 setup 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`.
-
- *Annie-Claude Côté*
-
-* Deprecate `SOURCE_ANNOTATION_DIRECTORIES` environment variable used by `rails notes`
- through `Rails::SourceAnnotationExtractor::Annotation` in favor of using `config.annotations.register_directories`.
-
- *Annie-Claude Côté*
-
-* Deprecate `rake notes` in favor of `rails notes`.
-
- *Annie-Claude Côté*
-
-* Don't generate unused files in `app:update` task.
-
- Skip the assets' initializer when sprockets isn't loaded.
-
- Skip `config/spring.rb` when spring isn't loaded.
-
- Skip yarn's contents when yarn integration isn't used.
-
- *Tsukuru Tanimichi*
-
-* Make the master.key file read-only for the owner upon generation on
- POSIX-compliant systems.
-
- Previously:
-
- $ ls -l config/master.key
- -rw-r--r-- 1 owner group 32 Jan 1 00:00 master.key
-
- Now:
-
- $ ls -l config/master.key
- -rw------- 1 owner group 32 Jan 1 00:00 master.key
-
- Fixes #32604.
-
- *Jose Luis Duran*
-
-* Deprecate support for using the `HOST` environment to specify the server IP.
-
- The `BINDING` environment should be used instead.
-
- Fixes #29516.
-
- *Yuji Yaginuma*
-
-* Deprecate passing Rack server name as a regular argument to `rails server`.
-
- Previously:
-
- $ bin/rails server thin
-
- There wasn't an explicit option for the Rack server to use, now we have the
- `--using` option with the `-u` short switch.
-
- Now:
-
- $ bin/rails server -u thin
-
- This change also improves the error message if a missing or mistyped rack
- server is given.
-
- *Genadi Samokovarov*
-
-* Add "rails routes --expanded" option to output routes in expanded mode like
- "psql --expanded". Result looks like:
-
- ```
- $ rails routes --expanded
- --[ Route 1 ]------------------------------------------------------------
- Prefix | high_scores
- Verb | GET
- URI | /high_scores(.:format)
- Controller#Action | high_scores#index
- --[ Route 2 ]------------------------------------------------------------
- Prefix | new_high_score
- Verb | GET
- URI | /high_scores/new(.:format)
- Controller#Action | high_scores#new
- ```
-
- *Benoit Tigeot*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *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.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/railties/CHANGELOG.md) for previous changes.
diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc
index 1c4252edd0..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
@@ -79,7 +79,7 @@ and may also be used independently outside \Rails.
* The \README file created within your application.
* {Getting Started with \Rails}[https://guides.rubyonrails.org/getting_started.html].
* {Ruby on \Rails Guides}[https://guides.rubyonrails.org].
- * {The API Documentation}[http://api.rubyonrails.org].
+ * {The API Documentation}[https://api.rubyonrails.org].
* {Ruby on \Rails Tutorial}[https://www.railstutorial.org/book].
== Contributing
@@ -88,7 +88,7 @@ We encourage you to contribute to Ruby on \Rails! Please check out the
{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 1c4725e2d6..51f46d1817 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",
@@ -38,14 +65,24 @@ namespace :test do
test_patterns = dirs.map { |dir| "test/#{dir}/*_test.rb" }
test_files = Dir[*test_patterns].select do |file|
- !file.start_with?("test/fixtures/")
- end.sort
+ !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
- test_files = test_files.each_slice(m).map { |slice| slice[n] }.compact
+ 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|
@@ -58,14 +95,18 @@ namespace :test do
])
puts fake_command
- # We could run these in parallel, but pretty much all of the
- # railties tests already run in parallel, so ¯\_(⊙︿⊙)_/¯
- Process.waitpid fork {
- ARGV.clear.concat test_options
- Rake.application = nil
-
- load file
- }
+ if Process.respond_to?(:fork)
+ # We could run these in parallel, but pretty much all of the
+ # railties tests already run in parallel, so ¯\_(⊙︿⊙)_/¯
+ Process.waitpid fork {
+ ARGV.clear.concat test_options
+ Rake.application = nil
+
+ load file
+ }
+ else
+ Process.wait spawn(fake_command)
+ end
unless $?.success?
failing_files << file
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/application.rb b/railties/lib/rails/application.rb
index 5a924ab8e6..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"
@@ -230,8 +231,8 @@ module Rails
config = YAML.load(ERB.new(yaml.read).result) || {}
config = (config["shared"] || {}).merge(config[env] || {})
- ActiveSupport::OrderedOptions.new.tap do |config_as_ordered_options|
- config_as_ordered_options.update(config.deep_symbolize_keys)
+ ActiveSupport::OrderedOptions.new.tap do |options|
+ options.update(NonSymbolAccessDeprecatedHash.new(config))
end
else
raise "Could not load configuration. No such file - #{yaml}"
@@ -408,14 +409,15 @@ module Rails
# 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
@@ -580,6 +582,22 @@ module Rails
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
@@ -590,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/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index e3c0759f95..50685a4d7a 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -19,14 +19,14 @@ module Rails
initializer :set_eager_load, group: :all do
if config.eager_load.nil?
- warn <<-INFO
-config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
+ warn <<~INFO
+ config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
- * development - set it to false
- * test - set it to false (unless you use a tool that preloads your test environment)
- * production - set it to true
+ * development - set it to false
+ * test - set it to false (unless you use a tool that preloads your test environment)
+ * production - set it to true
-INFO
+ INFO
config.eager_load = config.cache_classes
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index b7838f7e32..0b758dd3dd 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -18,9 +18,10 @@ module Rails
: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, :credentials
+ :content_security_policy_nonce_generator, :require_master_key, :credentials,
+ :disable_sandbox, :add_autoload_paths_to_load_path
- attr_reader :encoding, :api_only, :loaded_config_version
+ attr_reader :encoding, :api_only, :loaded_config_version, :autoloader
def initialize(*)
super
@@ -64,6 +65,9 @@ module Rails
@credentials = ActiveSupport::OrderedOptions.new
@credentials.content_path = default_credentials_content_path
@credentials.key_path = default_credentials_key_path
+ @autoloader = :classic
+ @disable_sandbox = false
+ @add_autoload_paths_to_load_path = true
end
def load_defaults(target_version)
@@ -117,6 +121,8 @@ 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
@@ -137,6 +143,12 @@ module Rails
active_storage.queues.analysis = :active_storage_analysis
active_storage.queues.purge = :active_storage_purge
end
+
+ if respond_to?(:active_record)
+ active_record.collection_cache_versioning = true
+ end
+ when "6.1"
+ load_defaults "6.0"
else
raise "Unknown version #{target_version.to_s.inspect}"
end
@@ -181,6 +193,26 @@ module Rails
end
end
+ # 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:
+ 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
# values stored in <tt>config/database.yml</tt>.
def database_configuration
@@ -267,6 +299,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
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index 193cc59f3a..9800b19274 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -49,6 +49,7 @@ module Rails
middleware.use ::Rails::Rack::Logger, config.log_tags
middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
middleware.use ::ActionDispatch::DebugExceptions, app, config.debug_exception_response_format
+ middleware.use ::ActionDispatch::ActionableExceptions
unless config.cache_classes
middleware.use ::ActionDispatch::Reloader, app.reloader
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..109c560c80 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+require "active_support/core_ext/string/inflections"
+require "active_support/core_ext/array/conversions"
+
module Rails
class Application
module Finisher
@@ -21,6 +24,47 @@ module Rails
end
end
+ # This will become an error if/when we remove classic mode. The plan is
+ # autoloaders won't be configured up to this point in the finisher, so
+ # constants just won't be found, raising regular NameError exceptions.
+ initializer :warn_if_autoloaded, before: :let_zeitwerk_take_over do
+ next if config.cache_classes
+ next if ActiveSupport::Dependencies.autoloaded_constants.empty?
+
+ autoloaded = ActiveSupport::Dependencies.autoloaded_constants
+ constants = "constant".pluralize(autoloaded.size)
+ enum = autoloaded.to_sentence
+ have = autoloaded.size == 1 ? "has" : "have"
+ these = autoloaded.size == 1 ? "This" : "These"
+ example = autoloaded.first
+ example_klass = example.constantize.class
+
+ ActiveSupport::DescendantsTracker.clear
+ ActiveSupport::Dependencies.clear
+
+ ActiveSupport::Deprecation.warn(<<~WARNING)
+ Initialization autoloaded the #{constants} #{enum}.
+
+ Being able to do this is deprecated. Autoloading during initialization is going
+ to be an error condition in future versions of Rails.
+
+ Reloading does not reboot the application, and therefore code executed during
+ initialization does not run again. So, if you reload #{example}, for example,
+ the expected changes won't be reflected in that stale #{example_klass} object.
+
+ #{these} autoloaded #{constants} #{have} been unloaded.
+
+ Please, check the "Autoloading and Reloading Constants" guide for solutions.
+ WARNING
+ 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 +110,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/command/behavior.rb b/railties/lib/rails/command/behavior.rb
index 7f32b04cf1..7fb2a99e67 100644
--- a/railties/lib/rails/command/behavior.rb
+++ b/railties/lib/rails/command/behavior.rb
@@ -71,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 fdc5ee92d9..9945fd1430 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,16 +9,18 @@ module Rails
extend ActiveSupport::Concern
included do
- class_option :environment, aliases: "-e", type: :string,
- desc: "Specifies the environment to run this console under (test/development/production)."
+ no_commands do
+ class_attribute :environment_desc, default: "Specifies the environment to run this #{self.command_name} under (test/development/production)."
+ end
+ class_option :environment, aliases: "-e", type: :string, desc: environment_desc
end
private
- def extract_environment_option_from_argument
+ 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/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 f7268a64d1..c8d3fb9eda 100644
--- a/railties/lib/rails/commands/credentials/USAGE
+++ b/railties/lib/rails/commands/credentials/USAGE
@@ -42,7 +42,7 @@ from leaking.
=== Environment Specific Credentials
The `credentials` command supports passing an `--environment` option to create an
-environment specific override. That override will takes precedence over the
+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
diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb
index 54ccd97506..e23a1b3008 100644
--- a/railties/lib/rails/commands/credentials/credentials_command.rb
+++ b/railties/lib/rails/commands/credentials/credentials_command.rb
@@ -2,14 +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
- class_option :environment, aliases: "-e", type: :string,
- desc: "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
+ self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
no_commands do
def help
@@ -20,6 +21,7 @@ module Rails
end
def edit
+ extract_environment_option_from_argument(default_environment: nil)
require_application!
ensure_editor_available(command: "bin/rails credentials:edit") || (return)
@@ -37,6 +39,7 @@ module Rails
end
def show
+ extract_environment_option_from_argument(default_environment: nil)
require_application!
say credentials.read.presence || missing_credentials_message
@@ -53,7 +56,11 @@ module Rails
end
def ensure_credentials_have_been_added
- encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
+ 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
@@ -93,6 +100,13 @@ module Rails
Rails::Generators::EncryptedFileGenerator.new
end
+
+ def credentials_generator
+ require "rails/generators"
+ require "rails/generators/rails/credentials/credentials_generator"
+
+ Rails::Generators::CredentialsGenerator.new
+ 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 0fac7d34a0..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
@@ -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
index a3f02f3172..9b2cb2b04a 100644
--- a/railties/lib/rails/commands/dev/dev_command.rb
+++ b/railties/lib/rails/commands/dev/dev_command.rb
@@ -5,8 +5,10 @@ require "rails/dev_caching"
module Rails
module Command
class DevCommand < Base # :nodoc:
- def help
- say "rails dev:cache # Toggle development mode caching on/off."
+ no_commands do
+ def help
+ say "rails dev:cache # Toggle development mode caching on/off."
+ end
end
def cache
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 8d5947652a..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
diff --git a/railties/lib/rails/commands/initializers/initializers_command.rb b/railties/lib/rails/commands/initializers/initializers_command.rb
index 33596177af..bd2f3bed67 100644
--- a/railties/lib/rails/commands/initializers/initializers_command.rb
+++ b/railties/lib/rails/commands/initializers/initializers_command.rb
@@ -1,10 +1,17 @@
# 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|
diff --git a/railties/lib/rails/commands/notes/notes_command.rb b/railties/lib/rails/commands/notes/notes_command.rb
index 64b339b3cd..94cf183855 100644
--- a/railties/lib/rails/commands/notes/notes_command.rb
+++ b/railties/lib/rails/commands/notes/notes_command.rb
@@ -5,7 +5,7 @@ require "rails/source_annotation_extractor"
module Rails
module Command
class NotesCommand < Base # :nodoc:
- class_option :annotations, aliases: "-a", desc: "Filter by specific annotations, e.g. Foobar TODO", type: :array, default: %w(OPTIMIZE FIXME TODO)
+ class_option :annotations, aliases: "-a", desc: "Filter by specific annotations, e.g. Foobar TODO", type: :array, default: Rails::SourceAnnotationExtractor::Annotation.tags
def perform(*)
require_application_and_environment!
diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb
index cb693bcf34..40fb5e4d89 100644
--- a/railties/lib/rails/commands/runner/runner_command.rb
+++ b/railties/lib/rails/commands/runner/runner_command.rb
@@ -1,11 +1,13 @@
# 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
@@ -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
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 47c3f05bb3..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
@@ -91,6 +92,8 @@ 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)
@@ -109,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,
@@ -130,6 +131,7 @@ module Rails
end
def perform
+ extract_environment_option_from_argument
set_application_directory!
prepare_restart
@@ -221,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"]
@@ -255,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
@@ -264,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.
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index d6c329b581..d1b8c7803f 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,9 +469,10 @@ 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|
# Starts after load_path plus a slash, ends before ".rb".
relname_range = (load_path.to_s.length + 1)...-3
@@ -532,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
@@ -549,12 +550,17 @@ module Rails
# Blog::Engine.load_seed
def load_seed
seed_file = paths["db/seeds.rb"].existent.first
- with_inline_jobs { load(seed_file) } if seed_file
+ return unless seed_file
+
+ if config.try(: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.
- initializer :set_load_path, before: :bootstrap_hook do
- _all_load_paths.reverse_each do |path|
+ initializer :set_load_path, before: :bootstrap_hook do |app|
+ _all_load_paths(app.config.add_autoload_paths_to_load_path).reverse_each do |path|
$LOAD_PATH.unshift(path) if File.directory?(path)
end
$LOAD_PATH.uniq!
@@ -569,12 +575,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
@@ -699,8 +708,12 @@ module Rails
@_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
end
- def _all_load_paths
- @_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
+ def _all_load_paths(add_autoload_paths_to_load_path)
+ @_all_load_paths ||= begin
+ load_paths = config.paths.load_paths
+ load_paths += _all_autoload_paths if add_autoload_paths_to_load_path
+ load_paths.uniq
+ end
end
def build_request(env)
diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb
index 82df56559c..a6b3ccf8f8 100644
--- a/railties/lib/rails/gem_version.rb
+++ b/railties/lib/rails/gem_version.rb
@@ -8,9 +8,9 @@ module Rails
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta1"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index b835b3f3fd..0be00d5151 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -35,6 +35,8 @@ module Rails
rails: {
actions: "-a",
orm: "-o",
+ javascripts: "-j",
+ javascript_engine: "-je",
resource_controller: "-c",
scaffold_controller: "-c",
stylesheets: "-y",
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 0eed552042..8782a85391 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -307,7 +307,7 @@ module Rails
def assets_gemfile_entry
return [] if options[:skip_sprockets]
- GemfileEntry.version("sass-rails", "~> 5.0", "Use SCSS for stylesheets")
+ GemfileEntry.version("sass-rails", ">= 5", "Use SCSS for stylesheets")
end
def webpacker_gemfile_entry
@@ -316,7 +316,7 @@ module Rails
if options.dev? || options.edge?
GemfileEntry.github "webpacker", "rails/webpacker", nil, "Use development version of Webpacker"
else
- GemfileEntry.version "webpacker", ">= 4.0.0.rc.3", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
+ GemfileEntry.version "webpacker", "~> 4.0", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
end
end
diff --git a/railties/lib/rails/generators/app_name.rb b/railties/lib/rails/generators/app_name.rb
index c4f71694d8..5bb735c4e8 100644
--- a/railties/lib/rails/generators/app_name.rb
+++ b/railties/lib/rails/generators/app_name.rb
@@ -7,11 +7,11 @@ module Rails
private
def app_name
- @app_name ||= original_app_name.tr("-", "_")
+ @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)).tr('\\', "").tr(". ", "_")
+ @original_app_name ||= defined_app_const_base? ? defined_app_name : File.basename(destination_root)
end
def defined_app_name
diff --git a/railties/lib/rails/generators/database.rb b/railties/lib/rails/generators/database.rb
index 18fc7be3ff..cc6e7b50e5 100644
--- a/railties/lib/rails/generators/database.rb
+++ b/railties/lib/rails/generators/database.rb
@@ -15,7 +15,7 @@ module Rails
case database
when "mysql" then ["mysql2", [">= 0.4.4"]]
when "postgresql" then ["pg", [">= 0.18", "< 2.0"]]
- when "sqlite3" then ["sqlite3", ["~> 1.3", ">= 1.3.6"]]
+ 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]
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/show.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
index 7deba07926..d3f996188c 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>
+<% if attribute.attachment? -%>
+ <%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %> if @<%= singular_table_name %>.<%= attribute.column_name %>.attached? %>
+<% 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 a8f7729fd3..1a80e71eae 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/time"
+require "active_support/deprecation"
module Rails
module Generators
@@ -51,6 +52,12 @@ module Rails
type = $1
provided_options = $2.split(/[,.-]/)
options = Hash[provided_options.map { |opt| [opt.to_sym, true] }]
+
+ if options[:required]
+ ActiveSupport::Deprecation.warn("Passing {required} option has no effect on the model generator. It will be removed in Rails 6.1.\n")
+ options.delete(:required)
+ end
+
return type, options
else
return type, {}
@@ -68,13 +75,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 +99,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
@@ -133,7 +144,7 @@ module Rails
end
def required?
- attr_options[:required]
+ reference? && Rails.application.config.active_record.belongs_to_required_by_default
end
def has_index?
@@ -152,6 +163,22 @@ 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
(+"").tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
end
@@ -163,7 +190,6 @@ module Rails
def options_for_migration
@attr_options.dup.tap do |options|
if required?
- options.delete(:required)
options[:null] = false
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
index d39b5d311f..cf5462f7dc 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
@@ -28,7 +28,7 @@ ruby <%= "'#{RUBY_VERSION}'" -%>
<% 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.4', require: false
<%- end -%>
<%- if options.api? -%>
@@ -69,8 +69,8 @@ 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 -%>
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
index 908487d500..e67e742263 100644
--- 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
@@ -13,3 +13,11 @@ require("@rails/activestorage").start()
<%- 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/bin/setup.tt b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
index 3f73bae3da..5928deb6aa 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
@@ -8,7 +8,8 @@ def system!(*args)
end
FileUtils.chdir APP_ROOT do
- # This script is a starting point to setup your application.
+ # This script is a way to setup or update your development environment automatically.
+ # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
# Add necessary setup steps to this file.
puts '== Installing dependencies =='
@@ -27,7 +28,7 @@ FileUtils.chdir APP_ROOT do
# end
puts "\n== Preparing database =="
- system! 'bin/rails db:setup'
+ system! 'bin/rails db:prepare'
<% end -%>
puts "\n== Removing old logs and tempfiles =="
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
deleted file mode 100644
index 03b77d0d46..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt
+++ /dev/null
@@ -1,33 +0,0 @@
-require 'fileutils'
-
-# path to your application root.
-APP_ROOT = File.expand_path('..', __dir__)
-
-def system!(*args)
- system(*args) || abort("\n== Command #{args} failed ==")
-end
-
-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_javascript? -%>
-
- # Install JavaScript dependencies
- # system('bin/yarn')
-<% end -%>
-<% unless options.skip_active_record? -%>
-
- puts "\n== Updating database =="
- system! 'rails db:migrate'
-<% end -%>
-
- puts "\n== Removing old logs and tempfiles =="
- system! 'rails log:clear tmp:clear'
-
- puts "\n== Restarting application server =="
- system! 'rails restart'
-end
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 2887bc2d67..404ab8b5de 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
@@ -15,9 +15,11 @@ Rails.application.configure do
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist?
+ <%- unless options.api? -%>
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
+ <%- end -%>
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}"
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 ed1cf09eeb..08d437a0be 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
@@ -12,7 +12,9 @@ Rails.application.configure do
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
+ <%- unless options.api? -%>
config.action_controller.perform_caching = true
+ <%- end -%>
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
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 63ed3fa952..c66e349442 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
@@ -1,11 +1,16 @@
+# The test environment is used exclusively to run your application's
+# test suite. You never need to work with it otherwise. Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs. Don't rely on the data there!
+
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
-
- # The test environment is used exclusively to run your application's
- # test suite. You never need to work with it otherwise. Remember that
- # your test database is "scratch space" for the test suite and is wiped
- # and recreated between test runs. Don't rely on the data there!
+ <%# Spring executes the reloaders when files change. %>
+ <%- if spring_install? -%>
+ config.cache_classes = false
+ <%- else -%>
config.cache_classes = true
+ <%- end -%>
# Do not eager load code on boot. This avoids loading your whole application
# just for the purpose of running a single test. If you are using a tool that
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/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
index 9ce8570172..e60637ff37 100644
--- a/railties/lib/rails/generators/rails/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -3,7 +3,10 @@
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
@@ -11,6 +14,10 @@ module Rails
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/db/system/change/change_generator.rb b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb
index 63849eb18d..24db92fad7 100644
--- a/railties/lib/rails/generators/rails/db/system/change/change_generator.rb
+++ b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb
@@ -35,8 +35,9 @@ module Rails
end
def edit_gemfile
- database_gem_name, _ = gem_for_database
- gsub_file("Gemfile", all_database_gems_regex, database_gem_name)
+ 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
@@ -48,6 +49,15 @@ module Rails
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
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 79a06648b5..895b3b2e92 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -144,17 +144,6 @@ task default: :test
end
end
- def javascripts
- return if options.skip_javascript?
-
- if mountable?
- template "rails/javascripts.js",
- "app/assets/javascripts/#{namespaced_name}/application.js"
- elsif full?
- empty_directory_with_keep_file "app/assets/javascripts/#{namespaced_name}"
- end
- end
-
def bin(force = false)
bin_file = engine? ? "bin/rails.tt" : "bin/test.tt"
template bin_file, force: force do |content|
@@ -236,10 +225,6 @@ task default: :test
build(:stylesheets) unless api?
end
- def create_javascript_files
- build(:javascripts) unless api?
- end
-
def create_bin_files
build(:bin)
end
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..e1ca54ec91 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,20 @@ module Rails
hook_for :helper, as: :scaffold do |invoked|
invoke invoked, [ controller_name ]
end
+
+ private
+
+ def permitted_params
+ attachments, others = attributes_names.partition { |name| attachments?(name) }
+ params = others.map { |name| ":#{name}" }
+ params += attachments.map { |name| "#{name}: []" }
+ params.join(", ")
+ end
+
+ def attachments?(name)
+ attribute = attributes.find { |attr| attr.name == name }
+ attribute&.attachments?
+ 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/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 6df50c3217..26002a0704 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -49,16 +49,21 @@ module TestUnit # :nodoc:
attributes_names.map do |name|
if %w(password password_confirmation).include?(name) && attributes.any?(&:password_digest?)
["#{name}", "'secret'"]
- else
+ elsif !virtual?(name)
["#{name}", "@#{singular_table_name}.#{name}"]
end
- end.sort.to_h
+ end.compact.sort.to_h
end
def boolean?(name)
attribute = attributes.find { |attr| attr.name == name }
attribute&.type == :boolean
end
+
+ def virtual?(name)
+ attribute = attributes.find { |attr| attr.name == name }
+ attribute&.virtual?
+ end
end
end
end
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index 95dae3ec2d..4a1942790b 100644
--- a/railties/lib/rails/mailers_controller.rb
+++ b/railties/lib/rails/mailers_controller.rb
@@ -5,8 +5,9 @@ require "rails/application_controller"
class Rails::MailersController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
+ around_action :set_locale, only: :preview
+ before_action :find_preview, only: :preview
before_action :require_local!, unless: :show_previews?
- before_action :find_preview, :set_locale, only: :preview
helper_method :part_query, :locale_query
@@ -38,7 +39,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}"
@@ -92,6 +93,8 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
end
def set_locale
- I18n.locale = params[:locale] || I18n.default_locale
+ I18n.with_locale(params[:locale] || I18n.default_locale) do
+ yield
+ end
end
end
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index d7170e6282..9ce22b96a6 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -29,6 +29,16 @@ module Rails
directories.push(*dirs)
end
+ def self.tags
+ @@tags ||= %w(OPTIMIZE FIXME TODO)
+ end
+
+ # Registers additional tags
+ # Rails::SourceAnnotationExtractor::Annotation.register_tags("TESTME", "DEPRECATEME")
+ def self.register_tags(*additional_tags)
+ tags.push(*additional_tags)
+ end
+
def self.extensions
@@extensions ||= {}
end
@@ -66,6 +76,8 @@ module Rails
# Prints all annotations with tag +tag+ under the root directories +app+,
# +config+, +db+, +lib+, and +test+ (recursively).
#
+ # If +tag+ is <tt>nil</tt>, annotations with either default or registered tags are printed.
+ #
# Specific directories can be explicitly set using the <tt>:dirs</tt> key in +options+.
#
# Rails::SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
@@ -75,7 +87,8 @@ module Rails
# See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
#
# This class method is the single entry point for the `rails notes` command.
- def self.enumerate(tag, options = {})
+ def self.enumerate(tag = nil, options = {})
+ tag ||= Annotation.tags.join("|")
extractor = new(tag)
dirs = options.delete(:dirs) || Annotation.directories
extractor.display(extractor.find(dirs), options)
diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb
index 2f644a20c9..0e592e15c3 100644
--- a/railties/lib/rails/tasks.rb
+++ b/railties/lib/rails/tasks.rb
@@ -15,6 +15,7 @@ require "rake"
routes
tmp
yarn
+ zeitwerk
).tap { |arr|
arr << "statistics" if Rake.application.current_scope.empty?
}.each do |task|
diff --git a/railties/lib/rails/tasks/zeitwerk.rake b/railties/lib/rails/tasks/zeitwerk.rake
new file mode 100644
index 0000000000..b7f5cd154b
--- /dev/null
+++ b/railties/lib/rails/tasks/zeitwerk.rake
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+ensure_classic_mode = ->() do
+ if Rails.autoloaders.zeitwerk_enabled?
+ abort <<~EOS
+ Please, enable temporarily :classic mode:
+
+ # config/application.rb
+ config.autoloader = :classic
+
+ and try again. When all is good, you can delete that line.
+ EOS
+ end
+end
+
+eager_load = ->() do
+ Rails.configuration.eager_load_namespaces.each(&:eager_load!)
+end
+
+mismatches = []
+check_directory = ->(directory, parent) do
+ # test/mailers/previews might not exist.
+ return unless File.exist?(directory)
+
+ Dir.foreach(directory) do |entry|
+ next if entry.start_with?(".")
+ next if parent == Object && entry == "concerns"
+
+ abspath = File.join(directory, entry)
+
+ if File.directory?(abspath) || abspath.end_with?(".rb")
+ print "."
+ cname = File.basename(abspath, ".rb").camelize.to_sym
+ if parent.const_defined?(cname, false)
+ if File.directory?(abspath)
+ check_directory[abspath, parent.const_get(cname)]
+ end
+ else
+ mismatches << [abspath, parent, cname]
+ end
+ end
+ end
+end
+
+report = ->() do
+ puts
+ if mismatches.empty?
+ puts "All is good!"
+ puts "Please, remember to delete `config.autoloader = :classic` from config/application.rb."
+ else
+ mismatches.each do |abspath, parent, cname|
+ relpath = abspath.sub(%r{\A#{Regexp.escape(Rails.root.to_path)}/}, "")
+ cpath = parent == Object ? cname : "#{parent.name}::#{cname}"
+ puts "expected #{relpath} to define #{cpath}"
+ end
+ puts
+ puts <<~EOS
+ Please revise the reported mismatches. You can normally fix them by adding
+ acronyms to config/initializers/inflections.rb or renaming the constants.
+ EOS
+ end
+end
+
+namespace :zeitwerk do
+ desc "Checks project structure for Zeitwerk compatibility"
+ task check: :environment do
+ ensure_classic_mode[]
+ eager_load[]
+
+ $stdout.sync = true
+ ActiveSupport::Dependencies.autoload_paths.each do |autoload_path|
+ check_directory[autoload_path, Object]
+ end
+ puts
+
+ report[]
+ end
+end
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index 1bfb0dfe48..519b08746a 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
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"
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
index 9c866263f0..9600194ed6 100644
--- a/railties/test/abstract_unit.rb
+++ b/railties/test/abstract_unit.rb
@@ -32,3 +32,5 @@ class ActiveSupport::TestCase
skip message if defined?(JRUBY_VERSION)
end
end
+
+require_relative "../../tools/test_common"
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 46ee0d670e..a80581211b 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -450,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"
@@ -466,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 a952d2466b..aa0da0931d 100644
--- a/railties/test/application/bin_setup_test.rb
+++ b/railties/test/application/bin_setup_test.rb
@@ -6,21 +6,12 @@ module ApplicationTests
class BinSetupTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
- def setup
- build_app
- end
-
- def teardown
- teardown_app
- end
+ setup :build_app
+ teardown :teardown_app
def test_bin_setup
Dir.chdir(app_path) do
- app_file "db/schema.rb", <<-RUBY
- ActiveRecord::Schema.define(version: 20140423102712) do
- create_table(:articles) {}
- end
- RUBY
+ rails "generate", "model", "article"
list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip }
File.write("log/test.log", "zomg!")
@@ -28,15 +19,20 @@ module ApplicationTests
assert_equal "[]", list_tables.call
assert_equal 5, File.size("log/test.log")
assert_not File.exist?("tmp/restart.txt")
+
`bin/setup 2>&1`
assert_equal 0, File.size("log/test.log")
- assert_equal '["articles", "schema_migrations", "ar_internal_metadata"]', list_tables.call
+ assert_equal '["schema_migrations", "ar_internal_metadata", "articles"]', list_tables.call
assert File.exist?("tmp/restart.txt")
end
end
def test_bin_setup_output
Dir.chdir(app_path) do
+ # SQLite3 seems to auto-create the database on first checkout.
+ rails "db:system:change", "--to=postgresql"
+ rails "db:drop"
+
app_file "db/schema.rb", ""
output = `bin/setup 2>&1`
@@ -53,8 +49,8 @@ module ApplicationTests
The Gemfile's dependencies are satisfied
== Preparing database ==
- Created database 'db/development.sqlite3'
- Created database 'db/test.sqlite3'
+ Created database 'app_development'
+ Created database 'app_test'
== Removing old logs and tempfiles ==
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 37fba72ee3..7c613585e0 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -4,6 +4,7 @@ require "isolation/abstract_unit"
require "rack/test"
require "env_helpers"
require "set"
+require "active_support/core_ext/string/starts_ends_with"
class ::MyMailInterceptor
def self.delivering_email(email); email; end
@@ -596,6 +597,30 @@ module ApplicationTests
assert_equal "some_value", verifier.verify(message)
end
+ 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
+ RUBY
+
+ # For test that works even if tmp dir does not exist.
+ Dir.chdir(app_path) { FileUtils.remove_dir("tmp") }
+
+ app "development"
+
+ assert_not_nil app.secrets.secret_key_base
+ assert File.exist?(app_path("tmp/development_secret.txt"))
+ end
+
+ 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.credentials.secret_key_base = nil
+ RUBY
+
+ 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
@@ -619,7 +644,6 @@ module ApplicationTests
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
@@ -1157,6 +1181,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"
@@ -1649,6 +1705,88 @@ module ApplicationTests
end
end
+ test "autoload paths are added to $LOAD_PATH by default" do
+ app "development"
+
+ # Action Mailer modifies AS::Dependencies.autoload_paths in-place.
+ autoload_paths = ActiveSupport::Dependencies.autoload_paths
+ autoload_paths_from_app_and_engines = autoload_paths.reject do |path|
+ path.ends_with?("mailers/previews")
+ end
+ assert_equal true, Rails.configuration.add_autoload_paths_to_load_path
+ assert_empty autoload_paths_from_app_and_engines - $LOAD_PATH
+
+ # Precondition, ensure we are testing something next.
+ assert_not_empty Rails.configuration.paths.load_paths
+ assert_empty Rails.configuration.paths.load_paths - $LOAD_PATH
+ end
+
+ test "autoload paths are not added to $LOAD_PATH if opted-out" do
+ add_to_config "config.add_autoload_paths_to_load_path = false"
+ app "development"
+
+ assert_empty ActiveSupport::Dependencies.autoload_paths & $LOAD_PATH
+
+ # Precondition, ensure we are testing something next.
+ assert_not_empty Rails.configuration.paths.load_paths
+ assert_empty Rails.configuration.paths.load_paths - $LOAD_PATH
+ end
+
+ test "autoloading during initialization gets deprecation message and clearing if config.cache_classes is false" do
+ app_file "lib/c.rb", <<~EOS
+ class C
+ extend ActiveSupport::DescendantsTracker
+ end
+
+ class X < C
+ end
+ EOS
+
+ app_file "app/models/d.rb", <<~EOS
+ require "c"
+
+ class D < C
+ end
+ EOS
+
+ app_file "config/initializers/autoload.rb", "D.class"
+
+ app "development"
+
+ # TODO: Test deprecation message, assert_depcrecated { app "development" }
+ # does not collect it.
+
+ assert_equal [X], C.descendants
+ assert_empty ActiveSupport::Dependencies.autoloaded_constants
+ end
+
+ test "autoloading during initialization triggers nothing if config.cache_classes is true" do
+ app_file "lib/c.rb", <<~EOS
+ class C
+ extend ActiveSupport::DescendantsTracker
+ end
+
+ class X < C
+ end
+ EOS
+
+ app_file "app/models/d.rb", <<~EOS
+ require "c"
+
+ class D < C
+ end
+ EOS
+
+ app_file "config/initializers/autoload.rb", "D.class"
+
+ app "production"
+
+ # TODO: Test no deprecation message is issued.
+
+ assert_equal [X, D], C.descendants
+ 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
@@ -1714,7 +1852,7 @@ module ApplicationTests
assert_equal true, Rails.application.config.action_mailer.show_previews
end
- test "config_for loads custom configuration from yaml accessible as symbol" do
+ test "config_for loads custom configuration from yaml accessible as symbol or string" do
app_file "config/custom.yml", <<-RUBY
development:
foo: 'bar'
@@ -1727,6 +1865,7 @@ module ApplicationTests
app "development"
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
@@ -1746,6 +1885,47 @@ module ApplicationTests
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:
@@ -1762,12 +1942,93 @@ module ApplicationTests
actual = Rails.application.config.my_custom_config
- assert_equal actual, foo: 0, bar: { baz: 1 }
- assert_equal actual.keys, [ :foo, :bar ]
- assert_equal actual.values, [ 0, baz: 1]
- assert_equal actual.to_h, foo: 0, bar: { baz: 1 }
- assert_equal actual[:foo], 0
- assert_equal actual[:bar], baz: 1
+ 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
@@ -2298,6 +2559,22 @@ module ApplicationTests
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
def force_lazy_load_hooks
yield # Tasty clarifying sugar, homie! We only need to reference a constant to load it.
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index b6270525f0..db16f4cc56 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -123,13 +123,17 @@ class FullStackConsoleTest < ActiveSupport::TestCase
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: @replica, out: @replica, err: @replica
)
- assert_output "> ", @primary, 30
+ if wait_for_prompt
+ assert_output "> ", @primary, 30
+ end
+
+ pid
end
def test_sandbox
@@ -148,6 +152,17 @@ class FullStackConsoleTest < ActiveSupport::TestCase
@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("-e test -- --verbose")
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 3cd4b8fe33..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
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index bfa66770bd..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
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index ba186bda44..fa9ed868c4 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")
@@ -513,6 +515,13 @@ module ApplicationTests
assert_match '<option selected value="locale=ja">ja', last_response.body
end
+ test "preview does not leak I18n global setting changes" do
+ I18n.with_locale(:en) do
+ get "/rails/mailers/notifier/foo.txt?locale=ja"
+ assert_equal :en, I18n.locale
+ end
+ end
+
test "mailer previews create correct links when loaded on a subdirectory" do
mailer "notifier", <<-RUBY
class Notifier < ActionMailer::Base
@@ -818,6 +827,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/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
index 2d659ade8d..5fae521937 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
@@ -136,5 +136,21 @@ module ApplicationTests
assert_match(/boooom/, last_response.body)
assert_match(/測試テスト시험/, last_response.body)
end
+
+ test "displays diagnostics message when malformed query parameters are provided" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ get "/foo?x[y]=1&x[y][][w]=2"
+ assert_equal 400, last_response.status
+ assert_match "Invalid query parameters", last_response.body
+ end
end
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 4242daf39a..54b2e95d75 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -38,6 +38,7 @@ module ApplicationTests
"Rails::Rack::Logger",
"ActionDispatch::ShowExceptions",
"ActionDispatch::DebugExceptions",
+ "ActionDispatch::ActionableExceptions",
"ActionDispatch::Reloader",
"ActionDispatch::Callbacks",
"ActiveRecord::Migration::CheckPending",
@@ -70,6 +71,7 @@ module ApplicationTests
"Rails::Rack::Logger",
"ActionDispatch::ShowExceptions",
"ActionDispatch::DebugExceptions",
+ "ActionDispatch::ActionableExceptions",
"ActionDispatch::Reloader",
"ActionDispatch::Callbacks",
"Rack::Head",
diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb
index 432344bccc..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 }
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 5879949b61..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
@@ -39,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
@@ -52,27 +53,73 @@ module ApplicationTests
test "db:create and db:drop respect environment setting" do
app_file "config/database.yml", <<-YAML
+ <% 1 %>
development:
- database: db/development.sqlite3
+ database: <%= Rails.application.config.database %>
adapter: sqlite3
YAML
app_file "config/environments/development.rb", <<-RUBY
Rails.application.configure do
- config.read_encrypted_secrets = true
+ config.database = "db/development.sqlite3"
end
RUBY
- app_file "lib/tasks/check_env.rake", <<-RUBY
- Rake::Task["db:create"].enhance do
- File.write("tmp/config_value", Rails.application.config.read_encrypted_secrets)
+ 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) do
- assert File.exist?("tmp/config_value")
- assert_equal "true", File.read("tmp/config_value")
- end
+ 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
@@ -139,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"
@@ -179,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
@@ -201,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
@@ -385,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/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb
index ef99365e75..31ea2246a9 100644
--- a/railties/test/application/rake/multi_dbs_test.rb
+++ b/railties/test/application/rake/multi_dbs_test.rb
@@ -24,7 +24,6 @@ module ApplicationTests
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)
@@ -137,6 +136,51 @@ module ApplicationTests
end
end
+ def db_up_and_down(version, namespace = nil)
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ rails("db:migrate")
+
+ if namespace
+ down_output = rails("db:migrate:down:#{namespace}", "VERSION=#{version}")
+ up_output = rails("db:migrate:up:#{namespace}", "VERSION=#{version}")
+ else
+ assert_raises RuntimeError, /You're using a multiple database application/ do
+ down_output = rails("db:migrate:down", "VERSION=#{version}")
+ end
+
+ assert_raises RuntimeError, /You're using a multiple database application/ do
+ up_output = rails("db:migrate:up", "VERSION=#{version}")
+ end
+ end
+
+ case namespace
+ when "primary"
+ assert_match(/OneMigration: reverting/, down_output)
+ assert_match(/OneMigration: migrated/, up_output)
+ when nil
+ else
+ assert_match(/TwoMigration: reverting/, down_output)
+ assert_match(/TwoMigration: migrated/, up_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")
@@ -170,6 +214,7 @@ module ApplicationTests
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
@@ -203,6 +248,34 @@ module ApplicationTests
end
end
+ test "db:migrate:down and db:migrate:up without a namespace raises in a multi-db application" do
+ require "#{app_path}/config/environment"
+
+ app_file "db/migrate/01_one_migration.rb", <<-MIGRATION
+ class OneMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ db_up_and_down "01"
+ end
+
+ test "db:migrate:down:namespace and db:migrate:up:namespace works" do
+ require "#{app_path}/config/environment"
+
+ app_file "db/migrate/01_one_migration.rb", <<-MIGRATION
+ class OneMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ app_file "db/animals_migrate/02_two_migration.rb", <<-MIGRATION
+ class TwoMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ db_up_and_down "01", "primary"
+ db_up_and_down "02", "animals"
+ end
+
test "db:migrate:status works on all databases" do
require "#{app_path}/config/environment"
db_migrate_and_migrate_status
@@ -225,6 +298,11 @@ module ApplicationTests
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/routes_test.rb b/railties/test/application/rake/routes_test.rb
index 9879d1f047..dffdae7bde 100644
--- a/railties/test/application/rake/routes_test.rb
+++ b/railties/test/application/rake/routes_test.rb
@@ -20,7 +20,6 @@ module ApplicationTests
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
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 44e3b0f66b..fe56e3d076 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -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
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 9df36b3444..5fe1b4e6e7 100644
--- a/railties/test/application/server_test.rb
+++ b/railties/test/application/server_test.rb
@@ -30,13 +30,13 @@ module ApplicationTests
pid = nil
Bundler.with_original_env do
- pid = Process.spawn("bin/rails server -P tmp/dummy.pid", chdir: app_path, in: replica, out: replica, err: replica)
+ 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", primary)
- assert_output("Inherited", 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 0bdd2b314d..1ab45abcd0 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -794,6 +794,32 @@ module ApplicationTests
assert_match "Capybara.reset_sessions! called", output
end
+ def test_failed_system_test_screenshot_should_be_taken_before_other_teardown
+ app_file "test/system/failed_system_test_screenshot_should_be_taken_before_other_teardown_test.rb", <<~RUBY
+ require "application_system_test_case"
+ require "selenium/webdriver"
+
+ class FailedSystemTestScreenshotShouldBeTakenBeforeOtherTeardownTest < ApplicationSystemTestCase
+ ActionDispatch::SystemTestCase.class_eval do
+ def take_failed_screenshot
+ puts "take_failed_screenshot called"
+ super
+ end
+ end
+
+ def teardown
+ puts "test teardown called"
+ super
+ end
+
+ test "dummy" do
+ end
+ end
+ RUBY
+ output = run_test_command("test/system/failed_system_test_screenshot_should_be_taken_before_other_teardown_test.rb")
+ assert_match(/take_failed_screenshot called\n.*test teardown 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"
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 6bcc0b0acb..dc737089f6 100644
--- a/railties/test/application/url_generation_test.rb
+++ b/railties/test/application/url_generation_test.rb
@@ -20,6 +20,7 @@ module ApplicationTests
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..9146222f73
--- /dev/null
+++ b/railties/test/application/zeitwerk_integration_test.rb
@@ -0,0 +1,350 @@
+# 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 "unloadable constants (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.autoloaded?("Post")
+ assert deps.autoloaded?(Post)
+ assert_not deps.autoloaded?("User")
+
+ assert_equal ["Post"], deps.autoloaded_constants
+ end
+
+ test "unloadable constants (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.autoloaded?("Foo")
+ assert_not deps.autoloaded?(Foo)
+ assert_not deps.autoloaded?("Bar")
+
+ assert_empty deps.autoloaded_constants
+ end
+
+ test "unloadable constants (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.autoloaded?("Post")
+ assert_not deps.autoloaded?(Post)
+ assert_not deps.autoloaded?("User")
+
+ assert_empty deps.autoloaded_constants
+ 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 "reloading raises if config.cache_classes is true" do
+ boot("production")
+
+ e = assert_raises(StandardError) do
+ deps.clear
+ end
+ assert_equal "reloading is disabled because config.cache_classes is true", e.message
+ 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
+end
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index 90e084ddca..6de23acebe 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -17,8 +17,18 @@ class BacktraceCleanerTest < ActiveSupport::TestCase
assert_equal 1, result.length
end
+ test "can filter for noise" 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, :noise)
+ assert_equal "/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'", result[0]
+ assert_equal "bin/rails:4:in `<main>'", result[1]
+ assert_equal 2, result.length
+ end
+
test "should omit ActionView template methods names" do
- method_name = ActionView::Template.new(nil, "app/views/application/index.html.erb", nil, {}).send :method_name
+ 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 "app/views/application/index.html.erb:4", result[0]
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index 1941c83d6d..f6df2b694a 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -129,7 +129,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
def build_app(console)
mocked_console = Class.new do
attr_accessor :sandbox
- attr_reader :console
+ attr_reader :console, :disable_sandbox
def initialize(console)
@console = console
diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb
index 10d0ba1cae..0ee36081c0 100644
--- a/railties/test/commands/credentials_test.rb
+++ b/railties/test/commands/credentials_test.rb
@@ -63,6 +63,14 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
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!
@@ -71,11 +79,20 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
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"
@@ -95,6 +112,14 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
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", environment: nil, **options)
switch_env("EDITOR", editor) do
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 65f6916acb..76a7cd055f 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -216,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
@@ -245,6 +245,18 @@ 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"])
diff --git a/railties/test/commands/initializers_test.rb b/railties/test/commands/initializers_test.rb
index bdfbb3021c..793365ef3d 100644
--- a/railties/test/commands/initializers_test.rb
+++ b/railties/test/commands/initializers_test.rb
@@ -25,8 +25,24 @@ class Rails::Command::InitializersTest < ActiveSupport::TestCase
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
- rails "initializers"
+ def run_initializers_command(args = [])
+ rails "initializers", args
end
end
diff --git a/railties/test/commands/notes_test.rb b/railties/test/commands/notes_test.rb
index 147019e299..9182541413 100644
--- a/railties/test/commands/notes_test.rb
+++ b/railties/test/commands/notes_test.rb
@@ -121,6 +121,47 @@ class Rails::Command::NotesTest < ActiveSupport::TestCase
OUTPUT
end
+ test "displays results from additional tags added to the default tags from a config file" do
+ app_file "app/models/profile.rb", "# TESTME: some method to test"
+ app_file "app/controllers/hello_controller.rb", "# DEPRECATEME: this action is no longer needed"
+ app_file "db/some_seeds.rb", "# TODO: default tags such as TODO are still present"
+
+ add_to_config 'config.annotations.register_tags "TESTME", "DEPRECATEME"'
+
+ assert_equal <<~OUTPUT, run_notes_command
+ app/controllers/hello_controller.rb:
+ * [1] [DEPRECATEME] this action is no longer needed
+
+ app/models/profile.rb:
+ * [1] [TESTME] some method to test
+
+ db/some_seeds.rb:
+ * [1] [TODO] default tags such as TODO are still present
+
+ OUTPUT
+ end
+
+ test "does not display results from tags that are neither default nor registered" do
+ app_file "app/models/profile.rb", "# TESTME: some method to test"
+ app_file "app/controllers/hello_controller.rb", "# DEPRECATEME: this action is no longer needed"
+ app_file "db/some_seeds.rb", "# TODO: default tags such as TODO are still present"
+ app_file "db/some_other_seeds.rb", "# BAD: this note should not be listed"
+
+ add_to_config 'config.annotations.register_tags "TESTME", "DEPRECATEME"'
+
+ assert_equal <<~OUTPUT, run_notes_command
+ app/controllers/hello_controller.rb:
+ * [1] [DEPRECATEME] this action is no longer needed
+
+ app/models/profile.rb:
+ * [1] [TESTME] some method to test
+
+ db/some_seeds.rb:
+ * [1] [TODO] default tags such as TODO are still present
+
+ OUTPUT
+ end
+
private
def run_notes_command(args = [])
rails "notes", args
diff --git a/railties/test/commands/routes_test.rb b/railties/test/commands/routes_test.rb
index b4f927060e..a2dbd944f5 100644
--- a/railties/test/commands/routes_test.rb
+++ b/railties/test/commands/routes_test.rb
@@ -62,7 +62,6 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
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
@@ -166,7 +165,6 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
assert_equal <<~MESSAGE, run_routes_command
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
@@ -207,101 +205,96 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
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 ]--------------
+ --[ Route 3 ]--------------
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 ]--------------
+ --[ Route 4 ]--------------
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 ]--------------
+ --[ Route 5 ]--------------
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 ]--------------
+ --[ Route 6 ]--------------
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 ]--------------
+ --[ Route 7 ]--------------
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 ]--------------
+ --[ Route 8 ]--------------
Prefix |
Verb | POST
URI | /rails/conductor/action_mailbox/inbound_emails(.:format)
Controller#Action | rails/conductor/action_mailbox/inbound_emails#create
- --[ Route 10 ]-------------
+ --[ Route 9 ]--------------
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 ]-------------
+ --[ Route 10 ]-------------
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 ]-------------
+ --[ Route 11 ]-------------
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 ]-------------
+ --[ Route 12 ]-------------
Prefix |
Verb | PATCH
URI | /rails/conductor/action_mailbox/inbound_emails/:id(.:format)
Controller#Action | rails/conductor/action_mailbox/inbound_emails#update
- --[ Route 14 ]-------------
+ --[ Route 13 ]-------------
Prefix |
Verb | PUT
URI | /rails/conductor/action_mailbox/inbound_emails/:id(.:format)
Controller#Action | rails/conductor/action_mailbox/inbound_emails#update
- --[ Route 15 ]-------------
+ --[ Route 14 ]-------------
Prefix |
Verb | DELETE
URI | /rails/conductor/action_mailbox/inbound_emails/:id(.:format)
Controller#Action | rails/conductor/action_mailbox/inbound_emails#destroy
- --[ Route 16 ]-------------
+ --[ Route 15 ]-------------
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 ]-------------
+ --[ Route 16 ]-------------
Prefix | rails_service_blob
Verb | GET
URI | /rails/active_storage/blobs/:signed_id/*filename(.:format)
Controller#Action | active_storage/blobs#show
- --[ Route 18 ]-------------
+ --[ Route 17 ]-------------
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 ]-------------
+ --[ Route 18 ]-------------
Prefix | rails_disk_service
Verb | GET
URI | /rails/active_storage/disk/:encoded_key/*filename(.:format)
Controller#Action | active_storage/disk#show
- --[ Route 20 ]-------------
+ --[ Route 19 ]-------------
Prefix | update_rails_disk_service
Verb | PUT
URI | /rails/active_storage/disk/:encoded_token(.:format)
Controller#Action | active_storage/disk#update
- --[ Route 21 ]-------------
+ --[ Route 20 ]-------------
Prefix | rails_direct_uploads
Verb | POST
URI | /rails/active_storage/direct_uploads(.:format)
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index 25b89ecbd8..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)
@@ -285,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/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 44d4e92256..d913bb5438 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -200,7 +200,7 @@ class ActionsTest < Rails::Generators::TestCase
run_generator
action :environment do
- _ = "# This wont be added"# assignment to silence parse-time warning "unused literal ignored"
+ _ = "# This wont be added" # assignment to silence parse-time warning "unused literal ignored"
"# This will be added"
end
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index 4b9878187b..d03178da66 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -50,6 +50,13 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
assert_file "config/application.rb", /config\.api_only = true/
assert_file "app/controllers/application_controller.rb", /ActionController::API/
+
+ assert_file "config/environments/development.rb" do |content|
+ assert_no_match(/action_controller\.perform_caching = true/, content)
+ end
+ assert_file "config/environments/production.rb" do |content|
+ assert_no_match(/action_controller\.perform_caching = true/, content)
+ end
end
def test_generator_if_skip_action_cable_is_given
@@ -120,7 +127,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
bin/rails
bin/rake
bin/setup
- bin/update
config/application.rb
config/boot.rb
config/cable.yml
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 1ee9e43e89..5b439fdcba 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -41,7 +41,6 @@ DEFAULT_APP_FILES = %w(
bin/rails
bin/rake
bin/setup
- bin/update
bin/yarn
config/application.rb
config/boot.rb
@@ -321,10 +320,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "#{app_root}/bin/setup" do |content|
assert_no_match(/system\('bin\/yarn'\)/, content)
end
-
- assert_file "#{app_root}/bin/update" do |content|
- assert_no_match(/system\('bin\/yarn'\)/, content)
- end
end
end
@@ -345,39 +340,44 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, "myapp")
run_generator [app_root, "--skip-spring"]
- FileUtils.cd(app_root) do
- quietly { system("bin/rails app:update") }
- end
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_spring: true }, { destination_root: app_root, shell: @shell }
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
- assert_no_file "#{app_root}/config/spring.rb"
+ assert_no_file "#{app_root}/config/spring.rb"
+ end
end
def test_app_update_does_not_generate_action_cable_contents_when_skip_action_cable_is_given
app_root = File.join(destination_root, "myapp")
run_generator [app_root, "--skip-action-cable"]
- FileUtils.cd(app_root) do
- quietly { system("bin/rails app:update") }
- end
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_action_cable: true }, { destination_root: app_root, shell: @shell }
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
- assert_no_file "#{app_root}/config/cable.yml"
- assert_file "#{app_root}/config/environments/production.rb" do |content|
- assert_no_match(/config\.action_cable/, content)
+ assert_no_file "#{app_root}/config/cable.yml"
+ 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
-
- 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
app_root = File.join(destination_root, "myapp")
run_generator [app_root, "--skip-bootsnap"]
- FileUtils.cd(app_root) do
- quietly { system("bin/rails app:update") }
- end
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_bootsnap: true }, { destination_root: app_root, shell: @shell }
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/boot.rb" do |content|
- assert_no_match(/require 'bootsnap\/setup'/, content)
+ assert_file "#{app_root}/config/boot.rb" do |content|
+ assert_no_match(/require 'bootsnap\/setup'/, content)
+ end
end
end
@@ -396,46 +396,50 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, "myapp")
run_generator [app_root, "--skip-active-storage"]
- FileUtils.cd(app_root) do
- quietly { system("bin/rails app:update") }
- end
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_active_storage: true }, { destination_root: app_root, shell: @shell }
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/environments/development.rb" do |content|
- assert_no_match(/config\.active_storage/, content)
- end
+ assert_file "#{app_root}/config/environments/development.rb" do |content|
+ assert_no_match(/config\.active_storage/, content)
+ end
- assert_file "#{app_root}/config/environments/production.rb" do |content|
- assert_no_match(/config\.active_storage/, content)
- end
+ assert_file "#{app_root}/config/environments/production.rb" do |content|
+ assert_no_match(/config\.active_storage/, content)
+ end
- assert_file "#{app_root}/config/environments/test.rb" do |content|
- assert_no_match(/config\.active_storage/, content)
- end
+ assert_file "#{app_root}/config/environments/test.rb" do |content|
+ assert_no_match(/config\.active_storage/, content)
+ end
- assert_no_file "#{app_root}/config/storage.yml"
+ assert_no_file "#{app_root}/config/storage.yml"
+ end
end
def test_app_update_does_not_generate_active_storage_contents_when_skip_active_record_is_given
app_root = File.join(destination_root, "myapp")
run_generator [app_root, "--skip-active-record"]
- FileUtils.cd(app_root) do
- quietly { system("bin/rails app:update") }
- end
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_active_record: true }, { destination_root: app_root, shell: @shell }
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/environments/development.rb" do |content|
- assert_no_match(/config\.active_storage/, content)
- end
+ assert_file "#{app_root}/config/environments/development.rb" do |content|
+ assert_no_match(/config\.active_storage/, content)
+ end
- assert_file "#{app_root}/config/environments/production.rb" do |content|
- assert_no_match(/config\.active_storage/, content)
- end
+ assert_file "#{app_root}/config/environments/production.rb" do |content|
+ assert_no_match(/config\.active_storage/, content)
+ end
- assert_file "#{app_root}/config/environments/test.rb" do |content|
- assert_no_match(/config\.active_storage/, content)
- end
+ assert_file "#{app_root}/config/environments/test.rb" do |content|
+ assert_no_match(/config\.active_storage/, content)
+ end
- assert_no_file "#{app_root}/config/storage.yml"
+ assert_no_file "#{app_root}/config/storage.yml"
+ end
end
def test_generator_skips_action_mailbox_when_skip_action_mailbox_is_given
@@ -469,16 +473,17 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_app_update_does_not_change_config_target_version
- run_generator
+ app_root = File.join(destination_root, "myapp")
+ run_generator [app_root, "--skip-spring"]
- FileUtils.cd(destination_root) do
+ FileUtils.cd(app_root) do
config = "config/application.rb"
content = File.read(config)
File.write(config, content.gsub(/config\.load_defaults #{Rails::VERSION::STRING.to_f}/, "config.load_defaults 5.1"))
quietly { system("bin/rails app:update") }
end
- assert_file "config/application.rb", /\s+config\.load_defaults 5\.1/
+ assert_file "#{app_root}/config/application.rb", /\s+config\.load_defaults 5\.1/
end
def test_app_update_does_not_change_app_name_when_app_name_is_hyphenated_name
@@ -495,13 +500,15 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_match(/hyphenated-app/, content)
end
- FileUtils.cd(app_root) do
- quietly { system("bin/rails app:update") }
- end
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, { destination_root: app_root, shell: @shell }
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/cable.yml" do |content|
- assert_match(/hyphenated_app/, content)
- assert_no_match(/hyphenated-app/, content)
+ assert_file "#{app_root}/config/cable.yml" do |content|
+ assert_match(/hyphenated_app/, content)
+ assert_no_match(/hyphenated-app/, content)
+ end
end
end
@@ -526,7 +533,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
if defined?(JRUBY_VERSION)
assert_gem "activerecord-jdbcsqlite3-adapter"
else
- assert_gem "sqlite3", "'~> 1.3', '>= 1.3.6'"
+ assert_gem "sqlite3", "'~> 1.4'"
end
end
@@ -617,7 +624,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
@@ -626,7 +633,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")
@@ -678,6 +685,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
@@ -812,6 +834,9 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_spring
run_generator
assert_gem "spring"
+ assert_file("config/environments/test.rb") do |contents|
+ assert_match("config.cache_classes = false", contents)
+ end
end
def test_bundler_binstub
@@ -830,7 +855,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_spring_no_fork
jruby_skip "spring doesn't run on JRuby"
- assert_called_with(Process, :respond_to?, [[:fork], [:fork]], returns: false) do
+ assert_called_with(Process, :respond_to?, [[:fork], [:fork], [:fork]], returns: false) do
run_generator
assert_no_gem "spring"
@@ -842,6 +867,9 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_file "config/spring.rb"
assert_no_gem "spring"
+ assert_file("config/environments/test.rb") do |contents|
+ assert_match("config.cache_classes = true", contents)
+ end
end
def test_spring_with_dev_option
@@ -968,6 +996,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
diff --git a/railties/test/generators/db_system_change_generator_test.rb b/railties/test/generators/db_system_change_generator_test.rb
index d476bfd2dc..607db96906 100644
--- a/railties/test/generators/db_system_change_generator_test.rb
+++ b/railties/test/generators/db_system_change_generator_test.rb
@@ -40,7 +40,7 @@ module Rails
assert_file("Gemfile") do |content|
assert_match "# Use pg as the database for Active Record", content
- assert_match "gem 'pg'", content
+ assert_match "gem 'pg', '>= 0.18', '< 2.0'", content
end
end
@@ -54,7 +54,7 @@ module Rails
assert_file("Gemfile") do |content|
assert_match "# Use mysql2 as the database for Active Record", content
- assert_match "gem 'mysql2'", content
+ assert_match "gem 'mysql2', '>= 0.4.4'", content
end
end
@@ -68,7 +68,22 @@ module Rails
assert_file("Gemfile") do |content|
assert_match "# Use sqlite3 as the database for Active Record", content
- assert_match "gem 'sqlite3'", 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
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index 772b4f6f0d..82d550a124 100644
--- a/railties/test/generators/generated_attribute_test.rb
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -6,6 +6,15 @@ require "rails/generators/generated_attribute"
class GeneratedAttributeTest < Rails::Generators::TestCase
include GeneratorsTestHelper
+ def setup
+ @old_belongs_to_required_by_default = Rails.application.config.active_record.belongs_to_required_by_default
+ Rails.application.config.active_record.belongs_to_required_by_default = true
+ end
+
+ def teardown
+ Rails.application.config.active_record.belongs_to_required_by_default = @old_belongs_to_required_by_default
+ end
+
def test_field_type_returns_number_field
assert_field_type :integer, :number_field
end
@@ -38,6 +47,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 +103,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
@@ -145,10 +164,16 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
end
def test_parse_required_attribute_with_index
- att = Rails::Generators::GeneratedAttribute.parse("supplier:references{required}:index")
+ att = Rails::Generators::GeneratedAttribute.parse("supplier:references:index")
assert_equal "supplier", att.name
assert_equal :references, att.type
assert_predicate att, :has_index?
assert_predicate att, :required?
end
+
+ def test_parse_required_attribute_with_index_false_when_belongs_to_required_by_default_global_config_is_false
+ Rails.application.config.active_record.belongs_to_required_by_default = false
+ att = Rails::Generators::GeneratedAttribute.parse("supplier:references:index")
+ assert_not_predicate att, :required?
+ end
end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index b8958f6f99..65cde91436 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -6,6 +6,16 @@ require "rails/generators/rails/migration/migration_generator"
class MigrationGeneratorTest < Rails::Generators::TestCase
include GeneratorsTestHelper
+ def setup
+ @old_belongs_to_required_by_default = Rails.application.config.active_record.belongs_to_required_by_default
+
+ Rails.application.config.active_record.belongs_to_required_by_default = true
+ end
+
+ def teardown
+ Rails.application.config.active_record.belongs_to_required_by_default = @old_belongs_to_required_by_default
+ end
+
def test_migration
migration = "change_title_body_from_posts"
run_generator [migration]
@@ -196,9 +206,9 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
- def test_add_migration_with_required_references
+ def test_add_migration_with_references_adds_null_false_by_default
migration = "add_references_to_books"
- run_generator [migration, "author:belongs_to{required}", "distributor:references{polymorphic,required}"]
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
assert_migration "db/migrate/#{migration}.rb" do |content|
assert_method :change, content do |change|
@@ -208,6 +218,21 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_add_migration_with_references_does_not_add_belongs_to_when_required_by_default_global_config_is_false
+ Rails.application.config.active_record.belongs_to_required_by_default = false
+
+ migration = "add_references_to_books"
+
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_reference :books, :author/, change)
+ assert_match(/add_reference :books, :distributor, polymorphic: true/, change)
+ end
+ end
+ end
+
def test_add_migration_with_references_adds_foreign_keys
migration = "add_references_to_books"
run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
@@ -280,6 +305,17 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
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"]
@@ -370,6 +406,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 134659f285..d6abaf7a6f 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -10,6 +10,14 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def setup
super
Rails::Generators::ModelHelpers.skip_warn = false
+ @old_belongs_to_required_by_default = Rails.application.config.active_record.belongs_to_required_by_default
+
+ Rails.application.config.active_record.belongs_to_required_by_default = true
+ end
+
+
+ def teardown
+ Rails.application.config.active_record.belongs_to_required_by_default = @old_belongs_to_required_by_default
end
def test_help_shows_invoked_generators_options
@@ -403,45 +411,68 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
- def test_required_belongs_to_adds_required_association
- run_generator ["account", "supplier:references{required}"]
+ 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_polymorphic_belongs_to_generates_correct_model
+ run_generator ["account", "supplier:references{polymorphic}"]
expected_file = <<~FILE
class Account < ApplicationRecord
- belongs_to :supplier, required: true
+ belongs_to :supplier, polymorphic: true
end
FILE
assert_file "app/models/account.rb", expected_file
end
- def test_required_polymorphic_belongs_to_generages_correct_model
- run_generator ["account", "supplier:references{required,polymorphic}"]
+ def test_passing_required_to_model_generator_is_deprecated
+ assert_deprecated do
+ run_generator ["account", "supplier:references{required}"]
+ end
- expected_file = <<~FILE
- class Account < ApplicationRecord
- belongs_to :supplier, polymorphic: true, required: true
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.references :supplier,.*\snull: false/, up)
end
- FILE
- assert_file "app/models/account.rb", expected_file
+ end
end
- def test_required_and_polymorphic_are_order_independent
- run_generator ["account", "supplier:references{polymorphic.required}"]
+ def test_null_false_is_added_for_references_by_default
+ run_generator ["account", "user:references"]
- expected_file = <<~FILE
- class Account < ApplicationRecord
- belongs_to :supplier, polymorphic: true, required: true
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.references :user,.*\snull: false/, up)
end
- FILE
- assert_file "app/models/account.rb", expected_file
+ end
end
- def test_required_adds_null_false_to_column
- run_generator ["account", "supplier:references{required}"]
+ def test_null_false_is_added_for_belongs_to_by_default
+ run_generator ["account", "user:belongs_to"]
assert_migration "db/migrate/create_accounts.rb" do |m|
assert_method :change, m do |up|
- assert_match(/t\.references :supplier,.*\snull: false/, up)
+ assert_match(/t\.belongs_to :user,.*\snull: false/, up)
+ end
+ end
+ end
+
+ def test_null_false_is_not_added_when_belongs_to_required_by_default_global_config_is_false
+ Rails.application.config.active_record.belongs_to_required_by_default = false
+
+ run_generator ["account", "user:belongs_to"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.belongs_to :user/, up)
end
end
end
@@ -488,6 +519,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/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index fd5aa817b4..8278b72a5a 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -80,6 +80,24 @@ 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_controller_permit_attachments_attributes_only
+ run_generator ["Message", "photos:attachments"]
+
+ assert_file "app/controllers/messages_controller.rb" do |content|
+ assert_match(/def message_params/, content)
+ assert_match(/params\.require\(:message\)\.permit\(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 +294,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 f672e301a7..fa9a42215b 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -471,6 +471,54 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
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
+
+ assert_file "app/views/messages/show.html.erb" do |content|
+ assert_match(/^\W{2}<%= link_to @message\.video\.filename, @message\.video if @message\.video\.attached\? %>/, content)
+ assert_match(/^\W{4}<div><%= link_to photo\.filename, photo %>/, content)
+ end
+
+ assert_file "test/system/messages_test.rb" do |content|
+ assert_no_match(/fill_in "Video"/, content)
+ assert_no_match(/fill_in "Photos"/, content)
+ end
+ end
+
+ def test_scaffold_generator_rich_text
+ run_generator ["message", "content:rich_text"]
+
+ assert_file "app/models/message.rb", /rich_text :content/
+
+ assert_file "app/controllers/messages_controller.rb" do |content|
+ assert_instance_method :message_params, content do |m|
+ assert_match(/permit\(:content\)/, m)
+ end
+ end
+
+ assert_file "app/views/messages/_form.html.erb" do |content|
+ assert_match(/^\W{4}<%= form\.rich_text_area :content %>/, content)
+ end
+
+ assert_file "test/system/messages_test.rb" do |content|
+ assert_no_match(/fill_in "Content"/, content)
+ end
+ end
+
def test_scaffold_generator_database
with_secondary_database_configuration do
run_generator ["posts", "--database=secondary"]
@@ -479,6 +527,14 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
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
run_generator ["user", "name", "password:digest"]
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 26ce487c5f..3e8ce1c018 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -191,10 +191,7 @@ module SharedGeneratorTests
assert_no_match(/fixtures :all/, helper_content)
end
assert_file "#{application_path}/bin/setup" do |setup_content|
- assert_no_match(/db:setup/, setup_content)
- end
- assert_file "#{application_path}/bin/update" do |update_content|
- assert_no_match(/db:migrate/, update_content)
+ assert_no_match(/db:prepare/, setup_content)
end
assert_file ".gitignore" do |content|
assert_no_match(/sqlite/i, content)
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index ca7601f6fe..fab704944b 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -18,7 +18,9 @@ require "active_support/testing/method_call_assertions"
require "active_support/test_case"
require "minitest/retry"
-Minitest::Retry.use!(verbose: false, retry_count: 1)
+if ENV["BUILDKITE"]
+ Minitest::Retry.use!(verbose: false, retry_count: 1)
+end
RAILS_FRAMEWORK_ROOT = File.expand_path("../../..", __dir__)
@@ -123,6 +125,8 @@ module TestHelpers
adapter: sqlite3
pool: 5
timeout: 5000
+ variables:
+ statement_timeout: 1000
development:
primary:
<<: *default
@@ -226,6 +230,7 @@ module TestHelpers
@app.config.session_store :cookie_store, key: "_myapp_session"
@app.config.active_support.deprecation = :log
@app.config.log_level = :info
+ @app.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
yield @app if block_given?
@app.initialize!
@@ -421,6 +426,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
@@ -454,12 +463,19 @@ 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
end
@@ -472,21 +488,24 @@ Module.new do
FileUtils.rm_rf(app_template_path)
FileUtils.mkdir_p(app_template_path)
- Dir.chdir "#{RAILS_FRAMEWORK_ROOT}/actionview" do
- `yarn build`
- end
-
- `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-bundle --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
- Dir.chdir(app_template_path) { `yarn add https://github.com/rails/webpacker.git` } # Use the latest version.
+ unless File.exist?("#{RAILS_FRAMEWORK_ROOT}/actionview/lib/assets/compiled/rails-ujs.js")
+ Dir.chdir("#{RAILS_FRAMEWORK_ROOT}/actionview") { `yarn build` }
+ end
- # Manually install `webpack` as bin symlinks are not created for sub dependencies
- # in workspaces. See https://github.com/yarnpkg/yarn/issues/4964
- Dir.chdir(app_template_path) { `yarn add webpack@4.17.1 --tilde` }
- Dir.chdir(app_template_path) { `yarn add webpack-cli` }
+ 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.
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/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 851407dede..8ce68dbcfa 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -704,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
@@ -877,7 +879,7 @@ YAML
assert Bukkits::Engine.config.bukkits_seeds_loaded
end
- test "jobs are ran inline while loading seeds" do
+ 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
@@ -889,6 +891,45 @@ YAML
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 "seed data can be loaded when ActiveJob is not present" do
+ @plugin.write "db/seeds.rb", <<-RUBY
+ Bukkits::Engine.config.bukkits_seeds_loaded = true
+ RUBY
+
+ app_file "db/seeds.rb", <<-RUBY
+ Rails.application.config.app_seeds_loaded = true
+ RUBY
+
+ boot_rails
+
+ # In a real app, config.active_job would be undefined when
+ # NOT requiring rails/all AND NOT requiring active_job/railtie
+ # that doesn't work as expected in this test environment, so:
+ undefine_config_option(:active_job)
+ assert_raise(NoMethodError) { Rails.application.config.active_job }
+
+ assert_raise(NoMethodError) { Rails.application.config.app_seeds_loaded }
+ assert_raise(NoMethodError) { Bukkits::Engine.config.bukkits_seeds_loaded }
+
+ Rails.application.load_seed
+ assert Rails.application.config.app_seeds_loaded
+ Bukkits::Engine.load_seed
+ assert Bukkits::Engine.config.bukkits_seeds_loaded
+ end
+
test "skips nonexistent seed data" do
FileUtils.rm "#{app_path}/db/seeds.rb"
boot_rails
@@ -1508,6 +1549,10 @@ YAML
Rails.application
end
+ def undefine_config_option(name)
+ Rails.application.config.class.class_variable_get(:@@options).delete(name)
+ end
+
# Restrict frameworks to load in order to avoid engine frameworks affect tests.
def restrict_frameworks
remove_from_config("require 'rails/all'")