Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
jarvis / bin / jarvis
Size: Mime:
#!/usr/bin/env ruby
# encoding: utf-8
$:.unshift File.expand_path('../../lib', __FILE__)

require 'pp'
require 'fileutils'
require 'optparse'
require 'daemons'
require 'jarvis'


class Launcher

  def initialize(options = {})
    @options            = options
    @options[:pid_file] = File.expand_path(options[:pid_file]) if pid_file?
    @options[:log_file] = File.expand_path(options[:log_file]) if log_file?
  end

  def launch(startable)
    @startable = startable
    puts "Launching..."
    puts startable.banner if startable.respond_to?(:banner)

    check_pid
    daemonize if options[:daemonize]
    write_pid
    trap_signals

    if log_file?
      redirect_output if options[:daemonize]
    elsif options[:daemonize]
      suppress_output
    end

    @startable.start! #blocking operation
  end

  def quit
    @startable.stop
    info "Finished"
    exit 0
  end

  private

  attr_reader :options

  def pretty_print(hash)
    pp hash
  end

  def log_file
    options[:log_file]
  end

  def pid_file
    options[:pid_file]
  end

  def log_file?
    !log_file.nil?
  end

  def pid_file?
    !pid_file.nil?
  end

  def info(msg)
    puts "[#{Process.pid}] [#{Time.now}] #{msg}"
  end

  #==========================================================================
  # DAEMONIZING, PID MANAGEMENT, and OUTPUT REDIRECTION
  #==========================================================================

  def daemonize
    exit if fork
    Process.setsid
    exit if fork
    Dir.chdir options[:work_dir]
  end

  def redirect_output
    FileUtils.mkdir_p(File.dirname(log_file), :mode => 0755)
    FileUtils.touch log_file
    File.chmod(0644, log_file)
    $stderr.reopen(log_file, 'a')
    $stdout.reopen($stderr)
    $stdout.sync = $stderr.sync = true
  end

  def suppress_output
    $stderr.reopen('/dev/null', 'a')
    $stdout.reopen($stderr)
  end

  def write_pid
    if pid_file?
      begin
        File.open(pid_file, ::File::CREAT | ::File::EXCL | ::File::WRONLY) { |f| f.write("#{Process.pid}") }
        at_exit { File.delete(pid_file) if File.exists?(pid_file) }
      rescue Errno::EEXIST
        check_pid
        retry
      end
    end
  end

  def check_pid
    if pid_file?
      case pid_status(pid_file)
      when :running, :not_owned
        puts "A server is already running. Check #{pid_file}"
        exit(1)
      when :dead
        File.delete(pid_file)
      end
    end
  end

  def pid_status(pid_file)
    return :exited unless File.exists?(pid_file)
    pid = ::File.read(pid_file).to_i
    return :dead if pid == 0
    Process.kill(0, pid)
    :running
  rescue Errno::ESRCH
    :dead
  rescue Errno::EPERM
    :not_owned
  end

  #==========================================================================
  # SIGNAL HANDLING
  #==========================================================================

  def trap_signals
    trap(:QUIT) do # graceful shutdown
      quit
    end
    trap("SIGINT") do
      quit
    end
    trap("TERM") do
      quit
    end
  end

  #==========================================================================

end

options = {
  daemonize: false,
  log_file:  '/tmp/jarvis.log',
  work_dir:  '.'
}

daemonize_help = 'run daemonized in the background (default: false)'
pid_file_help  = 'the pid filename'
log_file_help  = 'the log filename'
include_help   = 'an additional $LOAD_PATH (may be used more than once)'
debug_help     = 'set $DEBUG to true'
warn_help      = 'enable warnings'
work_dir_help  = 'set workdir for process (Default: /)'

op        = OptionParser.new
op.banner = 'J.A.R.V.I.S. Just Another Rather Very Intelligent System'
op.separator ''
op.separator 'Usage: jarvis [options]'
op.separator ''

op.separator ''
op.separator 'Process options:'
op.on('-d', '--daemonize', daemonize_help) { options[:daemonize] = true }
op.on('-p', '--pid PIDFILE', pid_file_help) { |value| options[:pid_file] = value }
op.on('-l', '--log LOGFILE', log_file_help) { |value| options[:log_file] = value }
op.on('-w', '--workdir DIR', work_dir_help) { |value| options[:work_dir] = value }

op.separator ''
op.separator 'Ruby options:'
op.on('--debug', debug_help) { $DEBUG = true }
op.on('--warn', warn_help) { $-w = true }
op.on('-I', '--include PATH', include_help) do |value|
  $LOAD_PATH.unshift(*value.split(":").map { |v| File.expand_path(v) })
end

op.separator ''
op.separator 'Common options:'
op.on('-h', '--help') { options[:action] = :help }
op.on('-v', '--version') { options[:action] = :version }

op.separator ''
op.parse!(ARGV)


class Startable
  include Jarvis::Daemon

  def setup_dependencies
    puts 'Startable::setup_dependencies'
  end

  def run
    puts 'non-blocking run method'
    puts Jarvis.configuration.to_h
  end
end


case options[:action]
when :help then
  puts op.to_s
when :version then
  puts Jarvis::VERSION
else
  contents = File.read('config.jrv')
  class_name = contents.match(%r(run (.*)))[1]
  Dir[Dir.pwd + "/app/*.rb"].each {|file| require file}
  clazz = Object.const_get class_name
  Launcher.new(options).launch clazz.new
end