pause 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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.0'
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,15 @@ 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
13
16
  def <=>(other)
14
- self.period_seconds <=> other.period_seconds
17
+ period_seconds <=> other.period_seconds
15
18
  end
16
19
  end
17
20
 
18
- class SetElement < Struct.new(:ts, :count)
21
+ SetElement = Struct.new(:ts, :count) do
19
22
  def <=>(other)
20
- self.ts <=> other.ts
23
+ ts <=> other.ts
21
24
  end
22
25
  end
23
26
 
@@ -27,17 +30,17 @@ module Pause
27
30
  end
28
31
 
29
32
  def adapter
30
- @adapter ||= config.sharded ?
31
- Pause::Redis::ShardedAdapter.new(config) :
32
- Pause::Redis::Adapter.new(config)
33
+ @adapter ||= if config.sharded
34
+ Pause::Redis::ShardedAdapter.new(config)
35
+ else
36
+ Pause::Redis::Adapter.new(config)
37
+ end
33
38
  end
34
39
 
35
- def adapter=(adapter)
36
- @adapter = adapter
37
- end
40
+ attr_writer :adapter
38
41
 
39
42
  def configure(&block)
40
- @configuration ||= Pause::Configuration.new.configure(&block)
43
+ @configure ||= Pause::Configuration.new.configure(&block)
41
44
  end
42
45
 
43
46
  def config(&block)
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
@@ -11,6 +11,10 @@ describe Pause::Action do
11
11
  end
12
12
 
13
13
  let(:resolution) { 10 }
14
+ let(:identifier) { '11112222' }
15
+ let(:action) { MyNotification.new(identifier) }
16
+ let(:other_identifier) { '8798734' }
17
+ let(:other_action) { MyNotification.new(other_identifier) }
14
18
  let(:history) { 60 }
15
19
  let(:configuration) { Pause::Configuration.new }
16
20
  let(:adapter) { Pause::Redis::Adapter.new(Pause.config) }
@@ -22,15 +26,9 @@ describe Pause::Action do
22
26
  allow(Pause).to receive(:adapter).and_return(adapter)
23
27
  end
24
28
 
25
- let(:identifier) { '11112222' }
26
- let(:action) { MyNotification.new(identifier) }
27
-
28
- let(:other_identifier) { '8798734' }
29
- let(:other_action) { MyNotification.new(other_identifier) }
30
-
31
29
  RSpec.shared_examples 'an action' do
32
30
  describe '#increment!' do
33
- it 'should increment' do
31
+ it 'increments' do
34
32
  time = Time.now
35
33
  Timecop.freeze time do
36
34
  expect(Pause.adapter).to receive(:increment).with(action.scope, identifier, time.to_i, 1)
@@ -40,7 +38,7 @@ describe Pause::Action do
40
38
  end
41
39
 
42
40
  describe '#ok?' do
43
- it 'should successfully return if the action is blocked or not' do
41
+ it 'successfullies return if the action is blocked or not' do
44
42
  time = Time.now
45
43
  Timecop.freeze time do
46
44
  4.times do
@@ -52,7 +50,7 @@ describe Pause::Action do
52
50
  end
53
51
  end
54
52
 
55
- it 'should successfully consider different period checks' do
53
+ it 'successfullies consider different period checks' do
56
54
  time = Time.parse('Sept 22, 11:34:00')
57
55
 
58
56
  Timecop.freeze time - 30 do
@@ -71,7 +69,7 @@ describe Pause::Action do
71
69
  end
72
70
  end
73
71
 
74
- it 'should return false and silently fail if redis is not available' do
72
+ it 'returns false and silently fail if redis is not available' do
75
73
  allow(Pause::Logger).to receive(:fatal)
76
74
  allow_any_instance_of(Redis).to receive(:zrange).and_raise Redis::CannotConnectError
77
75
  time = period_marker(resolution, Time.now.to_i)
@@ -86,7 +84,7 @@ describe Pause::Action do
86
84
  context 'action should not be rate limited' do
87
85
  it 'returns nil' do
88
86
  expect(adapter.rate_limited?(action.scope, action.identifier)).to be false
89
- expect(action.analyze).to be nil
87
+ expect(action.analyze).to be_nil
90
88
  end
91
89
  end
92
90
 
@@ -130,7 +128,7 @@ describe Pause::Action do
130
128
  end
131
129
 
132
130
  context 'actions under test' do
133
- ['123456', 'hello', 0, 999999].each do |id|
131
+ ['123456', 'hello', 0, 999_999].each do |id|
134
132
  let(:identifier) { id }
135
133
  let(:action) { MyNotification.new(identifier) }
136
134
  describe "action with identifier #{id}" do
@@ -151,38 +149,47 @@ describe Pause::Action do
151
149
  let(:bogus) { Struct.new(:name, :event).new }
152
150
 
153
151
  describe '#unless_rate_limited' do
154
- before do
155
- expect(bogus).to receive(:name).exactly(2).times
152
+ before { expect(bogus).to receive(:name).twice }
153
+
154
+ describe '#initialize' do
155
+ before { expect(bogus).to receive(:event).once }
156
+
157
+ it 'is able to use methods inside the #new block' do
158
+ b = bogus
159
+ CowRateLimited.new(identifier) do
160
+ unless_rate_limited { b.name } # this executes
161
+ unless_rate_limited { b.name } # this also executes
162
+ unless_rate_limited { b.name } # and this will be rate limited
163
+ if_rate_limited { b.event }
164
+ end
165
+ end
156
166
  end
157
- it 'should call through the block' do
167
+
168
+ it 'calls through the block' do
158
169
  action.unless_rate_limited { bogus.name }
159
170
  action.unless_rate_limited { bogus.name }
160
171
  result = action.unless_rate_limited { bogus.name }
161
- expect(result).to be_a_kind_of(::Pause::RateLimitedEvent)
172
+ expect(result).to be_a(Pause::RateLimitedEvent)
162
173
  end
163
- end
164
-
165
- describe '#unless_rate_limited' do
166
- before { expect(bogus).to receive(:name).exactly(2).times }
167
174
 
168
- it 'should call through the block' do
175
+ it 'calls through the block' do
169
176
  3.times { action.unless_rate_limited { bogus.name } }
170
177
  end
171
178
 
172
179
  describe '#if_rate_limited' do
173
180
  before { 2.times { action.unless_rate_limited { bogus.name } } }
174
181
 
175
- it 'it should not analyze during method call' do
182
+ it 'does not analyze during method call' do
176
183
  bogus.event = 1
177
184
  action.if_rate_limited { |event| bogus.event = event }
178
- expect(bogus.event).to be_a_kind_of(::Pause::RateLimitedEvent)
185
+ expect(bogus.event).to be_a(Pause::RateLimitedEvent)
179
186
  expect(bogus.event.identifier).to eq(identifier)
180
187
  end
181
188
 
182
- it 'should analyze if requested' do
189
+ it 'analyzes if requested' do
183
190
  action.unless_rate_limited { bogus.name }
184
191
  result = action.if_rate_limited { |event| bogus.event = event }
185
- expect(bogus.event).to be_a_kind_of(::Pause::RateLimitedEvent)
192
+ expect(bogus.event).to be_a(Pause::RateLimitedEvent)
186
193
  expect(result).to eq(bogus.event)
187
194
  end
188
195
  end
@@ -190,7 +197,7 @@ describe Pause::Action do
190
197
  end
191
198
 
192
199
  describe '.tracked_identifiers' do
193
- it 'should return all the identifiers tracked (but not blocked) so far' do
200
+ it 'returns all the identifiers tracked (but not blocked) so far' do
194
201
  action.increment!
195
202
  other_action.increment!
196
203
 
@@ -203,7 +210,7 @@ describe Pause::Action do
203
210
  end
204
211
 
205
212
  describe '.rate_limited_identifiers' do
206
- it 'should return all the identifiers blocked' do
213
+ it 'returns all the identifiers blocked' do
207
214
  action.increment!(100, Time.now.to_i)
208
215
  other_action.increment!(100, Time.now.to_i)
209
216
 
@@ -216,7 +223,7 @@ describe Pause::Action do
216
223
  end
217
224
 
218
225
  describe '.unblock_all' do
219
- it 'should unblock all the identifiers for a scope' do
226
+ it 'unblocks all the identifiers for a scope' do
220
227
  10.times { action.increment! }
221
228
  other_action.increment!
222
229
 
@@ -232,7 +239,6 @@ describe Pause::Action do
232
239
  expect(MyNotification.tracked_identifiers).to eq([other_action.identifier])
233
240
  end
234
241
  end
235
-
236
242
  end
237
243
 
238
244
  describe Pause::Action, '.check' do
@@ -250,22 +256,22 @@ describe Pause::Action, '.check' do
250
256
  check period_seconds: 50, block_ttl: 60, max_allowed: 100
251
257
  end
252
258
 
253
- it 'should define a period check on new instances' do
259
+ it 'defines a period check on new instances' do
254
260
  expect(ActionWithCheck.new('id').checks).to eq([
255
261
  Pause::PeriodCheck.new(100, 150, 200)
256
262
  ])
257
263
  end
258
264
 
259
- it 'should define a period check on new instances' do
265
+ it 'defines a period check on new instances' do
260
266
  expect(ActionWithMultipleChecks.new('id').checks).to \
261
267
  eq([
262
- Pause::PeriodCheck.new(100, 150, 200),
263
- Pause::PeriodCheck.new(200, 150, 200),
264
- Pause::PeriodCheck.new(300, 150, 200)
265
- ])
268
+ Pause::PeriodCheck.new(100, 150, 200),
269
+ Pause::PeriodCheck.new(200, 150, 200),
270
+ Pause::PeriodCheck.new(300, 150, 200)
271
+ ])
266
272
  end
267
273
 
268
- it 'should accept hash arguments' do
274
+ it 'accepts hash arguments' do
269
275
  expect(ActionWithHashChecks.new('id').checks).to eq([
270
276
  Pause::PeriodCheck.new(50, 100, 60)
271
277
  ])
@@ -278,7 +284,7 @@ describe Pause::Action, '.scope' do
278
284
  end
279
285
  end
280
286
 
281
- it 'should raise if scope is not defined' do
287
+ it 'raises if scope is not defined' do
282
288
  expect(MyApp::NoScope.new('1.2.3.4').scope).to eq 'myapp.noscope'
283
289
  end
284
290
 
@@ -286,7 +292,7 @@ describe Pause::Action, '.scope' do
286
292
  scope 'my:scope'
287
293
  end
288
294
 
289
- it 'should set scope on class' do
295
+ it 'sets scope on class' do
290
296
  expect(DefinedScopeAction.new('1.2.3.4').scope).to eq('my:scope')
291
297
  end
292
298
  end
@@ -309,26 +315,26 @@ describe Pause::Action, 'enabled/disabled states' do
309
315
  describe '#disable' do
310
316
  before do
311
317
  expect(action).to be_enabled
312
- expect(action).to_not be_disabled
318
+ expect(action).not_to be_disabled
313
319
  action.disable
314
320
  end
315
321
 
316
322
  it 'disables the action' do
317
323
  expect(action).to be_disabled
318
- expect(action).to_not be_enabled
324
+ expect(action).not_to be_enabled
319
325
  end
320
326
  end
321
327
 
322
328
  describe '#enable' do
323
329
  before do
324
330
  action.disable
325
- expect(action).to_not be_enabled
331
+ expect(action).not_to be_enabled
326
332
  action.enable
327
333
  end
328
334
 
329
335
  it 'enables the action' do
330
336
  expect(action).to be_enabled
331
- expect(action).to_not be_disabled
337
+ expect(action).not_to be_disabled
332
338
  end
333
339
  end
334
340
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'timecop'
3
5
 
@@ -11,20 +13,17 @@ describe Pause::Analyzer do
11
13
  end
12
14
 
13
15
  let(:resolution) { 10 }
16
+ let(:analyzer) { Pause.analyzer }
17
+ let(:action) { FollowPushNotification.new('1243123') }
14
18
  let(:history) { 60 }
15
19
  let(:configuration) { Pause::Configuration.new }
16
20
  let(:adapter) { Pause::Redis::Adapter.new(configuration) }
17
21
 
18
22
  before do
19
- allow(Pause).to receive(:config).and_return(configuration)
20
- allow(Pause.config).to receive(:resolution).and_return(resolution)
21
- allow(Pause.config).to receive(:history).and_return(history)
22
- allow(Pause).to receive(:adapter).and_return(adapter)
23
+ allow(Pause.config).to receive_messages(resolution: resolution, history: history)
24
+ allow(Pause).to receive_messages(config: configuration, adapter: adapter)
23
25
  end
24
26
 
25
- let(:analyzer) { Pause.analyzer }
26
- let(:action) { FollowPushNotification.new('1243123') }
27
-
28
27
  describe '#analyze' do
29
28
  it 'checks and blocks if max_allowed is reached' do
30
29
  time = Time.now
@@ -39,11 +38,11 @@ describe Pause::Analyzer do
39
38
  end
40
39
 
41
40
  describe '#check' do
42
- it 'should return nil if action is NOT blocked' do
43
- expect(analyzer.check(action)).to be nil
41
+ it 'returns nil if action is NOT blocked' do
42
+ expect(analyzer.check(action)).to be_nil
44
43
  end
45
44
 
46
- it 'should return blocked action if action is blocked' do
45
+ it 'returns blocked action if action is blocked' do
47
46
  Timecop.freeze Time.now do
48
47
  5.times do
49
48
  action.increment!
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Pause::Configuration, '#configure' do
6
+ subject { described_class.new }
4
7
 
5
- subject { Pause::Configuration.new }
6
-
7
- it 'should allow configuration via block' do
8
+ it 'allows configuration via block' do
8
9
  subject.configure do |c|
9
10
  c.redis_host = '128.23.12.8'
10
11
  c.redis_port = '2134'
@@ -24,7 +25,7 @@ describe Pause::Configuration, '#configure' do
24
25
  expect(subject.sharded).to be true
25
26
  end
26
27
 
27
- it 'should provide redis defaults' do
28
+ it 'provides redis defaults' do
28
29
  subject.configure do |config|
29
30
  # do nothing
30
31
  end
@@ -33,7 +34,7 @@ describe Pause::Configuration, '#configure' do
33
34
  expect(subject.redis_port).to eq(6379)
34
35
  expect(subject.redis_db).to eq('1')
35
36
  expect(subject.resolution).to eq(600) # 10 minutes
36
- expect(subject.history).to eq(86400) # one day
37
+ expect(subject.history).to eq(86_400) # one day
37
38
  expect(subject.sharded).to be false
38
39
  end
39
40
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Pause::Logger do
6
+ describe 'when accessed #puts' do
7
+ before do
8
+ expect($stdout).to receive(:puts).with('hello')
9
+ end
10
+
11
+ it 'calls through to puts without color' do
12
+ described_class.puts('hello')
13
+ end
14
+ end
15
+
16
+ describe 'when accessed via #fatal' do
17
+ before do
18
+ expect($stderr).to receive(:puts).with("\e[31mwhoops\e[0m")
19
+ end
20
+
21
+ it 'calls through to puts with color' do
22
+ described_class.fatal('whoops')
23
+ end
24
+ end
25
+ end