philiprehberger-token_bucket 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: e7ddac1684bf5df901153bdedaf854e3ab75dfa81fb6ca0fd84d409f7fbc204c
4
+ data.tar.gz: d33d3308029a9e87045698e7e1e1086feafa92dfccb12149c42c6448e35a4206
5
+ SHA512:
6
+ metadata.gz: 12ef0e320932d5a9f1922f2ec522844be24dffb6e79e885b4906917cfdabb32e50a06cf60d40c2a6ec3edcac8f284975106a77881cdf71857ed106ceac875dde
7
+ data.tar.gz: 90ccdc8daa02d3ec0200dc66146629f7e73a85c7e1e355259da864aec904abe1337484c90aeac8a9c5e1670c86b705f66fed00c834803371feaf3fa7d84033e8
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ All notable changes to this gem will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-03-22
11
+
12
+ ### Added
13
+ - Initial release
14
+ - Token bucket rate limiter with configurable capacity and refill rate
15
+ - Blocking take and non-blocking try_take methods
16
+ - Available token count and wait time estimation
17
+ - Thread-safe implementation with Mutex
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 philiprehberger
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # philiprehberger-token_bucket
2
+
3
+ [![Tests](https://github.com/philiprehberger/rb-token-bucket/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/rb-token-bucket/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/philiprehberger-token_bucket.svg)](https://rubygems.org/gems/philiprehberger-token_bucket)
5
+ [![License](https://img.shields.io/github/license/philiprehberger/rb-token-bucket)](LICENSE)
6
+
7
+ Token bucket rate limiter with configurable capacity and refill rate
8
+
9
+ ## Requirements
10
+
11
+ - Ruby >= 3.1
12
+
13
+ ## Installation
14
+
15
+ Add to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'philiprehberger-token_bucket'
19
+ ```
20
+
21
+ Or install directly:
22
+
23
+ ```bash
24
+ gem install philiprehberger-token_bucket
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ require 'philiprehberger/token_bucket'
31
+
32
+ bucket = Philiprehberger::TokenBucket::Bucket.new(capacity: 10, refill_rate: 5)
33
+ ```
34
+
35
+ ### Blocking Take
36
+
37
+ ```ruby
38
+ bucket.take(3) # blocks until 3 tokens are available, then consumes them
39
+ bucket.take # takes 1 token by default
40
+ ```
41
+
42
+ ### Non-blocking Take
43
+
44
+ ```ruby
45
+ if bucket.try_take(2)
46
+ # tokens acquired, proceed
47
+ else
48
+ # not enough tokens, try again later
49
+ end
50
+ ```
51
+
52
+ ### Checking Availability
53
+
54
+ ```ruby
55
+ bucket.available # => current number of tokens (Float)
56
+ bucket.wait_time(5) # => seconds until 5 tokens will be available
57
+ ```
58
+
59
+ ## API
60
+
61
+ ### `Philiprehberger::TokenBucket::Bucket`
62
+
63
+ | Method | Description |
64
+ |--------|-------------|
65
+ | `.new(capacity:, refill_rate:)` | Create a bucket with max tokens and tokens-per-second refill |
66
+ | `#take(n = 1)` | Block until n tokens are available, then consume them |
67
+ | `#try_take(n = 1)` | Consume n tokens if available, return true/false |
68
+ | `#available` | Return the current number of available tokens |
69
+ | `#wait_time(n = 1)` | Estimate seconds until n tokens will be available |
70
+
71
+ ## Development
72
+
73
+ ```bash
74
+ bundle install
75
+ bundle exec rspec
76
+ bundle exec rubocop
77
+ ```
78
+
79
+ ## License
80
+
81
+ MIT
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ module TokenBucket
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'token_bucket/version'
4
+
5
+ module Philiprehberger
6
+ module TokenBucket
7
+ class Error < StandardError; end
8
+
9
+ # A thread-safe token bucket rate limiter
10
+ class Bucket
11
+ # @param capacity [Numeric] maximum number of tokens
12
+ # @param refill_rate [Numeric] tokens added per second
13
+ def initialize(capacity:, refill_rate:)
14
+ raise Error, 'capacity must be positive' unless capacity.positive?
15
+ raise Error, 'refill_rate must be positive' unless refill_rate.positive?
16
+
17
+ @capacity = capacity.to_f
18
+ @refill_rate = refill_rate.to_f
19
+ @tokens = @capacity
20
+ @last_refill = now
21
+ @mutex = Mutex.new
22
+ end
23
+
24
+ # Take n tokens, blocking until they are available
25
+ #
26
+ # @param n [Numeric] number of tokens to take
27
+ # @return [void]
28
+ # @raise [Error] if n exceeds capacity
29
+ def take(n = 1)
30
+ raise Error, "cannot take #{n} tokens from bucket with capacity #{@capacity}" if n > @capacity
31
+
32
+ loop do
33
+ wait = nil
34
+ @mutex.synchronize do
35
+ refill
36
+ if @tokens >= n
37
+ @tokens -= n
38
+ return
39
+ end
40
+ wait = compute_wait_time(n)
41
+ end
42
+ sleep(wait)
43
+ end
44
+ end
45
+
46
+ # Try to take n tokens without blocking
47
+ #
48
+ # @param n [Numeric] number of tokens to take
49
+ # @return [Boolean] true if tokens were taken, false otherwise
50
+ def try_take(n = 1)
51
+ @mutex.synchronize do
52
+ refill
53
+ if @tokens >= n
54
+ @tokens -= n
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+ end
61
+
62
+ # Return the number of currently available tokens
63
+ #
64
+ # @return [Float] available tokens
65
+ def available
66
+ @mutex.synchronize do
67
+ refill
68
+ @tokens
69
+ end
70
+ end
71
+
72
+ # Calculate how long to wait for n tokens to become available
73
+ #
74
+ # @param n [Numeric] number of tokens needed
75
+ # @return [Float] seconds to wait (0.0 if tokens are already available)
76
+ def wait_time(n = 1)
77
+ @mutex.synchronize do
78
+ refill
79
+ compute_wait_time(n)
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def compute_wait_time(n)
86
+ deficit = n - @tokens
87
+ return 0.0 if deficit <= 0
88
+
89
+ deficit / @refill_rate
90
+ end
91
+
92
+ def refill
93
+ current = now
94
+ elapsed = current - @last_refill
95
+ @tokens = [@tokens + elapsed * @refill_rate, @capacity].min
96
+ @last_refill = current
97
+ end
98
+
99
+ def now
100
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
101
+ end
102
+ end
103
+ end
104
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: philiprehberger-token_bucket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Philip Rehberger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Thread-safe token bucket rate limiter with configurable capacity and
14
+ refill rate, supporting blocking and non-blocking token acquisition.
15
+ email:
16
+ - me@philiprehberger.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - CHANGELOG.md
22
+ - LICENSE
23
+ - README.md
24
+ - lib/philiprehberger/token_bucket.rb
25
+ - lib/philiprehberger/token_bucket/version.rb
26
+ homepage: https://github.com/philiprehberger/rb-token-bucket
27
+ licenses:
28
+ - MIT
29
+ metadata:
30
+ homepage_uri: https://github.com/philiprehberger/rb-token-bucket
31
+ source_code_uri: https://github.com/philiprehberger/rb-token-bucket
32
+ changelog_uri: https://github.com/philiprehberger/rb-token-bucket/blob/main/CHANGELOG.md
33
+ bug_tracker_uri: https://github.com/philiprehberger/rb-token-bucket/issues
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: Token bucket rate limiter with configurable capacity and refill rate
54
+ test_files: []