diff options
Diffstat (limited to 'railties')
68 files changed, 1064 insertions, 486 deletions
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index a38888afe5..83a57a8c6a 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,4 +1,55 @@ -## Rails 6.0.0.alpha (Unreleased) ## +* Don't generate unused files in `app:update` task + + Skip the assets' initializer when sprockets isn't loaded. + + Skip `config/spring.rb` when spring isn't loaded. + + Skip yarn's contents when yarn integration isn't used. + + *Tsukuru Tanimichi* + +* Make the master.key file read-only for the owner upon generation on + POSIX-compliant systems. + + Previously: + + $ ls -l config/master.key + -rw-r--r-- 1 owner group 32 Jan 1 00:00 master.key + + Now: + + $ ls -l config/master.key + -rw------- 1 owner group 32 Jan 1 00:00 master.key + + Fixes #32604. + + *Jose Luis Duran* + +* Deprecate support for using the `HOST` environment to specify the server IP. + + The `BINDING` environment should be used instead. + + Fixes #29516. + + *Yuji Yaginuma* + +* Deprecate passing Rack server name as a regular argument to `rails server`. + + Previously: + + $ bin/rails server thin + + There wasn't an explicit option for the Rack server to use, now we have the + `--using` option with the `-u` short switch. + + Now: + + $ bin/rails server -u thin + + This change also improves the error message if a missing or mistyped rack + server is given. + + *Genadi Samokovarov* * Add "rails routes --expanded" option to output routes in expanded mode like "psql --expanded". Result looks like: 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 7193abbc33..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 diff --git a/railties/lib/rails/app_updater.rb b/railties/lib/rails/app_updater.rb index a076d082d5..a243968a39 100644 --- a/railties/lib/rails/app_updater.rb +++ b/railties/lib/rails/app_updater.rb @@ -21,12 +21,15 @@ module Rails private def generator_options options = { api: !!Rails.application.config.api_only, update: true } + options[:skip_yarn] = !File.exist?(Rails.root.join("bin", "yarn")) options[:skip_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[:skip_bootsnap] = !defined?(Bootsnap) + options[:skip_spring] = !defined?(Spring) options end end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index a9dee10981..e346d5cc3a 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -427,7 +427,7 @@ 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( ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 912faed3e4..bba573499d 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -166,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 @@ -241,7 +253,7 @@ module Rails end def annotations - SourceAnnotationExtractor::Annotation + Rails::SourceAnnotationExtractor::Annotation end def content_security_policy(&block) 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/command.rb b/railties/lib/rails/command.rb index 078e9f937f..6d99ac9936 100644 --- a/railties/lib/rails/command.rb +++ b/railties/lib/rails/command.rb @@ -11,6 +11,7 @@ module Rails module Command extend ActiveSupport::Autoload + autoload :Spellchecker autoload :Behavior autoload :Base 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/spellchecker.rb b/railties/lib/rails/command/spellchecker.rb new file mode 100644 index 0000000000..04485097fa --- /dev/null +++ b/railties/lib/rails/command/spellchecker.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module Rails + module Command + module Spellchecker # :nodoc: + class << self + def suggest(word, from:) + if defined?(DidYouMean::SpellChecker) + DidYouMean::SpellChecker.new(dictionary: from.map(&:to_s)).correct(word).first + else + from.sort_by { |w| levenshtein_distance(word, w) }.first + end + 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 + 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 8df548b5de..806b7de6d6 100644 --- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb +++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb @@ -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/routes/routes_command.rb b/railties/lib/rails/commands/routes/routes_command.rb index c4f3717095..b592a5212f 100644 --- a/railties/lib/rails/commands/routes/routes_command.rb +++ b/railties/lib/rails/commands/routes/routes_command.rb @@ -5,45 +5,33 @@ require "rails/command" module Rails module Command class RoutesCommand < Base # :nodoc: - class_option :controller, aliases: "-c", type: :string, desc: "Specifies the controller." - class_option :grep_pattern, aliases: "-g", type: :string, desc: "Specifies grep pattern." - class_option :expanded_format, aliases: "--expanded", type: :string, desc: "Turn on expanded format mode." - - no_commands do - def help - say "Usage: Print out all defined routes in match order, with names." - say "" - say "Target specific controller with -c option, or grep routes using -g option" - say "Use expanded format with --expanded option" - say "" - end - end + 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" - all_routes = Rails.application.routes.routes - inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes) - - if options.has_key?("expanded_format") - say inspector.format(ActionDispatch::Routing::ConsoleFormatter::Expanded.new, routes_filter) - else - say inspector.format(ActionDispatch::Routing::ConsoleFormatter::Sheet.new, routes_filter) - end + say inspector.format(formatter, routes_filter) end private + def inspector + ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes) + end - def routes_filter - if options.has_key?("controller") - { controller: options["controller"] } - elsif options.has_key?("grep_pattern") - options["grep_pattern"] + def formatter + if options.key?("expanded") + ActionDispatch::Routing::ConsoleFormatter::Expanded.new else - nil + 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/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index e546fe3e4b..77b6c1f65d 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -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,6 +129,8 @@ 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, @@ -132,19 +141,27 @@ module Rails 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 @@ -152,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, @@ -202,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 @@ -217,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 @@ -226,7 +253,7 @@ 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 @@ -238,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/generators.rb b/railties/lib/rails/generators.rb index 6a7175c02b..f8460bd4ee 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -276,12 +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) - suggestions.map! { |s| "'#{s}'" } - msg = "Could not find generator '#{namespace}'. ".dup - msg << "Maybe you meant #{ suggestions[0...-1].join(', ')} or #{suggestions[-1]}\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/app_base.rb b/railties/lib/rails/generators/app_base.rb index e1889979d7..f51542f3ec 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -299,7 +299,7 @@ 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.4.4"]] + 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] @@ -440,7 +440,7 @@ module Rails end def depend_on_bootsnap? - !options[:skip_bootsnap] && !options[:dev] + !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION) end def os_supports_listen_out_of_the_box? diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb index e2ea66415f..997602cb8c 100644 --- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb @@ -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/migration.rb b/railties/lib/rails/generators/migration.rb index 1cbccfe461..5081060895 100644 --- a/railties/lib/rails/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb @@ -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/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 395ac7ef2f..34067240d7 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -95,11 +95,9 @@ module Rails end def bin_when_updating - bin_yarn_exist = File.exist?("bin/yarn") - bin - if options[:api] && !bin_yarn_exist + if options[:skip_yarn] remove_file "bin/yarn" end end @@ -146,6 +144,10 @@ module Rails template "config/storage.yml" end + if options[:skip_sprockets] && !assets_config_exist + remove_file "config/initializers/assets.rb" + end + unless rack_cors_config_exist remove_file "config/initializers/cors.rb" end @@ -155,10 +157,6 @@ module Rails remove_file "config/initializers/cookies_serializer.rb" end - unless assets_config_exist - remove_file "config/initializers/assets.rb" - end - unless csp_config_exist remove_file "config/initializers/content_security_policy.rb" end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt index 23bb89f4ce..1567333023 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt @@ -23,7 +23,7 @@ ruby <%= "'#{RUBY_VERSION}'" -%> <% unless skip_active_storage? -%> # Use ActiveStorage variant -# gem 'mini_magick', '~> 4.8' +# gem 'image_processing', '~> 1.2' <% end -%> # Use Capistrano for deployment @@ -69,7 +69,7 @@ end <%- if depends_on_system_test? -%> group :test do # Adds support for Capybara system testing and selenium driver - gem 'capybara', '~> 2.15' + gem 'capybara', '>= 2.15' gem 'selenium-webdriver' # Easy installation and use of chromedriver to run system tests with Chrome gem 'chromedriver-helper' 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 a87649b50f..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 @@ -28,7 +28,7 @@ Rails.application.configure do 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? -%> @@ -60,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 926326b5bb..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 @@ -43,12 +43,12 @@ Rails.application.configure do # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX <%- 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_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 ff4c89219a..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 @@ -29,7 +29,7 @@ Rails.application.configure do config.action_controller.allow_forgery_protection = false <%- unless skip_active_storage? -%> - # Store uploaded files on the local file system in a temporary directory + # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test <%- end -%> @@ -45,6 +45,9 @@ Rails.application.configure do # 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/storage.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt index 1c0cde0b09..7207c75086 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt @@ -24,7 +24,6 @@ local: # 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.credentials.dig(:azure_storage, :storage_access_key) %> # container: your_container_name diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore.tt b/railties/lib/rails/generators/rails/app/templates/gitignore.tt index 2cd8335aba..4e114fb1d9 100644 --- a/railties/lib/rails/generators/rails/app/templates/gitignore.tt +++ b/railties/lib/rails/generators/rails/app/templates/gitignore.tt @@ -24,8 +24,11 @@ <% 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 diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt index c444f33b0f..19f0d7f202 100644 --- a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt +++ b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt @@ -1 +1 @@ -<%= RUBY_VERSION -%> +<%= "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" -%> diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb index 6e2495d45f..eb75e7e661 100644 --- a/railties/lib/rails/generators/rails/controller/controller_generator.rb +++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb @@ -20,10 +20,20 @@ module Rails 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/encryption_key_file/encryption_key_file_generator.rb b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb index 90068c678d..e2359e9ded 100644 --- 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 @@ -27,6 +27,7 @@ module Rails 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)) 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 a99ce914c0..1dae3cb6a5 100644 --- a/railties/lib/rails/generators/test_unit/job/job_generator.rb +++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb @@ -10,6 +10,11 @@ module TestUnit # :nodoc: def create_test_file 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/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb index 610d47a729..ab8331f31c 100644 --- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb @@ -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/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/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb index f8d3311156..b2d44d9b8e 100644 --- a/railties/lib/rails/ruby_version_check.rb +++ b/railties/lib/rails/ruby_version_check.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -if RUBY_VERSION < "2.4.1" && 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 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/annotations.rake b/railties/lib/rails/tasks/annotations.rake index 93bc66e2db..60bcdc5e1b 100644 --- a/railties/lib/rails/tasks/annotations.rake +++ b/railties/lib/rails/tasks/annotations.rake @@ -4,19 +4,19 @@ 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/framework.rake b/railties/lib/rails/tasks/framework.rake index 7dfcd14bd0..1a3711c446 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -40,7 +40,7 @@ namespace :app do namespace :update do require "rails/app_updater" - # desc "Update config/boot.rb from your current rails install" + # desc "Update config files from your current rails install" task :configs do Rails::AppUpdater.invoke_from_app_generator :create_boot_file Rails::AppUpdater.invoke_from_app_generator :update_config_files 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 2a41c29602..e46364ba8a 100644 --- a/railties/lib/rails/templates/rails/mailers/email.html.erb +++ b/railties/lib/rails/templates/rails/mailers/email.html.erb @@ -98,7 +98,7 @@ <dt>Format:</dt> <% if @email.multipart? %> <dd> - <select id="part" onchange="refreshBody();"> + <select id="part" onchange="refreshBody(false);"> <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> @@ -110,7 +110,7 @@ <% if I18n.available_locales.count > 1 %> <dt>Locale:</dt> <dd> - <select id="locale" onchange="refreshBody();"> + <select id="locale" onchange="refreshBody(true);"> <% I18n.available_locales.each do |locale| %> <option <%= I18n.locale == locale ? 'selected' : '' %> value="<%= locale_query(locale) %>"><%= locale %></option> <% end %> @@ -130,7 +130,7 @@ <% end %> <script> - function refreshBody() { + function refreshBody(reload) { var part_select = document.querySelector('select#part'); var locale_select = document.querySelector('select#locale'); var iframe = document.getElementsByName('messageBody')[0]; @@ -146,10 +146,13 @@ } iframe.contentWindow.location = fresh_location; - if (history.replaceState) { - 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); + 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); + + if (reload) { + location.href = state_to_replace; + } else if (history.replaceState) { window.history.replaceState({}, '', state_to_replace); } } diff --git a/railties/railties.gemspec b/railties/railties.gemspec index 1df8b1fe39..6fdb4648c2 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -34,7 +34,7 @@ Gem::Specification.new do |s| s.add_dependency "actionpack", version s.add_dependency "rake", ">= 0.8.7" - s.add_dependency "thor", ">= 0.18.1", "< 2.0" + s.add_dependency "thor", ">= 0.19.0", "< 2.0" s.add_dependency "method_source" s.add_development_dependency "actionview", version 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..4ca6d02b85 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -47,7 +47,7 @@ module ApplicationTests end def assert_no_file_exists(filename) - assert !File.exist?(filename), "#{filename} does exist" + assert_not File.exist?(filename), "#{filename} does exist" end test "assets routes have higher priority" do @@ -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_test.rb b/railties/test/application/configuration_test.rb index bd9b87467c..c2699006f6 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -361,7 +361,7 @@ module ApplicationTests end RUBY - assert !$prepared + assert_not $prepared app "development" @@ -576,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 @@ -1416,7 +1417,7 @@ 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 @@ -1509,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 @@ -1978,6 +1979,23 @@ module ApplicationTests assert_equal true, ActionView::Helpers::FormTagHelper.default_enforce_utf8 end + test "ActionView::Template.finalize_compiled_template_methods is true by default" do + app "test" + assert_equal true, ActionView::Template.finalize_compiled_template_methods + end + + test "ActionView::Template.finalize_compiled_template_methods can be configured via config.action_view.finalize_compiled_template_methods" do + app_file "config/environments/test.rb", <<-RUBY + Rails.application.configure do + config.action_view.finalize_compiled_template_methods = false + end + RUBY + + app "test" + + assert_equal false, ActionView::Template.finalize_compiled_template_methods + 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/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index e631318f82..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 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/paths_test.rb b/railties/test/application/paths_test.rb index 0abc5cc9aa..28a9206daa 100644 --- a/railties/test/application/paths_test.rb +++ b/railties/test/application/paths_test.rb @@ -37,7 +37,7 @@ module ApplicationTests end def assert_not_in_load_path(*path) - assert !$:.any? { |p| File.expand_path(p) == root(*path) }, "Load path includes '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----" + assert_not $:.any? { |p| File.expand_path(p) == root(*path) }, "Load path includes '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----" end test "booting up Rails yields a valid paths object" do diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 5b4c42c189..0594236b1f 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -34,7 +34,7 @@ module ApplicationTests 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 diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index bf5b07afbd..47c5ac105a 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -417,7 +417,7 @@ module ApplicationTests version = output =~ %r{[^/]+db/migrate/(\d+)_create_authors\.rb} && $1 rails "db:migrate", "db:rollback", "db:forward", "db:migrate:up", "db:migrate:down", "VERSION=#{version}" - assert !File.exist?("db/schema.rb"), "should not dump schema when configured not to" + assert_not File.exist?("db/schema.rb"), "should not dump schema when configured not to" end add_to_config("config.active_record.dump_schema_after_migration = true") 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 9683230d07..1522a2bbc5 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -41,7 +41,7 @@ module ApplicationTests rails "db:create", "db:migrate" output = rails("db:test:prepare", "test") - refute_match(/ActiveRecord::ProtectedEnvironmentError/, output) + assert_no_match(/ActiveRecord::ProtectedEnvironmentError/, output) end end @@ -229,7 +229,7 @@ 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 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/server_test.rb b/railties/test/application/server_test.rb index f3a7e00a4d..92b991dd05 100644 --- a/railties/test/application/server_test.rb +++ b/railties/test/application/server_test.rb @@ -36,7 +36,7 @@ module ApplicationTests 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 "ENV['BUNDLE_GEMFILE'] = '#{Bundler.default_gemfile}'" f.puts "require 'bundler/setup'" 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/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/routes_test.rb b/railties/test/commands/routes_test.rb index 24af5de73a..77ed2bda61 100644 --- a/railties/test/commands/routes_test.rb +++ b/railties/test/commands/routes_test.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true require "isolation/abstract_unit" -require "env_helpers" 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 "test singular resource output in rake routes" do + test "singular resource output in rails routes" do app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do resource :post @@ -29,7 +29,7 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase assert_equal expected_output, output end - test "test rails routes with global search key" do + test "rails routes with global search key" do app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do get '/cart', to: 'cart#show' @@ -40,19 +40,18 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase 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_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show - rails_blob_preview GET /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) active_storage/previews#show - rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + 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 + 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"]) @@ -60,32 +59,33 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase "basketballs GET /basketballs(.:format) basketball#index\n", output end - test "test rails routes with controller search_key" do + 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 + 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(["routes", "-c", "Cart"]) + 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 "test rails routes with namespaced controller search key" do + 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 + 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", @@ -102,72 +102,73 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase assert_equal expected_output, output end - test "test rails routes displays message when no routes are defined" do + test "rails routes displays message when no routes are defined" do app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - end + 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_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show - rails_blob_preview GET /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) active_storage/previews#show - rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show - update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update - rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + Prefix Verb URI Pattern Controller#Action + rails_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 "test rails routes with expanded option" do - app_file "config/routes.rb", <<-RUBY + 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 = rails("routes", "--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_variation - Verb | GET - URI | /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) - Controller#Action | active_storage/variants#show - --[ Route 4 ]------------------------------------------------------------ - Prefix | rails_blob_preview - Verb | GET - URI | /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) - Controller#Action | active_storage/previews#show - --[ Route 5 ]------------------------------------------------------------ - Prefix | rails_disk_service - Verb | GET - URI | /rails/active_storage/disk/:encoded_key/*filename(.:format) - Controller#Action | active_storage/disk#show - --[ Route 6 ]------------------------------------------------------------ - Prefix | update_rails_disk_service - Verb | PUT - URI | /rails/active_storage/disk/:encoded_token(.:format) - Controller#Action | active_storage/disk#update - --[ Route 7 ]------------------------------------------------------------ - Prefix | rails_direct_uploads - Verb | POST - URI | /rails/active_storage/direct_uploads(.:format) - Controller#Action | active_storage/direct_uploads#create - MESSAGE + 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 diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 33715ea75f..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,6 +22,24 @@ 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) @@ -35,7 +53,7 @@ class Rails::ServerTest < ActiveSupport::TestCase 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) @@ -72,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 @@ -178,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] @@ -197,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 @@ -221,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/generators/actions_test.rb b/railties/test/generators/actions_test.rb index f421207025..a54a6dbc28 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -403,7 +403,7 @@ class ActionsTest < Rails::Generators::TestCase content.gsub!(/^ \#.*\n/, "") content.gsub!(/^\n/, "") - File.open(route_path, "wb") { |file| file.write(content) } + File.write(route_path, content) routes = <<-F Rails.application.routes.draw do diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 1d2e0fd354..3cb7d66bbb 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -296,6 +296,51 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_app_update_does_not_generate_yarn_contents_when_bin_yarn_is_not_used + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-yarn"] + + stub_rails_application(app_root) do + generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_yarn: true }, { destination_root: app_root, shell: @shell } + generator.send(:app_const) + quietly { generator.send(:update_bin_files) } + + assert_no_file "#{app_root}/bin/yarn" + + assert_file "#{app_root}/bin/setup" do |content| + assert_no_match(/system\('bin\/yarn'\)/, content) + end + + assert_file "#{app_root}/bin/update" do |content| + assert_no_match(/system\('bin\/yarn'\)/, content) + end + end + end + + def test_app_update_does_not_generate_assets_initializer_when_skip_sprockets_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-sprockets"] + + stub_rails_application(app_root) do + generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_sprockets: true }, { destination_root: app_root, shell: @shell } + generator.send(:app_const) + quietly { generator.send(:update_config_files) } + + assert_no_file "#{app_root}/config/initializers/assets.rb" + end + end + + def test_app_update_does_not_generate_spring_contents_when_skip_spring_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-spring"] + + FileUtils.cd(app_root) do + quietly { system("bin/rails app:update") } + end + + assert_no_file "#{app_root}/config/spring.rb" + end + def test_app_update_does_not_generate_action_cable_contents_when_skip_action_cable_is_given app_root = File.join(destination_root, "myapp") run_generator [app_root, "--skip-action-cable"] @@ -310,18 +355,28 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_active_storage_mini_magick_gem + def test_app_update_does_not_generate_bootsnap_contents_when_skip_bootsnap_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-bootsnap"] + + FileUtils.cd(app_root) do + quietly { system("bin/rails app:update") } + end + + assert_file "#{app_root}/config/boot.rb" do |content| + assert_no_match(/require 'bootsnap\/setup'/, content) + end + end + + def test_gem_for_active_storage run_generator - assert_file "Gemfile", /^# gem 'mini_magick'/ + assert_file "Gemfile", /^# gem 'image_processing'/ end - def test_mini_magick_gem_when_skip_active_storage_is_given - app_root = File.join(destination_root, "myapp") - run_generator [app_root, "--skip-active-storage"] + def test_gem_for_active_storage_when_skip_active_storage_is_given + run_generator [destination_root, "--skip-active-storage"] - assert_file "#{app_root}/Gemfile" do |content| - assert_no_match(/gem 'mini_magick'/, content) - end + assert_no_gem "image_processing" end def test_app_update_does_not_generate_active_storage_contents_when_skip_active_storage_is_given @@ -408,13 +463,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.4.4'" + assert_gem "mysql2", "'>= 0.4.4', '< 0.6.0'" end end @@ -474,9 +529,7 @@ class AppGeneratorTest < Rails::Generators::TestCase 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 @@ -496,22 +549,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) - assert_no_match(/chromedriver-helper/, 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) - assert_no_match(/chromedriver-helper/, content) - end + assert_no_gem "capybara" + assert_no_gem "selenium-webdriver" + assert_no_gem "chromedriver-helper" assert_directory("test") @@ -557,10 +606,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) @@ -584,9 +631,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 @@ -737,9 +782,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 @@ -747,17 +790,13 @@ 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) - end + assert_no_gem "spring" end def test_webpack_option @@ -802,9 +841,7 @@ class AppGeneratorTest < Rails::Generators::TestCase 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 @@ -816,18 +853,23 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_bootsnap run_generator - assert_gem "bootsnap" - assert_file "config/boot.rb" do |content| - assert_match(/require 'bootsnap\/setup'/, content) + 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_file "Gemfile" do |content| - assert_no_match(/bootsnap/, content) - end + assert_no_gem "bootsnap" assert_file "config/boot.rb" do |content| assert_no_match(/require 'bootsnap\/setup'/, content) end @@ -836,9 +878,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_bootsnap_with_dev_option run_generator [destination_root, "--dev"] - assert_file "Gemfile" do |content| - assert_no_match(/bootsnap/, content) - end + assert_no_gem "bootsnap" assert_file "config/boot.rb" do |content| assert_no_match(/require 'bootsnap\/setup'/, content) end @@ -851,7 +891,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 @@ -937,8 +977,17 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_system_tests_directory_generated run_generator - assert_file("test/system/.keep") assert_directory("test/system") + assert_file("test/system/.keep") + 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 @@ -961,6 +1010,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" @@ -971,9 +1026,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/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 91e4a86775..021004c9b8 100644 --- a/railties/test/generators/controller_generator_test.rb +++ b/railties/test/generators/controller_generator_test.rb @@ -116,4 +116,26 @@ class ControllerGeneratorTest < Rails::Generators::TestCase 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/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/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 29426cd99f..3e631f6021 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -347,7 +347,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase content = File.read(route_path).gsub(/\.routes\.draw do/) do |match| "#{match} |map|" end - File.open(route_path, "wb") { |file| file.write(content) } + File.write(route_path, content) run_generator ["product_line"], behavior: :revoke @@ -364,7 +364,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase content.gsub!(/^ \#.*\n/, "") content.gsub!(/^\n/, "") - File.open(route_path, "wb") { |file| file.write(content) } + File.write(route_path, content) assert_file "config/routes.rb", /\.routes\.draw do\n resources :product_lines\nend\n\z/ run_generator ["product_line"], behavior: :revoke diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 9f1f2a2289..a16a2d3f0a 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -33,7 +33,7 @@ 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 @@ -43,7 +43,7 @@ class GeneratorsTest < Rails::Generators::TestCase I18n.default_locale = :ja 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 ensure I18n.available_locales = orig_available_locales I18n.default_locale = orig_default_locale @@ -52,7 +52,7 @@ class GeneratorsTest < Rails::Generators::TestCase 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 diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 0a4d2a9167..3cde7c03b0 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -112,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 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 43b60b9144..50522c1be6 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -9,7 +9,7 @@ class InfoTest < ActiveSupport::TestCase 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 a59c63f343..9a3ddc8d5e 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -226,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 @@ -745,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 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 |