redis_recipes 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/README.md +26 -0
- data/Rakefile +8 -0
- data/lib/range_lookup/README.md +62 -0
- data/lib/range_lookup/add.lua +66 -0
- data/lib/range_lookup/lookup.lua +27 -0
- data/lib/range_lookup/remove.lua +62 -0
- data/lib/range_lookup_x/README.md +59 -0
- data/lib/range_lookup_x/add.lua +59 -0
- data/lib/range_lookup_x/lookup.lua +19 -0
- data/lib/range_lookup_x/remove.lua +51 -0
- data/redis_recipes.gemspec +23 -0
- data/spec/range_lookup/add_spec.rb +146 -0
- data/spec/range_lookup/lookup_spec.rb +64 -0
- data/spec/range_lookup/remove_spec.rb +189 -0
- data/spec/range_lookup_x/add_spec.rb +149 -0
- data/spec/range_lookup_x/lookup_spec.rb +65 -0
- data/spec/range_lookup_x/remove_spec.rb +186 -0
- data/spec/spec_helper.rb +64 -0
- metadata +121 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.platform = Gem::Platform::RUBY
|
4
|
+
s.required_ruby_version = '>= 1.9.0'
|
5
|
+
s.required_rubygems_version = ">= 1.3.6"
|
6
|
+
|
7
|
+
s.name = "redis_recipes"
|
8
|
+
s.summary = "Redis LUA recipes."
|
9
|
+
s.description = "Require Redis 2.6.0 or higher"
|
10
|
+
s.version = "0.3.0"
|
11
|
+
|
12
|
+
s.authors = ["Black Square Media"]
|
13
|
+
s.email = "info@blacksquaremedia.com"
|
14
|
+
s.homepage = "https://github.com/bsm/redis_recipes"
|
15
|
+
|
16
|
+
s.require_path = ['lib']
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
19
|
+
|
20
|
+
s.add_development_dependency "redis", "~> 3.0.0"
|
21
|
+
s.add_development_dependency "rake"
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Range Lookup: Adding" do
|
4
|
+
|
5
|
+
def state(c = :to_i)
|
6
|
+
ranges = {}
|
7
|
+
redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
|
8
|
+
ranges[score.send(c)] = redis.smembers("my_store:#{value}").sort.join
|
9
|
+
end
|
10
|
+
ranges
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(member, min, max)
|
14
|
+
evalsha :add, keys: ["my_store"], argv: [member, min, max]
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "general" do
|
18
|
+
|
19
|
+
it 'should catch invalid arguments' do
|
20
|
+
lambda { evalsha :add }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
21
|
+
lambda { evalsha :add, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
22
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
23
|
+
lambda { evalsha :add, keys: ["one", "two"], argv: ["one", 2, 3] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should catch non-numeric ranges' do
|
27
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", "two", 3] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
28
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", 2, "three"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
29
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", 3, 2] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "adding" do
|
35
|
+
before { add("A", 8, 17) }
|
36
|
+
|
37
|
+
it 'should support single-value ranges' do
|
38
|
+
lambda { add("B", 10, 10) }.should change { state }.
|
39
|
+
to 8 => "A", 10 => "AB", 17 => "A"
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should support float/decimal ranges' do
|
43
|
+
lambda { add("B", 10, 10.5) }.should change { state(:to_f) }.
|
44
|
+
to 8.0 => "A", 10.0 => "AB", 10.5 => "AB", 17.0 => "A"
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should support zero bounds' do
|
48
|
+
lambda { add("B", 0, 5) }.should change { state }.
|
49
|
+
to 0 => "B", 5 => "B", 8 => "A", 17 => "A"
|
50
|
+
|
51
|
+
lambda { add("C", -5, 0) }.should change { state }.
|
52
|
+
to -5 => "C", 0 => "BC", 5 => "B", 8 => "A", 17 => "A"
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should maintain an index' do
|
56
|
+
redis.zrange('my_store:~', 0, -1, with_scores: true).should == [["8", 8], ["17", 17]]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should store range values' do
|
60
|
+
redis.keys.should =~ ["my_store:~", "my_store:8", "my_store:17"]
|
61
|
+
state.should == { 8 => "A", 17 => "A" }
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should return OK if successful' do
|
65
|
+
add("B", 9, 18).should == "OK"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "merging" do
|
71
|
+
before { add("A", 8, 17) }
|
72
|
+
|
73
|
+
it 'should add non overlapping, left (B B A A)' do
|
74
|
+
lambda { add("B", 5, 6) }.should change { state }.
|
75
|
+
to 5 => "B", 6 => "B", 8 => "A", 17 => "A"
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should add non overlapping, right (A A B B)' do
|
79
|
+
lambda { add("B", 18, 23) }.should change { state }.
|
80
|
+
to 8 => "A", 17 => "A", 18 => "B", 23 => "B"
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should add non overlapping, inner (A B B A)' do
|
84
|
+
lambda { add("B", 9, 12) }.should change { state }.
|
85
|
+
to 8 => "A", 9 => "AB", 12 => "AB", 17 => "A"
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should add overlapping, left (B A B A)' do
|
89
|
+
lambda { add("B", 6, 10) }.should change { state }.
|
90
|
+
to 6 => "B", 8 => "AB", 10 => "AB", 17 => "A"
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should add overlapping, right (A B A B)' do
|
94
|
+
lambda { add("B", 15, 23) }.should change { state }.
|
95
|
+
to 8 => "A", 15 => "AB", 17 => "AB", 23 => "B"
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should add overlapping, both (AB BA)' do
|
99
|
+
lambda { add("B", 8, 17) }.should change { state }.
|
100
|
+
to 8 => "AB", 17 => "AB"
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should add tangent, outer left (B BA A)' do
|
104
|
+
lambda { add("B", 6, 8) }.should change { state }.
|
105
|
+
to 6 => "B", 8 => "AB", 17 => "A"
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should add tangent, inner left (AB B A)' do
|
109
|
+
lambda { add("B", 8, 10) }.should change { state }.
|
110
|
+
to 8 => "AB", 10 => "AB", 17 => "A"
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should add tangent, outer right (A AB B)' do
|
114
|
+
lambda { add("B", 17, 22) }.should change { state }.
|
115
|
+
to 8 => "A", 17 => "AB", 22 => "B"
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should add tangent, inner right (A B BA)' do
|
119
|
+
lambda { add("B", 12, 17) }.should change { state }.
|
120
|
+
to 8 => "A", 12 => "AB", 17 => "AB"
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should bring it all together' do
|
126
|
+
add("A", 8, 17)
|
127
|
+
add("B", 5, 6)
|
128
|
+
add("C", 18, 23)
|
129
|
+
add("D", 9, 12)
|
130
|
+
add("E", 6, 10)
|
131
|
+
add("F", 15, 23)
|
132
|
+
add("G", 8, 17)
|
133
|
+
add("H", 6, 8)
|
134
|
+
add("I", 8, 10)
|
135
|
+
add("J", 7, 22)
|
136
|
+
add("K", 12, 17)
|
137
|
+
|
138
|
+
state.should == {
|
139
|
+
5 => "B", 6 => "BEH", 7 => "EHJ",
|
140
|
+
8 => "AEGHIJ", 9 => "ADEGIJ", 10 => "ADEGIJ",
|
141
|
+
12 => "ADGJK", 15 => "AFGJK", 17 => "AFGJK",
|
142
|
+
18 => "CFJ", 22 => "CFJ", 23 => "CF"
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Range Lookup: Lookup" do
|
4
|
+
|
5
|
+
def add(member, min, max)
|
6
|
+
evalsha :add, keys: ["my_store"], argv: [member, min, max]
|
7
|
+
end
|
8
|
+
|
9
|
+
def lookup(value)
|
10
|
+
evalsha :lookup, keys: ["my_store"], argv: [value]
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
add("A", 0, 8)
|
15
|
+
add("B", 4, 6)
|
16
|
+
add("C", 2, 9)
|
17
|
+
add("D", 7, 10)
|
18
|
+
end
|
19
|
+
|
20
|
+
def state
|
21
|
+
ranges = {}
|
22
|
+
redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
|
23
|
+
ranges[score.to_f] = redis.smembers("my_store:#{value}").sort
|
24
|
+
end
|
25
|
+
ranges
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should catch invalid arguments' do
|
29
|
+
lambda { evalsha :lookup }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
30
|
+
lambda { evalsha :lookup, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
31
|
+
lambda { evalsha :lookup, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should catch non-numeric ranges' do
|
35
|
+
lambda { evalsha :lookup, keys: ["one"], argv: ["one"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return the members when found' do
|
39
|
+
lookup(0).should =~ ["A"]
|
40
|
+
lookup(1).should =~ ["A"]
|
41
|
+
lookup(2).should =~ ["A", "C"]
|
42
|
+
lookup(3).should =~ ["A", "C"]
|
43
|
+
lookup(4).should =~ ["A", "B", "C"]
|
44
|
+
lookup(5).should =~ ["A", "B", "C"]
|
45
|
+
lookup(6).should =~ ["A", "B", "C"]
|
46
|
+
lookup(7).should =~ ["A", "C", "D"]
|
47
|
+
lookup(8).should =~ ["A", "C", "D"]
|
48
|
+
lookup(9).should =~ ["C", "D"]
|
49
|
+
lookup(10).should =~ ["D"]
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should return an empty set if not found' do
|
53
|
+
lookup(-1).should =~ []
|
54
|
+
lookup(11).should =~ []
|
55
|
+
lookup(111).should =~ []
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should support float lookups' do
|
59
|
+
lookup(6.5).should =~ ["A", "C"]
|
60
|
+
lookup(7.5).should =~ ["A", "C", "D"]
|
61
|
+
lookup(10.1).should =~ []
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Range Lookup: Removing" do
|
4
|
+
|
5
|
+
def state(c = :to_i)
|
6
|
+
ranges = {}
|
7
|
+
redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
|
8
|
+
ranges[score.send(c)] = redis.smembers("my_store:#{value}").sort.join
|
9
|
+
end
|
10
|
+
ranges
|
11
|
+
end
|
12
|
+
|
13
|
+
def brackets
|
14
|
+
redis.zrange('my_store:~', 0, -1)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add(member, min, max)
|
18
|
+
evalsha :add, keys: ["my_store"], argv: [member, min, max]
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove(member, min, max)
|
22
|
+
evalsha :remove, keys: ["my_store"], argv: [member, min, max]
|
23
|
+
end
|
24
|
+
|
25
|
+
def scenario(pairs)
|
26
|
+
pairs.each do |member, range|
|
27
|
+
add member.to_s, range.first, range.last
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "general" do
|
32
|
+
|
33
|
+
it 'should catch invalid arguments' do
|
34
|
+
lambda { evalsha :remove }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
35
|
+
lambda { evalsha :remove, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
36
|
+
lambda { evalsha :remove, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
37
|
+
lambda { evalsha :remove, keys: ["one", "two"], argv: ["one", 2, 3] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should catch non-numeric ranges' do
|
41
|
+
lambda { evalsha :remove, keys: ["one"], argv: ["one", "two", 3] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
42
|
+
lambda { evalsha :remove, keys: ["one"], argv: ["one", 2, "three"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
43
|
+
lambda { evalsha :remove, keys: ["one"], argv: ["one", 3, 2] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return OK if successful' do
|
47
|
+
remove("A", 8, 17).should == "OK"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "removing" do
|
53
|
+
before { add("A", 8, 17) }
|
54
|
+
|
55
|
+
it 'should support zero bounds' do
|
56
|
+
scenario B: -5..0, C: 0..5
|
57
|
+
|
58
|
+
lambda {
|
59
|
+
remove("B", -5, 0)
|
60
|
+
remove("C", 0, 5)
|
61
|
+
}.should change { state }.
|
62
|
+
from(-5 => "B", 0 => "BC", 5 => "C", 8 => "A", 17 => "A").
|
63
|
+
to(8 => "A", 17 => "A")
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should clean up index' do
|
67
|
+
lambda {
|
68
|
+
add("B", 15, 22)
|
69
|
+
add("C", 15, 23)
|
70
|
+
}.should change { brackets }.to ["8", "15", "17", "22", "23"]
|
71
|
+
|
72
|
+
lambda {
|
73
|
+
remove("B", 15, 22)
|
74
|
+
}.should change { brackets }.to ["8", "15", "17", "23"]
|
75
|
+
|
76
|
+
lambda {
|
77
|
+
remove("C", 15, 23)
|
78
|
+
}.should change { brackets }.to ["8", "17"]
|
79
|
+
|
80
|
+
lambda {
|
81
|
+
remove("A", 8, 17)
|
82
|
+
}.should change { brackets }.to []
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "use cases" do
|
88
|
+
|
89
|
+
it "should remove from [A: 1-2, B: 3-4]" do
|
90
|
+
scenario A: 1..2, B: 3..4
|
91
|
+
lambda { remove("A", 1, 2) }.should change { state }.
|
92
|
+
from(1 => "A", 2 => "A", 3 => "B", 4 => "B").
|
93
|
+
to(3 => "B", 4 => "B")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should remove from [A: 3-4, B: 1-2]" do
|
97
|
+
scenario A: 3..4, B: 1..2
|
98
|
+
lambda { remove("A", 3, 4) }.should change { state }.
|
99
|
+
from(1 => "B", 2 => "B", 3 => "A", 4 => "A").
|
100
|
+
to(1 => "B", 2 => "B")
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should remove from [A: 1-3, B: 2-4]" do
|
104
|
+
scenario A: 1..3, B: 2..4
|
105
|
+
lambda { remove("A", 1, 3) }.should change { state }.
|
106
|
+
from(1 => "A", 2 => "AB", 3 => "AB", 4 => "B").
|
107
|
+
to(2 => "B", 4 => "B")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should remove from [A: 2-4, B: 1-3]" do
|
111
|
+
scenario A: 2..4, B: 1..3
|
112
|
+
lambda { remove("A", 2, 4) }.should change { state }.
|
113
|
+
from(1 => "B", 2 => "AB", 3 => "AB", 4 => "A").
|
114
|
+
to(1 => "B", 3 => "B")
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should remove from [A: 1-4, B: 2-3]" do
|
118
|
+
scenario A: 1..4, B: 2..3
|
119
|
+
lambda { remove("A", 1, 4) }.should change { state }.
|
120
|
+
from(1 => "A", 2 => "AB", 3 => "AB", 4 => "A").
|
121
|
+
to(2 => "B", 3 => "B")
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should remove from [A: 2-3, B: 1-4]" do
|
125
|
+
scenario A: 2..3, B: 1..4
|
126
|
+
lambda { remove("A", 2, 3) }.should change { state }.
|
127
|
+
from(1 => "B", 2 => "AB", 3 => "AB", 4 => "B").
|
128
|
+
to(1 => "B", 4 => "B")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should remove from [A: 1-3, B: 3-4]" do
|
132
|
+
scenario A: 1..3, B: 3..4
|
133
|
+
lambda { remove("A", 1, 3) }.should change { state }.
|
134
|
+
from(1 => "A", 3 => "AB", 4 => "B").
|
135
|
+
to(3 => "B", 4 => "B")
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should remove from [A: 1-4, B: 3-4]" do
|
139
|
+
scenario A: 1..4, B: 3..4
|
140
|
+
lambda { remove("A", 1, 4) }.should change { state }.
|
141
|
+
from(1 => "A", 3 => "AB", 4 => "AB").
|
142
|
+
to(3 => "B", 4 => "B")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should remove from [A: 3-4, B: 1-3]" do
|
146
|
+
scenario A: 3..4, B: 1..3
|
147
|
+
lambda { remove("A", 3, 4) }.should change { state }.
|
148
|
+
from(1 => "B", 3 => "AB", 4 => "A").
|
149
|
+
to(1 => "B", 3 => "B")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should remove from [A: 3-4, B: 1-4]" do
|
153
|
+
scenario A: 3..4, B: 1..4
|
154
|
+
lambda { remove("A", 3, 4) }.should change { state }.
|
155
|
+
from(1 => "B", 3 => "AB", 4 => "AB").
|
156
|
+
to(1 => "B", 4 => "B")
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should remove from [A: 1-4, B: 1-2]" do
|
160
|
+
scenario A: 1..4, B: 1..2
|
161
|
+
lambda { remove("A", 1, 4) }.should change { state }.
|
162
|
+
from(1 => "AB", 2 => "AB", 4 => "A").
|
163
|
+
to(1 => "B", 2 => "B")
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should remove from [A: 2-4, B: 1-2]" do
|
167
|
+
scenario A: 2..4, B: 1..2
|
168
|
+
lambda { remove("A", 2, 4) }.should change { state }.
|
169
|
+
from(1 => "B", 2 => "AB", 4 => "A").
|
170
|
+
to(1 => "B", 2 => "B")
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should remove from [A: 1-4, B: 1-4]" do
|
174
|
+
scenario A: 1..4, B: 1..4
|
175
|
+
lambda { remove("A", 1, 4) }.should change { state }.
|
176
|
+
from(1 => "AB", 4 => "AB").
|
177
|
+
to(1 => "B", 4 => "B")
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should remove from [A: 1-4]" do
|
181
|
+
scenario A: 1..4
|
182
|
+
lambda { remove("A", 1, 4) }.should change { state }.
|
183
|
+
from(1 => "A", 4 => "A").
|
184
|
+
to({})
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Range Lookup X: Adding" do
|
4
|
+
|
5
|
+
def state(c = :to_i)
|
6
|
+
ranges = {}
|
7
|
+
redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
|
8
|
+
ranges[score.send(c)] = redis.smembers("my_store:#{value}").sort.join
|
9
|
+
end
|
10
|
+
ranges
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(member, min, max)
|
14
|
+
evalsha :add, keys: ["my_store"], argv: [member, min, max]
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "general" do
|
18
|
+
|
19
|
+
it 'should catch invalid arguments' do
|
20
|
+
lambda { evalsha :add }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
21
|
+
lambda { evalsha :add, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
22
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
23
|
+
lambda { evalsha :add, keys: ["one", "two"], argv: ["one", 2, 3] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should catch non-numeric ranges' do
|
27
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", "two", 3] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
28
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", 2, "three"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
29
|
+
lambda { evalsha :add, keys: ["one"], argv: ["one", 3, 2] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should NOT support single-value ranges' do
|
33
|
+
lambda { add("B", 10, 10) }.should raise_error(Redis::CommandError, /not numeric or out of range/)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "adding" do
|
39
|
+
|
40
|
+
it 'should store ranges' do
|
41
|
+
lambda { add("A", 8, 17) }.should change { state }.
|
42
|
+
to 8 => "A", 17 => ""
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should support float/decimal ranges' do
|
46
|
+
lambda { add("A", 7.5, 17.9); add("B", 8.1, 10.6) }.should change { state(:to_f) }.
|
47
|
+
to 7.5 => "A", 8.1 => "AB", 10.6 => "A", 17.9 => ""
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should support nagative ranges and bounds' do
|
51
|
+
lambda { add("B", 0, 5) }.should change { state }.
|
52
|
+
to 0 => "B", 5 => ""
|
53
|
+
|
54
|
+
lambda { add("C", -5, 0) }.should change { state }.
|
55
|
+
to -5 => "C", 0 => "B", 5 => ""
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should maintain an index' do
|
59
|
+
lambda { add("A", 8, 17) }.should change {
|
60
|
+
redis.zrange('my_store:~', 0, -1, with_scores: true)
|
61
|
+
}.to [["8", 8.0], ["17", 17.0]]
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should store range members' do
|
65
|
+
lambda { add("A", 8, 17) }.should change { redis.keys.sort }.
|
66
|
+
to ["my_store:8", "my_store:~"]
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should return OK if successful' do
|
70
|
+
add("A", 8, 17).should == "OK"
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "merging" do
|
76
|
+
before { add("A", 8, 17) }
|
77
|
+
|
78
|
+
it 'should add non overlapping, left (B B A A)' do
|
79
|
+
lambda { add("B", 5, 6) }.should change { state }.
|
80
|
+
to 5 => "B", 6 => "", 8 => "A", 17 => ""
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should add non overlapping, right (A A B B)' do
|
84
|
+
lambda { add("B", 18, 23) }.should change { state }.
|
85
|
+
to 8 => "A", 17 => "", 18 => "B", 23 => ""
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should add non overlapping, inner (A B B A)' do
|
89
|
+
lambda { add("B", 9, 12) }.should change { state }.
|
90
|
+
to 8 => "A", 9 => "AB", 12 => "A", 17 => ""
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should add overlapping, left (B A B A)' do
|
94
|
+
lambda { add("B", 6, 10) }.should change { state }.
|
95
|
+
to 6 => "B", 8 => "AB", 10 => "A", 17 => ""
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should add overlapping, right (A B A B)' do
|
99
|
+
lambda { add("B", 15, 23) }.should change { state }.
|
100
|
+
to 8 => "A", 15 => "AB", 17 => "B", 23 => ""
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should add overlapping, both (AB BA)' do
|
104
|
+
lambda { add("B", 8, 17) }.should change { state }.
|
105
|
+
to 8 => "AB", 17 => ""
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should add tangent, outer left (B BA A)' do
|
109
|
+
lambda { add("B", 6, 8) }.should change { state }.
|
110
|
+
to 6 => "B", 8 => "A", 17 => ""
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should add tangent, inner left (AB B A)' do
|
114
|
+
lambda { add("B", 8, 10) }.should change { state }.
|
115
|
+
to 8 => "AB", 10 => "A", 17 => ""
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should add tangent, outer right (A AB B)' do
|
119
|
+
lambda { add("B", 17, 22) }.should change { state }.
|
120
|
+
to 8 => "A", 17 => "B", 22 => ""
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should add tangent, inner right (A B BA)' do
|
124
|
+
lambda { add("B", 12, 17) }.should change { state }.
|
125
|
+
to 8 => "A", 12 => "AB", 17 => ""
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should work with multiple ranges' do
|
129
|
+
add("B", 5, 6)
|
130
|
+
add("C", 18, 23)
|
131
|
+
add("D", 9, 12)
|
132
|
+
add("E", 6, 10)
|
133
|
+
add("F", 15, 23)
|
134
|
+
add("G", 8, 17)
|
135
|
+
add("H", 6, 8)
|
136
|
+
add("I", 8, 10)
|
137
|
+
add("J", 7, 22)
|
138
|
+
add("K", 12, 17)
|
139
|
+
|
140
|
+
state.should == {
|
141
|
+
5 => "B", 6 => "EH", 7 => "EHJ",
|
142
|
+
8 => "AEGIJ", 9 => "ADEGIJ", 10 => "ADGJ",
|
143
|
+
12 => "AGJK", 15 => "AFGJK", 17 => "FJ",
|
144
|
+
18 => "CFJ", 22 => "CF", 23 => ""
|
145
|
+
}
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|