rollie 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6e0f59a08393e6f0955646fc41f0598b65cccb7d45d1b0ded22c30806bfd2e2
4
- data.tar.gz: 209a877efa2dcef64db8c0f63099af0c029e31db39dea4cc13b228d1c5f0ab1d
3
+ metadata.gz: c4588225ac38e980c73e7d5f20b99dca196de34e217d6307b08f3a86f8ae7ef3
4
+ data.tar.gz: ee71e83156c12dea359f67f00593849c119ffb3e2cb867c4b1e07f2af22ac59e
5
5
  SHA512:
6
- metadata.gz: bc2a3c339d26f054d44f20b331643932c13330767e0e53a1ae2ecdf8b92dff64cdd4eecc6c93f62147857a145bd9b56d1c91eeca8cd5037af1f0f389fc95e538
7
- data.tar.gz: 5fed3ce9dd06e6b85e1e17cba4f92a2cb17ae448de79f7e83b22831ebcad42e17cdd64a1eca82acf9a202e2d450ecff75f55de90aa53ddc6d7651877d726e92d
6
+ metadata.gz: a399df06ad904645db53bbf56a4d9de9661ac3f62b5cb3f8d90c40e7f3106191aad322e1afd2b457e5a22f9f22cd0c0469e41d1f29b4dbe532353e7ecefd807a
7
+ data.tar.gz: cd284a9a5dd513a6c0a41311ed3b282e20a9115c1bf0e1ca626460580aa27a141d6bb5600bca62a3215e8a96177bcb8c2a8f394a1b1e45b234699c937979735b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - Support redis gem versions 3-5 #6 justinhoward
6
+
3
7
  ## 0.1.1
4
8
 
5
9
  * Remove maximum dependency versions
data/README.md CHANGED
@@ -1,17 +1,26 @@
1
- # Rollie
2
-
3
- ![CI](https://github.com/ParentSquare/rollie/workflows/CI/badge.svg)
4
-
5
- Rollie is a multi-purpose, fast, redis backed rate limiter that can be used to limit requests to external APIs, in Rack
6
- middleware, etc. Rollie uses a dedicated redis connection pool implemented using `connection_pool` for more efficient
7
- redis connection management. The redis algorithm was inspired by the
8
- [rolling-rate-limiter](https://www.npmjs.com/package/rolling-rate-limiter) node package.
9
-
10
- The key implementation detail is that Rollie utilizes a rolling window to bucket invocations in. Meaning, if you set
11
- a limit of 100 per 30 seconds, Rollie will start the clock in instant it is first executed with a given key.
1
+ Rollie
2
+ ===========
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/rollie.svg)](https://badge.fury.io/rb/rollie)
5
+ [![CI](https://github.com/ParentSquare/rollie/workflows/CI/badge.svg)](https://github.com/ParentSquare/rollie/actions?query=workflow%3ACI+branch%3Amaster)
6
+ [![Code Quality](https://app.codacy.com/project/badge/Grade/20f8a080aca5444cbdaebff3a4e7e702)](https://www.codacy.com/gh/ParentSquare/rollie/dashboard?utm_source=github.com&utm_medium=referral&utm_content=ParentSquare/rollie&utm_campaign=Badge_Grade)
7
+ [![Coverage Status](https://codecov.io/gh/ParentSquare/rollie/branch/master/graph/badge.svg?token=0I92PXGZCM)](https://codecov.io/gh/ParentSquare/rollie)
8
+ [![Online docs](https://img.shields.io/badge/docs-✓-green.svg)](https://www.rubydoc.info/github/ParentSquare/rollie)
9
+
10
+ Rollie is a multi-purpose, fast, Redis backed rate limiter that can be used to
11
+ limit requests to external APIs, in Rack middleware, etc. Rollie uses a
12
+ dedicated Redis connection pool implemented using `connection_pool` for more
13
+ efficient Redis connection management. The Redis algorithm was inspired by the
14
+ [rolling-rate-limiter](https://www.npmjs.com/package/rolling-rate-limiter) node
15
+ package.
16
+
17
+ The key implementation detail is that Rollie utilizes a rolling window to bucket
18
+ invocations in. Meaning, if you set a limit of 100 per 30 seconds, Rollie will
19
+ start the clock in instant it is first executed with a given key.
12
20
 
13
21
  For example, first execution:
14
- ```
22
+
23
+ ```ruby
15
24
  rollie = Rollie::RateLimiter.new("api", limit: 10, interval: 30000)
16
25
  rollie.within_limit do
17
26
  puts Time.now
@@ -19,25 +28,38 @@ end
19
28
  # => 2016-12-03 08:31:23.873
20
29
  ```
21
30
 
22
- This doesn't mean the count is reset back to 0 at `2016-12-03 08:31:53.873`. Its a continuous rolling count, the count
23
- is checked with every invocation over the last 30 seconds.
24
-
25
- If you invoke this rate 9 times at `2016-12-03 08:31:53.500`, you will only be able to make one more call until `2016-12-03 08:32:23.500`.
31
+ This doesn't mean the count is reset back to 0 at `2016-12-03 08:31:53.873`. Its
32
+ a continuous rolling count, the count is checked with every invocation over the
33
+ last 30 seconds.
34
+
35
+ If you invoke this rate 9 times at `2016-12-03 08:31:53.500`, you will only be
36
+ able to make one more call until `2016-12-03 08:32:23.500`.
26
37
 
27
38
  ## Install
28
39
 
40
+ Add it to your `Gemfile`:
41
+
42
+ ```ruby
43
+ gem 'rollie'
29
44
  ```
45
+
46
+ Or install it manually:
47
+
48
+ ```sh
30
49
  gem install rollie
31
50
  ```
32
51
 
33
- ## Usage
52
+ Usage
53
+ -----------
34
54
 
35
- Rollie is simple to use and has only one method, `within_limit`. `within_limit` expects a block and that block will be
36
- executed only if you are within the limit.
55
+ Rollie is simple to use and has only one method, `within_limit`. `within_limit`
56
+ expects a block and that block will be executed only if you are within the
57
+ limit.
37
58
 
38
- Initialize Rollie with a key used to uniquely identify what you are limiting. Use the options to set the limit and
39
- interval in milliseconds.
40
- ```
59
+ Initialize Rollie with a key used to uniquely identify what you are limiting.
60
+ Use the options to set the limit and interval in milliseconds.
61
+
62
+ ```ruby
41
63
  # limit 30 requests per second.
42
64
  twitter_rate = Rollie::RateLimiter.new("twitter_requests", limit: 30, interval: 1000)
43
65
  status = twitter_rate.within_limit do
@@ -45,8 +67,10 @@ status = twitter_rate.within_limit do
45
67
  end
46
68
  ```
47
69
 
48
- The status will tell you the current state. You can also see the current count and how long until the bucket resets.
49
- ```
70
+ The status will tell you the current state. You can also see the current count
71
+ and how long until the bucket resets.
72
+
73
+ ```ruby
50
74
  status.exceeded?
51
75
  # => false
52
76
  status.count
@@ -56,34 +80,53 @@ status.time_remaining
56
80
  ```
57
81
 
58
82
  Once exceeded:
59
- ```
83
+
84
+ ```ruby
60
85
  status.exceeded?
61
86
  # => true
62
87
  status.count
63
88
  # => 30
64
89
  status.time_remaining
65
90
  # => 461 # milliseconds
66
- ```
67
-
68
- You can also use a namespace if you want to track multiple entities, for example users.
69
91
  ```
70
- Rollie::RateLimiter.new(user_id, namespace: "user_messages", limit: 100, interval: 30000)
92
+
93
+ You can also use a namespace if you want to track multiple entities, for example
94
+ users.
95
+
96
+ ```ruby
97
+ Rollie::RateLimiter.new(
98
+ user_id,
99
+ namespace: "user_messages",
100
+ limit: 100,
101
+ interval: 30000
102
+ )
71
103
  ```
72
104
 
73
105
  ### Counting blocked actions
74
106
 
75
- By default, blocked actions are not counted against the callee. This allows for the block to be executed within the
76
- rate even when there is a continuous flood of action. If you wish to change this behaviour, for example to require the callee to back off before being allowed to excute again, set this option to true.
77
-
78
- ```
79
- request_rate = Rollie::RateLimiter.new(ip, namespace: "ip", limit: 30, interval: 1000, count_blocked: true)
107
+ By default, blocked actions are not counted against the callee. This allows for
108
+ the block to be executed within the rate even when there is a continuous flood
109
+ of action. If you wish to change this behaviour, for example to require the
110
+ callee to back off before being allowed to execute again, set this option to
111
+ true.
112
+
113
+ ```ruby
114
+ request_rate = Rollie::RateLimiter.new(
115
+ ip,
116
+ namespace: "ip",
117
+ limit: 30,
118
+ interval: 1000,
119
+ count_blocked: true
120
+ )
80
121
  ```
81
122
 
82
- ## Configuration
123
+ Configuration
124
+ -------------------
83
125
 
84
- By default Rollie will try to connect to redis using `ENV["REDIS_URL"]` if set or fallback to localhost:6379. You can
85
- set an alternate redis configuration:
86
- ```
126
+ By default Rollie will try to connect to Redis using `ENV["REDIS_URL"]` if set
127
+ or fallback to localhost:6379. You can set an alternate Redis configuration:
128
+
129
+ ```ruby
87
130
  Rollie.redis = {
88
131
  url: CONFIG[:redis_url],
89
132
  pool_size: 5,
@@ -92,4 +135,5 @@ Rollie.redis = {
92
135
  }
93
136
  ```
94
137
 
95
- If using rails, create an initializer `config/initializers/rollie.rb` with these settings.
138
+ If using rails, create an initializer `config/initializers/rollie.rb` with these
139
+ settings.
@@ -1,14 +1,15 @@
1
- module Rollie
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rollie
3
4
  class RateLimiter
4
-
5
5
  # Create a new RateLimiter instance.
6
6
  #
7
7
  # @param [String] key A unique name to track this rate limit against.
8
8
  # @option options [Integer] :limit The limit
9
9
  # @option options [Integer] :interval The interval in milliseconds for this rate limit
10
10
  # @option options [String] :namespace Optional namespace for this rate limit
11
- # @option options [Boolean] :count_blocked if true, all calls to within_limit will count towards total execution count, even if blocked.
11
+ # @option options [Boolean] :count_blocked if true, all calls to
12
+ # within_limit will count towards total execution count, even if blocked.
12
13
  #
13
14
  # @return [RateLimiter] RateLimiter instance
14
15
  def initialize(key, options = {})
@@ -22,7 +23,7 @@ module Rollie
22
23
  #
23
24
  # @return [Status] The current status for this RateLimiter.
24
25
  def within_limit
25
- raise ArgumentError, "requires a block" unless block_given?
26
+ raise ArgumentError, 'requires a block' unless block_given?
26
27
 
27
28
  Rollie.redis do |conn|
28
29
  status = inc(conn)
@@ -44,13 +45,13 @@ module Rollie
44
45
  private
45
46
 
46
47
  def inc(conn)
47
- time = (Time.now.to_r * 1000000).round
48
+ time = (Time.now.to_r * 1_000_000).round
48
49
  old = time - @interval
49
- range = conn.multi do
50
- conn.zremrangebyscore(@key, 0, old)
51
- conn.zadd(@key, time, time)
52
- conn.zrange(@key, 0, -1)
53
- conn.expire(@key, (@interval / 1000000.0).ceil)
50
+ range = conn.multi do |multi|
51
+ multi.zremrangebyscore(@key, 0, old)
52
+ multi.zadd(@key, time, time)
53
+ multi.zrange(@key, 0, -1)
54
+ multi.expire(@key, (@interval / 1_000_000.0).ceil)
54
55
  end[2]
55
56
 
56
57
  exceeded = range.length > @limit
@@ -64,6 +65,5 @@ module Rollie
64
65
 
65
66
  Rollie::Status.new((time_remaining / 1000).floor, current_count, exceeded)
66
67
  end
67
-
68
68
  end
69
69
  end
@@ -1,16 +1,17 @@
1
- require "connection_pool"
2
- require "redis"
3
- require "redis-namespace"
1
+ # frozen_string_literal: true
2
+
3
+ require 'connection_pool'
4
+ require 'redis'
5
+ require 'redis-namespace'
4
6
 
5
7
  module Rollie
6
8
  class RedisPool
7
9
  class << self
8
-
9
- def create(options={})
10
+ def create(options = {})
10
11
  pool_size = options[:pool_size] || 5
11
12
  pool_timeout = options[:pool_timeout] || 1
12
13
 
13
- ConnectionPool.new(:timeout => pool_timeout, :size => pool_size) do
14
+ ConnectionPool.new(timeout: pool_timeout, size: pool_size) do
14
15
  build_client(options)
15
16
  end
16
17
  end
@@ -18,19 +19,18 @@ module Rollie
18
19
  private
19
20
 
20
21
  def build_client(options)
21
- namespace = options[:namespace] || "Rollie"
22
+ namespace = options[:namespace] || 'Rollie'
22
23
  client = Redis.new redis_options(options)
23
24
  Redis::Namespace.new(namespace, redis: client)
24
25
  end
25
26
 
26
27
  def redis_options(options)
27
28
  redis = options.dup
28
- redis[:url] ||= ENV["REDIS_URL"]
29
- redis[:driver] ||= "ruby"
29
+ redis[:url] ||= ENV.fetch('REDIS_URL', nil)
30
+ redis[:driver] ||= 'ruby'
30
31
  redis.delete(:namespace)
31
32
  redis
32
33
  end
33
-
34
34
  end
35
35
  end
36
36
  end
data/lib/rollie/status.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rollie
2
4
  class Status
3
5
  attr_accessor :time_remaining, :count
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rollie
2
- VERSION = "0.1.1".freeze
4
+ VERSION = '0.1.2'
3
5
  end
data/lib/rollie.rb CHANGED
@@ -1,16 +1,16 @@
1
- require "rollie/rate_limiter"
2
- require "rollie/redis_pool"
3
- require "rollie/status"
4
- require "rollie/version"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rollie/rate_limiter'
4
+ require 'rollie/redis_pool'
5
+ require 'rollie/status'
6
+ require 'rollie/version'
5
7
 
6
8
  module Rollie
7
9
  class << self
10
+ def redis(&block)
11
+ raise ArgumentError, 'requires a block' unless block
8
12
 
9
- def redis
10
- raise ArgumentError, "requires a block" unless block_given?
11
- redis_pool.with do |conn|
12
- yield(conn)
13
- end
13
+ redis_pool.with(&block)
14
14
  end
15
15
 
16
16
  # Configures the redis connection pool. Options can be a hash of redis connection pool options or a pre-configured
@@ -24,14 +24,13 @@ module Rollie
24
24
  def redis=(options)
25
25
  @redis_pool = if options.is_a?(ConnectionPool)
26
26
  options
27
- else
27
+ else
28
28
  Rollie::RedisPool.create(options)
29
- end
29
+ end
30
30
  end
31
31
 
32
32
  def redis_pool
33
33
  @redis_pool ||= Rollie::RedisPool.create
34
34
  end
35
-
36
35
  end
37
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rollie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Davis
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-31 00:00:00.000000000 Z
12
+ date: 2023-04-04 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: connection_pool
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 2.2.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 2.2.0
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: redis
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -40,19 +54,61 @@ dependencies:
40
54
  - !ruby/object:Gem::Version
41
55
  version: 1.5.2
42
56
  - !ruby/object:Gem::Dependency
43
- name: connection_pool
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3.8'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.8'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rubocop
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '1.7'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.7'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rubocop-rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '2.1'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '2.1'
98
+ - !ruby/object:Gem::Dependency
99
+ name: timecop
44
100
  requirement: !ruby/object:Gem::Requirement
45
101
  requirements:
46
102
  - - ">="
47
103
  - !ruby/object:Gem::Version
48
- version: 2.2.0
49
- type: :runtime
104
+ version: '0.9'
105
+ type: :development
50
106
  prerelease: false
51
107
  version_requirements: !ruby/object:Gem::Requirement
52
108
  requirements:
53
109
  - - ">="
54
110
  - !ruby/object:Gem::Version
55
- version: 2.2.0
111
+ version: '0.9'
56
112
  description: Generic rate limiter backed by Redis for efficient limiting using sliding
57
113
  windows.
58
114
  email: justin.howard@parentsquare.com
@@ -60,29 +116,20 @@ executables: []
60
116
  extensions: []
61
117
  extra_rdoc_files: []
62
118
  files:
63
- - ".github/workflows/ci.yml"
64
- - ".gitignore"
65
- - ".rspec"
66
119
  - CHANGELOG.md
67
- - Gemfile
68
- - LICENSE
69
120
  - README.md
70
- - Rakefile
71
- - bin/check-version
72
- - bin/rspec
73
121
  - lib/rollie.rb
74
122
  - lib/rollie/rate_limiter.rb
75
123
  - lib/rollie/redis_pool.rb
76
124
  - lib/rollie/status.rb
77
125
  - lib/rollie/version.rb
78
- - rollie.gemspec
79
- - spec/rollie/rate_limiter_spec.rb
80
- - spec/rollie_spec.rb
81
- - spec/spec_helper.rb
82
126
  homepage: https://github.com/ParentSquare/rollie
83
127
  licenses:
84
128
  - MIT
85
- metadata: {}
129
+ metadata:
130
+ rubygems_mfa_required: 'true'
131
+ changelog_uri: https://github.com/ParentSquare/rollie/blob/master/CHANGELOG.md
132
+ documentation_uri: https://www.rubydoc.info/gems/rollie/0.1.2
86
133
  post_install_message:
87
134
  rdoc_options: []
88
135
  require_paths:
@@ -91,15 +138,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
138
  requirements:
92
139
  - - ">="
93
140
  - !ruby/object:Gem::Version
94
- version: '0'
141
+ version: '2.4'
95
142
  required_rubygems_version: !ruby/object:Gem::Requirement
96
143
  requirements:
97
144
  - - ">="
98
145
  - !ruby/object:Gem::Version
99
146
  version: '0'
100
147
  requirements: []
101
- rubyforge_project:
102
- rubygems_version: 2.7.6
148
+ rubygems_version: 3.3.5
103
149
  signing_key:
104
150
  specification_version: 4
105
151
  summary: Generic rate limiter backed by Redis for efficient limiting using sliding
@@ -1,38 +0,0 @@
1
- ---
2
- name: CI
3
- on:
4
- push:
5
- tags: ['v*']
6
- branches: [master]
7
- pull_request:
8
- branches: ['**']
9
- jobs:
10
- test:
11
- runs-on: ubuntu-latest
12
- strategy:
13
- fail-fast: false
14
- matrix:
15
- ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
16
- services:
17
- redis:
18
- image: redis
19
- ports:
20
- - 6379:6379
21
- steps:
22
- - uses: actions/checkout@v2
23
- - uses: ruby/setup-ruby@v1
24
- with:
25
- ruby-version: ${{ matrix.ruby }}
26
- bundler-cache: true
27
- - run: bundle exec rspec --format doc
28
- - run: bin/check-version
29
-
30
- release:
31
- needs: test
32
- if: startsWith(github.ref, 'refs/tags/v')
33
- runs-on: ubuntu-latest
34
- steps:
35
- - uses: actions/checkout@v2
36
- - uses: dawidd6/action-publish-gem@v1
37
- with:
38
- api_key: ${{secrets.RUBYGEMS_API_KEY}}
data/.gitignore DELETED
@@ -1,13 +0,0 @@
1
- *.gem
2
- *.rbc
3
- Gemfile.lock
4
- .DS_Store
5
- dump.rdb
6
- doc/
7
- rdoc/
8
- /spec/reports/
9
- /.bundle
10
- tmp/
11
- .ruby-gemset
12
- .ruby-version
13
- .idea/
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --color
2
- --require spec_helper
data/Gemfile DELETED
@@ -1,8 +0,0 @@
1
- source "https://rubygems.org"
2
- gemspec :name => "rollie"
3
-
4
- gem "rake"
5
-
6
- group :test do
7
- gem "rspec", "~> 3.4"
8
- end
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2016 Zach Davis
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/Rakefile DELETED
@@ -1,28 +0,0 @@
1
- require "rake"
2
- require "rdoc"
3
-
4
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[lib]))
5
- require "rollie/version"
6
-
7
- def name
8
- "rollie"
9
- end
10
-
11
- def version
12
- Rollie::VERSION
13
- end
14
-
15
- begin
16
- require "rspec/core/rake_task"
17
- RSpec::Core::RakeTask.new(:spec)
18
- rescue LoadError; end
19
-
20
- require "rdoc/task"
21
- Rake::RDocTask.new do |rdoc|
22
- rdoc.rdoc_dir = "rdoc"
23
- rdoc.title = "#{name} #{version}"
24
- rdoc.rdoc_files.include("README*")
25
- rdoc.rdoc_files.include("lib/**/*.rb")
26
- end
27
-
28
- task :default => :spec
data/bin/check-version DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env sh
2
-
3
- set -e
4
-
5
- tag="$(git describe --abbrev=0 2>/dev/null || echo)"
6
- echo "Tag: ${tag}"
7
- tag="${tag#v}"
8
- echo "Git Version: ${tag}"
9
- [ "$tag" = '' ] && exit 0
10
- gem_version="$(ruby -r ./lib/rollie/version -e "puts Rollie::VERSION" | tail -n1)"
11
- echo "Gem Version: ${gem_version}"
12
-
13
- tag_gt_version="$(ruby -r ./lib/rollie/version -e "puts Gem::Version.new(Rollie::VERSION) >= Gem::Version.new('${tag}')" | tail -n1)"
14
- test "$tag_gt_version" = true
data/bin/rspec DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rspec' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "pathname"
12
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
- Pathname.new(__FILE__).realpath)
14
-
15
- bundle_binstub = File.expand_path("../bundle", __FILE__)
16
-
17
- if File.file?(bundle_binstub)
18
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
- load(bundle_binstub)
20
- else
21
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
- end
24
- end
25
-
26
- require "rubygems"
27
- require "bundler/setup"
28
-
29
- load Gem.bin_path("rspec-core", "rspec")
data/rollie.gemspec DELETED
@@ -1,24 +0,0 @@
1
- $LOAD_PATH.push File.expand_path("../lib", __FILE__)
2
- require "rollie/version"
3
-
4
- Gem::Specification.new do |s|
5
- s.name = "rollie"
6
- s.version = Rollie::VERSION
7
- s.license = "MIT"
8
-
9
- s.summary = "Generic rate limiter backed by Redis for efficient limiting using sliding windows."
10
- s.description = s.summary
11
-
12
- s.authors = ["Zach Davis", "Justin Howard"]
13
- s.email = "justin.howard@parentsquare.com"
14
- s.homepage = "https://github.com/ParentSquare/rollie"
15
-
16
- s.files = `git ls-files -z`.split("\x0")
17
- s.test_files = `git ls-files -- test/*`.split("\n")
18
-
19
- s.require_paths = ["lib"]
20
-
21
- s.add_dependency "redis", ">= 3.2.1"
22
- s.add_dependency "redis-namespace", ">= 1.5.2"
23
- s.add_dependency "connection_pool", ">= 2.2.0"
24
- end
@@ -1,84 +0,0 @@
1
- require "spec_helper"
2
- require "securerandom"
3
-
4
- module Rollie
5
- describe RateLimiter do
6
-
7
- before do
8
- @r = RateLimiter.new(SecureRandom.hex(8), count_blocked: true)
9
- end
10
-
11
- describe :within_limit do
12
-
13
- it "should require a block" do
14
- expect{ @r.within_limit }.to raise_error(ArgumentError)
15
- end
16
-
17
- it "should return status" do
18
- status = @r.within_limit do; end
19
- expect(status.count).to eq(1)
20
- expect(status.exceeded?).to be(false)
21
- expect(status.time_remaining).to eq(1000)
22
- end
23
-
24
- it "should execute block only while within limit" do
25
- count = 0
26
- status = nil
27
- 30.times do
28
- status = @r.within_limit do
29
- count += 1
30
- end
31
- end
32
- expect(count).to eq(25)
33
- expect(status.count).to eq(30)
34
- expect(status.exceeded?).to be(true)
35
- end
36
-
37
- it "should block all actions within the window" do
38
- @r = RateLimiter.new(SecureRandom.hex(8), limit: 10, interval: 100, count_blocked: true)
39
- count = 0
40
- 30.times do
41
- @r.within_limit do
42
- count += 1
43
- end
44
- sleep 0.004
45
- end
46
- expect(count).to eq(10)
47
- end
48
-
49
- it "should allow blocked actions not to be counted" do
50
- @r = RateLimiter.new(SecureRandom.hex(8), limit: 10, interval: 100, count_blocked: false)
51
- count = 0
52
- 30.times do
53
- @r.within_limit do
54
- count += 1
55
- end
56
- sleep 0.0045
57
- end
58
- expect(count).to eq(20)
59
- end
60
-
61
- end
62
-
63
- describe :count do
64
-
65
- it "should return the current count" do
66
- 30.times do
67
- @r.within_limit do; sleep 0.001; end
68
- end
69
-
70
- expect(@r.count).to eq(30)
71
-
72
- @r = RateLimiter.new(SecureRandom.hex(8), limit: 10, count_blocked: false)
73
-
74
- 30.times do
75
- @r.within_limit do; sleep 0.001; end
76
- end
77
-
78
- expect(@r.count).to eq(10)
79
- end
80
-
81
- end
82
-
83
- end
84
- end
data/spec/rollie_spec.rb DELETED
@@ -1,36 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Rollie do
4
-
5
- describe :redis do
6
-
7
- it "should require a block" do
8
- expect{ Rollie.redis }.to raise_error(ArgumentError)
9
- end
10
-
11
- it "should return a Redis instance from the pool" do
12
- Rollie.redis do |conn|
13
- expect(conn.class).to eq(Redis::Namespace)
14
- end
15
- end
16
-
17
- end
18
-
19
- describe :redis= do
20
-
21
- it "should allow hash options to initialize connection pool" do
22
- options = {url: "redis://foo"}
23
- pool = ConnectionPool.new do; end
24
- expect(Rollie::RedisPool).to receive(:create).with(options).and_return(pool)
25
- Rollie.redis = options
26
- end
27
-
28
- it "should allow a connection pool" do
29
- pool = ConnectionPool.new do; end
30
- Rollie.redis = pool
31
- expect(Rollie.redis_pool).to eq(pool)
32
- end
33
-
34
- end
35
-
36
- end
data/spec/spec_helper.rb DELETED
@@ -1,106 +0,0 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # The generated `.rspec` file contains `--require spec_helper` which will cause
4
- # this file to always be loaded, without a need to explicitly require it in any
5
- # files.
6
- #
7
- # Given that it is always loaded, you are encouraged to keep this file as
8
- # light-weight as possible. Requiring heavyweight dependencies from this file
9
- # will add to the boot time of your test suite on EVERY test run, even for an
10
- # individual file that may not need all of that loaded. Instead, consider making
11
- # a separate helper file that requires the additional dependencies and performs
12
- # the additional setup, and require it from the spec files that actually need
13
- # it.
14
- #
15
- # The `.rspec` file also contains a few flags that are not defaults but that
16
- # users commonly want.
17
- #
18
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
-
20
- require "rollie"
21
-
22
- RSpec.configure do |config|
23
- # rspec-expectations config goes here. You can use an alternate
24
- # assertion/expectation library such as wrong or the stdlib/minitest
25
- # assertions if you prefer.
26
- config.expect_with :rspec do |expectations|
27
- # This option will default to `true` in RSpec 4. It makes the `description`
28
- # and `failure_message` of custom matchers include text for helper methods
29
- # defined using `chain`, e.g.:
30
- # be_bigger_than(2).and_smaller_than(4).description
31
- # # => "be bigger than 2 and smaller than 4"
32
- # ...rather than:
33
- # # => "be bigger than 2"
34
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
35
- end
36
-
37
- # rspec-mocks config goes here. You can use an alternate test double
38
- # library (such as bogus or mocha) by changing the `mock_with` option here.
39
- config.mock_with :rspec do |mocks|
40
- # Prevents you from mocking or stubbing a method that does not exist on
41
- # a real object. This is generally recommended, and will default to
42
- # `true` in RSpec 4.
43
- mocks.verify_partial_doubles = true
44
- end
45
-
46
- # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
47
- # have no way to turn it off -- the option exists only for backwards
48
- # compatibility in RSpec 3). It causes shared context metadata to be
49
- # inherited by the metadata hash of host groups and examples, rather than
50
- # triggering implicit auto-inclusion in groups with matching metadata.
51
- config.shared_context_metadata_behavior = :apply_to_host_groups
52
-
53
- # The settings below are suggested to provide a good initial experience
54
- # with RSpec, but feel free to customize to your heart's content.
55
- =begin
56
- # This allows you to limit a spec run to individual examples or groups
57
- # you care about by tagging them with `:focus` metadata. When nothing
58
- # is tagged with `:focus`, all examples get run. RSpec also provides
59
- # aliases for `it`, `describe`, and `context` that include `:focus`
60
- # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
61
- config.filter_run_when_matching :focus
62
-
63
- # Allows RSpec to persist some state between runs in order to support
64
- # the `--only-failures` and `--next-failure` CLI options. We recommend
65
- # you configure your source control system to ignore this file.
66
- config.example_status_persistence_file_path = "spec/examples.txt"
67
-
68
- # Limits the available syntax to the non-monkey patched syntax that is
69
- # recommended. For more details, see:
70
- # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
71
- # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
72
- # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
73
- config.disable_monkey_patching!
74
-
75
- # This setting enables warnings. It's recommended, but in some cases may
76
- # be too noisy due to issues in dependencies.
77
- config.warnings = true
78
-
79
- # Many RSpec users commonly either run the entire suite or an individual
80
- # file, and it's useful to allow more verbose output when running an
81
- # individual spec file.
82
- if config.files_to_run.one?
83
- # Use the documentation formatter for detailed output,
84
- # unless a formatter has already been configured
85
- # (e.g. via a command-line flag).
86
- config.default_formatter = 'doc'
87
- end
88
-
89
- # Print the 10 slowest examples and example groups at the
90
- # end of the spec run, to help surface which specs are running
91
- # particularly slow.
92
- config.profile_examples = 10
93
-
94
- # Run specs in random order to surface order dependencies. If you find an
95
- # order dependency and want to debug it, you can fix the order by providing
96
- # the seed, which is printed after each run.
97
- # --seed 1234
98
- config.order = :random
99
-
100
- # Seed global randomization in this process using the `--seed` CLI option.
101
- # Setting this allows you to use `--seed` to deterministically reproduce
102
- # test failures related to randomization by passing the same `--seed` value
103
- # as the one that triggered the failure.
104
- Kernel.srand config.seed
105
- =end
106
- end