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.



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