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

Committed improvements #35

Merged
merged 12 commits into from
Apr 10, 2018
10 changes: 10 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changes

## master

* Add `:pumactl` option
* Add `:restart_timeout` option
* Don't stop Puma if it was not started by Guard
* Don't notify about start when no start
* Improve `guard-compat` using (https://github.com/guard/guard-compat#migrating-your-api-calls)
* Remove unused `pry` dependency
* Update versions of dependencies

## 0.4.1

* Improve notifications. Via #30
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,21 @@ end

* `:port` is the port number to run on (default `4000`)
* `:environment` is the environment to use (default `development`)
* `:start_on_start` will start the server when starting Guard (default `true`)
* `:start_on_start` will start the server when starting Guard and stop the server when reloading/stopping Guard (default `true`)
* `:force_run` kills any process that's holding open the listen port before attempting to (re)start Puma (default `false`).
* `:daemon` runs the server as a daemon, without any output to the terminal that ran `guard` (default `false`).
* `:quiet` runs the server in quiet mode, suppressing output (default `true`).
* `:debugger` runs the server with the debugger enabled (default `false`). Required ruby-debug gem.
* `:timeout` waits this number of seconds when restarting the Puma server before reporting there's a problem (default `20`).
* `:restart_timeout` waits this number of seconds before the next restarting the Puma server (default `1`).
* `:config` is the path to the Puma config file (optional)
* `:bind` is URI to bind to (tcp:// and unix:// only) (optional)
* `:control_token` is the token to use as authentication for the control server(optional)
* `:control_port` is the port to use for the control server(optional)
* `:threads` is the min:max number of threads to use. Defaults to 0:16 (optional)
* `:pumactl` manages the server via `pumactl` executable instead of `puma` (default `false`)
* Incompatible with options such as `port`, `environment`, `daemon`, `bind`, `threads`
* Use with `config` option is preferred.
* `:notifications` is the list of notification types that will be sent. Defaults to `[:restarting, :restarted, :not_restarted, :stopped]` (optional)

## Contributing
Expand Down
7 changes: 3 additions & 4 deletions guard-puma.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ Gem::Specification.new do |gem|
gem.add_dependency "guard", "~> 2.14"
gem.add_dependency "guard-compat", "~> 1.2"
gem.add_dependency "puma", "~> 3.6"
gem.add_development_dependency "rake", "~> 10.4"
gem.add_development_dependency "rspec", "~> 3.5.0"
gem.add_development_dependency "guard-rspec", "~> 4.7.0"
gem.add_development_dependency "pry"
gem.add_development_dependency "rake", "~> 12"
gem.add_development_dependency "rspec", "~> 3.7"
gem.add_development_dependency "guard-rspec", "~> 4.7"
end
48 changes: 33 additions & 15 deletions lib/guard/puma.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
require "guard"
require "guard/plugin"
require "guard/puma/runner"
require "rbconfig"
require "guard/puma/version"
require "guard/compat/plugin"
require 'guard/compat/plugin'
require_relative 'puma/runner'
require_relative 'puma/version'

module Guard
class Puma < Plugin
Expand All @@ -14,11 +11,13 @@ def self.default_env
end

DEFAULT_OPTIONS = {
:pumactl => false,
:port => 4000,
:environment => default_env,
:start_on_start => true,
:force_run => false,
:timeout => 20,
:restart_timeout => 1,
:debugger => false,
:notifications => %i[restarting restarted not_restarted stopped]
}
Expand All @@ -28,35 +27,54 @@ def initialize(options = {})
@options = DEFAULT_OPTIONS.merge(options)
@options[:port] = nil if @options.key?(:config)
@runner = ::Guard::PumaRunner.new(@options)
@last_restarted = Time.now
end

def start
return unless options[:start_on_start]
server = options[:server] ? "#{options[:server]} and " : ""
UI.info "Puma starting#{port_text} in #{server}#{options[:environment]} environment."
runner.start if options[:start_on_start]
Compat::UI.info(
"Puma starting#{port_text} in #{server}#{options[:environment]} environment."
)
runner.start
end

def reload
UI.info "Restarting Puma..."
return if (Time.now - @last_restarted) < options[:restart_timeout]
@last_restarted = Time.now
Compat::UI.info "Restarting Puma..."
if options[:notifications].include?(:restarting)
Notifier.notify("Puma restarting#{port_text} in #{options[:environment]} environment...", :title => "Restarting Puma...", :image => :pending)
Compat::UI.notify(
"Puma restarting#{port_text} in #{options[:environment]} environment...",
title: "Restarting Puma...", image: :pending
)
end
if runner.restart
UI.info "Puma restarted"
Compat::UI.info "Puma restarted"
if options[:notifications].include?(:restarted)
Notifier.notify("Puma restarted#{port_text}.", :title => "Puma restarted!", :image => :success)
Compat::UI.notify(
"Puma restarted#{port_text}.",
title: "Puma restarted!", image: :success
)
end
else
UI.info "Puma NOT restarted, check your log files."
Compat::UI.info "Puma NOT restarted, check your log files."
if options[:notifications].include?(:not_restarted)
Notifier.notify("Puma NOT restarted, check your log files.", :title => "Puma NOT restarted!", :image => :failed)
Compat::UI.notify(
"Puma NOT restarted, check your log files.",
title: "Puma NOT restarted!", image: :failed
)
end
end
end

def stop
return unless options[:start_on_start]
if options[:notifications].include?(:stopped)
Notifier.notify("Until next time...", :title => "Puma shutting down.", :image => :pending)
Compat::UI.notify(
"Until next time...",
title: "Puma shutting down.", image: :pending
)
end
runner.halt
end
Expand Down
93 changes: 62 additions & 31 deletions lib/guard/puma/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,48 @@ class PumaRunner

MAX_WAIT_COUNT = 20

attr_reader :options, :control_url, :control_token, :cmd_opts
attr_reader :options, :control_url, :control_token, :cmd_opts, :pumactl

def initialize(options)
@control_token = options.delete(:control_token) { |_| ::Puma::Configuration.random_token }
@control = "localhost"
@control_port = (options.delete(:control_port) || '9293')
@control_url = "#{@control}:#{@control_port}"
@control_url = "localhost:#{@control_port}"
@quiet = options.delete(:quiet) { true }
@pumactl = options.delete(:pumactl) { false }
@options = options

puma_options = if options[:config]
{
'--config' => options[:config],
'--control-token' => @control_token,
'--control' => "tcp://#{@control_url}",
'--environment' => options[:environment]
}
puma_options = {
puma_options_key(:config) => options[:config],
puma_options_key(:control_token) => @control_token,
puma_options_key(:control_url) => "tcp://#{@control_url}"
}
if options[:config]
puma_options['--config'] = options[:config]
else
{
'--port' => options[:port],
'--control-token' => @control_token,
'--control' => "tcp://#{@control_url}",
'--environment' => options[:environment]
}
end
[:bind, :threads].each do |opt|
puma_options["--#{opt}"] = options[opt] if options[opt]
puma_options['--port'] = options[:port]
end
%i[bind threads environment]
.select { |opt| options[opt] }
.each do |opt|
if pumactl
Compat::UI.warning(
"`#{opt}` option is not compatible with `pumactl` option"
)
else
puma_options["--#{opt}"] = options[opt]
end
end
puma_options = puma_options.to_a.flatten
puma_options << '-q' if @quiet
puma_options << '--quiet' if @quiet
@cmd_opts = puma_options.join ' '
end

def start
if in_windows_cmd?
Kernel.system windows_start_cmd
else
Kernel.system nix_start_cmd
end
Kernel.system build_command('start')
end

def halt
Net::HTTP.get build_uri('halt')
run_puma_command!('halt')
# server may not have been stopped correctly, but we are halting so who cares.
return true
end
Expand All @@ -68,8 +67,30 @@ def sleep_time

private

PUMA_OPTIONS_KEYS_BY_PUMACTL = {
true => {
config: '--config-file',
control_url: '--control-url'
}.freeze,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These inner hashes don't also need to be frozen. The outer hash being frozen freezes them. ❄️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even Strings in frozen Hashes not frozen, as you can see in this example. So, we maybe should freeze '--config-file' and other strings.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, I must have mistyped something in a previous IRB session.

I don't think it's really necessay to deep freeze the whole thing anyway, at least not right now (given no real issues on it)

false => {
config: '--config',
control_url: '--control'
}.freeze
}.freeze

private_constant :PUMA_OPTIONS_KEYS_BY_PUMACTL

def puma_options_key(key)
keys = PUMA_OPTIONS_KEYS_BY_PUMACTL[@pumactl]
keys.fetch(key) { |k| "--#{k.to_s.tr('_', '-')}" }
end

def run_puma_command!(cmd)
Net::HTTP.get build_uri(cmd)
if pumactl
Kernel.system build_command(cmd)
else
Net::HTTP.get build_uri(cmd)
end
return true
rescue Errno::ECONNREFUSED => e
# server may not have been started correctly.
Expand All @@ -80,12 +101,22 @@ def build_uri(cmd)
URI "http://#{control_url}/#{cmd}?token=#{control_token}"
end

def nix_start_cmd
%{sh -c 'cd #{Dir.pwd} && puma #{cmd_opts} &'}
def build_command(cmd)
puma_cmd = "#{pumactl ? 'pumactl' : 'puma'} #{cmd_opts} #{cmd if pumactl}"
background = cmd == 'start'
if in_windows_cmd?
windows_cmd(puma_cmd, background)
else
nix_cmd(puma_cmd, background)
end
end

def nix_cmd(puma_cmd, background = false)
%(sh -c 'cd #{Dir.pwd} && #{puma_cmd} #{'&' if background}')
end

def windows_start_cmd
%{cd "#{Dir.pwd}" && start "" /B cmd /C "puma #{cmd_opts}"}
def windows_cmd(puma_cmd, background = false)
%(cd "#{Dir.pwd}" && #{'start "" /B' if background} cmd /C "#{puma_cmd}")
end

def in_windows_cmd?
Expand Down
Loading