require 'bundler' require 'thor' module Hat class CLI < Thor include Thor::Actions desc 'start WORKER [OPTIONS]', 'starts the named workers, or all of the workers in the default or provided' + 'worker directory' method_option :dir, default: Dir.getwd, desc: 'sets the root directory' method_option :daemonize, default: false, desc: 'starts the worker as a daemon', aliases: '-D', type: :boolean method_option :workers, default: 1, desc: 'sets the number of processes to run', aliases: '-w', type: :numeric method_option :log, desc: 'sets the location of the log file', aliases: '-l' def start(worker) worker_name = clean_worker_name(worker, options[:dir]) say_status(:info, "starting '#{worker_name}'", :yellow) full_path = (Pathname.new(worker).absolute?) ? worker : File.expand_path(File.join(Dir.getwd, worker)) raise Thor::Error.new("Unable to locate worker in path: '#{full_path}'") unless File.exists?(full_path) daemon_options = { workers: options[:workers], daemonize: options[:daemonize] } daemon_options[:pid_path] = options[:pid_path] || File.join(options[:dir], 'tmp', 'pids', clean_worker_name(worker, options[:dir]).gsub('/', '-') + '.pid') say_status(:info, "PID path set to '#{daemon_options[:pid_path]}'", :yellow) if daemon_options[:daemonize] daemon_options[:log] = options[:log] || File.join(options[:dir], 'log', clean_worker_name(worker, options[:dir]).gsub('/', '-') + '.log') end say_status(:info, "log path set to '#{daemon_options[:log_path]}'", :yellow) say_status(:info, "running #{daemon_options[:workers]} worker(s)", :yellow) require 'hat' require full_path # test that worker class is correctly defined begin worker = Object.const_get(classify_path(worker_name)).new(options) rescue NameError raise Thor::Error.new("Expected file '#{worker_name}' to define '#{classify_path(worker_name)}'") end require 'hat/runner' Hat::Runner.start!(daemon_options.merge(worker: worker)) end desc 'stop WORKER', 'gracefully stops the named worker' method_option :dir, default: Dir.getwd, desc: 'sets the root directory' def stop(worker) worker_name = clean_worker_name(worker, options[:dir]) say_status(:info, "stopping '#{worker_name}'", :yellow) pid_path ||= File.join(options[:dir], 'tmp', 'pids', clean_worker_name(worker, options[:dir]).gsub('/', '-') + '.pid') begin Process.kill(:TERM, File.read(pid_path).to_i) rescue Errno::ESRCH say_status(:fail, "process for '#{worker_name}' in pid file '#{pid_path}' was not found", :red) end end private def clean_worker_name(string, dir) string.gsub(dir + '/','').gsub('.rb', '') end def classify_path(string) string.split('/').collect(&:capitalize).join('::') end end end