rate-limit 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|