redistat 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -19,6 +19,7 @@ rdoc
19
19
  pkg/*
20
20
  *.gem
21
21
  .bundle
22
+ Gemfile.lock
22
23
 
23
24
  ## PROJECT::SPECIFIC
24
25
  .yardoc/*
data/README.md CHANGED
@@ -14,7 +14,7 @@ Redis fits perfectly with all of these requirements. It has atomic operations li
14
14
 
15
15
  gem install redistat
16
16
 
17
- If you are using Ruby 1.8.x, it's recommended you also install the `system_timer` gem, as the Redis gem will otherwise complain.
17
+ If you are using Ruby 1.8.x, it's recommended you also install the `SystemTimer` gem, as the Redis gem will otherwise complain.
18
18
 
19
19
  ## Usage
20
20
 
data/Rakefile CHANGED
@@ -14,6 +14,7 @@ end
14
14
  RSpec::Core::RakeTask.new(:rcov) do |spec|
15
15
  spec.pattern = 'spec/**/*_spec.rb'
16
16
  spec.rcov = true
17
+ spec.rcov_opts = ['--exclude', 'spec']
17
18
  end
18
19
 
19
20
  task :default => [:start, :spec, :stop]
data/lib/redistat.rb CHANGED
@@ -36,6 +36,7 @@ module Redistat
36
36
  KEY_EVENT = ".event:"
37
37
  KEY_LEBELS = "Redistat.lables:"
38
38
  KEY_EVENT_IDS = ".event_ids"
39
+ GROUP_SEPARATOR = "/"
39
40
 
40
41
  class InvalidOptions < ArgumentError; end
41
42
  class RedisServerIsTooOld < Exception; end
@@ -31,7 +31,10 @@ module Redistat
31
31
  end
32
32
 
33
33
  def default_options
34
- { :depth => :hour, :store_event => false, :connection_ref => nil }
34
+ { :depth => :hour,
35
+ :store_event => false,
36
+ :connection_ref => nil,
37
+ :enable_grouping => true }
35
38
  end
36
39
 
37
40
  def new?
@@ -72,7 +75,7 @@ module Redistat
72
75
 
73
76
  def save
74
77
  return false if !self.new?
75
- Summary.update_all(@key, @stats, depth_limit, @connection_ref)
78
+ Summary.update_all(@key, @stats, depth_limit, @connection_ref, @options[:enable_grouping])
76
79
  if @options[:store_event]
77
80
  @id = self.next_id
78
81
  db.hmset("#{self.scope}#{KEY_EVENT}#{@id}",
data/lib/redistat/key.rb CHANGED
@@ -39,10 +39,20 @@ module Redistat
39
39
  @label.hash
40
40
  end
41
41
 
42
+ def label_groups
43
+ @label.groups
44
+ end
45
+
42
46
  def label=(input)
43
47
  @label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input, @options)
44
48
  end
45
49
 
50
+ def groups
51
+ @groups ||= label_groups.map do |label_name|
52
+ self.class.new(@scope, label_name, self.date, @options)
53
+ end
54
+ end
55
+
46
56
  def to_s(depth = nil)
47
57
  depth ||= @options[:depth]
48
58
  key = self.prefix
@@ -5,6 +5,10 @@ module Redistat
5
5
  attr_reader :raw
6
6
  attr_reader :connection_ref
7
7
 
8
+ def self.create(name, options = {})
9
+ self.new(name, options).save
10
+ end
11
+
8
12
  def initialize(str, options = {})
9
13
  @options = options
10
14
  @raw = str.to_s
@@ -31,8 +35,18 @@ module Redistat
31
35
  @saved ||= false
32
36
  end
33
37
 
34
- def self.create(name, options = {})
35
- self.new(name, options).save
38
+ def groups
39
+ return @groups if @groups
40
+ @groups = []
41
+ parent = ""
42
+ @raw.split(GROUP_SEPARATOR).each do |part|
43
+ if !part.blank?
44
+ group = ((parent.blank?) ? "" : "#{parent}/") + part
45
+ @groups << group
46
+ parent = group
47
+ end
48
+ end
49
+ @groups.reverse!
36
50
  end
37
51
 
38
52
  end
@@ -2,23 +2,57 @@ module Redistat
2
2
  class Summary
3
3
  include Database
4
4
 
5
- def self.update_all(key, stats = {}, depth_limit = nil, connection_ref = nil)
5
+ def self.update_all(key, stats = {}, depth_limit = nil, connection_ref = nil, enable_grouping = nil)
6
6
  stats ||= {}
7
- depth_limit ||= key.depth
8
7
  return nil if stats.size == 0
8
+
9
+ depth_limit ||= key.depth
10
+ enable_grouping = true if enable_grouping.nil?
11
+
12
+ if enable_grouping
13
+ stats = inject_group_summaries(stats)
14
+ key.groups.each { |k|
15
+ update_key(k, stats, depth_limit, connection_ref)
16
+ }
17
+ else
18
+ update_key(key, stats, depth_limit, connection_ref)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def self.update_key(key, stats, depth_limit, connection_ref)
9
25
  Date::DEPTHS.each do |depth|
10
26
  update(key, stats, depth, connection_ref)
11
27
  break if depth == depth_limit
12
28
  end
13
29
  end
14
30
 
15
- private
16
-
17
31
  def self.update(key, stats, depth, connection_ref = nil)
18
32
  stats.each do |field, value|
19
33
  db(connection_ref).hincrby key.to_s(depth), field, value
20
34
  end
21
35
  end
22
36
 
37
+ def self.inject_group_summaries!(stats)
38
+ stats.each do |key, value|
39
+ parts = key.to_s.split(GROUP_SEPARATOR)
40
+ parts.pop
41
+ if parts.size > 0
42
+ sum_parts = []
43
+ parts.each do |part|
44
+ sum_parts << part
45
+ sum_key = sum_parts.join(GROUP_SEPARATOR)
46
+ (stats.has_key?(sum_key)) ? stats[sum_key] += value : stats[sum_key] = value
47
+ end
48
+ end
49
+ end
50
+ stats
51
+ end
52
+
53
+ def self.inject_group_summaries(stats)
54
+ inject_group_summaries!(stats.clone)
55
+ end
56
+
23
57
  end
24
58
  end
@@ -1,3 +1,3 @@
1
1
  module Redistat
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.0"
3
3
  end
data/redistat.gemspec CHANGED
@@ -25,5 +25,6 @@ Gem::Specification.new do |s|
25
25
  s.add_runtime_dependency 'time_ext', '>= 0.2.8'
26
26
 
27
27
  s.add_development_dependency 'rspec', '>= 2.1.0'
28
+ s.add_development_dependency 'rcov', '>= 0.9.9'
28
29
  s.add_development_dependency 'yard', '>= 0.6.3'
29
30
  end
data/spec/key_spec.rb CHANGED
@@ -14,6 +14,7 @@ describe Redistat::Key do
14
14
  @key.scope.should == @scope
15
15
  @key.label.should == @label
16
16
  @key.label_hash.should == @label_hash
17
+ @key.label_groups.should == @key.instance_variable_get("@label").groups
17
18
  @key.date.should be_instance_of(Redistat::Date)
18
19
  @key.date.to_time.to_s.should == @date.to_s
19
20
  end
@@ -60,4 +61,16 @@ describe Redistat::Key do
60
61
  @key.label_hash == @label_hash
61
62
  end
62
63
 
64
+ it "should create a group of keys from label group" do
65
+ label = 'message/public/offensive'
66
+ result = [ "message/public/offensive",
67
+ "message/public",
68
+ "message" ]
69
+
70
+ key = Redistat::Key.new(@scope, label, @date, {:depth => :hour})
71
+
72
+ key.label_groups.should == result
73
+ key.groups.map { |k| k.label }.should == result
74
+ end
75
+
63
76
  end
data/spec/label_spec.rb CHANGED
@@ -25,4 +25,24 @@ describe Redistat::Label do
25
25
  db.get("#{Redistat::KEY_LEBELS}#{label.hash}").should == name
26
26
  end
27
27
 
28
+ it "should separate label names into groups" do
29
+ name = "message/public/offensive"
30
+ label = Redistat::Label.new(name)
31
+ label.name.should == name
32
+ label.groups.should == [ "message/public/offensive",
33
+ "message/public",
34
+ "message" ]
35
+
36
+ name = "/message/public/"
37
+ label = Redistat::Label.new(name)
38
+ label.name.should == name
39
+ label.groups.should == [ "message/public",
40
+ "message" ]
41
+
42
+ name = "message"
43
+ label = Redistat::Label.new(name)
44
+ label.name.should == name
45
+ label.groups.should == [ "message" ]
46
+ end
47
+
28
48
  end
data/spec/summary_spec.rb CHANGED
@@ -46,4 +46,78 @@ describe Redistat::Summary do
46
46
  end
47
47
  end
48
48
 
49
+ it "should inject stats key grouping summaries" do
50
+ hash = { "count/hello" => 3, "count/world" => 7,
51
+ "death/bomb" => 4, "death/unicorn" => 3,
52
+ :"od/sugar" => 7, :"od/meth" => 8 }
53
+ res = Redistat::Summary.send(:inject_group_summaries, hash)
54
+ res.should == { "count" => 10, "count/hello" => 3, "count/world" => 7,
55
+ "death" => 7, "death/bomb" => 4, "death/unicorn" => 3,
56
+ "od" => 15, :"od/sugar" => 7, :"od/meth" => 8 }
57
+ end
58
+
59
+ it "should properly store key group summaries" do
60
+ stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4}
61
+ Redistat::Summary.update_all(@key, stats, :hour)
62
+ summary = db.hgetall(@key.to_s(:hour))
63
+ summary.should have(4).items
64
+ summary["views"].should == "3"
65
+ summary["visitors"].should == "6"
66
+ summary["visitors/eu"].should == "2"
67
+ summary["visitors/us"].should == "4"
68
+ end
69
+
70
+ it "should not store key group summaries when option is disabled" do
71
+ stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4}
72
+ Redistat::Summary.update_all(@key, stats, :hour, nil, false)
73
+ summary = db.hgetall(@key.to_s(:hour))
74
+ summary.should have(3).items
75
+ summary["views"].should == "3"
76
+ summary["visitors/eu"].should == "2"
77
+ summary["visitors/us"].should == "4"
78
+ end
79
+
80
+ it "should store label-based grouping enabled stats" do
81
+ stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4}
82
+ label = "views/about_us"
83
+ key = Redistat::Key.new(@scope, label, @date)
84
+ Redistat::Summary.update_all(key, stats, :hour)
85
+
86
+ key.groups[0].label.should == "views/about_us"
87
+ key.groups[1].label.should == "views"
88
+ child1 = key.groups[0]
89
+ parent = key.groups[1]
90
+
91
+ label = "views/contact"
92
+ key = Redistat::Key.new(@scope, label, @date)
93
+ Redistat::Summary.update_all(key, stats, :hour)
94
+
95
+ key.groups[0].label.should == "views/contact"
96
+ key.groups[1].label.should == "views"
97
+ child2 = key.groups[0]
98
+
99
+ summary = db.hgetall(child1.to_s(:hour))
100
+ summary["views"].should == "3"
101
+ summary["visitors/eu"].should == "2"
102
+ summary["visitors/us"].should == "4"
103
+
104
+ summary = db.hgetall(child2.to_s(:hour))
105
+ summary["views"].should == "3"
106
+ summary["visitors/eu"].should == "2"
107
+ summary["visitors/us"].should == "4"
108
+
109
+ summary = db.hgetall(parent.to_s(:hour))
110
+ summary["views"].should == "6"
111
+ summary["visitors/eu"].should == "4"
112
+ summary["visitors/us"].should == "8"
113
+ end
114
+
49
115
  end
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
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: 13
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 9
10
- version: 0.0.9
10
+ version: 0.1.0
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-02-23 00:00:00 +00:00
18
+ date: 2011-03-04 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -99,9 +99,25 @@ dependencies:
99
99
  type: :development
100
100
  version_requirements: *id005
101
101
  - !ruby/object:Gem::Dependency
102
- name: yard
102
+ name: rcov
103
103
  prerelease: false
104
104
  requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 41
110
+ segments:
111
+ - 0
112
+ - 9
113
+ - 9
114
+ version: 0.9.9
115
+ type: :development
116
+ version_requirements: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ name: yard
119
+ prerelease: false
120
+ requirement: &id007 !ruby/object:Gem::Requirement
105
121
  none: false
106
122
  requirements:
107
123
  - - ">="
@@ -113,7 +129,7 @@ dependencies:
113
129
  - 3
114
130
  version: 0.6.3
115
131
  type: :development
116
- version_requirements: *id006
132
+ version_requirements: *id007
117
133
  description: A Redis-backed statistics storage and querying library written in Ruby.
118
134
  email:
119
135
  - contact@jimeh.me