speed_limiter 0.0.1
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 +7 -0
- data/.github/workflows/lint.yml +24 -0
- data/.github/workflows/test.yml +44 -0
- data/.gitignore +16 -0
- data/.rubocop.yml +20 -0
- data/Gemfile +20 -0
- data/LICENSE +21 -0
- data/README.md +108 -0
- data/README_image.jpg +0 -0
- data/Rakefile +20 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/compose.yaml +5 -0
- data/lib/speed_limiter/config.rb +17 -0
- data/lib/speed_limiter/version.rb +5 -0
- data/lib/speed_limiter.rb +70 -0
- data/speed_limitter.gemspec +35 -0
- data/throttle_server/config.ru +64 -0
- data/throttle_server/puma.rb +8 -0
- data/throttle_server/tmp/.keep +0 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ee6151563233234c9c453843d4d3d98e8de465905e38e99437caaa6a0e258d6b
|
4
|
+
data.tar.gz: 9efaa7b0784d79a9a23c77015013519380d20b94645e39e725d390030ad48569
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9672f7b225e6325589c9bb8fae4cc76ad860024b9fbbcdb6f10edc5d4137ac593b4d6ac5370e6490b1ccc1186b0032bf20d572644ec8177afa783d5603cfe2d
|
7
|
+
data.tar.gz: 74020c4965b083e2cea5af94a05299455d2ee64060fc008e8fa9b7b5afacae0a587d432925ece9f7efb860388fa15886742d7c5d722751cdb877d6bf31d30110
|
@@ -0,0 +1,24 @@
|
|
1
|
+
name: lint
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
permissions: # added using https://github.com/step-security/secure-workflows
|
6
|
+
contents: read
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
lint:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
continue-on-error: true
|
12
|
+
timeout-minutes: 10
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v3
|
16
|
+
|
17
|
+
- name: Set up Ruby
|
18
|
+
uses: ruby/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: '3.1'
|
21
|
+
bundler-cache: true
|
22
|
+
|
23
|
+
- name: Run rubocop
|
24
|
+
run: bundle exec rubocop
|
@@ -0,0 +1,44 @@
|
|
1
|
+
name: test
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_dispatch:
|
5
|
+
schedule:
|
6
|
+
- cron: '40 8 * * MON'
|
7
|
+
push:
|
8
|
+
paths-ignore:
|
9
|
+
- '**/*.md'
|
10
|
+
- 'LICENSE'
|
11
|
+
|
12
|
+
permissions: # added using https://github.com/step-security/secure-workflows
|
13
|
+
contents: read
|
14
|
+
|
15
|
+
jobs:
|
16
|
+
test:
|
17
|
+
runs-on: ubuntu-latest
|
18
|
+
timeout-minutes: 10
|
19
|
+
|
20
|
+
strategy:
|
21
|
+
matrix:
|
22
|
+
ruby-version: ['3.0', '3.1', '3.2', ruby-head]
|
23
|
+
redis-version: ['5.0', '6.0', '6.2', '7.0', '7.2', latest]
|
24
|
+
|
25
|
+
services:
|
26
|
+
redis:
|
27
|
+
image: redis:${{ matrix.redis-version }}
|
28
|
+
ports:
|
29
|
+
- 6379:6379
|
30
|
+
|
31
|
+
steps:
|
32
|
+
- uses: actions/checkout@v3
|
33
|
+
|
34
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
35
|
+
uses: ruby/setup-ruby@v1
|
36
|
+
with:
|
37
|
+
ruby-version: ${{ matrix.ruby-version }}
|
38
|
+
bundler-cache: true
|
39
|
+
|
40
|
+
- name: Rackup test throttle server
|
41
|
+
run: bundle exec pumad -C throttle_server/puma.rb throttle_server/config.ru
|
42
|
+
|
43
|
+
- name: Run tests
|
44
|
+
run: bundle exec rspec -fd
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
- rubocop-rake
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
TargetRubyVersion: 3.0
|
7
|
+
NewCops: enable
|
8
|
+
|
9
|
+
Style/StringLiterals:
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Metrics/AbcSize:
|
13
|
+
Max: 30
|
14
|
+
|
15
|
+
Rspec/MultipleExpectations:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Rspec/ExampleLength:
|
19
|
+
Enabled: false
|
20
|
+
|
data/Gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in SpeedLimiter.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
group :development do
|
9
|
+
gem "parallel", "~> 1.23"
|
10
|
+
gem "puma", "~> 6.4"
|
11
|
+
gem "puma-daemon", "~> 0.3", require: false
|
12
|
+
gem "rack", "~> 3.0"
|
13
|
+
gem "rack-attack", "~> 6.7"
|
14
|
+
gem "rackup", "~> 2.1"
|
15
|
+
gem "rake"
|
16
|
+
gem "rspec"
|
17
|
+
gem "rubocop"
|
18
|
+
gem "rubocop-rake"
|
19
|
+
gem "rubocop-rspec"
|
20
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Seibii, Inc
|
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,108 @@
|
|
1
|
+
[](https://github.com/seibii/speed_limiter/actions/workflows/lint.yml) [](https://github.com/seibii/speed_limiter/actions/workflows/test.yml) [](https://badge.fury.io/rb/speed_limiter) [](LICENSE)
|
2
|
+
|
3
|
+
# SpeedLimiter
|
4
|
+
|
5
|
+
<img src="README_image.jpg" width="400px" />
|
6
|
+
|
7
|
+
This is a Gem for execution limits in multi-process and multi-threaded environments. By using Redis, you can limit execution across multiple processes and threads.
|
8
|
+
|
9
|
+
It was mainly created to avoid hitting access limits to the API server.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'speed_limiter'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle install
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install speed_limiter
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# Limit the number of executions to 10 times per second
|
31
|
+
SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1) do
|
32
|
+
# Do something
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
### Configuration
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
# config/initializers/speed_limiter.rb
|
40
|
+
SpeedLimiter.configure do |config|
|
41
|
+
config.redis_url = ENV['SPEED_LIMITER_REDIS_URL'] || 'redis://localhost:6379/2'
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
or Use Redis instance
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
# config/initializers/speed_limiter.rb
|
49
|
+
SpeedLimiter.configure do |config|
|
50
|
+
config.redis = Redis.new(host: 'localhost', port: 6379, db: 2)
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
If you do not want to impose a limit in the test environment, please set it as follows.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
# spec/support/speed_limiter.rb
|
58
|
+
RSpec.configure do |config|
|
59
|
+
config.before(:suite) do
|
60
|
+
SpeedLimiter.configure do |config|
|
61
|
+
config.no_limit = true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
## Compatibility
|
68
|
+
|
69
|
+
SpeedLimiter officially supports the following Ruby implementations and Redis :
|
70
|
+
|
71
|
+
- Ruby MRI 3.0, 3.1, 3.2
|
72
|
+
- Redis 5.0, 6.0, 6.2, 7.0, 7.2
|
73
|
+
|
74
|
+
|
75
|
+
## Development
|
76
|
+
|
77
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
78
|
+
Please run rspec referring to the following.
|
79
|
+
|
80
|
+
Before committing, run bundle exec rubocop to perform a style check.
|
81
|
+
You can also run bin/console for an interactive prompt that will allow you to experiment.
|
82
|
+
|
83
|
+
### rspec
|
84
|
+
|
85
|
+
Start a web server and Redis for testing with the following command.
|
86
|
+
|
87
|
+
```
|
88
|
+
$ rake test:throttle_server
|
89
|
+
$ docker compose up
|
90
|
+
```
|
91
|
+
|
92
|
+
After that, please run the test with the following command.
|
93
|
+
|
94
|
+
```
|
95
|
+
$ bundle exec rspec -fd
|
96
|
+
```
|
97
|
+
|
98
|
+
## Contribution
|
99
|
+
|
100
|
+
1. Fork it ( https://github.com/seibii/speed_limiter/fork )
|
101
|
+
2. Create your feature branch (git checkout -b my-new-feature)
|
102
|
+
3. Commit your changes (git commit -am 'Add some feature')
|
103
|
+
4. Push to the branch (git push origin my-new-feature)
|
104
|
+
5. Create new Pull Request
|
105
|
+
|
106
|
+
## License
|
107
|
+
|
108
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/README_image.jpg
ADDED
Binary file
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "bundler/setup"
|
5
|
+
|
6
|
+
require "bundler/gem_tasks"
|
7
|
+
require "rspec/core/rake_task"
|
8
|
+
require "rubocop/rake_task"
|
9
|
+
|
10
|
+
RSpec::Core::RakeTask.new(:spec)
|
11
|
+
RuboCop::RakeTask.new
|
12
|
+
|
13
|
+
task default: :spec
|
14
|
+
|
15
|
+
namespace :test do
|
16
|
+
desc "Run rackup for throttle server daemon"
|
17
|
+
task :throttle_server do
|
18
|
+
system "puma -C throttle_server/puma.rb throttle_server/config.ru"
|
19
|
+
end
|
20
|
+
end
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/compose.yaml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SpeedLimiter
|
4
|
+
# config model
|
5
|
+
class Config
|
6
|
+
attr_accessor :redis_url, :redis, :no_limit, :prefix
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@redis_url = "redis://localhost:6379/0"
|
10
|
+
@redis = nil
|
11
|
+
@no_limit = false
|
12
|
+
@prefix = "speed_limiter"
|
13
|
+
end
|
14
|
+
|
15
|
+
alias no_limit? no_limit
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "speed_limiter/version"
|
4
|
+
require "speed_limiter/config"
|
5
|
+
require "redis"
|
6
|
+
|
7
|
+
# Call speed limiter
|
8
|
+
module SpeedLimiter
|
9
|
+
class << self
|
10
|
+
def config
|
11
|
+
@config ||= Config.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def configure
|
15
|
+
yield(config)
|
16
|
+
end
|
17
|
+
|
18
|
+
def redis
|
19
|
+
@redis ||= config.redis || Redis.new(url: config.redis_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
def throttle(key, limit:, period:, &block)
|
23
|
+
return block&.call if config.no_limit?
|
24
|
+
|
25
|
+
key_name = "#{config.prefix}:#{key}"
|
26
|
+
loop do
|
27
|
+
count = increment(key_name, period)
|
28
|
+
|
29
|
+
break(block&.call) if count <= limit
|
30
|
+
|
31
|
+
wait_for_interval(key_name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def wait_for_interval(key)
|
38
|
+
pttl = redis.pttl(key)
|
39
|
+
ttl = pttl / 1000.0
|
40
|
+
|
41
|
+
return if ttl.negative?
|
42
|
+
|
43
|
+
sleep ttl
|
44
|
+
end
|
45
|
+
|
46
|
+
def increment(key, period) # rubocop:disable Metrics/MethodLength
|
47
|
+
if supports_expire_nx?
|
48
|
+
count, = redis.pipelined do |pipeline|
|
49
|
+
pipeline.incrby(key, 1)
|
50
|
+
pipeline.call(:expire, key, period.to_i, "NX")
|
51
|
+
end
|
52
|
+
else
|
53
|
+
count, ttl = redis.pipelined do |pipeline|
|
54
|
+
pipeline.incrby(key, 1)
|
55
|
+
pipeline.ttl(key)
|
56
|
+
end
|
57
|
+
redis.expire(key, period.to_i) if ttl.negative?
|
58
|
+
end
|
59
|
+
|
60
|
+
count
|
61
|
+
end
|
62
|
+
|
63
|
+
def supports_expire_nx?
|
64
|
+
return @supports_expire_nx if defined?(@supports_expire_nx)
|
65
|
+
|
66
|
+
redis_versions = redis.info("server")["redis_version"]
|
67
|
+
@supports_expire_nx = Gem::Version.new(redis_versions) >= Gem::Version.new("7.0.0")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "speed_limiter/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.required_ruby_version = ">= 3.0.0"
|
9
|
+
|
10
|
+
spec.name = "speed_limiter"
|
11
|
+
spec.version = SpeedLimiter::VERSION
|
12
|
+
spec.authors = ["yuhei mukoyama"]
|
13
|
+
spec.email = ["yuhei.mukoyama@seibii.com"]
|
14
|
+
|
15
|
+
spec.summary = "Limit the frequency of execution across multiple threads and processes"
|
16
|
+
spec.homepage = "https://github.com/seibii/speed_limiter"
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
20
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
21
|
+
if spec.respond_to?(:metadata)
|
22
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
23
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
24
|
+
else
|
25
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
26
|
+
"public gem pushes."
|
27
|
+
end
|
28
|
+
|
29
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
30
|
+
f.match(%r{^(test|spec|features)/})
|
31
|
+
end
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_dependency "redis"
|
35
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/attack"
|
4
|
+
use Rack::Attack
|
5
|
+
|
6
|
+
# Throttling test server
|
7
|
+
module ThrottlingServer
|
8
|
+
THROTTLE_MATCHER = %r{/(\d+)/(\d+)s(\?.+=.*)?}
|
9
|
+
|
10
|
+
def self.call(env)
|
11
|
+
case env["REQUEST_URI"]
|
12
|
+
when "/"
|
13
|
+
[200, { "content-type" => "text/plain" }, ["Hi!\nPlease access to /#\{limit}/#\{period}s\n\n", throttle_info]]
|
14
|
+
when THROTTLE_MATCHER
|
15
|
+
(limit, period) = Regexp.last_match.captures
|
16
|
+
|
17
|
+
puts "[OK] #{env['REQUEST_METHOD']} #{env['REQUEST_URI']}\n #{throttle_info.gsub("\n", "\n ")}"
|
18
|
+
[200, { "content-type" => "text/plain" }, ["OK\n#{limit} / #{period} seconds\n\n", throttle_info]]
|
19
|
+
else
|
20
|
+
[404, { "content-type" => "text/plain" }, ["Not Found"]]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.throttle_info
|
25
|
+
list = REDIS.scan_each.map { |key| "#{key} count: #{REDIS.get(key)}, ttl: #{REDIS.pttl(key)}ms" }.sort.join("\n")
|
26
|
+
"Throttle Info:\n#{list}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.limit(req)
|
30
|
+
case req.path
|
31
|
+
when THROTTLE_MATCHER
|
32
|
+
Regexp.last_match.captures[0].to_i
|
33
|
+
else
|
34
|
+
9999
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.period(req)
|
39
|
+
case req.path
|
40
|
+
when THROTTLE_MATCHER
|
41
|
+
Regexp.last_match.captures[1].to_i
|
42
|
+
else
|
43
|
+
9999
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Rack::Attack.throttle(
|
49
|
+
"requests by path", limit: ThrottlingServer.method(:limit),
|
50
|
+
period: ThrottlingServer.method(:period)
|
51
|
+
) do |request|
|
52
|
+
request.url if request.url.match(ThrottlingServer::THROTTLE_MATCHER)
|
53
|
+
end
|
54
|
+
|
55
|
+
Rack::Attack.throttled_responder = lambda do |env|
|
56
|
+
puts "[Retry later] #{env['REQUEST_METHOD']} #{env['REQUEST_URI']}\n " \
|
57
|
+
"#{ThrottlingServer.throttle_info.gsub("\n", "\n ")}"
|
58
|
+
[429, {}, ["Retry later\n\n", ThrottlingServer.throttle_info]]
|
59
|
+
end
|
60
|
+
|
61
|
+
require "redis"
|
62
|
+
REDIS = Rack::Attack.cache.store = Redis.new(url: ENV.fetch("THROTTLE_SERVER_REDIS_URL", "redis://localhost:6379/15"))
|
63
|
+
|
64
|
+
run ThrottlingServer
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: speed_limiter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- yuhei mukoyama
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-11-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redis
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- yuhei.mukoyama@seibii.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".github/workflows/lint.yml"
|
35
|
+
- ".github/workflows/test.yml"
|
36
|
+
- ".gitignore"
|
37
|
+
- ".rubocop.yml"
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE
|
40
|
+
- README.md
|
41
|
+
- README_image.jpg
|
42
|
+
- Rakefile
|
43
|
+
- bin/console
|
44
|
+
- bin/setup
|
45
|
+
- compose.yaml
|
46
|
+
- lib/speed_limiter.rb
|
47
|
+
- lib/speed_limiter/config.rb
|
48
|
+
- lib/speed_limiter/version.rb
|
49
|
+
- speed_limitter.gemspec
|
50
|
+
- throttle_server/config.ru
|
51
|
+
- throttle_server/puma.rb
|
52
|
+
- throttle_server/tmp/.keep
|
53
|
+
homepage: https://github.com/seibii/speed_limiter
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
metadata:
|
57
|
+
rubygems_mfa_required: 'true'
|
58
|
+
allowed_push_host: https://rubygems.org
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 3.0.0
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.2.22
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Limit the frequency of execution across multiple threads and processes
|
78
|
+
test_files: []
|