redistat 0.0.9 → 0.1.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/.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