redistat 0.1.0 → 0.1.1
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/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/redistat/finder.rb +123 -80
- data/lib/redistat/model.rb +30 -18
- data/lib/redistat/version.rb +1 -1
- data/spec/finder_spec.rb +64 -1
- data/spec/model_spec.rb +26 -14
- metadata +4 -4
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -79,7 +79,7 @@ When retrieving statistics for a given date range, Redistat figures out how to d
|
|
79
79
|
|
80
80
|
## License and Copyright
|
81
81
|
|
82
|
-
Copyright (c)
|
82
|
+
Copyright (c) 2011 Jim Myhrberg.
|
83
83
|
|
84
84
|
Permission is hereby granted, free of charge, to any person obtaining
|
85
85
|
a copy of this software and associated documentation files (the
|
data/lib/redistat/finder.rb
CHANGED
@@ -2,23 +2,119 @@ module Redistat
|
|
2
2
|
class Finder
|
3
3
|
include Database
|
4
4
|
|
5
|
+
class << self
|
6
|
+
def find(*args)
|
7
|
+
new.find(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def scope(scope)
|
11
|
+
new.scope(scope)
|
12
|
+
end
|
13
|
+
|
14
|
+
def label(label)
|
15
|
+
new.label(label)
|
16
|
+
end
|
17
|
+
|
18
|
+
def dates(from, till)
|
19
|
+
new.dates(from, till)
|
20
|
+
end
|
21
|
+
alias :date :dates
|
22
|
+
|
23
|
+
def from(date)
|
24
|
+
new.from(date)
|
25
|
+
end
|
26
|
+
|
27
|
+
def till(date)
|
28
|
+
new.till(date)
|
29
|
+
end
|
30
|
+
alias :untill :till
|
31
|
+
|
32
|
+
def depth(unit)
|
33
|
+
new.depth(unit)
|
34
|
+
end
|
35
|
+
|
36
|
+
def interval(unit)
|
37
|
+
new.interval(unit)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
5
41
|
attr_reader :options
|
6
42
|
|
7
43
|
def initialize(options = {})
|
8
44
|
@options = options
|
9
45
|
end
|
10
46
|
|
11
|
-
def
|
12
|
-
|
47
|
+
def all(reload = false)
|
48
|
+
@result = nil if reload
|
49
|
+
@result ||= find
|
13
50
|
end
|
14
51
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
52
|
+
def total
|
53
|
+
all.total
|
54
|
+
end
|
55
|
+
|
56
|
+
def each(&block)
|
57
|
+
all.each(&block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def map(&block)
|
61
|
+
all.map(&block)
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_with_index(&block)
|
65
|
+
all.each_with_index(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def connection_ref(ref)
|
69
|
+
reset! if @options[:connection_ref] != ref
|
70
|
+
@options[:connection_ref] = ref
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def scope(scope)
|
75
|
+
reset! if @options[:scope] != scope
|
76
|
+
@options[:scope] = scope
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def label(label)
|
81
|
+
reset! if @options[:label] != label
|
82
|
+
@options[:label] = label
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def dates(from, till)
|
87
|
+
from(from).till(till)
|
88
|
+
end
|
89
|
+
alias :date :dates
|
90
|
+
|
91
|
+
def from(date)
|
92
|
+
reset! if @options[:from] != date
|
93
|
+
@options[:from] = date
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def till(date)
|
98
|
+
reset! if @options[:till] != date
|
99
|
+
@options[:till] = date
|
100
|
+
self
|
101
|
+
end
|
102
|
+
alias :until :till
|
103
|
+
|
104
|
+
def depth(unit)
|
105
|
+
reset! if @options[:depth] != unit
|
106
|
+
@options[:depth] = unit
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def interval(unit)
|
111
|
+
reset! if @options[:interval] != unit
|
112
|
+
@options[:interval] = unit
|
113
|
+
self
|
18
114
|
end
|
19
115
|
|
20
116
|
def find(options = {})
|
21
|
-
|
117
|
+
set_options(options)
|
22
118
|
raise InvalidOptions.new if !valid_options?
|
23
119
|
if @options[:interval].nil? || !@options[:interval]
|
24
120
|
find_by_magic
|
@@ -27,8 +123,17 @@ module Redistat
|
|
27
123
|
end
|
28
124
|
end
|
29
125
|
|
126
|
+
private
|
127
|
+
|
128
|
+
def set_options(opts = {})
|
129
|
+
opts = opts.clone
|
130
|
+
opts.each do |key, value|
|
131
|
+
self.send(key, opts.delete(key)) if self.respond_to?(key)
|
132
|
+
end
|
133
|
+
@options.merge!(opts)
|
134
|
+
end
|
135
|
+
|
30
136
|
def find_by_interval(options = {})
|
31
|
-
@options.merge!(options)
|
32
137
|
raise InvalidOptions.new if !valid_options?
|
33
138
|
key = build_key
|
34
139
|
col = Collection.new(@options)
|
@@ -48,7 +153,6 @@ module Redistat
|
|
48
153
|
end
|
49
154
|
|
50
155
|
def find_by_magic(options = {})
|
51
|
-
@options.merge!(options)
|
52
156
|
raise InvalidOptions.new if !valid_options?
|
53
157
|
key = Key.new(@options[:scope], @options[:label])
|
54
158
|
col = Collection.new(@options)
|
@@ -64,6 +168,15 @@ module Redistat
|
|
64
168
|
end
|
65
169
|
col
|
66
170
|
end
|
171
|
+
|
172
|
+
def reset!
|
173
|
+
@result = nil
|
174
|
+
end
|
175
|
+
|
176
|
+
def valid_options?
|
177
|
+
return true if !@options[:scope].blank? && !@options[:label].blank? && !@options[:from].blank? && !@options[:till].blank?
|
178
|
+
false
|
179
|
+
end
|
67
180
|
|
68
181
|
def build_date_sets
|
69
182
|
Finder::DateSet.new(@options[:from], @options[:till], @options[:depth], @options[:interval])
|
@@ -91,78 +204,8 @@ module Redistat
|
|
91
204
|
sum
|
92
205
|
end
|
93
206
|
|
94
|
-
|
95
|
-
|
96
|
-
def find(*args)
|
97
|
-
new.find(*args)
|
98
|
-
end
|
99
|
-
|
100
|
-
def scope(scope)
|
101
|
-
new.scope(scope)
|
102
|
-
end
|
103
|
-
|
104
|
-
def label(label)
|
105
|
-
new.label(label)
|
106
|
-
end
|
107
|
-
|
108
|
-
def dates(from, till)
|
109
|
-
new.dates(from, till)
|
110
|
-
end
|
111
|
-
alias :date :dates
|
112
|
-
|
113
|
-
def from(date)
|
114
|
-
new.from(date)
|
115
|
-
end
|
116
|
-
|
117
|
-
def till(date)
|
118
|
-
new.till(date)
|
119
|
-
end
|
120
|
-
alias :untill :till
|
121
|
-
|
122
|
-
def depth(unit)
|
123
|
-
new.depth(unit)
|
124
|
-
end
|
125
|
-
|
126
|
-
def interval(unit)
|
127
|
-
new.interval(unit)
|
128
|
-
end
|
129
|
-
|
130
|
-
end
|
131
|
-
|
132
|
-
def scope(scope)
|
133
|
-
@options[:scope] = scope
|
134
|
-
self
|
135
|
-
end
|
136
|
-
|
137
|
-
def label(label)
|
138
|
-
@options[:label] = label
|
139
|
-
self
|
140
|
-
end
|
141
|
-
|
142
|
-
def dates(from, till)
|
143
|
-
from(from).till(till)
|
144
|
-
end
|
145
|
-
alias :date :dates
|
146
|
-
|
147
|
-
def from(date)
|
148
|
-
@options[:from] = date
|
149
|
-
self
|
150
|
-
end
|
151
|
-
|
152
|
-
def till(date)
|
153
|
-
@options[:till] = date
|
154
|
-
self
|
155
|
-
end
|
156
|
-
alias :until :till
|
157
|
-
|
158
|
-
def depth(unit)
|
159
|
-
@options[:depth] = unit
|
160
|
-
self
|
161
|
-
end
|
162
|
-
|
163
|
-
def interval(unit)
|
164
|
-
@options[:interval] = unit
|
165
|
-
self
|
207
|
+
def db
|
208
|
+
super(@options[:connection_ref])
|
166
209
|
end
|
167
210
|
|
168
211
|
end
|
data/lib/redistat/model.rb
CHANGED
@@ -6,31 +6,36 @@ module Redistat
|
|
6
6
|
base.extend(self)
|
7
7
|
end
|
8
8
|
|
9
|
+
#
|
10
|
+
# statistics store/fetch methods
|
11
|
+
#
|
12
|
+
|
9
13
|
def store(label, stats = {}, date = nil, meta = {}, opts = {})
|
10
14
|
Event.new(name, label, date, stats, options.merge(opts), meta).save
|
11
15
|
end
|
12
16
|
alias :event :store
|
17
|
+
|
18
|
+
def fetch(label, from, till, opts = {})
|
19
|
+
find(label, from, till, opts).all
|
20
|
+
end
|
21
|
+
alias :lookup :fetch
|
22
|
+
|
23
|
+
def find(label, from, till, opts = {})
|
24
|
+
Finder.new( { :scope => name,
|
25
|
+
:label => label,
|
26
|
+
:from => from,
|
27
|
+
:till => till }.merge(options.merge(opts)) )
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# options methods
|
32
|
+
#
|
13
33
|
|
14
34
|
def connect_to(opts = {})
|
15
35
|
Connection.create(opts.merge(:ref => name))
|
16
36
|
options[:connection_ref] = name
|
17
37
|
end
|
18
38
|
|
19
|
-
def connection
|
20
|
-
db(options[:connection_ref])
|
21
|
-
end
|
22
|
-
alias :redis :connection
|
23
|
-
|
24
|
-
def fetch(label, from, till, opts = {})
|
25
|
-
Finder.find({
|
26
|
-
:scope => name,
|
27
|
-
:label => label,
|
28
|
-
:from => from,
|
29
|
-
:till => till
|
30
|
-
}.merge(options.merge(opts)))
|
31
|
-
end
|
32
|
-
alias :lookup :fetch
|
33
|
-
|
34
39
|
def hashed_label(boolean = nil)
|
35
40
|
if !boolean.nil?
|
36
41
|
options[:hashed_label] = boolean
|
@@ -64,15 +69,22 @@ module Redistat
|
|
64
69
|
end
|
65
70
|
end
|
66
71
|
|
72
|
+
#
|
73
|
+
# resource access methods
|
74
|
+
#
|
75
|
+
|
76
|
+
def connection
|
77
|
+
db(options[:connection_ref])
|
78
|
+
end
|
79
|
+
alias :redis :connection
|
80
|
+
|
67
81
|
def options
|
68
82
|
@options ||= {}
|
69
83
|
end
|
70
84
|
|
71
|
-
private
|
72
|
-
|
73
85
|
def name
|
74
86
|
options[:class_name] || (@name ||= self.to_s)
|
75
87
|
end
|
76
|
-
|
88
|
+
|
77
89
|
end
|
78
90
|
end
|
data/lib/redistat/version.rb
CHANGED
data/spec/finder_spec.rb
CHANGED
@@ -19,6 +19,10 @@ describe Redistat::Finder do
|
|
19
19
|
|
20
20
|
finder = Redistat::Finder.new(options)
|
21
21
|
finder.options.should == options
|
22
|
+
|
23
|
+
finder = Redistat::Finder.new
|
24
|
+
finder.send(:set_options, options)
|
25
|
+
finder.options.should == options
|
22
26
|
|
23
27
|
finder = Redistat::Finder.dates(two_hours_ago, one_hour_ago).scope("PageViews").label("Label").depth(:hour).interval(:hour)
|
24
28
|
finder.options.should == options
|
@@ -85,6 +89,65 @@ describe Redistat::Finder do
|
|
85
89
|
lambda { Redistat::Finder.find(:from => 3.hours.ago) }.should raise_error(Redistat::InvalidOptions)
|
86
90
|
end
|
87
91
|
|
92
|
+
describe "Lazy-Loading" do
|
93
|
+
|
94
|
+
before(:each) do
|
95
|
+
@first_stat, @last_stat = create_example_stats
|
96
|
+
|
97
|
+
@finder = Redistat::Finder.new
|
98
|
+
@finder.from(@first_stat).till(@last_stat).scope(@scope).label(@label).depth(:hour)
|
99
|
+
|
100
|
+
@match = [{}, {"visitors"=>"4", "views"=>"6"},
|
101
|
+
{"visitors"=>"2", "views"=>"3"},
|
102
|
+
{"visitors"=>"2", "views"=>"3"}, {}]
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should lazy-load" do
|
106
|
+
|
107
|
+
@finder.instance_variable_get("@result").should be_nil
|
108
|
+
stats = @finder.all
|
109
|
+
@finder.instance_variable_get("@result").should_not be_nil
|
110
|
+
|
111
|
+
stats.should == @finder.find # find method directly fetches results
|
112
|
+
stats.total.should == @finder.total
|
113
|
+
stats.total.should == { "views" => 12, "visitors" => 8 }
|
114
|
+
stats.total.from.should == @first_stat
|
115
|
+
stats.total.till.should == @last_stat
|
116
|
+
stats.first.should == stats.total
|
117
|
+
|
118
|
+
@finder.all.object_id.should == stats.object_id
|
119
|
+
@finder.from(@first_stat + 2.hours)
|
120
|
+
@finder.instance_variable_get("@result").should be_nil
|
121
|
+
@finder.all.object_id.should_not == stats.object_id
|
122
|
+
stats = @finder.all
|
123
|
+
stats.total.should == { "views" => 6, "visitors" => 4 }
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should handle #map" do
|
127
|
+
@finder.interval(:hour)
|
128
|
+
@finder.map { |r| r }.should == @match
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should handle #each" do
|
132
|
+
@finder.interval(:hour)
|
133
|
+
|
134
|
+
res = []
|
135
|
+
@finder.each { |r| res << r }
|
136
|
+
res.should == @match
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should handle #each_with_index" do
|
140
|
+
@finder.interval(:hour)
|
141
|
+
|
142
|
+
res = {}
|
143
|
+
match = {}
|
144
|
+
@finder.each_with_index { |r, i| res[i] = r }
|
145
|
+
@match.each_with_index { |r, i| match[i] = r }
|
146
|
+
res.should == match
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
88
151
|
|
89
152
|
# helper methods
|
90
153
|
|
@@ -93,7 +156,7 @@ describe Redistat::Finder do
|
|
93
156
|
Redistat::Summary.update(key, @stats, :hour)
|
94
157
|
key = Redistat::Key.new(@scope, @label, Time.parse("2010-05-14 13:53"))
|
95
158
|
Redistat::Summary.update(key, @stats, :hour)
|
96
|
-
key = Redistat::Key.new(@scope, @label, Time.parse("2010-05-14 14:
|
159
|
+
key = Redistat::Key.new(@scope, @label, Time.parse("2010-05-14 14:52"))
|
97
160
|
Redistat::Summary.update(key, @stats, :hour)
|
98
161
|
key = Redistat::Key.new(@scope, @label, (last = Time.parse("2010-05-14 15:02")))
|
99
162
|
Redistat::Summary.update(key, @stats, :hour)
|
data/spec/model_spec.rb
CHANGED
@@ -5,6 +5,7 @@ describe Redistat::Model do
|
|
5
5
|
include Redistat::Database
|
6
6
|
|
7
7
|
before(:each) do
|
8
|
+
@time = Time.utc(2010, 8, 28, 12, 0, 0)
|
8
9
|
ModelHelper1.redis.flushdb
|
9
10
|
ModelHelper2.redis.flushdb
|
10
11
|
ModelHelper3.redis.flushdb
|
@@ -16,6 +17,17 @@ describe Redistat::Model do
|
|
16
17
|
ModelHelper2.send(:name).should == "ModelHelper2"
|
17
18
|
end
|
18
19
|
|
20
|
+
it "should return a Finder" do
|
21
|
+
two_hours_ago = 2.hours.ago
|
22
|
+
one_hour_ago = 1.hour.ago
|
23
|
+
finder = ModelHelper1.find('label', two_hours_ago, one_hour_ago)
|
24
|
+
finder.should be_a(Redistat::Finder)
|
25
|
+
finder.options[:scope].should == 'ModelHelper1'
|
26
|
+
finder.options[:label].should == 'label'
|
27
|
+
finder.options[:from].should == two_hours_ago
|
28
|
+
finder.options[:till].should == one_hour_ago
|
29
|
+
end
|
30
|
+
|
19
31
|
it "should listen to model-defined options" do
|
20
32
|
ModelHelper2.depth.should == :day
|
21
33
|
ModelHelper2.store_event.should == true
|
@@ -43,28 +55,28 @@ describe Redistat::Model do
|
|
43
55
|
end
|
44
56
|
|
45
57
|
it "should store and fetch stats" do
|
46
|
-
ModelHelper1.store("sheep.black", {:count => 6, :weight => 461}, 4
|
47
|
-
ModelHelper1.store("sheep.black", {:count => 2, :weight => 156})
|
58
|
+
ModelHelper1.store("sheep.black", {:count => 6, :weight => 461}, @time.hours_ago(4))
|
59
|
+
ModelHelper1.store("sheep.black", {:count => 2, :weight => 156}, @time)
|
48
60
|
|
49
|
-
stats = ModelHelper1.fetch("sheep.black", 2
|
61
|
+
stats = ModelHelper1.fetch("sheep.black", @time.hours_ago(2), @time.hours_since(1))
|
50
62
|
stats.total["count"].should == 2
|
51
63
|
stats.total["weight"].should == 156
|
52
64
|
stats.first.should == stats.total
|
53
65
|
|
54
|
-
stats = ModelHelper1.fetch("sheep.black", 5
|
66
|
+
stats = ModelHelper1.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1))
|
55
67
|
stats.total[:count].should == 8
|
56
68
|
stats.total[:weight].should == 617
|
57
69
|
stats.first.should == stats.total
|
58
70
|
|
59
|
-
ModelHelper1.store("sheep.white", {:count => 5, :weight => 393}, 4
|
60
|
-
ModelHelper1.store("sheep.white", {:count => 4, :weight => 316})
|
71
|
+
ModelHelper1.store("sheep.white", {:count => 5, :weight => 393}, @time.hours_ago(4))
|
72
|
+
ModelHelper1.store("sheep.white", {:count => 4, :weight => 316}, @time)
|
61
73
|
|
62
|
-
stats = ModelHelper1.fetch("sheep.white", 2
|
74
|
+
stats = ModelHelper1.fetch("sheep.white", @time.hours_ago(2), @time.hours_since(1))
|
63
75
|
stats.total[:count].should == 4
|
64
76
|
stats.total[:weight].should == 316
|
65
77
|
stats.first.should == stats.total
|
66
78
|
|
67
|
-
stats = ModelHelper1.fetch("sheep.white", 5
|
79
|
+
stats = ModelHelper1.fetch("sheep.white", @time.hours_ago(5), @time.hours_since(1))
|
68
80
|
stats.total[:count].should == 9
|
69
81
|
stats.total[:weight].should == 709
|
70
82
|
stats.first.should == stats.total
|
@@ -73,31 +85,31 @@ describe Redistat::Model do
|
|
73
85
|
it "should connect to different Redis servers on a per-model basis" do
|
74
86
|
ModelHelper3.redis.client.db.should == 14
|
75
87
|
|
76
|
-
ModelHelper3.store("sheep.black", {:count => 6, :weight => 461}, 4
|
77
|
-
ModelHelper3.store("sheep.black", {:count => 2, :weight => 156})
|
88
|
+
ModelHelper3.store("sheep.black", {:count => 6, :weight => 461}, @time.hours_ago(4))
|
89
|
+
ModelHelper3.store("sheep.black", {:count => 2, :weight => 156}, @time)
|
78
90
|
|
79
91
|
db.keys("*").should be_empty
|
80
92
|
ModelHelper1.redis.keys("*").should be_empty
|
81
93
|
db("ModelHelper3").keys("*").should have(5).items
|
82
94
|
ModelHelper3.redis.keys("*").should have(5).items
|
83
95
|
|
84
|
-
stats = ModelHelper3.fetch("sheep.black", 2
|
96
|
+
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(2), @time.hours_since(1))
|
85
97
|
stats.total["count"].should == 2
|
86
98
|
stats.total["weight"].should == 156
|
87
|
-
stats = ModelHelper3.fetch("sheep.black", 5
|
99
|
+
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1))
|
88
100
|
stats.total[:count].should == 8
|
89
101
|
stats.total[:weight].should == 617
|
90
102
|
|
91
103
|
ModelHelper3.connect_to(:port => 8379, :db => 13)
|
92
104
|
ModelHelper3.redis.client.db.should == 13
|
93
105
|
|
94
|
-
stats = ModelHelper3.fetch("sheep.black", 5
|
106
|
+
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1))
|
95
107
|
stats.total.should == {}
|
96
108
|
|
97
109
|
ModelHelper3.connect_to(:port => 8379, :db => 14)
|
98
110
|
ModelHelper3.redis.client.db.should == 14
|
99
111
|
|
100
|
-
stats = ModelHelper3.fetch("sheep.black", 5
|
112
|
+
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1))
|
101
113
|
stats.total[:count].should == 8
|
102
114
|
stats.total[:weight].should == 617
|
103
115
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redistat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jim Myhrberg
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-03-
|
18
|
+
date: 2011-03-09 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|