Class: FontProcessor::ExternalJSONProgram

Inherits:
Object
  • Object
show all
Defined in:
lib/fontprocessor/external_execution.rb

Class Method Summary collapse

Class Method Details

.run(program, parameters) ⇒ Object

Public: Executes a command in a blocking manner that expects it’s input to be json and will return it’s output as json.

program - The command to execute. parameters - A ruby object that can be safely transformed into

JSON that will be passed to the given program on
standard input.

parameters - A special parameter which determines how long the

command should run. It is removed from the
parameters hash before being passed to the
command.

Returns a Ruby Object representing the returned json output if no error occurred. Otherwise a Hash is returned with “status” set to “Failure” and a message key explaining why.

Raises ExternalProgramError if the command times out.

Raises:



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/fontprocessor/external_execution.rb', line 71

def self.run(program, parameters)
  timeout = parameters.delete(:timeout) || 10
  parsed = {}

  Open4::popen4(program) do |pid, stdin, stdout, stderr|
    stdin.write(parameters.to_json+"\n")
    stdin.close

    # Grab stdout or stderr. If stderr comes up first capture it until stdout is ready
    result = nil
    loop do
      result,_,_ = select([stdout,stderr], nil, nil, timeout)
      if result and result[0].eof?
        break
      elsif result and result[0] == stderr
        parsed['stderr'] = "" unless parsed.has_key? 'stderr'
        parsed['stderr'] += stderr.readpartial(4096)
      else
        break
      end
    end

    if result
      begin
        parsed = JSON.parse(stdout.read())

        if stderr
          parsed['stderr'] = "" unless parsed.has_key? 'stderr'
          parsed['stderr'] += stderr.read()
        end
      rescue
        raise Exception, stderr.read() if stderr
      end
    else
      parsed = {'status' => 'Failure', 'message' => "Took longer than #{timeout} seconds to respond"}
    end

    Process.kill "TERM", pid
  end

  raise ExternalProgramError, "#{program}: #{parsed['message']}" if parsed['status'] == 'Failure'

  return parsed
end