tiny-rest-client 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bbbf391dfae3b7c9331328d57465ee906873ada7424306f2338c33802a46f1d4
4
+ data.tar.gz: 300105bfe2e54479fb613cb977188b6eea7e80eafd56442a0f2ac9a85387fe9d
5
+ SHA512:
6
+ metadata.gz: ba5ff7eb795d3b30c97c96da58f5831a504a411730ae30f171debceac4d2254226929a100b6b264521c3c450f8028e71a768f8a54edc8c63bcd064404eb2b760
7
+ data.tar.gz: 1ced9f35ca6a4df30e55039dae8d1927143c95de9460bcc157e3e40d43e1c421814ddd33d27b84202d3718f1b761ab736773ace5adb27cd970247fe67f203013
data/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-02-22
9
+
10
+ ### Added
11
+ - First public release
12
+ - Core client class with DSL for api_path and authorization
13
+ - Authorization strategies:
14
+ - Bearer
15
+ - API Key with configurable location and name
16
+ - Basic Auth
17
+ - Request wrapper class around Typhoeus
18
+ - Simple success/failure response helpers
19
+ - Full Minitest suite (classic + spec style examples)
20
+ - Basic README documentation
21
+
22
+ [Unreleased]: https://github.com/Dabrovsky/tiny_rest_client/compare/v0.1.0...HEAD
23
+ [0.1.0]: https://github.com/Dabrovsky/tiny_rest_client/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Dabrovski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,232 @@
1
+ # TinyRestClient
2
+
3
+ A minimal, opinionated HTTP client wrapper for Rails, built on top of Typhoeus. Perfect for quickly building clean, opinionated API clients with almost no boilerplate.
4
+
5
+ ### Features
6
+
7
+ - Simple class-level DSL for configuration
8
+ - Built-in support for Bearer, API Key and Basic Auth
9
+ - Chainable-like request methods (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
10
+
11
+ ## Why TinyRestClient?
12
+
13
+ - Small, focused API clients
14
+ - Internal services and microservices
15
+ - Rails apps that want minimal abstraction
16
+ - Developers who prefer convention over configuration
17
+
18
+ ## Table of Contents
19
+
20
+ - [Installation](#installation)
21
+ - [Basic Usage](#basic-usage)
22
+ - [Configuration](#configuration)
23
+ - [Authorization](#authorization)
24
+ - [Making Requests](#making-requests)
25
+ - [Response Handling](#response-handling)
26
+ - [Contributing](#contributing)
27
+ - [License](#license)
28
+
29
+ ## Installation
30
+
31
+ Add this line to your Gemfile:
32
+
33
+ ```bash
34
+ gem "tiny-rest-client"
35
+ ```
36
+
37
+ Or install it manually:
38
+
39
+ ```bash
40
+ gem install tiny-rest-client
41
+ ```
42
+
43
+ ## Basic Usage
44
+
45
+ ```ruby
46
+ class MyClient < TinyRestClient::Core
47
+ # define base API path (required)
48
+ api_path "https://api.example.com/v1"
49
+
50
+ # set an authorization strategy (optional)
51
+ authorization :bearer, token: "your_token"
52
+
53
+ # pass single header (optional)
54
+ header "Accept", "application/json"
55
+
56
+ # pass bunch of headers (optional)
57
+ headers "User-Agent": "MyTest/1.0", "Accept": "application/json"
58
+
59
+ # define actions
60
+ def fetch_todos(**)
61
+ get("/todos", **)
62
+ end
63
+
64
+ def fetch_todo(id)
65
+ get("/todos/#{id}")
66
+ end
67
+
68
+ def create_todo(**)
69
+ post("/todos", **)
70
+ end
71
+
72
+ def update_todo(id, **)
73
+ patch("/todos/#{id}", **)
74
+ end
75
+
76
+ def destroy_todo(id)
77
+ delete("/todos/#{id}")
78
+ end
79
+ end
80
+
81
+ client = MyClient.new
82
+
83
+ client.fetch_todos(page: 1, per_page: 20)
84
+ client.fetch_todo(1)
85
+ client.create_todo(name: "Custom")
86
+ client.update_todo(status: "finished")
87
+ client.destroy_todo(1)
88
+ ```
89
+
90
+ ## Configuration
91
+
92
+ You can configure client setup at the class level:
93
+
94
+ ```ruby
95
+ class MyClient < TinyRestClient::Core
96
+ api_path "https://api.example.com/v1"
97
+ authorization :bearer, token: "your_token"
98
+ headers "Accept": "application/json", "User-Agent", "MyApp/1.0"
99
+ end
100
+
101
+ client = MyClient.new
102
+ ```
103
+
104
+ Or per instance:
105
+
106
+ ```ruby
107
+ class MyClient < TinyRestClient::Core; end
108
+
109
+ client = MyClient.new(
110
+ api_path: "https://api.example.com/v1",
111
+ auth: { bearer: { token: "your_token" } },
112
+ headers: { "User-Agent": "MyApp/1.0" }
113
+ )
114
+ ```
115
+
116
+ ## Authorization
117
+
118
+ The library ships with a small set of built‑in strategies:
119
+
120
+ * Bearer Token
121
+ * API Keys
122
+ * Basic Auth
123
+
124
+ You can configure authentication either at the class level using the
125
+ `authorization` helper or at runtime via the `auth` key when initializing a
126
+ client (see "Configuration" above).
127
+
128
+ #### Options Overview
129
+
130
+ ```ruby
131
+ # Bearer
132
+ :bearer, token: String # Authorization: Bearer <token>
133
+
134
+ # API key
135
+ :api_key, token: String # X-API-Key: <token>
136
+ :api_key, token: String, name: String # <Custom>: <token>
137
+ :api_key, token: String, in: :param # ?api_key=<token>
138
+ :api_key, token: String, in: :param, name: String # ?<custom>=<token>
139
+
140
+ # Basic
141
+ :basic_auth, user: String, password: String # Authorization: Basic <base64(user:password)>
142
+ ```
143
+
144
+ ### Bearer Token
145
+
146
+ ```ruby
147
+ class MyClient < TinyRestClient::Core
148
+ authorization :bearer, token: "your_token"
149
+ end
150
+
151
+ # per-instance
152
+ MyClient.new(auth: { bearer: { token: "your_token" } })
153
+ ```
154
+
155
+ ### API Key
156
+
157
+ ```ruby
158
+ class MyClient < TinyRestClient::Core
159
+ authorization :api_key, token: "your_token"
160
+ end
161
+
162
+ # per-instance
163
+ MyClient.new(auth: { api_key: { token: "your_token" } })
164
+ ```
165
+
166
+ ### Basic Auth
167
+
168
+ ```ruby
169
+ class MyClient < TinyRestClient::Core
170
+ authorization :basic_auth, user: "name", password: "secret"
171
+ end
172
+
173
+ # per-instance
174
+ MyClient.new(auth: { basic_auth: { user: "name", password: "secret" } })
175
+ ```
176
+
177
+ ## Making Requests
178
+
179
+ The standard HTTP methods are available directly on every client instance automatically include any headers and/or authorization configuration you defined.
180
+
181
+ ```ruby
182
+ get("/users", page: 1, per_page: 10) # GET request with query parameters
183
+ post("/users", name: "John", surname: "Doe") # POST request with body
184
+ put("/users/1") # PUT request
185
+ patch("/users/1") # PATCH request
186
+ delete("/users/1") # DELETE request
187
+ head("/users/1") # HEAD request
188
+ options("/users") # OPTIONS request
189
+ ```
190
+
191
+ ## Response Handling
192
+
193
+ Every request returns an instance of [TinyRestClient::Response](https://github.com/Dabrovsky/tiny-rest-client/blob/main/lib/tiny_rest_client/response.rb). A thin
194
+ wrapper around the raw Typhoeus response that provides a few convenient
195
+ helpers:
196
+
197
+ | Method | Description |
198
+ |--------|-------------|
199
+ | success? | Helper that represent successful response. |
200
+ | failure? | The opposite. |
201
+ | code | HTTP status code (e.g. `200`, `404`). |
202
+ | status | Return code symbol (e.g. `:ok`, `:couldnt_connect`). |
203
+ | headers | Hash of response headers (keys are not normalized). |
204
+ | body | Auto-parsed JSON (symbolized keys). If parsing fails, returns the original raw string. |
205
+
206
+ ```ruby
207
+ response.success? # true if code 200–299
208
+ response.failure? # opposite
209
+ response.code # HTTP status code (Integer)
210
+ response.status # Typhoeus return_code symbol
211
+ response.body # auto-parsed JSON hash or raw string
212
+ response.headers # hash of response headers
213
+ ```
214
+
215
+ ## Error Behavior
216
+
217
+ TinyRestClient does not raise exceptions for HTTP status codes.
218
+
219
+ Network-level errors (timeouts, connection failures, etc.) are reflected in:
220
+
221
+ * response.status
222
+ * response.success? / response.failure?
223
+
224
+ This makes error handling explicit and predictable.
225
+
226
+ ## Contributing
227
+
228
+ Bug reports and pull requests are welcome on GitHub.
229
+
230
+ ## License
231
+
232
+ The gem is available as open source under the terms of the [MIT License](https://github.com/Dabrovsky/tiny-rest-client/blob/main/LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "tiny_rest_client"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Request
4
+ attr_reader :method, :api_path, :headers, :body, :params, :endpoint, :rest
5
+
6
+ def initialize(method, api_path, endpoint: "", headers: nil, auth_config: nil, body: nil, params: nil)
7
+ @method = method || :get
8
+ @api_path = api_path || raise(ArgumentError, "Define api_path base URL")
9
+ @endpoint = endpoint
10
+ @headers = headers
11
+ @body = body
12
+ @params = params
13
+ @rest = {}
14
+
15
+ configure_auth(auth_config)
16
+ end
17
+
18
+ def run
19
+ Response.new(
20
+ code: response.code,
21
+ status: response.return_code,
22
+ body: response.body,
23
+ headers: response.headers
24
+ )
25
+ rescue Typhoeus::Errors::TyphoeusError => e
26
+ Response.new(
27
+ code: 0,
28
+ status: :internal_error,
29
+ body: e.message.capitalize,
30
+ headers: {}
31
+ )
32
+ end
33
+
34
+ private
35
+
36
+ def configure_auth(auth_config)
37
+ return unless auth_config
38
+
39
+ type, config = auth_config.first
40
+ strategy = Strategies::Auth::Registry.fetch(type)
41
+ strategy.new(**config[:credentials]).execute(self)
42
+ end
43
+
44
+ def request
45
+ @request = Typhoeus::Request.new(
46
+ [api_path, endpoint].join, method:, body:, params:, headers:, **rest
47
+ )
48
+ end
49
+
50
+ def response
51
+ @response ||= request.run
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Response class represents a response object that
4
+ # can be used to represent success or failure outcomes
5
+ class Response
6
+ attr_reader :code, :status, :body, :headers
7
+
8
+ def initialize(code: nil, status: nil, body: nil, headers: nil)
9
+ @code = code
10
+ @status = status
11
+ @headers = headers
12
+ @body = parse_body(body)
13
+ end
14
+
15
+ def success?
16
+ code.to_i.between?(200, 299)
17
+ end
18
+
19
+ def failure?
20
+ !success?
21
+ end
22
+
23
+ def parse_body(raw_body)
24
+ return raw_body unless raw_body.is_a?(String)
25
+
26
+ begin
27
+ JSON.parse(raw_body, symbolize_names: true)
28
+ rescue JSON::ParserError
29
+ raw_body
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strategies
4
+ module Auth
5
+ class ApiKey < Base
6
+ attr_reader :token, :location, :name
7
+
8
+ def initialize(credentials = {})
9
+ @token = require!(credentials[:token], :token)
10
+ @location = credentials.fetch(:in, :header).to_sym
11
+ @name = credentials.fetch(:name) do
12
+ location == :header ? "X-API-Key" : "api_key"
13
+ end
14
+ end
15
+
16
+ def execute(request)
17
+ case location
18
+ when :header
19
+ request.headers[name] = token
20
+ when :query, :param
21
+ request.params[name] = token
22
+ else
23
+ raise ArgumentError, "Invalid key: #{location}. Allowed keys: :header or :query."
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strategies
4
+ module Auth
5
+ class Base
6
+ def execute
7
+ raise NotImplementedError
8
+ end
9
+
10
+ private
11
+
12
+ def require!(value, name)
13
+ value || raise(ArgumentError, "Missing #{name} parameter")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strategies
4
+ module Auth
5
+ class BasicAuth < Base
6
+ attr_reader :user, :pass
7
+
8
+ def initialize(credentials = {})
9
+ @user = require!(credentials[:user], :user)
10
+ @pass = require!(credentials[:password], :password)
11
+ end
12
+
13
+ def execute(request)
14
+ request.rest[:userpwd] = "#{user}:#{pass}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strategies
4
+ module Auth
5
+ class Bearer < Base
6
+ attr_reader :token
7
+
8
+ def initialize(credentials = {})
9
+ @token = require!(credentials[:token], :token)
10
+ end
11
+
12
+ def execute(request)
13
+ request.headers["Authorization"] = "Bearer #{token}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strategies
4
+ module Auth
5
+ module Registry
6
+ STRATEGIES = {
7
+ bearer: Bearer,
8
+ api_key: ApiKey,
9
+ basic_auth: BasicAuth
10
+ }.freeze
11
+
12
+ module_function
13
+
14
+ def fetch(type)
15
+ STRATEGIES.fetch(type) do
16
+ raise ArgumentError, "Unsupported auth type: #{type}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinyRestClient
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "typhoeus"
4
+ require "json"
5
+
6
+ require "tiny_rest_client/strategies/auth/base"
7
+ require "tiny_rest_client/strategies/auth/basic_auth"
8
+ require "tiny_rest_client/strategies/auth/bearer"
9
+ require "tiny_rest_client/strategies/auth/api_key"
10
+ require "tiny_rest_client/strategies/auth/registry"
11
+ require "tiny_rest_client/request"
12
+ require "tiny_rest_client/response"
13
+ require "tiny_rest_client/version"
14
+
15
+ module TinyRestClient
16
+ class Core
17
+ class << self
18
+ def api_path(value = nil)
19
+ @api_path ||= value
20
+ end
21
+
22
+ def authorization(type = nil, **credentials)
23
+ if type
24
+ @auth_config = {
25
+ type.to_sym => { credentials: }
26
+ }
27
+ else
28
+ @auth_config
29
+ end
30
+ end
31
+
32
+ def header(name, value)
33
+ @headers ||= {}
34
+ @headers[name] = value
35
+ @headers
36
+ end
37
+
38
+ def headers(hash = nil)
39
+ if hash
40
+ @headers = (@headers || {}).merge(hash.transform_keys(&:to_s))
41
+ else
42
+ @headers || {}
43
+ end
44
+ end
45
+ end
46
+
47
+ attr_reader :api_path, :auth_config, :headers
48
+
49
+ def initialize(api_path: nil, auth: nil, headers: {})
50
+ @api_path = api_path || self.class.api_path || raise(ArgumentError, "Undefined api_path base URL")
51
+ @auth_config = auth || self.class.authorization
52
+ @headers = merge_headers(headers)
53
+ end
54
+
55
+ def head(endpoint, params = {})
56
+ Request.new(:head, api_path, endpoint:, params:, **common_params).run
57
+ end
58
+
59
+ def get(endpoint, params = {})
60
+ Request.new(:get, api_path, endpoint:, params:, **common_params).run
61
+ end
62
+
63
+ def post(endpoint, body = {})
64
+ Request.new(:post, api_path, endpoint:, body:, **common_params).run
65
+ end
66
+
67
+ def patch(endpoint, body = {})
68
+ Request.new(:patch, api_path, endpoint:, body:, **common_params).run
69
+ end
70
+
71
+ def put(endpoint, body = {})
72
+ Request.new(:put, api_path, endpoint:, body:, **common_params).run
73
+ end
74
+
75
+ def delete(endpoint, params = {})
76
+ Request.new(:delete, api_path, endpoint:, params:, **common_params).run
77
+ end
78
+
79
+ def options(endpoint, params = {})
80
+ Request.new(:options, api_path, endpoint:, params:, **common_params).run
81
+ end
82
+
83
+ private
84
+
85
+ def merge_headers(instance_headers)
86
+ (self.class.headers || {}).merge(instance_headers || {}).transform_keys(&:to_s)
87
+ end
88
+
89
+ def common_params
90
+ { headers:, auth_config: }
91
+ end
92
+ end
93
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tiny-rest-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dabrovski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: typhoeus
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ description: A lightweight, Ruby-friendly HTTP client that makes HTTP requests simple
28
+ and easy to use.
29
+ email:
30
+ - wojciech.dabrovski@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - CHANGELOG.md
36
+ - LICENSE
37
+ - README.md
38
+ - Rakefile
39
+ - bin/console
40
+ - bin/setup
41
+ - lib/tiny_rest_client.rb
42
+ - lib/tiny_rest_client/request.rb
43
+ - lib/tiny_rest_client/response.rb
44
+ - lib/tiny_rest_client/strategies/auth/api_key.rb
45
+ - lib/tiny_rest_client/strategies/auth/base.rb
46
+ - lib/tiny_rest_client/strategies/auth/basic_auth.rb
47
+ - lib/tiny_rest_client/strategies/auth/bearer.rb
48
+ - lib/tiny_rest_client/strategies/auth/registry.rb
49
+ - lib/tiny_rest_client/version.rb
50
+ homepage: https://github.com/Dabrovsky/tiny-rest-client
51
+ licenses:
52
+ - MIT
53
+ metadata:
54
+ homepage_uri: https://github.com/Dabrovsky/tiny-rest-client
55
+ source_code_uri: https://github.com/Dabrovsky/tiny-rest-client
56
+ changelog_uri: https://github.com/Dabrovsky/tiny-rest-client/blob/main/CHANGELOG.md
57
+ rubygems_mfa_required: 'true'
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 3.2.0
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.4.5
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: A minimal REST client gem for Rails
77
+ test_files: []