Class: HotCell::Response

Inherits:
Object
  • Object
show all
Extended by:
Fields
Defined in:
lib/hot_cell/response.rb

Overview

One line of JSON. Outputs are posted and flushed before success is reported, so the cold side may read as soon as it sees ok.

timing is present on every response, success or failure, and it is also the cell's tracing channel. A cell cannot reach a trace collector, so anything it knows about its own internals travels back here or not at all. An operation may add its own keys, and a client should treat the map as open.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(result: nil, failure: nil, timing: {}, version: PROTOCOL_VERSION) ⇒ Response

Returns a new instance of Response.



58
59
60
61
62
63
# File 'lib/hot_cell/response.rb', line 58

def initialize(result: nil, failure: nil, timing: {}, version: PROTOCOL_VERSION)
  @result = result
  @failure = failure
  @timing = timing
  @version = version
end

Instance Attribute Details

#failureObject (readonly)

Returns the value of attribute failure.



12
13
14
# File 'lib/hot_cell/response.rb', line 12

def failure
  @failure
end

#resultObject (readonly)

Returns the value of attribute result.



12
13
14
# File 'lib/hot_cell/response.rb', line 12

def result
  @result
end

#timingObject (readonly)

Returns the value of attribute timing.



12
13
14
# File 'lib/hot_cell/response.rb', line 12

def timing
  @timing
end

#versionObject (readonly)

Returns the value of attribute version.



12
13
14
# File 'lib/hot_cell/response.rb', line 12

def version
  @version
end

Class Method Details

.failed(failure, timing: {}) ⇒ Object



25
26
27
# File 'lib/hot_cell/response.rb', line 25

def failed(failure, timing: {})
  new failure: failure, timing: timing
end

.nounObject



17
18
19
# File 'lib/hot_cell/response.rb', line 17

def noun
  "response"
end

.ok(result: {}, timing: {}) ⇒ Object



21
22
23
# File 'lib/hot_cell/response.rb', line 21

def ok(result: {}, timing: {})
  new result: result, timing: timing
end

.parse(line) ⇒ Object

ok has to be the boolean it says it is rather than anything truthy. This is the field the whole taxonomy turns on, and Ruby's truthiness would read "false", 0 and [] as success — so a malformed or hostile response could turn a failure into an ok carrying no result at all.

Accepted risk. Nothing inside result is checked, and a compromised cell chooses all of it. Payload.validate! is not run on the way in, so a value JSON can carry but Ruby cannot serialize again reaches the caller — 1e400 parses to Float::INFINITY, and an application that writes a result to a JSON column raises there rather than here. The premise is that a result is data the caller acts on and only the caller knows its shape, so the framework has no rule to apply and a client with a specific expectation states it itself. ok is the exception because the taxonomy turns on it and every caller depends on it equally.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/hot_cell/response.rb', line 40

def parse(line)
  parse_message(line) do |parsed|
    ok = parsed[:ok]
    unless ok == true || ok == false
      raise MessageError, "response ok is #{ok.inspect} and must be true or false"
    end

    timing = parsed[:timing].is_a?(Hash) ? parsed[:timing] : {}

    if ok
      new version: parsed[:v], result: object(parsed, :result), timing: timing
    else
      new version: parsed[:v], failure: Failure.from_wire(object(parsed, :error)), timing: timing
    end
  end
end

Instance Method Details

#ok?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/hot_cell/response.rb', line 65

def ok?
  failure.nil?
end

#to_lineObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/hot_cell/response.rb', line 69

def to_line
  body = { v: version, ok: ok? }
  if ok?
    body[:result] = Payload.validate!(result, "result")
  else
    body[:error] = failure.to_h
  end
  body[:timing] = Payload.validate!(timing, "timing")

  line = JSON.generate(body) << "\n"
  if line.bytesize > MAX_RESPONSE_BYTES
    raise MessageError, "response is #{line.bytesize} bytes, over the #{MAX_RESPONSE_BYTES} byte limit"
  end

  line
end