Class: Puid::Client
- Inherits:
-
Object
- Object
- Puid::Client
- Defined in:
- lib/puid.rb
Overview
A PUID API client.
Class Method Summary collapse
-
.from_client_credentials(client_id:, client_secret:, scope: "puid:generate", endpoint: DEFAULT_ENDPOINT) ⇒ Object
Exchange OAuth2 client credentials for a bearer token and return a ready client.
- .parse_json(str) ⇒ Object
-
.request(uri, req, what) ⇒ Object
Perform an HTTP request, wrapping transport failures as Puid::Error.
Instance Method Summary collapse
-
#id ⇒ Object
Generate a single id.
-
#ids(count = 1) ⇒ Object
Generate
countids (1–10). -
#initialize(api_key: nil, access_token: nil, endpoint: DEFAULT_ENDPOINT) ⇒ Client
constructor
Provide exactly one of
api_key:(puid_live_…) oraccess_token:(puid_at_…). -
#ordinal(puid) ⇒ Object
Decode a PUID back to the counter value it encodes.
-
#quota ⇒ Object
Today’s usage and remaining daily quota.
Constructor Details
#initialize(api_key: nil, access_token: nil, endpoint: DEFAULT_ENDPOINT) ⇒ Client
Provide exactly one of api_key: (puid_live_…) or access_token: (puid_at_…).
34 35 36 37 38 39 40 |
# File 'lib/puid.rb', line 34 def initialize(api_key: nil, access_token: nil, endpoint: DEFAULT_ENDPOINT) raise Error.new("provide either api_key or access_token, not both") if api_key && access_token raise Error.new("provide an api_key (puid_live_…) or an access_token (puid_at_…)") unless api_key || access_token @endpoint = endpoint.to_s.chomp("/") @auth = access_token ? ["Authorization", "Bearer #{access_token}"] : ["X-API-Key", api_key] end |
Class Method Details
.from_client_credentials(client_id:, client_secret:, scope: "puid:generate", endpoint: DEFAULT_ENDPOINT) ⇒ Object
Exchange OAuth2 client credentials for a bearer token and return a ready client. This is how an app generates ids on a team’s behalf without ever handling the team’s API key.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/puid.rb', line 72 def self.from_client_credentials(client_id:, client_secret:, scope: "puid:generate", endpoint: DEFAULT_ENDPOINT) if client_id.to_s.empty? || client_secret.to_s.empty? raise Error.new("client_id and client_secret are required", code: "invalid_client") end uri = URI("#{endpoint.to_s.chomp("/")}/oauth/token") req = Net::HTTP::Post.new(uri) req.set_form_data(grant_type: "client_credentials", client_id: client_id, client_secret: client_secret, scope: scope) req["Accept"] = "application/json" res = request(uri, req, "token request") body = parse_json(res.body) unless res.is_a?(Net::HTTPSuccess) && body["access_token"] raise Error.new(body["error_description"] || body["error"] || "token request failed with HTTP #{res.code}", status: res.code.to_i, code: body["error"]) end new(access_token: body["access_token"], endpoint: endpoint) end |
.parse_json(str) ⇒ Object
99 100 101 102 103 |
# File 'lib/puid.rb', line 99 def self.parse_json(str) JSON.parse(str.to_s) rescue JSON::ParserError {} end |
.request(uri, req, what) ⇒ Object
Perform an HTTP request, wrapping transport failures as Puid::Error.
93 94 95 96 97 |
# File 'lib/puid.rb', line 93 def self.request(uri, req, what) Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) } rescue StandardError => e raise Error.new("#{what} to PUID failed: #{e.}", code: "network_error") end |
Instance Method Details
#id ⇒ Object
Generate a single id.
52 53 54 |
# File 'lib/puid.rb', line 52 def id ids(1).first end |
#ids(count = 1) ⇒ Object
Generate count ids (1–10). Returns an array of id strings.
43 44 45 46 47 48 49 |
# File 'lib/puid.rb', line 43 def ids(count = 1) unless count.is_a?(Integer) && count.between?(1, 10) raise Error.new("count must be between 1 and 10", code: "invalid_count") end get("/v1/ids?n=#{count}")["ids"] end |
#ordinal(puid) ⇒ Object
Decode a PUID back to the counter value it encodes. Ruby integers are arbitrary precision, so the 128-bit value fits.
58 59 60 61 62 |
# File 'lib/puid.rb', line 58 def ordinal(puid) raise Error.new("puid must be a non-empty string", code: "invalid_puid") unless puid.is_a?(String) && !puid.empty? Integer(get("/v1/ordinal/#{CGI.escape(puid)}")["ordinal"]) end |
#quota ⇒ Object
Today’s usage and remaining daily quota. Does not spend an id.
65 66 67 |
# File 'lib/puid.rb', line 65 def quota get("/v1/quota") end |