railties 5.2.3.rc1 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of railties might be problematic. Click here for more details.

Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +190 -130
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.rdoc +35 -28
  5. data/README.rdoc +1 -1
  6. data/lib/minitest/rails_plugin.rb +6 -10
  7. data/lib/rails/all.rb +4 -0
  8. data/lib/rails/api/generator.rb +2 -1
  9. data/lib/rails/api/task.rb +16 -0
  10. data/lib/rails/app_loader.rb +1 -1
  11. data/lib/rails/app_updater.rb +3 -1
  12. data/lib/rails/application.rb +21 -45
  13. data/lib/rails/application/configuration.rb +54 -12
  14. data/lib/rails/application/default_middleware_stack.rb +2 -0
  15. data/lib/rails/backtrace_cleaner.rb +5 -17
  16. data/lib/rails/code_statistics.rb +3 -3
  17. data/lib/rails/command.rb +11 -10
  18. data/lib/rails/command/actions.rb +0 -10
  19. data/lib/rails/command/base.rb +1 -1
  20. data/lib/rails/command/behavior.rb +4 -46
  21. data/lib/rails/command/environment_argument.rb +1 -11
  22. data/lib/rails/command/spellchecker.rb +58 -0
  23. data/lib/rails/commands/credentials/USAGE +19 -1
  24. data/lib/rails/commands/credentials/credentials_command.rb +42 -23
  25. data/lib/rails/commands/db/system/change/change_command.rb +20 -0
  26. data/lib/rails/commands/dbconsole/dbconsole_command.rb +2 -2
  27. data/lib/rails/commands/dev/dev_command.rb +17 -0
  28. data/lib/rails/commands/encrypted/encrypted_command.rb +2 -3
  29. data/lib/rails/commands/help/help_command.rb +1 -1
  30. data/lib/rails/commands/initializers/initializers_command.rb +16 -0
  31. data/lib/rails/commands/new/new_command.rb +2 -2
  32. data/lib/rails/commands/notes/notes_command.rb +39 -0
  33. data/lib/rails/commands/plugin/plugin_command.rb +1 -1
  34. data/lib/rails/commands/routes/routes_command.rb +37 -0
  35. data/lib/rails/commands/runner/runner_command.rb +6 -6
  36. data/lib/rails/commands/secrets/USAGE +3 -3
  37. data/lib/rails/commands/secrets/secrets_command.rb +3 -3
  38. data/lib/rails/commands/server/server_command.rb +109 -48
  39. data/lib/rails/configuration.rb +1 -7
  40. data/lib/rails/engine/configuration.rb +3 -1
  41. data/lib/rails/gem_version.rb +4 -4
  42. data/lib/rails/generators.rb +11 -12
  43. data/lib/rails/generators/actions.rb +48 -37
  44. data/lib/rails/generators/app_base.rb +56 -94
  45. data/lib/rails/generators/app_name.rb +50 -0
  46. data/lib/rails/generators/database.rb +57 -0
  47. data/lib/rails/generators/erb/mailer/mailer_generator.rb +1 -1
  48. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +1 -1
  49. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +1 -1
  50. data/lib/rails/generators/generated_attribute.rb +17 -17
  51. data/lib/rails/generators/model_helpers.rb +8 -1
  52. data/lib/rails/generators/named_base.rb +1 -5
  53. data/lib/rails/generators/rails/app/app_generator.rb +37 -71
  54. data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -6
  55. data/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt +0 -3
  56. data/lib/rails/generators/rails/app/templates/app/{assets/javascripts/cable.js.tt → javascript/channels/consumer.js} +2 -9
  57. data/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js +5 -0
  58. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +15 -0
  59. data/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt +5 -0
  60. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +4 -4
  61. data/lib/rails/generators/rails/app/templates/bin/setup.tt +4 -5
  62. data/lib/rails/generators/rails/app/templates/bin/update.tt +6 -7
  63. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +2 -0
  64. data/lib/rails/generators/rails/app/templates/config/cable.yml.tt +1 -1
  65. data/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt +2 -2
  66. data/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt +2 -2
  67. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +2 -2
  68. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +3 -3
  69. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +3 -3
  70. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +4 -4
  71. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +2 -2
  72. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +6 -6
  73. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +2 -2
  74. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +3 -2
  75. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +5 -12
  76. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +6 -2
  77. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +1 -1
  78. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
  79. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt +33 -0
  80. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +1 -1
  81. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
  82. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +1 -1
  83. data/lib/rails/generators/rails/app/templates/config/spring.rb.tt +6 -6
  84. data/lib/rails/generators/rails/app/templates/gitignore.tt +2 -7
  85. data/lib/rails/generators/rails/app/templates/package.json.tt +7 -1
  86. data/lib/rails/generators/rails/app/templates/ruby-version.tt +1 -1
  87. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +11 -0
  88. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +7 -0
  89. data/lib/rails/generators/rails/assets/USAGE +1 -4
  90. data/lib/rails/generators/rails/assets/assets_generator.rb +0 -8
  91. data/lib/rails/generators/rails/controller/controller_generator.rb +11 -1
  92. data/lib/rails/generators/rails/credentials/credentials_generator.rb +7 -8
  93. data/lib/rails/generators/rails/db/system/change/change_generator.rb +55 -0
  94. data/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb +4 -5
  95. data/lib/rails/generators/rails/helper/helper_generator.rb +5 -0
  96. data/lib/rails/generators/rails/plugin/plugin_generator.rb +9 -18
  97. data/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt +1 -1
  98. data/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt +1 -1
  99. data/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt +1 -1
  100. data/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt +1 -1
  101. data/lib/rails/generators/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt +1 -1
  102. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +1 -1
  103. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt +1 -1
  104. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt +1 -1
  105. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +1 -2
  106. data/lib/rails/generators/resource_helpers.rb +1 -6
  107. data/lib/rails/generators/test_unit/integration/integration_generator.rb +6 -0
  108. data/lib/rails/generators/test_unit/job/job_generator.rb +5 -0
  109. data/lib/rails/generators/test_unit/mailer/mailer_generator.rb +1 -1
  110. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +1 -1
  111. data/lib/rails/generators/test_unit/system/system_generator.rb +5 -0
  112. data/lib/rails/generators/testing/behaviour.rb +3 -0
  113. data/lib/rails/info.rb +2 -2
  114. data/lib/rails/info_controller.rb +1 -1
  115. data/lib/rails/mailers_controller.rb +1 -1
  116. data/lib/rails/paths.rb +19 -9
  117. data/lib/rails/railtie.rb +1 -1
  118. data/lib/rails/ruby_version_check.rb +3 -3
  119. data/lib/rails/secrets.rb +0 -1
  120. data/lib/rails/source_annotation_extractor.rb +125 -117
  121. data/lib/rails/tasks/annotations.rake +9 -9
  122. data/lib/rails/tasks/dev.rake +5 -4
  123. data/lib/rails/tasks/framework.rake +5 -1
  124. data/lib/rails/tasks/initializers.rake +5 -4
  125. data/lib/rails/tasks/log.rake +0 -1
  126. data/lib/rails/tasks/routes.rake +4 -26
  127. data/lib/rails/tasks/statistics.rake +1 -0
  128. data/lib/rails/tasks/yarn.rake +1 -1
  129. data/lib/rails/templates/rails/welcome/index.html.erb +2 -2
  130. data/lib/rails/test_help.rb +11 -9
  131. data/lib/rails/test_unit/reporter.rb +1 -1
  132. data/lib/rails/test_unit/runner.rb +5 -5
  133. data/lib/rails/test_unit/testing.rake +1 -1
  134. metadata +27 -21
  135. data/lib/rails/commands/encrypted/USAGE +0 -28
  136. data/lib/rails/generators/js/assets/assets_generator.rb +0 -15
  137. data/lib/rails/generators/js/assets/templates/javascript.js +0 -2
  138. data/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +0 -22
  139. data/lib/rails/generators/rails/app/templates/bin/bundle.tt +0 -2
  140. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt +0 -38
  141. data/lib/rails/generators/rails/assets/templates/javascript.js +0 -2
@@ -75,7 +75,7 @@ module Rails
75
75
  args += ["-P", "#{config['password']}"] if config["password"]
76
76
 
77
77
  if config["host"]
78
- host_arg = "#{config['host']}".dup
78
+ host_arg = +"#{config['host']}"
79
79
  host_arg << ":#{config['port']}" if config["port"]
80
80
  args += ["-S", host_arg]
81
81
  end
@@ -97,7 +97,7 @@ module Rails
97
97
  elsif configurations[environment].blank? && configurations[connection].blank?
98
98
  raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
99
99
  else
100
- configurations[environment].presence || configurations[connection]
100
+ configurations[connection] || configurations[environment].presence
101
101
  end
102
102
  end
103
103
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/dev_caching"
4
+
5
+ module Rails
6
+ module Command
7
+ class DevCommand < Base # :nodoc:
8
+ def help
9
+ say "rails dev:cache # Toggle development mode caching on/off."
10
+ end
11
+
12
+ def cache
13
+ Rails::DevCaching.enable_by_file
14
+ end
15
+ end
16
+ end
17
+ end
@@ -16,7 +16,6 @@ module Rails
16
16
  def help
17
17
  say "Usage:\n #{self.class.banner}"
18
18
  say ""
19
- say self.class.desc
20
19
  end
21
20
  end
22
21
 
@@ -77,9 +76,9 @@ module Rails
77
76
 
78
77
  def missing_encrypted_message(key:, key_path:, file_path:)
79
78
  if key.nil?
80
- "Missing '#{key_path}' to decrypt data. See bin/rails encrypted:help"
79
+ "Missing '#{key_path}' to decrypt data. See `rails encrypted:help`"
81
80
  else
82
- "File '#{file_path}' does not exist. Use bin/rails encrypted:edit #{file_path} to change that."
81
+ "File '#{file_path}' does not exist. Use `rails encrypted:edit #{file_path}` to change that."
83
82
  end
84
83
  end
85
84
  end
@@ -6,7 +6,7 @@ module Rails
6
6
  hide_command!
7
7
 
8
8
  def help(*)
9
- puts self.class.desc
9
+ say self.class.desc
10
10
 
11
11
  Rails::Command.print_commands
12
12
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Command
5
+ class InitializersCommand < Base # :nodoc:
6
+ desc "initializers", "Print out all defined initializers in the order they are invoked by Rails."
7
+ def perform
8
+ require_application_and_environment!
9
+
10
+ Rails.application.initializers.tsort_each do |initializer|
11
+ say "#{initializer.context_class}.#{initializer.name}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -10,8 +10,8 @@ module Rails
10
10
  end
11
11
 
12
12
  def perform(*)
13
- puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
14
- puts "Type 'rails' for help."
13
+ say "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
14
+ say "Type 'rails' for help."
15
15
  exit 1
16
16
  end
17
17
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/source_annotation_extractor"
4
+
5
+ module Rails
6
+ module Command
7
+ class NotesCommand < Base # :nodoc:
8
+ class_option :annotations, aliases: "-a", desc: "Filter by specific annotations, e.g. Foobar TODO", type: :array, default: %w(OPTIMIZE FIXME TODO)
9
+
10
+ def perform(*)
11
+ require_application_and_environment!
12
+
13
+ deprecation_warning
14
+ display_annotations
15
+ end
16
+
17
+ private
18
+ def display_annotations
19
+ annotations = options[:annotations]
20
+ tag = (annotations.length > 1)
21
+
22
+ Rails::SourceAnnotationExtractor.enumerate annotations.join("|"), tag: tag, dirs: directories
23
+ end
24
+
25
+ def directories
26
+ Rails::SourceAnnotationExtractor::Annotation.directories + source_annotation_directories
27
+ end
28
+
29
+ def deprecation_warning
30
+ return if source_annotation_directories.empty?
31
+ ActiveSupport::Deprecation.warn("`SOURCE_ANNOTATION_DIRECTORIES` is deprecated and will be removed in Rails 6.1. You can add default directories by using config.annotations.register_directories instead.")
32
+ end
33
+
34
+ def source_annotation_directories
35
+ ENV["SOURCE_ANNOTATION_DIRECTORIES"].to_s.split(",")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -26,7 +26,7 @@ module Rails
26
26
 
27
27
  if File.exist?(railsrc)
28
28
  extra_args = File.read(railsrc).split(/\n+/).flat_map(&:split)
29
- puts "Using #{extra_args.join(" ")} from #{railsrc}"
29
+ say "Using #{extra_args.join(" ")} from #{railsrc}"
30
30
  plugin_args.insert(1, *extra_args)
31
31
  end
32
32
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/command"
4
+
5
+ module Rails
6
+ module Command
7
+ class RoutesCommand < Base # :nodoc:
8
+ class_option :controller, aliases: "-c", desc: "Filter by a specific controller, e.g. PostsController or Admin::PostsController."
9
+ class_option :grep, aliases: "-g", desc: "Grep routes by a specific pattern."
10
+ class_option :expanded, type: :boolean, aliases: "-E", desc: "Print routes expanded vertically with parts explained."
11
+
12
+ def perform(*)
13
+ require_application_and_environment!
14
+ require "action_dispatch/routing/inspector"
15
+
16
+ say inspector.format(formatter, routes_filter)
17
+ end
18
+
19
+ private
20
+ def inspector
21
+ ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
22
+ end
23
+
24
+ def formatter
25
+ if options.key?("expanded")
26
+ ActionDispatch::Routing::ConsoleFormatter::Expanded.new
27
+ else
28
+ ActionDispatch::Routing::ConsoleFormatter::Sheet.new
29
+ end
30
+ end
31
+
32
+ def routes_filter
33
+ options.symbolize_keys.slice(:controller, :grep)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -10,7 +10,7 @@ module Rails
10
10
  no_commands do
11
11
  def help
12
12
  super
13
- puts self.class.desc
13
+ say self.class.desc
14
14
  end
15
15
  end
16
16
 
@@ -39,11 +39,11 @@ module Rails
39
39
  else
40
40
  begin
41
41
  eval(code_or_file, TOPLEVEL_BINDING, __FILE__, __LINE__)
42
- rescue SyntaxError, NameError => error
43
- $stderr.puts "Please specify a valid ruby command or the path of a script to run."
44
- $stderr.puts "Run '#{self.class.executable} -h' for help."
45
- $stderr.puts
46
- $stderr.puts error
42
+ rescue SyntaxError, NameError => e
43
+ error "Please specify a valid ruby command or the path of a script to run."
44
+ error "Run '#{self.class.executable} -h' for help."
45
+ error ""
46
+ error e
47
47
  exit 1
48
48
  end
49
49
  end
@@ -7,7 +7,7 @@ with the code.
7
7
 
8
8
  === Setup
9
9
 
10
- Run `bin/rails secrets:setup` to opt in and generate the `config/secrets.yml.key`
10
+ Run `rails secrets:setup` to opt in and generate the `config/secrets.yml.key`
11
11
  and `config/secrets.yml.enc` files.
12
12
 
13
13
  The latter contains all the keys to be encrypted while the former holds the
@@ -45,12 +45,12 @@ the key. Add this:
45
45
 
46
46
  config.read_encrypted_secrets = true
47
47
 
48
- to the environment you'd like to read encrypted secrets. `bin/rails secrets:setup`
48
+ to the environment you'd like to read encrypted secrets. `rails secrets:setup`
49
49
  inserts this into the production environment by default.
50
50
 
51
51
  === Editing Secrets
52
52
 
53
- After `bin/rails secrets:setup`, run `bin/rails secrets:edit`.
53
+ After `rails secrets:setup`, run `rails secrets:edit`.
54
54
 
55
55
  That command opens a temporary file in `$EDITOR` with the decrypted contents of
56
56
  `config/secrets.yml.enc` to edit the encrypted secrets.
@@ -22,7 +22,7 @@ module Rails
22
22
  if ENV["EDITOR"].to_s.empty?
23
23
  say "No $EDITOR to open decrypted secrets in. Assign one like this:"
24
24
  say ""
25
- say %(EDITOR="mate --wait" bin/rails secrets:edit)
25
+ say %(EDITOR="mate --wait" rails secrets:edit)
26
26
  say ""
27
27
  say "For editors that fork and exit immediately, it's important to pass a wait flag,"
28
28
  say "otherwise the secrets will be saved immediately with no chance to edit."
@@ -42,7 +42,7 @@ module Rails
42
42
  rescue Rails::Secrets::MissingKeyError => error
43
43
  say error.message
44
44
  rescue Errno::ENOENT => error
45
- if error.message =~ /secrets\.yml\.enc/
45
+ if /secrets\.yml\.enc/.match?(error.message)
46
46
  deprecate_in_favor_of_credentials_and_exit
47
47
  else
48
48
  raise
@@ -56,7 +56,7 @@ module Rails
56
56
  private
57
57
  def deprecate_in_favor_of_credentials_and_exit
58
58
  say "Encrypted secrets is deprecated in favor of credentials. Run:"
59
- say "bin/rails credentials:help"
59
+ say "rails credentials:help"
60
60
 
61
61
  exit 1
62
62
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
- require "optparse"
5
4
  require "action_dispatch"
6
5
  require "rails"
7
6
  require "active_support/deprecation"
@@ -22,19 +21,6 @@ module Rails
22
21
  set_environment
23
22
  end
24
23
 
25
- def app
26
- @app ||= begin
27
- app = super
28
- if app.is_a?(Class)
29
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
30
- Using `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0.
31
- Please change `run #{app}` to `run Rails.application` in config.ru.
32
- MSG
33
- end
34
- app.respond_to?(:to_app) ? app.to_app : app
35
- end
36
- end
37
-
38
24
  def opt_parser
39
25
  Options.new
40
26
  end
@@ -43,18 +29,22 @@ module Rails
43
29
  ENV["RAILS_ENV"] ||= options[:environment]
44
30
  end
45
31
 
46
- def start
47
- print_boot_information
32
+ def start(after_stop_callback = nil)
48
33
  trap(:INT) { exit }
49
34
  create_tmp_directories
50
35
  setup_dev_caching
51
36
  log_to_stdout if options[:log_stdout]
52
37
 
53
- super
38
+ super()
54
39
  ensure
55
- # The '-h' option calls exit before @options is set.
56
- # If we call 'options' with it unset, we get double help banners.
57
- puts "Exiting" unless @options && options[:daemonize]
40
+ after_stop_callback.call if after_stop_callback
41
+ end
42
+
43
+ def serveable? # :nodoc:
44
+ server
45
+ true
46
+ rescue LoadError, NameError
47
+ false
58
48
  end
59
49
 
60
50
  def middleware
@@ -65,6 +55,10 @@ module Rails
65
55
  super.merge(@default_options)
66
56
  end
67
57
 
58
+ def served_url
59
+ "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma?
60
+ end
61
+
68
62
  private
69
63
  def setup_dev_caching
70
64
  if options[:environment] == "development"
@@ -72,13 +66,6 @@ module Rails
72
66
  end
73
67
  end
74
68
 
75
- def print_boot_information
76
- url = "on #{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma?
77
- puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
78
- puts "=> Rails #{Rails.version} application starting in #{Rails.env} #{url}"
79
- puts "=> Run `rails server -h` for more startup options"
80
- end
81
-
82
69
  def create_tmp_directories
83
70
  %w(cache pids sockets).each do |dir_to_make|
84
71
  FileUtils.mkdir_p(File.join(Rails.root, "tmp", dir_to_make))
@@ -97,10 +84,6 @@ module Rails
97
84
  end
98
85
  end
99
86
 
100
- def restart_command
101
- "bin/rails server #{ARGV.join(' ')}"
102
- end
103
-
104
87
  def use_puma?
105
88
  server.to_s == "Rack::Handler::Puma"
106
89
  end
@@ -108,8 +91,14 @@ module Rails
108
91
 
109
92
  module Command
110
93
  class ServerCommand < Base # :nodoc:
94
+ # Hard-coding a bunch of handlers here as we don't have a public way of
95
+ # querying them from the Rack::Handler registry.
96
+ RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn)
97
+
111
98
  DEFAULT_PORT = 3000
112
- DEFAULT_PID_PATH = "tmp/pids/server.pid".freeze
99
+ DEFAULT_PID_PATH = "tmp/pids/server.pid"
100
+
101
+ argument :using, optional: true
113
102
 
114
103
  class_option :port, aliases: "-p", type: :numeric,
115
104
  desc: "Runs Rails on the specified port - defaults to 3000.", banner: :port
@@ -122,29 +111,41 @@ module Rails
122
111
  desc: "Runs server as a Daemon."
123
112
  class_option :environment, aliases: "-e", type: :string,
124
113
  desc: "Specifies the environment to run this server under (development/test/production).", banner: :name
114
+ class_option :using, aliases: "-u", type: :string,
115
+ desc: "Specifies the Rack server used to run the application (thin/puma/webrick).", banner: :name
125
116
  class_option :pid, aliases: "-P", type: :string, default: DEFAULT_PID_PATH,
126
117
  desc: "Specifies the PID file."
127
- class_option "dev-caching", aliases: "-C", type: :boolean, default: nil,
118
+ class_option :dev_caching, aliases: "-C", type: :boolean, default: nil,
128
119
  desc: "Specifies whether to perform caching in development."
129
- class_option "restart", type: :boolean, default: nil, hide: true
130
- class_option "early_hints", type: :boolean, default: nil, desc: "Enables HTTP/2 early hints."
120
+ class_option :restart, type: :boolean, default: nil, hide: true
121
+ class_option :early_hints, type: :boolean, default: nil, desc: "Enables HTTP/2 early hints."
122
+ class_option :log_to_stdout, type: :boolean, default: nil, optional: true,
123
+ desc: "Whether to log to stdout. Enabled by default in development when not daemonized."
131
124
 
132
- def initialize(args = [], local_options = {}, config = {})
133
- @original_options = local_options
125
+ def initialize(args, local_options, *)
134
126
  super
135
- @server = self.args.shift
136
- @log_stdout = options[:daemon].blank? && (options[:environment] || Rails.env) == "development"
127
+
128
+ @original_options = local_options - %w( --restart )
129
+ deprecate_positional_rack_server_and_rewrite_to_option(@original_options)
137
130
  end
138
131
 
139
132
  def perform
140
133
  set_application_directory!
141
134
  prepare_restart
135
+
142
136
  Rails::Server.new(server_options).tap do |server|
143
137
  # Require application after server sets environment to propagate
144
138
  # the --environment option.
145
139
  require APP_PATH
146
140
  Dir.chdir(Rails.application.root)
147
- server.start
141
+
142
+ if server.serveable?
143
+ print_boot_information(server.server, server.served_url)
144
+ after_stop_callback = -> { say "Exiting" unless options[:daemon] }
145
+ server.start(after_stop_callback)
146
+ else
147
+ say rack_server_suggestion(using)
148
+ end
148
149
  end
149
150
  end
150
151
 
@@ -152,8 +153,8 @@ module Rails
152
153
  def server_options
153
154
  {
154
155
  user_supplied_options: user_supplied_options,
155
- server: @server,
156
- log_stdout: @log_stdout,
156
+ server: using,
157
+ log_stdout: log_to_stdout?,
157
158
  Port: port,
158
159
  Host: host,
159
160
  DoNotReverseLookup: true,
@@ -161,7 +162,7 @@ module Rails
161
162
  environment: environment,
162
163
  daemonize: options[:daemon],
163
164
  pid: pid,
164
- caching: options["dev-caching"],
165
+ caching: options[:dev_caching],
165
166
  restart_cmd: restart_command,
166
167
  early_hints: early_hints
167
168
  }
@@ -194,7 +195,7 @@ module Rails
194
195
  name = :Port
195
196
  when :binding
196
197
  name = :Host
197
- when :"dev-caching"
198
+ when :dev_caching
198
199
  name = :caching
199
200
  when :daemonize
200
201
  name = :daemon
@@ -202,7 +203,7 @@ module Rails
202
203
  user_supplied_options << name
203
204
  end
204
205
  end
205
- user_supplied_options << :Host if ENV["HOST"]
206
+ user_supplied_options << :Host if ENV["HOST"] || ENV["BINDING"]
206
207
  user_supplied_options << :Port if ENV["PORT"]
207
208
  user_supplied_options.uniq
208
209
  end
@@ -217,7 +218,17 @@ module Rails
217
218
  options[:binding]
218
219
  else
219
220
  default_host = environment == "development" ? "localhost" : "0.0.0.0"
220
- ENV.fetch("HOST", default_host)
221
+
222
+ if ENV["HOST"] && !ENV["BINDING"]
223
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
224
+ Using the `HOST` environment to specify the IP is deprecated and will be removed in Rails 6.1.
225
+ Please use `BINDING` environment instead.
226
+ MSG
227
+
228
+ return ENV["HOST"]
229
+ end
230
+
231
+ ENV.fetch("BINDING", default_host)
221
232
  end
222
233
  end
223
234
 
@@ -226,24 +237,74 @@ module Rails
226
237
  end
227
238
 
228
239
  def restart_command
229
- "bin/rails server #{@server} #{@original_options.join(" ")} --restart"
240
+ "bin/rails server #{@original_options.join(" ")} --restart"
230
241
  end
231
242
 
232
243
  def early_hints
233
244
  options[:early_hints]
234
245
  end
235
246
 
247
+ def log_to_stdout?
248
+ options.fetch(:log_to_stdout) do
249
+ options[:daemon].blank? && environment == "development"
250
+ end
251
+ end
252
+
236
253
  def pid
237
254
  File.expand_path(options[:pid])
238
255
  end
239
256
 
240
257
  def self.banner(*)
241
- "rails server [puma, thin etc] [options]"
258
+ "rails server [thin/puma/webrick] [options]"
242
259
  end
243
260
 
244
261
  def prepare_restart
245
262
  FileUtils.rm_f(options[:pid]) if options[:restart]
246
263
  end
264
+
265
+ def deprecate_positional_rack_server_and_rewrite_to_option(original_options)
266
+ if using
267
+ ActiveSupport::Deprecation.warn(<<~MSG)
268
+ Passing the Rack server name as a regular argument is deprecated
269
+ and will be removed in the next Rails version. Please, use the -u
270
+ option instead.
271
+ MSG
272
+
273
+ original_options.concat [ "-u", using ]
274
+ else
275
+ # Use positional internally to get around Thor's immutable options.
276
+ # TODO: Replace `using` occurrences with `options[:using]` after deprecation removal.
277
+ @using = options[:using]
278
+ end
279
+ end
280
+
281
+ def rack_server_suggestion(server)
282
+ if server.in?(RACK_SERVERS)
283
+ <<~MSG
284
+ Could not load server "#{server}". Maybe you need to the add it to the Gemfile?
285
+
286
+ gem "#{server}"
287
+
288
+ Run `rails server --help` for more options.
289
+ MSG
290
+ else
291
+ suggestion = Rails::Command::Spellchecker.suggest(server, from: RACK_SERVERS)
292
+ suggestion_msg = "Maybe you meant #{suggestion.inspect}?" if suggestion
293
+
294
+ <<~MSG
295
+ Could not find server "#{server}". #{suggestion_msg}
296
+ Run `rails server --help` for more options.
297
+ MSG
298
+ end
299
+ end
300
+
301
+ def print_boot_information(server, url)
302
+ say <<~MSG
303
+ => Booting #{ActiveSupport::Inflector.demodulize(server)}
304
+ => Rails #{Rails.version} application starting in #{Rails.env} #{url}
305
+ => Run `rails server --help` for more startup options
306
+ MSG
307
+ end
247
308
  end
248
309
  end
249
310
  end