mirror of
https://github.com/jimeh/redistat.git
synced 2026-02-19 13:26:39 +00:00
Merge branch 'release/v0.1.0'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,6 +19,7 @@ rdoc
|
||||
pkg/*
|
||||
*.gem
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
|
||||
## PROJECT::SPECIFIC
|
||||
.yardoc/*
|
||||
|
||||
@@ -14,7 +14,7 @@ Redis fits perfectly with all of these requirements. It has atomic operations li
|
||||
|
||||
gem install redistat
|
||||
|
||||
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.
|
||||
If you are using Ruby 1.8.x, it's recommended you also install the `SystemTimer` gem, as the Redis gem will otherwise complain.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
1
Rakefile
1
Rakefile
@@ -14,6 +14,7 @@ end
|
||||
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
||||
spec.pattern = 'spec/**/*_spec.rb'
|
||||
spec.rcov = true
|
||||
spec.rcov_opts = ['--exclude', 'spec']
|
||||
end
|
||||
|
||||
task :default => [:start, :spec, :stop]
|
||||
|
||||
@@ -36,6 +36,7 @@ module Redistat
|
||||
KEY_EVENT = ".event:"
|
||||
KEY_LEBELS = "Redistat.lables:"
|
||||
KEY_EVENT_IDS = ".event_ids"
|
||||
GROUP_SEPARATOR = "/"
|
||||
|
||||
class InvalidOptions < ArgumentError; end
|
||||
class RedisServerIsTooOld < Exception; end
|
||||
|
||||
@@ -31,7 +31,10 @@ module Redistat
|
||||
end
|
||||
|
||||
def default_options
|
||||
{ :depth => :hour, :store_event => false, :connection_ref => nil }
|
||||
{ :depth => :hour,
|
||||
:store_event => false,
|
||||
:connection_ref => nil,
|
||||
:enable_grouping => true }
|
||||
end
|
||||
|
||||
def new?
|
||||
@@ -72,7 +75,7 @@ module Redistat
|
||||
|
||||
def save
|
||||
return false if !self.new?
|
||||
Summary.update_all(@key, @stats, depth_limit, @connection_ref)
|
||||
Summary.update_all(@key, @stats, depth_limit, @connection_ref, @options[:enable_grouping])
|
||||
if @options[:store_event]
|
||||
@id = self.next_id
|
||||
db.hmset("#{self.scope}#{KEY_EVENT}#{@id}",
|
||||
|
||||
@@ -39,10 +39,20 @@ module Redistat
|
||||
@label.hash
|
||||
end
|
||||
|
||||
def label_groups
|
||||
@label.groups
|
||||
end
|
||||
|
||||
def label=(input)
|
||||
@label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input, @options)
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= label_groups.map do |label_name|
|
||||
self.class.new(@scope, label_name, self.date, @options)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s(depth = nil)
|
||||
depth ||= @options[:depth]
|
||||
key = self.prefix
|
||||
|
||||
@@ -5,6 +5,10 @@ module Redistat
|
||||
attr_reader :raw
|
||||
attr_reader :connection_ref
|
||||
|
||||
def self.create(name, options = {})
|
||||
self.new(name, options).save
|
||||
end
|
||||
|
||||
def initialize(str, options = {})
|
||||
@options = options
|
||||
@raw = str.to_s
|
||||
@@ -31,8 +35,18 @@ module Redistat
|
||||
@saved ||= false
|
||||
end
|
||||
|
||||
def self.create(name, options = {})
|
||||
self.new(name, options).save
|
||||
def groups
|
||||
return @groups if @groups
|
||||
@groups = []
|
||||
parent = ""
|
||||
@raw.split(GROUP_SEPARATOR).each do |part|
|
||||
if !part.blank?
|
||||
group = ((parent.blank?) ? "" : "#{parent}/") + part
|
||||
@groups << group
|
||||
parent = group
|
||||
end
|
||||
end
|
||||
@groups.reverse!
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -2,23 +2,57 @@ module Redistat
|
||||
class Summary
|
||||
include Database
|
||||
|
||||
def self.update_all(key, stats = {}, depth_limit = nil, connection_ref = nil)
|
||||
def self.update_all(key, stats = {}, depth_limit = nil, connection_ref = nil, enable_grouping = nil)
|
||||
stats ||= {}
|
||||
depth_limit ||= key.depth
|
||||
return nil if stats.size == 0
|
||||
|
||||
depth_limit ||= key.depth
|
||||
enable_grouping = true if enable_grouping.nil?
|
||||
|
||||
if enable_grouping
|
||||
stats = inject_group_summaries(stats)
|
||||
key.groups.each { |k|
|
||||
update_key(k, stats, depth_limit, connection_ref)
|
||||
}
|
||||
else
|
||||
update_key(key, stats, depth_limit, connection_ref)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.update_key(key, stats, depth_limit, connection_ref)
|
||||
Date::DEPTHS.each do |depth|
|
||||
update(key, stats, depth, connection_ref)
|
||||
break if depth == depth_limit
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.update(key, stats, depth, connection_ref = nil)
|
||||
stats.each do |field, value|
|
||||
db(connection_ref).hincrby key.to_s(depth), field, value
|
||||
end
|
||||
end
|
||||
|
||||
def self.inject_group_summaries!(stats)
|
||||
stats.each do |key, value|
|
||||
parts = key.to_s.split(GROUP_SEPARATOR)
|
||||
parts.pop
|
||||
if parts.size > 0
|
||||
sum_parts = []
|
||||
parts.each do |part|
|
||||
sum_parts << part
|
||||
sum_key = sum_parts.join(GROUP_SEPARATOR)
|
||||
(stats.has_key?(sum_key)) ? stats[sum_key] += value : stats[sum_key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
stats
|
||||
end
|
||||
|
||||
def self.inject_group_summaries(stats)
|
||||
inject_group_summaries!(stats.clone)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,3 @@
|
||||
module Redistat
|
||||
VERSION = "0.0.9"
|
||||
VERSION = "0.1.0"
|
||||
end
|
||||
|
||||
@@ -25,5 +25,6 @@ Gem::Specification.new do |s|
|
||||
s.add_runtime_dependency 'time_ext', '>= 0.2.8'
|
||||
|
||||
s.add_development_dependency 'rspec', '>= 2.1.0'
|
||||
s.add_development_dependency 'rcov', '>= 0.9.9'
|
||||
s.add_development_dependency 'yard', '>= 0.6.3'
|
||||
end
|
||||
|
||||
@@ -14,6 +14,7 @@ describe Redistat::Key do
|
||||
@key.scope.should == @scope
|
||||
@key.label.should == @label
|
||||
@key.label_hash.should == @label_hash
|
||||
@key.label_groups.should == @key.instance_variable_get("@label").groups
|
||||
@key.date.should be_instance_of(Redistat::Date)
|
||||
@key.date.to_time.to_s.should == @date.to_s
|
||||
end
|
||||
@@ -60,4 +61,16 @@ describe Redistat::Key do
|
||||
@key.label_hash == @label_hash
|
||||
end
|
||||
|
||||
it "should create a group of keys from label group" do
|
||||
label = 'message/public/offensive'
|
||||
result = [ "message/public/offensive",
|
||||
"message/public",
|
||||
"message" ]
|
||||
|
||||
key = Redistat::Key.new(@scope, label, @date, {:depth => :hour})
|
||||
|
||||
key.label_groups.should == result
|
||||
key.groups.map { |k| k.label }.should == result
|
||||
end
|
||||
|
||||
end
|
||||
@@ -25,4 +25,24 @@ describe Redistat::Label do
|
||||
db.get("#{Redistat::KEY_LEBELS}#{label.hash}").should == name
|
||||
end
|
||||
|
||||
it "should separate label names into groups" do
|
||||
name = "message/public/offensive"
|
||||
label = Redistat::Label.new(name)
|
||||
label.name.should == name
|
||||
label.groups.should == [ "message/public/offensive",
|
||||
"message/public",
|
||||
"message" ]
|
||||
|
||||
name = "/message/public/"
|
||||
label = Redistat::Label.new(name)
|
||||
label.name.should == name
|
||||
label.groups.should == [ "message/public",
|
||||
"message" ]
|
||||
|
||||
name = "message"
|
||||
label = Redistat::Label.new(name)
|
||||
label.name.should == name
|
||||
label.groups.should == [ "message" ]
|
||||
end
|
||||
|
||||
end
|
||||
@@ -46,4 +46,78 @@ describe Redistat::Summary do
|
||||
end
|
||||
end
|
||||
|
||||
it "should inject stats key grouping summaries" do
|
||||
hash = { "count/hello" => 3, "count/world" => 7,
|
||||
"death/bomb" => 4, "death/unicorn" => 3,
|
||||
:"od/sugar" => 7, :"od/meth" => 8 }
|
||||
res = Redistat::Summary.send(:inject_group_summaries, hash)
|
||||
res.should == { "count" => 10, "count/hello" => 3, "count/world" => 7,
|
||||
"death" => 7, "death/bomb" => 4, "death/unicorn" => 3,
|
||||
"od" => 15, :"od/sugar" => 7, :"od/meth" => 8 }
|
||||
end
|
||||
|
||||
it "should properly store key group summaries" do
|
||||
stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4}
|
||||
Redistat::Summary.update_all(@key, stats, :hour)
|
||||
summary = db.hgetall(@key.to_s(:hour))
|
||||
summary.should have(4).items
|
||||
summary["views"].should == "3"
|
||||
summary["visitors"].should == "6"
|
||||
summary["visitors/eu"].should == "2"
|
||||
summary["visitors/us"].should == "4"
|
||||
end
|
||||
|
||||
it "should not store key group summaries when option is disabled" do
|
||||
stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4}
|
||||
Redistat::Summary.update_all(@key, stats, :hour, nil, false)
|
||||
summary = db.hgetall(@key.to_s(:hour))
|
||||
summary.should have(3).items
|
||||
summary["views"].should == "3"
|
||||
summary["visitors/eu"].should == "2"
|
||||
summary["visitors/us"].should == "4"
|
||||
end
|
||||
|
||||
it "should store label-based grouping enabled stats" do
|
||||
stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4}
|
||||
label = "views/about_us"
|
||||
key = Redistat::Key.new(@scope, label, @date)
|
||||
Redistat::Summary.update_all(key, stats, :hour)
|
||||
|
||||
key.groups[0].label.should == "views/about_us"
|
||||
key.groups[1].label.should == "views"
|
||||
child1 = key.groups[0]
|
||||
parent = key.groups[1]
|
||||
|
||||
label = "views/contact"
|
||||
key = Redistat::Key.new(@scope, label, @date)
|
||||
Redistat::Summary.update_all(key, stats, :hour)
|
||||
|
||||
key.groups[0].label.should == "views/contact"
|
||||
key.groups[1].label.should == "views"
|
||||
child2 = key.groups[0]
|
||||
|
||||
summary = db.hgetall(child1.to_s(:hour))
|
||||
summary["views"].should == "3"
|
||||
summary["visitors/eu"].should == "2"
|
||||
summary["visitors/us"].should == "4"
|
||||
|
||||
summary = db.hgetall(child2.to_s(:hour))
|
||||
summary["views"].should == "3"
|
||||
summary["visitors/eu"].should == "2"
|
||||
summary["visitors/us"].should == "4"
|
||||
|
||||
summary = db.hgetall(parent.to_s(:hour))
|
||||
summary["views"].should == "6"
|
||||
summary["visitors/eu"].should == "4"
|
||||
summary["visitors/us"].should == "8"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user