redis_recipes 0.3.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.
- 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
|