diff options
Diffstat (limited to 'railties')
295 files changed, 4163 insertions, 1776 deletions
diff --git a/railties/.gitignore b/railties/.gitignore index 80dd262d2f..c08562e016 100644 --- a/railties/.gitignore +++ b/railties/.gitignore @@ -1 +1,5 @@ -log/ +/log/ +/test/500.html +/test/fixtures/tmp/ +/test/initializer/root/log/ +/tmp/ diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index ff440b7939..a4d4a87a8b 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,143 +1,68 @@ -* Add `mini_magick` to default `Gemfile` as comment. - - *Yoshiyuki Hirano* - -* Derive `secret_key_base` from the app name in development and test environments. - - Spares away needless secret configs. - - *DHH*, *Kasper Timm Hansen* - -* Support multiple versions arguments for `gem` method of Generators. - - *Yoshiyuki Hirano* - -* Add `--skip-yarn` option to the plugin generator. - - *bogdanvlviv* - -* Optimize routes indentation. - - *Yoshiyuki Hirano* - -* Optimize indentation for generator actions. - - *Yoshiyuki Hirano* - -* Skip unused components when running `bin/rails` in Rails plugin. - - *Yoshiyuki Hirano* - -* Add `git_source` to `Gemfile` for plugin generator. - - *Yoshiyuki Hirano* - -* Add `--skip-action-cable` option to the plugin generator. - - *bogdanvlviv* - -* Deprecate support of use `Rails::Application` subclass to start Rails server. - - *Yuji Yaginuma* - -* Add `ruby x.x.x` version to `Gemfile` and create `.ruby-version` - root file containing the current Ruby version when new Rails applications are - created. - - *Alberto Almagro* - -* Support `-` as a platform-agnostic way to run a script from stdin with - `rails runner` - - *Cody Cutrer* - -* Add `bootsnap` to default `Gemfile`. - - *Burke Libbey* - -* Properly expand shortcuts for environment's name running the `console` - and `dbconsole` commands. - - *Robin Dupret* - -* Passing the environment's name as a regular argument to the - `rails dbconsole` and `rails console` commands is deprecated. - The `-e` option should be used instead. +* Make the master.key file read-only for the owner upon generation on + POSIX-compliant systems. Previously: - $ bin/rails dbconsole production + $ ls -l config/master.key + -rw-r--r-- 1 owner group 32 Jan 1 00:00 master.key Now: - $ bin/rails dbconsole -e production - - *Robin Dupret*, *Kasper Timm Hansen* - -* Allow passing a custom connection name to the `rails dbconsole` - command when using a 3-level database configuration. - - $ bin/rails dbconsole -c replica - - *Robin Dupret*, *Jeremy Daer* - -* Skip unused components when running `bin/rails app:update`. - - If the initial app generation skipped Action Cable, Active Record etc., - the update task honors those skips too. - - *Yuji Yaginuma* - -* Make Rails' test runner work better with minitest plugins. - - By demoting the Rails test runner to just another minitest plugin — - and thereby not eager loading it — we can co-exist much better with - other minitest plugins such as pride and minitest-focus. + $ ls -l config/master.key + -rw------- 1 owner group 32 Jan 1 00:00 master.key - *Kasper Timm Hansen* + Fixes #32604. -* Load environment file in `dbconsole` command. + *Jose Luis Duran* - Fixes #29717. +* Deprecate support for using the `HOST` environment to specify the server IP. - *Yuji Yaginuma* + The `BINDING` environment should be used instead. -* Add `rails secrets:show` command. + Fixes #29516. *Yuji Yaginuma* -* Allow mounting the same engine several times in different locations. - - Fixes #20204. - - *David RodrÃguez* +* Deprecate passing Rack server name as a regular argument to `rails server`. -* Clear screenshot files in `tmp:clear` task. - - *Yuji Yaginuma* + Previously: -* Add `railtie.rb` to the plugin generator + $ bin/rails server thin - *Tsukuru Tanimichi* + There wasn't an explicit option for the Rack server to use, now we have the + `--using` option with the `-u` short switch. -* Deprecate `capify!` method in generators and templates. + Now: - *Yuji Yaginuma* + $ bin/rails server -u thin -* Allow irb options to be passed from `rails console` command. + This change also improves the error message if a missing or mistyped rack + server is given. - Fixes #28988. + *Genadi Samokovarov* - *Yuji Yaginuma* +* Add "rails routes --expanded" option to output routes in expanded mode like + "psql --expanded". Result looks like: -* Added a shared section to `config/database.yml` that will be loaded for all environments. + ``` + $ 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 + ``` - *Pierre Schambacher* + *Benoit Tigeot* -* Namespace error pages' CSS selectors to stop the styles from bleeding into other pages - when using Turbolinks. +* Rails 6 requires Ruby 2.4.1 or newer. - *Jan Krutisch* + *Jeremy Daer* -Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/railties/CHANGELOG.md) for previous changes. +Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/railties/CHANGELOG.md) for previous changes. diff --git a/railties/MIT-LICENSE b/railties/MIT-LICENSE index f9e4444f07..cce00cbc3a 100644 --- a/railties/MIT-LICENSE +++ b/railties/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2017 David Heinemeier Hansson +Copyright (c) 2004-2018 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc index c70a9f0ba0..a4a4b6b235 100644 --- a/railties/RDOC_MAIN.rdoc +++ b/railties/RDOC_MAIN.rdoc @@ -1,4 +1,6 @@ -== Welcome to \Rails += Welcome to \Rails + +== What's \Rails \Rails is a web-application framework that includes everything needed to create database-backed web applications according to the @@ -6,43 +8,48 @@ create database-backed web applications according to the pattern. Understanding the MVC pattern is key to understanding \Rails. MVC divides your -application into three layers, each with a specific responsibility. +application into three layers: Model, View, and Controller, each with a specific responsibility. + +== Model layer -The <em>Model layer</em> represents your domain model (such as Account, Product, -Person, Post, etc.) and encapsulates the business logic that is specific to +The <em><b>Model layer</b></em> represents the domain model (such as Account, Product, +Person, Post, etc.) and encapsulates the business logic specific to your application. In \Rails, database-backed model classes are derived from -ActiveRecord::Base. Active Record allows you to present the data from +<tt>ActiveRecord::Base</tt>. {Active Record}[link:files/activerecord/README_rdoc.html] allows you to present the data from database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in its {README}[link:files/activerecord/README_rdoc.html]. -Although most \Rails models are backed by a database, models can also be ordinary +methods. Although most \Rails models are backed by a database, models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by -the Active Model module. You can read more about Active Model in its {README}[link:files/activemodel/README_rdoc.html]. +the {Active Model}[link:files/activemodel/README_rdoc.html] module. + +== Controller layer -The <em>Controller layer</em> is responsible for handling incoming HTTP requests and +The <em><b>Controller layer</b></em> is responsible for handling incoming HTTP requests and providing a suitable response. Usually this means returning \HTML, but \Rails controllers can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers load and manipulate models, and render view templates in order to generate the appropriate HTTP response. In \Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and -controller classes are derived from ActionController::Base. Action Dispatch and Action Controller -are bundled together in Action Pack. You can read more about Action Pack in its -{README}[link:files/actionpack/README_rdoc.html]. +controller classes are derived from <tt>ActionController::Base</tt>. Action Dispatch and Action Controller +are bundled together in {Action Pack}[link:files/actionpack/README_rdoc.html]. -The <em>View layer</em> is composed of "templates" that are responsible for providing +== View layer + +The <em><b>View layer</b></em> is composed of "templates" that are responsible for providing appropriate representations of your application's resources. Templates can come in a variety of formats, but most view templates are \HTML with embedded Ruby code (ERB files). Views are typically rendered to generate a controller response, -or to generate the body of an email. In \Rails, View generation is handled by Action View. -You can read more about Action View in its {README}[link:files/actionview/README_rdoc.html]. +or to generate the body of an email. In \Rails, View generation is handled by {Action View}[link:files/actionview/README_rdoc.html]. + +== Frameworks and libraries -Active Record, Active Model, Action Pack, and Action View can each be used independently outside \Rails. -In addition to that, \Rails also comes with Action Mailer ({README}[link:files/actionmailer/README_rdoc.html]), a library -to generate and send emails; Active Job ({README}[link:files/activejob/README_md.html]), a +{Active Record}[link:files/activerecord/README_rdoc.html], {Active Model}[link:files/activemodel/README_rdoc.html], +{Action Pack}[link:files/actionpack/README_rdoc.html], and {Action View}[link:files/actionview/README_rdoc.html] can each be used independently outside \Rails. +In addition to that, \Rails also comes with {Action Mailer}[link:files/actionmailer/README_rdoc.html], a library +to generate and send emails; {Active Job}[link:files/activejob/README_md.html], a framework for declaring jobs and making them run on a variety of queueing -backends; Action Cable ({README}[link:files/actioncable/README_md.html]), a framework to -integrate WebSockets with a \Rails application; -Active Storage ({README}[link:files/activestorage/README_md.html]), a library to attach cloud -and local files to \Rails applications; -and Active Support ({README}[link:files/activesupport/README_rdoc.html]), a collection +backends; {Action Cable}[link:files/actioncable/README_md.html], a framework to +integrate WebSockets with a \Rails application; {Active Storage}[link:files/activestorage/README_md.html], +a library to attach cloud and local files to \Rails applications; +and {Active Support}[link:files/activesupport/README_rdoc.html], a collection of utility classes and standard library extensions that are useful for \Rails, and may also be used independently outside \Rails. diff --git a/railties/lib/minitest/rails_plugin.rb b/railties/lib/minitest/rails_plugin.rb index 6901b0bbc8..6486fa1798 100644 --- a/railties/lib/minitest/rails_plugin.rb +++ b/railties/lib/minitest/rails_plugin.rb @@ -13,7 +13,7 @@ module Minitest end def self.plugin_rails_options(opts, options) - Rails::TestUnit::Runner.attach_before_load_options(opts) + ::Rails::TestUnit::Runner.attach_before_load_options(opts) opts.on("-b", "--backtrace", "Show the complete backtrace") do options[:full_backtrace] = true @@ -43,10 +43,15 @@ module Minitest Minitest.backtrace_filter = ::Rails.backtrace_cleaner if ::Rails.respond_to?(:backtrace_cleaner) end + # Suppress summary reports when outputting inline rerun snippets. + if reporter.reporters.reject! { |reporter| reporter.kind_of?(SummaryReporter) } + reporter << SuppressedSummaryReporter.new(options[:io], options) + end + # Replace progress reporter for colors. - reporter.reporters.delete_if { |reporter| reporter.kind_of?(SummaryReporter) || reporter.kind_of?(ProgressReporter) } - reporter << SuppressedSummaryReporter.new(options[:io], options) - reporter << ::Rails::TestUnitReporter.new(options[:io], options) + if reporter.reporters.reject! { |reporter| reporter.kind_of?(ProgressReporter) } + reporter << ::Rails::TestUnitReporter.new(options[:io], options) + end end # Backwardscompatibility with Rails 5.0 generated plugin test scripts diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index 04bb9ba94a..092105d502 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "rails/ruby_version_check" +require "rails/ruby_version_check" require "pathname" @@ -11,8 +11,8 @@ require "active_support/core_ext/module/delegation" require "active_support/core_ext/array/extract_options" require "active_support/core_ext/object/blank" -require_relative "rails/application" -require_relative "rails/version" +require "rails/application" +require "rails/version" require "active_support/railtie" require "action_dispatch/railtie" @@ -50,7 +50,7 @@ module Rails def backtrace_cleaner @backtrace_cleaner ||= begin # Relies on Active Support, so we have to lazy load to postpone definition until Active Support has been loaded - require_relative "rails/backtrace_cleaner" + require "rails/backtrace_cleaner" Rails::BacktraceCleaner.new end end diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb index e55b2e2433..f5dccd2381 100644 --- a/railties/lib/rails/all.rb +++ b/railties/lib/rails/all.rb @@ -4,12 +4,12 @@ require "rails" %w( active_record/railtie + active_storage/engine action_controller/railtie action_view/railtie action_mailer/railtie active_job/railtie action_cable/engine - active_storage/engine rails/test_unit/railtie sprockets/railtie ).each do |railtie| diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb index 184f5b14f1..e7f0557584 100644 --- a/railties/lib/rails/api/task.rb +++ b/railties/lib/rails/api/task.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "rdoc/task" -require_relative "generator" +require "rails/api/generator" module Rails module API diff --git a/railties/lib/rails/app_loader.rb b/railties/lib/rails/app_loader.rb index 3e9b3bd4bb..20eb75d95c 100644 --- a/railties/lib/rails/app_loader.rb +++ b/railties/lib/rails/app_loader.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "pathname" -require_relative "version" +require "rails/version" module Rails module AppLoader # :nodoc: @@ -56,7 +56,7 @@ EOS $stderr.puts(BUNDLER_WARNING) Object.const_set(:APP_PATH, File.expand_path("config/application", Dir.pwd)) require File.expand_path("../boot", APP_PATH) - require_relative "commands" + require "rails/commands" break end end diff --git a/railties/lib/rails/app_updater.rb b/railties/lib/rails/app_updater.rb index c2436a69f9..a076d082d5 100644 --- a/railties/lib/rails/app_updater.rb +++ b/railties/lib/rails/app_updater.rb @@ -21,11 +21,12 @@ module Rails private def generator_options options = { api: !!Rails.application.config.api_only, update: true } - options[:skip_active_record] = !defined?(ActiveRecord::Railtie) - options[:skip_action_mailer] = !defined?(ActionMailer::Railtie) - options[:skip_action_cable] = !defined?(ActionCable::Engine) - options[:skip_sprockets] = !defined?(Sprockets::Railtie) - options[:skip_puma] = !defined?(Puma) + options[:skip_active_record] = !defined?(ActiveRecord::Railtie) + options[:skip_active_storage] = !defined?(ActiveStorage::Engine) || !defined?(ActiveRecord::Railtie) + options[:skip_action_mailer] = !defined?(ActionMailer::Railtie) + options[:skip_action_cable] = !defined?(ActionCable::Engine) + options[:skip_sprockets] = !defined?(Sprockets::Railtie) + options[:skip_puma] = !defined?(Puma) options end end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index abfec90b6d..e346d5cc3a 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -6,8 +6,9 @@ require "active_support/core_ext/object/blank" require "active_support/key_generator" require "active_support/message_verifier" require "active_support/encrypted_configuration" -require_relative "engine" -require_relative "secrets" +require "active_support/deprecation" +require "rails/engine" +require "rails/secrets" module Rails # An Engine with the responsibility of coordinating the whole boot process. @@ -173,8 +174,9 @@ module Rails # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220 @caching_key_generator ||= if secret_key_base - ActiveSupport::CachingKeyGenerator.new \ + ActiveSupport::CachingKeyGenerator.new( ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000) + ) else ActiveSupport::LegacyKeyGenerator.new(secrets.secret_token) end @@ -259,8 +261,15 @@ module Rails "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt, "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt, "action_dispatch.authenticated_encrypted_cookie_salt" => config.action_dispatch.authenticated_encrypted_cookie_salt, + "action_dispatch.use_authenticated_cookie_encryption" => config.action_dispatch.use_authenticated_cookie_encryption, + "action_dispatch.encrypted_cookie_cipher" => config.action_dispatch.encrypted_cookie_cipher, + "action_dispatch.signed_cookie_digest" => config.action_dispatch.signed_cookie_digest, "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer, - "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest + "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest, + "action_dispatch.cookies_rotations" => config.action_dispatch.cookies_rotations, + "action_dispatch.content_security_policy" => config.content_security_policy, + "action_dispatch.content_security_policy_report_only" => config.content_security_policy_report_only, + "action_dispatch.content_security_policy_nonce_generator" => config.content_security_policy_nonce_generator ) end end @@ -394,6 +403,12 @@ module Rails # Fallback to config.secret_token if secrets.secret_token isn't set secrets.secret_token ||= config.secret_token + if secrets.secret_token.present? + ActiveSupport::Deprecation.warn( + "`secrets.secret_token` is deprecated in favor of `secret_key_base` and will be removed in Rails 6.0." + ) + end + secrets end end @@ -412,21 +427,55 @@ module Rails # the correct place to store it is in the encrypted credentials file. def secret_key_base if Rails.env.test? || Rails.env.development? - Digest::MD5.hexdigest self.class.name + secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name) else - validate_secret_key_base \ + validate_secret_key_base( ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base + ) end end - # Decrypts the credentials hash as kept in `config/credentials.yml.enc`. This file is encrypted with - # the Rails master key, which is either taken from ENV["RAILS_MASTER_KEY"] or from loading - # `config/master.key`. + # Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with + # the Rails master key, which is either taken from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading + # +config/master.key+. def credentials - @credentials ||= ActiveSupport::EncryptedConfiguration.new \ - config_path: Rails.root.join("config/credentials.yml.enc"), - key_path: Rails.root.join("config/master.key"), - env_key: "RAILS_MASTER_KEY" + @credentials ||= encrypted("config/credentials.yml.enc") + end + + # Shorthand to decrypt any encrypted configurations or files. + # + # For any file added with <tt>bin/rails encrypted:edit</tt> call +read+ to decrypt + # the file with the master key. + # The master key is either stored in +config/master.key+ or <tt>ENV["RAILS_MASTER_KEY"]</tt>. + # + # Rails.application.encrypted("config/mystery_man.txt.enc").read + # # => "We've met before, haven't we?" + # + # It's also possible to interpret encrypted YAML files with +config+. + # + # Rails.application.encrypted("config/credentials.yml.enc").config + # # => { next_guys_line: "I don't think so. Where was it you think we met?" } + # + # Any top-level configs are also accessible directly on the return value: + # + # Rails.application.encrypted("config/credentials.yml.enc").next_guys_line + # # => "I don't think so. Where was it you think we met?" + # + # The files or configs can also be encrypted with a custom key. To decrypt with + # a key in the +ENV+, use: + # + # Rails.application.encrypted("config/special_tokens.yml.enc", env_key: "SPECIAL_TOKENS") + # + # Or to decrypt with a file, that should be version control ignored, relative to +Rails.root+: + # + # Rails.application.encrypted("config/special_tokens.yml.enc", key_path: "config/special_tokens.key") + def encrypted(path, key_path: "config/master.key", env_key: "RAILS_MASTER_KEY") + ActiveSupport::EncryptedConfiguration.new( + config_path: Rails.root.join(path), + key_path: Rails.root.join(key_path), + env_key: env_key, + raise_if_missing_key: config.require_master_key + ) end def to_app #:nodoc: @@ -464,7 +513,7 @@ module Rails def run_tasks_blocks(app) #:nodoc: railties.each { |r| r.run_tasks_blocks(app) } super - require_relative "tasks" + require "rails/tasks" task :environment do ActiveSupport.on_load(:before_initialize) { config.eager_load = false } diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index c24d4573a9..e3c0759f95 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -4,7 +4,7 @@ require "fileutils" require "active_support/notifications" require "active_support/dependencies" require "active_support/descendants_tracker" -require_relative "../secrets" +require "rails/secrets" module Rails class Application diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index b65289177f..bba573499d 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -2,8 +2,8 @@ require "active_support/core_ext/kernel/reporting" require "active_support/file_update_checker" -require_relative "../engine/configuration" -require_relative "../source_annotation_extractor" +require "rails/engine/configuration" +require "rails/source_annotation_extractor" module Rails class Application @@ -16,44 +16,50 @@ module Rails :ssl_options, :public_file_server, :session_options, :time_zone, :reload_classes_only_on_change, :beginning_of_week, :filter_redirect, :x, :enable_dependency_loading, - :read_encrypted_secrets, :log_level + :read_encrypted_secrets, :log_level, :content_security_policy_report_only, + :content_security_policy_nonce_generator, :require_master_key - attr_reader :encoding, :api_only + attr_reader :encoding, :api_only, :loaded_config_version def initialize(*) super - self.encoding = Encoding::UTF_8 - @allow_concurrency = nil - @consider_all_requests_local = false - @filter_parameters = [] - @filter_redirect = [] - @helpers_paths = [] - @public_file_server = ActiveSupport::OrderedOptions.new - @public_file_server.enabled = true - @public_file_server.index_name = "index" - @force_ssl = false - @ssl_options = {} - @session_store = nil - @time_zone = "UTC" - @beginning_of_week = :monday - @log_level = :debug - @generators = app_generators - @cache_store = [ :file_store, "#{root}/tmp/cache/" ] - @railties_order = [:all] - @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"] - @reload_classes_only_on_change = true - @file_watcher = ActiveSupport::FileUpdateChecker - @exceptions_app = nil - @autoflush_log = true - @log_formatter = ActiveSupport::Logger::SimpleFormatter.new - @eager_load = nil - @secret_token = nil - @secret_key_base = nil - @api_only = false - @debug_exception_response_format = nil - @x = Custom.new - @enable_dependency_loading = false - @read_encrypted_secrets = false + self.encoding = Encoding::UTF_8 + @allow_concurrency = nil + @consider_all_requests_local = false + @filter_parameters = [] + @filter_redirect = [] + @helpers_paths = [] + @public_file_server = ActiveSupport::OrderedOptions.new + @public_file_server.enabled = true + @public_file_server.index_name = "index" + @force_ssl = false + @ssl_options = {} + @session_store = nil + @time_zone = "UTC" + @beginning_of_week = :monday + @log_level = :debug + @generators = app_generators + @cache_store = [ :file_store, "#{root}/tmp/cache/" ] + @railties_order = [:all] + @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"] + @reload_classes_only_on_change = true + @file_watcher = ActiveSupport::FileUpdateChecker + @exceptions_app = nil + @autoflush_log = true + @log_formatter = ActiveSupport::Logger::SimpleFormatter.new + @eager_load = nil + @secret_token = nil + @secret_key_base = nil + @api_only = false + @debug_exception_response_format = nil + @x = Custom.new + @enable_dependency_loading = false + @read_encrypted_secrets = false + @content_security_policy = nil + @content_security_policy_report_only = false + @content_security_policy_nonce_generator = nil + @require_master_key = false + @loaded_config_version = nil end def load_defaults(target_version) @@ -71,7 +77,6 @@ module Rails end self.ssl_options = { hsts: { subdomains: true } } - when "5.1" load_defaults "5.0" @@ -82,7 +87,6 @@ module Rails if respond_to?(:action_view) action_view.form_with_generates_remote_forms = true end - when "5.2" load_defaults "5.1" @@ -100,15 +104,27 @@ module Rails if respond_to?(:active_support) active_support.use_authenticated_message_encryption = true + active_support.use_sha1_digests = true end if respond_to?(:action_controller) action_controller.default_protect_from_forgery = true end + if respond_to?(:action_view) + action_view.form_with_generates_ids = true + end + when "6.0" + load_defaults "5.2" + + if respond_to?(:action_view) + action_view.default_enforce_utf8 = false + end else raise "Unknown version #{target_version.to_s.inspect}" end + + @loaded_config_version = target_version end def encoding=(value) @@ -150,6 +166,18 @@ module Rails end end + # Loads the database YAML without evaluating ERB. People seem to + # write ERB that makes the database configuration depend on + # Rails configuration. But we want Rails configuration (specifically + # `rake` and `rails` tasks) to be generated based on information in + # the database yaml, so we need a method that loads the database + # yaml *without* the context of the Rails application. + def load_database_yaml # :nodoc: + path = paths["config/database"].existent.first + return {} unless path + YAML.load_file(path.to_s) + end + # Loads and returns the entire raw configuration of database from # values stored in <tt>config/database.yml</tt>. def database_configuration @@ -225,7 +253,15 @@ module Rails end def annotations - SourceAnnotationExtractor::Annotation + Rails::SourceAnnotationExtractor::Annotation + end + + def content_security_policy(&block) + if block_given? + @content_security_policy = ActionDispatch::ContentSecurityPolicy.new(&block) + else + @content_security_policy + end end class Custom #:nodoc: diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb index ea2273c1f2..433a7ab41f 100644 --- a/railties/lib/rails/application/default_middleware_stack.rb +++ b/railties/lib/rails/application/default_middleware_stack.rb @@ -63,9 +63,15 @@ module Rails middleware.use ::ActionDispatch::Flash end + unless config.api_only + middleware.use ::ActionDispatch::ContentSecurityPolicy::Middleware + end + middleware.use ::Rack::Head middleware.use ::Rack::ConditionalGet middleware.use ::Rack::ETag, "no-cache" + + middleware.use ::Rack::TempfileReaper unless config.api_only end end diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index 3d938be951..c4b188aeee 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -58,7 +58,7 @@ module Rails end # This needs to happen before eager load so it happens - # in exactly the same point regardless of config.cache_classes + # in exactly the same point regardless of config.eager_load initializer :run_prepare_callbacks do |app| app.reloader.prepare! end diff --git a/railties/lib/rails/application_controller.rb b/railties/lib/rails/application_controller.rb index fa8793d81a..b3fe822218 100644 --- a/railties/lib/rails/application_controller.rb +++ b/railties/lib/rails/application_controller.rb @@ -4,6 +4,13 @@ class Rails::ApplicationController < ActionController::Base # :nodoc: self.view_paths = File.expand_path("templates", __dir__) layout "application" + before_action :disable_content_security_policy_nonce! + + content_security_policy do |policy| + policy.script_src :unsafe_inline + policy.style_src :unsafe_inline + end + private def require_local! @@ -15,4 +22,8 @@ class Rails::ApplicationController < ActionController::Base # :nodoc: def local_request? Rails.application.config.consider_all_requests_local || request.local? end + + def disable_content_security_policy_nonce! + request.content_security_policy_nonce_generator = nil + end end diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb index 50a2ed30cf..e56e604fdc 100644 --- a/railties/lib/rails/cli.rb +++ b/railties/lib/rails/cli.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require_relative "app_loader" +require "rails/app_loader" # If we are inside a Rails application this method performs an exec and thus # the rest of this script is not run. Rails::AppLoader.exec_app -require_relative "ruby_version_check" +require "rails/ruby_version_check" Signal.trap("INT") { puts; exit(1) } -require_relative "command" +require "rails/command" if ARGV.first == "plugin" ARGV.shift diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb index 7ceb86198f..9c447c366f 100644 --- a/railties/lib/rails/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "code_statistics_calculator" +require "rails/code_statistics_calculator" require "active_support/core_ext/enumerable" class CodeStatistics #:nodoc: diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb index 812e846837..6d99ac9936 100644 --- a/railties/lib/rails/command.rb +++ b/railties/lib/rails/command.rb @@ -4,7 +4,6 @@ require "active_support" require "active_support/dependencies/autoload" require "active_support/core_ext/enumerable" require "active_support/core_ext/object/blank" -require "active_support/core_ext/hash/transform_values" require "thor" @@ -12,6 +11,7 @@ module Rails module Command extend ActiveSupport::Autoload + autoload :Spellchecker autoload :Behavior autoload :Base diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb index 8df4f98d3e..fa462ef7e9 100644 --- a/railties/lib/rails/command/base.rb +++ b/railties/lib/rails/command/base.rb @@ -6,7 +6,7 @@ require "erb" require "active_support/core_ext/string/filters" require "active_support/core_ext/string/inflections" -require_relative "actions" +require "rails/command/actions" module Rails module Command diff --git a/railties/lib/rails/command/behavior.rb b/railties/lib/rails/command/behavior.rb index 7a6dd28e1a..718e2d9ab2 100644 --- a/railties/lib/rails/command/behavior.rb +++ b/railties/lib/rails/command/behavior.rb @@ -19,46 +19,6 @@ module Rails end private - - # This code is based directly on the Text gem implementation. - # Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher. - # - # Returns a value representing the "cost" of transforming str1 into str2. - def levenshtein_distance(str1, str2) # :doc: - s = str1 - t = str2 - n = s.length - m = t.length - - return m if (0 == n) - return n if (0 == m) - - d = (0..m).to_a - x = nil - - # avoid duplicating an enumerable object in the loop - str2_codepoint_enumerable = str2.each_codepoint - - str1.each_codepoint.with_index do |char1, i| - e = i + 1 - - str2_codepoint_enumerable.with_index do |char2, j| - cost = (char1 == char2) ? 0 : 1 - x = [ - d[j + 1] + 1, # insertion - e + 1, # deletion - d[j] + cost # substitution - ].min - d[j] = e - e = x - end - - d[m] = x - end - - x - end - # Prints a list of generators. def print_list(base, namespaces) return if namespaces.empty? diff --git a/railties/lib/rails/command/helpers/editor.rb b/railties/lib/rails/command/helpers/editor.rb new file mode 100644 index 0000000000..6191d97672 --- /dev/null +++ b/railties/lib/rails/command/helpers/editor.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "active_support/encrypted_file" + +module Rails + module Command + module Helpers + module Editor + private + def ensure_editor_available(command:) + if ENV["EDITOR"].to_s.empty? + say "No $EDITOR to open file in. Assign one like this:" + say "" + say %(EDITOR="mate --wait" #{command}) + say "" + say "For editors that fork and exit immediately, it's important to pass a wait flag," + say "otherwise the credentials will be saved immediately with no chance to edit." + + false + else + true + end + end + + def catch_editing_exceptions + yield + rescue Interrupt + say "Aborted changing file: nothing saved." + rescue ActiveSupport::EncryptedFile::MissingKeyError => error + say error.message + end + end + end + end +end diff --git a/railties/lib/rails/command/spellchecker.rb b/railties/lib/rails/command/spellchecker.rb new file mode 100644 index 0000000000..154358cd45 --- /dev/null +++ b/railties/lib/rails/command/spellchecker.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Rails + module Command + module Spellchecker # :nodoc: + def self.suggest(word, from:) + DidYouMean::SpellChecker.new(dictionary: from.map(&:to_s)).correct(word).first + end + end + end +end diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb index 1aea1e1a96..77961a0292 100644 --- a/railties/lib/rails/commands.rb +++ b/railties/lib/rails/commands.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "command" +require "rails/command" aliases = { "g" => "generate", diff --git a/railties/lib/rails/commands/application/application_command.rb b/railties/lib/rails/commands/application/application_command.rb index 13d47a63bc..f77553b830 100644 --- a/railties/lib/rails/commands/application/application_command.rb +++ b/railties/lib/rails/commands/application/application_command.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../generators" -require_relative "../../generators/rails/app/app_generator" +require "rails/generators" +require "rails/generators/rails/app/app_generator" module Rails module Generators diff --git a/railties/lib/rails/commands/console/console_command.rb b/railties/lib/rails/commands/console/console_command.rb index 5dc695c240..e35faa5b01 100644 --- a/railties/lib/rails/commands/console/console_command.rb +++ b/railties/lib/rails/commands/console/console_command.rb @@ -3,7 +3,7 @@ require "irb" require "irb/completion" -require_relative "../../command/environment_argument" +require "rails/command/environment_argument" module Rails class Console diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb index 88fb032d84..fa54c0362a 100644 --- a/railties/lib/rails/commands/credentials/credentials_command.rb +++ b/railties/lib/rails/commands/credentials/credentials_command.rb @@ -1,10 +1,13 @@ # frozen_string_literal: true require "active_support" +require "rails/command/helpers/editor" module Rails module Command class CredentialsCommand < Rails::Command::Base # :nodoc: + include Helpers::Editor + no_commands do def help say "Usage:\n #{self.class.banner}" @@ -16,43 +19,27 @@ module Rails def edit require_application_and_environment! - ensure_editor_available || (return) - ensure_master_key_has_been_added + ensure_editor_available(command: "bin/rails credentials:edit") || (return) + ensure_master_key_has_been_added if Rails.application.credentials.key.nil? ensure_credentials_have_been_added - change_credentials_in_system_editor + catch_editing_exceptions do + change_credentials_in_system_editor + end say "New credentials encrypted and saved." - rescue Interrupt - say "Aborted changing credentials: nothing saved." - rescue ActiveSupport::EncryptedFile::MissingKeyError => error - say error.message end def show require_application_and_environment! - say Rails.application.credentials.read.presence || - "No credentials have been added yet. Use bin/rails credentials:edit to change that." + + say Rails.application.credentials.read.presence || missing_credentials_message end private - def ensure_editor_available - if ENV["EDITOR"].to_s.empty? - say "No $EDITOR to open credentials in. Assign one like this:" - say "" - say %(EDITOR="mate --wait" bin/rails credentials:edit) - say "" - say "For editors that fork and exit immediately, it's important to pass a wait flag," - say "otherwise the credentials will be saved immediately with no chance to edit." - - false - else - true - end - end - def ensure_master_key_has_been_added master_key_generator.add_master_key_file + master_key_generator.ignore_master_key_file end def ensure_credentials_have_been_added @@ -67,18 +54,26 @@ module Rails def master_key_generator - require_relative "../../generators" - require_relative "../../generators/rails/master_key/master_key_generator" + require "rails/generators" + require "rails/generators/rails/master_key/master_key_generator" Rails::Generators::MasterKeyGenerator.new end def credentials_generator - require_relative "../../generators" - require_relative "../../generators/rails/credentials/credentials_generator" + require "rails/generators" + require "rails/generators/rails/credentials/credentials_generator" Rails::Generators::CredentialsGenerator.new end + + def missing_credentials_message + if Rails.application.credentials.key.nil? + "Missing master key to decrypt credentials. See bin/rails credentials:help" + else + "No credentials have been added yet. Use bin/rails credentials:edit to change that." + end + end end end end diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb index 5234969743..806b7de6d6 100644 --- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb +++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../command/environment_argument" +require "rails/command/environment_argument" module Rails class DBConsole @@ -97,7 +97,7 @@ module Rails elsif configurations[environment].blank? && configurations[connection].blank? raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}" else - configurations[environment].presence || configurations[connection] + configurations[connection] || configurations[environment].presence end end end diff --git a/railties/lib/rails/commands/destroy/destroy_command.rb b/railties/lib/rails/commands/destroy/destroy_command.rb index 686193ddb9..dd432d28fd 100644 --- a/railties/lib/rails/commands/destroy/destroy_command.rb +++ b/railties/lib/rails/commands/destroy/destroy_command.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../generators" +require "rails/generators" module Rails module Command diff --git a/railties/lib/rails/commands/encrypted/encrypted_command.rb b/railties/lib/rails/commands/encrypted/encrypted_command.rb new file mode 100644 index 0000000000..3bc8f76ce4 --- /dev/null +++ b/railties/lib/rails/commands/encrypted/encrypted_command.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require "pathname" +require "active_support" +require "rails/command/helpers/editor" + +module Rails + module Command + class EncryptedCommand < Rails::Command::Base # :nodoc: + include Helpers::Editor + + class_option :key, aliases: "-k", type: :string, + default: "config/master.key", desc: "The Rails.root relative path to the encryption key" + + no_commands do + def help + say "Usage:\n #{self.class.banner}" + say "" + end + end + + def edit(file_path) + require_application_and_environment! + encrypted = Rails.application.encrypted(file_path, key_path: options[:key]) + + ensure_editor_available(command: "bin/rails encrypted:edit") || (return) + ensure_encryption_key_has_been_added(options[:key]) if encrypted.key.nil? + ensure_encrypted_file_has_been_added(file_path, options[:key]) + + catch_editing_exceptions do + change_encrypted_file_in_system_editor(file_path, options[:key]) + end + + say "File encrypted and saved." + rescue ActiveSupport::MessageEncryptor::InvalidMessage + say "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?" + end + + def show(file_path) + require_application_and_environment! + encrypted = Rails.application.encrypted(file_path, key_path: options[:key]) + + say encrypted.read.presence || missing_encrypted_message(key: encrypted.key, key_path: options[:key], file_path: file_path) + end + + private + def ensure_encryption_key_has_been_added(key_path) + encryption_key_file_generator.add_key_file(key_path) + encryption_key_file_generator.ignore_key_file(key_path) + end + + def ensure_encrypted_file_has_been_added(file_path, key_path) + encrypted_file_generator.add_encrypted_file_silently(file_path, key_path) + end + + def change_encrypted_file_in_system_editor(file_path, key_path) + Rails.application.encrypted(file_path, key_path: key_path).change do |tmp_path| + system("#{ENV["EDITOR"]} #{tmp_path}") + end + end + + + def encryption_key_file_generator + require "rails/generators" + require "rails/generators/rails/encryption_key_file/encryption_key_file_generator" + + Rails::Generators::EncryptionKeyFileGenerator.new + end + + def encrypted_file_generator + require "rails/generators" + require "rails/generators/rails/encrypted_file/encrypted_file_generator" + + Rails::Generators::EncryptedFileGenerator.new + end + + def missing_encrypted_message(key:, key_path:, file_path:) + if key.nil? + "Missing '#{key_path}' to decrypt data. See bin/rails encrypted:help" + else + "File '#{file_path}' does not exist. Use bin/rails encrypted:edit #{file_path} to change that." + end + end + end + end +end diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb index 73f627637d..93d7a0ce3a 100644 --- a/railties/lib/rails/commands/generate/generate_command.rb +++ b/railties/lib/rails/commands/generate/generate_command.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../generators" +require "rails/generators" module Rails module Command diff --git a/railties/lib/rails/commands/plugin/plugin_command.rb b/railties/lib/rails/commands/plugin/plugin_command.rb index 5d3dfadf84..2b192abf9b 100644 --- a/railties/lib/rails/commands/plugin/plugin_command.rb +++ b/railties/lib/rails/commands/plugin/plugin_command.rb @@ -36,8 +36,8 @@ module Rails private def run_plugin_generator(plugin_args) - require_relative "../../generators" - require_relative "../../generators/rails/plugin/plugin_generator" + require "rails/generators" + require "rails/generators/rails/plugin/plugin_generator" Rails::Generators::PluginGenerator.start plugin_args end end diff --git a/railties/lib/rails/commands/routes/routes_command.rb b/railties/lib/rails/commands/routes/routes_command.rb new file mode 100644 index 0000000000..b592a5212f --- /dev/null +++ b/railties/lib/rails/commands/routes/routes_command.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "rails/command" + +module Rails + module Command + class RoutesCommand < Base # :nodoc: + class_option :controller, aliases: "-c", desc: "Filter by a specific controller, e.g. PostsController or Admin::PostsController." + class_option :grep, aliases: "-g", desc: "Grep routes by a specific pattern." + class_option :expanded, type: :boolean, aliases: "-E", desc: "Print routes expanded vertically with parts explained." + + def perform(*) + require_application_and_environment! + require "action_dispatch/routing/inspector" + + say inspector.format(formatter, routes_filter) + end + + private + def inspector + ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes) + end + + def formatter + if options.key?("expanded") + ActionDispatch::Routing::ConsoleFormatter::Expanded.new + else + ActionDispatch::Routing::ConsoleFormatter::Sheet.new + end + end + + def routes_filter + options.symbolize_keys.slice(:controller, :grep) + end + end + end +end diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb index cd9462e08f..30fbf04982 100644 --- a/railties/lib/rails/commands/runner/runner_command.rb +++ b/railties/lib/rails/commands/runner/runner_command.rb @@ -32,13 +32,13 @@ module Rails ARGV.replace(command_argv) if code_or_file == "-" - eval($stdin.read, binding, "stdin") + eval($stdin.read, TOPLEVEL_BINDING, "stdin") elsif File.exist?(code_or_file) $0 = code_or_file Kernel.load code_or_file else begin - eval(code_or_file, binding, __FILE__, __LINE__) + eval(code_or_file, TOPLEVEL_BINDING, __FILE__, __LINE__) rescue SyntaxError, NameError => error $stderr.puts "Please specify a valid ruby command or the path of a script to run." $stderr.puts "Run '#{self.class.executable} -h' for help." diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb index d93c4de74e..a36ccf314c 100644 --- a/railties/lib/rails/commands/secrets/secrets_command.rb +++ b/railties/lib/rails/commands/secrets/secrets_command.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support" -require_relative "../../secrets" +require "rails/secrets" module Rails module Command @@ -15,7 +15,7 @@ module Rails end def setup - generator.start + deprecate_in_favor_of_credentials_and_exit end def edit @@ -42,11 +42,10 @@ module Rails rescue Rails::Secrets::MissingKeyError => error say error.message rescue Errno::ENOENT => error - raise unless error.message =~ /secrets\.yml\.enc/ - - Rails::Secrets.read_template_for_editing do |tmp_path| - system("#{ENV["EDITOR"]} #{tmp_path}") - generator.skip_secrets_file { setup } + if error.message =~ /secrets\.yml\.enc/ + deprecate_in_favor_of_credentials_and_exit + else + raise end end @@ -55,11 +54,11 @@ module Rails end private - def generator - require_relative "../../generators" - require_relative "../../generators/rails/encrypted_secrets/encrypted_secrets_generator" + def deprecate_in_favor_of_credentials_and_exit + say "Encrypted secrets is deprecated in favor of credentials. Run:" + say "bin/rails credentials:help" - Rails::Generators::EncryptedSecretsGenerator + exit 1 end end end diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index 785265d766..77b6c1f65d 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -6,7 +6,7 @@ require "action_dispatch" require "rails" require "active_support/deprecation" require "active_support/core_ext/string/filters" -require_relative "../../dev_caching" +require "rails/dev_caching" module Rails class Server < ::Rack::Server @@ -27,7 +27,7 @@ module Rails app = super if app.is_a?(Class) ActiveSupport::Deprecation.warn(<<-MSG.squish) - Use `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0. + Using `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0. Please change `run #{app}` to `run Rails.application` in config.ru. MSG end @@ -43,18 +43,22 @@ module Rails ENV["RAILS_ENV"] ||= options[:environment] end - def start - print_boot_information + def start(after_stop_callback = nil) trap(:INT) { exit } create_tmp_directories setup_dev_caching log_to_stdout if options[:log_stdout] - super + super() ensure - # The '-h' option calls exit before @options is set. - # If we call 'options' with it unset, we get double help banners. - puts "Exiting" unless @options && options[:daemonize] + after_stop_callback.call if after_stop_callback + end + + def serveable? # :nodoc: + server + true + rescue LoadError, NameError + false end def middleware @@ -65,6 +69,10 @@ module Rails super.merge(@default_options) end + def served_url + "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma? + end + private def setup_dev_caching if options[:environment] == "development" @@ -72,13 +80,6 @@ module Rails end end - def print_boot_information - url = "on #{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma? - puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" - puts "=> Rails #{Rails.version} application starting in #{Rails.env} #{url}" - puts "=> Run `rails server -h` for more startup options" - end - def create_tmp_directories %w(cache pids sockets).each do |dir_to_make| FileUtils.mkdir_p(File.join(Rails.root, "tmp", dir_to_make)) @@ -108,9 +109,15 @@ module Rails module Command class ServerCommand < Base # :nodoc: + # Hard-coding a bunch of handlers here as we don't have a public way of + # querying them from the Rack::Handler registry. + RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn) + DEFAULT_PORT = 3000 DEFAULT_PID_PATH = "tmp/pids/server.pid".freeze + argument :using, optional: true + class_option :port, aliases: "-p", type: :numeric, desc: "Runs Rails on the specified port - defaults to 3000.", banner: :port class_option :binding, aliases: "-b", type: :string, @@ -122,28 +129,39 @@ module Rails 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, desc: "Specifies the PID file." class_option "dev-caching", aliases: "-C", type: :boolean, default: nil, desc: "Specifies whether to perform caching in development." class_option "restart", type: :boolean, default: nil, hide: true + class_option "early_hints", type: :boolean, default: nil, desc: "Enables HTTP/2 early hints." def initialize(args = [], local_options = {}, config = {}) @original_options = local_options super - @server = self.args.shift + @using = deprecated_positional_rack_server(using) || options[:using] @log_stdout = options[:daemon].blank? && (options[:environment] || Rails.env) == "development" end def perform set_application_directory! prepare_restart + Rails::Server.new(server_options).tap do |server| # Require application after server sets environment to propagate # the --environment option. require APP_PATH Dir.chdir(Rails.application.root) - server.start + + if server.serveable? + print_boot_information(server.server, server.served_url) + after_stop_callback = -> { say "Exiting" unless options[:daemon] } + server.start(after_stop_callback) + else + say rack_server_suggestion(using) + end end end @@ -151,7 +169,7 @@ module Rails def server_options { user_supplied_options: user_supplied_options, - server: @server, + server: using, log_stdout: @log_stdout, Port: port, Host: host, @@ -161,7 +179,8 @@ module Rails daemonize: options[:daemon], pid: pid, caching: options["dev-caching"], - restart_cmd: restart_command + restart_cmd: restart_command, + early_hints: early_hints } end end @@ -200,7 +219,7 @@ module Rails user_supplied_options << name end end - user_supplied_options << :Host if ENV["HOST"] + user_supplied_options << :Host if ENV["HOST"] || ENV["BINDING"] user_supplied_options << :Port if ENV["PORT"] user_supplied_options.uniq end @@ -215,7 +234,17 @@ module Rails options[:binding] else default_host = environment == "development" ? "localhost" : "0.0.0.0" - ENV.fetch("HOST", default_host) + + 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. + MSG + + return ENV["HOST"] + end + + ENV.fetch("BINDING", default_host) end end @@ -224,7 +253,11 @@ module Rails end def restart_command - "bin/rails server #{@server} #{@original_options.join(" ")} --restart" + "bin/rails server #{using} #{@original_options.join(" ")} --restart" + end + + def early_hints + options[:early_hints] end def pid @@ -232,12 +265,50 @@ module Rails end def self.banner(*) - "rails server [puma, thin etc] [options]" + "rails server [thin/puma/webrick] [options]" end def prepare_restart FileUtils.rm_f(options[:pid]) if options[:restart] end + + def deprecated_positional_rack_server(value) + if value + 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. + MSG + value + end + end + + def rack_server_suggestion(server) + if server.in?(RACK_SERVERS) + <<~MSG + Could not load server "#{server}". Maybe you need to the add it to the Gemfile? + + gem "#{server}" + + Run `rails server --help` for more options. + MSG + else + suggestions = Rails::Command::Spellchecker.suggest(server, from: RACK_SERVERS) + + <<~MSG + Could not find server "#{server}". Maybe you meant #{suggestions.inspect}? + Run `rails server --help` for more options. + MSG + end + end + + def print_boot_information(server, url) + say <<~MSG + => Booting #{ActiveSupport::Inflector.demodulize(server)} + => Rails #{Rails.version} application starting in #{Rails.env} #{url} + => Run `rails server --help` for more startup options + MSG + end end end end diff --git a/railties/lib/rails/commands/test/test_command.rb b/railties/lib/rails/commands/test/test_command.rb index a2216553ca..00ea9ac4a6 100644 --- a/railties/lib/rails/commands/test/test_command.rb +++ b/railties/lib/rails/commands/test/test_command.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../../command" -require_relative "../../test_unit/runner" -require_relative "../../test_unit/reporter" +require "rails/command" +require "rails/test_unit/runner" +require "rails/test_unit/reporter" module Rails module Command diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 70815d114d..d3a54d9364 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -2,8 +2,8 @@ require "active_support/ordered_options" require "active_support/core_ext/object" -require_relative "paths" -require_relative "rack" +require "rails/paths" +require "rails/rack" module Rails module Configuration diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index cc2030d37d..6a13a84108 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "railtie" -require_relative "engine/railties" +require "rails/railtie" +require "rails/engine/railties" require "active_support/core_ext/module/delegation" require "pathname" require "thread" @@ -439,8 +439,8 @@ module Rails # Load console and invoke the registered hooks. # Check <tt>Rails::Railtie.console</tt> for more info. def load_console(app = self) - require_relative "console/app" - require_relative "console/helpers" + require "rails/console/app" + require "rails/console/helpers" run_console_blocks(app) self end @@ -463,7 +463,7 @@ module Rails # Load Rails generators and invoke the registered hooks. # Check <tt>Rails::Railtie.generators</tt> for more info. def load_generators(app = self) - require_relative "generators" + require "rails/generators" run_generators_blocks(app) Rails::Generators.configure!(app.config.generators) self diff --git a/railties/lib/rails/engine/commands.rb b/railties/lib/rails/engine/commands.rb index 3854907507..05218640c6 100644 --- a/railties/lib/rails/engine/commands.rb +++ b/railties/lib/rails/engine/commands.rb @@ -6,4 +6,4 @@ unless defined?(APP_PATH) end end -require_relative "../commands" +require "rails/commands" diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index 16ba7f9eb8..6bf0406b21 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../railtie/configuration" +require "rails/railtie/configuration" module Rails class Engine diff --git a/railties/lib/rails/engine/updater.rb b/railties/lib/rails/engine/updater.rb index 21a0fc5562..be7a47124a 100644 --- a/railties/lib/rails/engine/updater.rb +++ b/railties/lib/rails/engine/updater.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../generators" -require_relative "../generators/rails/plugin/plugin_generator" +require "rails/generators" +require "rails/generators/rails/plugin/plugin_generator" module Rails class Engine diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb index 92b5e0392a..54bfbdd516 100644 --- a/railties/lib/rails/gem_version.rb +++ b/railties/lib/rails/gem_version.rb @@ -7,8 +7,8 @@ module Rails end module VERSION - MAJOR = 5 - MINOR = 2 + MAJOR = 6 + MINOR = 0 TINY = 0 PRE = "alpha" diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index a630d55e59..f8460bd4ee 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -4,7 +4,7 @@ activesupport_path = File.expand_path("../../../activesupport/lib", __dir__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) require "thor/group" -require_relative "command" +require "rails/command" require "active_support" require "active_support/core_ext/object/blank" @@ -218,6 +218,10 @@ module Rails rails.delete("app") rails.delete("plugin") rails.delete("encrypted_secrets") + rails.delete("encrypted_file") + rails.delete("encryption_key_file") + rails.delete("master_key") + rails.delete("credentials") hidden_namespaces.each { |n| groups.delete(n.to_s) } @@ -272,11 +276,11 @@ module Rails klass.start(args, config) else options = sorted_groups.flat_map(&:last) - suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3) - msg = "Could not find generator '#{namespace}'. ".dup - msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n" - msg << "Run `rails generate --help` for more options." - puts msg + suggestion = Rails::Command::Spellchecker.suggest(namespace.to_s, from: options) + puts <<~MSG + Could not find generator '#{namespace}'. Maybe you meant #{suggestion.inspect}? + Run `rails generate --help` for more options. + MSG end end diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 9800e5750a..d85bbfb03e 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "active_support/core_ext/string/strip" + module Rails module Generators module Actions @@ -221,6 +223,7 @@ module Rails # rake("db:migrate") # rake("db:migrate", env: "production") # rake("gems:install", sudo: true) + # rake("gems:install", capture: true) def rake(command, options = {}) execute_command :rake, command, options end @@ -230,6 +233,7 @@ module Rails # rails_command("db:migrate") # rails_command("db:migrate", env: "production") # rails_command("gems:install", sudo: true) + # rails_command("gems:install", capture: true) def rails_command(command, options = {}) execute_command :rails, command, options end @@ -292,7 +296,11 @@ module Rails log executor, command env = options[:env] || ENV["RAILS_ENV"] || "development" sudo = options[:sudo] && !Gem.win_platform? ? "sudo " : "" - in_root { run("#{sudo}#{extify(executor)} #{command} RAILS_ENV=#{env}", verbose: false) } + config = { verbose: false } + + config.merge!(capture: options[:capture]) if options[:capture] + + in_root { run("#{sudo}#{extify(executor)} #{command} RAILS_ENV=#{env}", config) } end # Add an extension to the given name based on the platform. diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index c8688fb7f3..f51542f3ec 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -2,11 +2,10 @@ require "fileutils" require "digest/md5" -require "active_support/core_ext/string/strip" -require_relative "../version" unless defined?(Rails::VERSION) +require "rails/version" unless defined?(Rails::VERSION) require "open-uri" require "uri" -require_relative "../generators" +require "rails/generators" require "active_support/core_ext/array/extract_options" module Rails @@ -26,75 +25,81 @@ module Rails end def self.add_shared_options_for(name) - class_option :template, type: :string, aliases: "-m", - desc: "Path to some #{name} template (can be a filesystem path or URL)" + class_option :template, type: :string, aliases: "-m", + desc: "Path to some #{name} template (can be a filesystem path or URL)" - class_option :database, type: :string, aliases: "-d", default: "sqlite3", - desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})" + class_option :database, type: :string, aliases: "-d", default: "sqlite3", + desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})" - class_option :skip_yarn, type: :boolean, default: false, - desc: "Don't use Yarn for managing JavaScript dependencies" + class_option :skip_yarn, type: :boolean, default: false, + desc: "Don't use Yarn for managing JavaScript dependencies" - class_option :skip_gemfile, type: :boolean, default: false, - desc: "Don't create a Gemfile" + class_option :skip_gemfile, type: :boolean, default: false, + desc: "Don't create a Gemfile" - class_option :skip_git, type: :boolean, aliases: "-G", default: false, - desc: "Skip .gitignore file" + class_option :skip_git, type: :boolean, aliases: "-G", default: false, + desc: "Skip .gitignore file" - class_option :skip_keeps, type: :boolean, default: false, - desc: "Skip source control .keep files" + class_option :skip_keeps, type: :boolean, default: false, + desc: "Skip source control .keep files" - class_option :skip_action_mailer, type: :boolean, aliases: "-M", - default: false, - desc: "Skip Action Mailer files" + class_option :skip_action_mailer, type: :boolean, aliases: "-M", + default: false, + desc: "Skip Action Mailer files" - class_option :skip_active_record, type: :boolean, aliases: "-O", default: false, - desc: "Skip Active Record files" + class_option :skip_active_record, type: :boolean, aliases: "-O", default: false, + desc: "Skip Active Record files" - class_option :skip_puma, type: :boolean, aliases: "-P", default: false, - desc: "Skip Puma related files" + class_option :skip_active_storage, type: :boolean, default: false, + desc: "Skip Active Storage files" - class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false, - desc: "Skip Action Cable files" + class_option :skip_puma, type: :boolean, aliases: "-P", default: false, + desc: "Skip Puma related files" - class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false, - desc: "Skip Sprockets files" + class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false, + desc: "Skip Action Cable files" - class_option :skip_spring, type: :boolean, default: false, - desc: "Don't install Spring application preloader" + class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false, + desc: "Skip Sprockets files" - class_option :skip_listen, type: :boolean, default: false, - desc: "Don't generate configuration that depends on the listen gem" + class_option :skip_spring, type: :boolean, default: false, + desc: "Don't install Spring application preloader" - class_option :skip_coffee, type: :boolean, default: false, - desc: "Don't use CoffeeScript" + class_option :skip_listen, type: :boolean, default: false, + desc: "Don't generate configuration that depends on the listen gem" - class_option :skip_javascript, type: :boolean, aliases: "-J", default: false, - desc: "Skip JavaScript files" + class_option :skip_coffee, type: :boolean, default: false, + desc: "Don't use CoffeeScript" - class_option :skip_turbolinks, type: :boolean, default: false, - desc: "Skip turbolinks gem" + class_option :skip_javascript, type: :boolean, aliases: "-J", default: false, + desc: "Skip JavaScript files" - class_option :skip_test, type: :boolean, aliases: "-T", default: false, - desc: "Skip test files" + class_option :skip_turbolinks, type: :boolean, default: false, + desc: "Skip turbolinks gem" - class_option :skip_system_test, type: :boolean, default: false, - desc: "Skip system test files" + class_option :skip_test, type: :boolean, aliases: "-T", default: false, + desc: "Skip test files" - class_option :dev, type: :boolean, default: false, - desc: "Setup the #{name} with Gemfile pointing to your Rails checkout" + class_option :skip_system_test, type: :boolean, default: false, + desc: "Skip system test files" - class_option :edge, type: :boolean, default: false, - desc: "Setup the #{name} with Gemfile pointing to Rails repository" + class_option :skip_bootsnap, type: :boolean, default: false, + desc: "Skip bootsnap gem" - class_option :rc, type: :string, default: nil, - desc: "Path to file containing extra configuration options for rails command" + class_option :dev, type: :boolean, default: false, + desc: "Setup the #{name} with Gemfile pointing to your Rails checkout" - class_option :no_rc, type: :boolean, default: false, - desc: "Skip loading of extra configuration options from .railsrc file" + class_option :edge, type: :boolean, default: false, + desc: "Setup the #{name} with Gemfile pointing to Rails repository" - class_option :help, type: :boolean, aliases: "-h", group: :rails, - desc: "Show this help message and quit" + class_option :rc, type: :string, default: nil, + desc: "Path to file containing extra configuration options for rails command" + + class_option :no_rc, type: :boolean, default: false, + desc: "Skip loading of extra configuration options from .railsrc file" + + class_option :help, type: :boolean, aliases: "-h", group: :rails, + desc: "Show this help message and quit" end def initialize(*args) @@ -189,15 +194,33 @@ module Rails def webserver_gemfile_entry # :doc: return [] if options[:skip_puma] comment = "Use Puma as the app server" - GemfileEntry.new("puma", "~> 3.7", comment) + GemfileEntry.new("puma", "~> 3.11", comment) end def include_all_railties? # :doc: - options.values_at(:skip_active_record, :skip_action_mailer, :skip_test, :skip_sprockets, :skip_action_cable).none? + [ + options.values_at( + :skip_active_record, + :skip_action_mailer, + :skip_test, + :skip_sprockets, + :skip_action_cable + ), + skip_active_storage? + ].flatten.none? end def comment_if(value) # :doc: - options[value] ? "# " : "" + question = "#{value}?" + + comment = + if respond_to?(question, true) + send(question) + else + options[value] + end + + comment ? "# " : "" end def keeps? # :doc: @@ -208,6 +231,10 @@ module Rails !options[:skip_active_record] && options[:database] == "sqlite3" end + def skip_active_storage? # :doc: + options[:skip_active_storage] || options[:skip_active_record] + end + class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out) def initialize(name, version, comment, options = {}, commented_out = false) super @@ -241,17 +268,14 @@ module Rails end def rails_gemfile_entry - dev_edge_common = [ - GemfileEntry.github("arel", "rails/arel"), - ] if options.dev? [ GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH) - ] + dev_edge_common + ] elsif options.edge? [ GemfileEntry.github("rails", "rails/rails") - ] + dev_edge_common + ] else [GemfileEntry.version("rails", rails_version_specifier, @@ -275,8 +299,8 @@ module Rails def gem_for_database # %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql ) case options[:database] - when "mysql" then ["mysql2", [">= 0.3.18", "< 0.5"]] - when "postgresql" then ["pg", ["~> 0.18"]] + when "mysql" then ["mysql2", [">= 0.4.4", "< 0.6.0"]] + when "postgresql" then ["pg", [">= 0.18", "< 2.0"]] when "oracle" then ["activerecord-oracle_enhanced-adapter", nil] when "frontbase" then ["ruby-frontbase", nil] when "sqlserver" then ["activerecord-sqlserver-adapter", nil] @@ -290,11 +314,13 @@ module Rails def convert_database_option_for_jruby if defined?(JRUBY_VERSION) - case options[:database] - when "postgresql" then options[:database].replace "jdbcpostgresql" - when "mysql" then options[:database].replace "jdbcmysql" - when "sqlite3" then options[:database].replace "jdbcsqlite3" + opt = options.dup + case opt[:database] + when "postgresql" then opt[:database] = "jdbcpostgresql" + when "mysql" then opt[:database] = "jdbcmysql" + when "sqlite3" then opt[:database] = "jdbcsqlite3" end + self.options = opt.freeze end end @@ -369,7 +395,7 @@ module Rails return [] if options[:skip_action_cable] comment = "Use Redis adapter to run Action Cable in production" gems = [] - gems << GemfileEntry.new("redis", "~> 3.0", comment, {}, true) + gems << GemfileEntry.new("redis", "~> 4.0", comment, {}, true) gems end @@ -413,6 +439,10 @@ module Rails !options[:skip_listen] && os_supports_listen_out_of_the_box? end + def depend_on_bootsnap? + !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION) + end + def os_supports_listen_out_of_the_box? RbConfig::CONFIG["host_os"] =~ /darwin|linux/ end diff --git a/railties/lib/rails/generators/css/assets/assets_generator.rb b/railties/lib/rails/generators/css/assets/assets_generator.rb index 5f7be769b2..f657d1e50f 100644 --- a/railties/lib/rails/generators/css/assets/assets_generator.rb +++ b/railties/lib/rails/generators/css/assets/assets_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../named_base" +require "rails/generators/named_base" module Css # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb index 5996cb1483..89c560f382 100644 --- a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb @@ -1,17 +1,17 @@ # frozen_string_literal: true -require_relative "../../named_base" +require "rails/generators/named_base" module Css # :nodoc: module Generators # :nodoc: class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc: + source_root Rails::Generators::ScaffoldGenerator.source_root + # In order to allow the Sass generators to pick up the default Rails CSS and # transform it, we leave it in a standard location for the CSS stylesheet # generators to handle. For the simple, default case, just copy it over. def copy_stylesheet - dir = Rails::Generators::ScaffoldGenerator.source_root - file = File.join(dir, "scaffold.css") - create_file "app/assets/stylesheets/scaffold.css", File.read(file) + copy_file "scaffold.css", "app/assets/stylesheets/scaffold.css" end end end diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb index 2c20834611..ba20bcd32a 100644 --- a/railties/lib/rails/generators/erb.rb +++ b/railties/lib/rails/generators/erb.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "named_base" +require "rails/generators/named_base" module Erb # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/erb/controller/controller_generator.rb b/railties/lib/rails/generators/erb/controller/controller_generator.rb index 1a6c84288b..8e13744b2a 100644 --- a/railties/lib/rails/generators/erb/controller/controller_generator.rb +++ b/railties/lib/rails/generators/erb/controller/controller_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../erb" +require "rails/generators/erb" module Erb # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/erb/controller/templates/view.html.erb b/railties/lib/rails/generators/erb/controller/templates/view.html.erb.tt index cd54d13d83..cd54d13d83 100644 --- a/railties/lib/rails/generators/erb/controller/templates/view.html.erb +++ b/railties/lib/rails/generators/erb/controller/templates/view.html.erb.tt diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb index 5774d86c8e..997602cb8c 100644 --- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../erb" +require "rails/generators/erb" module Erb # :nodoc: module Generators # :nodoc: @@ -35,7 +35,7 @@ module Erb # :nodoc: end def file_name - @_file_name ||= super.gsub(/_mailer/i, "") + @_file_name ||= super.sub(/_mailer\z/i, "") end end end diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.html.erb b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb.tt index b5045671b3..b5045671b3 100644 --- a/railties/lib/rails/generators/erb/mailer/templates/view.html.erb +++ b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb.tt diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.text.erb b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb.tt index 342285df19..342285df19 100644 --- a/railties/lib/rails/generators/erb/mailer/templates/view.text.erb +++ b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb.tt diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb index e80c6d4b7d..2fc04e4094 100644 --- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../erb" -require_relative "../../resource_helpers" +require "rails/generators/erb" +require "rails/generators/resource_helpers" module Erb # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt index 4f2e84f924..518cb1121e 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt @@ -1,4 +1,4 @@ -<%%= form_with(model: <%= singular_table_name %>, local: true) do |form| %> +<%%= form_with(model: <%= model_resource_name %>, local: true) do |form| %> <%% if <%= singular_table_name %>.errors.any? %> <div id="error_explanation"> <h2><%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2> @@ -15,15 +15,15 @@ <div class="field"> <% if attribute.password_digest? -%> <%%= form.label :password %> - <%%= form.password_field :password, id: :<%= field_id(:password) %> %> + <%%= form.password_field :password %> </div> <div class="field"> <%%= form.label :password_confirmation %> - <%%= form.password_field :password_confirmation, id: :<%= field_id(:password_confirmation) %> %> + <%%= form.password_field :password_confirmation %> <% else -%> <%%= form.label :<%= attribute.column_name %> %> - <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, id: :<%= field_id(attribute.column_name) %> %> + <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %> %> <% end -%> </div> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt index 81329473d9..81329473d9 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt index 5f4904fee1..e1ede7c713 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt @@ -18,9 +18,9 @@ <% attributes.reject(&:password_digest?).each do |attribute| -%> <td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td> <% end -%> - <td><%%= link_to 'Show', <%= singular_table_name %> %></td> - <td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td> - <td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td> + <td><%%= link_to 'Show', <%= model_resource_name %> %></td> + <td><%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %></td> + <td><%%= link_to 'Destroy', <%= model_resource_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <%% end %> </tbody> @@ -28,4 +28,4 @@ <br> -<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_table_name %>_path %> +<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_route_name %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt index 9b2b2f4875..9b2b2f4875 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt index 5e634153be..5e634153be 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb index 2728459968..f7fd30a5fb 100644 --- a/railties/lib/rails/generators/generated_attribute.rb +++ b/railties/lib/rails/generators/generated_attribute.rb @@ -75,7 +75,7 @@ module Rails when :date then :date_select when :text then :text_area when :boolean then :check_box - else + else :text_field end end @@ -91,7 +91,7 @@ module Rails when :text then "MyText" when :boolean then false when :references, :belongs_to then nil - else + else "" end end diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb index ea151aa04e..9d32c666dc 100644 --- a/railties/lib/rails/generators/js/assets/assets_generator.rb +++ b/railties/lib/rails/generators/js/assets/assets_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../named_base" +require "rails/generators/named_base" module Js # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb index 7162b8c0b4..5081060895 100644 --- a/railties/lib/rails/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/concern" -require_relative "actions/create_migration" +require "rails/generators/actions/create_migration" module Rails module Generators @@ -63,7 +63,12 @@ module Rails numbered_destination = File.join(dir, ["%migration_number%", base].join("_")) create_migration numbered_destination, nil, config do - ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context) + match = ERB.version.match(/\Aerb\.rb \[(?<version>[^ ]+) /) + if match && match[:version] >= "2.2.0" # Ruby 2.6+ + ERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer").result(context) + else + ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context) + end end end end diff --git a/railties/lib/rails/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb index aa3564476a..50078404b3 100644 --- a/railties/lib/rails/generators/model_helpers.rb +++ b/railties/lib/rails/generators/model_helpers.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "active_model" +require "rails/generators/active_model" module Rails module Generators diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index fe8447be23..d6732f8ff1 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true -require "active_support/core_ext/module/introspection" -require_relative "base" -require_relative "generated_attribute" +require "rails/generators/base" +require "rails/generators/generated_attribute" module Rails module Generators @@ -32,12 +31,8 @@ module Rails end end - # TODO Change this to private once we've dropped Ruby 2.2 support. - # Workaround for Ruby 2.2 "private attribute?" warning. - protected - attr_reader :file_name - private + attr_reader :file_name # FIXME: We are avoiding to use alias because a bug on thor that make # this method public and add it to the task list. @@ -100,11 +95,11 @@ module Rails end def index_helper # :doc: - uncountable? ? "#{plural_table_name}_index" : plural_table_name + uncountable? ? "#{plural_route_name}_index" : plural_route_name end def show_helper # :doc: - "#{singular_table_name}_url(@#{singular_table_name})" + "#{singular_route_name}_url(@#{singular_table_name})" end def edit_helper # :doc: @@ -112,11 +107,7 @@ module Rails end def new_helper # :doc: - "new_#{singular_table_name}_url" - end - - def field_id(attribute_name) - [singular_table_name, attribute_name].join("_") + "new_#{singular_route_name}_url" end def singular_table_name # :doc: @@ -152,6 +143,35 @@ module Rails end end + def redirect_resource_name # :doc: + model_resource_name(prefix: "@") + end + + def model_resource_name(prefix: "") # :doc: + resource_name = "#{prefix}#{singular_table_name}" + if options[:model_name] + "[#{controller_class_path.map { |name| ":" + name }.join(", ")}, #{resource_name}]" + else + resource_name + end + end + + def singular_route_name # :doc: + if options[:model_name] + "#{controller_class_path.join('_')}_#{singular_table_name}" + else + singular_table_name + end + end + + def plural_route_name # :doc: + if options[:model_name] + "#{controller_class_path.join('_')}_#{plural_table_name}" + else + plural_table_name + end + end + def assign_names!(name) @class_path = name.include?("/") ? name.split("/") : name.split("::") @class_path.map!(&:underscore) @@ -193,7 +213,7 @@ module Rails # def self.check_class_collision(options = {}) # :doc: define_method :check_class_collision do - name = if respond_to?(:controller_class_name) # for ScaffoldBase + name = if respond_to?(:controller_class_name) # for ResourceHelpers controller_class_name else class_name diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ac82ff6633..395ac7ef2f 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../app_base" +require "rails/generators/app_base" module Rails module ActionMethods # :nodoc: @@ -69,7 +69,7 @@ module Rails def version_control if !options[:skip_git] && !options[:pretend] - run "git init" + run "git init", capture: options[:quiet] end end @@ -114,7 +114,7 @@ module Rails template "cable.yml" unless options[:skip_action_cable] template "puma.rb" unless options[:skip_puma] template "spring.rb" if spring_install? - template "storage.yml" + template "storage.yml" unless skip_active_storage? directory "environments" directory "initializers" @@ -128,6 +128,9 @@ module Rails active_storage_config_exist = File.exist?("config/storage.yml") rack_cors_config_exist = File.exist?("config/initializers/cors.rb") assets_config_exist = File.exist?("config/initializers/assets.rb") + csp_config_exist = File.exist?("config/initializers/content_security_policy.rb") + + @config_target_version = Rails.application.config.loaded_config_version || "5.0" config @@ -139,7 +142,7 @@ module Rails template "config/cable.yml" end - if !active_storage_config_exist + if !skip_active_storage? && !active_storage_config_exist template "config/storage.yml" end @@ -155,27 +158,27 @@ module Rails unless assets_config_exist remove_file "config/initializers/assets.rb" end + + unless csp_config_exist + remove_file "config/initializers/content_security_policy.rb" + end end end def master_key - return if options[:pretend] - - require_relative "../master_key/master_key_generator" + return if options[:pretend] || options[:dummy_app] - after_bundle do - Rails::Generators::MasterKeyGenerator.new.add_master_key_file - end + require "rails/generators/rails/master_key/master_key_generator" + master_key_generator = Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet], force: options[:force]) + master_key_generator.add_master_key_file_silently + master_key_generator.ignore_master_key_file_silently end def credentials - return if options[:pretend] - - require_relative "../credentials/credentials_generator" + return if options[:pretend] || options[:dummy_app] - after_bundle do - Rails::Generators::CredentialsGenerator.new.add_credentials_file_silently - end + require "rails/generators/rails/credentials/credentials_generator" + Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently end def database_yml @@ -232,6 +235,10 @@ module Rails def vendor empty_directory_with_keep_file "vendor" end + + def config_target_version + defined?(@config_target_version) ? @config_target_version : Rails::VERSION::STRING.to_f + end end module Generators @@ -241,7 +248,7 @@ module Rails RESERVED_NAMES = %w[application destroy plugin runner test] class AppGenerator < AppBase # :nodoc: - WEBPACKS = %w( react vue angular elm ) + WEBPACKS = %w( react vue angular elm stimulus ) add_shared_options_for "application" @@ -347,6 +354,14 @@ module Rails build(:public_directory) end + def create_tmp_files + build(:tmp) + end + + def create_vendor_files + build(:vendor) + end + def create_test_files build(:test) unless options[:skip_test] end @@ -355,12 +370,8 @@ module Rails build(:system_test) if depends_on_system_test? end - def create_tmp_files - build(:tmp) - end - - def create_vendor_files - build(:vendor) + def create_storage_files + build(:storage) unless skip_active_storage? end def delete_app_assets_if_api_option @@ -378,9 +389,13 @@ module Rails end end - def delete_application_layout_file_if_api_option + def delete_app_views_if_api_option if options[:api] - remove_file "app/views/layouts/application.html.erb" + if options[:skip_action_mailer] + remove_dir "app/views" + else + remove_file "app/views/layouts/application.html.erb" + end end end @@ -432,6 +447,7 @@ module Rails def delete_non_api_initializers_if_api_option if options[:api] remove_file "config/initializers/cookies_serializer.rb" + remove_file "config/initializers/content_security_policy.rb" end end @@ -443,7 +459,7 @@ module Rails def delete_new_framework_defaults unless options[:update] - remove_file "config/initializers/new_framework_defaults_5_2.rb" + remove_file "config/initializers/new_framework_defaults_6_0.rb" end end @@ -509,10 +525,6 @@ module Rails end end - def app_secret - SecureRandom.hex(64) - end - def mysql_socket @mysql_socket ||= [ "/tmp/mysql.sock", # default @@ -559,7 +571,7 @@ module Rails def handle_version_request!(argument) if ["--version", "-v"].include?(argument) - require_relative "../../../version" + require "rails/version" puts "Rails #{Rails::VERSION::STRING}" exit(0) end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt index 770247d8ca..1567333023 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt @@ -20,16 +20,20 @@ ruby <%= "'#{RUBY_VERSION}'" -%> # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' +<% unless skip_active_storage? -%> # Use ActiveStorage variant -# gem 'mini_magick', '~> 4.8' +# gem 'image_processing', '~> 1.2' +<% end -%> # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +<% if depend_on_bootsnap? -%> # Reduces boot times through caching; required in config/boot.rb gem 'bootsnap', '>= 1.1.0', require: false +<%- end -%> <%- if options.api? -%> # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem 'rack-cors' @@ -39,11 +43,6 @@ gem 'bootsnap', '>= 1.1.0', require: false group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] - <%- if depends_on_system_test? -%> - # Adds support for Capybara system testing and selenium driver - gem 'capybara', '~> 2.13' - gem 'selenium-webdriver' - <%- end -%> end group :development do @@ -66,6 +65,16 @@ group :development do <% end -%> <% end -%> end + +<%- if depends_on_system_test? -%> +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' +end +<%- end -%> <% end -%> # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/railties/lib/rails/generators/rails/app/templates/README.md b/railties/lib/rails/generators/rails/app/templates/README.md.tt index 7db80e4ca1..7db80e4ca1 100644 --- a/railties/lib/rails/generators/rails/app/templates/README.md +++ b/railties/lib/rails/generators/rails/app/templates/README.md.tt diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile.tt index e85f913914..e85f913914 100644 --- a/railties/lib/rails/generators/rails/app/templates/Rakefile +++ b/railties/lib/rails/generators/rails/app/templates/Rakefile.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt index 62fd04f113..5183bcd256 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt @@ -12,7 +12,9 @@ // <% unless options[:skip_javascript] -%> //= require rails-ujs +<% unless skip_active_storage? -%> //= require activestorage +<% end -%> <% unless options[:skip_turbolinks] -%> //= require turbolinks <% end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt index 739aa5f022..739aa5f022 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt index d05ea0f511..d05ea0f511 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt index d672697283..d672697283 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt index 0ff5442f47..0ff5442f47 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb.tt index de6be7945c..de6be7945c 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt index a009ace51c..a009ace51c 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb b/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt index 286b2239d1..286b2239d1 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt index 10a4cba84d..10a4cba84d 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt index 5460155b3e..ef715f1368 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -3,6 +3,7 @@ <head> <title><%= camelized %></title> <%%= csrf_meta_tags %> + <%%= csp_meta_tag %> <%- if options[:skip_javascript] -%> <%%= stylesheet_link_tag 'application', media: 'all' %> diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle b/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt index a84f0afe47..a84f0afe47 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/bundle +++ b/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rails b/railties/lib/rails/generators/rails/app/templates/bin/rails.tt index 513a2e0183..513a2e0183 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/rails +++ b/railties/lib/rails/generators/rails/app/templates/bin/rails.tt diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rake b/railties/lib/rails/generators/rails/app/templates/bin/rake.tt index d14fc8395b..d14fc8395b 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/rake +++ b/railties/lib/rails/generators/rails/app/templates/bin/rake.tt diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt index d744bec32f..70cc71d83b 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt @@ -15,6 +15,11 @@ chdir APP_ROOT do puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') +<% unless options.skip_yarn? -%> + + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') +<% end -%> <% unless options.skip_active_record? -%> puts "\n== Updating database ==" diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn.tt index c2f9b6768a..90ddcc520e 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/yarn +++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn.tt @@ -1,7 +1,7 @@ -VENDOR_PATH = File.expand_path('..', __dir__) -Dir.chdir(VENDOR_PATH) do +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do begin - exec "yarnpkg #{ARGV.join(' ')}" + exec "yarnpkg", *ARGV rescue Errno::ENOENT $stderr.puts "Yarn executable was not detected in the system." $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru.tt index f7ba0b527b..f7ba0b527b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config.ru +++ b/railties/lib/rails/generators/rails/app/templates/config.ru.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt index dde09edb94..9a427113c7 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt @@ -8,10 +8,10 @@ require "rails" require "active_model/railtie" require "active_job/railtie" <%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" require "action_controller/railtie" <%= comment_if :skip_action_mailer %>require "action_mailer/railtie" require "action_view/railtie" -require "active_storage/engine" <%= comment_if :skip_action_cable %>require "action_cable/engine" <%= comment_if :skip_sprockets %>require "sprockets/railtie" <%= comment_if :skip_test %>require "rails/test_unit/railtie" @@ -24,11 +24,12 @@ Bundler.require(*Rails.groups) module <%= app_const_base %> class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults <%= Rails::VERSION::STRING.to_f %> + config.load_defaults <%= build(:config_target_version) %> # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. + # Application configuration can go into files in config/initializers + # -- all .rb files in that directory are automatically loaded after loading + # the framework and any gems in your application. <%- if options.api? -%> # Only loads a smaller set of middleware suitable for API only apps. diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb.tt index b9e460cef3..42d46b8175 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/boot.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb.tt @@ -1,4 +1,6 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. +<% if depend_on_bootsnap? -%> require 'bootsnap/setup' # Speed up boot time by caching expensive operations. +<%- end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/cable.yml b/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt index 8e53156c71..8e53156c71 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/cable.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt index 917b52e535..917b52e535 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt index d40117a27f..d40117a27f 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt index 563be77710..563be77710 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt index 2a67bdca25..2a67bdca25 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt index 70df04079d..70df04079d 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt index 371415e6a8..371415e6a8 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt index 04afaa0596..04afaa0596 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt index 6da0601b24..6da0601b24 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt index 145cfb7f74..145cfb7f74 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt index 9510568124..9510568124 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt index 049de65f22..049de65f22 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb.tt index 426333bb46..426333bb46 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb.tt 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 98689cc30d..3807c8a9aa 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 @@ -14,7 +14,7 @@ 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? + if Rails.root.join('tmp', 'caching-dev.txt').exist? config.action_controller.perform_caching = true config.cache_store = :memory_store @@ -26,9 +26,11 @@ Rails.application.configure do config.cache_store = :null_store end + <%- unless skip_active_storage? -%> - # Store uploaded files on the local file system (see config/storage.yml for options) + # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local + <%- end -%> <%- unless options.skip_action_mailer? -%> # Don't care if the mailer can't send. @@ -44,6 +46,9 @@ Rails.application.configure do # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + <%- end -%> <%- unless options.skip_sprockets? -%> # Debug mode disables concatenation and preprocessing of assets. @@ -55,7 +60,7 @@ Rails.application.configure do config.assets.quiet = true <%- end -%> - # Raises error for missing translations + # Raises error for missing translations. # config.action_view.raise_on_missing_translations = true # Use an evented file watcher to asynchronously detect changes in source code, 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 2e0b555f6f..d646694477 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 @@ -34,8 +34,6 @@ Rails.application.configure do # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - <%- end -%> # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.action_controller.asset_host = 'http://assets.example.com' @@ -44,11 +42,13 @@ Rails.application.configure do # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - # Store uploaded files on the local file system (see config/storage.yml for options) + <%- unless skip_active_storage? -%> + # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local + <%- end -%> <%- unless options[:skip_action_cable] -%> - # Mount Action Cable outside main process or domain + # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil # config.action_cable.url = 'wss://example.com/cable' # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] @@ -67,7 +67,7 @@ Rails.application.configure do # Use a different cache store in production. # config.cache_store = :mem_cache_store - # Use a real queuing backend for Active Job (and separate queues per environment) + # Use a real queuing backend for Active Job (and separate queues per environment). # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "<%= app_name %>_#{Rails.env}" 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 a53978bc6e..82f2a8aebe 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 @@ -28,8 +28,11 @@ Rails.application.configure do # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false - # Store uploaded files on the local file system in a temporary directory + <%- unless skip_active_storage? -%> + # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test + + <%- end -%> <%- unless options.skip_action_mailer? -%> config.action_mailer.perform_caching = false @@ -37,11 +40,14 @@ Rails.application.configure do # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - <%- end -%> + <%- end -%> # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Raises error for missing translations + # Raises error for missing translations. # config.action_view.raise_on_missing_translations = true + + # Prevent expensive template finalization at end of test suite runs. + config.action_view.finalize_compiled_template_methods = false end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt index 89d2efab2b..89d2efab2b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt index 59385cdf37..59385cdf37 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt new file mode 100644 index 0000000000..d3bcaa5ec8 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy +# For further information see the following documentation +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + +# Rails.application.config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https + +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end + +# If you are using UJS then enable automatic nonce generation +# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } + +# Report CSP violations to a specified URI +# For further information see the following documentation: +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only +# Rails.application.config.content_security_policy_report_only = true diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt index 5a6a32d371..5a6a32d371 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt index 3b1c1b5ed1..3b1c1b5ed1 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt index 4a994e1e7b..4a994e1e7b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt index ac033bf9dc..ac033bf9dc 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt index dc1899682b..dc1899682b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt deleted file mode 100644 index 25dcddb27a..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt +++ /dev/null @@ -1,27 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.2 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Make Active Record use stable #cache_key alongside new #cache_version method. -# This is needed for recyclable cache keys. -# Rails.application.config.active_record.cache_versioning = true - -# Use AES 256 GCM authenticated encryption for encrypted cookies. -# Existing cookies will be converted on read then written with the new scheme. -# Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true - -# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages -# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. -# Rails.application.config.active_support.use_authenticated_message_encryption = true - -# Add default protection from forgery to ActionController::Base instead of in -# ApplicationController. -# Rails.application.config.action_controller.default_protect_from_forgery = true - -# Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and -# 'f' after migrating old data. -# Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt new file mode 100644 index 0000000000..179b97de4a --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 6.0 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Don't force requests from old versions of IE to be UTF-8 encoded +# Rails.application.config.action_view.default_enforce_utf8 = false diff --git a/railties/lib/rails/generators/rails/app/templates/config/puma.rb b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt index 1e19380dcb..a5eccf816b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/puma.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt @@ -26,31 +26,9 @@ environment ENV.fetch("RAILS_ENV") { "development" } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code # before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. If you use this option -# you need to make sure to reconnect any threads in the `on_worker_boot` -# block. +# process behavior so workers use less memory. # # preload_app! -# If you are preloading your application and using Active Record, it's -# recommended that you close any connections to the database before workers -# are forked to prevent connection leakage. -# -# before_fork do -# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) -# end - -# The code in the `on_worker_boot` will be called if you are using -# clustered mode by specifying a number of `workers`. After each worker -# process is booted, this block will be run. If you are using the `preload_app!` -# option, you will want to use this block to reconnect to any threads -# or connections that may have been created at application boot, as Ruby -# cannot share connections between processes. -# -# on_worker_boot do -# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) -# end -# - # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt index 787824f888..787824f888 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml deleted file mode 100644 index ea9d47396c..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key is used for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! - -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -# You can use `rails secret` to generate a secure secret key. - -# Make sure the secrets in this file are kept private -# if you're sharing your code publicly. - -# Shared secrets are available across all environments. - -# shared: -# api_key: a1B2c3D4e5F6 - -# Environmental secrets are only available for that specific environment. - -development: - secret_key_base: <%= app_secret %> - -test: - secret_key_base: <%= app_secret %> - -# Do not keep production secrets in the unencrypted secrets file. -# Instead, either read values from the environment. -# Or, use `bin/rails secrets:setup` to configure encrypted secrets -# and move the `production:` environment over there. - -production: - secret_key_base: <%%= ENV["SECRET_KEY_BASE"] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/spring.rb b/railties/lib/rails/generators/rails/app/templates/config/spring.rb.tt index 9fa7863f99..9fa7863f99 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/spring.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/spring.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt index 089ed4567a..7207c75086 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt @@ -6,11 +6,11 @@ local: service: Disk root: <%%= Rails.root.join("storage") %> -# Use rails secrets:edit to set the AWS secrets (as shared:aws:access_key_id|secret_access_key) +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) # amazon: # service: S3 -# access_key_id: <%%= Rails.application.secrets.dig(:aws, :access_key_id) %> -# secret_access_key: <%%= Rails.application.secrets.dig(:aws, :secret_access_key) %> +# access_key_id: <%%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%%= Rails.application.credentials.dig(:aws, :secret_access_key) %> # region: us-east-1 # bucket: your_own_bucket @@ -18,15 +18,14 @@ local: # google: # service: GCS # project: your_project -# keyfile: <%%= Rails.root.join("path/to/gcs.keyfile") %> +# credentials: <%%= Rails.root.join("path/to/gcs.keyfile") %> # bucket: your_own_bucket -# Use rails secrets:edit to set the Azure Storage secret (as shared:azure_storage:storage_access_key) +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) # microsoft: # service: AzureStorage -# path: your_azure_storage_path # storage_account_name: your_account_name -# storage_access_key: <%%= Rails.application.secrets.dig(:azure_storage, :storage_access_key) %> +# storage_access_key: <%%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> # container: your_container_name # mirror: diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore.tt index 83a7b211aa..4e114fb1d9 100644 --- a/railties/lib/rails/generators/rails/app/templates/gitignore +++ b/railties/lib/rails/generators/rails/app/templates/gitignore.tt @@ -21,14 +21,19 @@ !/tmp/.keep <% end -%> +<% unless skip_active_storage? -%> # Ignore uploaded files in development /storage/* +<% if keeps? -%> +!/storage/.keep +<% end -%> +<% end -%> <% unless options.skip_yarn? -%> /node_modules /yarn-error.log -<% end -%> +<% end -%> <% unless options.api? -%> /public/assets <% end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/package.json b/railties/lib/rails/generators/rails/app/templates/package.json.tt index 46db57dcbe..46db57dcbe 100644 --- a/railties/lib/rails/generators/rails/app/templates/package.json +++ b/railties/lib/rails/generators/rails/app/templates/package.json.tt diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version b/railties/lib/rails/generators/rails/app/templates/ruby-version deleted file mode 100644 index c444f33b0f..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/ruby-version +++ /dev/null @@ -1 +0,0 @@ -<%= RUBY_VERSION -%> diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt new file mode 100644 index 0000000000..19f0d7f202 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt @@ -0,0 +1 @@ +<%= "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" -%> diff --git a/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb b/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt index d19212abd5..d19212abd5 100644 --- a/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb +++ b/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt index 6ad1f11781..c918b57eca 100644 --- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt @@ -1,7 +1,15 @@ +ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' require 'rails/test_help' class ActiveSupport::TestCase + # Run tests in parallel with specified workers +<% if defined?(JRUBY_VERSION) -%> + parallelize(workers: 2, with: :threads) +<%- else -%> + parallelize(workers: 2) +<% end -%> + <% unless options[:skip_active_record] -%> # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all diff --git a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css index 7594abf268..afad32db02 100644 --- a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css +++ b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css @@ -1,4 +1,4 @@ -/* +/* Place all the styles related to the matching controller here. They will automatically be included in application.css. */ diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb index 6d45d6e8f8..eb75e7e661 100644 --- a/railties/lib/rails/generators/rails/controller/controller_generator.rb +++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb @@ -16,13 +16,24 @@ module Rails def add_routes return if options[:skip_routes] + return if actions.empty? route generate_routing_code end - hook_for :template_engine, :test_framework, :helper, :assets + hook_for :template_engine, :test_framework, :helper, :assets do |generator| + invoke generator, [ remove_possible_suffix(name), actions ] + end private + def file_name + @_file_name ||= remove_possible_suffix(super) + end + + def remove_possible_suffix(name) + name.sub(/_?controller$/i, "") + end + # This method creates nested route entry for namespaced resources. # For eg. rails g controller foo/bar/baz index show # Will generate - diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb.tt index 633e0b3177..633e0b3177 100644 --- a/railties/lib/rails/generators/rails/controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb.tt diff --git a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb index ddcccd5ce5..719e0c1e4c 100644 --- a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb +++ b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb @@ -1,20 +1,17 @@ # frozen_string_literal: true -require_relative "../../base" -require_relative "../master_key/master_key_generator" +require "rails/generators/base" +require "rails/generators/rails/master_key/master_key_generator" require "active_support/encrypted_configuration" module Rails module Generators - class CredentialsGenerator < Base - CONFIG_PATH = "config/credentials.yml.enc" - KEY_PATH = "config/master.key" - + class CredentialsGenerator < Base # :nodoc: def add_credentials_file - unless File.exist?(CONFIG_PATH) + unless credentials.content_path.exist? template = credentials_template - say "Adding #{CONFIG_PATH} to store encrypted credentials." + say "Adding #{credentials.content_path} to store encrypted credentials." say "" say "The following content has been encrypted with the Rails master key:" say "" @@ -29,17 +26,30 @@ module Rails end def add_credentials_file_silently(template = nil) - unless File.exist?(CONFIG_PATH) - setup = { config_path: CONFIG_PATH, key_path: KEY_PATH, env_key: "RAILS_MASTER_KEY" } - ActiveSupport::EncryptedConfiguration.new(setup).write(credentials_template) + unless credentials.content_path.exist? + credentials.write(credentials_template) end end private + def credentials + ActiveSupport::EncryptedConfiguration.new( + config_path: "config/credentials.yml.enc", + key_path: "config/master.key", + env_key: "RAILS_MASTER_KEY", + raise_if_missing_key: true + ) + end + def credentials_template - "# amazon:\n# access_key_id: 123\n# secret_access_key: 345\n\n" + - "# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.\n" + - "secret_key_base: #{SecureRandom.hex(64)}" + <<~YAML + # aws: + # access_key_id: 123 + # secret_access_key: 345 + + # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. + secret_key_base: #{SecureRandom.hex(64)} + YAML end end end diff --git a/railties/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb b/railties/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb new file mode 100644 index 0000000000..867e28c6db --- /dev/null +++ b/railties/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "rails/generators/base" +require "active_support/encrypted_file" + +module Rails + module Generators + class EncryptedFileGenerator < Base # :nodoc: + def add_encrypted_file_silently(file_path, key_path, template = encrypted_file_template) + unless File.exist?(file_path) + setup = { content_path: file_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true } + ActiveSupport::EncryptedFile.new(setup).write(template) + end + end + + private + def encrypted_file_template + <<~YAML + # aws: + # access_key_id: 123 + # secret_access_key: 345 + + YAML + end + end + end +end diff --git a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb b/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb deleted file mode 100644 index d054e8cad2..0000000000 --- a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../base" -require_relative "../../../secrets" - -module Rails - module Generators - class EncryptedSecretsGenerator < Base - def add_secrets_key_file - unless File.exist?("config/secrets.yml.key") || File.exist?("config/secrets.yml.enc") - key = Rails::Secrets.generate_key - - say "Adding config/secrets.yml.key to store the encryption key: #{key}" - say "" - say "Save this in a password manager your team can access." - say "" - say "If you lose the key, no one, including you, can access any encrypted secrets." - - say "" - create_file "config/secrets.yml.key", key - say "" - end - end - - def ignore_key_file - if File.exist?(".gitignore") - unless File.read(".gitignore").include?(key_ignore) - say "Ignoring config/secrets.yml.key so it won't end up in Git history:" - say "" - append_to_file ".gitignore", key_ignore - say "" - end - else - say "IMPORTANT: Don't commit config/secrets.yml.key. Add this to your ignore file:" - say key_ignore, :on_green - say "" - end - end - - def add_encrypted_secrets_file - unless (defined?(@@skip_secrets_file) && @@skip_secrets_file) || File.exist?("config/secrets.yml.enc") - say "Adding config/secrets.yml.enc to store secrets that needs to be encrypted." - say "" - say "For now the file contains this but it's been encrypted with the generated key:" - say "" - say Secrets.template, :on_green - say "" - - Secrets.write(Secrets.template) - - say "You can edit encrypted secrets with `bin/rails secrets:edit`." - say "" - end - - say "Add this to your config/environments/production.rb:" - say "config.read_encrypted_secrets = true" - end - - def self.skip_secrets_file - @@skip_secrets_file = true - yield - ensure - @@skip_secrets_file = false - end - - private - def key_ignore - [ "", "# Ignore encrypted secrets key file.", "config/secrets.yml.key", "" ].join("\n") - end - end - end -end diff --git a/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb new file mode 100644 index 0000000000..e2359e9ded --- /dev/null +++ b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "pathname" +require "rails/generators/base" +require "active_support/encrypted_file" + +module Rails + module Generators + class EncryptionKeyFileGenerator < Base # :nodoc: + def add_key_file(key_path) + key_path = Pathname.new(key_path) + + unless key_path.exist? + key = ActiveSupport::EncryptedFile.generate_key + + log "Adding #{key_path} to store the encryption key: #{key}" + log "" + log "Save this in a password manager your team can access." + log "" + log "If you lose the key, no one, including you, can access anything encrypted with it." + + log "" + add_key_file_silently(key_path, key) + log "" + end + end + + def add_key_file_silently(key_path, key = nil) + create_file key_path, key || ActiveSupport::EncryptedFile.generate_key + key_path.chmod 0600 + end + + def ignore_key_file(key_path, ignore: key_ignore(key_path)) + if File.exist?(".gitignore") + unless File.read(".gitignore").include?(ignore) + log "Ignoring #{key_path} so it won't end up in Git history:" + log "" + append_to_file ".gitignore", ignore + log "" + end + else + log "IMPORTANT: Don't commit #{key_path}. Add this to your ignore file:" + log ignore, :on_green + log "" + end + end + + def ignore_key_file_silently(key_path, ignore: key_ignore(key_path)) + append_to_file ".gitignore", ignore if File.exist?(".gitignore") + end + + private + def key_ignore(key_path) + [ "", "/#{key_path}", "" ].join("\n") + end + end + end +end diff --git a/railties/lib/rails/generators/rails/helper/templates/helper.rb b/railties/lib/rails/generators/rails/helper/templates/helper.rb.tt index b4173151b4..b4173151b4 100644 --- a/railties/lib/rails/generators/rails/helper/templates/helper.rb +++ b/railties/lib/rails/generators/rails/helper/templates/helper.rb.tt diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index e49d3b39e0..21664ea86d 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -1,46 +1,48 @@ # frozen_string_literal: true -require_relative "../../base" require "pathname" +require "rails/generators/base" +require "rails/generators/rails/encryption_key_file/encryption_key_file_generator" require "active_support/encrypted_file" module Rails module Generators - class MasterKeyGenerator < Base + class MasterKeyGenerator < Base # :nodoc: MASTER_KEY_PATH = Pathname.new("config/master.key") def add_master_key_file unless MASTER_KEY_PATH.exist? key = ActiveSupport::EncryptedFile.generate_key - say "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}" - say "" - say "Save this in a password manager your team can access." - say "" - say "If you lose the key, no one, including you, can access anything encrypted with it." + log "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}" + log "" + log "Save this in a password manager your team can access." + log "" + log "If you lose the key, no one, including you, can access anything encrypted with it." - say "" - create_file MASTER_KEY_PATH, key - say "" + log "" + add_master_key_file_silently(key) + log "" + end + end - ignore_master_key_file + def add_master_key_file_silently(key = nil) + unless MASTER_KEY_PATH.exist? + key_file_generator.add_key_file_silently(MASTER_KEY_PATH, key) end end + def ignore_master_key_file + key_file_generator.ignore_key_file(MASTER_KEY_PATH, ignore: key_ignore) + end + + def ignore_master_key_file_silently + key_file_generator.ignore_key_file_silently(MASTER_KEY_PATH, ignore: key_ignore) + end + private - def ignore_master_key_file - if File.exist?(".gitignore") - unless File.read(".gitignore").include?(key_ignore) - say "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" - say "" - append_to_file ".gitignore", key_ignore - say "" - end - else - say "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" - say key_ignore, :on_green - say "" - end + def key_file_generator + EncryptionKeyFileGenerator.new([], options) end def key_ignore diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb index 1dca03e0bb..de4de2cae2 100644 --- a/railties/lib/rails/generators/rails/model/model_generator.rb +++ b/railties/lib/rails/generators/rails/model/model_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../model_helpers" +require "rails/generators/model_helpers" module Rails module Generators diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index eb941adf95..a83c911806 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true -require "active_support/core_ext/hash/slice" -require_relative "../app/app_generator" +require "rails/generators/rails/app/app_generator" require "date" module Rails @@ -88,17 +87,18 @@ task default: :test end PASSTHROUGH_OPTIONS = [ - :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database, + :skip_active_record, :skip_active_storage, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database, :javascript, :skip_yarn, :api, :quiet, :pretend, :skip ] def generate_test_dummy(force = false) - opts = (options || {}).slice(*PASSTHROUGH_OPTIONS) + opts = (options.dup || {}).keep_if { |k, _| PASSTHROUGH_OPTIONS.map(&:to_s).include?(k) } opts[:force] = force opts[:skip_bundle] = true opts[:skip_listen] = true opts[:skip_git] = true opts[:skip_turbolinks] = true + opts[:dummy_app] = true invoke Rails::Generators::AppGenerator, [ File.expand_path(dummy_path, destination_root) ], opts @@ -167,7 +167,7 @@ task default: :test gemfile_in_app_path = File.join(rails_app_path, "Gemfile") if File.exist? gemfile_in_app_path - entry = "gem '#{name}', path: '#{relative_path}'" + entry = "\ngem '#{name}', path: '#{relative_path}'" append_file gemfile_in_app_path, entry end end @@ -263,6 +263,10 @@ task default: :test public_task :apply_rails_template def run_after_bundle_callbacks + unless @after_bundle_callbacks.empty? + ActiveSupport::Deprecation.warn("`after_bundle` is deprecated and will be removed in the next version of Rails. ") + end + @after_bundle_callbacks.each do |callback| callback.call end diff --git a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt index 9a8c4bf098..9a8c4bf098 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec +++ b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/Gemfile b/railties/lib/rails/generators/rails/plugin/templates/Gemfile.tt index 290259b4db..290259b4db 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/Gemfile +++ b/railties/lib/rails/generators/rails/plugin/templates/Gemfile.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt index ff2fb3ba4e..ff2fb3ba4e 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE +++ b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/README.md b/railties/lib/rails/generators/rails/plugin/templates/README.md.tt index 1632409bea..1632409bea 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/README.md +++ b/railties/lib/rails/generators/rails/plugin/templates/README.md.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt index f3efe21cf1..f3efe21cf1 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt index abbacd9bec..b86ef0f2f8 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt @@ -1,4 +1,4 @@ -<%= wrap_in_modules <<-rb.strip_heredoc +<%= wrap_in_modules <<~rb class ApplicationController < ActionController::#{api? ? "API" : "Base"} #{ api? ? '# ' : '' }protect_from_forgery with: :exception end diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt index 25d692732d..be078f36de 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt @@ -1,4 +1,4 @@ -<%= wrap_in_modules <<-rb.strip_heredoc +<%= wrap_in_modules <<~rb module ApplicationHelper end rb diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt index bad1ff2d16..846863bc13 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt @@ -1,4 +1,4 @@ -<%= wrap_in_modules <<-rb.strip_heredoc +<%= wrap_in_modules <<~rb class ApplicationJob < ActiveJob::Base end rb diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt index 09aac13f42..246e274348 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt @@ -1,4 +1,4 @@ -<%= wrap_in_modules <<-rb.strip_heredoc +<%= wrap_in_modules <<~rb class ApplicationMailer < ActionMailer::Base default from: 'from@example.com' layout 'mailer' diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt index 8aa3de78f1..21465278be 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt @@ -1,4 +1,4 @@ -<%= wrap_in_modules <<-rb.strip_heredoc +<%= wrap_in_modules <<~rb class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt index 6bc480161d..6e54a1ce9d 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt @@ -2,9 +2,13 @@ <html> <head> <title><%= humanized %></title> + <%%= csrf_meta_tags %> + <%%= csp_meta_tag %> + <%%= stylesheet_link_tag "<%= namespaced_name %>/application", media: "all" %> + <%- unless options[:skip_javascript] -%> <%%= javascript_include_tag "<%= namespaced_name %>/application" %> - <%%= csrf_meta_tags %> + <%- end -%> </head> <body> diff --git a/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt index b3264509fc..ee8e469da2 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt @@ -19,10 +19,10 @@ require "rails" require "active_model/railtie" require "active_job/railtie" <%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" require "action_controller/railtie" <%= comment_if :skip_action_mailer %>require "action_mailer/railtie" require "action_view/railtie" -require "active_storage/engine" <%= comment_if :skip_action_cable %>require "action_cable/engine" <%= comment_if :skip_sprockets %>require "sprockets/railtie" <%= comment_if :skip_test %>require "rails/test_unit/railtie" diff --git a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb.tt index 154452bfe5..154452bfe5 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt index e15863d860..7a68da5c4b 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/gitignore +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt @@ -11,5 +11,8 @@ pkg/ <%= dummy_path %>/node_modules/ <%= dummy_path %>/yarn-error.log <% end -%> +<% unless skip_active_storage? -%> +<%= dummy_path %>/storage/ +<% end -%> <%= dummy_path %>/tmp/ <% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt index 3285055eb7..3285055eb7 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt index 8938770fc4..4ec1804940 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt @@ -1,4 +1,4 @@ -<%= wrap_in_modules <<-rb.strip_heredoc +<%= wrap_in_modules <<~rb class Engine < ::Rails::Engine #{mountable? ? ' isolate_namespace ' + camelized_modules : ' '} #{api? ? " config.generators.api_only = true" : ' '} diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt index 7bdf4ee5fb..b853fabcc3 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt @@ -1,4 +1,4 @@ -<%= wrap_in_modules <<-rb.strip_heredoc +<%= wrap_in_modules <<~rb class Railtie < ::Rails::Railtie end rb diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt index b08f4ef9ae..b08f4ef9ae 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt index 88a2c4120f..88a2c4120f 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt index 47b56ae3df..06ffe2f1ed 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt @@ -8,10 +8,10 @@ require "rails" require "active_model/railtie" require "active_job/railtie" <%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" require "action_controller/railtie" <%= comment_if :skip_action_mailer %>require "action_mailer/railtie" require "action_view/railtie" -require "active_storage/engine" <%= comment_if :skip_action_cable %>require "action_cable/engine" <%= comment_if :skip_sprockets %>require "sprockets/railtie" <%= comment_if :skip_test %>require "rails/test_unit/railtie" diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt index c9aef85d40..c9aef85d40 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt index 03937cf8ff..03937cf8ff 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt index 2f23844f5e..2f23844f5e 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt index e54c6461cc..51049826bf 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt @@ -10,4 +10,8 @@ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // +//= require rails-ujs +<% unless skip_active_storage? -%> +//= require activestorage +<% end -%> //= require_tree . diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb.tt index 694510edc0..694510edc0 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt index 1ee05d7871..1ee05d7871 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb b/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt index d19212abd5..d19212abd5 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt index 29e59d8407..29e59d8407 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt index 7fa9973931..755d19ef5d 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt @@ -1,3 +1,6 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + require_relative "<%= File.join('..', options[:dummy_path], 'config/environment') -%>" <% unless options[:skip_active_record] -%> ActiveRecord::Migrator.migrations_paths = [File.expand_path("../<%= options[:dummy_path] -%>/db/migrate", __dir__)] diff --git a/railties/lib/rails/generators/rails/resource/USAGE b/railties/lib/rails/generators/rails/resource/USAGE index e359cd574f..66d0ee546a 100644 --- a/railties/lib/rails/generators/rails/resource/USAGE +++ b/railties/lib/rails/generators/rails/resource/USAGE @@ -1,6 +1,6 @@ Description: Stubs out a new resource including an empty model and controller suitable - for a restful, resource-oriented application. Pass the singular model name, + for a RESTful, resource-oriented application. Pass the singular model name, either CamelCased or under_scored, as the first argument, and an optional list of attribute pairs. diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index 74b1574863..3ba25ef0fe 100644 --- a/railties/lib/rails/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../resource_helpers" -require_relative "../model/model_generator" +require "rails/generators/resource_helpers" +require "rails/generators/rails/model/model_generator" module Rails module Generators diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb index c0fb873d36..8beb7416c0 100644 --- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../resource/resource_generator" +require "rails/generators/rails/resource/resource_generator" module Rails module Generators 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 601a4b3a6e..7030561a33 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 @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../resource_helpers" +require "rails/generators/resource_helpers" module Rails module Generators diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt index 400afec6dc..400afec6dc 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt index 42b9e34274..05f1c2b2d3 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt @@ -29,7 +29,7 @@ class <%= controller_class_name %>Controller < ApplicationController @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> if @<%= orm_instance.save %> - redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully created.'" %> else render :new end @@ -38,7 +38,7 @@ class <%= controller_class_name %>Controller < ApplicationController # PATCH/PUT <%= route_url %>/1 def update if @<%= orm_instance.update("#{singular_table_name}_params") %> - redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> else render :edit end diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb.tt index 1e3ed5f158..1e3ed5f158 100644 --- a/railties/lib/rails/generators/rails/task/templates/task.rb +++ b/railties/lib/rails/generators/rails/task/templates/task.rb.tt diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb index 8bd5110940..5675faff70 100644 --- a/railties/lib/rails/generators/resource_helpers.rb +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "active_model" -require_relative "model_helpers" +require "rails/generators/active_model" +require "rails/generators/model_helpers" module Rails module Generators @@ -25,13 +25,8 @@ module Rails assign_controller_names!(controller_name.pluralize) end - # TODO Change this to private once we've dropped Ruby 2.2 support. - # Workaround for Ruby 2.2 "private attribute?" warning. - protected - - attr_reader :controller_name, :controller_file_name - private + attr_reader :controller_name, :controller_file_name def controller_class_path if options[:model_name] diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb index 9c06eed95a..5c71bf0be9 100644 --- a/railties/lib/rails/generators/test_case.rb +++ b/railties/lib/rails/generators/test_case.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "../generators" -require_relative "testing/behaviour" -require_relative "testing/setup_and_teardown" -require_relative "testing/assertions" +require "rails/generators" +require "rails/generators/testing/behaviour" +require "rails/generators/testing/setup_and_teardown" +require "rails/generators/testing/assertions" require "fileutils" module Rails diff --git a/railties/lib/rails/generators/test_unit.rb b/railties/lib/rails/generators/test_unit.rb index 1aa03ace99..1005ac557c 100644 --- a/railties/lib/rails/generators/test_unit.rb +++ b/railties/lib/rails/generators/test_unit.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "named_base" +require "rails/generators/named_base" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb index 3947b74205..1a9ac6bf2a 100644 --- a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb +++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt index ff41fef9e9..ff41fef9e9 100644 --- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb index 9f79e9eee1..19be4f2f51 100644 --- a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb +++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt index a7f1fc4fba..a7f1fc4fba 100644 --- a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb +++ b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb index 1c60c3573f..77308dcf7d 100644 --- a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb +++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb index c859ba5e4e..ae307c5cd9 100644 --- a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb +++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt index 118e0f1271..118e0f1271 100644 --- a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb +++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb index cbbcd1cd4e..1dae3cb6a5 100644 --- a/railties/lib/rails/generators/test_unit/job/job_generator.rb +++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: @@ -8,8 +8,13 @@ module TestUnit # :nodoc: check_class_collision suffix: "JobTest" def create_test_file - template "unit_test.rb.erb", File.join("test/jobs", class_path, "#{file_name}_job_test.rb") + template "unit_test.rb", File.join("test/jobs", class_path, "#{file_name}_job_test.rb") end + + private + def file_name + @_file_name ||= super.sub(/_job\z/i, "") + end end end end diff --git a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt index f5351d0ec6..f5351d0ec6 100644 --- a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb +++ b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb index 45f82158e9..ab8331f31c 100644 --- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: @@ -21,7 +21,7 @@ module TestUnit # :nodoc: private def file_name - @_file_name ||= super.gsub(/_mailer/i, "") + @_file_name ||= super.sub(/_mailer\z/i, "") end end end diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt index a2f2d30de5..a2f2d30de5 100644 --- a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt index b063cbc47b..b063cbc47b 100644 --- a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb +++ b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb index 2b1cc50c20..02d7502592 100644 --- a/railties/lib/rails/generators/test_unit/model/model_generator.rb +++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt index 0681780c97..0681780c97 100644 --- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml +++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt index c9bc7d5b90..c9bc7d5b90 100644 --- a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb +++ b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb index 10fe27b2c3..0657bc2389 100644 --- a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: 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 23d5c63614..e2e8b18eab 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../test_unit" -require_relative "../../resource_helpers" +require "rails/generators/test_unit" +require "rails/generators/resource_helpers" module TestUnit # :nodoc: module Generators # :nodoc: @@ -23,7 +23,7 @@ module TestUnit # :nodoc: template template_file, File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb") - unless options.api? || options[:system_tests].nil? + if !options.api? && options[:system_tests] template "system_test.rb", File.join("test/system", class_path, "#{file_name.pluralize}_test.rb") end end diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt index f21861d8e6..f21861d8e6 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt index 195d60be20..195d60be20 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt index f83f5a5c62..f83f5a5c62 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt diff --git a/railties/lib/rails/generators/test_unit/system/system_generator.rb b/railties/lib/rails/generators/test_unit/system/system_generator.rb index d3acbfd738..08504d4124 100644 --- a/railties/lib/rails/generators/test_unit/system/system_generator.rb +++ b/railties/lib/rails/generators/test_unit/system/system_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt index d19212abd5..d19212abd5 100644 --- a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb +++ b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt diff --git a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb.tt index b5ce2ba5c8..b5ce2ba5c8 100644 --- a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb +++ b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb.tt diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb index 97a09c1166..6ab88bd59f 100644 --- a/railties/lib/rails/generators/testing/behaviour.rb +++ b/railties/lib/rails/generators/testing/behaviour.rb @@ -6,7 +6,7 @@ require "active_support/core_ext/hash/reverse_merge" require "active_support/core_ext/kernel/reporting" require "active_support/testing/stream" require "active_support/concern" -require_relative "../../generators" +require "rails/generators" module Rails module Generators diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb index d8f361f524..d5c9973c6b 100644 --- a/railties/lib/rails/info.rb +++ b/railties/lib/rails/info.rb @@ -99,7 +99,7 @@ module Rails end property "Database schema version" do - ActiveRecord::Migrator.current_version rescue nil + ActiveRecord::Base.connection.migration_context.current_version rescue nil end end end diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb index 6535b5b6ad..b4f4a5922a 100644 --- a/railties/lib/rails/info_controller.rb +++ b/railties/lib/rails/info_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "application_controller" +require "rails/application_controller" require "action_dispatch/routing/inspector" class Rails::InfoController < Rails::ApplicationController # :nodoc: diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb index 6bd3161fdd..0b0e802358 100644 --- a/railties/lib/rails/mailers_controller.rb +++ b/railties/lib/rails/mailers_controller.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -require_relative "application_controller" +require "rails/application_controller" class Rails::MailersController < Rails::ApplicationController # :nodoc: prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH before_action :require_local!, unless: :show_previews? - before_action :find_preview, only: :preview + before_action :find_preview, :set_locale, only: :preview - helper_method :part_query + helper_method :part_query, :locale_query def index @previews = ActionMailer::Preview.all @@ -84,4 +84,12 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc: def part_query(mime_type) request.query_parameters.merge(part: mime_type).to_query end + + def locale_query(locale) + request.query_parameters.merge(locale: locale).to_query + end + + def set_locale + I18n.locale = params[:locale] || I18n.default_locale + end end diff --git a/railties/lib/rails/plugin/test.rb b/railties/lib/rails/plugin/test.rb index 2426521e35..18b6fd1757 100644 --- a/railties/lib/rails/plugin/test.rb +++ b/railties/lib/rails/plugin/test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../test_unit/runner" -require_relative "../test_unit/reporter" +require "rails/test_unit/runner" +require "rails/test_unit/reporter" Rails::TestUnitReporter.executable = "bin/test" diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb index ec5212ee76..4ea7e40319 100644 --- a/railties/lib/rails/rack/logger.rb +++ b/railties/lib/rails/rack/logger.rb @@ -35,9 +35,9 @@ module Rails instrumenter = ActiveSupport::Notifications.instrumenter instrumenter.start "request.action_dispatch", request: request logger.info { started_request_message(request) } - resp = @app.call(env) - resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) } - resp + status, headers, body = @app.call(env) + body = ::Rack::BodyProxy.new(body) { finish(request) } + [status, headers, body] rescue Exception finish(request) raise diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index 42c4a82b1b..88dd932370 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "initializable" +require "rails/initializable" require "active_support/inflector" require "active_support/core_ext/module/introspection" require "active_support/core_ext/module/delegation" diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb index d9997759f2..70274b948c 100644 --- a/railties/lib/rails/railtie/configuration.rb +++ b/railties/lib/rails/railtie/configuration.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../configuration" +require "rails/configuration" module Rails class Railtie @@ -55,7 +55,7 @@ module Rails ActiveSupport.on_load(:before_configuration, yield: true, &block) end - # Third configurable block to run. Does not run if +config.cache_classes+ + # Third configurable block to run. Does not run if +config.eager_load+ # set to false. def before_eager_load(&block) ActiveSupport.on_load(:before_eager_load, yield: true, &block) diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb index 76b6b80d28..b2d44d9b8e 100644 --- a/railties/lib/rails/ruby_version_check.rb +++ b/railties/lib/rails/ruby_version_check.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -if RUBY_VERSION < "2.2.2" && RUBY_ENGINE == "ruby" +if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4.1") && RUBY_ENGINE == "ruby" desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})" abort <<-end_message - Rails 5 requires Ruby 2.2.2 or newer. + Rails 6 requires Ruby 2.4.1 or newer. You're running #{desc} - Please upgrade to Ruby 2.2.2 or newer to continue. + Please upgrade to Ruby 2.4.1 or newer to continue. end_message end diff --git a/railties/lib/rails/secrets.rb b/railties/lib/rails/secrets.rb index aea72b2d01..747cf31d7a 100644 --- a/railties/lib/rails/secrets.rb +++ b/railties/lib/rails/secrets.rb @@ -2,7 +2,6 @@ require "yaml" require "active_support/message_encryptor" -require "active_support/core_ext/string/strip" module Rails # Greatly inspired by Ara T. Howard's magnificent sekrets gem. 😘 @@ -32,23 +31,10 @@ module Rails end end - def generate_key - SecureRandom.hex(OpenSSL::Cipher.new(@cipher).key_len) - end - def key ENV["RAILS_MASTER_KEY"] || read_key_file || handle_missing_key end - def template - <<-end_of_template.strip_heredoc - # See `secrets.yml` for tips on generating suitable keys. - # production: - # external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289 - - end_of_template - end - def encrypt(data) encryptor.encrypt_and_sign(data) end @@ -70,10 +56,6 @@ module Rails writing(read, &block) end - def read_template_for_editing(&block) - writing(template, &block) - end - private def handle_missing_key raise MissingKeyError diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb index 1db6c98537..7257aaeaae 100644 --- a/railties/lib/rails/source_annotation_extractor.rb +++ b/railties/lib/rails/source_annotation_extractor.rb @@ -1,141 +1,150 @@ # frozen_string_literal: true -# Implements the logic behind the rake tasks for annotations like -# -# rails notes -# rails notes:optimize -# -# and friends. See <tt>rails -T notes</tt> and <tt>railties/lib/rails/tasks/annotations.rake</tt>. -# -# Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that -# represent the line where the annotation lives, its tag, and its text. Note -# the filename is not stored. -# -# Annotations are looked for in comments and modulus whitespace they have to -# start with the tag optionally followed by a colon. Everything up to the end -# of the line (or closing ERB comment tag) is considered to be their text. -class SourceAnnotationExtractor - Annotation = Struct.new(:line, :tag, :text) do - def self.directories - @@directories ||= %w(app config db lib test) + (ENV["SOURCE_ANNOTATION_DIRECTORIES"] || "").split(",") - end +require "active_support/deprecation" - # Registers additional directories to be included - # SourceAnnotationExtractor::Annotation.register_directories("spec", "another") - def self.register_directories(*dirs) - directories.push(*dirs) - end +# Remove this deprecated class in the next minor version +#:nodoc: +SourceAnnotationExtractor = ActiveSupport::Deprecation::DeprecatedConstantProxy. + new("SourceAnnotationExtractor", "Rails::SourceAnnotationExtractor") - def self.extensions - @@extensions ||= {} - end +module Rails + # Implements the logic behind the rake tasks for annotations like + # + # rails notes + # rails notes:optimize + # + # and friends. See <tt>rails -T notes</tt> and <tt>railties/lib/rails/tasks/annotations.rake</tt>. + # + # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that + # represent the line where the annotation lives, its tag, and its text. Note + # the filename is not stored. + # + # Annotations are looked for in comments and modulus whitespace they have to + # start with the tag optionally followed by a colon. Everything up to the end + # of the line (or closing ERB comment tag) is considered to be their text. + class SourceAnnotationExtractor + class Annotation < Struct.new(:line, :tag, :text) + def self.directories + @@directories ||= %w(app config db lib test) + (ENV["SOURCE_ANNOTATION_DIRECTORIES"] || "").split(",") + end - # Registers new Annotations File Extensions - # SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ } - def self.register_extensions(*exts, &block) - extensions[/\.(#{exts.join("|")})$/] = block - end + # Registers additional directories to be included + # Rails::SourceAnnotationExtractor::Annotation.register_directories("spec", "another") + def self.register_directories(*dirs) + directories.push(*dirs) + end - register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ } - register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ } - register_extensions("erb") { |tag| /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ } + def self.extensions + @@extensions ||= {} + end - # Returns a representation of the annotation that looks like this: + # Registers new Annotations File Extensions + # Rails::SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ } + def self.register_extensions(*exts, &block) + extensions[/\.(#{exts.join("|")})$/] = block + end + + register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ } + register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ } + register_extensions("erb") { |tag| /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ } + + # Returns a representation of the annotation that looks like this: + # + # [126] [TODO] This algorithm is simple and clearly correct, make it faster. + # + # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above. + # Otherwise the string contains just line and text. + def to_s(options = {}) + s = "[#{line.to_s.rjust(options[:indent])}] ".dup + s << "[#{tag}] " if options[:tag] + s << text + end + end + + # Prints all annotations with tag +tag+ under the root directories +app+, + # +config+, +db+, +lib+, and +test+ (recursively). # - # [126] [TODO] This algorithm is simple and clearly correct, make it faster. + # Additional directories may be added using a comma-delimited list set using + # <tt>ENV['SOURCE_ANNOTATION_DIRECTORIES']</tt>. # - # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above. - # Otherwise the string contains just line and text. - def to_s(options = {}) - s = "[#{line.to_s.rjust(options[:indent])}] ".dup - s << "[#{tag}] " if options[:tag] - s << text + # Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+. + # + # Rails::SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true + # + # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+. + # + # 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 rake tasks. + def self.enumerate(tag, options = {}) + extractor = new(tag) + dirs = options.delete(:dirs) || Annotation.directories + extractor.display(extractor.find(dirs), options) end - end - # Prints all annotations with tag +tag+ under the root directories +app+, - # +config+, +db+, +lib+, and +test+ (recursively). - # - # Additional directories may be added using a comma-delimited list set using - # <tt>ENV['SOURCE_ANNOTATION_DIRECTORIES']</tt>. - # - # Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+. - # - # SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true - # - # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+. - # - # 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 rake tasks. - def self.enumerate(tag, options = {}) - extractor = new(tag) - dirs = options.delete(:dirs) || Annotation.directories - extractor.display(extractor.find(dirs), options) - end - - attr_reader :tag - - def initialize(tag) - @tag = tag - end + attr_reader :tag - # Returns a hash that maps filenames under +dirs+ (recursively) to arrays - # with their annotations. - def find(dirs) - dirs.inject({}) { |h, dir| h.update(find_in(dir)) } - end + def initialize(tag) + @tag = tag + end - # Returns a hash that maps filenames under +dir+ (recursively) to arrays - # with their annotations. Only files with annotations are included. Files - # with extension +.builder+, +.rb+, +.rake+, +.yml+, +.yaml+, +.ruby+, - # +.css+, +.js+ and +.erb+ are taken into account. - def find_in(dir) - results = {} - - Dir.glob("#{dir}/*") do |item| - next if File.basename(item)[0] == ?. - - if File.directory?(item) - results.update(find_in(item)) - else - extension = Annotation.extensions.detect do |regexp, _block| - regexp.match(item) - end + # Returns a hash that maps filenames under +dirs+ (recursively) to arrays + # with their annotations. + def find(dirs) + dirs.inject({}) { |h, dir| h.update(find_in(dir)) } + end - if extension - pattern = extension.last.call(tag) - results.update(extract_annotations_from(item, pattern)) if pattern + # Returns a hash that maps filenames under +dir+ (recursively) to arrays + # with their annotations. Files with extensions registered in + # <tt>Rails::SourceAnnotationExtractor::Annotation.extensions</tt> are + # taken into account. Only files with annotations are included. + def find_in(dir) + results = {} + + Dir.glob("#{dir}/*") do |item| + next if File.basename(item)[0] == ?. + + if File.directory?(item) + results.update(find_in(item)) + else + extension = Annotation.extensions.detect do |regexp, _block| + regexp.match(item) + end + + if extension + pattern = extension.last.call(tag) + results.update(extract_annotations_from(item, pattern)) if pattern + end end end - end - results - end + results + end - # If +file+ is the filename of a file that contains annotations this method returns - # a hash with a single entry that maps +file+ to an array of its annotations. - # Otherwise it returns an empty hash. - def extract_annotations_from(file, pattern) - lineno = 0 - result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line| - lineno += 1 - next list unless line =~ pattern - list << Annotation.new(lineno, $1, $2) + # If +file+ is the filename of a file that contains annotations this method returns + # a hash with a single entry that maps +file+ to an array of its annotations. + # Otherwise it returns an empty hash. + def extract_annotations_from(file, pattern) + lineno = 0 + result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line| + lineno += 1 + next list unless line =~ pattern + list << Annotation.new(lineno, $1, $2) + end + result.empty? ? {} : { file => result } end - result.empty? ? {} : { file => result } - end - # Prints the mapping from filenames to annotations in +results+ ordered by filename. - # The +options+ hash is passed to each annotation's +to_s+. - def display(results, options = {}) - options[:indent] = results.flat_map { |f, a| a.map(&:line) }.max.to_s.size - results.keys.sort.each do |file| - puts "#{file}:" - results[file].each do |note| - puts " * #{note.to_s(options)}" + # Prints the mapping from filenames to annotations in +results+ ordered by filename. + # The +options+ hash is passed to each annotation's +to_s+. + def display(results, options = {}) + options[:indent] = results.flat_map { |f, a| a.map(&:line) }.max.to_s.size + results.keys.sort.each do |file| + puts "#{file}:" + results[file].each do |note| + puts " * #{note.to_s(options)}" + end + puts end - puts end end end diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb index 2f644a20c9..56f2eba312 100644 --- a/railties/lib/rails/tasks.rb +++ b/railties/lib/rails/tasks.rb @@ -12,7 +12,6 @@ require "rake" middleware misc restart - routes tmp yarn ).tap { |arr| diff --git a/railties/lib/rails/tasks/annotations.rake b/railties/lib/rails/tasks/annotations.rake index 931f81ac7e..60bcdc5e1b 100644 --- a/railties/lib/rails/tasks/annotations.rake +++ b/railties/lib/rails/tasks/annotations.rake @@ -1,22 +1,22 @@ # frozen_string_literal: true -require_relative "../source_annotation_extractor" +require "rails/source_annotation_extractor" desc "Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)" task :notes do - SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", tag: true + Rails::SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", tag: true end namespace :notes do ["OPTIMIZE", "FIXME", "TODO"].each do |annotation| # desc "Enumerate all #{annotation} annotations" task annotation.downcase.intern do - SourceAnnotationExtractor.enumerate annotation + Rails::SourceAnnotationExtractor.enumerate annotation end end desc "Enumerate a custom annotation, specify with ANNOTATION=CUSTOM" task :custom do - SourceAnnotationExtractor.enumerate ENV["ANNOTATION"] + Rails::SourceAnnotationExtractor.enumerate ENV["ANNOTATION"] end end diff --git a/railties/lib/rails/tasks/dev.rake b/railties/lib/rails/tasks/dev.rake index 90fd3ccd8b..5aea6f7dc5 100644 --- a/railties/lib/rails/tasks/dev.rake +++ b/railties/lib/rails/tasks/dev.rake @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../dev_caching" +require "rails/dev_caching" namespace :dev do desc "Toggle development mode caching on/off" diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake index 3ce49a1641..8d77904210 100644 --- a/railties/lib/rails/tasks/engine.rake +++ b/railties/lib/rails/tasks/engine.rake @@ -8,7 +8,7 @@ task "load_app" do task update: [ "update:bin" ] namespace :update do - require_relative "../engine/updater" + require "rails/engine/updater" # desc "Adds new executables to the engine bin/ directory" task :bin do Rails::Engine::Updater.run(:create_bin_files) @@ -53,7 +53,7 @@ namespace :db do desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)." app_task "rollback" - desc "Create a db/schema.rb file that can be portably used against any DB supported by Active Record" + desc "Create a db/schema.rb file that can be portably used against any database supported by Active Record" app_task "schema:dump" desc "Load a schema.rb file into the database" @@ -62,7 +62,7 @@ namespace :db do desc "Load the seed data from db/seeds.rb" app_task "seed" - desc "Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)" + desc "Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the database first)" app_task "setup" desc "Dump the database structure to an SQL file" @@ -70,6 +70,9 @@ namespace :db do desc "Retrieves the current schema version number" app_task "version" + + # desc 'Load the test schema' + app_task "test:prepare" end def find_engine_path(path) diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 8931aabcda..7dfcd14bd0 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -9,8 +9,8 @@ namespace :app do template = ENV["LOCATION"] raise "No LOCATION value given. Please set LOCATION either as path to a file or a URL" if template.blank? template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} - require_relative "../generators" - require_relative "../generators/rails/app/app_generator" + require "rails/generators" + require "rails/generators/rails/app/app_generator" generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root } generator.apply template, verbose: false end @@ -38,7 +38,7 @@ namespace :app do end namespace :update do - require_relative "../app_updater" + require "rails/app_updater" # desc "Update config/boot.rb from your current rails install" task :configs do diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake deleted file mode 100644 index 403286d280..0000000000 --- a/railties/lib/rails/tasks/routes.rake +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -require "optparse" - -desc "Print out all defined routes in match order, with names. Target specific controller with -c option, or grep routes using -g option" -task routes: :environment do - all_routes = Rails.application.routes.routes - require "action_dispatch/routing/inspector" - inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes) - - routes_filter = nil - - OptionParser.new do |opts| - opts.banner = "Usage: rails routes [options]" - - Rake.application.standard_rake_options.each { |args| opts.on(*args) } - - opts.on("-c CONTROLLER") do |controller| - routes_filter = { controller: controller } - end - - opts.on("-g PATTERN") do |pattern| - routes_filter = pattern - end - - end.parse!(ARGV.reject { |x| x == "routes" }) - - puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, routes_filter) - - exit 0 # ensure extra arguments aren't interpreted as Rake tasks -end diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake index 0449a13586..594db91eec 100644 --- a/railties/lib/rails/tasks/statistics.rake +++ b/railties/lib/rails/tasks/statistics.rake @@ -26,6 +26,6 @@ end.select { |name, dir| File.directory?(dir) } desc "Report code statistics (KLOCs, etc) from the application or engine" task :stats do - require_relative "../code_statistics" + require "rails/code_statistics" CodeStatistics.new(*STATS_DIRECTORIES).to_s end diff --git a/railties/lib/rails/tasks/yarn.rake b/railties/lib/rails/tasks/yarn.rake index 48da7ffc51..10703a1808 100644 --- a/railties/lib/rails/tasks/yarn.rake +++ b/railties/lib/rails/tasks/yarn.rake @@ -3,7 +3,7 @@ namespace :yarn do desc "Install all JavaScript dependencies as specified via Yarn" task :install do - system("./bin/yarn install --no-progress --production") + system("./bin/yarn install --no-progress --frozen-lockfile --production") end end diff --git a/railties/lib/rails/templates/rails/mailers/email.html.erb b/railties/lib/rails/templates/rails/mailers/email.html.erb index 89c1129f90..2a41c29602 100644 --- a/railties/lib/rails/templates/rails/mailers/email.html.erb +++ b/railties/lib/rails/templates/rails/mailers/email.html.erb @@ -95,11 +95,25 @@ </dd> <% end %> + <dt>Format:</dt> <% if @email.multipart? %> <dd> - <select onchange="formatChanged(this);"> - <option <%= request.format == Mime[:html] ? 'selected' : '' %> value="?<%= part_query('text/html') %>">View as HTML email</option> - <option <%= request.format == Mime[:text] ? 'selected' : '' %> value="?<%= part_query('text/plain') %>">View as plain-text email</option> + <select id="part" onchange="refreshBody();"> + <option <%= request.format == Mime[:html] ? 'selected' : '' %> value="<%= part_query('text/html') %>">View as HTML email</option> + <option <%= request.format == Mime[:text] ? 'selected' : '' %> value="<%= part_query('text/plain') %>">View as plain-text email</option> + </select> + </dd> + <% else %> + <dd id="mime_type" data-mime-type="<%= part_query(@email.mime_type) %>"><%= @email.mime_type == 'text/html' ? 'HTML email' : 'plain-text email' %></dd> + <% end %> + + <% if I18n.available_locales.count > 1 %> + <dt>Locale:</dt> + <dd> + <select id="locale" onchange="refreshBody();"> + <% I18n.available_locales.each do |locale| %> + <option <%= I18n.locale == locale ? 'selected' : '' %> value="<%= locale_query(locale) %>"><%= locale %></option> + <% end %> </select> </dd> <% end %> @@ -116,15 +130,27 @@ <% end %> <script> - function formatChanged(form) { - var part_name = form.options[form.selectedIndex].value - var iframe =document.getElementsByName('messageBody')[0]; - iframe.contentWindow.location.replace(part_name); + function refreshBody() { + var part_select = document.querySelector('select#part'); + var locale_select = document.querySelector('select#locale'); + var iframe = document.getElementsByName('messageBody')[0]; + var part_param = part_select ? + part_select.options[part_select.selectedIndex].value : + document.querySelector('#mime_type').dataset.mimeType; + var locale_param = locale_select ? locale_select.options[locale_select.selectedIndex].value : null; + var fresh_location; + if (locale_param) { + fresh_location = '?' + part_param + '&' + locale_param; + } else { + fresh_location = '?' + part_param; + } + iframe.contentWindow.location = fresh_location; if (history.replaceState) { - var url = location.pathname.replace(/\.(txt|html)$/, ''); - var format = /html/.test(part_name) ? '.html' : '.txt'; - window.history.replaceState({}, '', url + format); + var url = location.pathname.replace(/\.(txt|html)$/, ''); + var format = /html/.test(part_param) ? '.html' : '.txt'; + var state_to_replace = locale_param ? (url + format + '?' + locale_param) : (url + format); + window.history.replaceState({}, '', state_to_replace); } } </script> diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index f755474488..4bd7d74b04 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -8,7 +8,7 @@ require "active_support/test_case" require "action_controller" require "action_controller/test_case" require "action_dispatch/testing/integration" -require_relative "generators/test_case" +require "rails/generators/test_case" require "active_support/testing/autorun" @@ -22,6 +22,7 @@ if defined?(ActiveRecord::Base) module ActiveSupport class TestCase + include ActiveRecord::TestDatabases include ActiveRecord::TestFixtures self.fixture_path = "#{Rails.root}/test/fixtures/" self.file_fixture_path = fixture_path + "files" @@ -29,10 +30,6 @@ if defined?(ActiveRecord::Base) end ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path - - def create_fixtures(*fixture_set_names, &block) - FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, {}, &block) - end end # :enddoc: diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb index 05f9a177d0..42b6daa3d1 100644 --- a/railties/lib/rails/test_unit/railtie.rb +++ b/railties/lib/rails/test_unit/railtie.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "line_filtering" +require "rails/test_unit/line_filtering" if defined?(Rake.application) && Rake.application.top_level_tasks.grep(/^(default$|test(:|$))/).any? ENV["RAILS_ENV"] ||= Rake.application.options.show_tasks ? "development" : "test" diff --git a/railties/lib/rails/test_unit/reporter.rb b/railties/lib/rails/test_unit/reporter.rb index 7d3164f1eb..28b93cee5a 100644 --- a/railties/lib/rails/test_unit/reporter.rb +++ b/railties/lib/rails/test_unit/reporter.rb @@ -64,11 +64,17 @@ module Rails end def format_line(result) - "%s#%s = %.2f s = %s" % [result.class, result.name, result.time, result.result_code] + klass = result.respond_to?(:klass) ? result.klass : result.class + "%s#%s = %.2f s = %s" % [klass, result.name, result.time, result.result_code] end def format_rerun_snippet(result) - location, line = result.method(result.name).source_location + location, line = if result.respond_to?(:source_location) + result.source_location + else + result.method(result.name).source_location + end + "#{executable} #{relative_path_for(location)}:#{line}" end diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb index 5c2f6451e2..de5744c662 100644 --- a/railties/lib/rails/test_unit/runner.rb +++ b/railties/lib/rails/test_unit/runner.rb @@ -13,7 +13,7 @@ module Rails class << self def attach_before_load_options(opts) opts.on("--warnings", "-w", "Run with Ruby warnings enabled") {} - opts.on("--environment", "-e", "Run tests in the ENV environment") {} + opts.on("-e", "--environment ENV", "Run tests in the ENV environment") {} end def parse_options(argv) diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index 18d699f806..32ac27a135 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -2,7 +2,7 @@ gem "minitest" require "minitest" -require_relative "runner" +require "rails/test_unit/runner" task default: :test diff --git a/railties/lib/rails/welcome_controller.rb b/railties/lib/rails/welcome_controller.rb index 0cb43e0f31..5b84b57679 100644 --- a/railties/lib/rails/welcome_controller.rb +++ b/railties/lib/rails/welcome_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "application_controller" +require "rails/application_controller" class Rails::WelcomeController < Rails::ApplicationController # :nodoc: layout false diff --git a/railties/railties.gemspec b/railties/railties.gemspec index 4c665cd546..1df8b1fe39 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.summary = "Tools for creating, working with, and running Rails applications." s.description = "Rails internals: application bootup, plugins, generators, and rake tasks." - s.required_ruby_version = ">= 2.2.2" + s.required_ruby_version = ">= 2.4.1" s.license = "MIT" diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb index bb556f1968..c7a6bdee1b 100644 --- a/railties/test/app_loader_test.rb +++ b/railties/test/app_loader_test.rb @@ -40,13 +40,13 @@ class AppLoaderTest < ActiveSupport::TestCase test "is not in a Rails application if #{exe} is not found in the current or parent directories" do def loader.find_executables; end - assert !loader.exec_app + assert_not loader.exec_app end test "is not in a Rails application if #{exe} exists but is a folder" do FileUtils.mkdir_p(exe) - assert !loader.exec_app + assert_not loader.exec_app end ["APP_PATH", "ENGINE_PATH"].each do |keyword| @@ -61,7 +61,7 @@ class AppLoaderTest < ActiveSupport::TestCase test "is not in a Rails application if #{exe} exists but doesn't contain #{keyword}" do write exe - assert !loader.exec_app + assert_not loader.exec_app end test "is in a Rails application if parent directory has #{exe} containing #{keyword} and chdirs to the root directory" do diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb index e56c7b958e..3e0f31860b 100644 --- a/railties/test/application/asset_debugging_test.rb +++ b/railties/test/application/asset_debugging_test.rb @@ -77,7 +77,8 @@ module ApplicationTests stylesheet_link_tag: %r{<link rel="stylesheet" media="screen" href="/stylesheets/#{contents}.css" />}, javascript_include_tag: %r{<script src="/javascripts/#{contents}.js">}, audio_tag: %r{<audio src="/audios/#{contents}"></audio>}, - video_tag: %r{<video src="/videos/#{contents}"></video>} + video_tag: %r{<video src="/videos/#{contents}"></video>}, + image_submit_tag: %r{<input type="image" src="/images/#{contents}" />} } cases.each do |(view_method, tag_match)| diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index 0d3262d6f6..9ef123c5b6 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -76,7 +76,7 @@ module ApplicationTests # Load app env app "production" - assert !defined?(Uglifier) + assert_not defined?(Uglifier) get "/assets/demo.js" assert_match "alert()", last_response.body assert defined?(Uglifier) @@ -270,10 +270,10 @@ module ApplicationTests app "production" # Checking if Uglifier is defined we can know if Sprockets was reached or not - assert !defined?(Uglifier) + assert_not defined?(Uglifier) get "/assets/#{asset_path}" assert_match "alert()", last_response.body - assert !defined?(Uglifier) + assert_not defined?(Uglifier) end test "precompile properly refers files referenced with asset_path" do diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb index 05b17b4a7a..608bc2fbe3 100644 --- a/railties/test/application/configuration/custom_test.rb +++ b/railties/test/application/configuration/custom_test.rb @@ -33,7 +33,7 @@ module ApplicationTests assert_nil x.i_do_not_exist.zomg # test that custom configuration responds to all messages - assert_equal true, x.respond_to?(:i_do_not_exist) + assert_respond_to x, :i_do_not_exist assert_kind_of Method, x.method(:i_do_not_exist) assert_kind_of ActiveSupport::OrderedOptions, x.i_do_not_exist end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c1a80eaeaf..84606d3b90 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -94,7 +94,7 @@ module ApplicationTests with_rails_env "development" do app "development" - assert Rails.application.config.log_tags.blank? + assert_predicate Rails.application.config.log_tags, :blank? end end @@ -361,7 +361,7 @@ module ApplicationTests end RUBY - assert !$prepared + assert_not $prepared app "development" @@ -487,6 +487,32 @@ module ApplicationTests assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message) end + test "config.secret_token is deprecated" do + app_file "config/initializers/secret_token.rb", <<-RUBY + Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" + RUBY + + app "production" + + assert_deprecated(/secret_token/) do + app.secrets + end + end + + test "secrets.secret_token is deprecated" do + app_file "config/secrets.yml", <<-YAML + production: + secret_token: "b3c631c314c0bbca50c1b2843150fe33" + YAML + + app "production" + + assert_deprecated(/secret_token/) do + app.secrets + 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 @@ -550,6 +576,7 @@ module ApplicationTests app "development" assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_key_base + assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secret_key_base end test "secret_key_base is copied from config to secrets when not set" do @@ -681,6 +708,14 @@ module ApplicationTests assert_match(/Missing.*RAILS_MASTER_KEY/, error) end + test "credentials does not raise error when require_master_key is false and master key does not exist" do + remove_file "config/master.key" + add_to_config "config.require_master_key = false" + app "development" + + assert_not app.credentials.secret_key_base + end + test "protect from forgery is the default in a new app" do make_basic_app @@ -731,6 +766,68 @@ module ApplicationTests assert_match(/label/, last_response.body) end + test "form_with can be configured with form_with_generates_ids" do + app_file "config/initializers/form_builder.rb", <<-RUBY + Rails.configuration.action_view.form_with_generates_ids = false + RUBY + + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + + assert_no_match(/id=('|")post_name('|")/, last_response.body) + end + + test "form_with outputs ids by default" do + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + + assert_match(/id=('|")post_name('|")/, last_response.body) + end + test "form_with can be configured with form_with_generates_remote_forms" do app_file "config/initializers/form_builder.rb", <<-RUBY Rails.configuration.action_view.form_with_generates_remote_forms = false @@ -1229,7 +1326,7 @@ module ApplicationTests assert_equal 200, last_response.status end - test "config.action_controller.action_on_unpermitted_parameters is :log by default on development" do + test "config.action_controller.action_on_unpermitted_parameters is :log by default in development" do app "development" force_lazy_load_hooks { ActionController::Base } @@ -1238,7 +1335,7 @@ module ApplicationTests assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters end - test "config.action_controller.action_on_unpermitted_parameters is :log by default on test" do + test "config.action_controller.action_on_unpermitted_parameters is :log by default in test" do app "test" force_lazy_load_hooks { ActionController::Base } @@ -1247,7 +1344,7 @@ module ApplicationTests assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters end - test "config.action_controller.action_on_unpermitted_parameters is false by default on production" do + test "config.action_controller.action_on_unpermitted_parameters is false by default in production" do app "production" force_lazy_load_hooks { ActionController::Base } @@ -1320,14 +1417,14 @@ module ApplicationTests assert_equal "XML", last_response.body end - test "Rails.application#env_config exists and include some existing parameters" do + test "Rails.application#env_config exists and includes some existing parameters" do make_basic_app - assert_equal app.env_config["action_dispatch.parameter_filter"], app.config.filter_parameters - assert_equal app.env_config["action_dispatch.show_exceptions"], app.config.action_dispatch.show_exceptions - assert_equal app.env_config["action_dispatch.logger"], Rails.logger - assert_equal app.env_config["action_dispatch.backtrace_cleaner"], Rails.backtrace_cleaner - assert_equal app.env_config["action_dispatch.key_generator"], Rails.application.key_generator + assert_equal app.env_config["action_dispatch.parameter_filter"], app.config.filter_parameters + assert_equal app.env_config["action_dispatch.show_exceptions"], app.config.action_dispatch.show_exceptions + assert_equal app.env_config["action_dispatch.logger"], Rails.logger + assert_equal app.env_config["action_dispatch.backtrace_cleaner"], Rails.backtrace_cleaner + assert_equal app.env_config["action_dispatch.key_generator"], Rails.application.key_generator end test "config.colorize_logging default is true" do @@ -1382,7 +1479,7 @@ module ApplicationTests test "respond_to? accepts include_private" do make_basic_app - assert_not Rails.configuration.respond_to?(:method_missing) + assert_not_respond_to Rails.configuration, :method_missing assert Rails.configuration.respond_to?(:method_missing, true) end @@ -1394,12 +1491,18 @@ module ApplicationTests assert_not ActiveRecord::Base.dump_schema_after_migration end - test "config.active_record.dump_schema_after_migration is true by default on development" do + test "config.active_record.dump_schema_after_migration is true by default in development" do app "development" assert ActiveRecord::Base.dump_schema_after_migration end + test "config.active_record.verbose_query_logs is false by default in development" do + app "development" + + assert_not ActiveRecord::Base.verbose_query_logs + end + test "config.annotations wrapping SourceAnnotationExtractor::Annotation class" do make_basic_app do |application| application.config.annotations.register_extensions("coffee") do |tag| @@ -1407,7 +1510,7 @@ module ApplicationTests end end - assert_not_nil SourceAnnotationExtractor::Annotation.extensions[/\.(coffee)$/] + assert_not_nil Rails::SourceAnnotationExtractor::Annotation.extensions[/\.(coffee)$/] end test "rake_tasks block works at instance level" do @@ -1637,9 +1740,7 @@ module ApplicationTests test "default SQLite3Adapter.represent_boolean_as_integer for 5.1 is false" do remove_from_config '.*config\.load_defaults.*\n' - add_to_top_of_config <<-RUBY - config.load_defaults 5.1 - RUBY + app_file "app/models/post.rb", <<-RUBY class Post < ActiveRecord::Base end @@ -1666,7 +1767,7 @@ module ApplicationTests test "represent_boolean_as_integer should be able to set via config.active_record.sqlite3.represent_boolean_as_integer" do remove_from_config '.*config\.load_defaults.*\n' - app_file "config/initializers/new_framework_defaults_5_2.rb", <<-RUBY + app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true RUBY @@ -1731,7 +1832,7 @@ module ApplicationTests test "api_only is false by default" do app "development" - refute Rails.application.config.api_only + assert_not Rails.application.config.api_only end test "api_only generator config is set when api_only is set" do @@ -1788,6 +1889,96 @@ module ApplicationTests assert_equal "https://example.org/", last_response.location end + test "ActiveSupport::MessageEncryptor.use_authenticated_message_encryption is true by default for new apps" do + app "development" + + assert_equal true, ActiveSupport::MessageEncryptor.use_authenticated_message_encryption + end + + test "ActiveSupport::MessageEncryptor.use_authenticated_message_encryption is false by default for upgraded apps" do + remove_from_config '.*config\.load_defaults.*\n' + + app "development" + + assert_equal false, ActiveSupport::MessageEncryptor.use_authenticated_message_encryption + end + + test "ActiveSupport::MessageEncryptor.use_authenticated_message_encryption can be configured via config.active_support.use_authenticated_message_encryption" do + remove_from_config '.*config\.load_defaults.*\n' + + app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY + Rails.application.config.active_support.use_authenticated_message_encryption = true + RUBY + + app "development" + + assert_equal true, ActiveSupport::MessageEncryptor.use_authenticated_message_encryption + end + + test "ActiveSupport::Digest.hash_digest_class is Digest::SHA1 by default for new apps" do + app "development" + + assert_equal Digest::SHA1, ActiveSupport::Digest.hash_digest_class + end + + test "ActiveSupport::Digest.hash_digest_class is Digest::MD5 by default for upgraded apps" do + remove_from_config '.*config\.load_defaults.*\n' + + app "development" + + assert_equal Digest::MD5, ActiveSupport::Digest.hash_digest_class + end + + test "ActiveSupport::Digest.hash_digest_class can be configured via config.active_support.use_sha1_digests" do + remove_from_config '.*config\.load_defaults.*\n' + + app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY + Rails.application.config.active_support.use_sha1_digests = true + RUBY + + app "development" + + assert_equal Digest::SHA1, ActiveSupport::Digest.hash_digest_class + end + + test "custom serializers should be able to set via config.active_job.custom_serializers in an initializer" do + class ::DummySerializer < ActiveJob::Serializers::ObjectSerializer; end + + app_file "config/initializers/custom_serializers.rb", <<-RUBY + Rails.application.config.active_job.custom_serializers << DummySerializer + RUBY + + app "development" + + assert_includes ActiveJob::Serializers.serializers, DummySerializer + end + + test "ActionView::Helpers::FormTagHelper.default_enforce_utf8 is false by default" do + app "development" + assert_equal false, ActionView::Helpers::FormTagHelper.default_enforce_utf8 + end + + test "ActionView::Helpers::FormTagHelper.default_enforce_utf8 is true in an upgraded app" do + remove_from_config '.*config\.load_defaults.*\n' + add_to_config 'config.load_defaults "5.2"' + + app "development" + + assert_equal true, ActionView::Helpers::FormTagHelper.default_enforce_utf8 + end + + test "ActionView::Helpers::FormTagHelper.default_enforce_utf8 can be configured via config.action_view.default_enforce_utf8" do + remove_from_config '.*config\.load_defaults.*\n' + + app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY + Rails.application.config.action_view.default_enforce_utf8 = true + RUBY + + app "development" + + assert_equal true, ActionView::Helpers::FormTagHelper.default_enforce_utf8 + 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 13164f49c2..4a14042cd3 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -73,7 +73,7 @@ class ConsoleTest < ActiveSupport::TestCase MODEL load_environment - assert User.new.respond_to?(:name) + assert_respond_to User.new, :name app_file "app/models/user.rb", <<-MODEL class User @@ -81,9 +81,9 @@ class ConsoleTest < ActiveSupport::TestCase end MODEL - assert !User.new.respond_to?(:age) + assert_not_respond_to User.new, :age irb_context.reload!(false) - assert User.new.respond_to?(:age) + assert_respond_to User.new, :age end def test_access_to_helpers diff --git a/railties/test/application/content_security_policy_test.rb b/railties/test/application/content_security_policy_test.rb new file mode 100644 index 0000000000..0d28df16f8 --- /dev/null +++ b/railties/test/application/content_security_policy_test.rb @@ -0,0 +1,223 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "rack/test" + +module ApplicationTests + class ContentSecurityPolicyTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + include Rack::Test::Methods + + def setup + build_app + end + + def teardown + teardown_app + end + + test "default content security policy is nil" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_nil last_response.headers["Content-Security-Policy"] + end + + test "empty content security policy is generated" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "" + end + + test "global content security policy in an initializer" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:" + end + + test "global report only content security policy in an initializer" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + + Rails.application.config.content_security_policy_report_only = true + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:", report_only: true + end + + test "override content security policy in a controller" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + content_security_policy do |p| + p.default_src "https://example.com" + end + + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src https://example.com" + end + + test "override content security policy to report only in a controller" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + content_security_policy_report_only + + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:", report_only: true + end + + test "global content security policy added to rack app" do + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + + app = ->(env) { + [200, { "Content-Type" => "text/html" }, ["<p>Hello, World!</p>"]] + } + + root to: app + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:" + end + + private + + def assert_policy(expected, report_only: false) + assert_equal 200, last_response.status + + if report_only + expected_header = "Content-Security-Policy-Report-Only" + unexpected_header = "Content-Security-Policy" + else + expected_header = "Content-Security-Policy" + unexpected_header = "Content-Security-Policy-Report-Only" + end + + assert_nil last_response.headers[unexpected_header] + assert_equal expected, last_response.headers[expected_header] + end + end +end diff --git a/railties/test/application/dbconsole_test.rb b/railties/test/application/dbconsole_test.rb index ba04c81b4d..8eb293c179 100644 --- a/railties/test/application/dbconsole_test.rb +++ b/railties/test/application/dbconsole_test.rb @@ -19,21 +19,19 @@ module ApplicationTests end def test_use_value_defined_in_environment_file_in_database_yml - Dir.chdir(app_path) do - app_file "config/database.yml", <<-YAML - development: - database: <%= Rails.application.config.database %> - adapter: sqlite3 - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 - YAML + app_file "config/database.yml", <<-YAML + development: + database: <%= Rails.application.config.database %> + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + YAML - app_file "config/environments/development.rb", <<-RUBY - Rails.application.configure do - config.database = "db/development.sqlite3" - end - RUBY - end + app_file "config/environments/development.rb", <<-RUBY + Rails.application.configure do + config.database = "db/development.sqlite3" + end + RUBY master, slave = PTY.open spawn_dbconsole(slave) @@ -43,22 +41,20 @@ module ApplicationTests end def test_respect_environment_option - Dir.chdir(app_path) do - app_file "config/database.yml", <<-YAML - default: &default - adapter: sqlite3 - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 + app_file "config/database.yml", <<-YAML + default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 - development: - <<: *default - database: db/development.sqlite3 + development: + <<: *default + database: db/development.sqlite3 - production: - <<: *default - database: db/production.sqlite3 - YAML - end + production: + <<: *default + database: db/production.sqlite3 + YAML master, slave = PTY.open spawn_dbconsole(slave, "-e production") diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index 47c815d221..e5e557d204 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -188,10 +188,11 @@ module ApplicationTests Rails::Command.send(:remove_const, "APP_PATH") end - test "help does not show hidden namespaces" do + test "help does not show hidden namespaces and hidden commands" do FileUtils.cd(rails_root) do output = rails("generate", "--help") assert_no_match "active_record:migration", output + assert_no_match "credentials", output output = rails("destroy", "--help") assert_no_match "active_record:migration", output diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index d2b77bd015..1530ea82d6 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -226,7 +226,7 @@ module ApplicationTests rails %w(generate model post title:string) rails %w(db:migrate db:schema:cache:dump db:rollback) require "#{app_path}/config/environment" - assert !ActiveRecord::Base.connection.schema_cache.data_sources("posts") + assert_not ActiveRecord::Base.connection.schema_cache.data_sources("posts") end test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do @@ -266,7 +266,7 @@ module ApplicationTests ActiveRecord::Base.connection RUBY require "#{app_path}/config/environment" - assert !ActiveRecord::Base.connection_pool.active_connection? + assert_not_predicate ActiveRecord::Base.connection_pool, :active_connection? end end end diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb index 23b20d578c..c65c955734 100644 --- a/railties/test/application/initializers/notifications_test.rb +++ b/railties/test/application/initializers/notifications_test.rb @@ -32,6 +32,7 @@ module ApplicationTests logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new ActiveRecord::Base.logger = logger + ActiveRecord::Base.verbose_query_logs = false # Mimic Active Record notifications instrument "sql.active_record", name: "SQL", sql: "SHOW tables" diff --git a/railties/test/application/integration_test_case_test.rb b/railties/test/application/integration_test_case_test.rb index 9edc907fce..c08761092b 100644 --- a/railties/test/application/integration_test_case_test.rb +++ b/railties/test/application/integration_test_case_test.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true require "isolation/abstract_unit" +require "env_helpers" module ApplicationTests class IntegrationTestCaseTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers setup do build_app @@ -39,13 +40,14 @@ module ApplicationTests end RUBY + with_rails_env("test") { rails("db:migrate") } output = rails("test") assert_match(/0 failures, 0 errors/, output) end end class IntegrationTestDefaultApp < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers setup do build_app @@ -66,6 +68,7 @@ module ApplicationTests end RUBY + with_rails_env("test") { rails("db:migrate") } output = rails("test") assert_match(/0 failures, 0 errors/, output) end diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index 11c886e991..889ad16fb8 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -300,7 +300,7 @@ class LoadingTest < ActiveSupport::TestCase end MIGRATION - Dir.chdir(app_path) { `rake db:migrate` } + rails("db:migrate") require "#{rails_root}/config/environment" get "/title" @@ -314,7 +314,7 @@ class LoadingTest < ActiveSupport::TestCase end MIGRATION - Dir.chdir(app_path) { `rake db:migrate` } + rails("db:migrate") get "/body" assert_equal "BODY", last_response.body @@ -352,11 +352,23 @@ class LoadingTest < ActiveSupport::TestCase def test_initialize_can_be_called_at_any_time require "#{app_path}/config/application" - assert !Rails.initialized? - assert !Rails.application.initialized? + assert_not_predicate Rails, :initialized? + assert_not_predicate Rails.application, :initialized? Rails.initialize! - assert Rails.initialized? - assert Rails.application.initialized? + assert_predicate Rails, :initialized? + assert_predicate Rails.application, :initialized? + end + + test "frameworks aren't loaded during initialization" do + app_file "config/initializers/raise_when_frameworks_load.rb", <<-RUBY + %i(action_controller action_mailer active_job active_record).each do |framework| + ActiveSupport.on_load(framework) { raise "\#{framework} loaded!" } + end + RUBY + + assert_nothing_raised do + require "#{app_path}/config/environment" + end end private diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb index 4e77cece1b..ba186bda44 100644 --- a/railties/test/application/mailer_previews_test.rb +++ b/railties/test/application/mailer_previews_test.rb @@ -296,7 +296,7 @@ module ApplicationTests test "mailer preview not found" do app("development") get "/rails/mailers/notifier" - assert last_response.not_found? + assert_predicate last_response, :not_found? assert_match "Mailer preview 'notifier' not found", last_response.body end @@ -326,7 +326,7 @@ module ApplicationTests app("development") get "/rails/mailers/notifier/bar" - assert last_response.not_found? + assert_predicate last_response, :not_found? assert_match "Email 'bar' not found in NotifierPreview", last_response.body end @@ -382,7 +382,7 @@ module ApplicationTests app("development") get "/rails/mailers/notifier/foo?part=text%2Fhtml" - assert last_response.not_found? + assert_predicate last_response, :not_found? assert_match "Email part 'text/html' not found in NotifierPreview#foo", last_response.body end @@ -450,11 +450,67 @@ module ApplicationTests get "/rails/mailers/notifier/foo.html" assert_equal 200, last_response.status - assert_match '<option selected value="?part=text%2Fhtml">View as HTML email</option>', last_response.body + assert_match '<option selected value="part=text%2Fhtml">View as HTML email</option>', last_response.body get "/rails/mailers/notifier/foo.txt" assert_equal 200, last_response.status - assert_match '<option selected value="?part=text%2Fplain">View as plain-text email</option>', last_response.body + assert_match '<option selected value="part=text%2Fplain">View as plain-text email</option>', last_response.body + end + + test "locale menu selects correct option" do + app_file "config/initializers/available_locales.rb", <<-RUBY + Rails.application.configure do + config.i18n.available_locales = %i[en ja] + end + RUBY + + mailer "notifier", <<-RUBY + class Notifier < ActionMailer::Base + default from: "from@example.com" + + def foo + mail to: "to@example.org" + end + end + RUBY + + html_template "notifier/foo", <<-RUBY + <p>Hello, World!</p> + RUBY + + text_template "notifier/foo", <<-RUBY + Hello, World! + RUBY + + mailer_preview "notifier", <<-RUBY + class NotifierPreview < ActionMailer::Preview + def foo + Notifier.foo + end + end + RUBY + + app("development") + + get "/rails/mailers/notifier/foo.html" + assert_equal 200, last_response.status + assert_match '<option selected value="locale=en">en', last_response.body + assert_match '<option value="locale=ja">ja', last_response.body + + get "/rails/mailers/notifier/foo.html?locale=ja" + assert_equal 200, last_response.status + assert_match '<option value="locale=en">en', last_response.body + assert_match '<option selected value="locale=ja">ja', last_response.body + + get "/rails/mailers/notifier/foo.txt" + assert_equal 200, last_response.status + assert_match '<option selected value="locale=en">en', last_response.body + assert_match '<option value="locale=ja">ja', last_response.body + + get "/rails/mailers/notifier/foo.txt?locale=ja" + assert_equal 200, last_response.status + assert_match '<option value="locale=en">en', last_response.body + assert_match '<option selected value="locale=ja">ja', last_response.body end test "mailer previews create correct links when loaded on a subdirectory" do @@ -520,8 +576,8 @@ module ApplicationTests get "/rails/mailers/notifier/foo.txt" assert_equal 200, last_response.status assert_match '<iframe seamless name="messageBody" src="?part=text%2Fplain">', last_response.body - assert_match '<option selected value="?part=text%2Fplain">', last_response.body - assert_match '<option value="?part=text%2Fhtml">', last_response.body + assert_match '<option selected value="part=text%2Fplain">', last_response.body + assert_match '<option value="part=text%2Fhtml">', last_response.body get "/rails/mailers/notifier/foo?part=text%2Fplain" assert_equal 200, last_response.status @@ -530,8 +586,8 @@ module ApplicationTests get "/rails/mailers/notifier/foo.html?name=Ruby" assert_equal 200, last_response.status assert_match '<iframe seamless name="messageBody" src="?name=Ruby&part=text%2Fhtml">', last_response.body - assert_match '<option selected value="?name=Ruby&part=text%2Fhtml">', last_response.body - assert_match '<option value="?name=Ruby&part=text%2Fplain">', last_response.body + assert_match '<option selected value="name=Ruby&part=text%2Fhtml">', last_response.body + assert_match '<option value="name=Ruby&part=text%2Fplain">', last_response.body get "/rails/mailers/notifier/foo?name=Ruby&part=text%2Fhtml" assert_equal 200, last_response.status diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb index 9822ec563d..3768d8ce2d 100644 --- a/railties/test/application/middleware/cache_test.rb +++ b/railties/test/application/middleware/cache_test.rb @@ -140,7 +140,7 @@ module ApplicationTests etag = last_response.headers["ETag"] get "/expires/expires_etag", { private: true }, { "HTTP_IF_NONE_MATCH" => etag } - assert_equal "miss", last_response.headers["X-Rack-Cache"] + assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end @@ -174,7 +174,7 @@ module ApplicationTests last = last_response.headers["Last-Modified"] get "/expires/expires_last_modified", { private: true }, { "HTTP_IF_MODIFIED_SINCE" => last } - assert_equal "miss", last_response.headers["X-Rack-Cache"] + assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end end diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb index 23f1ec3e35..ecb4ee3446 100644 --- a/railties/test/application/middleware/cookies_test.rb +++ b/railties/test/application/middleware/cookies_test.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true require "isolation/abstract_unit" +require "rack/test" module ApplicationTests class CookiesTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation + include Rack::Test::Methods def new_app File.expand_path("#{app_path}/../new_app") @@ -15,6 +17,10 @@ module ApplicationTests FileUtils.rm_rf("#{app_path}/config/environments") end + def app + Rails.application + end + def teardown teardown_app FileUtils.rm_rf(new_app) if File.directory?(new_app) @@ -44,5 +50,144 @@ module ApplicationTests require "#{app_path}/config/environment" assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie end + + test "signed cookies with SHA512 digest and rotated out SHA256 and SHA1 digests" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get ':controller(/:action)' + post ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + protect_from_forgery with: :null_session + + def write_raw_cookie_sha1 + cookies[:signed_cookie] = TestVerifiers.sha1.generate("signed cookie") + head :ok + end + + def write_raw_cookie_sha256 + cookies[:signed_cookie] = TestVerifiers.sha256.generate("signed cookie") + head :ok + end + + def read_signed + render plain: cookies.signed[:signed_cookie].inspect + end + + def read_raw_cookie + render plain: cookies[:signed_cookie] + end + end + RUBY + + add_to_config <<-RUBY + sha1_secret = Rails.application.key_generator.generate_key("sha1") + sha256_secret = Rails.application.key_generator.generate_key("sha256") + + ::TestVerifiers = Class.new do + class_attribute :sha1, default: ActiveSupport::MessageVerifier.new(sha1_secret, digest: "SHA1") + class_attribute :sha256, default: ActiveSupport::MessageVerifier.new(sha256_secret, digest: "SHA256") + end + + config.action_dispatch.signed_cookie_digest = "SHA512" + config.action_dispatch.signed_cookie_salt = "sha512 salt" + + config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :signed, sha1_secret, digest: "SHA1" + cookies.rotate :signed, sha256_secret, digest: "SHA256" + end + RUBY + + require "#{app_path}/config/environment" + + verifier_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512) + + get "/foo/write_raw_cookie_sha1" + get "/foo/read_signed" + assert_equal "signed cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "signed cookie", verifier_sha512.verify(last_response.body) + + get "/foo/write_raw_cookie_sha256" + get "/foo/read_signed" + assert_equal "signed cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "signed cookie", verifier_sha512.verify(last_response.body) + end + + test "encrypted cookies rotating multiple encryption keys" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get ':controller(/:action)' + post ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + protect_from_forgery with: :null_session + + def write_raw_cookie_one + cookies[:encrypted_cookie] = TestEncryptors.first_gcm.encrypt_and_sign("encrypted cookie") + head :ok + end + + def write_raw_cookie_two + cookies[:encrypted_cookie] = TestEncryptors.second_gcm.encrypt_and_sign("encrypted cookie") + head :ok + end + + def read_encrypted + render plain: cookies.encrypted[:encrypted_cookie].inspect + end + + def read_raw_cookie + render plain: cookies[:encrypted_cookie] + end + end + RUBY + + add_to_config <<-RUBY + first_secret = Rails.application.key_generator.generate_key("first", 32) + second_secret = Rails.application.key_generator.generate_key("second", 32) + + ::TestEncryptors = Class.new do + class_attribute :first_gcm, default: ActiveSupport::MessageEncryptor.new(first_secret, cipher: "aes-256-gcm") + class_attribute :second_gcm, default: ActiveSupport::MessageEncryptor.new(second_secret, cipher: "aes-256-gcm") + end + + config.action_dispatch.use_authenticated_cookie_encryption = true + config.action_dispatch.encrypted_cookie_cipher = "aes-256-gcm" + config.action_dispatch.authenticated_encrypted_cookie_salt = "salt" + + config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :encrypted, first_secret + cookies.rotate :encrypted, second_secret + end + RUBY + + require "#{app_path}/config/environment" + + encryptor = ActiveSupport::MessageEncryptor.new(app.key_generator.generate_key("salt", 32), cipher: "aes-256-gcm") + + get "/foo/write_raw_cookie_one" + get "/foo/read_encrypted" + assert_equal "encrypted cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body) + + get "/foo/write_raw_cookie_sha256" + get "/foo/read_encrypted" + assert_equal "encrypted cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body) + end end end diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb index 75afeec905..2d659ade8d 100644 --- a/railties/test/application/middleware/exceptions_test.rb +++ b/railties/test/application/middleware/exceptions_test.rb @@ -102,7 +102,7 @@ module ApplicationTests end end - test "routing to an nonexistent controller when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do + test "routing to a nonexistent controller when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do resources :articles diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb index 9def3a0ce7..818ad61c64 100644 --- a/railties/test/application/middleware/sendfile_test.rb +++ b/railties/test/application/middleware/sendfile_test.rb @@ -29,7 +29,7 @@ module ApplicationTests simple_controller get "/" - assert !last_response.headers["X-Sendfile"] + assert_not last_response.headers["X-Sendfile"] assert_equal File.read(__FILE__), last_response.body end diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index a17988235a..9182a63ab7 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -31,7 +31,7 @@ module ApplicationTests add_to_config "config.force_ssl = true" add_to_config "config.ssl_options = { secure_cookies: false }" require "#{app_path}/config/environment" - assert !app.config.session_options[:secure] + assert_not app.config.session_options[:secure] end test "session is not loaded if it's not used" do @@ -51,7 +51,7 @@ module ApplicationTests get "/" assert last_request.env["HTTP_COOKIE"] - assert !last_response.headers["Set-Cookie"] + assert_not last_response.headers["Set-Cookie"] end test "session is empty and isn't saved on unverified request when using :null_session protect method" do diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 0a5a524692..5efaf841d4 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -42,9 +42,11 @@ module ApplicationTests "ActionDispatch::Cookies", "ActionDispatch::Session::CookieStore", "ActionDispatch::Flash", + "ActionDispatch::ContentSecurityPolicy::Middleware", "Rack::Head", "Rack::ConditionalGet", - "Rack::ETag" + "Rack::ETag", + "Rack::TempfileReaper" ], middleware end @@ -248,7 +250,7 @@ module ApplicationTests test "can't change middleware after it's built" do boot! - assert_raise RuntimeError do + assert_raise frozen_error_class do app.config.middleware.use Rack::Config end end diff --git a/railties/test/application/per_request_digest_cache_test.rb b/railties/test/application/per_request_digest_cache_test.rb index e9bc91785c..10d3313f6e 100644 --- a/railties/test/application/per_request_digest_cache_test.rb +++ b/railties/test/application/per_request_digest_cache_test.rb @@ -59,7 +59,7 @@ class PerRequestDigestCacheTest < ActiveSupport::TestCase assert_equal 200, last_response.status values = ActionView::LookupContext::DetailsKey.digest_caches.first.values - assert_equal [ "8ba099b7749542fe765ff34a6824d548" ], values + assert_equal [ "effc8928d0b33535c8a21d24ec617161" ], values assert_equal %w(david dingus), last_response.body.split.map(&:strip) end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 391676aa31..0594236b1f 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -26,15 +26,15 @@ module ApplicationTests FileUtils.rm_rf("#{app_path}/config/database.yml") end - def db_create_and_drop(expected_database) + def db_create_and_drop(expected_database, environment_loaded: true) Dir.chdir(app_path) do output = rails("db:create") assert_match(/Created database/, output) assert File.exist?(expected_database) - assert_equal expected_database, ActiveRecord::Base.connection_config[:database] + assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded output = rails("db:drop") assert_match(/Dropped database/, output) - assert !File.exist?(expected_database) + assert_not File.exist?(expected_database) end end @@ -49,6 +49,22 @@ module ApplicationTests db_create_and_drop database_url_db_name end + test "db:create and db:drop respect environment setting" do + app_file "config/database.yml", <<-YAML + development: + database: <%= Rails.application.config.database %> + adapter: sqlite3 + YAML + + app_file "config/environments/development.rb", <<-RUBY + Rails.application.configure do + config.database = "db/development.sqlite3" + end + RUBY + + db_create_and_drop "db/development.sqlite3", environment_loaded: false + end + def with_database_existing Dir.chdir(app_path) do set_database_url @@ -82,13 +98,25 @@ module ApplicationTests end end - test "db:drop failure because database does not exist" do - Dir.chdir(app_path) do - output = rails("db:drop:_unsafe", "--trace") - assert_match(/does not exist/, output) + test "db:create works when schema cache exists and database does not exist" do + use_postgresql + + begin + rails %w(db:create db:migrate db:schema:cache:dump) + + rails "db:drop" + rails "db:create" + assert_equal 0, $?.exitstatus + ensure + rails "db:drop" rescue nil end end + test "db:drop failure because database does not exist" do + output = rails("db:drop:_unsafe", "--trace") + assert_match(/does not exist/, output) + end + test "db:drop failure because bad permissions" do with_database_existing do with_bad_permissions do @@ -100,13 +128,11 @@ module ApplicationTests end def db_migrate_and_status(expected_database) - Dir.chdir(app_path) do - rails "generate", "model", "book", "title:string" - rails "db:migrate" - output = rails("db:migrate:status") - assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output) - assert_match(/up\s+\d{14}\s+Create books/, output) - end + rails "generate", "model", "book", "title:string" + rails "db:migrate" + output = rails("db:migrate:status") + assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output) + assert_match(/up\s+\d{14}\s+Create books/, output) end test "db:migrate and db:migrate:status without database_url" do @@ -161,12 +187,11 @@ module ApplicationTests test "db:fixtures:load with namespaced fixture" do require "#{app_path}/config/environment" - Dir.chdir(app_path) do - rails "generate", "model", "admin::book", "title:string" - rails "db:migrate", "db:fixtures:load" - require "#{app_path}/app/models/admin/book" - assert_equal 2, Admin::Book.count - end + + rails "generate", "model", "admin::book", "title:string" + rails "db:migrate", "db:fixtures:load" + require "#{app_path}/app/models/admin/book" + assert_equal 2, Admin::Book.count end def db_structure_dump_and_load(expected_database) @@ -178,7 +203,7 @@ module ApplicationTests rails "environment", "db:drop", "db:structure:load" assert_match expected_database, ActiveRecord::Base.connection_config[:database] require "#{app_path}/app/models/book" - #if structure is not loaded correctly, exception would be raised + # if structure is not loaded correctly, exception would be raised assert_equal 0, Book.count end end @@ -194,6 +219,14 @@ module ApplicationTests db_structure_dump_and_load database_url_db_name end + test "db:structure:dump and db:structure:load set ar_internal_metadata" do + require "#{app_path}/config/environment" + db_structure_dump_and_load ActiveRecord::Base.configurations[Rails.env]["database"] + + assert_equal "test", rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip + assert_equal "development", rails("runner", "puts ActiveRecord::InternalMetadata[:environment]").strip + end + test "db:structure:dump does not dump schema information when no migrations are used" do # create table without migrations rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" @@ -205,56 +238,52 @@ module ApplicationTests end test "db:schema:load and db:structure:load do not purge the existing database" do - Dir.chdir(app_path) do - rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" + rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" - app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do - create_table(:comments) {} - end - RUBY + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 20140423102712) do + create_table(:comments) {} + end + RUBY - list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip } + list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip } - assert_equal '["posts"]', list_tables[] - rails "db:schema:load" - assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata"]', list_tables[] + assert_equal '["posts"]', list_tables[] + rails "db:schema:load" + assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata"]', list_tables[] - app_file "db/structure.sql", <<-SQL - CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)); - SQL + app_file "db/structure.sql", <<-SQL + CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)); + SQL - rails "db:structure:load" - assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata", "users"]', list_tables[] - end + rails "db:structure:load" + assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata", "users"]', list_tables[] end test "db:schema:load with inflections" do - Dir.chdir(app_path) do - app_file "config/initializers/inflection.rb", <<-RUBY - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular 'goose', 'geese' - end - RUBY - app_file "config/initializers/primary_key_table_name.rb", <<-RUBY - ActiveRecord::Base.primary_key_prefix_type = :table_name - RUBY - app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do - create_table("goose".pluralize) do |t| - t.string :name - end + app_file "config/initializers/inflection.rb", <<-RUBY + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular 'goose', 'geese' + end + RUBY + app_file "config/initializers/primary_key_table_name.rb", <<-RUBY + ActiveRecord::Base.primary_key_prefix_type = :table_name + RUBY + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 20140423102712) do + create_table("goose".pluralize) do |t| + t.string :name end - RUBY + end + RUBY - rails "db:schema:load" + rails "db:schema:load" - tables = rails("runner", "p ActiveRecord::Base.connection.tables").strip - assert_match(/"geese"/, tables) + tables = rails("runner", "p ActiveRecord::Base.connection.tables").strip + assert_match(/"geese"/, tables) - columns = rails("runner", "p ActiveRecord::Base.connection.columns('geese').map(&:name)").strip - assert_equal columns, '["gooseid", "name"]' - end + columns = rails("runner", "p ActiveRecord::Base.connection.columns('geese').map(&:name)").strip + assert_equal columns, '["gooseid", "name"]' end test "db:schema:load fails if schema.rb doesn't exist yet" do @@ -269,7 +298,7 @@ module ApplicationTests ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Base.establish_connection :test require "#{app_path}/app/models/book" - #if structure is not loaded correctly, exception would be raised + # if structure is not loaded correctly, exception would be raised assert_equal 0, Book.count assert_match ActiveRecord::Base.configurations["test"]["database"], ActiveRecord::Base.connection_config[:database] @@ -300,15 +329,52 @@ module ApplicationTests puts ActiveRecord::Base.connection_config[:database] RUBY - Dir.chdir(app_path) do - database_path = rails("db:setup") - assert_equal "development.sqlite3", File.basename(database_path.strip) - end + database_path = rails("db:setup") + assert_equal "development.sqlite3", File.basename(database_path.strip) ensure ENV["RAILS_ENV"] = @old_rails_env ENV["RACK_ENV"] = @old_rack_env end end + + test "db:setup sets ar_internal_metadata" do + app_file "db/schema.rb", "" + rails "db:setup" + + test_environment = lambda { rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip } + development_environment = lambda { rails("runner", "puts ActiveRecord::InternalMetadata[:environment]").strip } + + assert_equal "test", test_environment.call + assert_equal "development", development_environment.call + + app_file "db/structure.sql", "" + app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY + Rails.application.config.active_record.schema_format = :sql + RUBY + + rails "db:setup" + + assert_equal "test", test_environment.call + assert_equal "development", development_environment.call + end + + test "db:test:prepare sets test ar_internal_metadata" do + app_file "db/schema.rb", "" + rails "db:test:prepare" + + test_environment = lambda { rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip } + + assert_equal "test", test_environment.call + + app_file "db/structure.sql", "" + app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY + Rails.application.config.active_record.schema_format = :sql + RUBY + + rails "db:test:prepare" + + assert_equal "test", test_environment.call + end end end end diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index b0d51eb22e..bf5b07afbd 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -15,69 +15,166 @@ module ApplicationTests end test "running migrations with given scope" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "model", "user", "username:string", "password:string" - app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION - class AMigration < ActiveRecord::Migration::Current - end - MIGRATION + app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION + class AMigration < ActiveRecord::Migration::Current + end + MIGRATION - output = rails("db:migrate", "SCOPE=bukkits") - assert_no_match(/create_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/add_column\(:users, :email, :string\)/, output) + output = rails("db:migrate", "SCOPE=bukkits") + assert_no_match(/create_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AMigration: migrated/, output) + assert_match(/AMigration: migrated/, output) - output = rails("db:migrate", "SCOPE=bukkits", "VERSION=0") - assert_no_match(/drop_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/remove_column\(:users, :email\)/, output) + # run all the migrations to test scope for down + output = rails("db:migrate") + assert_match(/CreateUsers: migrated/, output) - assert_match(/AMigration: reverted/, output) - end + output = rails("db:migrate", "SCOPE=bukkits", "VERSION=0") + assert_no_match(/drop_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/remove_column\(:users, :email\)/, output) + + assert_match(/AMigration: reverted/, output) + + output = rails("db:migrate", "VERSION=0") + + assert_match(/CreateUsers: reverted/, output) + end + + test "version outputs current version" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:version") + assert_match(/Current version: 1/, output) + end + + test "migrate with specified VERSION in different formats" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/03_three_migration.rb", <<-MIGRATION + class ThreeMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + assert_match(/up\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=01_one_migration.rb" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) + assert_match(/down\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=3" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + assert_match(/up\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=001" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) + assert_match(/down\s+003\s+Three migration/, output) end test "migration with empty version" do - Dir.chdir(app_path) do - output = rails("db:migrate", "VERSION=", allow_failure: true) - assert_match(/Empty VERSION provided/, output) + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION - output = rails("db:migrate:redo", "VERSION=", allow_failure: true) - assert_match(/Empty VERSION provided/, output) + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION - output = rails("db:migrate:up", "VERSION=", allow_failure: true) - assert_match(/VERSION is required/, output) + rails("db:migrate", "VERSION=") - output = rails("db:migrate:up", allow_failure: true) - assert_match(/VERSION is required/, output) + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) - output = rails("db:migrate:down", "VERSION=", allow_failure: true) - assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + output = rails("db:migrate:redo", "VERSION=", allow_failure: true) + assert_match(/Empty VERSION provided/, output) - output = rails("db:migrate:down", allow_failure: true) - assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) - end + output = rails("db:migrate:up", "VERSION=", allow_failure: true) + assert_match(/VERSION is required/, output) + + output = rails("db:migrate:up", allow_failure: true) + assert_match(/VERSION is required/, output) + + output = rails("db:migrate:down", "VERSION=", allow_failure: true) + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + + output = rails("db:migrate:down", allow_failure: true) + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) end - test "model and migration generator with change syntax" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" + test "migration with 0 version" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION - output = rails("db:migrate") - assert_match(/create_table\(:users\)/, output) - assert_match(/CreateUsers: migrated/, output) - assert_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AddEmailToUsers: migrated/, output) - - output = rails("db:rollback", "STEP=2") - assert_match(/drop_table\(:users\)/, output) - assert_match(/CreateUsers: reverted/, output) - assert_match(/remove_column\(:users, :email, :string\)/, output) - assert_match(/AddEmailToUsers: reverted/, output) - end + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + + rails "db:migrate", "VERSION=0" + + output = rails("db:migrate:status") + assert_match(/down\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) + end + + test "model and migration generator with change syntax" do + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + + output = rails("db:migrate") + assert_match(/create_table\(:users\)/, output) + assert_match(/CreateUsers: migrated/, output) + assert_match(/add_column\(:users, :email, :string\)/, output) + assert_match(/AddEmailToUsers: migrated/, output) + + output = rails("db:rollback", "STEP=2") + assert_match(/drop_table\(:users\)/, output) + assert_match(/CreateUsers: reverted/, output) + assert_match(/remove_column\(:users, :email, :string\)/, output) + assert_match(/AddEmailToUsers: reverted/, output) end test "migration status when schema migrations table is not present" do @@ -86,93 +183,85 @@ module ApplicationTests end test "migration status" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - rails "db:rollback", "STEP=1" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=1" + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) - end + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) end test "migration status without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - rails "db:rollback", "STEP=1" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=1" + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) - end + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) end test "migration status after rollback and redo" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - rails "db:rollback", "STEP=2" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") - assert_match(/down\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) - rails "db:migrate:redo" - output = rails("db:migrate:status") + rails "db:migrate:redo" + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) - end + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) end test "migration status after rollback and forward" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - rails "db:rollback", "STEP=2" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") - assert_match(/down\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) - rails "db:forward", "STEP=2" - output = rails("db:migrate:status") + rails "db:forward", "STEP=2" + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) - end + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) end test "raise error on any move when current migration does not exist" do @@ -206,53 +295,117 @@ module ApplicationTests end end + test "raise error on any move when target migration does not exist" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + + output = rails("db:migrate", "VERSION=3", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output) + assert_match(/No migration with version number 3/, output) + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + end + + test "raise error on any move when VERSION has invalid format" do + output = rails("db:migrate", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=0.1.11", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1.1.11", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION='0 '", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1.", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1_", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1_name", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:redo", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:up", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:down", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + end + test "migration status after rollback and redo without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - rails "db:rollback", "STEP=2" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") - assert_match(/down\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) + assert_match(/down\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) - rails "db:migrate:redo" - output = rails("db:migrate:status") + rails "db:migrate:redo" + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) - end + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) end test "running migrations with not timestamp head migration files" do - Dir.chdir(app_path) do - - app_file "db/migrate/1_one_migration.rb", <<-MIGRATION - class OneMigration < ActiveRecord::Migration::Current - end - MIGRATION + app_file "db/migrate/1_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION - app_file "db/migrate/02_two_migration.rb", <<-MIGRATION - class TwoMigration < ActiveRecord::Migration::Current - end - MIGRATION + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION - rails "db:migrate" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+001\s+One migration/, output) - assert_match(/up\s+002\s+Two migration/, output) - end + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) end test "schema generation when dump_schema_after_migration is set" do diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb new file mode 100644 index 0000000000..07d96fcb56 --- /dev/null +++ b/railties/test/application/rake/multi_dbs_test.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" + +module ApplicationTests + module RakeTests + class RakeMultiDbsTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app(multi_db: true) + FileUtils.rm_rf("#{app_path}/config/environments") + end + + def teardown + teardown_app + end + + def db_create_and_drop(namespace, expected_database, environment_loaded: true) + Dir.chdir(app_path) do + output = rails("db:create") + assert_match(/Created database/, output) + assert_match_namespace(namespace, output) + assert File.exist?(expected_database) + + output = rails("db:drop") + assert_match(/Dropped database/, output) + assert_match_namespace(namespace, output) + assert_not File.exist?(expected_database) + end + end + + def db_create_and_drop_namespace(namespace, expected_database, environment_loaded: true) + Dir.chdir(app_path) do + output = rails("db:create:#{namespace}") + assert_match(/Created database/, output) + assert_match_namespace(namespace, output) + assert File.exist?(expected_database) + + output = rails("db:drop:#{namespace}") + assert_match(/Dropped database/, output) + assert_match_namespace(namespace, output) + assert_not File.exist?(expected_database) + end + end + + def assert_match_namespace(namespace, output) + if namespace == "primary" + assert_match(/#{Rails.env}.sqlite3/, output) + else + assert_match(/#{Rails.env}_#{namespace}.sqlite3/, output) + end + end + + def db_migrate_and_schema_dump_and_load(namespace, expected_database, format) + Dir.chdir(app_path) do + rails "generate", "model", "book", "title:string" + rails "generate", "model", "dog", "name:string" + write_models_for_animals + rails "db:migrate", "db:#{format}:dump" + + if format == "schema" + schema_dump = File.read("db/#{format}.rb") + schema_dump_animals = File.read("db/animals_#{format}.rb") + assert_match(/create_table \"books\"/, schema_dump) + assert_match(/create_table \"dogs\"/, schema_dump_animals) + else + schema_dump = File.read("db/#{format}.sql") + schema_dump_animals = File.read("db/animals_#{format}.sql") + assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"books\"/, schema_dump) + assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"dogs\"/, schema_dump_animals) + end + + rails "db:#{format}:load" + + ar_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip } + animals_tables = lambda { rails("runner", "p AnimalsBase.connection.tables").strip } + + assert_equal '["schema_migrations", "ar_internal_metadata", "books"]', ar_tables[] + assert_equal '["schema_migrations", "ar_internal_metadata", "dogs"]', animals_tables[] + end + end + + def db_migrate_namespaced(namespace, expected_database) + Dir.chdir(app_path) do + rails "generate", "model", "book", "title:string" + rails "generate", "model", "dog", "name:string" + write_models_for_animals + output = rails("db:migrate:#{namespace}") + if namespace == "primary" + assert_match(/CreateBooks: migrated/, output) + else + assert_match(/CreateDogs: migrated/, output) + end + end + end + + def write_models_for_animals + # make a directory for the animals migration + FileUtils.mkdir_p("#{app_path}/db/animals_migrate") + # move the dogs migration if it unless it already lives there + FileUtils.mv(Dir.glob("#{app_path}/db/migrate/**/*dogs.rb").first, "db/animals_migrate/") unless Dir.glob("#{app_path}/db/animals_migrate/**/*dogs.rb").first + # delete the dogs migration if it's still present in the + # migrate folder. This is necessary because sometimes + # the code isn't fast enough and an extra migration gets made + FileUtils.rm(Dir.glob("#{app_path}/db/migrate/**/*dogs.rb").first) if Dir.glob("#{app_path}/db/migrate/**/*dogs.rb").first + + # change the base of the dog model + app_path("/app/models/dog.rb") do |file_name| + file = File.read("#{app_path}/app/models/dog.rb") + file.sub!(/ApplicationRecord/, "AnimalsBase") + File.write(file_name, file) + end + + # create the base model for dog to inherit from + File.open("#{app_path}/app/models/animals_base.rb", "w") do |file| + file.write(<<-EOS +class AnimalsBase < ActiveRecord::Base + self.abstract_class = true + + establish_connection :animals +end +EOS +) + end + end + + test "db:create and db:drop works on all databases for env" do + require "#{app_path}/config/environment" + ActiveRecord::Base.configurations[Rails.env].each do |namespace, config| + db_create_and_drop namespace, config["database"] + end + end + + test "db:create:namespace and db:drop:namespace works on specified databases" do + require "#{app_path}/config/environment" + ActiveRecord::Base.configurations[Rails.env].each do |namespace, config| + db_create_and_drop_namespace namespace, config["database"] + end + end + + test "db:migrate and db:schema:dump and db:schema:load works on all databases" do + require "#{app_path}/config/environment" + ActiveRecord::Base.configurations[Rails.env].each do |namespace, config| + db_migrate_and_schema_dump_and_load namespace, config["database"], "schema" + end + end + + test "db:migrate and db:structure:dump and db:structure:load works on all databases" do + require "#{app_path}/config/environment" + ActiveRecord::Base.configurations[Rails.env].each do |namespace, config| + db_migrate_and_schema_dump_and_load namespace, config["database"], "structure" + end + end + + test "db:migrate:namespace works" do + require "#{app_path}/config/environment" + ActiveRecord::Base.configurations[Rails.env].each do |namespace, config| + db_migrate_namespaced namespace, config["database"] + end + end + end + end +end diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb index 8e9fe9b6b4..d73e5cdfa3 100644 --- a/railties/test/application/rake/notes_test.rb +++ b/railties/test/application/rake/notes_test.rb @@ -101,7 +101,7 @@ module ApplicationTests task :notes_custom do tags = 'TODO|FIXME' opts = { dirs: %w(lib test), tag: true } - SourceAnnotationExtractor.enumerate(tags, opts) + Rails::SourceAnnotationExtractor.enumerate(tags, opts) end EOS diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 76bc0ce1d7..1522a2bbc5 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -2,7 +2,6 @@ require "isolation/abstract_unit" require "env_helpers" -require "active_support/core_ext/string/strip" module ApplicationTests class RakeTest < ActiveSupport::TestCase @@ -40,9 +39,9 @@ module ApplicationTests with_rails_env "test" do rails "generate", "model", "product", "name:string" rails "db:create", "db:migrate" - output = Dir.chdir(app_path) { rails("db:test:prepare", "test") } + output = rails("db:test:prepare", "test") - refute_match(/ActiveRecord::ProtectedEnvironmentError/, output) + assert_no_match(/ActiveRecord::ProtectedEnvironmentError/, output) end end @@ -101,6 +100,7 @@ module ApplicationTests add_to_config <<-RUBY rake_tasks do task do_nothing: :environment do + puts 'There is nothing' end end RUBY @@ -113,10 +113,8 @@ module ApplicationTests raise 'should not be pre-required for rake even eager_load=true' RUBY - Dir.chdir(app_path) do - assert system("bin/rails do_nothing RAILS_ENV=production"), - "should not be pre-required for rake even eager_load=true" - end + output = rails("do_nothing", "RAILS_ENV=production") + assert_match "There is nothing", output end def test_code_statistics_sanity @@ -124,138 +122,6 @@ module ApplicationTests rails("stats") end - def test_rails_routes_calls_the_route_inspector - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - get '/cart', to: 'cart#show' - end - RUBY - - output = rails("routes") - assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - cart GET /cart(.:format) cart#show - MESSAGE - end - - def test_singular_resource_output_in_rake_routes - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - resource :post - end - RUBY - - expected_output = [" Prefix Verb URI Pattern Controller#Action", - " new_post GET /post/new(.:format) posts#new", - "edit_post GET /post/edit(.:format) posts#edit", - " post GET /post(.:format) posts#show", - " PATCH /post(.:format) posts#update", - " PUT /post(.:format) posts#update", - " DELETE /post(.:format) posts#destroy", - " POST /post(.:format) posts#create\n"].join("\n") - - output = rails("routes", "-c", "PostController") - assert_equal expected_output, output - end - - def test_rails_routes_with_global_search_key - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - get '/cart', to: 'cart#show' - post '/cart', to: 'cart#create' - get '/basketballs', to: 'basketball#index' - end - RUBY - - output = rails("routes", "-g", "show", allow_failure: true) - assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - cart GET /cart(.:format) cart#show - MESSAGE - - output = rails("routes", "-g", "POST") - assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - POST /cart(.:format) cart#create - MESSAGE - - output = rails("routes", "-g", "basketballs") - assert_equal " Prefix Verb URI Pattern Controller#Action\n" \ - "basketballs GET /basketballs(.:format) basketball#index\n", output - end - - def test_rails_routes_with_controller_search_key - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - get '/cart', to: 'cart#show' - get '/basketball', to: 'basketball#index' - end - RUBY - - output = rails("routes", "-c", "cart") - assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output - - output = rails("routes", "-c", "Cart") - assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output - - output = rails("routes", "-c", "CartController") - assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output - end - - def test_rails_routes_with_namespaced_controller_search_key - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - namespace :admin do - resource :post - end - end - RUBY - expected_output = [" Prefix Verb URI Pattern Controller#Action", - " new_admin_post GET /admin/post/new(.:format) admin/posts#new", - "edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit", - " admin_post GET /admin/post(.:format) admin/posts#show", - " PATCH /admin/post(.:format) admin/posts#update", - " PUT /admin/post(.:format) admin/posts#update", - " DELETE /admin/post(.:format) admin/posts#destroy", - " POST /admin/post(.:format) admin/posts#create\n"].join("\n") - - output = rails("routes", "-c", "Admin::PostController") - assert_equal expected_output, output - - output = rails("routes", "-c", "PostController") - assert_equal expected_output, output - end - - def test_rails_routes_displays_message_when_no_routes_are_defined - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - end - RUBY - - assert_equal <<-MESSAGE.strip_heredoc, rails("routes") - You don't have any routes defined! - - Please add some routes in config/routes.rb. - - For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html. - MESSAGE - end - - def test_rake_routes_with_rake_options - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - get '/cart', to: 'cart#show' - end - RUBY - - output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile routes` } - - assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - cart GET /cart(.:format) cart#show - MESSAGE - end - def test_logger_is_flushed_when_exiting_production_rake_tasks add_to_config <<-RUBY rake_tasks do @@ -294,9 +160,8 @@ module ApplicationTests def test_scaffold_tests_pass_by_default rails "generate", "scaffold", "user", "username:string", "password:string" - output = Dir.chdir(app_path) do - `RAILS_ENV=test bin/rails db:migrate test` - end + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) @@ -313,9 +178,8 @@ module ApplicationTests RUBY rails "generate", "scaffold", "user", "username:string", "password:string" - output = Dir.chdir(app_path) do - `RAILS_ENV=test bin/rails db:migrate test` - end + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) @@ -325,9 +189,8 @@ module ApplicationTests rails "generate", "model", "Product" rails "generate", "model", "Cart" rails "generate", "scaffold", "LineItems", "product:references", "cart:belongs_to" - output = Dir.chdir(app_path) do - `RAILS_ENV=test bin/rails db:migrate test` - end + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) @@ -337,9 +200,7 @@ module ApplicationTests add_to_config "config.active_record.schema_format = :sql" rails "generate", "scaffold", "user", "username:string" rails "db:migrate" - output = with_rails_env("test") do - rails "db:test:prepare", "--trace" - end + output = rails("db:test:prepare", "--trace") assert_match(/Execute db:test:load_structure/, output) end @@ -368,18 +229,16 @@ module ApplicationTests def test_rake_clear_schema_cache rails "db:schema:cache:dump", "db:schema:cache:clear" - assert !File.exist?(File.join(app_path, "db", "schema_cache.yml")) + assert_not File.exist?(File.join(app_path, "db", "schema_cache.yml")) end def test_copy_templates - Dir.chdir(app_path) do - rails "app:templates:copy" - %w(controller mailer scaffold).each do |dir| - assert File.exist?(File.join(app_path, "lib", "templates", "erb", dir)) - end - %w(controller helper scaffold_controller assets).each do |dir| - assert File.exist?(File.join(app_path, "lib", "templates", "rails", dir)) - end + rails "app:templates:copy" + %w(controller mailer scaffold).each do |dir| + assert File.exist?(File.join(app_path, "lib", "templates", "erb", dir)) + end + %w(controller helper scaffold_controller assets).each do |dir| + assert File.exist?(File.join(app_path, "lib", "templates", "rails", dir)) end end diff --git a/railties/test/application/rendering_test.rb b/railties/test/application/rendering_test.rb index 3724886c54..ab1591f388 100644 --- a/railties/test/application/rendering_test.rb +++ b/railties/test/application/rendering_test.rb @@ -4,7 +4,7 @@ require "isolation/abstract_unit" require "rack/test" module ApplicationTests - class RoutingTest < ActiveSupport::TestCase + class RenderingTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation include Rack::Test::Methods diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb index 64c46c4b45..8f5f48c281 100644 --- a/railties/test/application/runner_test.rb +++ b/railties/test/application/runner_test.rb @@ -107,13 +107,13 @@ module ApplicationTests def test_runner_detects_syntax_errors output = rails("runner", "puts 'hello world", allow_failure: true) - assert_not $?.success? + assert_not_predicate $?, :success? assert_match "unterminated string meets end of file", output end def test_runner_detects_bad_script_name output = rails("runner", "iuiqwiourowe", allow_failure: true) - assert_not $?.success? + assert_not_predicate $?, :success? assert_match "undefined local variable or method `iuiqwiourowe' for", output end @@ -128,5 +128,17 @@ module ApplicationTests assert_match "production", rails("runner", "puts Rails.env") end end + + def test_can_call_same_name_class_as_defined_in_thor + app_file "app/models/task.rb", <<-MODEL + class Task + def self.count + 42 + end + end + MODEL + + assert_match "42", rails("runner", "puts Task.count") + end end end diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb index 6db9a3b9e8..f3a7e00a4d 100644 --- a/railties/test/application/server_test.rb +++ b/railties/test/application/server_test.rb @@ -29,12 +29,17 @@ module ApplicationTests server.app log = File.read(Rails.application.config.paths["log"].first) - assert_match(/DEPRECATION WARNING: Use `Rails::Application` subclass to start the server is deprecated/, log) + assert_match(/DEPRECATION WARNING: Using `Rails::Application` subclass to start the server is deprecated/, log) end test "restart rails server with custom pid file path" do skip "PTY unavailable" unless available_pty? + File.open("#{app_path}/config/boot.rb", "w") do |f| + f.puts "ENV['BUNDLE_GEMFILE'] = '#{Bundler.default_gemfile.to_s}'" + f.puts "require 'bundler/setup'" + end + master, slave = PTY.open pid = nil @@ -42,7 +47,7 @@ module ApplicationTests pid = Process.spawn("#{app_path}/bin/rails server -P tmp/dummy.pid", in: slave, out: slave, err: slave) assert_output("Listening", master) - Dir.chdir(app_path) { system("bin/rails restart") } + rails("restart") assert_output("Restarting", master) assert_output("Inherited", master) diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index e92a0466dd..8e5ccf94cc 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "isolation/abstract_unit" -require "active_support/core_ext/string/strip" require "env_helpers" module ApplicationTests @@ -502,10 +501,10 @@ module ApplicationTests end def test_output_inline_by_default - create_test_file :models, "post", pass: false + create_test_file :models, "post", pass: false, print: false output = run_test_command("test/models/post_test.rb") - expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/models/post_test.rb:6\]:\nwups!\n\nbin/rails test test/models/post_test.rb:4\n\n\n\n} + expect = %r{Running:\n\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/models/post_test.rb:6\]:\nwups!\n\nbin/rails test test/models/post_test.rb:4\n\n\n\n} assert_match expect, output end @@ -523,6 +522,28 @@ module ApplicationTests capture(:stderr) { run_test_command("test/models/post_test.rb --fail-fast", stderr: true) }) end + def test_run_in_parallel_with_processes + file_name = create_parallel_processes_test_file + + output = run_test_command(file_name) + + assert_match %r{Finished in.*\n2 runs, 2 assertions}, output + end + + def test_run_in_parallel_with_threads + app_path("/test/test_helper.rb") do |file_name| + file = File.read(file_name) + file.sub!(/parallelize\(([^\)]*)\)/, "parallelize(\\1, with: :threads)") + File.write(file_name, file) + end + + file_name = create_parallel_threads_test_file + + output = run_test_command(file_name) + + assert_match %r{Finished in.*\n2 runs, 2 assertions}, output + end + def test_raise_error_when_specified_file_does_not_exist error = capture(:stderr) { run_test_command("test/not_exists.rb", stderr: true) } assert_match(%r{cannot load such file.+test/not_exists\.rb}, error) @@ -569,6 +590,40 @@ module ApplicationTests assert_match "AccountTest#test_truth", output, "passing TEST= should run selected test" end + def test_running_with_ruby_gets_test_env_by_default + # Subshells inherit `ENV`, so we need to ensure `RAILS_ENV` is set to + # nil before we run the tests in the test app. + re, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], nil + + file = create_test_for_env("test") + results = Dir.chdir(app_path) { + `ruby -Ilib:test #{file}`.each_line.map { |line| JSON.parse line } + } + assert_equal 1, results.length + failures = results.first["failures"] + flunk(failures.first) unless failures.empty? + + ensure + ENV["RAILS_ENV"] = re + end + + def test_running_with_ruby_can_set_env_via_cmdline + # Subshells inherit `ENV`, so we need to ensure `RAILS_ENV` is set to + # nil before we run the tests in the test app. + re, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], nil + + file = create_test_for_env("development") + results = Dir.chdir(app_path) { + `RAILS_ENV=development ruby -Ilib:test #{file}`.each_line.map { |line| JSON.parse line } + } + assert_equal 1, results.length + failures = results.first["failures"] + flunk(failures.first) unless failures.empty? + + ensure + ENV["RAILS_ENV"] = re + end + def test_rake_passes_multiple_TESTOPTS_to_minitest create_test_file :models, "account" output = Dir.chdir(app_path) { `bin/rake test TESTOPTS='-v --seed=1234'` } @@ -684,7 +739,7 @@ module ApplicationTests def create_model_with_fixture rails "generate", "model", "user", "name:string" - app_file "test/fixtures/users.yml", <<-YAML.strip_heredoc + app_file "test/fixtures/users.yml", <<~YAML vampire: id: 1 name: Koyomi Araragi @@ -727,19 +782,109 @@ module ApplicationTests app_file "db/schema.rb", "" end - def create_test_file(path = :unit, name = "test", pass: true) + def create_test_for_env(env) + app_file "test/models/environment_test.rb", <<-RUBY + require 'test_helper' + class JSONReporter < Minitest::AbstractReporter + def record(result) + puts JSON.dump(klass: result.class.name, + name: result.name, + failures: result.failures, + assertions: result.assertions, + time: result.time) + end + end + + def Minitest.plugin_json_reporter_init(opts) + Minitest.reporter.reporters.clear + Minitest.reporter.reporters << JSONReporter.new + end + + Minitest.extensions << "rails" + Minitest.extensions << "json_reporter" + + # Minitest uses RubyGems to find plugins, and since RubyGems + # doesn't know about the Rails installation we're pointing at, + # Minitest won't require the Rails minitest plugin when we run + # these integration tests. So we have to manually require the + # Minitest plugin here. + require 'minitest/rails_plugin' + + class EnvironmentTest < ActiveSupport::TestCase + def test_environment + test_db = ActiveRecord::Base.configurations[#{env.dump}]["database"] + db_file = ActiveRecord::Base.connection_config[:database] + assert_match(test_db, db_file) + assert_equal #{env.dump}, ENV["RAILS_ENV"] + end + end + RUBY + end + + def create_test_file(path = :unit, name = "test", pass: true, print: true) app_file "test/#{path}/#{name}_test.rb", <<-RUBY require 'test_helper' class #{name.camelize}Test < ActiveSupport::TestCase def test_truth - puts "#{name.camelize}Test" + puts "#{name.camelize}Test" if #{print} assert #{pass}, 'wups!' end end RUBY end + def create_parallel_processes_test_file + app_file "test/models/parallel_test.rb", <<-RUBY + require 'test_helper' + + class ParallelTest < ActiveSupport::TestCase + RD1, WR1 = IO.pipe + RD2, WR2 = IO.pipe + + test "one" do + WR1.close + assert_equal "x", RD1.read(1) # blocks until two runs + + RD2.close + WR2.write "y" # Allow two to run + WR2.close + end + + test "two" do + RD1.close + WR1.write "x" # Allow one to run + WR1.close + + WR2.close + assert_equal "y", RD2.read(1) # blocks until one runs + end + end + RUBY + end + + def create_parallel_threads_test_file + app_file "test/models/parallel_test.rb", <<-RUBY + require 'test_helper' + + class ParallelTest < ActiveSupport::TestCase + Q1 = Queue.new + Q2 = Queue.new + test "one" do + assert_equal "x", Q1.pop # blocks until two runs + + Q2 << "y" + end + + test "two" do + Q1 << "x" + + assert_equal "y", Q2.pop # blocks until one runs + end + end + RUBY + end + def create_env_test app_file "test/unit/env_test.rb", <<-RUBY require 'test_helper' diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb index 0a51e98656..fb43bebfbe 100644 --- a/railties/test/application/test_test.rb +++ b/railties/test/application/test_test.rb @@ -7,10 +7,15 @@ module ApplicationTests include ActiveSupport::Testing::Isolation def setup + @old = ENV["PARALLEL_WORKERS"] + ENV["PARALLEL_WORKERS"] = "0" + build_app end def teardown + ENV["PARALLEL_WORKERS"] = @old + teardown_app end diff --git a/railties/test/command/spellchecker_test.rb b/railties/test/command/spellchecker_test.rb new file mode 100644 index 0000000000..e6a7a3957c --- /dev/null +++ b/railties/test/command/spellchecker_test.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "rails/command/spellchecker" + +class Rails::Command::SpellcheckerTest < ActiveSupport::TestCase + test "suggests a word correction from dictionary" do + assert_equal "thin", Rails::Command::Spellchecker.suggest("tin", from: %w(puma thin cgi)) + end +end diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb index 7eb26c355c..b7cdb8229e 100644 --- a/railties/test/commands/console_test.rb +++ b/railties/test/commands/console_test.rb @@ -20,30 +20,30 @@ class Rails::ConsoleTest < ActiveSupport::TestCase def test_sandbox_option console = Rails::Console.new(app, parse_arguments(["--sandbox"])) - assert console.sandbox? + assert_predicate console, :sandbox? end def test_short_version_of_sandbox_option console = Rails::Console.new(app, parse_arguments(["-s"])) - assert console.sandbox? + assert_predicate console, :sandbox? end def test_no_options console = Rails::Console.new(app, parse_arguments([])) - assert !console.sandbox? + assert_not_predicate console, :sandbox? end def test_start start - assert app.console.started? + assert_predicate app.console, :started? assert_match(/Loading \w+ environment \(Rails/, output) end def test_start_with_sandbox start ["--sandbox"] - assert app.console.started? + assert_predicate app.console, :started? assert app.sandbox assert_match(/Loading \w+ environment in sandbox \(Rails/, output) end @@ -172,21 +172,8 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end def parse_arguments(args) - Rails::Command::ConsoleCommand.class_eval do - alias_method :old_perform, :perform - define_method(:perform) do - extract_environment_option_from_argument - - options - end - end - - Rails::Command.invoke(:console, args) - ensure - Rails::Command::ConsoleCommand.class_eval do - undef_method :perform - alias_method :perform, :old_perform - undef_method :old_perform - end + command = Rails::Command::ConsoleCommand.new([], args) + command.send(:extract_environment_option_from_argument) + command.options end end diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb index 743fb5f788..663ee73bcd 100644 --- a/railties/test/commands/credentials_test.rb +++ b/railties/test/commands/credentials_test.rb @@ -13,7 +13,10 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase teardown { teardown_app } test "edit without editor gives hint" do - assert_match "No $EDITOR to open credentials in", run_edit_command(editor: "") + run_edit_command(editor: "").tap do |output| + assert_match "No $EDITOR to open file in", output + assert_match "bin/rails credentials:edit", output + end end test "edit credentials" do @@ -23,10 +26,6 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase end end - test "show credentials" do - assert_match(/access_key_id: 123/, run_show_command) - end - test "edit command does not add master key to gitignore when already exist" do run_edit_command @@ -36,6 +35,44 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase end end + test "edit command does not overwrite by default if credentials already exists" do + run_edit_command(editor: "eval echo api_key: abc >") + assert_match(/api_key: abc/, run_show_command) + + run_edit_command + assert_match(/api_key: abc/, run_show_command) + end + + test "edit command does not add master key when `RAILS_MASTER_KEY` env specified" do + Dir.chdir(app_path) do + key = IO.binread("config/master.key").strip + FileUtils.rm("config/master.key") + + switch_env("RAILS_MASTER_KEY", key) do + run_edit_command + assert_not File.exist?("config/master.key") + end + end + 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 + remove_file "config/master.key" + add_to_config "config.require_master_key = true" + + assert_match(/Missing encryption key to decrypt file with/, run_show_command(allow_failure: true)) + end + + test "show command does not raise error when require_master_key is false and master key does not exist" do + remove_file "config/master.key" + add_to_config "config.require_master_key = false" + + assert_match(/Missing master key to decrypt credentials/, run_show_command) + end + private def run_edit_command(editor: "cat") switch_env("EDITOR", editor) do @@ -43,7 +80,7 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase end end - def run_show_command - rails "credentials:show" + def run_show_command(**options) + rails "credentials:show", **options end end diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb index 6ad96b28c7..0aea21051a 100644 --- a/railties/test/commands/dbconsole_test.rb +++ b/railties/test/commands/dbconsole_test.rb @@ -123,31 +123,31 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_mysql start(adapter: "mysql2", database: "db") - assert !aborted + assert_not aborted assert_equal [%w[mysql mysql5], "db"], dbconsole.find_cmd_and_exec_args end def test_mysql_full start(adapter: "mysql2", database: "db", host: "locahost", port: 1234, socket: "socket", username: "user", password: "qwerty", encoding: "UTF-8") - assert !aborted + assert_not aborted assert_equal [%w[mysql mysql5], "--host=locahost", "--port=1234", "--socket=socket", "--user=user", "--default-character-set=UTF-8", "-p", "db"], dbconsole.find_cmd_and_exec_args end def test_mysql_include_password start({ adapter: "mysql2", database: "db", username: "user", password: "qwerty" }, ["-p"]) - assert !aborted + assert_not aborted assert_equal [%w[mysql mysql5], "--user=user", "--password=qwerty", "db"], dbconsole.find_cmd_and_exec_args end def test_postgresql start(adapter: "postgresql", database: "db") - assert !aborted + assert_not aborted assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args end def test_postgresql_full start(adapter: "postgresql", database: "db", username: "user", password: "q1w2e3", host: "host", port: 5432) - assert !aborted + assert_not aborted assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args assert_equal "user", ENV["PGUSER"] assert_equal "host", ENV["PGHOST"] @@ -157,7 +157,7 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_postgresql_include_password start({ adapter: "postgresql", database: "db", username: "user", password: "q1w2e3" }, ["-p"]) - assert !aborted + assert_not aborted assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args assert_equal "user", ENV["PGUSER"] assert_equal "q1w2e3", ENV["PGPASSWORD"] @@ -165,13 +165,13 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_sqlite3 start(adapter: "sqlite3", database: "db.sqlite3") - assert !aborted + assert_not aborted assert_equal ["sqlite3", Rails.root.join("db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args end def test_sqlite3_mode start({ adapter: "sqlite3", database: "db.sqlite3" }, ["--mode", "html"]) - assert !aborted + assert_not aborted assert_equal ["sqlite3", "-html", Rails.root.join("db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args end @@ -182,27 +182,27 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_sqlite3_db_absolute_path start(adapter: "sqlite3", database: "/tmp/db.sqlite3") - assert !aborted + assert_not aborted assert_equal ["sqlite3", "/tmp/db.sqlite3"], dbconsole.find_cmd_and_exec_args end def test_sqlite3_db_without_defined_rails_root Rails.stub(:respond_to?, false) do start(adapter: "sqlite3", database: "config/db.sqlite3") - assert !aborted + assert_not aborted assert_equal ["sqlite3", Rails.root.join("../config/db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args end end def test_oracle start(adapter: "oracle", database: "db", username: "user", password: "secret") - assert !aborted + assert_not aborted assert_equal ["sqlplus", "user@db"], dbconsole.find_cmd_and_exec_args end def test_oracle_include_password start({ adapter: "oracle", database: "db", username: "user", password: "secret" }, ["-p"]) - assert !aborted + assert_not aborted assert_equal ["sqlplus", "user/secret@db"], dbconsole.find_cmd_and_exec_args end diff --git a/railties/test/commands/encrypted_test.rb b/railties/test/commands/encrypted_test.rb new file mode 100644 index 0000000000..9fc73d5f18 --- /dev/null +++ b/railties/test/commands/encrypted_test.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "env_helpers" +require "rails/command" +require "rails/commands/encrypted/encrypted_command" + +class Rails::Command::EncryptedCommandTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation, EnvHelpers + + setup :build_app + teardown :teardown_app + + test "edit without editor gives hint" do + run_edit_command("config/tokens.yml.enc", editor: "").tap do |output| + assert_match "No $EDITOR to open file in", output + assert_match "bin/rails encrypted:edit", output + end + end + + test "edit encrypted file" do + # Run twice to ensure file can be reread after first edit pass. + 2.times do + assert_match(/access_key_id: 123/, run_edit_command("config/tokens.yml.enc")) + end + end + + test "edit command does not add master key to gitignore when already exist" do + run_edit_command("config/tokens.yml.enc") + + Dir.chdir(app_path) do + assert_match "/config/master.key", File.read(".gitignore") + end + end + + test "edit command does not add master key when `RAILS_MASTER_KEY` env specified" do + Dir.chdir(app_path) do + key = IO.binread("config/master.key").strip + FileUtils.rm("config/master.key") + + switch_env("RAILS_MASTER_KEY", key) do + run_edit_command("config/tokens.yml.enc") + assert_not File.exist?("config/master.key") + end + end + end + + test "edit encrypts file with custom key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + Dir.chdir(app_path) do + assert File.exist?("config/tokens.yml.enc") + assert File.exist?("config/tokens.key") + + assert_match "/config/tokens.key", File.read(".gitignore") + end + + assert_match(/access_key_id: 123/, run_edit_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + test "show encrypted file with custom key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + assert_match(/access_key_id: 123/, run_show_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + test "show command raise error when require_master_key is specified and key does not exist" do + add_to_config "config.require_master_key = true" + + assert_match(/Missing encryption key to decrypt file with/, + run_show_command("config/tokens.yml.enc", key: "unexist.key", allow_failure: true)) + end + + test "show command does not raise error when require_master_key is false and master key does not exist" do + remove_file "config/master.key" + add_to_config "config.require_master_key = false" + + assert_match(/Missing 'config\/master\.key' to decrypt data/, run_show_command("config/tokens.yml.enc")) + end + + test "won't corrupt encrypted file when passed wrong key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + assert_match "passed the wrong key", + run_edit_command("config/tokens.yml.enc", allow_failure: true) + + assert_match(/access_key_id: 123/, run_show_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + private + def run_edit_command(file, key: nil, editor: "cat", **options) + switch_env("EDITOR", editor) do + rails "encrypted:edit", prepare_args(file, key), **options + end + end + + def run_show_command(file, key: nil, **options) + rails "encrypted:show", prepare_args(file, key), **options + end + + def prepare_args(file, key) + args = [ file ] + args.push("--key", key) if key + args + end +end diff --git a/railties/test/commands/routes_test.rb b/railties/test/commands/routes_test.rb new file mode 100644 index 0000000000..77ed2bda61 --- /dev/null +++ b/railties/test/commands/routes_test.rb @@ -0,0 +1,175 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "rails/command" +require "rails/commands/routes/routes_command" +require "io/console/size" + +class Rails::Command::RoutesTest < ActiveSupport::TestCase + setup :build_app + teardown :teardown_app + + test "singular resource output in rails routes" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + resource :post + end + RUBY + + expected_output = [" Prefix Verb URI Pattern Controller#Action", + " new_post GET /post/new(.:format) posts#new", + "edit_post GET /post/edit(.:format) posts#edit", + " post GET /post(.:format) posts#show", + " PATCH /post(.:format) posts#update", + " PUT /post(.:format) posts#update", + " DELETE /post(.:format) posts#destroy", + " POST /post(.:format) posts#create\n"].join("\n") + + output = run_routes_command(["-c", "PostController"]) + assert_equal expected_output, output + end + + test "rails routes with global search key" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get '/cart', to: 'cart#show' + post '/cart', to: 'cart#create' + get '/basketballs', to: 'basketball#index' + end + RUBY + + output = run_routes_command(["-g", "show"]) + assert_equal <<~MESSAGE, output + Prefix Verb URI Pattern Controller#Action + cart GET /cart(.:format) cart#show + rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show + rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show + rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + MESSAGE + + output = run_routes_command(["-g", "POST"]) + assert_equal <<~MESSAGE, output + Prefix Verb URI Pattern Controller#Action + POST /cart(.:format) cart#create + rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + MESSAGE + + output = run_routes_command(["-g", "basketballs"]) + assert_equal " Prefix Verb URI Pattern Controller#Action\n" \ + "basketballs GET /basketballs(.:format) basketball#index\n", output + end + + test "rails routes with controller search key" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get '/cart', to: 'cart#show' + get '/basketball', to: 'basketball#index' + end + RUBY + + output = run_routes_command(["-c", "cart"]) + assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output + + output = run_routes_command(["-c", "Cart"]) + assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output + + output = run_routes_command(["-c", "CartController"]) + assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output + end + + test "rails routes with namespaced controller search key" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + namespace :admin do + resource :post + end + end + RUBY + + expected_output = [" Prefix Verb URI Pattern Controller#Action", + " new_admin_post GET /admin/post/new(.:format) admin/posts#new", + "edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit", + " admin_post GET /admin/post(.:format) admin/posts#show", + " PATCH /admin/post(.:format) admin/posts#update", + " PUT /admin/post(.:format) admin/posts#update", + " DELETE /admin/post(.:format) admin/posts#destroy", + " POST /admin/post(.:format) admin/posts#create\n"].join("\n") + + output = run_routes_command(["-c", "Admin::PostController"]) + assert_equal expected_output, output + + output = run_routes_command(["-c", "PostController"]) + assert_equal expected_output, output + end + + test "rails routes displays message when no routes are defined" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + end + RUBY + + assert_equal <<~MESSAGE, run_routes_command + Prefix Verb URI Pattern Controller#Action + rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show + rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show + rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update + rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + MESSAGE + end + + test "rails routes with expanded option" do + begin + previous_console_winsize = IO.console.winsize + IO.console.winsize = [0, 27] + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get '/cart', to: 'cart#show' + end + RUBY + + output = run_routes_command(["--expanded"]) + + assert_equal <<~MESSAGE, output + --[ Route 1 ]-------------- + Prefix | cart + Verb | GET + URI | /cart(.:format) + Controller#Action | cart#show + --[ Route 2 ]-------------- + Prefix | rails_service_blob + Verb | GET + URI | /rails/active_storage/blobs/:signed_id/*filename(.:format) + Controller#Action | active_storage/blobs#show + --[ Route 3 ]-------------- + Prefix | rails_blob_representation + Verb | GET + URI | /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) + Controller#Action | active_storage/representations#show + --[ Route 4 ]-------------- + Prefix | rails_disk_service + Verb | GET + URI | /rails/active_storage/disk/:encoded_key/*filename(.:format) + Controller#Action | active_storage/disk#show + --[ Route 5 ]-------------- + Prefix | update_rails_disk_service + Verb | PUT + URI | /rails/active_storage/disk/:encoded_token(.:format) + Controller#Action | active_storage/disk#update + --[ Route 6 ]-------------- + Prefix | rails_direct_uploads + Verb | POST + URI | /rails/active_storage/direct_uploads(.:format) + Controller#Action | active_storage/direct_uploads#create + MESSAGE + ensure + IO.console.winsize = previous_console_winsize + end + end + + private + def run_routes_command(args = []) + rails "routes", args + end +end diff --git a/railties/test/commands/secrets_test.rb b/railties/test/commands/secrets_test.rb index 66a81826e3..6b9f284a0c 100644 --- a/railties/test/commands/secrets_test.rb +++ b/railties/test/commands/secrets_test.rb @@ -8,21 +8,38 @@ require "rails/commands/secrets/secrets_command" class Rails::Command::SecretsCommandTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation, EnvHelpers - def setup - build_app + setup :build_app + teardown :teardown_app + + test "edit without editor gives hint" do + assert_match "No $EDITOR to open decrypted secrets in", run_edit_command(editor: "") end - def teardown - teardown_app + test "encrypted secrets are deprecated when using credentials" do + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") end - test "edit without editor gives hint" do - assert_match "No $EDITOR to open decrypted secrets in", run_edit_command(editor: "") + test "encrypted secrets are deprecated when running edit without setup" do + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") + end + + test "encrypted secrets are deprecated for 5.1 config/secrets.yml apps" do + Dir.chdir(app_path) do + FileUtils.rm("config/credentials.yml.enc") + FileUtils.touch("config/secrets.yml") + + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") + end end test "edit secrets" do - # Runs setup before first edit. - assert_match(/Adding config\/secrets\.yml\.key to store the encryption key/, run_edit_command) + prevent_deprecation # Run twice to ensure encrypted secrets can be reread after first edit pass. 2.times do @@ -31,22 +48,30 @@ class Rails::Command::SecretsCommandTest < ActiveSupport::TestCase end test "show secrets" do - run_setup_command + prevent_deprecation + assert_match(/external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289/, run_show_command) end private + def prevent_deprecation + Dir.chdir(app_path) do + File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") + File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") + end + end + def run_edit_command(editor: "cat") switch_env("EDITOR", editor) do - rails "secrets:edit" + rails "secrets:edit", allow_failure: true end end def run_show_command - rails "secrets:show" + rails "secrets:show", allow_failure: true end def run_setup_command - rails "secrets:setup" + rails "secrets:setup", allow_failure: true end end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 556c2289e7..e7a56b3e6d 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require "abstract_unit" +require "isolation/abstract_unit" require "env_helpers" require "rails/command" require "rails/commands/server/server_command" -class Rails::ServerTest < ActiveSupport::TestCase +class Rails::Command::ServerCommandTest < ActiveSupport::TestCase include EnvHelpers def test_environment_with_server_option - args = ["thin", "-e", "production"] + args = ["-u", "thin", "-e", "production"] options = parse_arguments(args) assert_equal "production", options[:environment] assert_equal "thin", options[:server] @@ -22,8 +22,38 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_nil options[:server] end + def test_explicit_using_option + args = ["-u", "thin"] + options = parse_arguments(args) + assert_equal "thin", options[:server] + end + + def test_using_server_mistype + assert_match(/Could not find server "tin". Maybe you meant "thin"?/, run_command("--using", "tin")) + end + + def test_using_positional_argument_deprecation + assert_match(/DEPRECATION WARNING/, run_command("tin")) + end + + def test_using_known_server_that_isnt_in_the_gemfile + assert_match(/Could not load server "unicorn". Maybe you need to the add it to the Gemfile/, run_command("-u", "unicorn")) + end + + def test_daemon_with_option + args = ["-d"] + options = parse_arguments(args) + assert_equal true, options[:daemonize] + end + + def test_daemon_without_option + args = [] + options = parse_arguments(args) + assert_equal false, options[:daemonize] + end + def test_server_option_without_environment - args = ["thin"] + args = ["-u", "thin"] with_rack_env nil do with_rails_env nil do options = parse_arguments(args) @@ -60,6 +90,15 @@ class Rails::ServerTest < ActiveSupport::TestCase def test_environment_with_host switch_env "HOST", "1.2.3.4" do + assert_deprecated do + options = parse_arguments + assert_equal "1.2.3.4", options[:Host] + end + end + end + + def test_environment_with_binding + switch_env "BINDING", "1.2.3.4" do options = parse_arguments assert_equal "1.2.3.4", options[:Host] end @@ -81,6 +120,18 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_equal false, options[:caching] end + def test_early_hints_with_option + args = ["--early-hints"] + options = parse_arguments(args) + assert_equal true, options[:early_hints] + end + + def test_early_hints_is_nil_by_default + args = [] + options = parse_arguments(args) + assert_nil options[:early_hints] + end + def test_log_stdout with_rack_env nil do with_rails_env nil do @@ -154,7 +205,7 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_equal 3000, options[:Port] end - switch_env "HOST", "1.2.3.4" do + switch_env "BINDING", "1.2.3.4" do args = ["-b", "127.0.0.1"] options = parse_arguments(args) assert_equal "127.0.0.1", options[:Host] @@ -173,6 +224,11 @@ class Rails::ServerTest < ActiveSupport::TestCase server_options = parse_arguments(["--port=3001"]) assert_equal [:Port], server_options[:user_supplied_options] + + switch_env "BINDING", "1.2.3.4" do + server_options = parse_arguments + assert_equal [:Host], server_options[:user_supplied_options] + end end def test_default_options @@ -197,7 +253,20 @@ class Rails::ServerTest < ActiveSupport::TestCase ARGV.replace original_args end + def test_served_url + args = %w(-u webrick -b 127.0.0.1 -p 4567) + server = Rails::Server.new(parse_arguments(args)) + assert_equal "http://127.0.0.1:4567", server.served_url + end + private + def run_command(*args) + build_app + rails "server", *args + ensure + teardown_app + end + def parse_arguments(args = []) Rails::Command::ServerCommand.new([], args).server_options end diff --git a/railties/test/engine/test_test.rb b/railties/test/engine/test_test.rb new file mode 100644 index 0000000000..18af85a0aa --- /dev/null +++ b/railties/test/engine/test_test.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class Rails::Engine::TestTest < ActiveSupport::TestCase + setup do + @destination_root = Dir.mktmpdir("bukkits") + Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --mountable` } + end + + teardown do + FileUtils.rm_rf(@destination_root) + end + + test "automatically synchronize test schema" do + Dir.chdir(plugin_path) do + # In order to confirm that migration files are loaded, generate multiple migration files. + `bin/rails generate model user name:string; + bin/rails generate model todo name:string; + RAILS_ENV=development bin/rails db:migrate` + + output = `bin/rails test test/models/bukkits/user_test.rb` + assert_includes(output, "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips") + end + end + + private + def plugin_path + "#{@destination_root}/bukkits" + end +end diff --git a/railties/test/engine_test.rb b/railties/test/engine_test.rb index 4bd8a07085..19379e200c 100644 --- a/railties/test/engine_test.rb +++ b/railties/test/engine_test.rb @@ -11,7 +11,7 @@ class EngineTest < ActiveSupport::TestCase end end - assert !engine.routes? + assert_not_predicate engine, :routes? end def test_application_can_be_subclassed diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml b/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml deleted file mode 100644 index fe80872a16..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml +++ /dev/null @@ -1 +0,0 @@ -# an empty YAML file - any content in here seems to get parsed as a string
\ No newline at end of file diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb deleted file mode 100644 index 1a82a2bdd4..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -# intentionally empty diff --git a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb deleted file mode 100644 index 1a82a2bdd4..0000000000 --- a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -# intentionally empty diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 3ecfb4edd9..f421207025 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -308,6 +308,14 @@ class ActionsTest < Rails::Generators::TestCase end end + test "rake command with capture option should run rake command with capture" do + assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=development", verbose: false, capture: true]) do + with_rails_env nil do + action :rake, "log:clear", capture: true + end + end + end + test "rails command should run rails_command with default env" do assert_called_with(generator, :run, ["rails log:clear RAILS_ENV=development", verbose: false]) do with_rails_env nil do @@ -346,6 +354,14 @@ class ActionsTest < Rails::Generators::TestCase end end + test "rails command with capture option should run rails_command with capture" do + assert_called_with(generator, :run, ["rails log:clear RAILS_ENV=development", verbose: false, capture: true]) do + with_rails_env nil do + action :rails_command, "log:clear", capture: true + end + end + end + def test_capify_should_run_the_capify_command content = capture(:stderr) do assert_called_with(generator, :run, ["capify .", verbose: false]) do diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb index 7791d472d8..9c523ad372 100644 --- a/railties/test/generators/api_app_generator_test.rb +++ b/railties/test/generators/api_app_generator_test.rb @@ -13,7 +13,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase Rails.application = TestApp::Application super - Kernel::silence_warnings do + Kernel.silence_warnings do Thor::Base.shell.send(:attr_accessor, :always_force) @shell = Thor::Base.shell.new @shell.send(:always_force=, true) @@ -63,6 +63,23 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase end end + def test_generator_if_skip_action_mailer_is_given + run_generator [destination_root, "--api", "--skip-action-mailer"] + assert_file "config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ + assert_file "config/environments/development.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_file "config/environments/test.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_file "config/environments/production.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_no_directory "app/mailers" + assert_no_directory "test/mailers" + assert_no_directory "app/views" + end + def test_app_update_does_not_generate_unnecessary_config_files run_generator @@ -72,6 +89,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase assert_no_file "config/initializers/cookies_serializer.rb" assert_no_file "config/initializers/assets.rb" + assert_no_file "config/initializers/content_security_policy.rb" end def test_app_update_does_not_generate_unnecessary_bin_files @@ -149,6 +167,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase bin/yarn config/initializers/assets.rb config/initializers/cookies_serializer.rb + config/initializers/content_security_policy.rb lib/assets test/helpers tmp/cache/assets diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 904e2a5c84..a820d958f1 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -56,6 +56,7 @@ DEFAULT_APP_FILES = %w( config/initializers/assets.rb config/initializers/backtrace_silencers.rb config/initializers/cookies_serializer.rb + config/initializers/content_security_policy.rb config/initializers/filter_parameter_logging.rb config/initializers/inflections.rb config/initializers/mime_types.rb @@ -75,6 +76,7 @@ DEFAULT_APP_FILES = %w( log package.json public + storage test/application_system_test_case.rb test/test_helper.rb test/fixtures @@ -89,6 +91,7 @@ DEFAULT_APP_FILES = %w( tmp tmp/cache tmp/cache/assets + tmp/storage ) class AppGeneratorTest < Rails::Generators::TestCase @@ -102,6 +105,15 @@ class AppGeneratorTest < Rails::Generators::TestCase ::DEFAULT_APP_FILES end + def test_skip_bundle + assert_not_called(generator([destination_root], skip_bundle: true), :bundle_command) do + quietly { generator.invoke_all } + # skip_bundle is only about running bundle install, ensure the Gemfile is still + # generated. + assert_file "Gemfile" + end + end + def test_assets run_generator @@ -199,7 +211,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end def test_new_application_doesnt_need_defaults - assert_no_file "config/initializers/new_framework_defaults_5_2.rb" + assert_no_file "config/initializers/new_framework_defaults_6_0.rb" end def test_new_application_load_defaults @@ -207,6 +219,8 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator [app_root] output = nil + assert_file "#{app_root}/config/application.rb", /\s+config\.load_defaults #{Rails::VERSION::STRING.to_f}/ + Dir.chdir(app_root) do output = `./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"` end @@ -245,14 +259,14 @@ class AppGeneratorTest < Rails::Generators::TestCase app_root = File.join(destination_root, "myapp") run_generator [app_root] - assert_no_file "#{app_root}/config/initializers/new_framework_defaults_5_2.rb" + assert_no_file "#{app_root}/config/initializers/new_framework_defaults_6_0.rb" 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/initializers/new_framework_defaults_5_2.rb" + assert_file "#{app_root}/config/initializers/new_framework_defaults_6_0.rb" end end @@ -296,6 +310,76 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_gem_for_active_storage + run_generator + assert_file "Gemfile", /^# gem 'image_processing'/ + end + + def test_gem_for_active_storage_when_skip_active_storage_is_given + run_generator [destination_root, "--skip-active-storage"] + + assert_no_gem "image_processing" + end + + def test_app_update_does_not_generate_active_storage_contents_when_skip_active_storage_is_given + 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 + + 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/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{app_root}/config/storage.yml" + 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 + + 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/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{app_root}/config/storage.yml" + end + + def test_app_update_does_not_change_config_target_version + run_generator + + FileUtils.cd(destination_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/ + end + def test_application_names_are_not_singularized run_generator [File.join(destination_root, "hats")] assert_file "hats/config/environment.rb", /Rails\.application\.initialize!/ @@ -321,13 +405,13 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_config_another_database + def test_config_mysql_database run_generator([destination_root, "-d", "mysql"]) assert_file "config/database.yml", /mysql/ if defined?(JRUBY_VERSION) assert_gem "activerecord-jdbcmysql-adapter" else - assert_gem "mysql2", "'>= 0.3.18', '< 0.5'" + assert_gem "mysql2", "'>= 0.4.4', '< 0.6.0'" end end @@ -342,7 +426,7 @@ class AppGeneratorTest < Rails::Generators::TestCase if defined?(JRUBY_VERSION) assert_gem "activerecord-jdbcpostgresql-adapter" else - assert_gem "pg", "'~> 0.18'" + assert_gem "pg", "'>= 0.18', '< 2.0'" end end @@ -381,15 +465,13 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_generator_defaults_to_puma_version run_generator [destination_root] - assert_gem "puma", "'~> 3.7'" + assert_gem "puma", "'~> 3.11'" end def test_generator_if_skip_puma_is_given run_generator [destination_root, "--skip-puma"] assert_no_file "config/puma.rb" - assert_file "Gemfile" do |content| - assert_no_match(/puma/, content) - end + assert_no_gem "puma" end def test_generator_has_assets_gems @@ -409,20 +491,18 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/ - assert_file "Gemfile" do |content| - assert_no_match(/capybara/, content) - assert_no_match(/selenium-webdriver/, content) - end + assert_no_gem "capybara" + assert_no_gem "selenium-webdriver" + assert_no_gem "chromedriver-helper" assert_no_directory("test") end def test_generator_if_skip_system_test_is_given run_generator [destination_root, "--skip-system-test"] - assert_file "Gemfile" do |content| - assert_no_match(/capybara/, content) - assert_no_match(/selenium-webdriver/, content) - end + assert_no_gem "capybara" + assert_no_gem "selenium-webdriver" + assert_no_gem "chromedriver-helper" assert_directory("test") @@ -468,10 +548,8 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_match(/javascript_include_tag\s+'application' \%>/, contents) end - assert_file "Gemfile" do |content| - assert_no_match(/coffee-rails/, content) - assert_no_match(/uglifier/, content) - end + assert_no_gem "coffee-rails" + assert_no_gem "uglifier" assert_file "config/environments/production.rb" do |content| assert_no_match(/config\.assets\.js_compressor = :uglifier/, content) @@ -495,9 +573,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_inclusion_of_a_debugger run_generator if defined?(JRUBY_VERSION) || RUBY_ENGINE == "rbx" - assert_file "Gemfile" do |content| - assert_no_match(/byebug/, content) - end + assert_no_gem "byebug" else assert_gem "byebug" end @@ -560,6 +636,18 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_match(/run git init/, output) end + def test_quiet_option + output = run_generator [File.join(destination_root, "myapp"), "--quiet"] + assert_empty output + end + + def test_force_option_overwrites_every_file_except_master_key + run_generator [File.join(destination_root, "myapp")] + output = run_generator [File.join(destination_root, "myapp"), "--force"] + assert_match(/force/, output) + assert_no_match("force config/master.key", output) + end + def test_application_name_with_spaces path = File.join(destination_root, "foo bar") @@ -636,9 +724,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_called_with(Process, :respond_to?, [[:fork], [:fork]], returns: false) do run_generator - assert_file "Gemfile" do |content| - assert_no_match(/spring/, content) - end + assert_no_gem "spring" end end @@ -646,25 +732,58 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--skip-spring"] assert_no_file "config/spring.rb" - assert_file "Gemfile" do |content| - assert_no_match(/spring/, content) - end + assert_no_gem "spring" end def test_spring_with_dev_option run_generator [destination_root, "--dev"] - assert_file "Gemfile" do |content| - assert_no_match(/spring/, content) + assert_no_gem "spring" + end + + def test_webpack_option + command_check = -> command, *_ do + @called ||= 0 + if command == "webpacker:install" + @called += 1 + assert_equal 1, @called, "webpacker:install expected to be called once, but was called #{@called} times." + end + end + + generator([destination_root], webpack: "webpack").stub(:rails_command, command_check) do + generator.stub :bundle_command, nil do + quietly { generator.invoke_all } + end + end + + assert_gem "webpacker" + end + + def test_webpack_option_with_js_framework + command_check = -> command, *_ do + case command + when "webpacker:install" + @webpacker ||= 0 + @webpacker += 1 + assert_equal 1, @webpacker, "webpacker:install expected to be called once, but was called #{@webpacker} times." + when "webpacker:install:react" + @react ||= 0 + @react += 1 + assert_equal 1, @react, "webpacker:install:react expected to be called once, but was called #{@react} times." + end + end + + generator([destination_root], webpack: "react").stub(:rails_command, command_check) do + generator.stub :bundle_command, nil do + quietly { generator.invoke_all } + end end end def test_generator_if_skip_turbolinks_is_given run_generator [destination_root, "--skip-turbolinks"] - assert_file "Gemfile" do |content| - assert_no_match(/turbolinks/, content) - end + assert_no_gem "turbolinks" assert_file "app/views/layouts/application.html.erb" do |content| assert_no_match(/data-turbolinks-track/, content) end @@ -673,6 +792,40 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_bootsnap + run_generator + + unless defined?(JRUBY_VERSION) + assert_gem "bootsnap" + assert_file "config/boot.rb" do |content| + assert_match(/require 'bootsnap\/setup'/, content) + end + else + assert_no_gem "bootsnap" + assert_file "config/boot.rb" do |content| + assert_no_match(/require 'bootsnap\/setup'/, content) + end + end + end + + def test_skip_bootsnap + run_generator [destination_root, "--skip-bootsnap"] + + assert_no_gem "bootsnap" + assert_file "config/boot.rb" do |content| + assert_no_match(/require 'bootsnap\/setup'/, content) + end + end + + def test_bootsnap_with_dev_option + run_generator [destination_root, "--dev"] + + assert_no_gem "bootsnap" + assert_file "config/boot.rb" do |content| + assert_no_match(/require 'bootsnap\/setup'/, content) + end + end + def test_inclusion_of_ruby_version run_generator @@ -680,7 +833,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_match(/ruby '#{RUBY_VERSION}'/, content) end assert_file ".ruby-version" do |content| - assert_match(/#{RUBY_VERSION}/, content) + assert_match(/#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}/, content) end end @@ -737,7 +890,7 @@ class AppGeneratorTest < Rails::Generators::TestCase sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"] @sequence_step ||= 0 - ensure_bundler_first = -> command do + ensure_bundler_first = -> command, options = nil do assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" @sequence_step += 1 end @@ -745,7 +898,9 @@ class AppGeneratorTest < Rails::Generators::TestCase generator([destination_root], template: path).stub(:open, check_open, template) do generator.stub(:bundle_command, ensure_bundler_first) do generator.stub(:run, ensure_bundler_first) do - quietly { generator.invoke_all } + generator.stub(:rails_command, ensure_bundler_first) do + quietly { generator.invoke_all } + end end end end @@ -753,6 +908,14 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_equal 4, @sequence_step end + def test_gitignore + run_generator + + assert_file ".gitignore" do |content| + assert_match(/config\/master\.key/, content) + end + end + def test_system_tests_directory_generated run_generator @@ -760,6 +923,15 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_directory("test/system") end + unless Gem.win_platform? + def test_master_key_is_only_readable_by_the_owner + run_generator + + stat = File.stat("config/master.key") + assert_equal "100600", sprintf("%o", stat.mode) + end + end + private def stub_rails_application(root) Rails.application.config.root = root @@ -780,6 +952,12 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def assert_no_gem(gem) + assert_file "Gemfile" do |content| + assert_no_match(gem, content) + end + end + def assert_listen_related_configuration assert_gem "listen" assert_gem "spring-watcher-listen" @@ -790,9 +968,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end def assert_no_listen_related_configuration - assert_file "Gemfile" do |content| - assert_no_match(/listen/, content) - end + assert_no_gem "listen" assert_file "config/environments/development.rb" do |content| assert_match(/^\s*# config\.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) diff --git a/railties/test/generators/argv_scrubber_test.rb b/railties/test/generators/argv_scrubber_test.rb index 3f4c985a33..9ef61dc978 100644 --- a/railties/test/generators/argv_scrubber_test.rb +++ b/railties/test/generators/argv_scrubber_test.rb @@ -82,9 +82,8 @@ module Rails file.puts "--hello --world" file.flush - message = nil scrubber = Class.new(ARGVScrubber) { - define_method(:puts) { |msg| message = msg } + define_method(:puts) { |msg| } }.new ["new", "--rc=#{file.path}"] args = scrubber.prepare! assert_equal ["--hello", "--world"], args diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb index e238197eba..e543cc11b8 100644 --- a/railties/test/generators/channel_generator_test.rb +++ b/railties/test/generators/channel_generator_test.rb @@ -76,4 +76,14 @@ class ChannelGeneratorTest < Rails::Generators::TestCase assert_file "app/channels/application_cable/connection.rb" assert_file "app/assets/javascripts/cable.js" end + + def test_channel_suffix_is_not_duplicated + run_generator ["chat_channel"] + + assert_no_file "app/channels/chat_channel_channel.rb" + assert_file "app/channels/chat_channel.rb" + + assert_no_file "app/assets/javascripts/channels/chat_channel.js" + assert_file "app/assets/javascripts/channels/chat.js" + end end diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb index a3218951a6..021004c9b8 100644 --- a/railties/test/generators/controller_generator_test.rb +++ b/railties/test/generators/controller_generator_test.rb @@ -109,4 +109,33 @@ class ControllerGeneratorTest < Rails::Generators::TestCase assert_match(/^ namespace :admin do\n get 'dashboard\/index'\n get 'dashboard\/show'\n end$/, route) end end + + def test_does_not_add_routes_when_action_is_not_specified + run_generator ["admin/dashboard"] + assert_file "config/routes.rb" do |routes| + assert_no_match(/namespace :admin/, routes) + end + end + + def test_controller_suffix_is_not_duplicated + run_generator ["account_controller"] + + assert_no_file "app/controllers/account_controller_controller.rb" + assert_file "app/controllers/account_controller.rb" + + assert_no_file "app/views/account_controller/" + assert_file "app/views/account/" + + assert_no_file "test/controllers/account_controller_controller_test.rb" + assert_file "test/controllers/account_controller_test.rb" + + assert_no_file "app/helpers/account_controller_helper.rb" + assert_file "app/helpers/account_helper.rb" + + assert_no_file "app/assets/javascripts/account_controller.js" + assert_file "app/assets/javascripts/account.js" + + assert_no_file "app/assets/stylesheets/account_controller.css" + assert_file "app/assets/stylesheets/account.css" + end end diff --git a/railties/test/generators/create_migration_test.rb b/railties/test/generators/create_migration_test.rb index 3cb7fd6baa..2ae38045c5 100644 --- a/railties/test/generators/create_migration_test.rb +++ b/railties/test/generators/create_migration_test.rb @@ -70,7 +70,7 @@ class CreateMigrationTest < Rails::Generators::TestCase create_migration assert_match(/identical db\/migrate\/1_create_articles\.rb\n/, invoke!) - assert @migration.identical? + assert_predicate @migration, :identical? end def test_invoke_when_exists_not_identical diff --git a/railties/test/generators/encrypted_secrets_generator_test.rb b/railties/test/generators/encrypted_secrets_generator_test.rb deleted file mode 100644 index eacb5166c0..0000000000 --- a/railties/test/generators/encrypted_secrets_generator_test.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require "generators/generators_test_helper" -require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" - -class EncryptedSecretsGeneratorTest < Rails::Generators::TestCase - include GeneratorsTestHelper - - def setup - super - cd destination_root - end - - def test_generates_key_file_and_encrypted_secrets_file - run_generator - - assert_file "config/secrets.yml.key", /\w+/ - - assert File.exist?("config/secrets.yml.enc") - assert_no_match(/# production:\n# external_api_key: \w+/, IO.binread("config/secrets.yml.enc")) - assert_match(/# production:\n# external_api_key: \w+/, Rails::Secrets.read) - end - - def test_appends_to_gitignore - FileUtils.touch(".gitignore") - - run_generator - - assert_file ".gitignore", /config\/secrets.yml.key/, /(?!config\/secrets.yml.enc)/ - end - - def test_warns_when_ignore_is_missing - assert_match(/Add this to your ignore file/i, run_generator) - end - - def test_doesnt_generate_a_new_key_file_if_already_opted_in_to_encrypted_secrets - FileUtils.mkdir("config") - File.open("config/secrets.yml.enc", "w") { |f| f.puts "already secrety" } - - run_generator - - assert_no_file "config/secrets.yml.key" - end -end diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb index c6f7bdeda1..772b4f6f0d 100644 --- a/railties/test/generators/generated_attribute_test.rb +++ b/railties/test/generators/generated_attribute_test.rb @@ -104,25 +104,25 @@ class GeneratedAttributeTest < Rails::Generators::TestCase def test_reference_is_true %w(references belongs_to).each do |attribute_type| - assert create_generated_attribute(attribute_type).reference? + assert_predicate create_generated_attribute(attribute_type), :reference? end end def test_reference_is_false %w(foo bar baz).each do |attribute_type| - assert !create_generated_attribute(attribute_type).reference? + assert_not_predicate create_generated_attribute(attribute_type), :reference? end end def test_polymorphic_reference_is_true %w(references belongs_to).each do |attribute_type| - assert create_generated_attribute("#{attribute_type}{polymorphic}").polymorphic? + assert_predicate create_generated_attribute("#{attribute_type}{polymorphic}"), :polymorphic? end end def test_polymorphic_reference_is_false %w(foo bar baz).each do |attribute_type| - assert !create_generated_attribute("#{attribute_type}{polymorphic}").polymorphic? + assert_not_predicate create_generated_attribute("#{attribute_type}{polymorphic}"), :polymorphic? end end @@ -148,7 +148,7 @@ class GeneratedAttributeTest < Rails::Generators::TestCase att = Rails::Generators::GeneratedAttribute.parse("supplier:references{required}:index") assert_equal "supplier", att.name assert_equal :references, att.type - assert att.has_index? - assert att.required? + assert_predicate att, :has_index? + assert_predicate att, :required? end end diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb index cb5d8da7b1..ad2a55f496 100644 --- a/railties/test/generators/generators_test_helper.rb +++ b/railties/test/generators/generators_test_helper.rb @@ -43,9 +43,9 @@ module GeneratorsTestHelper end def copy_routes - routes = File.expand_path("../../lib/rails/generators/rails/app/templates/config/routes.rb", __dir__) + routes = File.expand_path("../../lib/rails/generators/rails/app/templates/config/routes.rb.tt", __dir__) destination = File.join(destination_root, "config") FileUtils.mkdir_p(destination) - FileUtils.cp routes, destination + FileUtils.cp routes, File.join(destination, "routes.rb") end end diff --git a/railties/test/generators/job_generator_test.rb b/railties/test/generators/job_generator_test.rb index 13276fac65..234ba6dad7 100644 --- a/railties/test/generators/job_generator_test.rb +++ b/railties/test/generators/job_generator_test.rb @@ -35,4 +35,14 @@ class JobGeneratorTest < Rails::Generators::TestCase assert_match(/class ApplicationJob < ActiveJob::Base/, job) end end + + def test_job_suffix_is_not_duplicated + run_generator ["notifier_job"] + + assert_no_file "app/jobs/notifier_job_job.rb" + assert_file "app/jobs/notifier_job.rb" + + assert_no_file "test/jobs/notifier_job_job_test.rb" + assert_file "test/jobs/notifier_job_test.rb" + end end diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 516aa0704f..8d933e82c3 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -2,7 +2,6 @@ require "generators/generators_test_helper" require "rails/generators/rails/model/model_generator" -require "active_support/core_ext/string/strip" class ModelGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper @@ -379,10 +378,10 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_required_belongs_to_adds_required_association run_generator ["account", "supplier:references{required}"] - expected_file = <<-FILE.strip_heredoc - class Account < ApplicationRecord - belongs_to :supplier, required: true - end + expected_file = <<~FILE + class Account < ApplicationRecord + belongs_to :supplier, required: true + end FILE assert_file "app/models/account.rb", expected_file end @@ -390,10 +389,10 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_required_polymorphic_belongs_to_generages_correct_model run_generator ["account", "supplier:references{required,polymorphic}"] - expected_file = <<-FILE.strip_heredoc - class Account < ApplicationRecord - belongs_to :supplier, polymorphic: true, required: true - end + expected_file = <<~FILE + class Account < ApplicationRecord + belongs_to :supplier, polymorphic: true, required: true + end FILE assert_file "app/models/account.rb", expected_file end @@ -401,10 +400,10 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_required_and_polymorphic_are_order_independent run_generator ["account", "supplier:references{polymorphic.required}"] - expected_file = <<-FILE.strip_heredoc - class Account < ApplicationRecord - belongs_to :supplier, polymorphic: true, required: true - end + expected_file = <<~FILE + class Account < ApplicationRecord + belongs_to :supplier, polymorphic: true, required: true + end FILE assert_file "app/models/account.rb", expected_file end @@ -452,11 +451,11 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_token_option_adds_has_secure_token run_generator ["user", "token:token", "auth_token:token"] - expected_file = <<-FILE.strip_heredoc - class User < ApplicationRecord - has_secure_token - has_secure_token :auth_token - end + expected_file = <<~FILE + class User < ApplicationRecord + has_secure_token + has_secure_token :auth_token + end FILE assert_file "app/models/user.rb", expected_file end diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 67f05926e3..4e61b660d7 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -33,6 +33,17 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "foos", :plural_name assert_name g, "admin.foo", :i18n_scope assert_name g, "admin_foos", :table_name + assert_name g, "admin/foos", :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, "Admin::Foos", :controller_class_name + assert_name g, "admin/foos", :controller_file_path + assert_name g, "foos", :controller_file_name + assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_foo", :singular_route_name + assert_name g, "admin_foos", :plural_route_name + assert_name g, "@admin_foo", :redirect_resource_name + assert_name g, "admin_foo", :model_resource_name + assert_name g, "admin_foos", :index_helper end def test_named_generator_attributes_as_ruby @@ -47,6 +58,17 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "foos", :plural_name assert_name g, "admin.foo", :i18n_scope assert_name g, "admin_foos", :table_name + assert_name g, "Admin::Foos", :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, "Admin::Foos", :controller_class_name + assert_name g, "admin/foos", :controller_file_path + assert_name g, "foos", :controller_file_name + assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_foo", :singular_route_name + assert_name g, "admin_foos", :plural_route_name + assert_name g, "@admin_foo", :redirect_resource_name + assert_name g, "admin_foo", :model_resource_name + assert_name g, "admin_foos", :index_helper end def test_named_generator_attributes_without_pluralized @@ -59,7 +81,7 @@ class NamedBaseTest < Rails::Generators::TestCase ActiveRecord::Base.pluralize_table_names = original_pluralize_table_names end - def test_scaffold_plural_names + def test_namespaced_scaffold_plural_names g = generator ["admin/foo"] assert_name g, "admin/foos", :controller_name assert_name g, %w(admin), :controller_class_path @@ -69,7 +91,7 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "admin.foos", :controller_i18n_scope end - def test_scaffold_plural_names_as_ruby + def test_namespaced_scaffold_plural_names_as_ruby g = generator ["Admin::Foo"] assert_name g, "Admin::Foos", :controller_name assert_name g, %w(admin), :controller_class_path @@ -131,6 +153,19 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "admin/foos", :controller_file_path assert_name g, "foos", :controller_file_name assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_user", :singular_route_name + assert_name g, "admin_users", :plural_route_name + assert_name g, "[:admin, @user]", :redirect_resource_name + assert_name g, "[:admin, user]", :model_resource_name + assert_name g, "admin_users", :index_helper + end + + def test_scaffold_plural_names + g = generator ["User"] + assert_name g, "@user", :redirect_resource_name + assert_name g, "user", :model_resource_name + assert_name g, "user", :singular_route_name + assert_name g, "users", :plural_route_name end private diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 38130ceb68..28ac3611b7 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -82,11 +82,12 @@ class PluginGeneratorTest < Rails::Generators::TestCase end def test_generating_in_full_mode_with_almost_of_all_skip_options - run_generator [destination_root, "--full", "-M", "-O", "-C", "-S", "-T"] + run_generator [destination_root, "--full", "-M", "-O", "-C", "-S", "-T", "--skip-active-storage"] assert_file "bin/rails" do |content| assert_no_match(/\s+require\s+["']rails\/all["']/, content) end assert_file "bin/rails", /#\s+require\s+["']active_record\/railtie["']/ + assert_file "bin/rails", /#\s+require\s+["']active_storage\/engine["']/ assert_file "bin/rails", /#\s+require\s+["']action_mailer\/railtie["']/ assert_file "bin/rails", /#\s+require\s+["']action_cable\/engine["']/ assert_file "bin/rails", /#\s+require\s+["']sprockets\/railtie["']/ @@ -216,12 +217,22 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_javascripts_generation run_generator [destination_root, "--mountable"] - assert_file "app/assets/javascripts/bukkits/application.js" + assert_file "app/assets/javascripts/bukkits/application.js" do |content| + assert_match "//= require rails-ujs", content + assert_match "//= require activestorage", content + assert_match "//= require_tree .", content + end + assert_file "app/views/layouts/bukkits/application.html.erb" do |content| + assert_match "javascript_include_tag", content + end end def test_skip_javascripts run_generator [destination_root, "--skip-javascript", "--mountable"] assert_no_file "app/assets/javascripts/bukkits/application.js" + assert_file "app/views/layouts/bukkits/application.html.erb" do |content| + assert_no_match "javascript_include_tag", content + end end def test_template_from_dir_pwd @@ -240,8 +251,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--full", "--skip_active_record"] FileUtils.cd destination_root quietly { system "bundle install" } - # FIXME: Active Storage will provoke a test error without ActiveRecord (fix by allowing to skip active storage) - assert_match(/1 runs, 0 assertions, 0 failures, 1 errors/, `bundle exec rake test 2>&1`) + assert_match(/1 runs, 1 assertions, 0 failures, 0 errors/, `bundle exec rake test 2>&1`) end def test_ensure_that_migration_tasks_work_with_mountable_option @@ -321,8 +331,11 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "app/helpers/bukkits/application_helper.rb", /module Bukkits\n module ApplicationHelper/ assert_file "app/views/layouts/bukkits/application.html.erb" do |contents| assert_match "<title>Bukkits</title>", contents + assert_match "<%= csrf_meta_tags %>", contents + assert_match "<%= csp_meta_tag %>", contents assert_match(/stylesheet_link_tag\s+['"]bukkits\/application['"]/, contents) assert_match(/javascript_include_tag\s+['"]bukkits\/application['"]/, contents) + assert_match "<%= yield %>", contents end assert_file "test/test_helper.rb" do |content| assert_match(/ActiveRecord::Migrator\.migrations_paths.+\.\.\/test\/dummy\/db\/migrate/, content) @@ -475,6 +488,8 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_no_file "test/dummy/Gemfile" assert_no_file "test/dummy/public/robots.txt" assert_no_file "test/dummy/README.md" + assert_no_file "test/dummy/config/master.key" + assert_no_file "test/dummy/config/credentials.yml.enc" assert_no_directory "test/dummy/lib/tasks" assert_no_directory "test/dummy/test" assert_no_directory "test/dummy/vendor" @@ -514,10 +529,11 @@ class PluginGeneratorTest < Rails::Generators::TestCase gemfile_path = "#{Rails.root}/Gemfile" Object.const_set("APP_PATH", Rails.root) FileUtils.touch gemfile_path + File.write(gemfile_path, "#foo") run_generator - assert_file gemfile_path, /gem 'bukkits', path: 'tmp\/bukkits'/ + assert_file gemfile_path, /^gem 'bukkits', path: 'tmp\/bukkits'/ ensure Object.send(:remove_const, "APP_PATH") FileUtils.rm gemfile_path @@ -719,6 +735,38 @@ class PluginGeneratorTest < Rails::Generators::TestCase Object.send(:remove_const, "ENGINE_ROOT") end + def test_after_bundle_callback + path = "http://example.org/rails_template" + template = %{ after_bundle { run "echo ran after_bundle" } }.dup + template.instance_eval "def read; self; end" # Make the string respond to read + + check_open = -> *args do + assert_equal [ path, "Accept" => "application/x-thor-template" ], args + template + end + + sequence = ["echo ran after_bundle"] + @sequence_step ||= 0 + ensure_bundler_first = -> command do + assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" + @sequence_step += 1 + end + + content = nil + generator([destination_root], template: path).stub(:open, check_open, template) do + generator.stub(:bundle_command, ensure_bundler_first) do + generator.stub(:run, ensure_bundler_first) do + silence_stream($stdout) do + content = capture(:stderr) { generator.invoke_all } + end + end + end + end + + assert_equal 1, @sequence_step + assert_match(/DEPRECATION WARNING: `after_bundle` is deprecated/, content) + end + private def action(*args, &block) diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 384524aba9..fd5aa817b4 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -174,6 +174,29 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_instance_method :index, content do |m| assert_match("@users = User.all", m) end + + assert_instance_method :create, content do |m| + assert_match("redirect_to [:admin, @user]", m) + end + + assert_instance_method :update, content do |m| + assert_match("redirect_to [:admin, @user]", m) + end + end + + assert_file "app/views/admin/users/index.html.erb" do |content| + assert_match("'Show', [:admin, user]", content) + assert_match("'Edit', edit_admin_user_path(user)", content) + assert_match("'Destroy', [:admin, user]", content) + assert_match("'New User', new_admin_user_path", content) + end + + assert_file "app/views/admin/users/new.html.erb" do |content| + assert_match("'Back', admin_users_path", content) + end + + assert_file "app/views/admin/users/_form.html.erb" do |content| + assert_match("model: [:admin, user]", content) end end @@ -184,6 +207,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase Dir.chdir(engine_path) do quietly { `bin/rails g controller dashboard foo` } + quietly { `bin/rails db:migrate RAILS_ENV=test` } assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end @@ -195,6 +219,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase Dir.chdir(engine_path) do quietly { `bin/rails g controller dashboard foo` } + quietly { `bin/rails db:migrate RAILS_ENV=test` } assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 03322c1c59..29426cd99f 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -282,7 +282,14 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase /class Admin::RolesTest < ApplicationSystemTestCase/ # Views - %w(index edit new show _form).each do |view| + assert_file "app/views/admin/roles/index.html.erb" do |content| + assert_match("'Show', admin_role", content) + assert_match("'Edit', edit_admin_role_path(admin_role)", content) + assert_match("'Destroy', admin_role", content) + assert_match("'New Admin Role', new_admin_role_path", content) + end + + %w(edit new show _form).each do |view| assert_file "app/views/admin/roles/#{view}.html.erb" end assert_no_file "app/views/layouts/admin/roles.html.erb" @@ -464,8 +471,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "app/views/accounts/_form.html.erb" do |content| - assert_match(/^\W{4}<%= form\.text_field :name, id: :account_name %>/, content) - assert_match(/^\W{4}<%= form\.text_field :currency_id, id: :account_currency_id %>/, content) + assert_match(/^\W{4}<%= form\.text_field :name %>/, content) + assert_match(/^\W{4}<%= form\.text_field :currency_id %>/, content) end end @@ -488,8 +495,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "app/views/users/_form.html.erb" do |content| - assert_match(/<%= form\.password_field :password, id: :user_password %>/, content) - assert_match(/<%= form\.password_field :password_confirmation, id: :user_password_confirmation %>/, content) + assert_match(/<%= form\.password_field :password %>/, content) + assert_match(/<%= form\.password_field :password_confirmation %>/, content) end assert_file "app/views/users/index.html.erb" do |content| diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index 654d16de65..aa577e4234 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -9,7 +9,7 @@ module SharedGeneratorTests super Rails::Generators::AppGenerator.instance_variable_set("@desc", nil) - Kernel::silence_warnings do + Kernel.silence_warnings do Thor::Base.shell.send(:attr_accessor, :always_force) @shell = Thor::Base.shell.new @shell.send(:always_force=, true) @@ -92,7 +92,9 @@ module SharedGeneratorTests end generator([destination_root], template: path).stub(:open, check_open, template) do - quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) } + generator.stub :bundle_command, nil do + quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) } + end end end @@ -103,15 +105,6 @@ module SharedGeneratorTests end end - def test_skip_bundle - assert_not_called(generator([destination_root], skip_bundle: true), :bundle_command) do - quietly { generator.invoke_all } - # skip_bundle is only about running bundle install, ensure the Gemfile is still - # generated. - assert_file "Gemfile" - end - end - def test_skip_git run_generator [destination_root, "--skip-git", "--full"] assert_no_file(".gitignore") @@ -129,13 +122,26 @@ module SharedGeneratorTests end def test_default_frameworks_are_required_when_others_are_removed - run_generator [destination_root, "--skip-active-record", "--skip-action-mailer", "--skip-action-cable", "--skip-sprockets"] - assert_file "#{application_path}/config/application.rb", /require\s+["']rails["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']active_model\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']active_job\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']action_controller\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']action_view\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']active_storage\/engine["']/ + run_generator [ + destination_root, + "--skip-active-record", + "--skip-active-storage", + "--skip-action-mailer", + "--skip-action-cable", + "--skip-sprockets" + ] + + assert_file "#{application_path}/config/application.rb", /^require\s+["']rails["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']active_model\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']active_job\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']active_record\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']active_storage\/engine["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']action_controller\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_mailer\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']action_view\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_cable\/engine["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']sprockets\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']rails\/test_unit\/railtie["']/ end def test_generator_without_skips @@ -189,6 +195,94 @@ module SharedGeneratorTests end end + def test_generator_for_active_storage + run_generator + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/storage.yml" + assert_directory "#{application_path}/storage" + assert_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_match(/\/storage\//, content) + end + end + + def test_generator_if_skip_active_storage_is_given + run_generator [destination_root, "--skip-active-storage"] + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/ + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_no_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{application_path}/config/storage.yml" + assert_no_directory "#{application_path}/storage" + assert_no_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_no_match(/\/storage\//, content) + end + end + + def test_generator_does_not_generate_active_storage_contents_if_skip_active_record_is_given + run_generator [destination_root, "--skip-active-record"] + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/ + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_no_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{application_path}/config/storage.yml" + assert_no_directory "#{application_path}/storage" + assert_no_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_no_match(/\/storage\//, content) + end + end + def test_generator_if_skip_action_mailer_is_given run_generator [destination_root, "--skip-action-mailer"] assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 28e7617d7f..a16a2d3f0a 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -33,13 +33,26 @@ class GeneratorsTest < Rails::Generators::TestCase def test_generator_suggestions name = :migrationz output = capture(:stdout) { Rails::Generators.invoke name } - assert_match "Maybe you meant 'migration'", output + assert_match 'Maybe you meant "migration"?', output + end + + def test_generator_suggestions_except_en_locale + orig_available_locales = I18n.available_locales + orig_default_locale = I18n.default_locale + I18n.available_locales = :ja + I18n.default_locale = :ja + name = :tas + output = capture(:stdout) { Rails::Generators.invoke name } + assert_match 'Maybe you meant "task"?', output + ensure + I18n.available_locales = orig_available_locales + I18n.default_locale = orig_default_locale end def test_generator_multiple_suggestions name = :tas output = capture(:stdout) { Rails::Generators.invoke name } - assert_match "Maybe you meant 'task', 'job' or", output + assert_match 'Maybe you meant "task"?', output end def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments @@ -51,7 +64,7 @@ class GeneratorsTest < Rails::Generators::TestCase assert File.exist?(File.join(@path, "generators", "model_generator.rb")) assert_called_with(Rails::Generators::ModelGenerator, :start, [["Account"], {}]) do warnings = capture(:stderr) { Rails::Generators.invoke :model, ["Account"] } - assert warnings.empty? + assert_empty warnings end end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index b7f214cb73..3cde7c03b0 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -38,7 +38,12 @@ module TestHelpers end def app_path(*args) - tmp_path(*%w[app] + args) + path = tmp_path(*%w[app] + args) + if block_given? + yield path + else + path + end end def framework_path @@ -82,22 +87,6 @@ module TestHelpers assert_match "charset=utf-8", resp[1]["Content-Type"] assert extract_body(resp).match(/Yay! You.*re on Rails!/) end - - def assert_success(resp) - assert_equal 202, resp[0] - end - - def assert_missing(resp) - assert_equal 404, resp[0] - end - - def assert_header(key, value, resp) - assert_equal value, resp[1][key.to_s] - end - - def assert_body(expected, resp) - assert_equal expected, extract_body(resp) - end end module Generation @@ -123,22 +112,57 @@ module TestHelpers end end - File.open("#{app_path}/config/database.yml", "w") do |f| - f.puts <<-YAML - default: &default - adapter: sqlite3 - pool: 5 - timeout: 5000 - development: - <<: *default - database: db/development.sqlite3 - test: - <<: *default - database: db/test.sqlite3 - production: - <<: *default - database: db/production.sqlite3 - YAML + if options[:multi_db] + File.open("#{app_path}/config/database.yml", "w") do |f| + f.puts <<-YAML + default: &default + adapter: sqlite3 + pool: 5 + timeout: 5000 + development: + primary: + <<: *default + database: db/development.sqlite3 + animals: + <<: *default + database: db/development_animals.sqlite3 + migrations_paths: db/animals_migrate + test: + primary: + <<: *default + database: db/test.sqlite3 + animals: + <<: *default + database: db/test_animals.sqlite3 + migrations_paths: db/animals_migrate + production: + primary: + <<: *default + database: db/production.sqlite3 + animals: + <<: *default + database: db/production_animals.sqlite3 + migrations_paths: db/animals_migrate + YAML + end + else + File.open("#{app_path}/config/database.yml", "w") do |f| + f.puts <<-YAML + default: &default + adapter: sqlite3 + pool: 5 + timeout: 5000 + development: + <<: *default + database: db/development.sqlite3 + test: + <<: *default + database: db/test.sqlite3 + production: + <<: *default + database: db/production.sqlite3 + YAML + end end add_to_config <<-RUBY @@ -153,6 +177,7 @@ module TestHelpers def teardown_app ENV["RAILS_ENV"] = @prev_rails_env if @prev_rails_env + FileUtils.rm_rf(tmp_path) end # Make a very basic app, without creating the whole directory structure. @@ -235,7 +260,7 @@ module TestHelpers # Invoke a bin/rails command inside the app # - # allow_failures:: true to return normally if the command exits with + # allow_failure:: true to return normally if the command exits with # a non-zero status. By default, this method will raise. # stderr:: true to pass STDERR output straight to the "real" STDERR. # By default, the STDERR and STDOUT of the process will be @@ -357,10 +382,12 @@ module TestHelpers end def app_file(path, contents, mode = "w") - FileUtils.mkdir_p File.dirname("#{app_path}/#{path}") - File.open("#{app_path}/#{path}", mode) do |f| + file_name = "#{app_path}/#{path}" + FileUtils.mkdir_p File.dirname(file_name) + File.open(file_name, mode) do |f| f.puts contents end + file_name end def remove_file(path) @@ -380,6 +407,21 @@ module TestHelpers $:.reject! { |path| path =~ %r'/(#{to_remove.join('|')})/' } end + + def use_postgresql + File.open("#{app_path}/config/database.yml", "w") do |f| + f.puts <<-YAML + default: &default + adapter: postgresql + pool: 5 + database: railties_test + development: + <<: *default + test: + <<: *default + YAML + end + end end end @@ -388,6 +430,10 @@ class ActiveSupport::TestCase include TestHelpers::Rack include TestHelpers::Generation include ActiveSupport::Testing::Stream + + def frozen_error_class + Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError + end end # Create a scope and build a fixture rails app diff --git a/railties/test/minitest/rails_plugin_test.rb b/railties/test/minitest/rails_plugin_test.rb new file mode 100644 index 0000000000..7c3a2022a9 --- /dev/null +++ b/railties/test/minitest/rails_plugin_test.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class Minitest::RailsPluginTest < ActiveSupport::TestCase + setup do + @options = Minitest.process_args [] + @output = StringIO.new("".encode("UTF-8")) + end + + test "default reporters are replaced" do + with_reporter Minitest::CompositeReporter.new do |reporter| + reporter << Minitest::SummaryReporter.new(@output, @options) + reporter << Minitest::ProgressReporter.new(@output, @options) + reporter << Minitest::Reporter.new(@output, @options) + + Minitest.plugin_rails_init({}) + + assert_equal 3, reporter.reporters.count + assert reporter.reporters.any? { |candidate| candidate.kind_of?(Minitest::SuppressedSummaryReporter) } + assert reporter.reporters.any? { |candidate| candidate.kind_of?(::Rails::TestUnitReporter) } + assert reporter.reporters.any? { |candidate| candidate.kind_of?(Minitest::Reporter) } + end + end + + test "no custom reporters are added if nothing to replace" do + with_reporter Minitest::CompositeReporter.new do |reporter| + Minitest.plugin_rails_init({}) + + assert_empty reporter.reporters + end + end + + private + def with_reporter(reporter) + old_reporter, Minitest.reporter = Minitest.reporter, reporter + + yield reporter + ensure + Minitest.reporter = old_reporter + end +end diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb index 854b4448a4..9f5bb37c20 100644 --- a/railties/test/paths_test.rb +++ b/railties/test/paths_test.rb @@ -104,7 +104,7 @@ class PathsTest < ActiveSupport::TestCase File.stub(:exist?, true) do @root.add "app", with: "/app" @root["app"].autoload_once! - assert @root["app"].autoload_once? + assert_predicate @root["app"], :autoload_once? assert_includes @root.autoload_once, @root["app"].expanded.first end end @@ -112,17 +112,17 @@ class PathsTest < ActiveSupport::TestCase test "it is possible to remove a path that should be autoloaded only once" do @root["app"] = "/app" @root["app"].autoload_once! - assert @root["app"].autoload_once? + assert_predicate @root["app"], :autoload_once? @root["app"].skip_autoload_once! - assert !@root["app"].autoload_once? + assert_not_predicate @root["app"], :autoload_once? assert_not_includes @root.autoload_once, @root["app"].expanded.first end test "it is possible to add a path without assignment and specify it should be loaded only once" do File.stub(:exist?, true) do @root.add "app", with: "/app", autoload_once: true - assert @root["app"].autoload_once? + assert_predicate @root["app"], :autoload_once? assert_includes @root.autoload_once, "/app" end end @@ -130,7 +130,7 @@ class PathsTest < ActiveSupport::TestCase test "it is possible to add multiple paths without assignment and specify it should be loaded only once" do File.stub(:exist?, true) do @root.add "app", with: ["/app", "/app2"], autoload_once: true - assert @root["app"].autoload_once? + assert_predicate @root["app"], :autoload_once? assert_includes @root.autoload_once, "/app" assert_includes @root.autoload_once, "/app2" end @@ -158,7 +158,7 @@ class PathsTest < ActiveSupport::TestCase File.stub(:exist?, true) do @root["app"] = "/app" @root["app"].eager_load! - assert @root["app"].eager_load? + assert_predicate @root["app"], :eager_load? assert_includes @root.eager_load, @root["app"].to_a.first end end @@ -166,17 +166,17 @@ class PathsTest < ActiveSupport::TestCase test "it is possible to skip a path from eager loading" do @root["app"] = "/app" @root["app"].eager_load! - assert @root["app"].eager_load? + assert_predicate @root["app"], :eager_load? @root["app"].skip_eager_load! - assert !@root["app"].eager_load? + assert_not_predicate @root["app"], :eager_load? assert_not_includes @root.eager_load, @root["app"].to_a.first end test "it is possible to add a path without assignment and mark it as eager" do File.stub(:exist?, true) do @root.add "app", with: "/app", eager_load: true - assert @root["app"].eager_load? + assert_predicate @root["app"], :eager_load? assert_includes @root.eager_load, "/app" end end @@ -184,7 +184,7 @@ class PathsTest < ActiveSupport::TestCase test "it is possible to add multiple paths without assignment and mark them as eager" do File.stub(:exist?, true) do @root.add "app", with: ["/app", "/app2"], eager_load: true - assert @root["app"].eager_load? + assert_predicate @root["app"], :eager_load? assert_includes @root.eager_load, "/app" assert_includes @root.eager_load, "/app2" end @@ -193,8 +193,8 @@ class PathsTest < ActiveSupport::TestCase test "it is possible to create a path without assignment and mark it both as eager and load once" do File.stub(:exist?, true) do @root.add "app", with: "/app", eager_load: true, autoload_once: true - assert @root["app"].eager_load? - assert @root["app"].autoload_once? + assert_predicate @root["app"], :eager_load? + assert_predicate @root["app"], :autoload_once? assert_includes @root.eager_load, "/app" assert_includes @root.autoload_once, "/app" end @@ -254,7 +254,7 @@ class PathsTest < ActiveSupport::TestCase test "a path can be added to the load path on creation" do File.stub(:exist?, true) do @root.add "app", with: "/app", load_path: true - assert @root["app"].load_path? + assert_predicate @root["app"], :load_path? assert_equal ["/app"], @root.load_paths end end @@ -271,7 +271,7 @@ class PathsTest < ActiveSupport::TestCase test "a path can be marked as autoload on creation" do File.stub(:exist?, true) do @root.add "app", with: "/app", autoload: true - assert @root["app"].autoload? + assert_predicate @root["app"], :autoload? assert_equal ["/app"], @root.autoload_paths end end diff --git a/railties/test/rack_logger_test.rb b/railties/test/rack_logger_test.rb index e47f30d5b6..6e8f333e1d 100644 --- a/railties/test/rack_logger_test.rb +++ b/railties/test/rack_logger_test.rb @@ -14,14 +14,21 @@ module Rails attr_reader :logger - def initialize(logger = NULL, taggers = nil, &block) - super(->(_) { block.call; [200, {}, []] }, taggers) + def initialize(logger = NULL, app: nil, taggers: nil, &block) + app ||= ->(_) { block.call; [200, {}, []] } + super(app, taggers) @logger = logger end def development?; false; end end + class TestApp < Struct.new(:response) + def call(_env) + response + end + end + Subscriber = Struct.new(:starts, :finishes) do def initialize(starts = [], finishes = []) super @@ -72,6 +79,17 @@ module Rails end end end + + def test_logger_does_not_mutate_app_return + response = [].freeze + app = TestApp.new(response) + logger = TestLogger.new(app: app) + assert_no_changes("response") do + assert_nothing_raised do + logger.call("REQUEST_METHOD" => "GET") + end + end + end end end end diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb index 227a739b71..50522c1be6 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -2,27 +2,14 @@ require "abstract_unit" -unless defined?(Rails) && defined?(Rails::Info) - module Rails - class Info; end - end -end - -require "active_support/core_ext/kernel/reporting" - class InfoTest < ActiveSupport::TestCase - def setup - Rails.send :remove_const, :Info - silence_warnings { load "rails/info.rb" } - end - def test_property_with_block_swallows_exceptions_and_ignores_property assert_nothing_raised do Rails::Info.module_eval do property("Bogus") { raise } end end - assert !property_defined?("Bogus") + assert_not property_defined?("Bogus") end def test_property_with_string diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index e6964b4b18..9a3ddc8d5e 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -32,6 +32,11 @@ module RailtiesTest require "#{app_path}/config/environment" end + def migrations + migration_root = File.expand_path(ActiveRecord::Migrator.migrations_paths.first, app_path) + ActiveRecord::MigrationContext.new(migration_root).migrations + end + test "serving sprocket's assets" do @plugin.write "app/assets/javascripts/engine.js.erb", "<%= :alert %>();" add_to_env_config "development", "config.assets.digest = false" @@ -82,31 +87,32 @@ module RailtiesTest end RUBY - add_to_config "ActiveRecord::Base.timestamped_migrations = false" - boot_rails Dir.chdir(app_path) do + # Install Active Storage migration file first so as not to affect test. + `bundle exec rake active_storage:install` output = `bundle exec rake bukkits:install:migrations` - assert File.exist?("#{app_path}/db/migrate/2_create_users.bukkits.rb") - assert File.exist?("#{app_path}/db/migrate/3_add_last_name_to_users.bukkits.rb") - assert_match(/Copied migration 2_create_users\.bukkits\.rb from bukkits/, output) - assert_match(/Copied migration 3_add_last_name_to_users\.bukkits\.rb from bukkits/, output) - assert_match(/NOTE: Migration 3_create_sessions\.rb from bukkits has been skipped/, output) - assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length - - output = `bundle exec rake railties:install:migrations`.split("\n") + ["CreateUsers", "AddLastNameToUsers", "CreateSessions"].each do |migration_name| + assert migrations.detect { |migration| migration.name == migration_name } + end + assert_match(/Copied migration \d+_create_users\.bukkits\.rb from bukkits/, output) + assert_match(/Copied migration \d+_add_last_name_to_users\.bukkits\.rb from bukkits/, output) + assert_match(/NOTE: Migration \d+_create_sessions\.rb from bukkits has been skipped/, output) - assert_no_match(/2_create_users/, output.join("\n")) + migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length - bukkits_migration_order = output.index(output.detect { |o| /NOTE: Migration 3_create_sessions\.rb from bukkits has been skipped/ =~ o }) - assert_not_nil bukkits_migration_order, "Expected migration to be skipped" + assert_equal migrations.length, migrations_count - migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length - `bundle exec rake railties:install:migrations` + output = `bundle exec rake railties:install:migrations`.split("\n") assert_equal migrations_count, Dir["#{app_path}/db/migrate/*.rb"].length + + assert_no_match(/\d+_create_users/, output.join("\n")) + + bukkits_migration_order = output.index(output.detect { |o| /NOTE: Migration \d+_create_sessions\.rb from bukkits has been skipped/ =~ o }) + assert_not_nil bukkits_migration_order, "Expected migration to be skipped" end end @@ -171,10 +177,12 @@ module RailtiesTest boot_rails Dir.chdir(app_path) do + # Install Active Storage migration file first so as not to affect test. + `bundle exec rake active_storage:install` output = `bundle exec rake railties:install:migrations`.split("\n") - assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.second) - assert_match(/Copied migration \d+_create_keys\.api_engine\.rb from api_engine/, output.last) + assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.first) + assert_match(/Copied migration \d+_create_keys\.api_engine\.rb from api_engine/, output.second) end end @@ -203,9 +211,12 @@ module RailtiesTest Dir.chdir(@plugin.path) do output = `bundle exec rake app:bukkits:install:migrations` - assert File.exist?("#{app_path}/db/migrate/0_add_first_name_to_users.bukkits.rb") - assert_match(/Copied migration 0_add_first_name_to_users\.bukkits\.rb from bukkits/, output) - assert_equal 1, Dir["#{app_path}/db/migrate/*.rb"].length + + migration_with_engine_path = migrations.detect { |migration| migration.name == "AddFirstNameToUsers" } + assert migration_with_engine_path + assert_match(/\/db\/migrate\/\d+_add_first_name_to_users\.bukkits\.rb/, migration_with_engine_path.filename) + assert_match(/Copied migration \d+_add_first_name_to_users\.bukkits\.rb from bukkits/, output) + assert_equal migrations.length, Dir["#{app_path}/db/migrate/*.rb"].length end end @@ -215,7 +226,7 @@ module RailtiesTest require "rdoc/task" require "rake/testtask" Rails.application.load_tasks - assert !Rake::Task.task_defined?("bukkits:install:migrations") + assert_not Rake::Task.task_defined?("bukkits:install:migrations") end test "puts its lib directory on load path" do @@ -734,7 +745,7 @@ YAML assert_equal "bukkits", Bukkits::Engine.engine_name assert_equal Bukkits.railtie_namespace, Bukkits::Engine assert ::Bukkits::MyMailer.method_defined?(:foo_url) - assert !::Bukkits::MyMailer.method_defined?(:bar_url) + assert_not ::Bukkits::MyMailer.method_defined?(:bar_url) get("/bukkits/from_app") assert_equal "false", last_response.body @@ -969,14 +980,14 @@ YAML boot_rails app_generators = Rails.application.config.generators.options[:rails] - assert_equal :mongoid , app_generators[:orm] - assert_equal :liquid , app_generators[:template_engine] + assert_equal :mongoid, app_generators[:orm] + assert_equal :liquid, app_generators[:template_engine] assert_equal :test_unit, app_generators[:test_framework] generators = Bukkits::Engine.config.generators.options[:rails] assert_equal :data_mapper, generators[:orm] - assert_equal :haml , generators[:template_engine] - assert_equal :rspec , generators[:test_framework] + assert_equal :haml, generators[:template_engine] + assert_equal :rspec, generators[:test_framework] end test "engine should get default generators with ability to overwrite them" do @@ -992,10 +1003,10 @@ YAML generators = Bukkits::Engine.config.generators.options[:rails] assert_equal :active_record, generators[:orm] - assert_equal :rspec , generators[:test_framework] + assert_equal :rspec, generators[:test_framework] app_generators = Rails.application.config.generators.options[:rails] - assert_equal :test_unit , app_generators[:test_framework] + assert_equal :test_unit, app_generators[:test_framework] end test "do not create table_name_prefix method if it already exists" do @@ -1468,6 +1479,21 @@ YAML assert_equal "/fruits/2/bukkits/posts", last_response.body end + test "active_storage:install task works within engine" do + @plugin.write "Rakefile", <<-RUBY + APP_RAKEFILE = '#{app_path}/Rakefile' + load 'rails/tasks/engine.rake' + RUBY + + Dir.chdir(@plugin.path) do + output = `bundle exec rake app:active_storage:install` + assert $?.success?, output + + active_storage_migration = migrations.detect { |migration| migration.name == "CreateActiveStorageTables" } + assert active_storage_migration + end + end + private def app Rails.application diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb index 359ab0fdae..7c3d1e3759 100644 --- a/railties/test/railties/railtie_test.rb +++ b/railties/test/railties/railtie_test.rb @@ -65,7 +65,7 @@ module RailtiesTest test "railtie can add to_prepare callbacks" do $to_prepare = false class Foo < Rails::Railtie ; config.to_prepare { $to_prepare = true } ; end - assert !$to_prepare + assert_not $to_prepare require "#{app_path}/config/environment" require "rack/test" extend Rack::Test::Methods @@ -91,7 +91,7 @@ module RailtiesTest test "railtie can add after_initialize callbacks" do $after_initialize = false class Foo < Rails::Railtie ; config.after_initialize { $after_initialize = true } ; end - assert !$after_initialize + assert_not $after_initialize require "#{app_path}/config/environment" assert $after_initialize end @@ -107,7 +107,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block require "rake" require "rake/testtask" require "rdoc/task" @@ -151,7 +151,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block Rails.application.load_generators assert $ran_block end @@ -167,7 +167,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block Rails.application.load_console assert $ran_block end @@ -183,7 +183,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block Rails.application.load_runner assert $ran_block end @@ -197,7 +197,7 @@ module RailtiesTest end end - assert !$ran_block + assert_not $ran_block require "#{app_path}/config/environment" assert $ran_block end diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb index 888fee173a..06877bc76a 100644 --- a/railties/test/secrets_test.rb +++ b/railties/test/secrets_test.rb @@ -1,20 +1,13 @@ # frozen_string_literal: true require "isolation/abstract_unit" -require "rails/generators" -require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" require "rails/secrets" class Rails::SecretsTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation - def setup - build_app - end - - def teardown - teardown_app - end + setup :build_app + teardown :teardown_app test "setting read to false skips parsing" do run_secrets_generator do @@ -172,9 +165,8 @@ class Rails::SecretsTest < ActiveSupport::TestCase private def run_secrets_generator Dir.chdir(app_path) do - capture(:stdout) do - Rails::Generators::EncryptedSecretsGenerator.start - end + File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") + File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") add_to_config <<-RUBY config.read_encrypted_secrets = true diff --git a/railties/test/test_unit/reporter_test.rb b/railties/test/test_unit/reporter_test.rb index ad852d0f35..91cb47779b 100644 --- a/railties/test/test_unit/reporter_test.rb +++ b/railties/test/test_unit/reporter_test.rb @@ -163,7 +163,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase end def failed_test - ft = ExampleTest.new(:woot) + ft = Minitest::Result.from(ExampleTest.new(:woot)) ft.failures << begin raise Minitest::Assertion, "boo" rescue Minitest::Assertion => e @@ -176,17 +176,17 @@ class TestUnitReporterTest < ActiveSupport::TestCase error = ArgumentError.new("wups") error.set_backtrace([ "some_test.rb:4" ]) - et = ExampleTest.new(:woot) + et = Minitest::Result.from(ExampleTest.new(:woot)) et.failures << Minitest::UnexpectedError.new(error) et end def passing_test - ExampleTest.new(:woot) + Minitest::Result.from(ExampleTest.new(:woot)) end def skipped_test - st = ExampleTest.new(:woot) + st = Minitest::Result.from(ExampleTest.new(:woot)) st.failures << begin raise Minitest::Skip, "skipchurches, misstemples" rescue Minitest::Assertion => e |