aboutsummaryrefslogtreecommitdiffstats
path: root/railties
diff options
context:
space:
mode:
Diffstat (limited to 'railties')
-rw-r--r--railties/CHANGELOG.md37
-rw-r--r--railties/Rakefile41
-rw-r--r--railties/lib/rails/application/configuration.rb4
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb1
-rw-r--r--railties/lib/rails/application/finisher.rb39
-rw-r--r--railties/lib/rails/command/environment_argument.rb4
-rw-r--r--railties/lib/rails/commands/dev/dev_command.rb6
-rw-r--r--railties/lib/rails/commands/notes/notes_command.rb2
-rw-r--r--railties/lib/rails/engine.rb8
-rw-r--r--railties/lib/rails/generators/app_base.rb2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb10
-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/test.rb.tt15
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb15
-rw-r--r--railties/lib/rails/mailers_controller.rb7
-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/test/application/bin_setup_test.rb26
-rw-r--r--railties/test/application/configuration_test.rb55
-rw-r--r--railties/test/application/mailer_previews_test.rb7
-rw-r--r--railties/test/application/middleware_test.rb2
-rw-r--r--railties/test/application/rake/multi_dbs_test.rb59
-rw-r--r--railties/test/application/rake/routes_test.rb1
-rw-r--r--railties/test/application/test_runner_test.rb26
-rw-r--r--railties/test/application/zeitwerk_integration_test.rb62
-rw-r--r--railties/test/backtrace_cleaner_test.rb10
-rw-r--r--railties/test/commands/notes_test.rb41
-rw-r--r--railties/test/commands/routes_test.rb43
-rw-r--r--railties/test/generators/actions_test.rb2
-rw-r--r--railties/test/generators/api_app_generator_test.rb1
-rw-r--r--railties/test/generators/app_generator_test.rb129
-rw-r--r--railties/test/generators/generated_attribute_test.rb17
-rw-r--r--railties/test/generators/migration_generator_test.rb29
-rw-r--r--railties/test/generators/model_generator_test.rb60
-rw-r--r--railties/test/generators/shared_generator_tests.rb5
-rw-r--r--railties/test/isolation/abstract_unit.rb4
-rw-r--r--railties/test/railties/engine_test.rb45
39 files changed, 739 insertions, 208 deletions
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index c5136b7ab0..84974d8f03 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,40 @@
+* Applications upgrading to Rails 6 can run the command
+
+ ```
+ bin/rails zeitwerk:check
+ ```
+
+ to check if the project structure they were using with the classic
+ autoloader is compatible with `:zeitwerk` mode.
+
+ *Matilda Smeds* & *Xavier Noria*
+
+* Allow loading seeds without ActiveJob.
+
+ Fixes #35782
+
+ *Jeremy Weathers*
+
+* `null: false` is set in the migrations by default for column pointed by
+ `belongs_to` / `references` association generated by model generator.
+
+ Also deprecate passing {required} to the model generator.
+
+ *Prathamesh Sonpatki*
+
+* New applications get `config.cache_classes = false` in `config/environments/test.rb`
+ unless `--skip-spring`.
+
+ *Xavier Noria*
+
+* Autoloading during initialization is deprecated.
+
+ *Xavier Noria*
+
+* Only force `:async` ActiveJob adapter to `:inline` during seeding.
+
+ *BatedUrGonnaDie*
+
* The `connection` option of `rails dbconsole` command is deprecated in
favor of `database` option.
diff --git a/railties/Rakefile b/railties/Rakefile
index 4ae546b202..0f305ea332 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -11,6 +11,33 @@ task test: "test:isolated"
namespace :test do
task :isolated do
+ estimated_duration = {
+ "test/application/test_runner_test.rb" => 201,
+ "test/application/assets_test.rb" => 131,
+ "test/application/rake/migrations_test.rb" => 65,
+ "test/generators/scaffold_generator_test.rb" => 57,
+ "test/generators/plugin_test_runner_test.rb" => 57,
+ "test/application/test_test.rb" => 52,
+ "test/application/configuration_test.rb" => 49,
+ "test/generators/app_generator_test.rb" => 43,
+ "test/application/rake/dbs_test.rb" => 43,
+ "test/application/rake_test.rb" => 33,
+ "test/generators/plugin_generator_test.rb" => 30,
+ "test/railties/engine_test.rb" => 27,
+ "test/generators/scaffold_controller_generator_test.rb" => 23,
+ "test/railties/generators_test.rb" => 19,
+ "test/application/console_test.rb" => 16,
+ "test/engine/commands_test.rb" => 15,
+ "test/application/routing_test.rb" => 15,
+ "test/application/mailer_previews_test.rb" => 15,
+ "test/application/rake/multi_dbs_test.rb" => 13,
+ "test/application/asset_debugging_test.rb" => 12,
+ "test/application/bin_setup_test.rb" => 11,
+ "test/engine/test_test.rb" => 10,
+ "test/application/runner_test.rb" => 10,
+ }
+ estimated_duration.default = 1
+
dash_i = [
"test",
"lib",
@@ -39,13 +66,23 @@ 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/") && !file.start_with?("test/isolation/assets/")
- end.sort
+ 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|
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index b79dbdbc6f..d743c1c0d9 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -142,6 +142,10 @@ 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
else
raise "Unknown version #{target_version.to_s.inspect}"
end
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/finisher.rb b/railties/lib/rails/application/finisher.rb
index 8d2c13d2a8..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,10 +24,44 @@ 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
+ ActiveSupport::Dependencies::ZeitwerkIntegration.take_over(enable_reloading: !config.cache_classes)
end
end
diff --git a/railties/lib/rails/command/environment_argument.rb b/railties/lib/rails/command/environment_argument.rb
index 0cb3f1ce1e..9945fd1430 100644
--- a/railties/lib/rails/command/environment_argument.rb
+++ b/railties/lib/rails/command/environment_argument.rb
@@ -9,7 +9,9 @@ module Rails
extend ActiveSupport::Concern
included do
- class_attribute :environment_desc, default: "Specifies the environment to run this #{self.command_name} 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
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/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/engine.rb b/railties/lib/rails/engine.rb
index 778bbebe75..06b4019fc7 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -550,7 +550,13 @@ 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.
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 66f6b57379..7e3560d9d2 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
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index 99c1bc4269..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, {}
@@ -137,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?
@@ -183,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/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/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/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/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index 5cffa52860..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
@@ -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..cf84bb6948
--- /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.exists?(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/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 b8e167b488..62d9b1c813 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -1704,6 +1704,61 @@ module ApplicationTests
end
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
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index fb84276b8a..fa9ed868c4 100644
--- a/railties/test/application/mailer_previews_test.rb
+++ b/railties/test/application/mailer_previews_test.rb
@@ -515,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
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/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb
index 147b8f94e1..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,36 @@ 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
@@ -219,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
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/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/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb
index dc6db429a9..9146222f73 100644
--- a/railties/test/application/zeitwerk_integration_test.rb
+++ b/railties/test/application/zeitwerk_integration_test.rb
@@ -98,24 +98,47 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase
assert_nil deps.safe_constantize("Admin")
end
- test "autoloaded_constants returns autoloaded constant paths" do
- app_file "app/models/admin/user.rb", "class Admin::User; 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 Admin::User
- assert_equal ["Admin", "Admin::User"], deps.autoloaded_constants
+ assert Post
+
+ assert deps.autoloaded?("Post")
+ assert deps.autoloaded?(Post)
+ assert_not deps.autoloaded?("User")
+
+ assert_equal ["Post"], deps.autoloaded_constants
end
- test "autoloaded? says if a constant has been autoloaded" do
+ 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
+ boot("production")
assert Post
- assert deps.autoloaded?("Post")
- assert deps.autoloaded?(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
@@ -131,6 +154,29 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase
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
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index ec512b6b64..6de23acebe 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -17,6 +17,16 @@ 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, locals: []).send :method_name
backtrace = [ "app/views/application/index.html.erb:4:in `block in #{method_name}'"]
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/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..503564beec 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -120,7 +120,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 3bdbc70990..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
@@ -827,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
@@ -845,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"
@@ -857,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
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index bf60d6bc22..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
@@ -155,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 540bed551b..e6b614a935 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}"]
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index c97cd17ec6..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
@@ -414,45 +422,57 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
- def test_required_belongs_to_adds_required_association
- run_generator ["account", "supplier:references{required}"]
+ 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
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 3fcfaa9623..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__)
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 69f6e34d58..8ce68dbcfa 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -879,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
@@ -891,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
@@ -1510,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'")