class NodeTask

Constants

RESPONSE_TIMEOUT
START_MAX_RETRIES

Attributes

logger[W]
node_command[W]
working_dir[W]
task[RW]

Public Class Methods

alive?() click to toggle source
# File lib/node_task/node_task.rb, line 163
def alive?
  current_pid = nil
  alive = false
  if @controller
    begin
      current_pid = @controller.pid
    rescue Errno::ENOENT
    end
  end
  if current_pid
    begin
      Process.getpgid(current_pid)
      alive = true
    rescue Errno::ESRCH
    end
  end
  alive
end
check_error() click to toggle source
# File lib/node_task/node_task.rb, line 117
def check_error
  if File.exist? error_log_file
    # TODO: raise error
    logger.error File.open(error_log_file).read
    File.unlink error_log_file
    true
  end
end
daemon_env() click to toggle source
# File lib/node_task/node_task.rb, line 196
def daemon_env
  {
    "NODE_TASK_SOCK_PATH" => socket_path,
    "NODE_TASK_CWD" => working_dir,
    "NODE_TASK_DAEMON_ID" => daemon_identifier,
    "NODE_TASK_PARENT_PID" => Process.pid.to_s,
    "NODE_TASK_PARENT_CHECK_INTERVAL" => parent_check_interval.to_s,
    "NODE_ENV" => ENV["RACK_ENV"],
  }
end
daemon_identifier() click to toggle source
# File lib/node_task/node_task.rb, line 59
def daemon_identifier
  'ruby_node_task'
end
daemon_start_script() click to toggle source
# File lib/node_task/node_task.rb, line 71
def daemon_start_script
  File.join(gem_dir, 'nodeTask.js').to_s
end
ensure_connection() click to toggle source

really try to successfully connect, starting the daemon if required

# File lib/node_task/node_task.rb, line 90
def ensure_connection
  attempt = 0
  begin
    server # make sure daemon is running

    socket = server.connect do
      begin
        _make_connection
      rescue Errno::ENOENT => e 
        # daemon_controller doesn't understand ENOENT
        raise Errno::ECONNREFUSED, e.message
      end
    end
  rescue DaemonController::StartTimeout, DaemonController::StartError => e
    logger.error e.message
    if attempt < START_MAX_RETRIES
      attempt += 1
      logger.error "retrying attempt #{attempt}"
      retry
    else
      raise e
    end
  end

  socket
end
error_log_file() click to toggle source
# File lib/node_task/node_task.rb, line 47
def error_log_file
  File.join(working_dir, "#{daemon_identifier}-error.log")
end
gem_dir() click to toggle source
# File lib/node_task/node_task.rb, line 55
def gem_dir
  @gem_dir ||= File.dirname(File.expand_path(__FILE__))
end
logger() click to toggle source
# File lib/node_task/node_task.rb, line 36
def logger
  return @logger unless @logger.nil?
  @logger = Logger.new(STDERR)
  @logger.level = ENV["NODE_TASK_DEBUG"] ? Logger::DEBUG : Logger::INFO 
  @logger
end
new(_task) click to toggle source
# File lib/node_task/node_task.rb, line 256
def initialize(_task)
  @task = _task
end
node_command() click to toggle source
# File lib/node_task/node_task.rb, line 67
def node_command
  @node_command || ENV["NODE_COMMAND"] || 'node'
end
parent_check_interval() click to toggle source
# File lib/node_task/node_task.rb, line 207
def parent_check_interval
  1000
end
parse_response(socket) click to toggle source

get a json response from socket

# File lib/node_task/node_task.rb, line 127
def parse_response(socket)
  # only take one message - the result
  # response terminated by newline
  response_text = nil
  loop do
    response_text = socket.gets("\n")
    break if response_text
    break if check_error
  end
  if response_text
    JSON.parse(response_text, symbolize_names: true)
  else
    logger.error 'no response for message'
    nil
  end
end
pid_file() click to toggle source
# File lib/node_task/node_task.rb, line 51
def pid_file
  File.join(working_dir, "#{daemon_identifier}.pid")
end
release() click to toggle source

stop the daemon

# File lib/node_task/node_task.rb, line 183
def release
  return unless alive?

  logger.debug "stopping daemon #{@controller.pid}"
  @controller.stop

  begin
    File.unlink socket_path
  rescue Errno::ENOENT => e
    # socket file's already gone
  end
end
request(socket, message) click to toggle source

make a single request, get a response and close the connection

# File lib/node_task/node_task.rb, line 145
def request(socket, message)
  socket.write(message.to_json+"\n")

  result = nil
  begin
    Timeout::timeout(RESPONSE_TIMEOUT) do
      result = parse_response(socket)
    end
  rescue Timeout::Error, Exception => e
    logger.error e.message
  ensure
    # disconnect after receiving response
    socket.close
  end

  result
end
server() click to toggle source

get configured daemon controller for daemon, and start it

# File lib/node_task/node_task.rb, line 76
def server
  @controller ||= _make_daemon_controller

  begin
    @controller.start
    logger.debug "spawned server #{@controller.pid}"
  rescue DaemonController::AlreadyStarted => e
    logger.debug "server already running #{@controller.pid}"
  end

  @controller
end
socket_path() click to toggle source
# File lib/node_task/node_task.rb, line 63
def socket_path
  @socket_path ||= _make_sock_path(working_dir, daemon_identifier)
end
windows?() click to toggle source
# File lib/node_task/node_task.rb, line 32
def windows?
  (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
end
working_dir() click to toggle source
# File lib/node_task/node_task.rb, line 43
def working_dir
  @working_dir || Dir.pwd
end

Private Class Methods

_make_connection() click to toggle source
# File lib/node_task/node_task.rb, line 213
def _make_connection
  UNIXSocket.new socket_path
end
_make_daemon_controller() click to toggle source

TODO:

  • some server errors not reported

# File lib/node_task/node_task.rb, line 227
def _make_daemon_controller
  logger.debug "socket_path #{socket_path}"
  logger.debug "starting #{node_command} #{daemon_start_script}"

  controller = DaemonController.new(
    identifier: daemon_identifier,
    start_command: "#{node_command} #{daemon_start_script}",
    ping_command: Proc.new{
      begin
        _make_connection
      rescue Errno::ENOENT => e 
        # daemon_controller doesn't understand ENOENT
        raise Errno::ECONNREFUSED, e.message
      end
    },
    pid_file: pid_file,
    log_file: error_log_file,
    env: daemon_env,
    log_file_activity_timeout: RESPONSE_TIMEOUT,
    start_timeout: RESPONSE_TIMEOUT,
    daemonize_for_me: true,
  )

  controller
end
_make_sock_path(dir, name) click to toggle source
# File lib/node_task/node_task.rb, line 217
def _make_sock_path(dir, name)
  if windows?
    "\\\\.\\pipe\\#{name}\\#{File.expand_path(dir)}"
  else
    File.join(dir, "#{name}.sock")
  end
end

Public Instance Methods

run(opts = nil) click to toggle source
# File lib/node_task/node_task.rb, line 260
def run(opts = nil)
  socket = self.class.ensure_connection

  message = {
    task: task,
    opts: opts,
  }

  response = self.class.request(socket, message)
  if response
    if response[:error]
      raise NodeTask::Error, response[:error]
    else
      response[:result]
    end
  end
end