berater 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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