redistat 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Jim Myhrberg.
1
+ Copyright (c) 2011 Jim Myhrberg.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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) 2010 Jim Myhrberg.
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
@@ -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 db
12
- super(@options[:connection_ref])
47
+ def all(reload = false)
48
+ @result = nil if reload
49
+ @result ||= find
13
50
  end
14
51
 
15
- def valid_options?
16
- return true if !@options[:scope].blank? && !@options[:label].blank? && !@options[:from].blank? && !@options[:till].blank?
17
- false
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
- @options.merge!(options)
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
- class << self
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Redistat
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -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:32"))
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)
@@ -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.hours.ago)
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.hours.ago, 1.hour.from_now)
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.hours.ago, 1.hour.from_now)
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.hours.ago)
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.hours.ago, 1.hour.from_now)
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.hours.ago, 1.hour.from_now)
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.hours.ago)
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.hours.ago, 1.hour.from_now)
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.hours.ago, 1.hour.from_now)
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.hours.ago, 1.hour.from_now)
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.hours.ago, 1.hour.from_now)
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: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 0
10
- version: 0.1.0
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-04 00:00:00 +00:00
18
+ date: 2011-03-09 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency