berater 0.5.0 → 0.6.0

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: 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