lib/hanami/commands/server.rb in hanami-0.7.3 vs lib/hanami/commands/server.rb in hanami-0.8.0
- old
+ new
@@ -1,80 +1,156 @@
require 'rack'
+require 'hanami/server'
module Hanami
module Commands
- # Rack compatible server.
- #
- # It is run with:
- #
- # `bundle exec hanami server`
- #
- # It runs the application, by using the server specified in your `Gemfile`
- # (eg. Puma or Unicorn).
- #
- # It enables code reloading by default.
- # This feature is implemented via process fork and requires `shotgun` gem.
- #
- # @since 0.1.0
- # @api private
- class Server < ::Rack::Server
- attr_reader :options
+ class Server
- # @param options [Hash] Environment's options
+ # Message text when Shotgun enabled but interpreter does not support `fork`
#
- # @since 0.1.0
- # @see Hanami::Environment#initialize
+ # @since 0.8.0
+ # @api private
+ WARNING_MESSAGE = 'Your platform doesn\'t support code reloading.'.freeze
+
+ ENTR_EXECUTE_COMMAND = "find %{paths} -type f | entr -r bundle exec hanami rackserver %{args}".freeze
+
+ attr_reader :server
+
def initialize(options)
- @_env = Hanami::Environment.new(options)
- @options = _extract_options(@_env)
+ @options = options
+ detect_strategy!
+ prepare_server!
+ end
- if code_reloading?
- require 'shotgun'
- @app = Shotgun::Loader.new(@_env.rackup.to_s)
+ def start
+ preload_applications!
+
+ case @strategy
+ when :entr
+ exec ENTR_EXECUTE_COMMAND % {paths: project_paths, args: server_options}
+ when :shotgun
+ Shotgun.enable_copy_on_write
+ Shotgun.preload
+ @server.start
+ when :rackup
+ @server.start
end
end
- # Primarily this removes the ::Rack::Chunked middleware
- # which is the cause of Safari content-length bugs.
- #
- # @since 0.1.0
- def middleware
- mw = Hash.new { |e, m| e[m] = [] }
- mw["deployment"].concat([::Rack::ContentLength, ::Rack::CommonLogger])
- mw["development"].concat(mw["deployment"] + [::Rack::ShowExceptions, ::Rack::Lint])
- mw["development"].push(::Shotgun::Static) if code_reloading?
- mw
+ private
+
+ def server_options
+ _options = @options.dup
+ _options.delete(:code_reloading)
+ _options.inject([]) {|res, (k, v)| res << "--#{k}=#{v}" ; res}.join(" ")
end
- # Kickstart shotgun preloader if code reloading is supported
+ def project_paths
+ applications = Hanami::Environment.new.container? ? 'apps' : 'app'
+ "#{ applications } config db lib"
+ end
+
+ def prepare_server!
+ case @strategy
+ when :rackup
+ @server = Hanami::Server.new(@options)
+ when :shotgun
+ @server = Hanami::Server.new(@options)
+ @server.app = Shotgun::Loader.new(@server.rackup_config)
+ end
+ end
+
+ # Determine server strategy
#
- # @since 0.1.0
- def start
- if code_reloading?
- Shotgun.enable_copy_on_write
- Shotgun.preload
+ # In order to decide the value, it looks up the following sources:
+ #
+ # * CLI option `code_reloading`
+ #
+ # If those are missing it falls back to the following defaults:
+ #
+ # * :shotgun for development and if Shotgun enabled and `fork supported
+ # * :entr for development and Shotgun disabled but `entr` installed
+ # * :rackup for all other cases
+ #
+ # @return [:shotgun,:entr, :rackup] the result of the check
+ #
+ # @since 0.8.0
+ #
+ # @see Hanami::Environment::CODE_RELOADING
+ def detect_strategy!
+ @strategy = :rackup
+ if Hanami::Environment.new(@options).code_reloading?
+ if shotgun_enabled?
+ if fork_supported?
+ @strategy = :shotgun
+ else
+ puts WARNING_MESSAGE
+ end
+ elsif entr_enabled?
+ @strategy = :entr
+ end
end
- super
+ @strategy
end
- private
+ def preload_applications!
+ Hanami::Environment.new(@options).require_application_environment
+ Hanami::Application.preload!
+ end
- # @since 0.1.0
+ # Check if entr(1) is installed
+ #
+ # @return [Boolean]
+ #
+ # @since 0.8.0
# @api private
- def _extract_options(env)
- env.to_options.merge(
- config: env.rackup.to_s,
- Host: env.host,
- Port: env.port,
- AccessLog: []
- )
+ def entr_enabled?
+ !!which('entr')
end
- # @since 0.1.0
+
+ # Check if Shotgun is enabled
+ #
+ # @return [Boolean]
+ #
+ # @since 0.8.0
# @api private
- def code_reloading?
- @_env.code_reloading?
+ def shotgun_enabled?
+ begin
+ require 'shotgun'
+ true
+ rescue LoadError
+ false
+ end
+ end
+
+ # Check if ruby interpreter supports `fork`
+ #
+ # @return [Boolean]
+ #
+ # @since 0.8.0
+ # @api private
+ def fork_supported?
+ Kernel.respond_to?(:fork)
+ end
+
+ # Cross-platform way of finding an executable in the $PATH.
+ #
+ # Usage:
+ # which('ruby') #=> /usr/bin/ruby
+ #
+ # @since 0.8.0
+ # @api private
+ def which(cmd)
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+ exts.each { |ext|
+ exe = File.join(path, "#{cmd}#{ext}")
+ return exe if File.executable?(exe) && !File.directory?(exe)
+ }
+ end
+ return nil
end
end
end
end