pause 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,11 +1,17 @@
1
- # Pause
2
1
 
3
- [![Gem Version](https://badge.fury.io/rb/pause.png)](http://badge.fury.io/rb/pause)
4
- [![Build Status](https://travis-ci.org/kigster/pause.svg?branch=master)](https://travis-ci.org/kigster/pause)
2
+ [![RSpec](https://github.com/kigster/pause/actions/workflows/rspec.yml/badge.svg?style=for-the-badge)](https://github.com/kigster/pause/actions/workflows/rspec.yml)
3
+ [![Rubocop](https://github.com/kigster/pause/actions/workflows/rubocop.yml/badge.svg?style=for-the-badge)](https://github.com/kigster/pause/actions/workflows/rubocop.yml)
5
4
 
6
- ## In a Nutshell
5
+ [![Gem Version](https://badge.fury.io/rb/pause@2x.png?icon=si%3Arubygems)](https://badge.fury.io/rb/pause)
7
6
 
8
- **Pause** is a fast and very flexible Redis-backed rate-limiter. You can use it to track events, with
7
+ [![Downloads](https://img.shields.io/gem/dt/pause.svg?style=for-the-badge&color=0AF)](https://rubygems.org/gems/pause)
8
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge&color=0AF)](https://opensource.org/licenses/MIT)
9
+
10
+ # Pause — A Redis-backed Rate Limiter
11
+
12
+ ## Overview
13
+
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
9
15
  rules around how often they are allowed to occur within configured time checks.
10
16
 
11
17
  Sample applications include:
@@ -49,17 +55,21 @@ Pause.configure do |config|
49
55
  config.redis_host = '127.0.0.1'
50
56
  config.redis_port = 6379
51
57
  config.redis_db = 1
52
-
53
- # aggregate all events into 10 minute blocks.
54
- # Larger blocks require less RAM and CPU, smaller blocks are more
55
- # computationally expensive.
56
- config.resolution = 600
57
-
58
- # discard all events older than 1 day
59
- config.history = 86400
58
+ config.resolution = 600
59
+ config.history = 7 * 86400 # discard events older than 7 days
60
60
  end
61
61
  ```
62
62
 
63
+ > NOTE: **resolution** is an setting that's key to understanding how Pause works. It represents the length of time during which similar events are aggregated into a Hash-like object, where the key is the identifier, and the value is the count within that period.
64
+ >
65
+ > Because of this,
66
+ >
67
+ > * _Larger resolution requires less RAM and CPU and are faster to compute_
68
+ > * _Smaller resolution is more computationally expensive, but provides higher granularity_.
69
+ >
70
+ > The resolution setting must set to the smallest rate-limit period across all of your checks. Below it is set to 10 minutes, meaning that you can use Pause to **rate limit any event to no more than N times within a period of 10 minutes or more.**
71
+
72
+
63
73
  #### Define Rate Limited "Action"
64
74
 
65
75
  Next we must define the rate limited action based on the specification above. This is how easy it is:
@@ -69,13 +79,22 @@ module MyApp
69
79
  class UserNotificationLimiter < ::Pause::Action
70
80
  # this is a redis key namespace added to all data in this action
71
81
  scope 'un'
72
- check period_seconds: 120, max_allowed: 1
73
- check period_seconds: 86400, max_allowed: 3
74
- check period_seconds: 7 * 86400, max_allowed: 7
82
+
83
+ check period_seconds: 120,
84
+ max_allowed: 1,
85
+ block_ttl: 240
86
+
87
+ check period_seconds: 86400,
88
+ max_allowed: 3
89
+
90
+ check period_seconds: 7 * 86400,
91
+ max_allowed: 7
75
92
  end
76
93
  end
77
94
  ```
78
95
 
96
+ > NOTE: for each check, `block_ttl` defaults to `period_seconds`, and represents the duration of time the action will consider itself as "rate limited" after a particular check reaches the limit. Note, that all actions will automatically leave the "rate limited" state after `block_ttl` seconds have passed.
97
+
79
98
  #### Perform operation, but only if the user is not rate-limited
80
99
 
81
100
  Now we simply instantiate this limiter by passing user ID (any unique identifier works). We can then ask the limiter, `ok?` or `rate_limited?`, or we can use two convenient methods that only execute enclosed block if the described condition is satisfied:
@@ -83,17 +102,18 @@ Now we simply instantiate this limiter by passing user ID (any unique identifier
83
102
  ```ruby
84
103
  class NotificationsWorker
85
104
  def perform(user_id)
86
- limiter = MyApp::UserNotificationLimiter.new(user_id)
87
-
88
- limiter.unless_rate_limited do
89
- user = User.find(user_id)
90
- user.send_push_notification!
91
- end
92
-
93
- # You can also do something in case the user is rate limited:
94
- limiter.if_rate_limited do |rate_limit_event|
95
- Rails.logger.info("user #{user.id} has exceeded rate limit: #{rate_limit_event}")
96
- end
105
+ MyApp::UserNotificationLimiter.new(user_id) do
106
+ unless_rate_limited do
107
+ # this block ONLY runs if rate limit is not reached
108
+ user = User.find(user_id)
109
+ PushNotifications.new(user).send_push_notification!
110
+ end
111
+
112
+ if_rate_limited do |rate_limit_event|
113
+ # this block ONLY runs if the action has reached it's rate limit.
114
+ Rails.logger.info("user #{user.id} has exceeded rate limit: #{rate_limit_event}")
115
+ end
116
+ end
97
117
  end
98
118
  end
99
119
  ```
@@ -119,12 +139,17 @@ Or install it yourself as:
119
139
 
120
140
  ### Configuration
121
141
 
122
- 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.
123
143
 
124
- * resolution - The time resolution (in seconds) defining the minimum period into which action counts are
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
125
149
  aggregated. This defines the size of the persistent store. The higher the number, the less data needs
126
150
  to be persisted in Redis.
127
- * history - The maximum amount of time (in seconds) that data is persisted
151
+
152
+ * **history** - The maximum amount of time (in seconds) that data is persisted.
128
153
 
129
154
  ```ruby
130
155
  Pause.configure do |config|
@@ -136,12 +161,10 @@ Pause.configure do |config|
136
161
  end
137
162
  ```
138
163
 
164
+
139
165
  ### Actions
140
166
 
141
- Define local actions for your application. Actions define a scope by
142
- which they are identified in the persistent store (aka "namespace"), and a set of checks. Checks define various
143
- thresholds (`max_allowed`) against periods of time (`period_seconds`). When a threshold it triggered,
144
- 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.
145
168
 
146
169
  #### Checks
147
170
 
@@ -185,6 +208,7 @@ When an event occurs, you increment an instance of your action, optionally with
185
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:
186
209
 
187
210
  ```ruby
211
+ # app/controllers/follows_controller.rb
188
212
  class FollowsController < ApplicationController
189
213
  def create
190
214
  action = FollowAction.new(user.id)
@@ -196,6 +220,7 @@ class FollowsController < ApplicationController
196
220
  end
197
221
  end
198
222
 
223
+ # app/controlers/other_controller.rb
199
224
  class OtherController < ApplicationController
200
225
  def index
201
226
  action = OtherAction.new(params[:thing])d
@@ -217,11 +242,16 @@ while true
217
242
  action.increment!
218
243
 
219
244
  rate_limit_event = action.analyze
220
- if rate_limit_event
221
- puts rate_limit_event.identifier # which key got rate limited ("thing")
222
- puts rate_limit_event.sum # total count that triggered a rate limit
223
- puts rate_limit_event.timestamp # timestamp when rate limiting occurred
224
- puts rate_limit_event.period_check # period check object, that triggered this rate limiting event
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
225
255
  else
226
256
  # not rate-limited, same as action.ok?
227
257
  end
@@ -301,21 +331,19 @@ Pause.configure do |config|
301
331
  end
302
332
  ```
303
333
 
304
- With this configuration, any Pause operation that we know is not supported by Twemproxy will raise
305
- `Pause::Redis::OperationNotSupported`. For instance, when sharding we are unable to get a list of all
306
- tracked identifiers.
334
+ With this configuration, any Pause operation that we know is not supported by Twemproxy will raise `Pause::Redis::OperationNotSupported`. For instance, when sharding we are unable to get a list of all tracked identifiers.
307
335
 
308
336
  The action block list is implemented as a sorted set, so it should still be usable when sharding.
309
337
 
310
338
  ## Testing
311
339
 
312
- By default, `fakeredis` gem is used to emulate Redis in development. However, the same test-suite should be able to run against a real redis — however, be aware that it will flush the current db during spec run. In order to run specs against real redis, make sure you have Redis running locally on the default port, and that you are able to connect to it using `redis-cli`.
340
+ By default, `fakeredis` gem is used to emulate Redis in development.
313
341
 
314
- Please note that Travis suite, as well as the default rake task, run both.
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`.
315
343
 
316
- ### Unit Testing with Fakeredis
344
+ ### Unit Testing with FakeRedis
317
345
 
318
- Fakeredis is the default, and is also run whenever `bundle exec rspec` is executed, or `rake spec` task invoked.
346
+ The gem `fakeredis` is the default for testing, and is also run whenever `bundle exec rspec` is executed, or `rake spec` task invoked.
319
347
 
320
348
  ```bash
321
349
  bundle exec rake spec:unit
@@ -327,6 +355,16 @@ bundle exec rake spec:unit
327
355
  bundle exec rake spec:integration
328
356
  ```
329
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
+
330
368
  ## Contributing
331
369
 
332
370
  Want to make it better? Cool. Here's how:
@@ -339,8 +377,16 @@ Want to make it better? Cool. Here's how:
339
377
 
340
378
  ## Authors
341
379
 
342
- This gem was written by Eric Saxby, Atasay Gokkaya and Konstantin Gredeskoul at Wanelo, Inc.
380
+ * This gem was donated to Open Source by [Wanelo, Inc](https://www.crunchbase.com/organization/wanelo)
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/)
343
389
 
344
- Please see the LICENSE.txt file for further details.
390
+ Please see the [LICENSE.txt](LICENSE.txt) file for further details.
345
391
 
346
392
 
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
  require 'yard'
4
6
 
5
7
  RSpec::Core::RakeTask.new(:spec)
6
8
 
7
- task :default => %w(spec:unit spec:integration)
9
+ task default: %w[spec:unit spec:integration]
8
10
 
9
11
  namespace :spec do
10
12
  desc 'Run specs using fakeredis'
@@ -25,23 +27,22 @@ def shell(*args)
25
27
  end
26
28
 
27
29
  task :clean do
28
- shell('rm -rf pkg/ tmp/ coverage/ doc/ ' )
30
+ shell('rm -rf pkg/ tmp/ coverage/ doc/ ')
29
31
  end
30
32
 
31
- task :gem => [:build] do
33
+ task gem: [:build] do
32
34
  shell('gem install pkg/*')
33
35
  end
34
36
 
35
- task :permissions => [ :clean ] do
37
+ task permissions: [:clean] do
36
38
  shell('chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*')
37
- shell("find . -type d -exec chmod o+x,g+x {} \\;")
39
+ shell('find . -type d -exec chmod o+x,g+x {} \\;')
38
40
  end
39
41
 
40
- task :build => :permissions
42
+ task build: :permissions
41
43
 
42
44
  YARD::Rake::YardocTask.new(:doc) do |t|
43
- t.files = %w(lib/**/*.rb exe/*.rb - README.md LICENSE.txt)
44
- t.options.unshift('--title','"Pause - Redis-backed Rate Limiter"')
45
- t.after = ->() { exec('open doc/index.html') }
45
+ t.files = %w[lib/**/*.rb exe/*.rb - README.md LICENSE.txt]
46
+ t.options.unshift('--title', '"Pause - Redis-backed Rate Limiter"')
47
+ t.after = -> { exec('open doc/index.html') }
46
48
  end
47
-
data/bin/spec ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env bash
2
+
3
+ retry-errors() {
4
+ sleep 1
5
+ bundle exec rspec --only-failures
6
+ }
7
+
8
+ specs() {
9
+ bundle exec rspec && \
10
+ PAUSE_REAL_REDIS=true bundle exec rspec
11
+ }
12
+
13
+ specs || retry-errors
14
+
data/lib/pause/action.rb CHANGED
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pause
2
4
  class Action
3
5
  attr_accessor :identifier
4
6
 
5
- def initialize(identifier)
6
- @identifier = identifier
7
+ def initialize(identifier, &block)
8
+ @identifier = identifier
7
9
  self.class.checks ||= []
10
+ instance_exec(&block) if block
8
11
  end
9
12
 
10
13
  def scope
@@ -22,9 +25,8 @@ module Pause
22
25
  # scope "my:scope"
23
26
  # end
24
27
  #
25
- @scope = klass.name.downcase.gsub(/::/, '.')
28
+ @scope = klass.name.downcase.gsub('::', '.')
26
29
  class << self
27
-
28
30
  # @param [String] args
29
31
  def scope(*args)
30
32
  @scope = args.first if args && args.size == 1
@@ -115,7 +117,7 @@ module Pause
115
117
  end
116
118
  end
117
119
 
118
- def if_rate_limited(&_block)
120
+ def if_rate_limited(&)
119
121
  check_result = analyze(recalculate: true)
120
122
  yield(check_result) unless check_result.nil?
121
123
  end
@@ -156,6 +158,5 @@ module Pause
156
158
  def adapter
157
159
  self.class.adapter
158
160
  end
159
-
160
161
  end
161
162
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pause/helper/timing'
2
4
 
3
5
  module Pause
@@ -12,12 +14,14 @@ module Pause
12
14
  # @return [Pause::RateLimitedEvent] the action was blocked as a result of this check
13
15
  def check(action, recalculate: false)
14
16
  return false if adapter.rate_limited?(action.scope, action.identifier) && !recalculate
17
+
15
18
  timestamp = period_marker(Pause.config.resolution, Time.now.to_i)
16
19
  set = adapter.key_history(action.scope, action.identifier)
17
20
  action.checks.each do |period_check|
18
21
  start_time = timestamp - period_check.period_seconds
19
22
  set.reverse.inject(0) do |sum, element|
20
23
  break if element.ts < start_time
24
+
21
25
  sum += element.count
22
26
  if sum >= period_check.max_allowed
23
27
  adapter.rate_limit!(action.scope, action.identifier, period_check.block_ttl)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pause
2
4
  class Configuration
3
5
  attr_writer :redis_host, :redis_port, :redis_db, :resolution, :history, :sharded
@@ -24,7 +26,7 @@ module Pause
24
26
  end
25
27
 
26
28
  def history
27
- (@history || 86400).to_i
29
+ (@history || 86_400).to_i
28
30
  end
29
31
 
30
32
  def sharded
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pause
2
4
  module Helper
3
5
  module Timing
data/lib/pause/logger.rb CHANGED
@@ -1,11 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pause
4
+ # @description Logger class for Pause
2
5
  class Logger
3
- def self.puts message
4
- STDOUT.puts message
5
- end
6
+ class << self
7
+ def puts(message)
8
+ $stdout.puts message
9
+ end
6
10
 
7
- def self.fatal message
8
- STDERR.puts message
11
+ def fatal(message)
12
+ # rubocop: disable Style/StderrPuts
13
+ $stderr.puts message.red
14
+ # rubocop: enable Style/StderrPuts
15
+ end
9
16
  end
10
17
  end
11
18
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pause
2
4
  class RateLimitedEvent
3
5
  attr_accessor :action, :identifier, :period_check, :sum, :timestamp
@@ -9,6 +11,5 @@ module Pause
9
11
  @sum = sum
10
12
  @timestamp = timestamp
11
13
  end
12
-
13
14
  end
14
15
  end
@@ -1,19 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pause/helper/timing'
2
4
 
3
5
  module Pause
4
6
  module Redis
5
-
6
7
  # This class encapsulates Redis operations used by Pause
7
8
  class Adapter
8
9
  class << self
9
10
  def redis
10
- @redis_conn ||= ::Redis.new(redis_connection_opts)
11
+ @redis ||= ::Redis.new(redis_connection_opts)
11
12
  end
12
13
 
13
14
  def redis_connection_opts
14
15
  { host: Pause.config.redis_host,
15
16
  port: Pause.config.redis_port,
16
- db: Pause.config.redis_db }
17
+ db: Pause.config.redis_db }
17
18
  end
18
19
  end
19
20
 
@@ -72,6 +73,7 @@ module Pause
72
73
  # @return count [Integer] the number of items deleted
73
74
  def delete_rate_limited_keys(scope)
74
75
  return 0 unless rate_limited_keys(scope).any?
76
+
75
77
  delete_tracking_keys(scope, rate_limited_keys(scope))
76
78
  redis.zremrangebyscore(rate_limited_list(scope), '-inf', '+inf').tap do |_count|
77
79
  redis.del rate_limited_list(scope)
@@ -84,7 +86,7 @@ module Pause
84
86
  end
85
87
 
86
88
  def disable(scope)
87
- redis.set("internal:|#{scope}|:disabled", "1")
89
+ redis.set("internal:|#{scope}|:disabled", '1')
88
90
  end
89
91
 
90
92
  def enable(scope)
@@ -110,11 +112,11 @@ module Pause
110
112
  end
111
113
 
112
114
  def truncate_set_for(k)
113
- if redis.zcard(k) > time_blocks_to_keep
114
- list = extract_set_elements(k)
115
- to_remove = list.slice(0, (list.size - time_blocks_to_keep)).map(&:ts)
116
- redis.zrem(k, to_remove) if k && to_remove && to_remove.size > 0
117
- end
115
+ return unless redis.zcard(k) > time_blocks_to_keep
116
+
117
+ list = extract_set_elements(k)
118
+ to_remove = list.slice(0, (list.size - time_blocks_to_keep)).map(&:ts)
119
+ redis.zrem(k, to_remove) if k && to_remove&.size&.positive?
118
120
  end
119
121
 
120
122
  def delete_tracking_keys(scope, ids)
@@ -137,7 +139,7 @@ module Pause
137
139
 
138
140
  def keys(key_scope)
139
141
  redis.keys("#{key_scope}:*").map do |key|
140
- key.gsub(/^#{key_scope}:/, "").tr('|', '')
142
+ key.gsub(/^#{key_scope}:/, '').tr('|', '')
141
143
  end
142
144
  end
143
145
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pause
2
4
  module Redis
3
5
  class OperationNotSupported < StandardError
@@ -7,7 +9,6 @@ module Pause
7
9
  # Operations that are not possible when data is sharded
8
10
  # raise an error.
9
11
  class ShardedAdapter < Adapter
10
-
11
12
  # Overrides real multi which is not possible when sharded.
12
13
  def with_multi
13
14
  yield(redis) if block_given?
@@ -23,7 +24,7 @@ module Pause
23
24
  private
24
25
 
25
26
  def keys(_key_scope)
26
- raise OperationNotSupported.new('Can not be executed when Pause is configured in sharded mode')
27
+ raise OperationNotSupported, 'Can not be executed when Pause is configured in sharded mode'
27
28
  end
28
29
  end
29
30
  end
data/lib/pause/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pause
2
- VERSION = '0.4.0'
4
+ VERSION = '0.5.1'
3
5
  end
data/lib/pause.rb CHANGED
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'redis'
4
+ require 'colored2'
2
5
  require 'pause/version'
3
6
  require 'pause/configuration'
4
7
  require 'pause/action'
@@ -9,15 +12,23 @@ require 'pause/redis/sharded_adapter'
9
12
  require 'pause/rate_limited_event'
10
13
 
11
14
  module Pause
12
- class PeriodCheck < Struct.new(:period_seconds, :max_allowed, :block_ttl)
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
+
13
24
  def <=>(other)
14
- self.period_seconds <=> other.period_seconds
25
+ period_seconds <=> other.period_seconds
15
26
  end
16
27
  end
17
28
 
18
- class SetElement < Struct.new(:ts, :count)
29
+ SetElement = Struct.new(:ts, :count) do
19
30
  def <=>(other)
20
- self.ts <=> other.ts
31
+ ts <=> other.ts
21
32
  end
22
33
  end
23
34
 
@@ -27,21 +38,21 @@ module Pause
27
38
  end
28
39
 
29
40
  def adapter
30
- @adapter ||= config.sharded ?
31
- Pause::Redis::ShardedAdapter.new(config) :
32
- Pause::Redis::Adapter.new(config)
41
+ @adapter ||= if config.sharded
42
+ Pause::Redis::ShardedAdapter.new(config)
43
+ else
44
+ Pause::Redis::Adapter.new(config)
45
+ end
33
46
  end
34
47
 
35
- def adapter=(adapter)
36
- @adapter = adapter
37
- end
48
+ attr_writer :adapter
38
49
 
39
- def configure(&block)
40
- @configuration ||= Pause::Configuration.new.configure(&block)
50
+ def configure(&)
51
+ @configure ||= Pause::Configuration.new.configure(&)
41
52
  end
42
53
 
43
- def config(&block)
44
- configure(&block)
54
+ def config(&)
55
+ configure(&)
45
56
  end
46
57
  end
47
58
  end
data/pause.gemspec CHANGED
@@ -1,30 +1,27 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ lib = File.expand_path('lib', __dir__)
3
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
6
  require 'pause/version'
5
7
 
6
8
  Gem::Specification.new do |gem|
7
9
  gem.name = 'pause'
8
10
  gem.version = Pause::VERSION
9
- gem.authors = ['Atasay Gokkaya', 'Paul Henry', 'Eric Saxby', 'Konstantin Gredeskoul']
10
- gem.email = %w(atasay@wanelo.com paul@wanelo.com sax@ericsaxby.com kigster@gmail.com)
11
- gem.summary = %q(Fast, scalable, and flexible real time rate limiting library for distributed Ruby environments backed by Redis.)
12
- gem.description = %q(This gem provides highly flexible and easy to use interface to define rate limit checks, register events as they come, and verify if the rate limit is reached. Multiple checks for the same metric are easily supported. This gem is used at very high scale on several popular web sites.)
11
+ gem.authors = ['Konstantin Gredeskoul', 'Atasay Gokkaya', 'Paul Henry', 'Eric Saxby']
12
+ gem.email = %w[kigster@gmail.com atasay@wanelo.com paul@wanelo.com sax@ericsaxby.com]
13
+ gem.summary = 'Fast, scalable, and flexible real time rate limiting library for distributed Ruby environments backed by Redis.'
14
+ gem.description = 'This gem provides highly flexible and easy to use interface to define rate limit checks, register events as they come, and verify if the rate limit is reached. Multiple checks for the same metric are easily supported. This gem is used at very high scale on several popular web sites.'
13
15
  gem.homepage = 'https://github.com/kigster/pause'
14
16
 
15
- gem.files = `git ls-files`.split($/)
16
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
18
19
  gem.require_paths = ['lib']
19
20
 
21
+ gem.add_dependency 'colored2'
20
22
  gem.add_dependency 'redis'
21
- gem.add_dependency 'hiredis'
22
23
 
23
- gem.add_development_dependency 'simplecov'
24
- gem.add_development_dependency 'yard'
25
- gem.add_development_dependency 'rspec'
26
- gem.add_development_dependency 'fakeredis'
27
- gem.add_development_dependency 'guard-rspec'
28
- gem.add_development_dependency 'timecop'
29
- gem.add_development_dependency 'rake'
24
+ # optional
25
+ # gem.add_dependency 'hiredis'
26
+ gem.metadata['rubygems_mfa_required'] = 'true'
30
27
  end