Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce explicit rails server handler option #32058

Merged
merged 2 commits into from Mar 4, 2018

Conversation

@gsamokovarov
Copy link
Contributor

@gsamokovarov gsamokovarov commented Feb 19, 2018

I mistype rails server production instead of rails server -e production expecting to lunch a server in the production environment
all the time. However, the signature of rails server --help is:

Usage:
  rails server [puma, thin etc] [options]

This means that the production argument is being interpreted as a Rack
server handler like Puma, Thin or Unicorn.

Should we argue for the rails server production? I'm not sure of the
reasons, but the rails console production behavior was deprecated in:
#29358, so parity with the existing
rails console production usage may not hold anymore.

In any case, this PR introduces an explicit option for the Rack servers
configuration. I call them --handlers (or -x for short, as -h is
already as the shortcut for --help) to avoid the rails server --server tantrum.

The new interface of rails server is:

Usage:
  rails server [handler] [options]

Options:
  -p, [--port=port]                        # Runs Rails on the specified port - defaults to 3000.
  -b, [--binding=IP]                       # Binds Rails to the specified IP - defaults to 'localhost' in development and '0.0.0.0' in other environments'.
  -c, [--config=file]                      # Uses a custom rackup configuration.
                                           # Default: config.ru
  -d, [--daemon], [--no-daemon]            # Runs server as a Daemon.
  -e, [--environment=name]                 # Specifies the environment to run this server under (development/test/production).
  -x, [--handler=name]                     # Specifies the Rack server to run the application (thin/puma/webrick).
  -P, [--pid=PID]                          # Specifies the PID file.
                                           # Default: tmp/pids/server.pid
  -C, [--dev-caching], [--no-dev-caching]  # Specifies whether to perform caching in development.
      [--early-hints], [--no-early-hints]  # Enables HTTP/2 early hints.

As a bonus, if you mistype the handler, you'll get an auto-correction
message:

$ rails s tin
Could not find handler 'tin'. Maybe you meant 'thin' or 'cgi'?
Run `rails server --help` for more options.
@rails-bot
Copy link

@rails-bot rails-bot commented Feb 19, 2018

r? @schneems

(@rails-bot has picked a reviewer for you, use r? to override)

@gsamokovarov
Copy link
Contributor Author

@gsamokovarov gsamokovarov commented Feb 19, 2018

The previous error was:

$ rails server production
Exiting
Traceback (most recent call last):
	36: from bin/rails:3:in `<main>'
	35: from bin/rails:3:in `load'
	34: from /Users/genadi/Development/bithost/dashboard.bithost.io/bin/spring:15:in `<top (required)>'
	33: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:70:in `require'
	32: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:70:in `require'
	31: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/spring-2.0.2/lib/spring/binstub.rb:31:in `<top (required)>'
	30: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/spring-2.0.2/lib/spring/binstub.rb:31:in `load'
	29: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/spring-2.0.2/bin/spring:49:in `<top (required)>'
	28: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/spring-2.0.2/lib/spring/client.rb:30:in `run'
	27: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/spring-2.0.2/lib/spring/client/command.rb:7:in `call'
	26: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/spring-2.0.2/lib/spring/client/rails.rb:28:in `call'
	25: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/spring-2.0.2/lib/spring/client/rails.rb:28:in `load'
	24: from /Users/genadi/Development/bithost/dashboard.bithost.io/bin/rails:9:in `<top (required)>'
	23: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0.rc1/lib/active_support/dependencies.rb:283:in `require'
	22: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0.rc1/lib/active_support/dependencies.rb:249:in `load_dependency'
	21: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0.rc1/lib/active_support/dependencies.rb:283:in `block in require'
	20: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/bootsnap-1.1.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:17:in `require'
	19: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/bootsnap-1.1.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:17:in `require'
	18: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/commands.rb:18:in `<main>'
	17: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/command.rb:46:in `invoke'
	16: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/command/base.rb:65:in `perform'
	15: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor.rb:387:in `dispatch'
	14: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor/invocation.rb:126:in `invoke_command'
	13: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor/command.rb:27:in `run'
	12: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/commands/server/server_command.rb:142:in `perform'
	11: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/commands/server/server_command.rb:142:in `tap'
	10: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/commands/server/server_command.rb:147:in `block in perform'
	 9: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/commands/server/server_command.rb:47:in `start'
	 8: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/commands/server/server_command.rb:76:in `print_boot_information'
	 7: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-5.2.0.rc1/lib/rails/commands/server/server_command.rb:105:in `use_puma?'
	 6: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/rack-2.0.4/lib/rack/server.rb:301:in `server'
	 5: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/rack-2.0.4/lib/rack/handler.rb:16:in `get'
	 4: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/rack-2.0.4/lib/rack/handler.rb:74:in `try_require'
	 3: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0.rc1/lib/active_support/dependencies.rb:283:in `require'
	 2: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0.rc1/lib/active_support/dependencies.rb:249:in `load_dependency'
	 1: from /Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0.rc1/lib/active_support/dependencies.rb:283:in `block in require'
/Users/genadi/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/bootsnap-1.1.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:19:in `require': cannot load such file -- rack/handler/production (LoadError)
Copy link
Member

@kaspth kaspth left a comment

👍 to adding the argument validation. But I don't understand why it follows that we should turn the server positional arg into an option?

railties/lib/rails/commands/server/server_command.rb Outdated

msg = "Could not find handler '#{handler}'. ".dup
msg << "Maybe you meant #{suggestions.first} or #{suggestions.second}?\n"
msg << "Run `rails server --help` for more options."

This comment has been minimized.

@kaspth

kaspth Feb 24, 2018
Member

We're on Ruby 2.3 now, so lets use the squiggly heredoc <<~

railties/lib/rails/commands/server/server_command.rb Outdated
HANDLERS = %w[cgi fastcgi webrick lsws scgi thin puma unicorn]

def self.message(handler)
suggestions = HANDLERS

This comment has been minimized.

@kaspth

kaspth Feb 24, 2018
Member

Think this might be clearer as:

suggestions = HANDLERS.sort_by { |h| levenshstein_distance(handler, h) }.map(&:inspect)

Don't think the first(2) makes sense since we use first and second below — there's only 8 things in the array, we aren't saving anything with it anyway.

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 25, 2018
Author Contributor

The only subtle difference with .map(&:inspect) is that the message contains double quotes instead of single ones.

railties/lib/rails/commands/server/server_command.rb Outdated

# Hard-coding a bunch of handlers here as we don't have a public way of
# querying them from the Rack::Handler registry.
HANDLERS = %w[cgi fastcgi webrick lsws scgi thin puma unicorn]

This comment has been minimized.

@kaspth

kaspth Feb 24, 2018
Member

I think we might prefer %w( … )

railties/lib/rails/commands/server/server_command.rb Outdated
@@ -122,6 +148,8 @@ class ServerCommand < Base # :nodoc:
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 :handler, aliases: "-x", type: :string,

This comment has been minimized.

@kaspth

kaspth Feb 24, 2018
Member

I know Rack uses handler, but it reads a tad too generic here for me — especially if the shorthand is -x. How about -r for runner? E.g. the thing to run the rails server.

This comment has been minimized.

@matthewd

matthewd Feb 24, 2018
Member

--using? Runner seems particularly problematic given rails runner.

This comment has been minimized.

@kaspth

kaspth Feb 24, 2018
Member

@matthewd yeah, definitely thought that -r is on sketchy ground because of that clash. I like --using. 👍

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 25, 2018
Author Contributor

--using sounds good to me too.

railties/test/commands/server_test.rb Outdated

def test_handler_mistype_in_positional_argument
run_command(["tink"])
assert_match(/Could not find handler 'tink'. Maybe you meant 'thin' or 'cgi'/, output)

This comment has been minimized.

@kaspth

kaspth Feb 24, 2018
Member

Don't think attr_reader :output and @output makes sense. We can just do:

assert_match(/Could not find handler 'tink'. Maybe you meant 'thin' or 'cgi'/, run_command(["tink"]))

Then it'll match our test/application/test_runner_test.rb tests too.

railties/test/commands/server_test.rb Outdated

def test_handler_mistype
run_command(["-x", "tin"])
assert_match(/Could not find handler 'tin'. Maybe you meant 'thin' or 'cgi'/, output)

This comment has been minimized.

@kaspth

kaspth Feb 24, 2018
Member

How deterministic is it that cgi will be the other option here? I don't think we'd want intermittent test failures here.

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 25, 2018
Author Contributor

The handlers list is hard-coded, as we don't have a clean way to query the handlers from rack/lib/rack/handler.rb. If the order of the HANDLERS doesn't change often, the test should be stable.

@matthewd
Copy link
Member

@matthewd matthewd commented Feb 24, 2018

@kaspth we removed the positional parameter for console in #29358; I don't see why we'd want to retain it for server but not console.

(Specifically, given that we can only load a handler that's available in the Gemfile, and that we automatically load the first handler that's available in the Gemfile, it seems pretty rare to me for people to have cause to name a handler at all.)

railties/lib/rails/commands/server/server_command.rb Outdated
.map! { |s| "'#{s}'" }

msg = "Could not find handler '#{handler}'. ".dup
msg << "Maybe you meant #{suggestions.first} or #{suggestions.second}?\n"

This comment has been minimized.

@matthewd

matthewd Feb 24, 2018
Member

AFAICS this could end up suggesting exactly what they typed, which seems not-good. If handler is in HANDLERS, maybe we should instead suggest that they're missing a Gemfile entry.

@kaspth
Copy link
Member

@kaspth kaspth commented Feb 24, 2018

@matthewd sure, but that was for the environment, no? I thought bin/rails dbconsole replicant -e production was still possible?

Anyway, you're right that passing it manually is probably a minority case, so lets go with the deprecation — and --using 👍

@gsamokovarov gsamokovarov force-pushed the gsamokovarov:rails-server-x-option branch Feb 25, 2018
@gsamokovarov
Copy link
Contributor Author

@gsamokovarov gsamokovarov commented Feb 25, 2018

I have changed the option to --using and deprecated the passing of the Rack server as a regular argument.

@gsamokovarov gsamokovarov force-pushed the gsamokovarov:rails-server-x-option branch Feb 25, 2018
@kaspth
Copy link
Member

@kaspth kaspth commented Feb 25, 2018

This is probably for another time, but… I just realized that now we're on 2.3+ we could leverage the DidYouMean gem's spellchecker: https://github.com/yuki24/did_you_mean/blob/4d4484940cdab27566538e6453ff428d39fbad7a/lib/did_you_mean/spell_checker.rb#L8

unless HANDLERS.include?(options[:using])
  DidYouMean::SpellChecker.new(HANDLERS).correct(options[:using])
end
irb(main):002:0> c = DidYouMean::SpellChecker.new(dictionary: [ 'cgi', 'puma' ])
=> #<DidYouMean::SpellChecker:0x00007fa055866ee0 @dictionary=["cgi", "puma"]>
irb(main):003:0> c.correct('cgy')
=> ["cgi"]
irb(main):004:0> c.correct('pumma')
=> ["puma"]

We could also remove Rails' Levenshtein distance method from other places.

Copy link
Member

@kaspth kaspth left a comment

Getting real close!

railties/lib/rails/commands/server/server_command.rb Outdated
Could not find server "#{handler}". Maybe you meant #{suggestions.first} or #{suggestions.second}?
Run `rails server --help` for more options.
MSG
end

This comment has been minimized.

@kaspth

kaspth Feb 25, 2018
Member

Doing argv validation seems like it should a concern of the command and not the server itself. Both include Rails::Command::Behavior and rails server --help seem to allude as much.

Could we move it to the command if we do this?

capturing_missing_handler do
  server.start
end

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 25, 2018
Author Contributor

Tried that as well – it's still messy because levenshtein_distance is a private class method:

        def suggest_rack_server(handler)
          if handler.in?(HANDLERS)
            <<~MSG
              Could not load server "#{handler}". Maybe you need the add it to the Gemfile?

                gem "#{handler}"

              Run `rails server --help` for more options.
            MSG
          else
            suggestions = HANDLERS.sort_by { |h| self.class.send(:levenshtein_distance, handler, h) }.map(&:inspect)

            <<~MSG
              Could not find server "#{handler}". Maybe you meant #{suggestions.first} or #{suggestions.second}?
              Run `rails server --help` for more options.
            MSG
          end
        end
railties/lib/rails/commands/server/server_command.rb Outdated
# 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]
unless missing_handler

This comment has been minimized.

@kaspth

kaspth Feb 25, 2018
Member

I'm guessing the stopgap to inlining the HandlerSuggestion module in the command is this unless. But I don't fully understand why we need it? Isn't it fine for the server to say Exiting in the off case that a handler is missing?

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 25, 2018
Author Contributor

Actually, the stopgap is the levenstein_distance method being private and defined in the Rails::Command::Behavior module which carries command specific helpers. Decided to at least isolate it. I'd love to revisit the levenshtein_distance usage or swap it for the algo in DidYouMean, but I'd like to do it in a follow-up change, as it will have to touch the generator commands as well.

The guard here is for my personal aesthetic reasons. 😅 This is the message with the trailing Exiting:

Could not load server "unicorn". Maybe you need the add it to the Gemfile?

  gem "unicorn"

Run `rails server --help` for more options.
Exiting

The Exiting line seems redundant to me, especially with "Run rails server --help for more options." as a final suggestion. Maybe the user mistyped an option and triggered this?

Can an option be to complicate the existing guard a bit and move the missing_handler check there?

    # ...
    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] || missing_handler
    end
railties/lib/rails/commands/server/server_command.rb Outdated
@@ -132,7 +170,17 @@ class ServerCommand < Base # :nodoc:
def initialize(args = [], local_options = {}, config = {})
@original_options = local_options
super
@server = self.args.shift
@server = if using

This comment has been minimized.

@kaspth

kaspth Feb 25, 2018
Member

Personally I'd probably extract the message into a method like below, but it's probably fine like this :)

@server = deprecate_positional_using(using) || options[:using]

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 25, 2018
Author Contributor

👍 I think I can extract it in a separate method.

railties/lib/rails/commands/server/server_command.rb Outdated
@@ -132,7 +170,17 @@ class ServerCommand < Base # :nodoc:
def initialize(args = [], local_options = {}, config = {})
@original_options = local_options
super
@server = self.args.shift
@server = if using
ActiveSupport::Deprecation.warn(<<-MSG.squish)

This comment has been minimized.

@kaspth

kaspth Feb 25, 2018
Member

Why not use <<~ here?

This comment has been minimized.

@matthewd

matthewd Feb 25, 2018
Member

It still needs the squish; deprecation messages should not be hard-wrapped on output.

railties/lib/rails/commands/server/server_command.rb Outdated
print_boot_information
trap(:INT) { exit }
create_tmp_directories
setup_dev_caching
log_to_stdout if options[:log_stdout]

super
rescue LoadError

This comment has been minimized.

@matthewd

matthewd Feb 25, 2018
Member

We should make sure to only rescue a LoadError for "rack/handler/#{options[:using]}"; if something else is wrong, the original exception will do a better job of describing the issue.

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 25, 2018
Author Contributor

Rack::Handler.get can raise either LoadError or NameError, if the Puma constant does not exist after the rack/handler/puma require, for example. I think I can override the Rack::Server#server method to isolate the error catch.

This comment has been minimized.

@y-yagi

y-yagi Feb 25, 2018
Member

Can not deal with this by explicitly calling Rack::Handler.get before starting server?
I am also concerned that other errors by LoadError will be wrapped by this.

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 26, 2018
Author Contributor

We can, I'll do the changes accordingly.

@gsamokovarov gsamokovarov force-pushed the gsamokovarov:rails-server-x-option branch Feb 25, 2018
@gsamokovarov
Copy link
Contributor Author

@gsamokovarov gsamokovarov commented Feb 25, 2018

@kaspth, I think I'll be able to get the printing out of the Rails::Server class and put it into the command. I had a couple of naive attempts but part of the printing needs to know about whether Rails::Server#server is puma and this crosses some boundaries, but I'm sure we can get it.

As for moving the handler suggestion logic entirely into the Rails::Command::ServerCommand it may not be as pretty because of the current Rails::Command::Behavior concern ahem, behavior 😅, that introduces the levenstein_distance method as private class method, which is pretty specific to the first place it was used.

railties/CHANGELOG.md Outdated

Now:

$ bin/rails dbconsole -u thin

This comment has been minimized.

@y-yagi

y-yagi Feb 25, 2018
Member

bin/rails server ?

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 26, 2018
Author Contributor

Good catch! 😅

railties/lib/rails/commands/server/server_command.rb Outdated
Run `rails server --help` for more options.
MSG
else
suggestions = HANDLERS.sort_by { |h| levenshtein_distance(handler, h) }.map(&:inspect)

This comment has been minimized.

@y-yagi

y-yagi Feb 25, 2018
Member

I'm curious. Is the user using the server option so much that mistyped check is needed?
I understand that mistakenly specify server as env. But will not it solve the necessity of option to specify server?

This comment has been minimized.

@gsamokovarov

gsamokovarov Mar 2, 2018
Author Contributor

@y-yagi doubt it's used often. This tries to give you more context on what actually happens when you pass a positional argument. I'm setting the suggestions here by suggestion (rim-shot). 😅 Anyway, I think it's quite easy to setup the suggestions and we can follow the example in more command options that accept a fixed set of values.

@gsamokovarov gsamokovarov force-pushed the gsamokovarov:rails-server-x-option branch Feb 26, 2018
@gsamokovarov
Copy link
Contributor Author

@gsamokovarov gsamokovarov commented Feb 26, 2018

Friends, I have moved the printing out of Rails::Server. Want to take another look? There are still some funky bits, but I think we're getting there.

railties/lib/rails/commands/server/server_command.rb Outdated
Dir.chdir(Rails.application.root)

server = Rails::Server.new(server_options)
handler = begin

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 26, 2018
Author Contributor

I can wrap this in a capturing method.

railties/lib/rails/commands/server/server_command.rb Outdated
return
end

begin

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 26, 2018
Author Contributor

Same here. The purpose of the Exiting message is to show up, for example, when you CTRL-C/terminate a running server.

railties/lib/rails/commands/server/server_command.rb Outdated

def print_boot_information(handler)
use_puma = handler.to_s == "Rack::Handler::Puma"
url = "on #{options[:SSLEnable] ? 'https' : 'http'}://#{host}:#{port}" unless use_puma

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 26, 2018
Author Contributor

url can be nil here, if we're not on Puma.

Copy link
Member

@kaspth kaspth left a comment

I really like where you took this in this round, Genadi! Hadn't thought of extracting all the printing to the command.

Also don't be afraid to say if you've had enough review rounds and would rather focus on wrapping it up. Then we'll find a way to cut it.

I've been using this case to ease back into the command infrastructure and what exactly the responsibility of a command should be. So if it feels like it's playing around a little bit — that's because it is! 😄

railties/lib/rails/commands/server/server_command.rb Outdated
def handler
server
rescue LoadError, NameError
raise MissingError

This comment has been minimized.

@kaspth

kaspth Feb 26, 2018
Member

How about:

def serveable? # :nodoc:
  server
  true
rescue LoadError, NameError
  false
end

Then in the command:

unless server.serveable?
  say HandlerSuggestion.message(using) # We ought to use `say` now that we're in the command.
  return
end

Hell, we could even get server.start to yield to a finalizer block when it exits. Then it'd be:

server.start do |exited_successfully|
  if exited_successfully
    say "Exiting" unless options[:daemon]
  else
    say HandlerSuggestion.message(using)
  end
end

This comment has been minimized.

@kaspth

kaspth Feb 26, 2018
Member

Ugh. The last suggestion won't work because of print_boot_information. Maybe there's still something useful we can dig out of the suggestion though? 😊

This comment has been minimized.

@gsamokovarov

gsamokovarov Mar 2, 2018
Author Contributor

Kasper, I have tried something similar in the last round. I wasn't able to extract all the params out of print_boot_information, because using can be nil, or empty if we coerce it, but that still won't give us a server name to print.

railties/lib/rails/commands/server/server_command.rb Outdated
Run `rails server --help` for more options.
MSG
else
suggestions = HANDLERS.sort_by { |h| levenshtein_distance(handler, h) }.map(&:inspect)

This comment has been minimized.

@kaspth

kaspth Feb 26, 2018
Member

If this being private is what stops us from removing this HandlerSuggestion then lets make it public. It's marked as :doc: anyway, so it's already public API.

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 27, 2018
Author Contributor

It's also a class method, so I'd have to call it through self.class. We go through lots of hoops to use it now, so I'll extract it in a Rails::Command::Spellchecker module as we already blew up the PR a bit. 😅

railties/lib/rails/commands/server/server_command.rb Outdated
url = "on #{ssl ? 'https' : 'http'}://#{host}:#{port}" unless use_puma

puts <<~MSG
=> Booting #{ActiveSupport::Inflector.demodulize(handler)}

This comment has been minimized.

@kaspth

kaspth Feb 26, 2018
Member

Shouldn't this just use using?

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 27, 2018
Author Contributor

=> Booting Puma, where using is usually puma. But yeah, we can save this argument pass.

railties/lib/rails/commands/server/server_command.rb Outdated

def print_boot_information(handler, ssl)
use_puma = handler.to_s == "Rack::Handler::Puma"
url = "on #{ssl ? 'https' : 'http'}://#{host}:#{port}" unless use_puma

This comment has been minimized.

@kaspth

kaspth Feb 26, 2018
Member

Let's let the server build this URL in a nodoc'ed served_url, so we can do server.served_url below.

Then we can keep use_puma?

@@ -132,27 +155,34 @@ class ServerCommand < Base # :nodoc:
def initialize(args = [], local_options = {}, config = {})
@original_options = local_options
super
@server = self.args.shift

This comment has been minimized.

@kaspth

kaspth Feb 26, 2018
Member

I think it would be better and clearer to do this than the @_using construct:

super

deprecate_positional_using if @using
@using ||= options[:using]

@log_stdout = 
@gsamokovarov
Copy link
Contributor Author

@gsamokovarov gsamokovarov commented Feb 27, 2018

Don't worry Kasper I'm having fun as well. 😀

railties/lib/rails/commands/server/server_command.rb Outdated
@@ -9,7 +9,7 @@
require "rails/dev_caching"

module Rails
class Server < ::Rack::Server
class Server < ::Rack::Server #:nodoc:

This comment has been minimized.

@gsamokovarov

gsamokovarov Feb 28, 2018
Author Contributor

This could technically be a backward incompatible change, but:

  1. I don't think we should list Rails::Server in the documentation at all.
  2. This change aims Rails 6, so it should be okay.

This comment has been minimized.

@kaspth

kaspth Mar 3, 2018
Member

  1. We have listed it in the past, so it's technically public API.
  2. Just because it's a major doesn't mean we should just make undue backwards incompatible changes willy-nilly.

I am learning towards the Rails::Server published as public API being a mistake and not being intended. I don't think any of the other Railties command helpers are public.

What does a GitHub search for Rails::Server say? It might corroborate the above 😊

This comment has been minimized.

@kaspth

kaspth Mar 3, 2018
Member

If we find there's no real usage of Rails::Server on GitHub, I'm 👍 on # :nodoc: (but do add the space so it matches the command nodoc 🤓)

@kaspth
kaspth approved these changes Mar 3, 2018
Copy link
Member

@kaspth kaspth left a comment

Really like how this turned out 👍

Feels right that the command handles the error printing and the server is more focused on its own boot.

Some minor nits left, then I'll merge.

server.start

if server.serveable?
print_boot_information(server.server, server.served_url)

This comment has been minimized.

@kaspth

kaspth Mar 3, 2018
Member

Let's put a newline after this.

railties/lib/rails/commands/server/server_command.rb Outdated
def rack_server_suggestion(server)
if server.in?(RACK_SERVERS)
<<~MSG
Could not load server "#{server}". Maybe you need the add it to the Gemfile?

This comment has been minimized.

@kaspth

kaspth Mar 3, 2018
Member

you need to

<<~MSG
Could not find server "#{server}". Maybe you meant #{suggestions.first} or #{suggestions.second}?
Run `rails server --help` for more options.
MSG

This comment has been minimized.

@kaspth

kaspth Mar 3, 2018
Member

For another time: I could totally see us enriching Thor's argument/option with a :suggestions to get messages like this for free. E.g.:

class_option :using, aliases: "-u", type: :string, suggestions: RACK_SERVERS,
  desc: "Specifies the Rack server used to run the application (thin/puma/webrick).", banner: :name
trap(:INT) { exit }
create_tmp_directories
setup_dev_caching
log_to_stdout if options[:log_stdout]

super
ensure
# The '-h' option calls exit before @options is set.
# If we call 'options' with it unset, we get double help banners.

This comment has been minimized.

@kaspth

kaspth Mar 3, 2018
Member

👍, dig that we got rid of these comments.

railties/lib/rails/commands/server/server_command.rb Outdated
@@ -9,7 +9,7 @@
require "rails/dev_caching"

module Rails
class Server < ::Rack::Server
class Server < ::Rack::Server #:nodoc:

This comment has been minimized.

@kaspth

kaspth Mar 3, 2018
Member

If we find there's no real usage of Rails::Server on GitHub, I'm 👍 on # :nodoc: (but do add the space so it matches the command nodoc 🤓)

@kaspth
Copy link
Member

@kaspth kaspth commented Mar 3, 2018

Oh, needs a commit squash and rebase too! Normally we'd want 1 commit, but I'll grant you a commit for the spellchecker change first, then one for the server changes after that if you want. 1 commit is fine too 👍

@gsamokovarov gsamokovarov force-pushed the gsamokovarov:rails-server-x-option branch 2 times, most recently Mar 4, 2018
I mistype `rails server production` instead of `rails server -e
production` expecting to lunch a server in the production environment
all the time. However, the signature of `rails server --help` is:

```
Usage:
  rails server [puma, thin etc] [options]
```

This means that the `production` argument is being interpreted as a Rack
server handler like Puma, Thin or Unicorn.

Should we argue for the `rails server production`? I'm not sure of the
reasons, but the `rails console production` behavior was deprecated in:
#29358, so parity with the existing
`rails console production` usage may not hold anymore.

In any case, this PR introduces an explicit option for the Rack servers
configuration. The option is called `--using` (or `-u` for short) to
avoid the `rails server --server` tantrum.

The new interface of `rails server` is:

```
Usage:
  rails server [using] [options]

Options:
  -p, [--port=port]                        # Runs Rails on the specified port - defaults to 3000.
  -b, [--binding=IP]                       # Binds Rails to the specified IP - defaults to 'localhost' in development and '0.0.0.0' in other environments'.
  -c, [--config=file]                      # Uses a custom rackup configuration.
                                           # Default: config.ru
  -d, [--daemon], [--no-daemon]            # Runs server as a Daemon.
  -e, [--environment=name]                 # Specifies the environment to run this server under (development/test/production).
  -u, [--using=name]                       # Specifies the Rack server used to run the application (thin/puma/webrick).
  -P, [--pid=PID]                          # Specifies the PID file.
                                           # Default: tmp/pids/server.pid
  -C, [--dev-caching], [--no-dev-caching]  # Specifies whether to perform caching in development.
      [--early-hints], [--no-early-hints]  # Enables HTTP/2 early hints.
```

As a bonus, if you mistype the server to use, you'll get an
auto-correction message:

```
$ rails s tin
Could not find handler "tin". Maybe you meant "thin" or "cgi"?
Run `rails server --help` for more options.
```
@gsamokovarov gsamokovarov force-pushed the gsamokovarov:rails-server-x-option branch to a951729 Mar 4, 2018
@gsamokovarov
Copy link
Contributor Author

@gsamokovarov gsamokovarov commented Mar 4, 2018

Kasper, I have searched for Rails::Server usage in GitHub, but the two keywords: rails and server yield a lot of uninteresting results for us. See https://help.github.com/articles/searching-code:

You can't use the following wildcard characters as part of your search query: . , : ; / \ ` ' " = * ! ? # $ & + ^ | ~ < > ( ) { } [ ]. The search will simply ignore these symbols.

This means that we cannot match Rails::Server exactly, because of the : character ignore and we can't be exactly sure whether it's used. I left the # :nodoc: only to the serveable? method.

@kaspth kaspth merged commit f4ddbff into rails:master Mar 4, 2018
1 of 2 checks passed
1 of 2 checks passed
continuous-integration/travis-ci/pr The Travis CI build could not complete due to an error
Details
codeclimate All good!
Details
@kaspth
Copy link
Member

@kaspth kaspth commented Mar 4, 2018

Fine with me! Really glad how this turned out 👏

@gsamokovarov
Copy link
Contributor Author

@gsamokovarov gsamokovarov commented Mar 4, 2018

Thanks for the review! 🙇‍♂️

@gsamokovarov gsamokovarov deleted the gsamokovarov:rails-server-x-option branch Mar 4, 2018
@kaspth
Copy link
Member

@kaspth kaspth commented Jul 7, 2018

Did some work on the server command a0061d2...1d97b8f and realized I forgot to backport this to 5-2-stable 🤭

y-yagi added a commit that referenced this pull request Jan 18, 2019
…mand"

This reverts commit fa791fb.

Reason: `server` argument was deprecated in Rails 6.0. Ref: #32058.
kaspth pushed a commit that referenced this pull request Mar 11, 2019
- Because just passing the server argument to this command is
  deprecated in #32058
@kunal6673
Copy link

@kunal6673 kunal6673 commented Aug 29, 2019

Recover all my page

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

7 participants
You can’t perform that action at this time.