redisrank 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +27 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +297 -0
  8. data/Rakefile +69 -0
  9. data/lib/redisrank.rb +106 -0
  10. data/lib/redisrank/buffer.rb +110 -0
  11. data/lib/redisrank/collection.rb +20 -0
  12. data/lib/redisrank/connection.rb +89 -0
  13. data/lib/redisrank/core_ext.rb +5 -0
  14. data/lib/redisrank/core_ext/bignum.rb +8 -0
  15. data/lib/redisrank/core_ext/date.rb +8 -0
  16. data/lib/redisrank/core_ext/fixnum.rb +8 -0
  17. data/lib/redisrank/core_ext/hash.rb +20 -0
  18. data/lib/redisrank/core_ext/time.rb +3 -0
  19. data/lib/redisrank/date.rb +88 -0
  20. data/lib/redisrank/event.rb +98 -0
  21. data/lib/redisrank/finder.rb +245 -0
  22. data/lib/redisrank/finder/date_set.rb +99 -0
  23. data/lib/redisrank/key.rb +84 -0
  24. data/lib/redisrank/label.rb +69 -0
  25. data/lib/redisrank/mixins/database.rb +11 -0
  26. data/lib/redisrank/mixins/date_helper.rb +8 -0
  27. data/lib/redisrank/mixins/options.rb +41 -0
  28. data/lib/redisrank/mixins/synchronize.rb +52 -0
  29. data/lib/redisrank/model.rb +77 -0
  30. data/lib/redisrank/result.rb +18 -0
  31. data/lib/redisrank/scope.rb +18 -0
  32. data/lib/redisrank/summary.rb +90 -0
  33. data/lib/redisrank/version.rb +3 -0
  34. data/redisrank.gemspec +31 -0
  35. data/spec/Find Results +3349 -0
  36. data/spec/buffer_spec.rb +104 -0
  37. data/spec/collection_spec.rb +20 -0
  38. data/spec/connection_spec.rb +67 -0
  39. data/spec/core_ext/hash_spec.rb +26 -0
  40. data/spec/database_spec.rb +10 -0
  41. data/spec/date_spec.rb +95 -0
  42. data/spec/event_spec.rb +86 -0
  43. data/spec/finder/date_set_spec.rb +527 -0
  44. data/spec/finder_spec.rb +205 -0
  45. data/spec/key_spec.rb +129 -0
  46. data/spec/label_spec.rb +86 -0
  47. data/spec/model_helper.rb +31 -0
  48. data/spec/model_spec.rb +191 -0
  49. data/spec/options_spec.rb +36 -0
  50. data/spec/redis-test.conf +9 -0
  51. data/spec/result_spec.rb +23 -0
  52. data/spec/scope_spec.rb +27 -0
  53. data/spec/spec_helper.rb +18 -0
  54. data/spec/summary_spec.rb +177 -0
  55. data/spec/synchronize_spec.rb +125 -0
  56. data/spec/thread_safety_spec.rb +39 -0
  57. metadata +235 -0
@@ -0,0 +1,205 @@
1
+ require "spec_helper"
2
+
3
+ describe Redisrank::Finder do
4
+ include Redisrank::Database
5
+
6
+ before(:each) do
7
+ db.flushdb
8
+ @scope = "PageViews"
9
+ @label = "about_us"
10
+ @date = Time.now
11
+ @key = Redisrank::Key.new(@scope, @label, @date, {:depth => :day})
12
+ @stats = {"user_3" => 3, "user_2" => 2}
13
+ @stats2 = {"user_3" => 6, "user_2" => 2}
14
+ @two_hours_ago = 2.hours.ago
15
+ @one_hour_ago = 1.hour.ago
16
+ end
17
+
18
+ it "should initialize properly" do
19
+ options = {:scope => "PageViews", :label => "Label", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour}
20
+
21
+ finder = Redisrank::Finder.new
22
+ finder.send(:set_options, options)
23
+ finder.options[:scope].should be_a(Redisrank::Scope)
24
+ finder.options[:scope].to_s.should == options[:scope]
25
+ finder.options[:label].should be_a(Redisrank::Label)
26
+ finder.options[:label].to_s.should == options[:label]
27
+ finder.options.should == options.merge(:scope => finder.options[:scope], :label => finder.options[:label])
28
+
29
+ finder = Redisrank::Finder.scope("hello")
30
+ finder.options[:scope].to_s.should == "hello"
31
+ finder.scope.to_s.should == "hello"
32
+
33
+ finder = Redisrank::Finder.label("hello")
34
+ finder.options[:label].to_s.should == "hello"
35
+ finder.label.to_s.should == "hello"
36
+
37
+ finder = Redisrank::Finder.dates(@two_hours_ago, @one_hour_ago)
38
+ finder.options[:from].should == @two_hours_ago
39
+ finder.options[:till].should == @one_hour_ago
40
+
41
+ finder = Redisrank::Finder.from(@two_hours_ago)
42
+ finder.options[:from].should == @two_hours_ago
43
+ finder.from.should == @two_hours_ago
44
+
45
+ finder = Redisrank::Finder.till(@one_hour_ago)
46
+ finder.options[:till].should == @one_hour_ago
47
+ finder.till.should == @one_hour_ago
48
+
49
+ finder = Redisrank::Finder.depth(:hour)
50
+ finder.options[:depth].should == :hour
51
+ finder.depth.should == :hour
52
+
53
+ finder = Redisrank::Finder.interval(true)
54
+ expect(finder.options[:interval]).to be true
55
+ expect(finder.interval).to be true
56
+ finder = Redisrank::Finder.interval(false)
57
+ expect(finder.options[:interval]).to be false
58
+ expect(finder.interval).to be false
59
+ end
60
+
61
+ it "should fetch stats properly" do
62
+ first_stat, last_stat = create_example_stats
63
+
64
+ stats = Redisrank::Finder.find({:from => first_stat, :till => last_stat, :scope => @scope, :label => @label, :depth => :hour})
65
+ stats.from.should == first_stat
66
+ stats.till.should == last_stat
67
+ stats.depth.should == :hour
68
+
69
+ stats.rank.should == @stats2
70
+ end
71
+
72
+ it "should fetch data per unit when interval option is specified" do
73
+ first_stat, last_stat = create_example_stats
74
+
75
+ stats = Redisrank::Finder.find(:from => first_stat, :till => last_stat, :scope => @scope, :label => @label, :depth => :hour, :interval => :hour)
76
+ stats.from.should == first_stat
77
+ stats.till.should == last_stat
78
+ stats.rank.should == @stats2
79
+ stats[0].should == {}
80
+ stats[0].date.should == Time.parse("2010-05-14 12:00")
81
+ stats[1].should == @stats
82
+ stats[1].date.should == Time.parse("2010-05-14 13:00")
83
+ stats[2].should == @stats
84
+ stats[2].date.should == Time.parse("2010-05-14 14:00")
85
+ stats[3].should == @stats2
86
+ stats[3].date.should == Time.parse("2010-05-14 15:00")
87
+ stats[4].should == {}
88
+ stats[4].date.should == Time.parse("2010-05-14 16:00")
89
+ end
90
+
91
+ it "should return empty hash when attempting to fetch non-existent results" do
92
+ stats = Redisrank::Finder.find({:from => 3.hours.ago, :till => 2.hours.from_now, :scope => @scope, :label => @label, :depth => :hour})
93
+ stats.rank.should == {}
94
+ end
95
+
96
+ it "should throw error on invalid options" do
97
+ lambda { Redisrank::Finder.find(:from => 3.hours.ago) }.should raise_error(Redisrank::InvalidOptions)
98
+ end
99
+
100
+ describe "Grouping" do
101
+ before(:each) do
102
+ @options = {:scope => "PageViews", :label => "message/public", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour}
103
+ @finder = Redisrank::Finder.new(@options)
104
+ end
105
+
106
+ it "should return parent finder" do
107
+ @finder.instance_variable_get("@parent").should be_nil
108
+ @finder.parent.should be_a(Redisrank::Finder)
109
+ @finder.instance_variable_get("@parent").should_not be_nil
110
+ @finder.parent.options[:label].to_s.should == 'message'
111
+ @finder.label('message')
112
+ @finder.instance_variable_get("@parent").should be_nil
113
+ @finder.parent.should_not be_nil
114
+ @finder.parent.options[:label].should be_nil
115
+ @finder.parent.parent.should be_nil
116
+ end
117
+
118
+ it "should find children" do
119
+ Redisrank::Key.new("PageViews", "message/public/die").update_index
120
+ Redisrank::Key.new("PageViews", "message/public/live").update_index
121
+ Redisrank::Key.new("PageViews", "message/public/fester").update_index
122
+ members = db.smembers("#{@scope}#{Redisrank::LABEL_INDEX}message/public") # checking 'message/public'
123
+ @finder.children.first.should be_a(Redisrank::Finder)
124
+ subs = @finder.children.map { |f| f.options[:label].me }
125
+ expect(subs.count).to eq(3)
126
+ expect(subs).to include('die')
127
+ expect(subs).to include('live')
128
+ expect(subs).to include('fester')
129
+ end
130
+ end
131
+
132
+ describe "Lazy-Loading" do
133
+
134
+ before(:each) do
135
+ @first_stat, @last_stat = create_example_stats
136
+
137
+ @finder = Redisrank::Finder.new
138
+ @finder.from(@first_stat).till(@last_stat).scope(@scope).label(@label).depth(:hour)
139
+
140
+ @match = [{},
141
+ {"user_3"=>3.0, "user_2"=>2.0},
142
+ {"user_3"=>3.0, "user_2"=>2.0},
143
+ {"user_3"=>6.0, "user_2"=>2.0}, {}]
144
+ end
145
+
146
+ it "should lazy-load" do
147
+ @finder.instance_variable_get("@result").should be_nil
148
+ stats = @finder.all
149
+ @finder.instance_variable_get("@result").should_not be_nil
150
+
151
+ stats.should == @finder.find # find method directly fetches results
152
+ stats.rank.should == @finder.rank
153
+ stats.rank.should == @stats2
154
+ stats.rank.from.should == @first_stat
155
+ stats.rank.till.should == @last_stat
156
+
157
+ @finder.all.object_id.should == stats.object_id
158
+ @finder.from(@first_stat + 2.hours)
159
+ @finder.instance_variable_get("@result").should be_nil
160
+ @finder.all.object_id.should_not == stats.object_id
161
+ stats = @finder.all
162
+ stats.rank.should == @stats2
163
+ end
164
+
165
+ it "should handle #map" do
166
+ @finder.interval(:hour)
167
+ @finder.map { |r| r }.should == @match
168
+ end
169
+
170
+ it "should handle #each" do
171
+ @finder.interval(:hour)
172
+
173
+ res = []
174
+ @finder.each { |r| res << r }
175
+ res.should == @match
176
+ end
177
+
178
+ it "should handle #each_with_index" do
179
+ @finder.interval(:hour)
180
+
181
+ res = {}
182
+ match = {}
183
+ @finder.each_with_index { |r, i| res[i] = r }
184
+ @match.each_with_index { |r, i| match[i] = r }
185
+ res.should == match
186
+ end
187
+
188
+ end # "Lazy-Loading"
189
+
190
+
191
+ # helper methods
192
+
193
+ def create_example_stats
194
+ key = Redisrank::Key.new(@scope, @label, (first = Time.parse("2010-05-14 13:43")))
195
+ Redisrank::Summary.send(:update_fields, key, @stats, :hour)
196
+ key = Redisrank::Key.new(@scope, @label, Time.parse("2010-05-14 13:53"))
197
+ Redisrank::Summary.send(:update_fields, key, @stats, :hour)
198
+ key = Redisrank::Key.new(@scope, @label, Time.parse("2010-05-14 14:52"))
199
+ Redisrank::Summary.send(:update_fields, key, @stats, :hour)
200
+ key = Redisrank::Key.new(@scope, @label, (last = Time.parse("2010-05-14 15:02")))
201
+ Redisrank::Summary.send(:update_fields, key, @stats2, :hour)
202
+ [first - 1.hour, last + 1.hour]
203
+ end
204
+
205
+ end
@@ -0,0 +1,129 @@
1
+ require "spec_helper"
2
+
3
+ describe Redisrank::Key do
4
+ include Redisrank::Database
5
+
6
+ before(:each) do
7
+ db.flushdb
8
+ @scope = "PageViews"
9
+ @label = "about_us"
10
+ @label_hash = Digest::SHA1.hexdigest(@label)
11
+ @date = Time.now
12
+ @key = Redisrank::Key.new(@scope, @label, @date, {:depth => :hour})
13
+ end
14
+
15
+ it "should initialize properly" do
16
+ @key.scope.to_s.should == @scope
17
+ @key.label.to_s.should == @label
18
+ @key.label_hash.should == @label_hash
19
+ @key.groups.map { |k| k.instance_variable_get("@label") }.should == @key.instance_variable_get("@label").groups
20
+ @key.date.should be_instance_of(Redisrank::Date)
21
+ @key.date.to_time.to_s.should == @date.to_s
22
+ end
23
+
24
+ it "should convert to string properly" do
25
+ @key.to_s.should == "#{@scope}/#{@label}:#{@key.date.to_s(:hour)}"
26
+ props = [:year, :month, :day, :hour, :min, :sec]
27
+ props.each do
28
+ @key.to_s(props.last).should == "#{@scope}/#{@label}:#{@key.date.to_s(props.last)}"
29
+ props.pop
30
+ end
31
+ key = Redisrank::Key.new(@scope, nil, @date, {:depth => :hour})
32
+ key.to_s.should == "#{@scope}:#{key.date.to_s(:hour)}"
33
+ end
34
+
35
+ it "should abide to hashed_label option" do
36
+ @key = Redisrank::Key.new(@scope, @label, @date, {:depth => :hour, :hashed_label => true})
37
+ @key.to_s.should == "#{@scope}/#{@label_hash}:#{@key.date.to_s(:hour)}"
38
+ @key = Redisrank::Key.new(@scope, @label, @date, {:depth => :hour, :hashed_label => false})
39
+ @key.to_s.should == "#{@scope}/#{@label}:#{@key.date.to_s(:hour)}"
40
+ end
41
+
42
+ it "should have default depth option" do
43
+ @key = Redisrank::Key.new(@scope, @label, @date)
44
+ @key.depth.should == :hour
45
+ end
46
+
47
+ it "should allow changing attributes" do
48
+ # scope
49
+ @key.scope.to_s.should == @scope
50
+ @scope = "VisitorCount"
51
+ @key.scope = @scope
52
+ @key.scope.to_s.should == @scope
53
+ # date
54
+ @key.date.to_time.to_s.should == @date.to_s
55
+ @date = Time.now
56
+ @key.date = @date
57
+ @key.date.to_time.to_s.should == @date.to_s
58
+ # label
59
+ @key.label.to_s.should == @label
60
+ @key.label_hash == @label_hash
61
+ @label = "contact_us"
62
+ @label_hash = Digest::SHA1.hexdigest(@label)
63
+ @key.label = @label
64
+ @key.label.to_s.should == @label
65
+ @key.label_hash == @label_hash
66
+ end
67
+
68
+ describe "Grouping" do
69
+ before(:each) do
70
+ @label = "message/public/offensive"
71
+ @key = Redisrank::Key.new(@scope, @label, @date, {:depth => :hour})
72
+ end
73
+
74
+ it "should create a group of keys from label group" do
75
+ label = 'message/public/offensive'
76
+ result = [ "message/public/offensive",
77
+ "message/public",
78
+ "message" ]
79
+
80
+ key = Redisrank::Key.new(@scope, label, @date, {:depth => :hour})
81
+
82
+ key.groups.map { |k| k.label.to_s }.should == result
83
+ end
84
+
85
+ it "should know it's parent" do
86
+ @key.parent.should be_a(Redisrank::Key)
87
+ @key.parent.label.to_s.should == 'message/public'
88
+ Redisrank::Key.new(@scope, 'hello', @date).parent.should be_nil
89
+ end
90
+
91
+ it "should update label index and return children" do
92
+ db.smembers("#{@scope}#{Redisrank::LABEL_INDEX}#{@key.label.parent}").should == []
93
+ @key.children.count.should be(0)
94
+
95
+ @key.update_index # indexing 'message/publish/offensive'
96
+ Redisrank::Key.new("PageViews", "message/public/die").update_index # indexing 'message/publish/die'
97
+ Redisrank::Key.new("PageViews", "message/public/live").update_index # indexing 'message/publish/live'
98
+
99
+ members = db.smembers("#{@scope}#{Redisrank::LABEL_INDEX}#{@key.label.parent}") # checking 'message/public'
100
+ members.count.should be(3)
101
+ members.should include('offensive')
102
+ members.should include('live')
103
+ members.should include('die')
104
+
105
+ key = @key.parent
106
+ key.children.first.should be_a(Redisrank::Key)
107
+ key.children.count.should be(3)
108
+ key.children.map { |k| k.label.me }.should == members
109
+
110
+ members = db.smembers("#{@scope}#{Redisrank::LABEL_INDEX}#{key.label.parent}") # checking 'message'
111
+ members.count.should be(1)
112
+ members.should include('public')
113
+
114
+ key = key.parent
115
+ key.children.count.should be(1)
116
+ key.children.map { |k| k.label.me }.should == members
117
+
118
+ members = db.smembers("#{@scope}#{Redisrank::LABEL_INDEX}") # checking ''
119
+ members.count.should be(1)
120
+ members.should include('message')
121
+
122
+ key.parent.should be_nil
123
+ key = Redisrank::Key.new("PageViews")
124
+ key.children.count.should be(1)
125
+ key.children.map { |k| k.label.me }.should include('message')
126
+ end
127
+ end
128
+
129
+ end
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+
3
+ describe Redisrank::Label do
4
+ include Redisrank::Database
5
+
6
+ before(:each) do
7
+ db.flushdb
8
+ @name = "about_us"
9
+ @label = Redisrank::Label.new(@name)
10
+ end
11
+
12
+ it "should initialize properly and SHA1 hash the label name" do
13
+ @label.name.should == @name
14
+ @label.hash.should == Digest::SHA1.hexdigest(@name)
15
+ end
16
+
17
+ it "should store a label hash lookup key" do
18
+ label = Redisrank::Label.new(@name, {:hashed_label => true}).save
19
+ label.saved?.should be(true)
20
+ db.hget(Redisrank::KEY_LABELS, label.hash).should == @name
21
+
22
+ name = "contact_us"
23
+ label = Redisrank::Label.create(name, {:hashed_label => true})
24
+ label.saved?.should be(true)
25
+ db.hget(Redisrank::KEY_LABELS, label.hash).should == name
26
+ end
27
+
28
+ it "should join labels" do
29
+ include Redisrank
30
+ label = Redisrank::Label.join('email', 'message', 'public')
31
+ label.should be_a(Redisrank::Label)
32
+ label.to_s.should == 'email/message/public'
33
+ label = Redisrank::Label.join(Redisrank::Label.new('email'), Redisrank::Label.new('message'), Redisrank::Label.new('public'))
34
+ label.should be_a(Redisrank::Label)
35
+ label.to_s.should == 'email/message/public'
36
+ label = Redisrank::Label.join('email', '', 'message', nil, 'public')
37
+ label.should be_a(Redisrank::Label)
38
+ label.to_s.should == 'email/message/public'
39
+ end
40
+
41
+ it "should allow you to use a different group separator" do
42
+ include Redisrank
43
+ Redisrank.group_separator = '|'
44
+ label = Redisrank::Label.join('email', 'message', 'public')
45
+ label.should be_a(Redisrank::Label)
46
+ label.to_s.should == 'email|message|public'
47
+ label = Redisrank::Label.join(Redisrank::Label.new('email'), Redisrank::Label.new('message'), Redisrank::Label.new('public'))
48
+ label.should be_a(Redisrank::Label)
49
+ label.to_s.should == 'email|message|public'
50
+ label = Redisrank::Label.join('email', '', 'message', nil, 'public')
51
+ label.should be_a(Redisrank::Label)
52
+ label.to_s.should == 'email|message|public'
53
+ Redisrank.group_separator = Redisrank::GROUP_SEPARATOR
54
+ end
55
+
56
+ describe "Grouping" do
57
+ before(:each) do
58
+ @name = "message/public/offensive"
59
+ @label = Redisrank::Label.new(@name)
60
+ end
61
+
62
+ it "should know it's parent label group" do
63
+ @label.parent.to_s.should == 'message/public'
64
+ Redisrank::Label.new('hello').parent.should be_nil
65
+ end
66
+
67
+ it "should separate label names into groups" do
68
+ @label.name.should == @name
69
+ @label.groups.map { |l| l.to_s }.should == [ "message/public/offensive",
70
+ "message/public",
71
+ "message" ]
72
+
73
+ @name = "/message/public/"
74
+ @label = Redisrank::Label.new(@name)
75
+ @label.name.should == @name
76
+ @label.groups.map { |l| l.to_s }.should == [ "message/public",
77
+ "message" ]
78
+
79
+ @name = "message"
80
+ @label = Redisrank::Label.new(@name)
81
+ @label.name.should == @name
82
+ @label.groups.map { |l| l.to_s }.should == [ "message" ]
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,31 @@
1
+ require "redisrank"
2
+
3
+ class ModelHelper1
4
+ include Redisrank::Model
5
+
6
+
7
+ end
8
+
9
+ class ModelHelper2
10
+ include Redisrank::Model
11
+
12
+ depth :day
13
+ store_event true
14
+ hashed_label true
15
+
16
+ end
17
+
18
+ class ModelHelper3
19
+ include Redisrank::Model
20
+
21
+ connect_to :port => 8379, :db => 14
22
+
23
+ end
24
+
25
+ class ModelHelper4
26
+ include Redisrank::Model
27
+
28
+ scope "FancyHelper"
29
+ expire :hour => 24*3600
30
+
31
+ end