lib/utils/probe_server.rb
require 'tins/xt' require 'term/ansicolor' class String include Term::ANSIColor end module Utils class ProbeServer class Job def initialize(probe_server, args) @id = probe_server.next_job_id @args = Array(args) end attr_reader :id attr_reader :args attr_writer :ok def ok case @ok when false then 'n' when true then 'y' else '…' end end def ok_colorize(string) case @ok when false then string.white.on_red when true then string.black.on_green else string end end def inspect ok_colorize("#{id} #{args.map { |a| a.include?(' ') ? a.inspect : a } * ' '}") end alias to_s inspect end def initialize(uri) @uri = uri @history = [].freeze @jobs_queue = Queue.new @current_job_id = 0 Thread.new { work_loop } end def print(*msg) if msg.first !~ /^irb: warn: can't alias / # shut your god d*mn wh*re mouth super end end def start output_message "Starting probe server listening to #{@uri.inspect}.", type: :info DRb.start_service(@uri, self) begin DRb.thread.join rescue Interrupt ARGV.clear << '-f' output_message %{\nEntering interactive mode.}, type: :info help begin old, $VERBOSE = $VERBOSE, nil examine(self) ensure $VERBOSE = old end output_message "Quitting interactive mode, but still listening to #{@uri.inspect}.", type: :info retry end end def inspect "#<Probe #queue=#{@jobs_queue.size}>" end alias to_s inspect annotate :doc annotate :shortcut doc 'Display this help.' shortcut :h def help docs = doc_annotations.sort_by(&:first) docs_size = docs.map { |a| a.first.size }.max format = '%-20s %-3s %s' output_message [ (format % %w[ command sho description ]).on_color(20).white ] << docs.map { |cmd, doc| shortcut = shortcut_of(cmd) and shortcut = "(#{shortcut})" format % [ cmd, shortcut, doc ] } end doc 'Enqueue a new job with the argument array <job_args>.' shortcut :e def job_enqueue(job_args) job = Job.new(self, job_args) output_message " → #{job.inspect} enqueued.", type: :info @jobs_queue.push job end alias enqueue job_enqueue doc 'Send the <signal> to the process that is working on the current job, if any.' doc 'Quit the server.' shortcut :q def shutdown output_message "Server was shutdown down – HARD!", type: :warn exit 23 end doc 'Repeat the job with <job_id> or the last, it will be assigned a new id, though.' shortcut :r def job_repeat(job_id = @history.last) Job === job_id and job_id = job_id.id if old_job = @history.find { |job| job.id == job_id } job_enqueue old_job.args true else false end end doc 'List the history of run jobs.' shortcut :l def history_list output_message @history end doc 'Clear the history of run jobs.' def history_clear @history = [] true end class LogWrapper < BasicObject def initialize(server, object) @server, @object = server, object end def []=(name, value) name, value = name.to_s, value.to_s @server.output_message("Setting #{name}=#{value.inspect}.", type: :info) @object[name] = value end def method_missing(*a, &b) @object.__send__(*a, &b) end end doc "The environment of the server process, use env['a'] = 'b' and env['a']." memoize_method def env LogWrapper.new(self, ENV) end doc "Clear the terminal screen" shortcut :c def clear system "clear" end for (method_name, shortcut) in shortcut_annotations alias_method shortcut, method_name end def next_job_id @current_job_id += 1 end def output_message(msg, type: nil) msg.respond_to?(:to_a) and msg = msg.to_a * "\n" msg = case type when :success msg.on_color(22).white when :info msg.on_color(20).white when :warn msg.on_color(94).white when :failure msg.on_color(124).blink.white else msg end STDOUT.puts msg STDOUT.flush self end private def run_job(job) output_message " → #{job.inspect} now running.", type: :info system *cmd(job.args) message = " → #{job.inspect} was just run" if $?.success? job.ok = true message << " successfully." output_message message, type: :success else job.ok = false message << " and failed with exit status #{$?.exitstatus}!" output_message message, type: :failure end @history += [ @job.freeze ] @history.freeze @job = nil end def work_loop loop do @job = @jobs_queue.shift run_job @job end end def cmd(job) call = [] if ENV.key?('BUNDLE_GEMFILE') and bundle = `which bundle`.full?(:chomp) call << bundle << 'exec' end call.push($0, *job) #output_message "Executing #{call.inspect} now.", type: :info call end end end