class Tork::Server

Public Class Methods

address(program=$0) click to toggle source
# File lib/tork/server.rb, line 10
def self.address program=$0
  ".#{program}.sock"
end
new() click to toggle source
# File lib/tork/server.rb, line 14
def initialize
  begin
    @welcome = UNIXServer.open(address = Server.address)
    # UNIX domain socket files are not automatically deleted on close
    at_exit { File.delete address if File.socket? address }
  rescue Errno::EADDRINUSE
    # another instance of this program is already running in the same
    # directory so become a remote control for it rather than exiting
    warn "#{$0}: remotely controlling existing instance..."
    exec 'tork-remote', $0, *ARGV
  end

  # only JSON messages are supposed to be emitted on STDOUT
  # so make puts() in the user code write to STDERR instead
  @stdout = STDOUT.dup
  STDOUT.reopen STDERR

  @servers = Set.new.add(@welcome)
  @clients = Set.new; join STDIN # parent process connected on STDIN
end

Public Instance Methods

loop() click to toggle source
# File lib/tork/server.rb, line 35
def loop
  catch :quit do
    while @clients.include? STDIN
      IO.select((@servers + @clients).to_a).first.each do |stream|
        if stream == @welcome
          join stream.accept

        elsif (stream.eof? rescue true)
          part stream

        elsif @command = hear(stream, stream.gets) and not @command.empty?
          recv stream, @command
        end
      end
    end
  end
end
quit() click to toggle source
# File lib/tork/server.rb, line 53
def quit
  throw :quit
end

Protected Instance Methods

hear(sender, message) click to toggle source

Returns nil if the message received was not meant for processing.

# File lib/tork/server.rb, line 68
def hear sender, message
  JSON.parse message
rescue JSON::ParserError => error
  if @clients.include? sender
    # accept non-JSON "command lines" from clients
    Shellwords.split message
  else
    # forward tell() output from servers to clients
    tell @clients, message, false
    nil
  end
end
join(client) click to toggle source
# File lib/tork/server.rb, line 59
def join client
  @clients.add client
end
part(client) click to toggle source
# File lib/tork/server.rb, line 63
def part client
  @clients.delete client
end
pclose(child) click to toggle source
# File lib/tork/server.rb, line 128
def pclose child
  child.disconnect if @servers.delete? child
end
popen(command) click to toggle source
# File lib/tork/server.rb, line 122
def popen command
  child = Bridge.new(command)
  @servers.add child
  child
end
recv(client, command) click to toggle source

Sets the @client variable to the client we are currently serving.

# File lib/tork/server.rb, line 82
def recv client, command
  @client = client
  __send__(*command)
rescue => error
  tell client, error
end
send(one_or_more_clients, message) click to toggle source
# File lib/tork/server.rb, line 89
def send one_or_more_clients, message
  tell one_or_more_clients, JSON.dump(message), false
end
tell(one_or_more_clients, message, prefix=true) click to toggle source
# File lib/tork/server.rb, line 93
def tell one_or_more_clients, message, prefix=true
  if message.kind_of? Exception
    message = [message.inspect, message.backtrace]
  end

  if prefix
    message = Array(message).join("\n").gsub(/^/, "#{$0}: ")
  end

  targets =
    if one_or_more_clients.respond_to? :to_io
      [one_or_more_clients]
    else
      Array(one_or_more_clients)
    end

  targets.each do |target|
    target = @stdout if target == STDIN
    begin
      target.puts message
      target.flush
    rescue Errno::EPIPE
      # the target closed itself asynchronously
      # https://github.com/sunaku/tork/issues/53
      next
    end
  end
end