Class: SmilyCli::Result

Inherits:
Object
  • Object
show all
Defined in:
lib/smily_cli/result.rb

Overview

A normalized view over one or more API v3 responses. It pulls the record array out of the JSON:API envelope (the single top-level key that isn't links/linked/meta), while keeping the raw envelope, meta, sideloaded linked records, pagination relations and rate-limit headers available for formatters and verbose output.

Constant Summary collapse

SPECIAL_KEYS =
%w[links linked meta].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(records:, single: false, envelope: nil, key: nil, status: nil, headers: {}) ⇒ Result

Returns a new instance of Result.

Parameters:

  • records (Array<Hash>)
  • single (Boolean) (defaults to: false)

    whether this represents one member (get/create)

  • envelope (Hash, nil) (defaults to: nil)
  • key (String, nil) (defaults to: nil)
  • status (Integer, nil) (defaults to: nil)
  • headers (Hash) (defaults to: {})


35
36
37
38
39
40
41
42
43
44
# File 'lib/smily_cli/result.rb', line 35

def initialize(records:, single: false, envelope: nil, key: nil, status: nil, headers: {})
  @records = records
  @single = single
  @envelope = envelope
  @key = key
  @meta = envelope.is_a?(Hash) ? envelope["meta"] : nil
  @linked = envelope.is_a?(Hash) ? envelope["linked"] : nil
  @status = status
  @headers = headers || {}
end

Instance Attribute Details

#envelopeHash? (readonly)

Returns the raw parsed envelope of the last response.

Returns:

  • (Hash, nil)

    the raw parsed envelope of the last response



17
18
19
# File 'lib/smily_cli/result.rb', line 17

def envelope
  @envelope
end

#headersHash (readonly)

Returns response headers of the last response.

Returns:

  • (Hash)

    response headers of the last response



25
26
27
# File 'lib/smily_cli/result.rb', line 25

def headers
  @headers
end

#keyString? (readonly)

Returns resolved JSON:API key the records live under.

Returns:

  • (String, nil)

    resolved JSON:API key the records live under



27
28
29
# File 'lib/smily_cli/result.rb', line 27

def key
  @key
end

#linkedHash? (readonly)

Returns sideloaded associations (linked).

Returns:

  • (Hash, nil)

    sideloaded associations (linked)



21
22
23
# File 'lib/smily_cli/result.rb', line 21

def linked
  @linked
end

#metaHash? (readonly)

Returns JSON:API meta.

Returns:

  • (Hash, nil)

    JSON:API meta



19
20
21
# File 'lib/smily_cli/result.rb', line 19

def meta
  @meta
end

#recordsArray<Hash> (readonly)

Returns the resource records (always an array).

Returns:

  • (Array<Hash>)

    the resource records (always an array)



15
16
17
# File 'lib/smily_cli/result.rb', line 15

def records
  @records
end

#statusInteger? (readonly)

Returns HTTP status of the last response.

Returns:

  • (Integer, nil)

    HTTP status of the last response



23
24
25
# File 'lib/smily_cli/result.rb', line 23

def status
  @status
end

Class Method Details

.empty(single: false, status: nil, headers: {}) ⇒ Result

An empty result (e.g. a 204 delete). records is empty.

Returns:



70
71
72
# File 'lib/smily_cli/result.rb', line 70

def self.empty(single: false, status: nil, headers: {})
  new(records: [], single: single, envelope: nil, key: nil, status: status, headers: headers)
end

.extract_records(body, key) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



138
139
140
141
142
143
144
145
146
147
# File 'lib/smily_cli/result.rb', line 138

def self.extract_records(body, key)
  return [] unless body.is_a?(Hash) && key

  # v3 always wraps records in an array, but tolerate a bare object too.
  case (value = body[key])
  when Array then value
  when nil then []
  else [value]
  end
end

.from_body(body, single: false) ⇒ Result

Build a Result from an already-parsed JSON:API envelope (e.g. the payload an MCP tool returns), rather than an HTTP response.

Parameters:

  • body (Hash)
  • single (Boolean) (defaults to: false)

Returns:



80
81
82
83
# File 'lib/smily_cli/result.rb', line 80

def self.from_body(body, single: false)
  key = resource_key(body)
  new(records: extract_records(body, key), single: single, envelope: body, key: key)
end

.from_response(response, single: false) ⇒ Result

Build a Result from a single BookingSync::API::Response.

Parameters:

  • response (BookingSync::API::Response, nil)

    nil for 204 No Content

  • single (Boolean) (defaults to: false)

Returns:



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/smily_cli/result.rb', line 51

def self.from_response(response, single: false)
  return empty(single: single) if response.nil?

  body = parse(response.body)
  key = resource_key(body)
  records = extract_records(body, key)
  new(
    records: records,
    single: single,
    envelope: body,
    key: key,
    status: response.status,
    headers: response.headers
  )
end

.parse(raw) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



122
123
124
125
126
127
128
# File 'lib/smily_cli/result.rb', line 122

def self.parse(raw)
  return {} if raw.nil? || raw.to_s.empty?

  JSON.parse(raw)
rescue JSON::ParserError
  {}
end

.resource_key(body) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



131
132
133
134
135
# File 'lib/smily_cli/result.rb', line 131

def self.resource_key(body)
  return nil unless body.is_a?(Hash)

  (body.keys.map(&:to_s) - SPECIAL_KEYS).first
end

Instance Method Details

#countInteger

Returns:

  • (Integer)


101
102
103
# File 'lib/smily_cli/result.rb', line 101

def count
  records.length
end

#empty?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/smily_cli/result.rb', line 96

def empty?
  records.empty?
end

#rate_limit_remainingInteger?

Remaining requests in the current rate-limit window, if reported.

Returns:

  • (Integer, nil)


108
109
110
111
# File 'lib/smily_cli/result.rb', line 108

def rate_limit_remaining
  value = headers["X-RateLimit-Remaining"] || headers["x-ratelimit-remaining"]
  value&.to_i
end

#recordHash?

Returns the sole record, when #single?.

Returns:

  • (Hash, nil)

    the sole record, when #single?



91
92
93
# File 'lib/smily_cli/result.rb', line 91

def record
  records.first
end

#single?Boolean

Returns whether this result represents a single member.

Returns:

  • (Boolean)

    whether this result represents a single member



86
87
88
# File 'lib/smily_cli/result.rb', line 86

def single?
  @single
end

#total_pagesInteger?

Total number of pages reported by the API, if any.

Returns:

  • (Integer, nil)


116
117
118
119
# File 'lib/smily_cli/result.rb', line 116

def total_pages
  value = headers["X-Total-Pages"] || headers["x-total-pages"]
  value&.to_i
end