berater 0.5.0 → 0.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa5d51d6eea9d4691e58e8dd7065a1d596a57abdac1227fa19bd22cd2f42c15d
4
- data.tar.gz: 55ac15bf6f2f295c525fdea57b3af9b518935a12dd4b644e3c0c9c085429ca50
3
+ metadata.gz: 87d3bdb61a68a8b69dd7cb285dafb174a403acf19ff7586f90ed8b7cb50b7f76
4
+ data.tar.gz: fff71dd4d8ae28baf7c504efdc27df71f03219b32c1a959dddfd3d035845d058
5
5
  SHA512:
6
- metadata.gz: 52df5781f9c37c7f1c9dde09e579c8da1f2d62f405835b771c6087cda8af046018e85a88629f7c5d3a2be9aafd2680e348d1b2f91bc63fe456e6e51ec5d0fd02
7
- data.tar.gz: '0928cb6f2e8e07810dec96f5af302ff9f62d519b9f19ef6f28acb5621569f13edb6d784cbd7084b738df8dfc232ace26e3ce0c6c4a7f2ac3e4391c97789bafae'
6
+ metadata.gz: c9fb5c0c7ec100d2f50a3b6c6cfa449af052c1b78cff174b545e179c5fcc1324c2a5faafc90e7f7ec64ce200e47571d51b02eb56398d716c487abc7731c0cece
7
+ data.tar.gz: 148c566413d6593f229143e2871116b45947eae5b101d8bb2f59ce3bdfe89ed4ee1b7aaedfcaedbc80eef8a25a14fab83ad5e540172c41f31f27d7f296161235
@@ -14,7 +14,7 @@ module Berater
14
14
  private def timeout=(timeout)
15
15
  @timeout = timeout
16
16
  timeout = 0 if timeout == Float::INFINITY
17
- @timeout_usec = Berater::Utils.to_usec(timeout)
17
+ @timeout_msec = Berater::Utils.to_msec(timeout)
18
18
  end
19
19
 
20
20
  LUA_SCRIPT = Berater::LuaScript(<<~LUA
@@ -65,24 +65,29 @@ module Berater
65
65
 
66
66
  def limit(capacity: nil, cost: 1, &block)
67
67
  capacity ||= @capacity
68
- # cost is Integer >= 0
69
68
 
70
- # timestamp in microseconds
71
- ts = (Time.now.to_f * 10**6).to_i
69
+ # since fractional cost is not supported, capacity behaves like int
70
+ capacity = capacity.to_i
71
+
72
+ unless cost.is_a?(Integer) && cost >= 0
73
+ raise ArgumentError, "invalid cost: #{cost}"
74
+ end
75
+
76
+ # timestamp in milliseconds
77
+ ts = (Time.now.to_f * 10**3).to_i
72
78
 
73
79
  count, *lock_ids = LUA_SCRIPT.eval(
74
80
  redis,
75
81
  [ cache_key(key), cache_key('lock_id') ],
76
- [ capacity, ts, @timeout_usec, cost ]
82
+ [ capacity, ts, @timeout_msec, cost ]
77
83
  )
78
84
 
79
85
  raise Incapacitated if lock_ids.empty?
80
86
 
81
- if cost == 0
82
- lock = Lock.new(self, nil, count)
83
- else
84
- lock = Lock.new(self, lock_ids[0], count, -> { release(lock_ids) })
87
+ release_fn = if cost > 0
88
+ proc { release(lock_ids) }
85
89
  end
90
+ lock = Lock.new(capacity, count, release_fn)
86
91
 
87
92
  yield_lock(lock, &block)
88
93
  end
@@ -47,11 +47,15 @@ module Berater
47
47
  end
48
48
 
49
49
  def capacity=(capacity)
50
- unless capacity.is_a?(Integer) || capacity == Float::INFINITY
51
- raise ArgumentError, "expected Integer, found #{capacity.class}"
50
+ unless capacity.is_a?(Numeric)
51
+ raise ArgumentError, "expected Numeric, found #{capacity.class}"
52
52
  end
53
53
 
54
- raise ArgumentError, "capacity must be >= 0" unless capacity >= 0
54
+ if capacity == Float::INFINITY
55
+ raise ArgumentError, 'infinite capacity not supported, use Unlimiter'
56
+ end
57
+
58
+ raise ArgumentError, 'capacity must be >= 0' unless capacity >= 0
55
59
 
56
60
  @capacity = capacity
57
61
  end
data/lib/berater/lock.rb CHANGED
@@ -1,11 +1,10 @@
1
1
  module Berater
2
2
  class Lock
3
3
 
4
- attr_reader :limiter, :id, :contention
4
+ attr_reader :capacity, :contention
5
5
 
6
- def initialize(limiter, id, contention, release_fn = nil)
7
- @limiter = limiter
8
- @id = id
6
+ def initialize(capacity, contention, release_fn = nil)
7
+ @capacity = capacity
9
8
  @contention = contention
10
9
  @locked_at = Time.now
11
10
  @release_fn = release_fn
@@ -7,12 +7,16 @@ module Berater
7
7
 
8
8
  def initialize(key, capacity, interval, **opts)
9
9
  self.interval = interval
10
- super(key, capacity, @interval_usec, **opts)
10
+ super(key, capacity, @interval_msec, **opts)
11
11
  end
12
12
 
13
13
  private def interval=(interval)
14
14
  @interval = interval
15
- @interval_usec = Berater::Utils.to_usec(interval)
15
+ @interval_msec = Berater::Utils.to_msec(interval)
16
+
17
+ unless @interval_msec > 0
18
+ raise ArgumentError, 'interval must be > 0'
19
+ end
16
20
  end
17
21
 
18
22
  LUA_SCRIPT = Berater::LuaScript(<<~LUA
@@ -20,11 +24,11 @@ module Berater
20
24
  local ts_key = KEYS[2]
21
25
  local ts = tonumber(ARGV[1])
22
26
  local capacity = tonumber(ARGV[2])
23
- local interval_usec = tonumber(ARGV[3])
27
+ local interval_msec = tonumber(ARGV[3])
24
28
  local cost = tonumber(ARGV[4])
25
29
  local count = 0
26
30
  local allowed
27
- local usec_per_drip = interval_usec / capacity
31
+ local msec_per_drip = interval_msec / capacity
28
32
 
29
33
  -- timestamp of last update
30
34
  local last_ts = tonumber(redis.call('GET', ts_key))
@@ -33,7 +37,7 @@ module Berater
33
37
  count = tonumber(redis.call('GET', key)) or 0
34
38
 
35
39
  -- adjust for time passing
36
- local drips = math.floor((ts - last_ts) / usec_per_drip)
40
+ local drips = math.floor((ts - last_ts) / msec_per_drip)
37
41
  count = math.max(0, count - drips)
38
42
  end
39
43
 
@@ -47,7 +51,7 @@ module Berater
47
51
  count = count + cost
48
52
 
49
53
  -- time for bucket to empty, in milliseconds
50
- local ttl = math.ceil((count * usec_per_drip) / 1000)
54
+ local ttl = math.ceil(count * msec_per_drip)
51
55
 
52
56
  -- update count and last_ts, with expirations
53
57
  redis.call('SET', key, count, 'PX', ttl)
@@ -62,18 +66,18 @@ module Berater
62
66
  def limit(capacity: nil, cost: 1, &block)
63
67
  capacity ||= @capacity
64
68
 
65
- # timestamp in microseconds
66
- ts = (Time.now.to_f * 10**6).to_i
69
+ # timestamp in milliseconds
70
+ ts = (Time.now.to_f * 10**3).to_i
67
71
 
68
72
  count, allowed = LUA_SCRIPT.eval(
69
73
  redis,
70
74
  [ cache_key(key), cache_key("#{key}-ts") ],
71
- [ ts, capacity, @interval_usec, cost ]
75
+ [ ts, capacity, @interval_msec, cost ]
72
76
  )
73
77
 
74
78
  raise Overrated unless allowed
75
79
 
76
- lock = Lock.new(self, ts, count)
80
+ lock = Lock.new(capacity, count)
77
81
  yield_lock(lock, &block)
78
82
  end
79
83
 
data/lib/berater/rspec.rb CHANGED
@@ -4,7 +4,7 @@ require 'berater/test_mode'
4
4
  require 'rspec'
5
5
 
6
6
  RSpec.configure do |config|
7
- config.include(BeraterMatchers)
7
+ config.include(Berater::Matchers)
8
8
 
9
9
  config.after do
10
10
  Berater.expunge rescue nil
@@ -1,62 +1,83 @@
1
- module BeraterMatchers
2
- class Overloaded
3
- def initialize(type)
4
- @type = type
5
- end
1
+ module Berater
2
+ module Matchers
3
+ class Overloaded
4
+ def initialize(type)
5
+ @type = type
6
+ end
6
7
 
7
- def supports_block_expectations?
8
- true
9
- end
8
+ def supports_block_expectations?
9
+ true
10
+ end
11
+
12
+ def matches?(obj)
13
+ begin
14
+ case obj
15
+ when Proc
16
+ # eg. expect { ... }.to be_overrated
17
+ res = obj.call
10
18
 
11
- def matches?(obj)
12
- begin
13
- case obj
14
- when Proc
15
- # eg. expect { ... }.to be_overrated
16
- res = obj.call
17
-
18
- if res.is_a? Berater::Limiter
19
- # eg. expect { Berater.new(...) }.to be_overloaded
20
- res.overloaded?
21
- else
22
- # eg. expect { Berater(...) }.to be_overloaded
23
- # eg. expect { limiter.limit }.to be_overloaded
24
- false
19
+ if res.is_a? Berater::Limiter
20
+ # eg. expect { Berater.new(...) }.to be_overloaded
21
+ @limiter = res
22
+ res.overloaded?
23
+ else
24
+ # eg. expect { Berater(...) }.to be_overloaded
25
+ # eg. expect { limiter.limit }.to be_overloaded
26
+ false
27
+ end
28
+ when Berater::Limiter
29
+ # eg. expect(Berater.new(...)).to be_overloaded
30
+ @limiter = obj
31
+ obj.overloaded?
25
32
  end
26
- when Berater::Limiter
27
- # eg. expect(Berater.new(...)).to be_overloaded
28
- obj.overloaded?
33
+ rescue @type
34
+ true
29
35
  end
30
- rescue @type
31
- true
32
36
  end
33
- end
34
37
 
35
- # def description
36
- # it { expect { Berater.new(:inhibitor) }.not_to be_overrated }
38
+ def description
39
+ if @limiter
40
+ "be #{verb}"
41
+ else
42
+ "raise #{@type}"
43
+ end
44
+ end
37
45
 
38
- def failure_message
39
- "expected #{@type} to be raised"
40
- end
46
+ def failure_message
47
+ if @limiter
48
+ "expected to be #{verb}"
49
+ else
50
+ "expected #{@type} to be raised"
51
+ end
52
+ end
41
53
 
42
- def failure_message_when_negated
43
- "did not expect #{@type} to be raised"
54
+ def failure_message_when_negated
55
+ if @limiter
56
+ "expected not to be #{verb}"
57
+ else
58
+ "did not expect #{@type} to be raised"
59
+ end
60
+ end
61
+
62
+ private def verb
63
+ @type.to_s.split('::')[-1].downcase
64
+ end
44
65
  end
45
- end
46
66
 
47
- def be_overloaded
48
- Overloaded.new(Berater::Overloaded)
49
- end
67
+ def be_overloaded
68
+ Overloaded.new(Berater::Overloaded)
69
+ end
50
70
 
51
- def be_overrated
52
- Overloaded.new(Berater::RateLimiter::Overrated)
53
- end
71
+ def be_overrated
72
+ Overloaded.new(Berater::RateLimiter::Overrated)
73
+ end
54
74
 
55
- def be_incapacitated
56
- Overloaded.new(Berater::ConcurrencyLimiter::Incapacitated)
57
- end
75
+ def be_incapacitated
76
+ Overloaded.new(Berater::ConcurrencyLimiter::Incapacitated)
77
+ end
58
78
 
59
- def be_inhibited
60
- Overloaded.new(Berater::Inhibitor::Inhibited)
79
+ def be_inhibited
80
+ Overloaded.new(Berater::Inhibitor::Inhibited)
81
+ end
61
82
  end
62
83
  end
@@ -6,12 +6,16 @@ module Berater
6
6
  end
7
7
 
8
8
  def limit(**opts, &block)
9
- yield_lock(Lock.new(self, Float::INFINITY, 0), &block)
9
+ yield_lock(Lock.new(Float::INFINITY, 0), &block)
10
10
  end
11
11
 
12
12
  def overloaded?
13
13
  false
14
14
  end
15
15
 
16
+ protected def capacity=(*)
17
+ @capacity = Float::INFINITY
18
+ end
19
+
16
20
  end
17
21
  end
data/lib/berater/utils.rb CHANGED
@@ -3,12 +3,12 @@ module Berater
3
3
  extend self
4
4
 
5
5
  refine Object do
6
- def to_usec
7
- Berater::Utils.to_usec(self)
6
+ def to_msec
7
+ Berater::Utils.to_msec(self)
8
8
  end
9
9
  end
10
10
 
11
- def to_usec(val)
11
+ def to_msec(val)
12
12
  res = val
13
13
 
14
14
  if val.is_a? String
@@ -39,7 +39,7 @@ module Berater
39
39
  raise ArgumentError, "infinite values not allowed"
40
40
  end
41
41
 
42
- (res * 10**6).to_i
42
+ (res * 10**3).to_i
43
43
  end
44
44
 
45
45
  end
@@ -1,3 +1,3 @@
1
1
  module Berater
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  end
@@ -23,6 +23,7 @@ describe Berater::ConcurrencyLimiter do
23
23
 
24
24
  it { expect_capacity(0) }
25
25
  it { expect_capacity(1) }
26
+ it { expect_capacity(1.5) }
26
27
  it { expect_capacity(10_000) }
27
28
 
28
29
  context 'with erroneous values' do
@@ -32,7 +33,6 @@ describe Berater::ConcurrencyLimiter do
32
33
  end.to raise_error ArgumentError
33
34
  end
34
35
 
35
- it { expect_bad_capacity(0.5) }
36
36
  it { expect_bad_capacity(-1) }
37
37
  it { expect_bad_capacity('1') }
38
38
  it { expect_bad_capacity(:one) }
@@ -42,16 +42,16 @@ describe Berater::ConcurrencyLimiter do
42
42
  describe '#timeout' do
43
43
  # see spec/utils_spec.rb
44
44
 
45
- it 'saves the interval in original and microsecond format' do
45
+ it 'saves the interval in original and millisecond format' do
46
46
  limiter = described_class.new(:key, 1, timeout: 3)
47
47
  expect(limiter.timeout).to be 3
48
- expect(limiter.instance_variable_get(:@timeout_usec)).to be (3 * 10**6)
48
+ expect(limiter.instance_variable_get(:@timeout_msec)).to be (3 * 10**3)
49
49
  end
50
50
 
51
51
  it 'handles infinity' do
52
52
  limiter = described_class.new(:key, 1, timeout: Float::INFINITY)
53
53
  expect(limiter.timeout).to be Float::INFINITY
54
- expect(limiter.instance_variable_get(:@timeout_usec)).to be 0
54
+ expect(limiter.instance_variable_get(:@timeout_msec)).to be 0
55
55
  end
56
56
  end
57
57
 
@@ -88,6 +88,18 @@ describe Berater::ConcurrencyLimiter do
88
88
  end
89
89
  end
90
90
 
91
+ context 'when capacity is a Float' do
92
+ let(:limiter) { described_class.new(:key, 1.5) }
93
+
94
+ it 'still works' do
95
+ lock = limiter.limit
96
+
97
+ # since fractional cost is not supported
98
+ expect(lock.capacity).to be 1
99
+ expect(limiter).to be_incapacitated
100
+ end
101
+ end
102
+
91
103
  it 'limit resets over time' do
92
104
  2.times { limiter.limit }
93
105
  expect(limiter).to be_incapacitated
@@ -149,6 +161,11 @@ describe Berater::ConcurrencyLimiter do
149
161
  5.times { limiter.limit(capacity: 10) }
150
162
  expect { limiter }.to be_incapacitated
151
163
  end
164
+
165
+ it 'only allows positive, Integer values' do
166
+ expect { limiter.limit(cost: -1) }.to raise_error(ArgumentError)
167
+ expect { limiter.limit(cost: 1.5) }.to raise_error(ArgumentError)
168
+ end
152
169
  end
153
170
 
154
171
  context 'with same key, different limiters' do
@@ -1,4 +1,5 @@
1
- describe 'be_overloaded' do
1
+ describe Berater::Matchers::Overloaded do
2
+
2
3
  context 'Berater::Unlimiter' do
3
4
  let(:limiter) { Berater.new(:key, :unlimited) }
4
5
 
@@ -16,12 +17,6 @@ describe 'be_overloaded' do
16
17
  it { expect { limiter.limit }.not_to be_inhibited }
17
18
  it { expect { limiter.limit }.not_to be_overrated }
18
19
  it { expect { limiter.limit }.not_to be_incapacitated }
19
-
20
- it 'catches false positives' do
21
- expect {
22
- expect { limiter }.to be_overloaded
23
- }.to fail
24
- end
25
20
  end
26
21
 
27
22
  context 'Berater::Inhibitor' do
@@ -35,12 +30,6 @@ describe 'be_overloaded' do
35
30
 
36
31
  it { expect { limiter.limit }.to be_overloaded }
37
32
  it { expect { limiter.limit }.to be_inhibited }
38
-
39
- it 'catches false negatives' do
40
- expect {
41
- expect { limiter }.not_to be_overloaded
42
- }.to fail
43
- end
44
33
  end
45
34
 
46
35
  context 'Berater::RateLimiter' do
@@ -127,4 +116,71 @@ describe 'be_overloaded' do
127
116
  end
128
117
  end
129
118
  end
119
+
120
+ context 'when matchers fail' do
121
+ let(:unlimiter) { Berater::Unlimiter.new }
122
+ let(:inhibitor) { Berater::Inhibitor.new }
123
+
124
+ it 'catches false negatives' do
125
+ expect {
126
+ expect(unlimiter).to be_overloaded
127
+ }.to fail_including('expected to be overloaded')
128
+
129
+ expect {
130
+ expect { unlimiter }.to be_overloaded
131
+ }.to fail_including('expected to be overloaded')
132
+
133
+ expect {
134
+ expect { unlimiter.limit }.to be_overloaded
135
+ }.to fail_including("expected #{Berater::Overloaded} to be raised")
136
+
137
+ expect {
138
+ expect { 123 }.to be_overloaded
139
+ }.to fail_including("expected #{Berater::Overloaded} to be raised")
140
+ end
141
+
142
+ it 'catches false positives' do
143
+ expect {
144
+ expect(inhibitor).not_to be_overloaded
145
+ }.to fail_including('expected not to be overloaded')
146
+
147
+ expect {
148
+ expect { inhibitor }.not_to be_overloaded
149
+ }.to fail_including('expected not to be overloaded')
150
+
151
+ expect {
152
+ expect { inhibitor.limit }.not_to be_overloaded
153
+ }.to fail_including("did not expect #{Berater::Overloaded} to be raised")
154
+
155
+ expect {
156
+ expect { raise Berater::Overloaded }.not_to be_overloaded
157
+ }.to fail_including("did not expect #{Berater::Overloaded} to be raised")
158
+ end
159
+
160
+ it 'supports different verbs' do
161
+ expect {
162
+ expect { unlimiter }.to be_overrated
163
+ }.to fail_including('expected to be overrated')
164
+
165
+ expect {
166
+ expect { unlimiter }.to be_incapacitated
167
+ }.to fail_including('expected to be incapacitated')
168
+ end
169
+
170
+ it 'supports different exceptions' do
171
+ expect {
172
+ expect { 123 }.to be_overrated
173
+ }.to fail_including(
174
+ "expected #{Berater::RateLimiter::Overrated} to be raised"
175
+ )
176
+
177
+ expect {
178
+ expect {
179
+ raise Berater::ConcurrencyLimiter::Incapacitated
180
+ }.not_to be_incapacitated
181
+ }.to fail_including(
182
+ "did not expect #{Berater::ConcurrencyLimiter::Incapacitated} to be raised"
183
+ )
184
+ end
185
+ end
130
186
  end
@@ -23,6 +23,7 @@ describe Berater::RateLimiter do
23
23
 
24
24
  it { expect_capacity(0) }
25
25
  it { expect_capacity(1) }
26
+ it { expect_capacity(1.5) }
26
27
  it { expect_capacity(100) }
27
28
 
28
29
  context 'with erroneous values' do
@@ -32,7 +33,6 @@ describe Berater::RateLimiter do
32
33
  end.to raise_error ArgumentError
33
34
  end
34
35
 
35
- it { expect_bad_capacity(0.5) }
36
36
  it { expect_bad_capacity(-1) }
37
37
  it { expect_bad_capacity('1') }
38
38
  it { expect_bad_capacity(:one) }
@@ -44,9 +44,19 @@ describe Berater::RateLimiter do
44
44
 
45
45
  subject { described_class.new(:key, 1, :second) }
46
46
 
47
- it 'saves the interval in original and microsecond format' do
47
+ it 'saves the interval in original and millisecond format' do
48
48
  expect(subject.interval).to be :second
49
- expect(subject.instance_variable_get(:@interval_usec)).to be 10**6
49
+ expect(subject.instance_variable_get(:@interval_msec)).to be 10**3
50
+ end
51
+
52
+ it 'must be > 0' do
53
+ expect {
54
+ described_class.new(:key, 1, 0)
55
+ }.to raise_error(ArgumentError)
56
+
57
+ expect {
58
+ described_class.new(:key, 1, -1)
59
+ }.to raise_error(ArgumentError)
50
60
  end
51
61
  end
52
62
 
@@ -68,26 +78,57 @@ describe Berater::RateLimiter do
68
78
  expect(limiter).to be_overrated
69
79
  end
70
80
 
71
- it 'limit resets over time, with millisecond precision' do
81
+ it 'resets limit over time' do
72
82
  3.times { limiter.limit }
73
83
  expect(limiter).to be_overrated
74
84
 
75
- # travel forward to just before the count decrements
76
- Timecop.freeze(0.333)
77
- expect(limiter).to be_overrated
78
-
79
- # traveling one more millisecond will decrement the count
80
- Timecop.freeze(0.001)
81
- limiter.limit
82
- expect(limiter).to be_overrated
83
-
84
- # traveling 1 second will reset the count
85
85
  Timecop.freeze(1)
86
86
 
87
87
  3.times { limiter.limit }
88
88
  expect(limiter).to be_overrated
89
89
  end
90
90
 
91
+ context 'with millisecond precision' do
92
+ it 'resets limit over time' do
93
+ 3.times { limiter.limit }
94
+ expect(limiter).to be_overrated
95
+
96
+ # travel forward to just before the count decrements
97
+ Timecop.freeze(0.333)
98
+ expect(limiter).to be_overrated
99
+
100
+ # traveling one more millisecond will decrement the count
101
+ Timecop.freeze(0.001)
102
+ limiter.limit
103
+ expect(limiter).to be_overrated
104
+ end
105
+
106
+ it 'works when drip rate is < 1 per millisecond' do
107
+ limiter = described_class.new(:key, 2_000, :second)
108
+
109
+ limiter.capacity.times { limiter.limit }
110
+ expect(limiter).to be_overrated
111
+
112
+ Timecop.freeze(0.001)
113
+ expect(limiter).not_to be_overrated
114
+
115
+ 2.times { limiter.limit }
116
+ end
117
+ end
118
+
119
+ context 'when capacity is a Float' do
120
+ let(:limiter) { described_class.new(:key, 1.5, :second) }
121
+
122
+ it 'still works' do
123
+ limiter.limit
124
+ expect(limiter).not_to be_overrated
125
+
126
+ expect { limiter.limit }.to be_overrated
127
+
128
+ limiter.limit(cost: 0.5)
129
+ end
130
+ end
131
+
91
132
  it 'accepts a dynamic capacity' do
92
133
  limiter = described_class.new(:key, 1, :second)
93
134
 
@@ -172,20 +172,11 @@ describe Berater::TestMode, order: :defined do
172
172
  describe 'ConcurrencyLimiter' do
173
173
  subject { Berater::ConcurrencyLimiter.new(:key, 1) }
174
174
 
175
- shared_examples 'a ConcurrencyLimiter' do
176
- it { expect(subject).to be_a Berater::ConcurrencyLimiter }
177
-
178
- it 'checks arguments' do
179
- expect {
180
- Berater::ConcurrencyLimiter.new(:key, 1.0)
181
- }.to raise_error(ArgumentError)
182
- end
183
- end
184
-
185
175
  context 'when test_mode = nil' do
186
176
  before { Berater.test_mode = nil }
187
177
 
188
- it_behaves_like 'a ConcurrencyLimiter'
178
+ it { is_expected.to be_a Berater::ConcurrencyLimiter }
179
+
189
180
  it_behaves_like 'it is not overloaded'
190
181
 
191
182
  it 'works per usual' do
@@ -198,14 +189,16 @@ describe Berater::TestMode, order: :defined do
198
189
  context 'when test_mode = :pass' do
199
190
  before { Berater.test_mode = :pass }
200
191
 
201
- it_behaves_like 'a ConcurrencyLimiter'
192
+ it { is_expected.to be_a Berater::ConcurrencyLimiter }
193
+
202
194
  it_behaves_like 'it always works, without redis'
203
195
  end
204
196
 
205
197
  context 'when test_mode = :fail' do
206
198
  before { Berater.test_mode = :fail }
207
199
 
208
- it_behaves_like 'a ConcurrencyLimiter'
200
+ it { is_expected.to be_a Berater::ConcurrencyLimiter }
201
+
209
202
  it_behaves_like 'it never works, without redis'
210
203
  end
211
204
  end
data/spec/utils_spec.rb CHANGED
@@ -1,63 +1,63 @@
1
1
  describe Berater::Utils do
2
2
  using Berater::Utils
3
3
 
4
- describe '.to_usec' do
4
+ describe '.to_msec' do
5
5
  def f(val)
6
- (val * 10**6).to_i
6
+ (val * 10**3).to_i
7
7
  end
8
8
 
9
9
  it 'works with integers' do
10
- expect(0.to_usec).to be f(0)
11
- expect(3.to_usec).to be f(3)
10
+ expect(0.to_msec).to be f(0)
11
+ expect(3.to_msec).to be f(3)
12
12
  end
13
13
 
14
14
  it 'works with floats' do
15
- expect(0.1.to_usec).to be f(0.1)
16
- expect(3.0.to_usec).to be f(3)
15
+ expect(0.1.to_msec).to be f(0.1)
16
+ expect(3.0.to_msec).to be f(3)
17
17
  end
18
18
 
19
- it 'has great precision' do
20
- expect(0.123456.to_usec).to be 123456
21
- expect(123456.654321.to_usec).to be 123456654321
19
+ it 'truncates excessive precision' do
20
+ expect(0.123456.to_msec).to be 123
21
+ expect(123456.654321.to_msec).to be 123456654
22
22
  end
23
23
 
24
24
  it 'works with symbols that are keywords' do
25
- expect(:sec.to_usec).to be f(1)
26
- expect(:second.to_usec).to be f(1)
27
- expect(:seconds.to_usec).to be f(1)
25
+ expect(:sec.to_msec).to be f(1)
26
+ expect(:second.to_msec).to be f(1)
27
+ expect(:seconds.to_msec).to be f(1)
28
28
 
29
- expect(:min.to_usec).to be f(60)
30
- expect(:minute.to_usec).to be f(60)
31
- expect(:minutes.to_usec).to be f(60)
29
+ expect(:min.to_msec).to be f(60)
30
+ expect(:minute.to_msec).to be f(60)
31
+ expect(:minutes.to_msec).to be f(60)
32
32
 
33
- expect(:hour.to_usec).to be f(60 * 60)
34
- expect(:hours.to_usec).to be f(60 * 60)
33
+ expect(:hour.to_msec).to be f(60 * 60)
34
+ expect(:hours.to_msec).to be f(60 * 60)
35
35
  end
36
36
 
37
37
  it 'works with strings that are keywords' do
38
- expect('sec'.to_usec).to be f(1)
39
- expect('second'.to_usec).to be f(1)
40
- expect('seconds'.to_usec).to be f(1)
38
+ expect('sec'.to_msec).to be f(1)
39
+ expect('second'.to_msec).to be f(1)
40
+ expect('seconds'.to_msec).to be f(1)
41
41
 
42
- expect('min'.to_usec).to be f(60)
43
- expect('minute'.to_usec).to be f(60)
44
- expect('minutes'.to_usec).to be f(60)
42
+ expect('min'.to_msec).to be f(60)
43
+ expect('minute'.to_msec).to be f(60)
44
+ expect('minutes'.to_msec).to be f(60)
45
45
 
46
- expect('hour'.to_usec).to be f(60 * 60)
47
- expect('hours'.to_usec).to be f(60 * 60)
46
+ expect('hour'.to_msec).to be f(60 * 60)
47
+ expect('hours'.to_msec).to be f(60 * 60)
48
48
  end
49
49
 
50
50
  it 'works with strings that are numeric' do
51
- expect('0'.to_usec).to be f(0)
52
- expect('3'.to_usec).to be f(3)
51
+ expect('0'.to_msec).to be f(0)
52
+ expect('3'.to_msec).to be f(3)
53
53
 
54
- expect('0.1'.to_usec).to be f(0.1)
55
- expect('3.0'.to_usec).to be f(3)
54
+ expect('0.1'.to_msec).to be f(0.1)
55
+ expect('3.0'.to_msec).to be f(3)
56
56
  end
57
57
 
58
58
  context 'with erroneous values' do
59
59
  def e(val)
60
- expect { val.to_usec }.to raise_error(ArgumentError)
60
+ expect { val.to_msec }.to raise_error(ArgumentError)
61
61
  end
62
62
 
63
63
  it 'rejects negative numbers' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berater
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Pepper
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-06 00:00:00.000000000 Z
11
+ date: 2021-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis