sudhanva 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbe82330602c646a93171c209c60239fc9ada05a2f6ec12595f0f18c8daff762
4
- data.tar.gz: 8b1a707ca41396832b107df893fa08032832663b63610a4c9fc26ed16f306960
3
+ metadata.gz: 6efe7d85f59f7bfcb72b959ff3a183467a8803376fa3877778ce2ddd005ff33e
4
+ data.tar.gz: e530372b176598e5c49ad188f734d15095e3a932c414ab633f3bef1be646fa60
5
5
  SHA512:
6
- metadata.gz: d2c6bcddb7efe2d5671a28ca756f6cd0dc4e72c534bd61266ae1aebdca98e2e9628814fd534483f78b0aba3bba111d8873a1852232c4752bd4c88ce7c62e264b
7
- data.tar.gz: 5edcb475d5f20bca97689cf312afa7242818d17e0858f6af9da75c21c2482cda3df7fc51a6eaab752cfbf55531d0f25d3b954699392f1a44e3f697699ead225d
6
+ metadata.gz: 692ad73bb3d682ecc4c1a9c9c8ebe9334361f608c23ab06d488e2bd60d1c530c134121cf8fcc05839c333fee1f4aafdb482c07f058eea25ac8789e27d3b6a711
7
+ data.tar.gz: 6a4cafdade895142218580a49ed98324ac9d980cdc5f61a14630fee6664de2d1d599aaaec73dd9407281a1c5e3d3045e78bbf58835ebc563fa37902192121b43
data/README.md CHANGED
@@ -9,14 +9,16 @@ The API is public and requires no credentials. Do not send private data.
9
9
 
10
10
  ## Install
11
11
 
12
+ Requires Ruby 3.3 or newer.
13
+
12
14
  ```bash
13
- gem install sudhanva
15
+ gem install sudhanva --version 0.2.0
14
16
  ```
15
17
 
16
18
  Or add it to a `Gemfile`:
17
19
 
18
20
  ```ruby
19
- gem "sudhanva", "~> 0.1"
21
+ gem "sudhanva", "~> 0.2.0"
20
22
  ```
21
23
 
22
24
  ## Use
@@ -39,7 +41,9 @@ result = client.wait_for_profile_insight(job["job_id"])
39
41
  ```
40
42
 
41
43
  All methods return decoded JSON hashes. Non-success responses raise `Sudhanva::APIError` with
42
- `status`, `code`, and the decoded response body.
44
+ `status`, `code`, `hint`, `docs_url`, and the decoded response body; the exception message includes
45
+ the API's explanation. RFC 9457 problem responses from the profile-insight endpoints map `detail`
46
+ (or `title`) to the message and the last segment of `type` to `code`.
43
47
 
44
48
  ## API coverage
45
49
 
@@ -8,14 +8,55 @@ module Sudhanva
8
8
  Response = Struct.new(:status, :headers, :body, keyword_init: true)
9
9
 
10
10
  class APIError < StandardError
11
- attr_reader :status, :code, :body
11
+ attr_reader :status, :code, :body, :hint, :docs_url
12
12
 
13
- def initialize(status:, code:, message:, body:)
13
+ def initialize(status:, code:, message:, body:, hint: nil, docs_url: nil)
14
14
  super("#{status} #{code}: #{message}")
15
15
  @status = status
16
16
  @code = code
17
17
  @body = body
18
+ @hint = hint
19
+ @docs_url = docs_url
18
20
  end
21
+
22
+ # Builds an error from the standard envelope or an RFC 9457 problem.
23
+ def self.from_response(status, payload)
24
+ body = payload.is_a?(Hash) ? payload : {}
25
+ nested = body["error"]
26
+ error = nested.is_a?(Hash) ? nested : body
27
+
28
+ code = text(error["code"]) || text(body["code"])
29
+ message = text(error["message"]) || text(body["message"])
30
+ if code.nil? && message.nil?
31
+ title = text(body["title"])
32
+ code = problem_code(text(body["type"])) || title
33
+ message = text(body["detail"]) || title
34
+ end
35
+ message ||= nested if nested.is_a?(String) && !nested.empty?
36
+
37
+ new(
38
+ status: status,
39
+ code: code || "api_error",
40
+ message: message || "Request failed",
41
+ body: payload,
42
+ hint: text(error["hint"]),
43
+ docs_url: text(error["docs_url"])
44
+ )
45
+ end
46
+
47
+ def self.text(value)
48
+ value if value.is_a?(String) && !value.empty?
49
+ end
50
+
51
+ # Returns the last fragment or path segment of a problem type URI.
52
+ def self.problem_code(type)
53
+ return nil if type.nil? || type == "about:blank"
54
+
55
+ base, _, fragment = type.partition("#")
56
+ text(fragment) || text(base.sub(%r{/+\z}, "").split("/").last)
57
+ end
58
+
59
+ private_class_method :text, :problem_code
19
60
  end
20
61
 
21
62
  class Client
@@ -127,10 +168,7 @@ module Sudhanva
127
168
  response = @transport.call(method, uri, request_headers, body, @timeout)
128
169
  payload = response.body.to_s.empty? ? {} : JSON.parse(response.body)
129
170
  unless response.status.between?(200, 299)
130
- error = payload.fetch("error", payload)
131
- code = error.fetch("code", payload.fetch("code", "api_error"))
132
- message = error.fetch("message", payload.fetch("message", "Request failed"))
133
- raise APIError.new(status: response.status, code: code, message: message, body: payload)
171
+ raise APIError.from_response(response.status, payload)
134
172
  end
135
173
 
136
174
  raise APIError.new(status: response.status, code: "invalid_response", message: "API returned a non-object response", body: payload) unless payload.is_a?(Hash)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sudhanva
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sudhanva
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sudhanva Narayana
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-08-24 00:00:00.000000000 Z
12
- dependencies: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: minitest
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.25'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.25'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '13.2'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.2'
13
40
  description: A dependency-free client for published profile data, articles, search,
14
41
  batch reads, and profile-insight jobs.
15
42
  email:
@@ -32,7 +59,6 @@ metadata:
32
59
  documentation_uri: https://sudhanva.me/developers/sdks/
33
60
  bug_tracker_uri: https://github.com/nsudhanva/sudhanva-ruby/issues
34
61
  rubygems_mfa_required: 'true'
35
- post_install_message:
36
62
  rdoc_options: []
37
63
  require_paths:
38
64
  - lib
@@ -40,15 +66,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
40
66
  requirements:
41
67
  - - ">="
42
68
  - !ruby/object:Gem::Version
43
- version: '3.1'
69
+ version: '3.3'
44
70
  required_rubygems_version: !ruby/object:Gem::Requirement
45
71
  requirements:
46
72
  - - ">="
47
73
  - !ruby/object:Gem::Version
48
74
  version: '0'
49
75
  requirements: []
50
- rubygems_version: 3.0.3.1
51
- signing_key:
76
+ rubygems_version: 4.0.20
52
77
  specification_version: 4
53
78
  summary: Minimal Ruby client for the public sudhanva.me API.
54
79
  test_files: []