pause 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +1 -50
- data/LICENSE.txt +1 -1
- data/README.md +47 -21
- data/lib/pause/action.rb +1 -1
- data/lib/pause/version.rb +1 -1
- data/lib/pause.rb +12 -4
- data/spec/pause/analyzer_spec.rb +4 -0
- data/spec/pause/period_check_spec.rb +53 -0
- metadata +4 -3
- data/Guardfile +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e50b977e8a0291be3ffa4c7c3cd29e7e7dd4d35d7eaabf7135a50e285ba4e6e
|
4
|
+
data.tar.gz: 29090acb4bd36888191ecb05e5f233a87cf3d2f44e1c83ef5929406dee6b7568
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09db81cdc48b8bc669a1a138cc00f1a2c7d230eee954551e15c09261d1caf7f2a0f465d4559e1cf0337dc5257c50585e85839365e59da35173737eed537625c5'
|
7
|
+
data.tar.gz: ade37423f09768bf16fc55c80d0252f412808b86457b4fc792efc1d86b499fcc9e346d5547f43d2a058d3f916075cf00b5e8d3fc4ad33c0232521e985ae3bc94
|
data/.rubocop.yml
CHANGED
@@ -17,6 +17,7 @@ inherit_from: .rubocop_todo.yml
|
|
17
17
|
#
|
18
18
|
AllCops:
|
19
19
|
NewCops: enable
|
20
|
+
TargetRubyVersion: 3.2
|
20
21
|
|
21
22
|
RSpec/MultipleMemoizedHelpers:
|
22
23
|
Enabled: false
|
@@ -26,3 +27,9 @@ RSpec/ExampleLength:
|
|
26
27
|
|
27
28
|
RSpec/MultipleExpectations:
|
28
29
|
Enabled: false
|
30
|
+
|
31
|
+
RSpec/IndexedLet:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Naming/VariableNumber:
|
35
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [Unreleased](https://github.com/kigster/pause/tree/HEAD)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/kigster/pause/compare/v0.5.0...HEAD)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- Improving test coverage by adding missing tests [\#3](https://github.com/kigster/pause/pull/3) ([kigster](https://github.com/kigster))
|
10
|
+
|
11
|
+
## [v0.5.0](https://github.com/kigster/pause/tree/v0.5.0) (2025-02-07)
|
12
|
+
|
13
|
+
[Full Changelog](https://github.com/kigster/pause/compare/2bad44d9026e4bd24c35384d28f5945187a061f5...v0.5.0)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- Adding Guthub Actions and removing Travis [\#2](https://github.com/kigster/pause/pull/2) ([kigster](https://github.com/kigster))
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pause (0.5.
|
4
|
+
pause (0.5.1)
|
5
5
|
colored2
|
6
6
|
redis
|
7
7
|
|
@@ -9,66 +9,20 @@ GEM
|
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
11
|
ast (2.4.2)
|
12
|
-
coderay (1.1.3)
|
13
12
|
colored2 (4.0.3)
|
14
13
|
diff-lcs (1.5.1)
|
15
14
|
docile (1.4.1)
|
16
15
|
fakeredis (0.9.2)
|
17
16
|
redis (~> 4.8)
|
18
|
-
ffi (1.17.1)
|
19
|
-
ffi (1.17.1-aarch64-linux-gnu)
|
20
|
-
ffi (1.17.1-aarch64-linux-musl)
|
21
|
-
ffi (1.17.1-arm-linux-gnu)
|
22
|
-
ffi (1.17.1-arm-linux-musl)
|
23
|
-
ffi (1.17.1-arm64-darwin)
|
24
|
-
ffi (1.17.1-x86-linux-gnu)
|
25
|
-
ffi (1.17.1-x86-linux-musl)
|
26
|
-
ffi (1.17.1-x86_64-darwin)
|
27
|
-
ffi (1.17.1-x86_64-linux-gnu)
|
28
|
-
ffi (1.17.1-x86_64-linux-musl)
|
29
|
-
formatador (1.1.0)
|
30
|
-
guard (2.19.1)
|
31
|
-
formatador (>= 0.2.4)
|
32
|
-
listen (>= 2.7, < 4.0)
|
33
|
-
logger (~> 1.6)
|
34
|
-
lumberjack (>= 1.0.12, < 2.0)
|
35
|
-
nenv (~> 0.1)
|
36
|
-
notiffany (~> 0.0)
|
37
|
-
ostruct (~> 0.6)
|
38
|
-
pry (>= 0.13.0)
|
39
|
-
shellany (~> 0.0)
|
40
|
-
thor (>= 0.18.1)
|
41
|
-
guard-compat (1.2.1)
|
42
|
-
guard-rspec (4.7.3)
|
43
|
-
guard (~> 2.1)
|
44
|
-
guard-compat (~> 1.1)
|
45
|
-
rspec (>= 2.99.0, < 4.0)
|
46
17
|
json (2.9.1)
|
47
18
|
language_server-protocol (3.17.0.4)
|
48
|
-
listen (3.9.0)
|
49
|
-
rb-fsevent (~> 0.10, >= 0.10.3)
|
50
|
-
rb-inotify (~> 0.9, >= 0.9.10)
|
51
|
-
logger (1.6.5)
|
52
|
-
lumberjack (1.2.10)
|
53
|
-
method_source (1.1.0)
|
54
|
-
nenv (0.3.0)
|
55
|
-
notiffany (0.1.3)
|
56
|
-
nenv (~> 0.1)
|
57
|
-
shellany (~> 0.0)
|
58
|
-
ostruct (0.6.1)
|
59
19
|
parallel (1.26.3)
|
60
20
|
parser (3.3.7.1)
|
61
21
|
ast (~> 2.4.1)
|
62
22
|
racc
|
63
|
-
pry (0.15.2)
|
64
|
-
coderay (~> 1.1)
|
65
|
-
method_source (~> 1.0)
|
66
23
|
racc (1.8.1)
|
67
24
|
rainbow (3.1.1)
|
68
25
|
rake (13.2.1)
|
69
|
-
rb-fsevent (0.11.2)
|
70
|
-
rb-inotify (0.11.1)
|
71
|
-
ffi (~> 1.0)
|
72
26
|
redis (4.8.1)
|
73
27
|
regexp_parser (2.10.0)
|
74
28
|
rspec (3.13.0)
|
@@ -101,14 +55,12 @@ GEM
|
|
101
55
|
rubocop-rspec (3.4.0)
|
102
56
|
rubocop (~> 1.61)
|
103
57
|
ruby-progressbar (1.13.0)
|
104
|
-
shellany (0.0.1)
|
105
58
|
simplecov (0.22.0)
|
106
59
|
docile (~> 1.1)
|
107
60
|
simplecov-html (~> 0.11)
|
108
61
|
simplecov_json_formatter (~> 0.1)
|
109
62
|
simplecov-html (0.13.1)
|
110
63
|
simplecov_json_formatter (0.1.4)
|
111
|
-
thor (1.3.2)
|
112
64
|
timecop (0.9.10)
|
113
65
|
unicode-display_width (3.1.4)
|
114
66
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
@@ -130,7 +82,6 @@ PLATFORMS
|
|
130
82
|
|
131
83
|
DEPENDENCIES
|
132
84
|
fakeredis
|
133
|
-
guard-rspec
|
134
85
|
pause!
|
135
86
|
rake
|
136
87
|
rspec
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -7,11 +7,11 @@
|
|
7
7
|
[](https://rubygems.org/gems/pause)
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
9
9
|
|
10
|
-
# Pause
|
10
|
+
# Pause — A Redis-backed Rate Limiter
|
11
11
|
|
12
|
-
##
|
12
|
+
## Overview
|
13
13
|
|
14
|
-
**Pause** is a fast and very flexible Redis-backed rate-limiter. You can use it to track events, with
|
14
|
+
**Pause** is a fast and very flexible Redis-backed rate-limiter, written originally at [Wanelo.com](https://www.crunchbase.com/organization/wanelo). You can use it to track events, with
|
15
15
|
rules around how often they are allowed to occur within configured time checks.
|
16
16
|
|
17
17
|
Sample applications include:
|
@@ -139,12 +139,17 @@ Or install it yourself as:
|
|
139
139
|
|
140
140
|
### Configuration
|
141
141
|
|
142
|
-
Configure Pause. This could be in a Rails initializer.
|
142
|
+
Configure Pause. This could be in a Rails initializer. At the moment, pause can only be used as a singleton, i.e. you can not use `pause` with multiple configurations, or multiple redis backends. This is something that can be rather easily fixed, if necessary.
|
143
143
|
|
144
|
-
|
144
|
+
Therefore, you configure the Pause singleton with the following options:
|
145
|
+
|
146
|
+
* **redis connection parameters** - The host, port and db of the Redis instance to use.
|
147
|
+
|
148
|
+
* **resolution** - the length of time (in seconds) defining the minimum period into which action counts are
|
145
149
|
aggregated. This defines the size of the persistent store. The higher the number, the less data needs
|
146
150
|
to be persisted in Redis.
|
147
|
-
|
151
|
+
|
152
|
+
* **history** - The maximum amount of time (in seconds) that data is persisted.
|
148
153
|
|
149
154
|
```ruby
|
150
155
|
Pause.configure do |config|
|
@@ -156,12 +161,10 @@ Pause.configure do |config|
|
|
156
161
|
end
|
157
162
|
```
|
158
163
|
|
164
|
+
|
159
165
|
### Actions
|
160
166
|
|
161
|
-
Define local actions for your application. Actions define a scope by
|
162
|
-
which they are identified in the persistent store (aka "namespace"), and a set of checks. Checks define various
|
163
|
-
thresholds (`max_allowed`) against periods of time (`period_seconds`). When a threshold it triggered,
|
164
|
-
the action is rate limited, and stays rate limited for the duration of `block_ttl` seconds.
|
167
|
+
Define local actions for your application. Actions define a scope by which they are identified in the persistent store (aka "namespace"), and a set of checks. Checks define various thresholds (`max_allowed`) against periods of time (`period_seconds`). When a threshold it triggered, the action is rate limited, and stays rate limited for the duration of `block_ttl` seconds.
|
165
168
|
|
166
169
|
#### Checks
|
167
170
|
|
@@ -205,6 +208,7 @@ When an event occurs, you increment an instance of your action, optionally with
|
|
205
208
|
In the example at the top of the README you saw how we used `#unless_rate_limited` and `#if_rate_limited` methods. These are the recommended API methods, but if you must get a finer-grained control over the actions, you can also use methods such as `#ok?`, `#rate_limited?`, `#increment!` to do manually what the block methods do already. Below is an example of this "manual" implementation:
|
206
209
|
|
207
210
|
```ruby
|
211
|
+
# app/controllers/follows_controller.rb
|
208
212
|
class FollowsController < ApplicationController
|
209
213
|
def create
|
210
214
|
action = FollowAction.new(user.id)
|
@@ -216,6 +220,7 @@ class FollowsController < ApplicationController
|
|
216
220
|
end
|
217
221
|
end
|
218
222
|
|
223
|
+
# app/controlers/other_controller.rb
|
219
224
|
class OtherController < ApplicationController
|
220
225
|
def index
|
221
226
|
action = OtherAction.new(params[:thing])d
|
@@ -237,11 +242,16 @@ while true
|
|
237
242
|
action.increment!
|
238
243
|
|
239
244
|
rate_limit_event = action.analyze
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
+
|
246
|
+
if rate_limit_event # or action.ok?
|
247
|
+
# which key got rate limited ("thing")
|
248
|
+
rate_limit_event.identifier
|
249
|
+
# total count that triggered a rate limit
|
250
|
+
rate_limit_event.sum
|
251
|
+
# timestamp when rate limiting occurred
|
252
|
+
rate_limit_event.timestamp
|
253
|
+
# period check object, that triggered this rate limiting event
|
254
|
+
rate_limit_event.period_check
|
245
255
|
else
|
246
256
|
# not rate-limited, same as action.ok?
|
247
257
|
end
|
@@ -327,13 +337,13 @@ The action block list is implemented as a sorted set, so it should still be usab
|
|
327
337
|
|
328
338
|
## Testing
|
329
339
|
|
330
|
-
By default, `fakeredis` gem is used to emulate Redis in development.
|
340
|
+
By default, `fakeredis` gem is used to emulate Redis in development.
|
331
341
|
|
332
|
-
|
342
|
+
However, the same test-suite runs against a real redis, just be aware that using real redis server running locally on 127.0.0.1 will result in a `flush` operation on the current redis db during the spec run. In order to run specs against real redis, please make sure you have Redis running locally on the default port (6379), and that you are able to connect to it using `redis-cli -p 6379 -h 127.0.0.1`.
|
333
343
|
|
334
|
-
### Unit Testing with
|
344
|
+
### Unit Testing with FakeRedis
|
335
345
|
|
336
|
-
|
346
|
+
The gem `fakeredis` is the default for testing, and is also run whenever `bundle exec rspec` is executed, or `rake spec` task invoked.
|
337
347
|
|
338
348
|
```bash
|
339
349
|
bundle exec rake spec:unit
|
@@ -345,6 +355,16 @@ bundle exec rake spec:unit
|
|
345
355
|
bundle exec rake spec:integration
|
346
356
|
```
|
347
357
|
|
358
|
+
OR
|
359
|
+
|
360
|
+
```bash
|
361
|
+
PAUSE_REAL_REDIS=1 bundle exec rspec
|
362
|
+
```
|
363
|
+
|
364
|
+
### Test Coverage
|
365
|
+
|
366
|
+
At the time of this writing the gem has 100% test coverage. Please keep it that way ;)
|
367
|
+
|
348
368
|
## Contributing
|
349
369
|
|
350
370
|
Want to make it better? Cool. Here's how:
|
@@ -357,9 +377,15 @@ Want to make it better? Cool. Here's how:
|
|
357
377
|
|
358
378
|
## Authors
|
359
379
|
|
360
|
-
* This gem was
|
361
|
-
* It's been updated and refreshed by Konstantin Gredeskoul.
|
380
|
+
* This gem was donated to Open Source by [Wanelo, Inc](https://www.crunchbase.com/organization/wanelo)
|
362
381
|
|
382
|
+
* The original authors are:
|
383
|
+
* [Atasay Gökkaya](https://github.com/atasay)
|
384
|
+
* [Konstantin Gredeskoul](https://github.com/kigster)
|
385
|
+
* [Eric Saxby](https://github.com/sax)
|
386
|
+
* Paul Henry
|
387
|
+
|
388
|
+
* The gem is currently maintained and kept up to date by [Konstantin Gredeskoul](https://kig.re/)
|
363
389
|
|
364
390
|
Please see the [LICENSE.txt](LICENSE.txt) file for further details.
|
365
391
|
|
data/lib/pause/action.rb
CHANGED
data/lib/pause/version.rb
CHANGED
data/lib/pause.rb
CHANGED
@@ -13,6 +13,14 @@ require 'pause/rate_limited_event'
|
|
13
13
|
|
14
14
|
module Pause
|
15
15
|
PeriodCheck = Struct.new(:period_seconds, :max_allowed, :block_ttl) do
|
16
|
+
def initialize(*args, period_seconds: nil, max_allowed: nil, block_ttl: nil)
|
17
|
+
if args.any?
|
18
|
+
super(*args)
|
19
|
+
else
|
20
|
+
super(period_seconds, max_allowed, block_ttl)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
16
24
|
def <=>(other)
|
17
25
|
period_seconds <=> other.period_seconds
|
18
26
|
end
|
@@ -39,12 +47,12 @@ module Pause
|
|
39
47
|
|
40
48
|
attr_writer :adapter
|
41
49
|
|
42
|
-
def configure(&
|
43
|
-
@configure ||= Pause::Configuration.new.configure(&
|
50
|
+
def configure(&)
|
51
|
+
@configure ||= Pause::Configuration.new.configure(&)
|
44
52
|
end
|
45
53
|
|
46
|
-
def config(&
|
47
|
-
configure(&
|
54
|
+
def config(&)
|
55
|
+
configure(&)
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
data/spec/pause/analyzer_spec.rb
CHANGED
@@ -42,6 +42,10 @@ describe Pause::Analyzer do
|
|
42
42
|
expect(analyzer.check(action)).to be_nil
|
43
43
|
end
|
44
44
|
|
45
|
+
it 'returns nil if action is NOT rate limited' do
|
46
|
+
expect(action).not_to be_rate_limited
|
47
|
+
end
|
48
|
+
|
45
49
|
it 'returns blocked action if action is blocked' do
|
46
50
|
Timecop.freeze Time.now do
|
47
51
|
5.times do
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module Pause
|
6
|
+
RSpec.describe PeriodCheck do
|
7
|
+
let(:period_seconds) { 10 }
|
8
|
+
let(:max_allowed) { 2 }
|
9
|
+
let(:block_ttl) { 10 }
|
10
|
+
|
11
|
+
let(:period_check_1) do
|
12
|
+
described_class.new(
|
13
|
+
period_seconds: period_seconds,
|
14
|
+
max_allowed: max_allowed,
|
15
|
+
block_ttl: block_ttl
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:period_check_2) do
|
20
|
+
described_class.new(
|
21
|
+
period_seconds: 2 * period_seconds,
|
22
|
+
max_allowed: 1.5 * max_allowed,
|
23
|
+
block_ttl:
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#==' do
|
28
|
+
let(:period_check_3) do
|
29
|
+
described_class.new(
|
30
|
+
period_seconds: 2 * period_seconds,
|
31
|
+
max_allowed: 1.5 * max_allowed,
|
32
|
+
block_ttl:
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is equal' do
|
37
|
+
expect(period_check_3).to eq(period_check_2)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is not equal' do
|
41
|
+
expect(period_check_3).not_to eq(period_check_1)
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#sort' do
|
45
|
+
let(:list) { [period_check_2, period_check_1] }
|
46
|
+
|
47
|
+
it 'sorts' do
|
48
|
+
expect(list.sort).to eq([period_check_1, period_check_2])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pause
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Gredeskoul
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
- Eric Saxby
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2025-02-
|
13
|
+
date: 2025-02-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: colored2
|
@@ -61,9 +61,9 @@ files:
|
|
61
61
|
- ".rubocop.yml"
|
62
62
|
- ".rubocop_todo.yml"
|
63
63
|
- ".rvmrc"
|
64
|
+
- CHANGELOG.md
|
64
65
|
- Gemfile
|
65
66
|
- Gemfile.lock
|
66
|
-
- Guardfile
|
67
67
|
- LICENSE.txt
|
68
68
|
- README.md
|
69
69
|
- Rakefile
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- spec/pause/configuration_spec.rb
|
85
85
|
- spec/pause/logger_spec.rb
|
86
86
|
- spec/pause/pause_spec.rb
|
87
|
+
- spec/pause/period_check_spec.rb
|
87
88
|
- spec/pause/redis/adapter_spec.rb
|
88
89
|
- spec/pause/redis/sharded_adapter_spec.rb
|
89
90
|
- spec/spec_helper.rb
|
data/Guardfile
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# ^syntax detection
|
5
|
-
|
6
|
-
# A sample Guardfile
|
7
|
-
# More info at https://github.com/guard/guard#readme
|
8
|
-
|
9
|
-
guard 'rspec' do
|
10
|
-
watch(/^spanx\.gemspec/) { 'spec' }
|
11
|
-
watch(%r{^lib/(.+)\.rb$}) { 'spec' }
|
12
|
-
|
13
|
-
watch(%r{^spec/.+_spec\.rb$})
|
14
|
-
watch('spec/spec_helper.rb') { 'spec' }
|
15
|
-
watch(%r{spec/support/.*}) { 'spec' }
|
16
|
-
end
|