rate-limit 0.0.1 → 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 +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +20 -8
- data/lib/rate-limit.rb +3 -0
- data/lib/rate_limit/base.rb +16 -10
- data/lib/rate_limit/config.rb +10 -0
- data/lib/rate_limit/result.rb +22 -0
- data/lib/rate_limit/throttler.rb +15 -39
- data/lib/rate_limit/version.rb +1 -1
- data/lib/rate_limit/window.rb +9 -5
- data/lib/rate_limit/worker.rb +45 -0
- data/lib/rate_limit.rb +2 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b73af03f6ea918dd965b1f1d89b8f7131e1fbba2b94166a397c100510c4a426
|
4
|
+
data.tar.gz: fcd12f564f38c214a088032b9ee912337dac4a566a1cd97c39256180e2e8e8a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a9e190cda1984c96ffd6d93751e56a323d783778c170a492d0b67b151d4a85ef83c88907526f48dc0cc574bdb19880a09667f2472b4e5873d54262e8b7dd976
|
7
|
+
data.tar.gz: b0529be97de2244366f3ac99f6192e7e649ff8574e15b44f51e2d56030dbcb9163238a1a03d77ed3b49d67c5669cba865c674216aa83dfe73877ab837aae02dd
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,43 @@
|
|
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],
|
6
|
+
and this project adheres to [Semantic Versioning].
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [v0.1.0] - 2022-09-16
|
11
|
+
|
12
|
+
|
13
|
+
### Added
|
14
|
+
|
15
|
+
- [https://github.com/catawiki/rate-limit/pull/11] `RateLimit::Result` class
|
16
|
+
- [https://github.com/catawiki/rate-limit/pull/12] `RateLimit::Worker` class
|
17
|
+
- [https://github.com/catawiki/rate-limit/pull/13] `RateLimit::Config#on_success` and `RateLimit::Config#on_failure`
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
|
21
|
+
- `RateLimit.throttle` to not accept block
|
22
|
+
- `RateLimit.throttle` to return `RateLimit::Result` object
|
23
|
+
- `RateLimit::Throttler` from class to module while moving responsibilities to `RateLimit::Worker` class
|
24
|
+
- renamed `RateLimit.throttle!` to `RateLimit.throttle_with_block!`
|
25
|
+
- renamed `RateLimit.throttle_only_failures` to `RateLimit.throttle_only_failures_with_block!`
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
- [https://github.com/catawiki/rate-limit/issues/7] Symbol topic names does not load the correct limits
|
30
|
+
- [https://github.com/catawiki/rate-limit/issues/6] Main Module (RateLimit) fails to autoload
|
31
|
+
|
32
|
+
|
33
|
+
## v0.0.1 - 2022-09-09
|
34
|
+
|
35
|
+
- Initial gem release
|
36
|
+
|
37
|
+
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
|
38
|
+
[Semantic Versioning]: https://semver.org/spec/v2.0.0.html
|
39
|
+
|
40
|
+
<!-- versions -->
|
41
|
+
|
42
|
+
[Unreleased]: https://github.com/catawiki/rate-limit/compare/v0.1.0...HEAD
|
43
|
+
[v0.1.0]: https://github.com/catawiki/rate-limit/compare/v0.0.1...v0.1.0
|
data/README.md
CHANGED
@@ -33,14 +33,18 @@ Or install it yourself as:
|
|
33
33
|
#### Basic `RateLimit.throttle`
|
34
34
|
|
35
35
|
```ruby
|
36
|
-
|
36
|
+
result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
|
37
|
+
|
38
|
+
if result.success?
|
37
39
|
# Do something
|
38
40
|
end
|
39
41
|
```
|
40
42
|
or
|
41
43
|
|
42
44
|
```ruby
|
43
|
-
|
45
|
+
result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
|
46
|
+
|
47
|
+
if result.success?
|
44
48
|
# Do something
|
45
49
|
end
|
46
50
|
```
|
@@ -49,7 +53,7 @@ end
|
|
49
53
|
|
50
54
|
```ruby
|
51
55
|
begin
|
52
|
-
RateLimit.
|
56
|
+
RateLimit.throttle_with_block!(topic: :send_sms, namespace: :user_id, value: id) do
|
53
57
|
# Logic goes Here
|
54
58
|
end
|
55
59
|
rescue RateLimit::Errors::LimitExceededError => e
|
@@ -65,10 +69,10 @@ end
|
|
65
69
|
#### Advanced
|
66
70
|
|
67
71
|
```ruby
|
68
|
-
throttler = RateLimit::
|
72
|
+
throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
|
69
73
|
|
70
74
|
begin
|
71
|
-
throttler.
|
75
|
+
throttler.throttle_with_block! do
|
72
76
|
# Logic goes Here
|
73
77
|
end
|
74
78
|
rescue RateLimit::Errors::LimitExceededError => e
|
@@ -79,7 +83,7 @@ end
|
|
79
83
|
#### Manual
|
80
84
|
|
81
85
|
```ruby
|
82
|
-
throttler = RateLimit::
|
86
|
+
throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
|
83
87
|
|
84
88
|
unless throttler.limit_exceeded?
|
85
89
|
# Logic goes Here
|
@@ -92,8 +96,8 @@ end
|
|
92
96
|
|
93
97
|
```ruby
|
94
98
|
begin
|
95
|
-
RateLimit.
|
96
|
-
RateLimit.
|
99
|
+
RateLimit.throttle_with_block!(topic: :send_sms, namespace: :user_id, value: id) do
|
100
|
+
RateLimit.throttle_with_block!(topic: :send_sms, namespace: :phone_number, value: number) do
|
97
101
|
# Logic goes Here
|
98
102
|
end
|
99
103
|
end
|
@@ -113,6 +117,14 @@ RateLimit.configure do |config|
|
|
113
117
|
config.default_interval = 60
|
114
118
|
config.default_threshold = 2
|
115
119
|
config.limits_file_path = 'config/rate-limit.yml'
|
120
|
+
config.on_success = proc { |result|
|
121
|
+
# Success Logic Goes HERE
|
122
|
+
# result.topic, result.namespace, result.value
|
123
|
+
}
|
124
|
+
config.on_failure = proc { |result|
|
125
|
+
# Failure Logic Goes HERE
|
126
|
+
# result.topic, result.namespace, result.value, result.threshold, result.interval
|
127
|
+
}
|
116
128
|
end
|
117
129
|
```
|
118
130
|
|
data/lib/rate-limit.rb
ADDED
data/lib/rate_limit/base.rb
CHANGED
@@ -3,29 +3,35 @@
|
|
3
3
|
module RateLimit
|
4
4
|
module Base
|
5
5
|
def throttle(**args)
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
worker = Worker.new(**args)
|
7
|
+
worker.throttle
|
8
|
+
worker.result
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
11
|
+
def throttle_with_block!(**args, &block)
|
12
|
+
worker = Worker.new(**args)
|
13
|
+
|
14
|
+
worker.throttle_with_block!(&block)
|
15
|
+
worker.result
|
13
16
|
end
|
14
17
|
|
15
|
-
def
|
16
|
-
|
18
|
+
def throttle_only_failures_with_block!(**args, &block)
|
19
|
+
worker = Worker.new(**args)
|
20
|
+
|
21
|
+
worker.throttle_only_failures_with_block!(&block)
|
22
|
+
worker.result
|
17
23
|
end
|
18
24
|
|
19
25
|
def limit_exceeded?(**args)
|
20
|
-
|
26
|
+
Worker.new(**args).reloaded_limit_exceeded?
|
21
27
|
end
|
22
28
|
|
23
29
|
def reset_counters(**args)
|
24
|
-
|
30
|
+
Worker.new(**args).clear_cache_counter
|
25
31
|
end
|
26
32
|
|
27
33
|
def increment_counters(**args)
|
28
|
-
|
34
|
+
Worker.new(**args).increment_cache_counter
|
29
35
|
end
|
30
36
|
end
|
31
37
|
end
|
data/lib/rate_limit/config.rb
CHANGED
@@ -9,6 +9,8 @@ module RateLimit
|
|
9
9
|
attr_accessor :default_interval,
|
10
10
|
:default_threshold,
|
11
11
|
:limits_file_path,
|
12
|
+
:on_success,
|
13
|
+
:on_failure,
|
12
14
|
:fail_safe,
|
13
15
|
:redis
|
14
16
|
|
@@ -24,6 +26,14 @@ module RateLimit
|
|
24
26
|
raw_limits[topic] || Defaults.raw_limits
|
25
27
|
end
|
26
28
|
|
29
|
+
def success_callback(*args)
|
30
|
+
on_success&.call(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_callback(*args)
|
34
|
+
on_failure&.call(*args)
|
35
|
+
end
|
36
|
+
|
27
37
|
private
|
28
38
|
|
29
39
|
def raw_limits
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RateLimit
|
4
|
+
class Result
|
5
|
+
# Attributes
|
6
|
+
attr_accessor :topic, :namespace, :value, :threshold, :interval
|
7
|
+
|
8
|
+
# Methods
|
9
|
+
def initialize(worker, success)
|
10
|
+
@topic = worker.topic
|
11
|
+
@namespace = worker.namespace
|
12
|
+
@value = worker.value
|
13
|
+
@threshold = worker.exceeded_window&.threshold
|
14
|
+
@interval = worker.exceeded_window&.interval
|
15
|
+
@success = success
|
16
|
+
end
|
17
|
+
|
18
|
+
def success?
|
19
|
+
@success
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/rate_limit/throttler.rb
CHANGED
@@ -1,57 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RateLimit
|
4
|
-
|
5
|
-
|
4
|
+
module Throttler
|
5
|
+
def throttle
|
6
|
+
return failure! if reloaded_limit_exceeded?
|
6
7
|
|
7
|
-
|
8
|
-
@topic = topic.to_s
|
9
|
-
@value = value.to_i
|
10
|
-
@namespace = namespace&.to_s
|
11
|
-
@windows = Limit.fetch(topic).map { |limit| Window.new(self, limit) }
|
8
|
+
success!
|
12
9
|
end
|
13
10
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
def throttle_with_block!
|
12
|
+
if reloaded_limit_exceeded?
|
13
|
+
failure!
|
14
|
+
raise Errors::LimitExceededError, exceeded_window
|
15
|
+
end
|
18
16
|
|
19
|
-
|
17
|
+
yield
|
20
18
|
|
21
|
-
|
19
|
+
success!
|
22
20
|
end
|
23
21
|
|
24
|
-
def
|
25
|
-
|
22
|
+
def throttle_only_failures_with_block!
|
23
|
+
return failure! if reloaded_limit_exceeded?
|
26
24
|
|
27
25
|
begin
|
28
|
-
yield
|
26
|
+
yield
|
29
27
|
rescue StandardError => e
|
30
|
-
|
28
|
+
success!
|
31
29
|
raise e
|
32
30
|
end
|
33
|
-
|
34
|
-
true
|
35
|
-
end
|
36
|
-
|
37
|
-
def limit_exceeded?
|
38
|
-
Window.find_exceeded(windows).present?
|
39
|
-
end
|
40
|
-
|
41
|
-
def increment_cache_counter
|
42
|
-
Window.increment_cache_counter(windows)
|
43
|
-
end
|
44
|
-
|
45
|
-
def clear_cache_counter
|
46
|
-
Window.clear_cache_counter(windows)
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def validate_limit!
|
52
|
-
exceeded_window = Window.find_exceeded(windows)
|
53
|
-
|
54
|
-
raise Errors::LimitExceededError, exceeded_window if exceeded_window
|
55
31
|
end
|
56
32
|
end
|
57
33
|
end
|
data/lib/rate_limit/version.rb
CHANGED
data/lib/rate_limit/window.rb
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
module RateLimit
|
4
4
|
class Window
|
5
|
-
attr_accessor :
|
5
|
+
attr_accessor :worker, :limit
|
6
6
|
|
7
|
-
delegate :topic, :namespace, :value, to: :
|
7
|
+
delegate :topic, :namespace, :value, to: :worker
|
8
8
|
delegate :threshold, :interval, to: :limit
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@limit
|
10
|
+
def initialize(worker, limit)
|
11
|
+
@worker = worker
|
12
|
+
@limit = limit
|
13
13
|
end
|
14
14
|
|
15
15
|
def key
|
@@ -21,6 +21,10 @@ module RateLimit
|
|
21
21
|
end
|
22
22
|
|
23
23
|
class << self
|
24
|
+
def find_all(topic:, worker:)
|
25
|
+
Limit.fetch(topic).map { |limit| Window.new(worker, limit) }
|
26
|
+
end
|
27
|
+
|
24
28
|
def find_exceeded(windows)
|
25
29
|
windows.find { |w| w.cached_counter >= w.threshold }
|
26
30
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RateLimit
|
4
|
+
class Worker
|
5
|
+
include Throttler
|
6
|
+
|
7
|
+
attr_accessor :topic, :namespace, :value, :limits, :windows, :exceeded_window, :result
|
8
|
+
|
9
|
+
def initialize(topic:, value:, namespace: nil)
|
10
|
+
@topic = topic.to_s
|
11
|
+
@value = value.to_i
|
12
|
+
@namespace = namespace&.to_s
|
13
|
+
@windows = Window.find_all(worker: self, topic: @topic)
|
14
|
+
end
|
15
|
+
|
16
|
+
def increment_cache_counter
|
17
|
+
Window.increment_cache_counter(windows)
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear_cache_counter
|
21
|
+
Window.clear_cache_counter(windows)
|
22
|
+
end
|
23
|
+
|
24
|
+
def reloaded_limit_exceeded?
|
25
|
+
@exceeded_window = Window.find_exceeded(windows)
|
26
|
+
|
27
|
+
limit_exceeded?
|
28
|
+
end
|
29
|
+
|
30
|
+
def limit_exceeded?
|
31
|
+
exceeded_window.present?
|
32
|
+
end
|
33
|
+
|
34
|
+
def success!
|
35
|
+
increment_cache_counter
|
36
|
+
@result = Result.new(self, true)
|
37
|
+
RateLimit.config.success_callback(result)
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure!
|
41
|
+
@result = Result.new(self, false)
|
42
|
+
RateLimit.config.failure_callback(result)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/rate_limit.rb
CHANGED
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/module'
|
4
4
|
require_relative 'rate_limit/configurable'
|
5
|
+
require_relative 'rate_limit/result'
|
5
6
|
require_relative 'rate_limit/cache'
|
6
7
|
require_relative 'rate_limit/window'
|
7
8
|
require_relative 'rate_limit/throttler'
|
9
|
+
require_relative 'rate_limit/worker'
|
8
10
|
require_relative 'rate_limit/limit'
|
9
11
|
require_relative 'rate_limit/errors/limit_exceeded_error'
|
10
12
|
require_relative 'rate_limit/base'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rate-limit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mohamed Motaweh
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -60,6 +60,7 @@ files:
|
|
60
60
|
- CHANGELOG.md
|
61
61
|
- LICENSE
|
62
62
|
- README.md
|
63
|
+
- lib/rate-limit.rb
|
63
64
|
- lib/rate_limit.rb
|
64
65
|
- lib/rate_limit/base.rb
|
65
66
|
- lib/rate_limit/cache.rb
|
@@ -69,9 +70,11 @@ files:
|
|
69
70
|
- lib/rate_limit/configurable.rb
|
70
71
|
- lib/rate_limit/errors/limit_exceeded_error.rb
|
71
72
|
- lib/rate_limit/limit.rb
|
73
|
+
- lib/rate_limit/result.rb
|
72
74
|
- lib/rate_limit/throttler.rb
|
73
75
|
- lib/rate_limit/version.rb
|
74
76
|
- lib/rate_limit/window.rb
|
77
|
+
- lib/rate_limit/worker.rb
|
75
78
|
homepage: https://github.com/catawiki/rate-limit
|
76
79
|
licenses:
|
77
80
|
- MIT
|
@@ -95,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
98
|
- !ruby/object:Gem::Version
|
96
99
|
version: '0'
|
97
100
|
requirements: []
|
98
|
-
rubygems_version: 3.
|
101
|
+
rubygems_version: 3.2.3
|
99
102
|
signing_key:
|
100
103
|
specification_version: 4
|
101
104
|
summary: A Rate Limiting Gem
|