sorted-travel 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d9bfa8d4533c0e406b5b22e215cd77fc14d56680f6ad5d6d70c680b02cc7ddb
4
+ data.tar.gz: 73b1eb43b52c5c3fca221ed04e67e074d7287bea6214134e737d6519e0bdf5fb
5
+ SHA512:
6
+ metadata.gz: 8a3cf8bb19139741a2dd313fcc0ef2f54a9cd0a25665913fc0173ddb1bdb07a93597ea10a3c081a436987c3fc9c725e9eac55671d0bc5c2d3853b7ddacc400bf
7
+ data.tar.gz: faae7eea35289cfcc448d065a2bf907545d32a28328c69e10f593098d7b64e631d153a7b4a657bf6c87597df308876a1729d763b244bfada050d8346880f1364
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # sorted-travel Ruby SDK
2
+
3
+ Official Ruby client and CLI for the [Sorted Travel](https://sorted.travel) REST API.
4
+
5
+ - Homepage: [https://sorted.travel](https://sorted.travel)
6
+ - Developer portal: [https://sorted.travel/developers](https://sorted.travel/developers)
7
+ - SDK guide: [https://sorted.travel/sdks.md](https://sorted.travel/sdks.md)
8
+ - Repository: [https://github.com/a-l-e-x-k/sorted-travel-sdk](https://github.com/a-l-e-x-k/sorted-travel-sdk)
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ gem install sorted-travel
14
+ ```
15
+
16
+ ## Quickstart
17
+
18
+ ```ruby
19
+ require "sorted_travel"
20
+
21
+ client = SortedTravel::Client.new
22
+ client.status
23
+ client.list_destinations(limit: 20)
24
+ ```
25
+
26
+ Destination catalog, status, sandbox, and jobs are zero-auth. Pass `api_key:` or set `SORTED_TRAVEL_API_KEY` only when a host requires Bearer auth.
27
+
28
+ Interactive ranking, weather, and visa still go through MCP at `https://sorted.travel/mcp`.
data/bin/sorted-travel ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/sorted_travel"
5
+
6
+ exit SortedTravel::CLI.run(ARGV)
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "optparse"
5
+
6
+ module SortedTravel
7
+ # Command-line entry point for the Sorted Travel REST SDK.
8
+ module CLI
9
+ module_function
10
+
11
+ # Run the sorted-travel CLI and return an exit code.
12
+ def run(argv = ARGV)
13
+ options = { api_key: nil, base_url: nil }
14
+ parser = OptionParser.new do |opts|
15
+ opts.banner = "Usage: sorted-travel [options] COMMAND"
16
+ opts.on("--base-url URL", "API origin (default https://sorted.travel or SORTED_TRAVEL_BASE_URL)") do |value|
17
+ options[:base_url] = value
18
+ end
19
+ opts.on("--api-key KEY", "Optional Bearer token (default SORTED_TRAVEL_API_KEY)") do |value|
20
+ options[:api_key] = value
21
+ end
22
+ end
23
+
24
+ positional = parser.order(argv)
25
+ parser.parse!(argv)
26
+ command = positional.shift
27
+ if command.nil?
28
+ warn parser
29
+ return 2
30
+ end
31
+
32
+ client = Client.new(api_key: options[:api_key], base_url: options[:base_url])
33
+
34
+ case command
35
+ when "status"
36
+ print_json(client.status)
37
+ when "sandbox"
38
+ print_json(client.sandbox)
39
+ when "api-key"
40
+ print_json(client.create_api_key)
41
+ when "destinations"
42
+ destination_options = { cursor: nil, limit: nil }
43
+ OptionParser.new do |opts|
44
+ opts.on("--cursor VALUE") { |value| destination_options[:cursor] = value }
45
+ opts.on("--limit INTEGER", Integer) { |value| destination_options[:limit] = value }
46
+ end.parse!(positional)
47
+ print_json(client.list_destinations(**destination_options))
48
+ when "create-job"
49
+ operation = positional.shift
50
+ if operation.nil?
51
+ warn "missing operation"
52
+ return 2
53
+ end
54
+ job_options = {}
55
+ OptionParser.new do |opts|
56
+ opts.on("--limit INTEGER", Integer) { |value| job_options[:limit] = value }
57
+ end.parse!(positional)
58
+ print_json(client.create_job(operation, **job_options))
59
+ when "job"
60
+ job_id = positional.shift
61
+ if job_id.nil?
62
+ warn "missing job_id"
63
+ return 2
64
+ end
65
+ print_json(client.get_job(job_id))
66
+ else
67
+ warn "unknown command #{command}"
68
+ return 2
69
+ end
70
+ 0
71
+ rescue OptionParser::ParseError => err
72
+ warn err.message
73
+ 2
74
+ end
75
+
76
+ def print_json(value)
77
+ puts JSON.pretty_generate(value)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "net/http"
5
+ require "uri"
6
+
7
+ module SortedTravel
8
+ # Base error for this SDK.
9
+ class SortedTravelError < StandardError
10
+ end
11
+
12
+ # Returned for non-2xx HTTP responses.
13
+ class APIError < SortedTravelError
14
+ attr_reader :status, :body
15
+
16
+ # Store HTTP status and parsed response body.
17
+ def initialize(status, body)
18
+ @status = status
19
+ @body = body
20
+ super("HTTP #{status}: #{truncate_body(body)}")
21
+ end
22
+
23
+ private
24
+
25
+ def truncate_body(value, limit = 300)
26
+ text = value.is_a?(String) ? value : JSON.generate(value)
27
+ return text if text.length <= limit
28
+
29
+ "#{text[0, limit - 1]}…"
30
+ end
31
+ end
32
+
33
+ # Thin client for the Sorted Travel REST API.
34
+ class Client
35
+ DEFAULT_BASE_URL = "https://sorted.travel"
36
+ DEFAULT_TIMEOUT = 30.0
37
+ USER_AGENT = "sorted-travel-ruby/#{SortedTravel::VERSION} (+https://sorted.travel)"
38
+
39
+ # Build a client from options and optional environment variables.
40
+ def initialize(api_key: nil, base_url: nil, timeout: DEFAULT_TIMEOUT, transport: nil, env: ENV)
41
+ @api_key = api_key || env["SORTED_TRAVEL_API_KEY"]
42
+ configured_base = base_url || env["SORTED_TRAVEL_BASE_URL"] || DEFAULT_BASE_URL
43
+ @base_url = configured_base.sub(%r{/+\z}, "")
44
+ @timeout = timeout
45
+ @transport = transport || method(:default_transport)
46
+ end
47
+
48
+ # GET /api/v1/status.
49
+ def status
50
+ get("/api/v1/status")
51
+ end
52
+
53
+ # GET /sandbox.
54
+ def sandbox
55
+ get("/sandbox")
56
+ end
57
+
58
+ # GET /api/v1/destinations with cursor pagination.
59
+ def list_destinations(cursor: nil, limit: nil)
60
+ params = {}
61
+ params["cursor"] = cursor unless cursor.nil?
62
+ params["limit"] = limit unless limit.nil?
63
+ get("/api/v1/destinations", params)
64
+ end
65
+
66
+ # POST /api/v1/api-keys.
67
+ def create_api_key
68
+ post("/api/v1/api-keys", {})
69
+ end
70
+
71
+ # POST /api/v1/jobs.
72
+ def create_job(operation, **body)
73
+ post("/api/v1/jobs", { operation: operation, **body })
74
+ end
75
+
76
+ # GET /api/v1/jobs/{job_id}.
77
+ def get_job(job_id)
78
+ encoded_job_id = URI.encode_www_form_component(job_id)
79
+ get("/api/v1/jobs/#{encoded_job_id}")
80
+ end
81
+
82
+ # GET a host-relative REST path.
83
+ def get(path, params = {})
84
+ request("GET", path, params: params)
85
+ end
86
+
87
+ # POST JSON to a host-relative REST path.
88
+ def post(path, body)
89
+ request("POST", path, body: body)
90
+ end
91
+
92
+ private
93
+
94
+ def request(method, path, params: {}, body: nil)
95
+ raise ArgumentError, "REST paths must start with '/'" unless path.start_with?("/")
96
+
97
+ url = @base_url + path
98
+ unless params.empty?
99
+ query = URI.encode_www_form(stringify_params(params))
100
+ url = "#{url}?#{query}"
101
+ end
102
+
103
+ headers = {
104
+ "User-Agent" => USER_AGENT,
105
+ "Accept" => "application/json",
106
+ }
107
+ encoded_body = nil
108
+ unless body.nil?
109
+ headers["Content-Type"] = "application/json"
110
+ encoded_body = JSON.generate(body)
111
+ end
112
+ headers["Authorization"] = "Bearer #{@api_key}" if @api_key
113
+
114
+ status_code, _content_type, text = @transport.call(
115
+ url: url,
116
+ method: method,
117
+ headers: headers,
118
+ body: encoded_body,
119
+ timeout: @timeout,
120
+ )
121
+ value = parse_body(text)
122
+ raise APIError.new(status_code, value) if status_code < 200 || status_code >= 300
123
+
124
+ value
125
+ end
126
+
127
+ def stringify_params(params)
128
+ params.transform_values do |value|
129
+ case value
130
+ when true
131
+ "true"
132
+ when false
133
+ "false"
134
+ else
135
+ value.to_s
136
+ end
137
+ end
138
+ end
139
+
140
+ def parse_body(text)
141
+ return text if text.nil? || text.empty?
142
+
143
+ JSON.parse(text)
144
+ rescue JSON::ParserError
145
+ text
146
+ end
147
+
148
+ def default_transport(url:, method:, headers:, body:, timeout:)
149
+ uri = URI(url)
150
+ http = Net::HTTP.new(uri.host, uri.port)
151
+ http.use_ssl = uri.scheme == "https"
152
+ http.open_timeout = timeout
153
+ http.read_timeout = timeout
154
+
155
+ request_class = Net::HTTP.const_get(method.capitalize)
156
+ http_request = request_class.new(uri)
157
+ headers.each { |key, value| http_request[key] = value }
158
+ http_request.body = body if body
159
+
160
+ response = http.request(http_request)
161
+ [response.code.to_i, response["Content-Type"], response.body]
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SortedTravel
4
+ # Gem version, kept in sync with other language SDKs.
5
+ VERSION = "0.1.1"
6
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sorted_travel/version"
4
+ require_relative "sorted_travel/client"
5
+ require_relative "sorted_travel/cli"
6
+
7
+ module SortedTravel
8
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sorted-travel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Sorted Travel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-09-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Official Ruby SDK and CLI for the Sorted Travel REST API.
14
+ email:
15
+ - support@sorted.travel
16
+ executables:
17
+ - sorted-travel
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - bin/sorted-travel
23
+ - lib/sorted_travel.rb
24
+ - lib/sorted_travel/cli.rb
25
+ - lib/sorted_travel/client.rb
26
+ - lib/sorted_travel/version.rb
27
+ homepage: https://sorted.travel
28
+ licenses:
29
+ - MIT
30
+ metadata:
31
+ homepage_uri: https://sorted.travel
32
+ source_code_uri: https://github.com/a-l-e-x-k/sorted-travel-sdk
33
+ documentation_uri: https://sorted.travel/sdks.md
34
+ rubygems_mfa_required: 'true'
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 3.1.0
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.5.22
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Official Ruby SDK and CLI for the Sorted Travel REST API.
54
+ test_files: []