Class: TwelvedataRuby::Response
- Inherits:
-
Object
- Object
- TwelvedataRuby::Response
- Defined in:
- lib/twelvedata_ruby/response.rb
Constant Summary collapse
- CSV_COL_SEP =
CSV column separator used by Twelve Data
";"
- BODY_MAX_BYTESIZE =
Maximum response body size to keep in memory
16_000
- HTTP_STATUSES =
HTTP status code ranges
{ http_error: (400..600), success: (200..299), }.freeze
- CONTENT_TYPE_HANDLERS =
Content type handlers for different response formats
{ json: { parser: :parse_json, dumper: :dump_json }, csv: { parser: :parse_csv, dumper: :dump_csv }, plain: { parser: :parse_plain, dumper: :to_s }, }.freeze
Instance Attribute Summary collapse
-
#body_bytesize ⇒ Object
readonly
Returns the value of attribute body_bytesize.
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#http_response ⇒ Object
readonly
Returns the value of attribute http_response.
-
#request ⇒ Object
readonly
Returns the value of attribute request.
Class Method Summary collapse
- .create_error_from_response(http_response, request) ⇒ Object private
- .determine_error_class(status_code) ⇒ Object private
- .extract_error_data(http_response) ⇒ Object private
-
.resolve(http_response, request) ⇒ Response, ResponseError
Resolve HTTP response into Response or ResponseError.
- .success_status?(status) ⇒ Boolean private
-
.valid_status_codes ⇒ Array<Integer>
Get all valid HTTP status codes.
Instance Method Summary collapse
-
#attachment_filename ⇒ String?
Get attachment filename from response headers.
-
#content_type ⇒ Symbol?
Get content type from response.
- #create_response_error ⇒ Object private
- #detect_content_type ⇒ Object private
- #dump_csv ⇒ Object private
- #dump_json ⇒ Object private
-
#dump_parsed_body ⇒ String
Get dumped (serialized) version of parsed body.
-
#error ⇒ ResponseError?
Get response error if present.
- #extract_filename_from_headers ⇒ Object private
- #extract_status_code ⇒ Object private
-
#http_status_code ⇒ Integer
Get HTTP status code.
-
#initialize(http_response:, request:) ⇒ Response
constructor
Initialize response with HTTP response and request.
-
#inspect ⇒ String
Detailed inspection of response.
- #parse_body_content(content) ⇒ Object private
- #parse_csv(content) ⇒ Object private
- #parse_json(content) ⇒ Object private
- #parse_large_body_content ⇒ Object private
- #parse_plain(content) ⇒ Object private
- #parse_response_body ⇒ Object private
-
#parsed_body ⇒ Hash, ...
(also: #body)
Get parsed response body.
-
#save_to_file(file_path = nil) ⇒ File?
Save response to disk file.
-
#status_code ⇒ Integer
Get API status code (from response body).
-
#success? ⇒ Boolean
Check if HTTP response was successful.
-
#to_s ⇒ String
String representation of response.
Constructor Details
#initialize(http_response:, request:) ⇒ Response
Initialize response with HTTP response and request
100 101 102 103 104 105 106 |
# File 'lib/twelvedata_ruby/response.rb', line 100 def initialize(http_response:, request:) @http_response = http_response @request = request @headers = http_response.headers @body_bytesize = http_response.body.bytesize @parsed_body = nil end |
Instance Attribute Details
#body_bytesize ⇒ Object (readonly)
Returns the value of attribute body_bytesize.
94 95 96 |
# File 'lib/twelvedata_ruby/response.rb', line 94 def body_bytesize @body_bytesize end |
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
94 95 96 |
# File 'lib/twelvedata_ruby/response.rb', line 94 def headers @headers end |
#http_response ⇒ Object (readonly)
Returns the value of attribute http_response.
94 95 96 |
# File 'lib/twelvedata_ruby/response.rb', line 94 def http_response @http_response end |
#request ⇒ Object (readonly)
Returns the value of attribute request.
94 95 96 |
# File 'lib/twelvedata_ruby/response.rb', line 94 def request @request end |
Class Method Details
.create_error_from_response(http_response, request) ⇒ Object (private)
62 63 64 65 66 |
# File 'lib/twelvedata_ruby/response.rb', line 62 def create_error_from_response(http_response, request) json_data = extract_error_data(http_response) error_class = determine_error_class(http_response.status) error_class.new(json_data:, request:, status_code: http_response.status, message: json_data[:message]) end |
.determine_error_class(status_code) ⇒ Object (private)
82 83 84 85 86 87 88 89 90 91 |
# File 'lib/twelvedata_ruby/response.rb', line 82 def determine_error_class(status_code) error_class_name = ResponseError.error_class_for_code(status_code, :http) || ResponseError.error_class_for_code(status_code, :api) if error_class_name TwelvedataRuby.const_get(error_class_name) else ResponseError end end |
.extract_error_data(http_response) ⇒ Object (private)
68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/twelvedata_ruby/response.rb', line 68 def extract_error_data(http_response) if http_response.respond_to?(:error) && http_response.error { message: http_response.error., code: http_response.error.class.name, } else { message: http_response.body.to_s, code: http_response.status, } end end |
.resolve(http_response, request) ⇒ Response, ResponseError
Resolve HTTP response into Response or ResponseError
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/twelvedata_ruby/response.rb', line 35 def resolve(http_response, request) if success_status?(http_response.status) new(http_response: http_response, request: request) else create_error_from_response(http_response, request) end rescue StandardError => e ResponseError.new( message: "Failed to resolve response: #{e.}", request: request, original_error: e, ) end |
.success_status?(status) ⇒ Boolean (private)
58 59 60 |
# File 'lib/twelvedata_ruby/response.rb', line 58 def success_status?(status) HTTP_STATUSES[:success].include?(status) end |
.valid_status_codes ⇒ Array<Integer>
Get all valid HTTP status codes
52 53 54 |
# File 'lib/twelvedata_ruby/response.rb', line 52 def valid_status_codes @valid_status_codes ||= HTTP_STATUSES.values.flat_map(&:to_a) end |
Instance Method Details
#attachment_filename ⇒ String?
Get attachment filename from response headers
111 112 113 114 115 |
# File 'lib/twelvedata_ruby/response.rb', line 111 def return nil unless headers["content-disposition"] @attachment_filename ||= extract_filename_from_headers end |
#content_type ⇒ Symbol?
Get content type from response
120 121 122 |
# File 'lib/twelvedata_ruby/response.rb', line 120 def content_type @content_type ||= detect_content_type end |
#create_response_error ⇒ Object (private)
302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/twelvedata_ruby/response.rb', line 302 def create_response_error error_class_name = ResponseError.error_class_for_code(status_code) error_class = error_class_name ? TwelvedataRuby.const_get(error_class_name) : ResponseError error_class.new( json_data: parsed_body, request: request, status_code: status_code, message: parsed_body[:message], ) end |
#detect_content_type ⇒ Object (private)
217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/twelvedata_ruby/response.rb', line 217 def detect_content_type content_type_header = headers["content-type"] return :plain unless content_type_header case content_type_header when /json/ :json when /csv/ :csv else :plain end end |
#dump_csv ⇒ Object (private)
288 289 290 291 292 |
# File 'lib/twelvedata_ruby/response.rb', line 288 def dump_csv return nil unless parsed_body.is_a?(CSV::Table) parsed_body.to_csv(col_sep: CSV_COL_SEP) end |
#dump_json ⇒ Object (private)
282 283 284 285 286 |
# File 'lib/twelvedata_ruby/response.rb', line 282 def dump_json return nil unless parsed_body.is_a?(Hash) JSON.dump(parsed_body) end |
#dump_parsed_body ⇒ String
Get dumped (serialized) version of parsed body
183 184 185 186 187 188 |
# File 'lib/twelvedata_ruby/response.rb', line 183 def dump_parsed_body handler = CONTENT_TYPE_HANDLERS[content_type] return parsed_body.to_s unless handler send(handler[:dumper]) end |
#error ⇒ ResponseError?
Get response error if present
135 136 137 138 139 |
# File 'lib/twelvedata_ruby/response.rb', line 135 def error return nil unless parsed_body.is_a?(Hash) && parsed_body.key?(:code) @error ||= create_response_error end |
#extract_filename_from_headers ⇒ Object (private)
208 209 210 211 212 213 214 215 |
# File 'lib/twelvedata_ruby/response.rb', line 208 def extract_filename_from_headers disposition = headers["content-disposition"] return nil unless disposition # Extract filename from Content-Disposition header match = disposition.match(/filename="([^"]+)"/) match ? match[1] : nil end |
#extract_status_code ⇒ Object (private)
294 295 296 297 298 299 300 |
# File 'lib/twelvedata_ruby/response.rb', line 294 def extract_status_code if parsed_body.is_a?(Hash) && parsed_body[:code] parsed_body[:code] else http_status_code end end |
#http_status_code ⇒ Integer
Get HTTP status code
144 145 146 |
# File 'lib/twelvedata_ruby/response.rb', line 144 def http_status_code http_response.status end |
#inspect ⇒ String
Detailed inspection of response
201 202 203 204 |
# File 'lib/twelvedata_ruby/response.rb', line 201 def inspect "#<#{self.class.name}:#{object_id} status=#{http_status_code} content_type=#{content_type} " \ "size=#{body_bytesize} error=#{error ? "yes" : "no"}>" end |
#parse_body_content(content) ⇒ Object (private)
245 246 247 248 249 250 |
# File 'lib/twelvedata_ruby/response.rb', line 245 def parse_body_content(content) handler = CONTENT_TYPE_HANDLERS[content_type] return content unless handler send(handler[:parser], content) end |
#parse_csv(content) ⇒ Object (private)
269 270 271 272 273 274 275 276 |
# File 'lib/twelvedata_ruby/response.rb', line 269 def parse_csv(content) CSV.parse(content, headers: true, col_sep: CSV_COL_SEP) rescue CSV::MalformedCSVError => e raise ResponseError.new( message: "Failed to parse CSV response: #{e.}", original_error: e, ) end |
#parse_json(content) ⇒ Object (private)
260 261 262 263 264 265 266 267 |
# File 'lib/twelvedata_ruby/response.rb', line 260 def parse_json(content) JSON.parse(content, symbolize_names: true) rescue JSON::ParserError => e raise ResponseError.new( message: "Failed to parse JSON response: #{e.}", original_error: e, ) end |
#parse_large_body_content ⇒ Object (private)
252 253 254 255 256 257 258 |
# File 'lib/twelvedata_ruby/response.rb', line 252 def parse_large_body_content Tempfile.create do |temp_file| http_response.body.copy_to(temp_file) temp_file.rewind parse_body_content(temp_file.read) end end |
#parse_plain(content) ⇒ Object (private)
278 279 280 |
# File 'lib/twelvedata_ruby/response.rb', line 278 def parse_plain(content) content.to_s end |
#parse_response_body ⇒ Object (private)
231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/twelvedata_ruby/response.rb', line 231 def parse_response_body return nil unless http_response.body begin if body_bytesize < BODY_MAX_BYTESIZE parse_body_content(http_response.body.to_s) else parse_large_body_content end ensure http_response.body.close if http_response.body.respond_to?(:close) end end |
#parsed_body ⇒ Hash, ... Also known as: body
Get parsed response body
127 128 129 |
# File 'lib/twelvedata_ruby/response.rb', line 127 def parsed_body @parsed_body ||= parse_response_body end |
#save_to_file(file_path = nil) ⇒ File?
Save response to disk file
166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/twelvedata_ruby/response.rb', line 166 def save_to_file(file_path = nil) file_path ||= return nil unless file_path File.open(file_path, "w") do |file| file.write(dump_parsed_body) end rescue StandardError => e raise ResponseError.new( message: "Failed to save response to file: #{e.}", original_error: e, ) end |
#status_code ⇒ Integer
Get API status code (from response body)
151 152 153 |
# File 'lib/twelvedata_ruby/response.rb', line 151 def status_code @status_code ||= extract_status_code end |
#success? ⇒ Boolean
Check if HTTP response was successful
158 159 160 |
# File 'lib/twelvedata_ruby/response.rb', line 158 def success? HTTP_STATUSES[:success].include?(http_status_code) end |
#to_s ⇒ String
String representation of response
193 194 195 196 |
# File 'lib/twelvedata_ruby/response.rb', line 193 def to_s status_info = "#{http_status_code} (#{success? ? "success" : "error"})" "Response: #{status_info}, Content-Type: #{content_type}, Size: #{body_bytesize} bytes" end |