berater 0.1.0 → 0.2.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.
@@ -1,3 +1,3 @@
1
1
  module Berater
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
data/spec/berater_spec.rb CHANGED
@@ -9,10 +9,10 @@ describe Berater do
9
9
  describe '.configure' do
10
10
  it 'can be set via configure' do
11
11
  Berater.configure do |c|
12
- c.mode = :rate
12
+ c.redis = :redis
13
13
  end
14
14
 
15
- expect(Berater.mode).to eq :rate
15
+ expect(Berater.redis).to eq :redis
16
16
  end
17
17
  end
18
18
 
@@ -24,20 +24,13 @@ describe Berater do
24
24
  end
25
25
  end
26
26
 
27
- describe '.mode' do
28
- it 'validates inputs' do
29
- expect { Berater.mode = :foo }.to raise_error(ArgumentError)
30
- end
31
- end
32
-
33
- context 'unlimited mode' do
34
- before { Berater.mode = :unlimited }
35
-
36
- describe '.new' do
37
- let(:limiter) { Berater.new(:unlimited) }
27
+ describe '.new' do
28
+ context 'unlimited mode' do
29
+ let(:limiter) { Berater.new(:key, :unlimited) }
38
30
 
39
31
  it 'instantiates an Unlimiter' do
40
32
  expect(limiter).to be_a Berater::Unlimiter
33
+ expect(limiter.key).to be :key
41
34
  end
42
35
 
43
36
  it 'inherits redis' do
@@ -46,35 +39,22 @@ describe Berater do
46
39
 
47
40
  it 'accepts options' do
48
41
  redis = double('Redis')
49
- limiter = Berater.new(:unlimited, key: 'key', redis: redis)
50
- expect(limiter.key).to match(/key/)
42
+ limiter = Berater.new(:key, :unlimited, redis: redis)
51
43
  expect(limiter.redis).to be redis
52
44
  end
53
- end
54
-
55
- describe '.limit' do
56
- it 'works' do
57
- expect(Berater.limit).to be_nil
58
- end
59
-
60
- it 'yields' do
61
- expect {|b| Berater.limit(&b) }.to yield_control
62
- end
63
45
 
64
- it 'never limits' do
65
- 10.times { expect(Berater.limit { 123 } ).to eq 123 }
46
+ it 'works with convinience' do
47
+ expect(Berater).to receive(:new).and_return(limiter)
48
+ expect {|b| Berater(:key, :unlimited, &b) }.to yield_control
66
49
  end
67
50
  end
68
- end
69
-
70
- context 'inhibited mode' do
71
- before { Berater.mode = :inhibited }
72
51
 
73
- describe '.new' do
74
- let(:limiter) { Berater.new(:inhibited) }
52
+ context 'inhibited mode' do
53
+ let(:limiter) { Berater.new(:key, :inhibited) }
75
54
 
76
55
  it 'instantiates an Inhibitor' do
77
56
  expect(limiter).to be_a Berater::Inhibitor
57
+ expect(limiter.key).to be :key
78
58
  end
79
59
 
80
60
  it 'inherits redis' do
@@ -83,27 +63,22 @@ describe Berater do
83
63
 
84
64
  it 'accepts options' do
85
65
  redis = double('Redis')
86
- limiter = Berater.new(:inhibited, key: 'key', redis: redis)
87
- expect(limiter.key).to match(/key/)
66
+ limiter = Berater.new(:key, :inhibited, redis: redis)
88
67
  expect(limiter.redis).to be redis
89
68
  end
90
- end
91
69
 
92
- describe '.limit' do
93
- it 'always limits' do
94
- expect { Berater.limit }.to be_inhibited
70
+ it 'works with convinience' do
71
+ expect(Berater).to receive(:new).and_return(limiter)
72
+ expect { Berater(:key, :inhibited) }.to be_inhibited
95
73
  end
96
74
  end
97
- end
98
-
99
- context 'rate mode' do
100
- before { Berater.mode = :rate }
101
75
 
102
- describe '.limiter' do
103
- let(:limiter) { Berater.new(:rate, 1, :second) }
76
+ context 'rate mode' do
77
+ let(:limiter) { Berater.new(:key, :rate, 1, :second) }
104
78
 
105
79
  it 'instantiates a RateLimiter' do
106
80
  expect(limiter).to be_a Berater::RateLimiter
81
+ expect(limiter.key).to be :key
107
82
  end
108
83
 
109
84
  it 'inherits redis' do
@@ -112,44 +87,22 @@ describe Berater do
112
87
 
113
88
  it 'accepts options' do
114
89
  redis = double('Redis')
115
- limiter = Berater.new(:rate, 1, :second, key: 'key', redis: redis)
116
- expect(limiter.key).to match(/key/)
90
+ limiter = Berater.new(:key, :rate, 1, :second, redis: redis)
117
91
  expect(limiter.redis).to be redis
118
92
  end
119
- end
120
-
121
- describe '.limit' do
122
- it 'works' do
123
- expect(Berater.limit(1, :second)).to eq 1
124
- end
125
-
126
- it 'yields' do
127
- expect {|b| Berater.limit(2, :second, &b) }.to yield_control
128
- expect(Berater.limit(2, :second) { 123 }).to eq 123
129
- end
130
93
 
131
- it 'limits excessive calls' do
132
- expect(Berater.limit(1, :second)).to eq 1
133
- expect { Berater.limit(1, :second) }.to be_overrated
134
- end
135
-
136
- it 'accepts options' do
137
- redis = double('Redis')
138
- expect(redis).to receive(:multi)
139
-
140
- Berater.limit(1, :second, redis: redis) rescue nil
94
+ it 'works with convinience' do
95
+ expect(Berater).to receive(:new).and_return(limiter)
96
+ expect {|b| Berater(:key, :rate, 1, :second, &b) }.to yield_control
141
97
  end
142
98
  end
143
- end
144
99
 
145
- context 'concurrency mode' do
146
- before { Berater.mode = :concurrency }
147
-
148
- describe '.limiter' do
149
- let(:limiter) { Berater.new(:concurrency, 1) }
100
+ context 'concurrency mode' do
101
+ let(:limiter) { Berater.new(:key, :concurrency, 1) }
150
102
 
151
103
  it 'instantiates a ConcurrencyLimiter' do
152
104
  expect(limiter).to be_a Berater::ConcurrencyLimiter
105
+ expect(limiter.key).to be :key
153
106
  end
154
107
 
155
108
  it 'inherits redis' do
@@ -158,33 +111,13 @@ describe Berater do
158
111
 
159
112
  it 'accepts options' do
160
113
  redis = double('Redis')
161
- limiter = Berater.new(:concurrency, 1, key: 'key', redis: redis)
162
- expect(limiter.key).to match(/key/)
114
+ limiter = Berater.new(:key, :concurrency, 1, redis: redis)
163
115
  expect(limiter.redis).to be redis
164
116
  end
165
- end
166
-
167
- describe '.limit' do
168
- it 'works (without blocks by returning a lock)' do
169
- lock = Berater.limit(1)
170
- expect(lock).to be_a Berater::ConcurrencyLimiter::Lock
171
- expect(lock.release).to be true
172
- end
173
-
174
- it 'yields' do
175
- expect {|b| Berater.limit(1, &b) }.to yield_control
176
- end
177
-
178
- it 'limits excessive calls' do
179
- Berater.limit(1)
180
- expect { Berater.limit(1) }.to be_incapacitated
181
- end
182
-
183
- it 'accepts options' do
184
- redis = double('Redis')
185
- expect(redis).to receive(:eval)
186
117
 
187
- Berater.limit(1, redis: redis) rescue nil
118
+ it 'works with convinience' do
119
+ expect(Berater).to receive(:new).and_return(limiter)
120
+ expect {|b| Berater(:key, :concurrency, 1, &b) }.to yield_control
188
121
  end
189
122
  end
190
123
  end
@@ -1,22 +1,20 @@
1
1
  describe Berater::ConcurrencyLimiter do
2
- before { Berater.mode = :concurrency }
3
-
4
2
  describe '.new' do
5
- let(:limiter) { described_class.new(1) }
3
+ let(:limiter) { described_class.new(:key, 1) }
6
4
 
7
5
  it 'initializes' do
6
+ expect(limiter.key).to be :key
8
7
  expect(limiter.capacity).to be 1
9
8
  end
10
9
 
11
10
  it 'has default values' do
12
- expect(limiter.key).to eq described_class.to_s
13
11
  expect(limiter.redis).to be Berater.redis
14
12
  end
15
13
  end
16
14
 
17
15
  describe '#capacity' do
18
16
  def expect_capacity(capacity)
19
- limiter = described_class.new(capacity)
17
+ limiter = described_class.new(:key, capacity)
20
18
  expect(limiter.capacity).to eq capacity
21
19
  end
22
20
 
@@ -27,7 +25,7 @@ describe Berater::ConcurrencyLimiter do
27
25
  context 'with erroneous values' do
28
26
  def expect_bad_capacity(capacity)
29
27
  expect do
30
- described_class.new(capacity)
28
+ described_class.new(:key, capacity)
31
29
  end.to raise_error ArgumentError
32
30
  end
33
31
 
@@ -40,7 +38,7 @@ describe Berater::ConcurrencyLimiter do
40
38
 
41
39
  describe '#timeout' do
42
40
  def expect_timeout(timeout)
43
- limiter = described_class.new(1, timeout: timeout)
41
+ limiter = described_class.new(:key, 1, timeout: timeout)
44
42
  expect(limiter.timeout).to eq timeout
45
43
  end
46
44
 
@@ -51,7 +49,7 @@ describe Berater::ConcurrencyLimiter do
51
49
  context 'with erroneous values' do
52
50
  def expect_bad_timeout(timeout)
53
51
  expect do
54
- described_class.new(1, timeout: timeout)
52
+ described_class.new(:key, 1, timeout: timeout)
55
53
  end.to raise_error ArgumentError
56
54
  end
57
55
 
@@ -63,126 +61,113 @@ describe Berater::ConcurrencyLimiter do
63
61
  end
64
62
 
65
63
  describe '#limit' do
66
- let(:limiter) { described_class.new(2, timeout: 1) }
64
+ let(:limiter) { described_class.new(:key, 2, timeout: 1) }
67
65
 
68
66
  it 'works' do
69
67
  expect {|b| limiter.limit(&b) }.to yield_control
70
68
  end
71
69
 
72
- it 'works many times if workers complete and return locks' do
70
+ it 'works many times if workers release locks' do
73
71
  30.times do
74
72
  expect {|b| limiter.limit(&b) }.to yield_control
75
73
  end
74
+
75
+ 30.times do
76
+ lock = limiter.limit
77
+ lock.release
78
+ end
76
79
  end
77
80
 
78
81
  it 'limits excessive calls' do
79
- expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
80
- expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
82
+ expect(limiter.limit).to be_a Berater::Lock
83
+ expect(limiter.limit).to be_a Berater::Lock
81
84
 
82
- expect { limiter }.to be_incapacitated
85
+ expect(limiter).to be_incapacitated
83
86
  end
84
87
 
85
88
  it 'times out locks' do
86
- expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
87
- expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
88
- expect { limiter }.to be_incapacitated
89
+ expect(limiter.limit).to be_a Berater::Lock
90
+ expect(limiter.limit).to be_a Berater::Lock
91
+ expect(limiter).to be_incapacitated
89
92
 
90
- sleep(1)
93
+ Timecop.travel(1)
91
94
 
92
- expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
93
- expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
94
- expect { limiter }.to be_incapacitated
95
+ expect(limiter.limit).to be_a Berater::Lock
96
+ expect(limiter.limit).to be_a Berater::Lock
97
+ expect(limiter).to be_incapacitated
95
98
  end
96
- end
97
99
 
98
- context 'with same key, different limiters' do
99
- let(:limiter_one) { described_class.new(1) }
100
- let(:limiter_two) { described_class.new(1) }
101
-
102
- it { expect(limiter_one.key).to eq limiter_two.key }
103
-
104
- it 'works as expected' do
105
- expect(limiter_one.limit).to be_a Berater::ConcurrencyLimiter::Lock
100
+ context 'with capacity 0' do
101
+ let(:limiter) { described_class.new(:key, 0) }
106
102
 
107
- expect { limiter_one }.to be_incapacitated
108
- expect { limiter_two }.to be_incapacitated
103
+ it 'always fails' do
104
+ expect(limiter).to be_incapacitated
105
+ end
109
106
  end
110
107
  end
111
108
 
112
- context 'with different keys, same limiter' do
113
- let(:limiter) { described_class.new(1) }
114
-
115
- it 'works as expected' do
116
- one_lock = limiter.limit(key: :one)
117
- expect(one_lock).to be_a Berater::ConcurrencyLimiter::Lock
118
-
119
- expect { limiter.limit(key: :one) {} }.to be_incapacitated
120
- expect { limiter.limit(key: :two) {} }.not_to be_incapacitated
121
-
122
- two_lock = limiter.limit(key: :two)
123
- expect(two_lock).to be_a Berater::ConcurrencyLimiter::Lock
109
+ context 'with same key, different limiters' do
110
+ let(:limiter_one) { described_class.new(:key, 1) }
111
+ let(:limiter_two) { described_class.new(:key, 1) }
124
112
 
125
- expect { limiter.limit(key: :one) {} }.to be_incapacitated
126
- expect { limiter.limit(key: :two) {} }.to be_incapacitated
113
+ it { expect(limiter_one.key).to eq limiter_two.key }
127
114
 
128
- one_lock.release
129
- expect { limiter.limit(key: :one) {} }.not_to be_incapacitated
130
- expect { limiter.limit(key: :two) {} }.to be_incapacitated
115
+ it 'works as expected' do
116
+ expect(limiter_one.limit).to be_a Berater::Lock
131
117
 
132
- two_lock.release
133
- expect { limiter.limit(key: :one) {} }.not_to be_incapacitated
134
- expect { limiter.limit(key: :two) {} }.not_to be_incapacitated
118
+ expect(limiter_one).to be_incapacitated
119
+ expect(limiter_two).to be_incapacitated
135
120
  end
136
121
  end
137
122
 
138
123
  context 'with same key, different capacities' do
139
- let(:limiter_one) { described_class.new(1) }
140
- let(:limiter_two) { described_class.new(2) }
124
+ let(:limiter_one) { described_class.new(:key, 1) }
125
+ let(:limiter_two) { described_class.new(:key, 2) }
141
126
 
142
127
  it { expect(limiter_one.capacity).not_to eq limiter_two.capacity }
143
128
 
144
129
  it 'works as expected' do
145
130
  one_lock = limiter_one.limit
146
- expect(one_lock).to be_a Berater::ConcurrencyLimiter::Lock
131
+ expect(one_lock).to be_a Berater::Lock
147
132
 
148
- expect { limiter_one }.to be_incapacitated
149
- expect { limiter_two }.not_to be_incapacitated
133
+ expect(limiter_one).to be_incapacitated
134
+ expect(limiter_two).not_to be_incapacitated
150
135
 
151
136
  two_lock = limiter_two.limit
152
- expect(two_lock).to be_a Berater::ConcurrencyLimiter::Lock
137
+ expect(two_lock).to be_a Berater::Lock
153
138
 
154
- expect { limiter_one }.to be_incapacitated
155
- expect { limiter_two }.to be_incapacitated
139
+ expect(limiter_one).to be_incapacitated
140
+ expect(limiter_two).to be_incapacitated
156
141
 
157
142
  one_lock.release
158
- expect { limiter_one }.to be_incapacitated
159
- expect { limiter_two }.not_to be_incapacitated
143
+ expect(limiter_one).to be_incapacitated
144
+ expect(limiter_two).not_to be_incapacitated
160
145
 
161
146
  two_lock.release
162
- expect { limiter_one }.not_to be_incapacitated
163
- expect { limiter_two }.not_to be_incapacitated
147
+ expect(limiter_one).not_to be_incapacitated
148
+ expect(limiter_two).not_to be_incapacitated
164
149
  end
165
150
  end
166
151
 
167
152
  context 'with different keys, different limiters' do
168
- let(:limiter_one) { described_class.new(1, key: :one) }
169
- let(:limiter_two) { described_class.new(1, key: :two) }
153
+ let(:limiter_one) { described_class.new(:one, 1) }
154
+ let(:limiter_two) { described_class.new(:two, 1) }
170
155
 
171
156
  it 'works as expected' do
172
- expect { limiter_one }.not_to be_incapacitated
173
- expect { limiter_two }.not_to be_incapacitated
157
+ expect(limiter_one).not_to be_incapacitated
158
+ expect(limiter_two).not_to be_incapacitated
174
159
 
175
160
  one_lock = limiter_one.limit
176
- expect(one_lock).to be_a Berater::ConcurrencyLimiter::Lock
161
+ expect(one_lock).to be_a Berater::Lock
177
162
 
178
- expect { limiter_one }.to be_incapacitated
179
- expect { limiter_two }.not_to be_incapacitated
163
+ expect(limiter_one).to be_incapacitated
164
+ expect(limiter_two).not_to be_incapacitated
180
165
 
181
166
  two_lock = limiter_two.limit
182
- expect(two_lock).to be_a Berater::ConcurrencyLimiter::Lock
167
+ expect(two_lock).to be_a Berater::Lock
183
168
 
184
- expect { limiter_one }.to be_incapacitated
185
- expect { limiter_two }.to be_incapacitated
169
+ expect(limiter_one).to be_incapacitated
170
+ expect(limiter_two).to be_incapacitated
186
171
  end
187
172
  end
188
173
 
@@ -1,34 +1,39 @@
1
- describe Berater::ConcurrencyLimiter::Lock do
2
- subject { Berater.limit(1, timeout: 1) }
1
+ describe Berater::Lock do
2
+ it_behaves_like 'a lock', Berater.new(:key, :concurrency, 3)
3
3
 
4
- before { Berater.mode = :concurrency }
4
+ let(:limiter) { Berater.new(:key, :concurrency, 3) }
5
5
 
6
- it { expect(subject.released?).to be false }
7
- it { expect(subject.expired?).to be false }
6
+ describe '#expired?' do
7
+ let!(:lock) { limiter.limit }
8
8
 
9
- context 'after being released' do
10
- before { subject.release }
9
+ context 'when timeout is not set' do
10
+ it { expect(limiter.timeout).to eq 0 }
11
11
 
12
- it { expect(subject.released?).to be true }
13
- it { expect(subject.expired?).to be false }
12
+ it 'never expires' do
13
+ expect(lock.locked?).to be true
14
+ expect(lock.expired?).to be false
14
15
 
15
- it 'can not be released again' do
16
- expect { subject.release }.to raise_error(RuntimeError, /already/)
16
+ Timecop.travel(1_000)
17
+
18
+ expect(lock.locked?).to be true
19
+ expect(lock.expired?).to be false
20
+ end
17
21
  end
18
- end
19
22
 
20
- context 'when enough time passes' do
21
- before { subject; Timecop.freeze(2) }
23
+ context 'when timeout is set and exceeded' do
24
+ before { Timecop.travel(1) }
22
25
 
23
- it 'expires' do
24
- expect(subject.expired?).to be true
25
- end
26
+ let(:limiter) { Berater.new(:key, :concurrency, 3, timeout: 1) }
26
27
 
27
- it 'fails to release' do
28
- expect { subject.release }.to raise_error(RuntimeError, /expired/)
29
- end
28
+ it 'expires' do
29
+ expect(lock.expired?).to be true
30
+ expect(lock.locked?).to be false
31
+ end
30
32
 
31
- it { expect(subject.released?).to be false }
33
+ it 'fails to release' do
34
+ expect { lock.release }.to raise_error(RuntimeError, /expired/)
35
+ end
36
+ end
32
37
  end
33
38
 
34
39
  end