tiny-rest-client 0.2.0 → 0.3.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: 0ae594ba15f60d217c03513ffd00bfb867e607cf24e86f2faba66309ec41b252
4
- data.tar.gz: c13258411fd7212dafade551a2b7384973837c47e47803eb35fb3244e1cad7ce
3
+ metadata.gz: 72d74c14f37ab28b47e2c0cf52bfa9f092d2457bad0d5487bbcbb32f1c5b12d5
4
+ data.tar.gz: 49eac5d225c02dd3675d1c90a41d71067fa150cebb0018d5126d2c8b0e19102e
5
5
  SHA512:
6
- metadata.gz: '08e9d7be9ecb2f17dc0052cc4965f6336408d35712f9443a2be1e7315a7b2d7ee76ce715d0b181799370d1e6b986fd2ff3175d2790ec8c44031dc100920af77c'
7
- data.tar.gz: 3d314db3b3a4d8efd17dd3730f82ef1adbf2dd545d4884a8d20ca0c30e479481ef596d6c8727fc8118a2f1fde6fb9770e8d753e42f62a6324daf7620afadb9db
6
+ metadata.gz: ee2b67e844a132f1d549cf814098d4af9592884d7a61723391fe98774d50c525b7ba346c59bd2fdfe27d0e2ba869071ca59402c473aac1438c1791cf4d1ca94f
7
+ data.tar.gz: b02d9ffc1f3e6601a942a7e6f4f2fa752f4e580fca1a7fee3a243887eb2fd9246813bc7372fa0a35416a27784969f6178cde4973f0a5985237740845dc28b7e7
data/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Class-level retries with configurable delay (throttling)
12
+ - `retries 3, delay: 0.5` - retry 3 times on 5xx errors with 500ms delay between attempts
13
+ - Default retryable codes: 500, 502, 503, 504
14
+ - Instance-level override supported
15
+
16
+ ## [0.2.1] - 2026-03-03
17
+
18
+ ### Fixed
19
+ - Ensure the gem can be required without `require:` option in Gemfile
20
+ - Add `lib/tiny-rest-client.rb` loader file for RubyGems auto-require compatibility
21
+
8
22
  ## [0.2.0] - 2026-03-03
9
23
 
10
24
  ### Added
@@ -12,7 +26,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12
26
  - Supports nested client names (e.g. `api/v1/stripe`)
13
27
  - Optional base URL argument
14
28
  - Configurable root namespace via `--namespace`
15
- - Automatically generates properly namespaced client classes under `app/`
16
29
 
17
30
  ## [0.1.0] - 2026-02-22
18
31
 
@@ -28,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
41
  - Full Minitest suite (classic + spec style examples)
29
42
  - Basic README documentation
30
43
 
31
- [Unreleased]: https://github.com/Dabrovsky/tiny_rest_client/compare/v0.2.0...HEAD
44
+ [Unreleased]: https://github.com/Dabrovsky/tiny_rest_client/compare/v0.2.1...HEAD
45
+ [0.2.1]: https://github.com/Dabrovsky/tiny_rest_client/compare/v0.2.0...v0.2.1
32
46
  [0.2.0]: https://github.com/Dabrovsky/tiny_rest_client/compare/v0.1.0...v0.2.0
33
47
  [0.1.0]: https://github.com/Dabrovsky/tiny_rest_client/releases/tag/v0.1.0
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TinyRestClient
2
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.
3
+ A minimal HTTP client wrapper for Rails, built on top of Typhoeus. Perfect for quickly building clean, opinionated API clients with almost no boilerplate.
4
4
 
5
5
  ### Features
6
6
 
@@ -22,6 +22,7 @@ A minimal, opinionated HTTP client wrapper for Rails, built on top of Typhoeus.
22
22
  - [Rails Generator](#rails-generator)
23
23
  - [Configuration](#configuration)
24
24
  - [Authorization](#authorization)
25
+ - [Retries & Delay (Throttling)](#retries--delay-throttling)
25
26
  - [Making Requests](#making-requests)
26
27
  - [Response Handling](#response-handling)
27
28
  - [Contributing](#contributing)
@@ -32,7 +33,7 @@ A minimal, opinionated HTTP client wrapper for Rails, built on top of Typhoeus.
32
33
  Add this line to your Gemfile:
33
34
 
34
35
  ```bash
35
- gem "tiny-rest-client", require: "tiny_rest_client"
36
+ gem "tiny-rest-client"
36
37
  ```
37
38
 
38
39
  Or install it manually:
@@ -57,7 +58,7 @@ class MyClient < TinyRestClient::Core
57
58
  # pass bunch of headers (optional)
58
59
  headers "User-Agent": "MyTest/1.0", "Accept": "application/json"
59
60
 
60
- # define actions
61
+ # define actions (optional)
61
62
  def fetch_todos(**)
62
63
  get("/todos", **)
63
64
  end
@@ -84,7 +85,7 @@ client = MyClient.new
84
85
  client.fetch_todos(page: 1, per_page: 20)
85
86
  client.fetch_todo(1)
86
87
  client.create_todo(name: "Custom")
87
- client.update_todo(status: "finished")
88
+ client.update_todo(1, { status: "finished" })
88
89
  client.destroy_todo(1)
89
90
  ```
90
91
 
@@ -98,11 +99,11 @@ rails generate tiny_rest_client:client NAME [URL] [options]
98
99
 
99
100
  ### Available options
100
101
 
101
- | Option | Default | Description | Example Usage |
102
- |-----------------|---------------|-------------------------------------------------------------|----------------------|
103
- | NAME | (required) | Client name (supports nesting like `api/v1/stripe`) | `rails g ... api/v1/stripe` |
102
+ | Option | Default | Description |
103
+ |-----------------|---------------|-------------------------------------------------------------|
104
+ | NAME | (required) | Client name (supports nesting like `api/v1/stripe`) |
104
105
  | URL | (none) | Base API URL | `rails g ... stripe https://api.stripe.com` |
105
- | --namespace | `clients` | Root folder under `app/` (e.g. `app/services/...`) | `--namespace=services` |
106
+ | --namespace | `clients` | Root folder under `app/` (e.g. `app/services/`) |
106
107
 
107
108
  ### Basic usage
108
109
 
@@ -128,7 +129,7 @@ You can configure client setup at the class level:
128
129
  class MyClient < TinyRestClient::Core
129
130
  api_path "https://api.example.com/v1"
130
131
  authorization :bearer, token: "your_token"
131
- headers "Accept": "application/json", "User-Agent", "MyApp/1.0"
132
+ headers "Accept": "application/json", "User-Agent": "MyApp/1.0"
132
133
  end
133
134
 
134
135
  client = MyClient.new
@@ -207,6 +208,40 @@ end
207
208
  MyClient.new(auth: { basic_auth: { user: "name", password: "secret" } })
208
209
  ```
209
210
 
211
+ ## Retries & Delay (Throttling)
212
+
213
+ The library supports automatic retries on server errors and a simple fixed delay (throttling) between requests, both configurable at class or instance level.
214
+
215
+ ### Retries
216
+
217
+ Retries only happen on configurable HTTP status codes (default: 500, 502, 503, 504).
218
+
219
+ ```ruby
220
+ class MyClient < TinyRestClient::Core
221
+ # retry 3 times on default 5xx errors
222
+ retries 3
223
+
224
+ # custom retryable codes
225
+ retries 5, on: [500..599]
226
+ end
227
+
228
+ # per-instance
229
+ client = MyClient.new(retries: { count: 3, on: [500..599] })
230
+ ```
231
+
232
+ ### Delay (Throttling)
233
+
234
+ Add a fixed delay (seconds) between each request - useful for rate-limited APIs (default: 1.0 second).
235
+
236
+ ```ruby
237
+ class MyClient < TinyRestClient::Core
238
+ retries 3, delay: 1.0 # wait 1 second between every retry
239
+ end
240
+
241
+ # per-instance
242
+ client = MyClient.new(retries: { count: 3, delay: 1.0 })
243
+ ```
244
+
210
245
  ## Making Requests
211
246
 
212
247
  The standard HTTP methods are available directly on every client instance automatically include any headers or authorization configuration you defined.
@@ -231,14 +266,16 @@ helpers:
231
266
  |--------|-------------|
232
267
  | success? | Helper that represent successful response. |
233
268
  | failure? | The opposite. |
269
+ | timed_out? | Returns request timeout status |
234
270
  | code | HTTP status code (e.g. `200`, `404`). |
235
- | status | Return code symbol (e.g. `:ok`, `:couldnt_connect`). |
271
+ | status | Returns code symbol (e.g. `:ok`, `:couldnt_connect`). |
236
272
  | headers | Hash of response headers (keys are not normalized). |
237
273
  | body | Auto-parsed JSON (symbolized keys). If parsing fails, returns the original raw string. |
238
274
 
239
275
  ```ruby
240
276
  response.success? # true if code 200–299
241
277
  response.failure? # opposite
278
+ response.timed_out? # Typhoeus timed_out status
242
279
  response.code # HTTP status code
243
280
  response.status # Typhoeus return_code symbol
244
281
  response.body # auto-parsed JSON hash or raw string
@@ -251,8 +288,9 @@ TinyRestClient does not raise exceptions for HTTP status codes.
251
288
 
252
289
  Network-level errors (timeouts, connection failures, etc.) are reflected in:
253
290
 
254
- * response.status
255
291
  * response.success? / response.failure?
292
+ * response.timed_out?
293
+ * response.status
256
294
 
257
295
  This makes error handling explicit and predictable.
258
296
 
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "#{File.dirname(__FILE__)}/tiny_rest_client"
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Middleware
4
+ module Retry
5
+ DEFAULT_OPTIONS = {
6
+ count: 0, # 0 = no retries by default
7
+ on: [500, 502, 503, 504], # only these server errors by default
8
+ delay: 1.0 # 1 second between retries by default
9
+ }.freeze
10
+
11
+ def self.call(request, options = {})
12
+ opts = DEFAULT_OPTIONS.merge(options)
13
+ attempt = 0
14
+
15
+ loop do
16
+ attempt += 1
17
+
18
+ begin
19
+ response = request.run
20
+ return response if response.success?
21
+
22
+ if attempt <= opts[:count] && opts[:on].include?(response.code)
23
+ sleep opts[:delay].to_f
24
+ next
25
+ end
26
+
27
+ return response
28
+ rescue Typhoeus::Errors::TyphoeusError => e
29
+ if attempt <= opts[:count]
30
+ sleep opts[:delay].to_f
31
+ retry
32
+ end
33
+
34
+ raise Typhoeus::Errors::TyphoeusError, e
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Request
4
- attr_reader :method, :api_path, :headers, :body, :params, :endpoint, :rest
4
+ attr_reader :method, :api_path, :headers, :body, :params, :endpoint, :retries, :rest
5
5
 
6
- def initialize(method, api_path, endpoint: "", headers: nil, auth_config: nil, body: nil, params: nil)
6
+ def initialize(method, api_path, endpoint: "", headers: nil, auth_config: nil, body: nil, params: nil, retries: nil)
7
7
  @method = method || :get
8
8
  @api_path = api_path || raise(ArgumentError, "Define api_path base URL")
9
9
  @endpoint = endpoint
10
10
  @headers = headers
11
11
  @body = body
12
12
  @params = params
13
+ @retries = retries
13
14
  @rest = {}
14
15
 
15
16
  configure_auth(auth_config)
@@ -20,7 +21,8 @@ class Request
20
21
  code: response.code,
21
22
  status: response.return_code,
22
23
  body: response.body,
23
- headers: response.headers
24
+ headers: response.headers,
25
+ timed_out: response.timed_out?
24
26
  )
25
27
  rescue Typhoeus::Errors::TyphoeusError => e
26
28
  Response.new(
@@ -48,6 +50,6 @@ class Request
48
50
  end
49
51
 
50
52
  def response
51
- @response ||= request.run
53
+ @response ||= Middleware::Retry.call(request, retries)
52
54
  end
53
55
  end
@@ -3,13 +3,14 @@
3
3
  # The Response class represents a response object that
4
4
  # can be used to represent success or failure outcomes
5
5
  class Response
6
- attr_reader :code, :status, :body, :headers
6
+ attr_reader :code, :status, :body, :headers, :timed_out
7
7
 
8
- def initialize(code: nil, status: nil, body: nil, headers: nil)
8
+ def initialize(code: nil, status: nil, body: nil, headers: nil, timed_out: false)
9
9
  @code = code&.to_i
10
10
  @status = status
11
11
  @headers = headers
12
12
  @body = parse_body(body)
13
+ @timed_out = timed_out
13
14
  end
14
15
 
15
16
  def success?
@@ -20,6 +21,12 @@ class Response
20
21
  !success?
21
22
  end
22
23
 
24
+ def timed_out?
25
+ timed_out
26
+ end
27
+
28
+ private
29
+
23
30
  def parse_body(raw_body)
24
31
  return raw_body unless raw_body.is_a?(String)
25
32
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TinyRestClient
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -8,6 +8,7 @@ require "tiny_rest_client/strategies/auth/basic_auth"
8
8
  require "tiny_rest_client/strategies/auth/bearer"
9
9
  require "tiny_rest_client/strategies/auth/api_key"
10
10
  require "tiny_rest_client/strategies/auth/registry"
11
+ require "tiny_rest_client/middleware/retry"
11
12
  require "tiny_rest_client/request"
12
13
  require "tiny_rest_client/response"
13
14
  require "tiny_rest_client/version"
@@ -44,14 +45,27 @@ module TinyRestClient
44
45
  @headers || {}
45
46
  end
46
47
  end
48
+
49
+ def retries(count = nil, on: nil, delay: nil)
50
+ if count.nil?
51
+ @retry_options ||= Middleware::Retry::DEFAULT_OPTIONS.dup
52
+ else
53
+ @retry_options = {
54
+ count: count || Middleware::Retry::DEFAULT_OPTIONS[:count],
55
+ on: Array(on || Middleware::Retry::DEFAULT_OPTIONS[:on]),
56
+ delay: delay || Middleware::Retry::DEFAULT_OPTIONS[:delay]
57
+ }
58
+ end
59
+ end
47
60
  end
48
61
 
49
- attr_reader :api_path, :auth_config, :headers
62
+ attr_reader :api_path, :auth_config, :headers, :retries
50
63
 
51
- def initialize(api_path: nil, auth: nil, headers: {})
64
+ def initialize(api_path: nil, auth: nil, headers: {}, retries: nil)
52
65
  @api_path = api_path || self.class.api_path || raise(ArgumentError, "Undefined api_path base URL")
53
66
  @auth_config = auth || self.class.authorization
54
67
  @headers = merge_headers(headers)
68
+ @retries = retries || self.class.retries
55
69
  end
56
70
 
57
71
  def head(endpoint, params = {})
@@ -89,7 +103,7 @@ module TinyRestClient
89
103
  end
90
104
 
91
105
  def common_params
92
- { headers:, auth_config: }
106
+ { headers:, auth_config:, retries: }.compact_blank
93
107
  end
94
108
  end
95
109
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny-rest-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dabrovski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-03 00:00:00.000000000 Z
11
+ date: 2026-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
@@ -39,7 +39,9 @@ files:
39
39
  - lib/generators/tiny_rest_client/templates/client.rb.tt
40
40
  - lib/generators/tiny_rest_client/templates/client_spec.rb.tt
41
41
  - lib/generators/tiny_rest_client/templates/client_test.rb.tt
42
+ - lib/tiny-rest-client.rb
42
43
  - lib/tiny_rest_client.rb
44
+ - lib/tiny_rest_client/middleware/retry.rb
43
45
  - lib/tiny_rest_client/railtie.rb
44
46
  - lib/tiny_rest_client/request.rb
45
47
  - lib/tiny_rest_client/response.rb