diff options
author | Kasper Timm Hansen <kaspth@gmail.com> | 2018-03-04 20:52:48 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-04 20:52:48 +0100 |
commit | f4ddbff71c3f2171ad0bd7cb73e44ebf3a4ca245 (patch) | |
tree | 73fcf6444192b5a944954cba67effb17c60a7ba4 | |
parent | 43dd8478b4aeb2fc40990a511b30b274a83e2556 (diff) | |
parent | a951729f45c579aa17b76a73f72880f90df9e910 (diff) | |
download | rails-f4ddbff71c3f2171ad0bd7cb73e44ebf3a4ca245.tar.gz rails-f4ddbff71c3f2171ad0bd7cb73e44ebf3a4ca245.tar.bz2 rails-f4ddbff71c3f2171ad0bd7cb73e44ebf3a4ca245.zip |
Merge pull request #32058 from gsamokovarov/rails-server-x-option
Introduce explicit rails server handler option
-rw-r--r-- | railties/CHANGELOG.md | 18 | ||||
-rw-r--r-- | railties/lib/rails/command.rb | 1 | ||||
-rw-r--r-- | railties/lib/rails/command/behavior.rb | 40 | ||||
-rw-r--r-- | railties/lib/rails/command/spellchecker.rb | 53 | ||||
-rw-r--r-- | railties/lib/rails/commands/server/server_command.rb | 88 | ||||
-rw-r--r-- | railties/lib/rails/generators.rb | 2 | ||||
-rw-r--r-- | railties/test/command/spellchecker_test.rb | 10 | ||||
-rw-r--r-- | railties/test/commands/server_test.rb | 33 |
8 files changed, 184 insertions, 61 deletions
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index a38888afe5..335eb6c4e3 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,5 +1,23 @@ ## Rails 6.0.0.alpha (Unreleased) ## +* 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/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..59ccab4ea2 --- /dev/null +++ b/railties/lib/rails/command/spellchecker.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Rails + module Command + module Spellchecker # :nodoc: + class << self + def suggest(word, from:, count: 3) + from.sort_by { |w| levenshtein_distance(word, w) }.take(count) + 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/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index e546fe3e4b..44b26d3846 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -44,7 +44,6 @@ module Rails end def start - print_boot_information trap(:INT) { exit } create_tmp_directories setup_dev_caching @@ -52,9 +51,14 @@ module Rails 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] + yield + 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'}://#{host}:#{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,28 @@ 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) + server.start do + say "Exiting" unless options[:daemon] + end + else + say rack_server_suggestion(using) + end end end @@ -152,7 +170,7 @@ module Rails def server_options { user_supplied_options: user_supplied_options, - server: @server, + server: using, log_stdout: @log_stdout, Port: port, Host: host, @@ -226,7 +244,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 +256,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).map(&:inspect) + + <<~MSG + Could not find server "#{server}". Maybe you meant #{suggestions.first} or #{suggestions.second}? + 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..7248fbbc94 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -276,7 +276,7 @@ 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 = Rails::Command::Spellchecker.suggest(namespace.to_s, from: options, count: 3) suggestions.map! { |s| "'#{s}'" } msg = "Could not find generator '#{namespace}'. ".dup msg << "Maybe you meant #{ suggestions[0...-1].join(', ')} or #{suggestions[-1]}\n" diff --git a/railties/test/command/spellchecker_test.rb b/railties/test/command/spellchecker_test.rb new file mode 100644 index 0000000000..aff50a3e73 --- /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 %w(thin cgi puma), Rails::Command::Spellchecker.suggest("tin", from: %w(puma thin cgi)) + end +end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 33715ea75f..c84be439e4 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" or "cgi"/, 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) @@ -222,6 +240,13 @@ class Rails::ServerTest < ActiveSupport::TestCase 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 |