mirror of
https://github.com/jimeh/redistat.git
synced 2026-02-19 05:16:39 +00:00
Merge branch 'release/v0.2.0'
This commit is contained in:
@@ -66,6 +66,11 @@ When retrieving statistics for a given date range, Redistat figures out how to d
|
||||
* Anything else that becomes apparent after real-world use.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
[Global Personals](http://globalpersonals.co.uk/) deserves a thank you. Currently the primary user of Redistat, they've allowed me to spend some company time to further develop the project.
|
||||
|
||||
|
||||
## Note on Patches/Pull Requests
|
||||
|
||||
* Fork the project.
|
||||
|
||||
@@ -9,9 +9,10 @@ require 'time_ext'
|
||||
require 'json'
|
||||
require 'digest/sha1'
|
||||
|
||||
require 'redistat/collection'
|
||||
require 'redistat/options'
|
||||
require 'redistat/connection'
|
||||
require 'redistat/database'
|
||||
require 'redistat/collection'
|
||||
require 'redistat/date'
|
||||
require 'redistat/date_helper'
|
||||
require 'redistat/event'
|
||||
@@ -34,8 +35,9 @@ module Redistat
|
||||
|
||||
KEY_NEXT_ID = ".next_id"
|
||||
KEY_EVENT = ".event:"
|
||||
KEY_LEBELS = "Redistat.lables:"
|
||||
KEY_LEBELS = "Redistat.labels:" # used for reverse label hash lookup
|
||||
KEY_EVENT_IDS = ".event_ids"
|
||||
LABEL_INDEX = ".label_index:"
|
||||
GROUP_SEPARATOR = "/"
|
||||
|
||||
class InvalidOptions < ArgumentError; end
|
||||
|
||||
@@ -4,6 +4,7 @@ module Redistat
|
||||
base.extend(Database)
|
||||
end
|
||||
def db(ref = nil)
|
||||
ref ||= @options[:connection_ref] if !@options.nil?
|
||||
Redistat.connection(ref)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,40 +1,28 @@
|
||||
module Redistat
|
||||
class Event
|
||||
include Database
|
||||
include Options
|
||||
|
||||
attr_reader :id
|
||||
attr_reader :key
|
||||
attr_reader :connection_ref
|
||||
|
||||
attr_accessor :stats
|
||||
attr_accessor :meta
|
||||
attr_accessor :options
|
||||
|
||||
def initialize(scope, label = nil, date = nil, stats = {}, options = {}, meta = {}, is_new = true)
|
||||
@options = parse_options(options)
|
||||
@connection_ref = @options[:connection_ref]
|
||||
@key = Key.new(scope, label, date, @options)
|
||||
@stats = stats ||= {}
|
||||
@meta = meta ||= {}
|
||||
@new = is_new
|
||||
end
|
||||
|
||||
def db
|
||||
super(@connection_ref)
|
||||
end
|
||||
|
||||
def parse_options(options)
|
||||
default_options.each do |opt, val|
|
||||
options[opt] = val if options[opt].nil?
|
||||
end
|
||||
options
|
||||
end
|
||||
|
||||
def default_options
|
||||
{ :depth => :hour,
|
||||
:store_event => false,
|
||||
:connection_ref => nil,
|
||||
:enable_grouping => true }
|
||||
:enable_grouping => true,
|
||||
:label_indexing => true }
|
||||
end
|
||||
|
||||
def initialize(scope, label = nil, date = nil, stats = {}, opts = {}, meta = {}, is_new = true)
|
||||
parse_options(opts)
|
||||
@key = Key.new(scope, label, date, @options)
|
||||
@stats = stats ||= {}
|
||||
@meta = meta ||= {}
|
||||
@new = is_new
|
||||
end
|
||||
|
||||
def new?
|
||||
@@ -75,7 +63,7 @@ module Redistat
|
||||
|
||||
def save
|
||||
return false if !self.new?
|
||||
Summary.update_all(@key, @stats, depth_limit, @connection_ref, @options[:enable_grouping])
|
||||
Summary.update_all(@key, @stats, depth_limit, @options)
|
||||
if @options[:store_event]
|
||||
@id = self.next_id
|
||||
db.hmset("#{self.scope}#{KEY_EVENT}#{@id}",
|
||||
|
||||
@@ -40,8 +40,12 @@ module Redistat
|
||||
|
||||
attr_reader :options
|
||||
|
||||
def initialize(options = {})
|
||||
@options = options
|
||||
def initialize(opts = {})
|
||||
set_options(opts)
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= {}
|
||||
end
|
||||
|
||||
def all(reload = false)
|
||||
@@ -65,21 +69,27 @@ module Redistat
|
||||
all.each_with_index(&block)
|
||||
end
|
||||
|
||||
def children
|
||||
build_key.children.map { |key|
|
||||
self.class.new(options.merge(:label => key.label.to_s))
|
||||
}
|
||||
end
|
||||
|
||||
def connection_ref(ref)
|
||||
reset! if @options[:connection_ref] != ref
|
||||
@options[:connection_ref] = ref
|
||||
reset! if options[:connection_ref] != ref
|
||||
options[:connection_ref] = ref
|
||||
self
|
||||
end
|
||||
|
||||
def scope(scope)
|
||||
reset! if @options[:scope] != scope
|
||||
@options[:scope] = scope
|
||||
reset! if !options[:scope].nil? && options[:scope].to_s != scope
|
||||
options[:scope] = Scope.new(scope)
|
||||
self
|
||||
end
|
||||
|
||||
def label(label)
|
||||
reset! if @options[:label] != label
|
||||
@options[:label] = label
|
||||
reset! if !options[:label].nil? && options[:label].to_s != label
|
||||
options[:label] = Label.new(label)
|
||||
self
|
||||
end
|
||||
|
||||
@@ -89,34 +99,34 @@ module Redistat
|
||||
alias :date :dates
|
||||
|
||||
def from(date)
|
||||
reset! if @options[:from] != date
|
||||
@options[:from] = date
|
||||
reset! if options[:from] != date
|
||||
options[:from] = date
|
||||
self
|
||||
end
|
||||
|
||||
def till(date)
|
||||
reset! if @options[:till] != date
|
||||
@options[:till] = date
|
||||
reset! if options[:till] != date
|
||||
options[:till] = date
|
||||
self
|
||||
end
|
||||
alias :until :till
|
||||
|
||||
def depth(unit)
|
||||
reset! if @options[:depth] != unit
|
||||
@options[:depth] = unit
|
||||
reset! if options[:depth] != unit
|
||||
options[:depth] = unit
|
||||
self
|
||||
end
|
||||
|
||||
def interval(unit)
|
||||
reset! if @options[:interval] != unit
|
||||
@options[:interval] = unit
|
||||
reset! if options[:interval] != unit
|
||||
options[:interval] = unit
|
||||
self
|
||||
end
|
||||
|
||||
def find(options = {})
|
||||
set_options(options)
|
||||
def find(opts = {})
|
||||
set_options(opts)
|
||||
raise InvalidOptions.new if !valid_options?
|
||||
if @options[:interval].nil? || !@options[:interval]
|
||||
if options[:interval].nil? || !options[:interval]
|
||||
find_by_magic
|
||||
else
|
||||
find_by_interval
|
||||
@@ -130,14 +140,14 @@ module Redistat
|
||||
opts.each do |key, value|
|
||||
self.send(key, opts.delete(key)) if self.respond_to?(key)
|
||||
end
|
||||
@options.merge!(opts)
|
||||
self.options.merge!(opts)
|
||||
end
|
||||
|
||||
def find_by_interval(options = {})
|
||||
def find_by_interval
|
||||
raise InvalidOptions.new if !valid_options?
|
||||
key = build_key
|
||||
col = Collection.new(@options)
|
||||
col.total = Result.new(@options)
|
||||
col = Collection.new(options)
|
||||
col.total = Result.new(options)
|
||||
build_date_sets.each do |set|
|
||||
set[:add].each do |date|
|
||||
result = Result.new
|
||||
@@ -152,11 +162,11 @@ module Redistat
|
||||
col
|
||||
end
|
||||
|
||||
def find_by_magic(options = {})
|
||||
def find_by_magic
|
||||
raise InvalidOptions.new if !valid_options?
|
||||
key = Key.new(@options[:scope], @options[:label])
|
||||
col = Collection.new(@options)
|
||||
col.total = Result.new(@options)
|
||||
key = build_key
|
||||
col = Collection.new(options)
|
||||
col.total = Result.new(options)
|
||||
col << col.total
|
||||
build_date_sets.each do |set|
|
||||
sum = Result.new
|
||||
@@ -174,16 +184,16 @@ module Redistat
|
||||
end
|
||||
|
||||
def valid_options?
|
||||
return true if !@options[:scope].blank? && !@options[:label].blank? && !@options[:from].blank? && !@options[:till].blank?
|
||||
return true if !options[:scope].blank? && !options[:label].blank? && !options[:from].blank? && !options[:till].blank?
|
||||
false
|
||||
end
|
||||
|
||||
def build_date_sets
|
||||
Finder::DateSet.new(@options[:from], @options[:till], @options[:depth], @options[:interval])
|
||||
Finder::DateSet.new(options[:from], options[:till], options[:depth], options[:interval])
|
||||
end
|
||||
|
||||
def build_key
|
||||
Key.new(@options[:scope], @options[:label])
|
||||
Key.new(options[:scope], options[:label])
|
||||
end
|
||||
|
||||
def summarize_add_keys(sets, key, sum)
|
||||
@@ -205,7 +215,7 @@ module Redistat
|
||||
end
|
||||
|
||||
def db
|
||||
super(@options[:connection_ref])
|
||||
super(options[:connection_ref])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
module Redistat
|
||||
class Key
|
||||
include Database
|
||||
include Options
|
||||
|
||||
attr_accessor :scope
|
||||
attr_accessor :date
|
||||
attr_accessor :options
|
||||
def default_options
|
||||
{ :depth => :hour }
|
||||
end
|
||||
|
||||
def initialize(scope, label_name = nil, time_stamp = nil, options = {})
|
||||
@options = default_options.merge(options || {})
|
||||
@scope = scope
|
||||
def initialize(scope, label_name = nil, time_stamp = nil, opts = {})
|
||||
parse_options(opts)
|
||||
self.scope = scope
|
||||
self.label = label_name if !label_name.nil?
|
||||
self.date = time_stamp ||= Time.now
|
||||
end
|
||||
|
||||
def default_options
|
||||
{ :depth => :hour, :hashed_label => false }
|
||||
end
|
||||
|
||||
def prefix
|
||||
key = "#{@scope}"
|
||||
key << "/#{label}" if !label.nil?
|
||||
key << "/#{label.name}" if !label.nil?
|
||||
key << ":"
|
||||
key
|
||||
end
|
||||
@@ -26,30 +24,51 @@ module Redistat
|
||||
def date=(input)
|
||||
@date = (input.instance_of?(Redistat::Date)) ? input : Date.new(input) # Redistat::Date, not ::Date
|
||||
end
|
||||
attr_reader :date
|
||||
|
||||
def depth
|
||||
@options[:depth]
|
||||
options[:depth]
|
||||
end
|
||||
|
||||
def label
|
||||
@label.name
|
||||
def scope
|
||||
@scope.to_s
|
||||
end
|
||||
|
||||
def label_hash
|
||||
@label.hash
|
||||
end
|
||||
|
||||
def label_groups
|
||||
@label.groups
|
||||
def scope=(input)
|
||||
@scope = (input.instance_of?(Redistat::Scope)) ? input : Scope.new(input)
|
||||
end
|
||||
|
||||
def label=(input)
|
||||
@label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input, @options)
|
||||
end
|
||||
attr_reader :label
|
||||
|
||||
def groups
|
||||
@groups ||= label_groups.map do |label_name|
|
||||
self.class.new(@scope, label_name, self.date, @options)
|
||||
def label_hash
|
||||
@label.hash
|
||||
end
|
||||
|
||||
def parent
|
||||
@parent ||= self.class.new(self.scope, @label.parent, self.date, @options) unless @label.parent.nil?
|
||||
end
|
||||
|
||||
def children
|
||||
db.smembers("#{scope}#{LABEL_INDEX}#{@label}").map { |member|
|
||||
child_label = [@label, member].reject { |i| i.nil? }
|
||||
self.class.new(self.scope, child_label.join(GROUP_SEPARATOR), self.date, @options)
|
||||
}
|
||||
end
|
||||
|
||||
def update_index
|
||||
@label.groups.each do |label|
|
||||
# break if label.parent.nil?
|
||||
parent = (label.parent || "")
|
||||
db.sadd("#{scope}#{LABEL_INDEX}#{parent}", label.me)
|
||||
end
|
||||
end
|
||||
|
||||
def groups # TODO: Is this useless?
|
||||
@groups ||= @label.groups.map do |label|
|
||||
self.class.new(@scope, label, self.date, @options)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,48 +1,59 @@
|
||||
module Redistat
|
||||
class Label
|
||||
include Database
|
||||
include Options
|
||||
|
||||
attr_reader :raw
|
||||
attr_reader :connection_ref
|
||||
|
||||
def self.create(name, options = {})
|
||||
self.new(name, options).save
|
||||
def default_options
|
||||
{ :hashed_label => false }
|
||||
end
|
||||
|
||||
def initialize(str, options = {})
|
||||
@options = options
|
||||
def self.create(name, opts = {})
|
||||
self.new(name, opts).save
|
||||
end
|
||||
|
||||
def initialize(str, opts = {})
|
||||
parse_options(opts)
|
||||
@raw = str.to_s
|
||||
end
|
||||
|
||||
def db
|
||||
super(@options[:connection_ref])
|
||||
|
||||
def to_s
|
||||
@raw
|
||||
end
|
||||
|
||||
def name
|
||||
@options[:hashed_label] ? hash : @raw
|
||||
@options[:hashed_label] ? hash : self.to_s
|
||||
end
|
||||
|
||||
def hash
|
||||
@hash ||= Digest::SHA1.hexdigest(@raw)
|
||||
@hash ||= Digest::SHA1.hexdigest(self.to_s)
|
||||
end
|
||||
|
||||
def save
|
||||
@saved = (db.set("#{KEY_LEBELS}#{hash}", @raw) == "OK") if @options[:hashed_label]
|
||||
@saved = db.hset(KEY_LEBELS, hash, self.to_s) if @options[:hashed_label]
|
||||
self
|
||||
end
|
||||
|
||||
def saved?
|
||||
return true unless @options[:hashed_label]
|
||||
@saved ||= false
|
||||
end
|
||||
|
||||
def parent
|
||||
@parent ||= groups[1] if groups.size > 1
|
||||
end
|
||||
|
||||
def me
|
||||
self.to_s.split(GROUP_SEPARATOR).last
|
||||
end
|
||||
|
||||
def groups
|
||||
return @groups if @groups
|
||||
return @groups unless @groups.nil?
|
||||
@groups = []
|
||||
parent = ""
|
||||
@raw.split(GROUP_SEPARATOR).each do |part|
|
||||
self.to_s.split(GROUP_SEPARATOR).each do |part|
|
||||
if !part.blank?
|
||||
group = ((parent.blank?) ? "" : "#{parent}/") + part
|
||||
@groups << group
|
||||
group = ((parent.blank?) ? "" : "#{parent}#{GROUP_SEPARATOR}") + part
|
||||
@groups << Label.new(group)
|
||||
parent = group
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
module Redistat
|
||||
module Model
|
||||
include Redistat::Database
|
||||
include Database
|
||||
include Options
|
||||
|
||||
def self.included(base)
|
||||
base.extend(self)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# statistics store/fetch methods
|
||||
#
|
||||
|
||||
def store(label, stats = {}, date = nil, meta = {}, opts = {})
|
||||
def store(label, stats = {}, date = nil, opts = {}, meta = {})
|
||||
Event.new(name, label, date, stats, options.merge(opts), meta).save
|
||||
end
|
||||
alias :event :store
|
||||
@@ -21,53 +23,30 @@ module Redistat
|
||||
alias :lookup :fetch
|
||||
|
||||
def find(label, from, till, opts = {})
|
||||
Finder.new( { :scope => name,
|
||||
Finder.new( { :scope => self.name,
|
||||
:label => label,
|
||||
:from => from,
|
||||
:till => till }.merge(options.merge(opts)) )
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# options methods
|
||||
#
|
||||
|
||||
option_accessor :depth
|
||||
option_accessor :class_name
|
||||
option_accessor :store_event
|
||||
option_accessor :hashed_label
|
||||
option_accessor :label_indexing
|
||||
|
||||
alias :scope :class_name
|
||||
|
||||
def connect_to(opts = {})
|
||||
Connection.create(opts.merge(:ref => name))
|
||||
options[:connection_ref] = name
|
||||
end
|
||||
|
||||
def hashed_label(boolean = nil)
|
||||
if !boolean.nil?
|
||||
options[:hashed_label] = boolean
|
||||
else
|
||||
options[:hashed_label] || nil
|
||||
end
|
||||
end
|
||||
|
||||
def class_name(class_name = nil)
|
||||
if !class_name.nil?
|
||||
options[:class_name] = class_name
|
||||
else
|
||||
options[:class_name] || nil
|
||||
end
|
||||
end
|
||||
alias :scope :class_name
|
||||
|
||||
def depth(depth = nil)
|
||||
if !depth.nil?
|
||||
options[:depth] = depth
|
||||
else
|
||||
options[:depth] || nil
|
||||
end
|
||||
end
|
||||
|
||||
def store_event(boolean = nil)
|
||||
if !boolean.nil?
|
||||
options[:store_event] = boolean
|
||||
else
|
||||
options[:store_event] || nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# resource access methods
|
||||
@@ -78,10 +57,6 @@ module Redistat
|
||||
end
|
||||
alias :redis :connection
|
||||
|
||||
def options
|
||||
@options ||= {}
|
||||
end
|
||||
|
||||
def name
|
||||
options[:class_name] || (@name ||= self.to_s)
|
||||
end
|
||||
|
||||
43
lib/redistat/options.rb
Normal file
43
lib/redistat/options.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
module Redistat
|
||||
module Options
|
||||
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
class InvalidDefaultOptions < ArgumentError; end
|
||||
|
||||
module ClassMethods
|
||||
def option_accessor(*opts)
|
||||
opts.each do |option|
|
||||
define_method(option) do |*args|
|
||||
if !args.first.nil?
|
||||
options[option.to_sym] = args.first
|
||||
else
|
||||
options[option.to_sym] || nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_options(opts)
|
||||
opts ||= {}
|
||||
@raw_options = opts
|
||||
@options = default_options.merge(opts.reject { |k,v| v.nil? })
|
||||
end
|
||||
|
||||
def default_options
|
||||
{}
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= {}
|
||||
end
|
||||
|
||||
def raw_options
|
||||
@raw_options ||= {}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -2,20 +2,28 @@ module Redistat
|
||||
class Summary
|
||||
include Database
|
||||
|
||||
def self.update_all(key, stats = {}, depth_limit = nil, connection_ref = nil, enable_grouping = nil)
|
||||
def self.default_options
|
||||
{ :enable_grouping => true,
|
||||
:label_indexing => true,
|
||||
:connection_ref => nil }
|
||||
end
|
||||
|
||||
def self.update_all(key, stats = {}, depth_limit = nil, opts = {})
|
||||
stats ||= {}
|
||||
return nil if stats.size == 0
|
||||
|
||||
depth_limit ||= key.depth
|
||||
enable_grouping = true if enable_grouping.nil?
|
||||
options = default_options.merge((opts || {}).reject { |k,v| v.nil? })
|
||||
|
||||
if enable_grouping
|
||||
depth_limit ||= key.depth
|
||||
|
||||
if options[:enable_grouping]
|
||||
stats = inject_group_summaries(stats)
|
||||
key.groups.each { |k|
|
||||
update_key(k, stats, depth_limit, connection_ref)
|
||||
update_key(k, stats, depth_limit, options[:connection_ref])
|
||||
k.update_index if options[:label_indexing]
|
||||
}
|
||||
else
|
||||
update_key(key, stats, depth_limit, connection_ref)
|
||||
update_key(key, stats, depth_limit, options[:connection_ref])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module Redistat
|
||||
VERSION = "0.1.1"
|
||||
VERSION = "0.2.0"
|
||||
end
|
||||
|
||||
@@ -27,4 +27,5 @@ Gem::Specification.new do |s|
|
||||
s.add_development_dependency 'rspec', '>= 2.1.0'
|
||||
s.add_development_dependency 'rcov', '>= 0.9.9'
|
||||
s.add_development_dependency 'yard', '>= 0.6.3'
|
||||
s.add_development_dependency 'ruby-debug'
|
||||
end
|
||||
|
||||
@@ -18,7 +18,7 @@ describe Redistat::Event do
|
||||
it "should initialize properly" do
|
||||
@event.id.should be_nil
|
||||
@event.scope.should == @scope
|
||||
@event.label.should == @label
|
||||
@event.label.to_s.should == @label
|
||||
@event.label_hash.should == @label_hash
|
||||
@event.date.to_time.to_s.should == @date.to_s
|
||||
@event.stats.should == @stats
|
||||
@@ -33,12 +33,12 @@ describe Redistat::Event do
|
||||
@event.date = @date
|
||||
@event.date.to_time.to_s.should == @date.to_s
|
||||
# label
|
||||
@event.label.should == @label
|
||||
@event.label.to_s.should == @label
|
||||
@event.label_hash.should == @label_hash
|
||||
@label = "contact_us"
|
||||
@label_hash = Digest::SHA1.hexdigest(@label)
|
||||
@event.label = @label
|
||||
@event.label.should == @label
|
||||
@event.label.to_s.should == @label
|
||||
@event.label_hash.should == @label_hash
|
||||
end
|
||||
|
||||
@@ -64,7 +64,7 @@ describe Redistat::Event do
|
||||
@event = Redistat::Event.new(@scope, @label, @date, @stats, @options.merge({:store_event => true}), @meta).save
|
||||
fetched = Redistat::Event.find(@scope, @event.id)
|
||||
@event.scope.should == fetched.scope
|
||||
@event.label.should == fetched.label
|
||||
@event.label.to_s.should == fetched.label.to_s
|
||||
@event.date.to_s.should == fetched.date.to_s
|
||||
end
|
||||
|
||||
|
||||
@@ -9,41 +9,43 @@ describe Redistat::Finder do
|
||||
@label = "about_us"
|
||||
@date = Time.now
|
||||
@key = Redistat::Key.new(@scope, @label, @date, {:depth => :day})
|
||||
@stats = {"views" => 3, "visitors" => 2}
|
||||
@stats = {"views" => 3, "visitors" => 2}
|
||||
@two_hours_ago = 2.hours.ago
|
||||
@one_hour_ago = 1.hour.ago
|
||||
end
|
||||
|
||||
it "should initialize properly" do
|
||||
two_hours_ago = 2.hours.ago
|
||||
one_hour_ago = 1.hour.ago
|
||||
options = {:scope => "PageViews", :label => "Label", :from => two_hours_ago, :till => one_hour_ago, :depth => :hour, :interval => :hour}
|
||||
|
||||
finder = Redistat::Finder.new(options)
|
||||
finder.options.should == options
|
||||
options = {:scope => "PageViews", :label => "Label", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour}
|
||||
|
||||
finder = Redistat::Finder.new
|
||||
finder.send(:set_options, options)
|
||||
finder.options.should == options
|
||||
|
||||
finder = Redistat::Finder.dates(two_hours_ago, one_hour_ago).scope("PageViews").label("Label").depth(:hour).interval(:hour)
|
||||
finder.options.should == options
|
||||
|
||||
finder = Redistat::Finder.scope("PageViews").label("Label").from(two_hours_ago).till(one_hour_ago).depth(:hour).interval(:hour)
|
||||
finder.options.should == options
|
||||
finder.options[:scope].should be_a(Redistat::Scope)
|
||||
finder.options[:scope].to_s.should == options[:scope]
|
||||
finder.options[:label].should be_a(Redistat::Label)
|
||||
finder.options[:label].to_s.should == options[:label]
|
||||
finder.options.should == options.merge(:scope => finder.options[:scope], :label => finder.options[:label])
|
||||
|
||||
finder = Redistat::Finder.label("Label").from(two_hours_ago).till(one_hour_ago).depth(:hour).interval(:hour).scope("PageViews")
|
||||
finder.options.should == options
|
||||
finder = Redistat::Finder.dates(@two_hours_ago, @one_hour_ago)
|
||||
finder.options[:from].should == @two_hours_ago
|
||||
finder.options[:till].should == @one_hour_ago
|
||||
|
||||
finder = Redistat::Finder.from(two_hours_ago).till(one_hour_ago).depth(:hour).interval(:hour).scope("PageViews").label("Label")
|
||||
finder.options.should == options
|
||||
finder = Redistat::Finder.scope("hello")
|
||||
finder.options[:scope].to_s.should == "hello"
|
||||
|
||||
finder = Redistat::Finder.till(one_hour_ago).depth(:hour).interval(:hour).scope("PageViews").label("Label").from(two_hours_ago)
|
||||
finder.options.should == options
|
||||
finder = Redistat::Finder.label("hello")
|
||||
finder.options[:label].to_s.should == "hello"
|
||||
|
||||
finder = Redistat::Finder.depth(:hour).interval(:hour).scope("PageViews").label("Label").from(two_hours_ago).till(one_hour_ago)
|
||||
finder.options.should == options
|
||||
finder = Redistat::Finder.from(@two_hours_ago)
|
||||
finder.options[:from].should == @two_hours_ago
|
||||
|
||||
finder = Redistat::Finder.interval(:hour).scope("PageViews").label("Label").from(two_hours_ago).till(one_hour_ago).depth(:hour)
|
||||
finder.options.should == options
|
||||
finder = Redistat::Finder.till(@one_hour_ago)
|
||||
finder.options[:till].should == @one_hour_ago
|
||||
|
||||
finder = Redistat::Finder.depth(:hour)
|
||||
finder.options[:depth].should == :hour
|
||||
|
||||
finder = Redistat::Finder.interval(:hour)
|
||||
finder.options[:interval].should == :hour
|
||||
|
||||
end
|
||||
|
||||
@@ -89,6 +91,21 @@ describe Redistat::Finder do
|
||||
lambda { Redistat::Finder.find(:from => 3.hours.ago) }.should raise_error(Redistat::InvalidOptions)
|
||||
end
|
||||
|
||||
it "should find children" do
|
||||
Redistat::Key.new("PageViews", "message/public/die").update_index
|
||||
Redistat::Key.new("PageViews", "message/public/live").update_index
|
||||
Redistat::Key.new("PageViews", "message/public/fester").update_index
|
||||
members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}message/public") # checking 'message/public'
|
||||
options = {:scope => "PageViews", :label => "message/public", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour}
|
||||
finder = Redistat::Finder.new(options)
|
||||
finder.children.first.should be_a(Redistat::Finder)
|
||||
subs = finder.children.map { |f| f.options[:label].me }
|
||||
subs.should have(3).items
|
||||
subs.should include('die')
|
||||
subs.should include('live')
|
||||
subs.should include('fester')
|
||||
end
|
||||
|
||||
describe "Lazy-Loading" do
|
||||
|
||||
before(:each) do
|
||||
@@ -103,7 +120,6 @@ describe Redistat::Finder do
|
||||
end
|
||||
|
||||
it "should lazy-load" do
|
||||
|
||||
@finder.instance_variable_get("@result").should be_nil
|
||||
stats = @finder.all
|
||||
@finder.instance_variable_get("@result").should_not be_nil
|
||||
@@ -146,7 +162,7 @@ describe Redistat::Finder do
|
||||
res.should == match
|
||||
end
|
||||
|
||||
end
|
||||
end # "Lazy-Loading"
|
||||
|
||||
|
||||
# helper methods
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe Redistat::Key do
|
||||
include Redistat::Database
|
||||
|
||||
before(:each) do
|
||||
db.flushdb
|
||||
@scope = "PageViews"
|
||||
@label = "about_us"
|
||||
@label_hash = Digest::SHA1.hexdigest(@label)
|
||||
@@ -12,9 +14,9 @@ describe Redistat::Key do
|
||||
|
||||
it "should initialize properly" do
|
||||
@key.scope.should == @scope
|
||||
@key.label.should == @label
|
||||
@key.label.to_s.should == @label
|
||||
@key.label_hash.should == @label_hash
|
||||
@key.label_groups.should == @key.instance_variable_get("@label").groups
|
||||
@key.groups.map { |k| k.instance_variable_get("@label") }.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
|
||||
@@ -52,25 +54,74 @@ describe Redistat::Key do
|
||||
@key.date = @date
|
||||
@key.date.to_time.to_s.should == @date.to_s
|
||||
# label
|
||||
@key.label.should == @label
|
||||
@key.label.to_s.should == @label
|
||||
@key.label_hash == @label_hash
|
||||
@label = "contact_us"
|
||||
@label_hash = Digest::SHA1.hexdigest(@label)
|
||||
@key.label = @label
|
||||
@key.label.should == @label
|
||||
@key.label.to_s.should == @label
|
||||
@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" ]
|
||||
describe "Grouping" do
|
||||
before(:each) do
|
||||
@label = "message/public/offensive"
|
||||
@key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour})
|
||||
end
|
||||
|
||||
key = Redistat::Key.new(@scope, label, @date, {:depth => :hour})
|
||||
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.groups.map { |k| k.label.to_s }.should == result
|
||||
end
|
||||
|
||||
key.label_groups.should == result
|
||||
key.groups.map { |k| k.label }.should == result
|
||||
it "should know it's parent" do
|
||||
@key.parent.should be_a(Redistat::Key)
|
||||
@key.parent.label.to_s.should == 'message/public'
|
||||
Redistat::Key.new(@scope, 'hello', @date).parent.should be_nil
|
||||
end
|
||||
|
||||
it "should update label index and return children" do
|
||||
db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{@key.label.parent}").should == []
|
||||
@key.children.should have(0).items
|
||||
|
||||
@key.update_index # indexing 'message/publish/offensive'
|
||||
Redistat::Key.new("PageViews", "message/public/die").update_index # indexing 'message/publish/die'
|
||||
Redistat::Key.new("PageViews", "message/public/live").update_index # indexing 'message/publish/live'
|
||||
|
||||
members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{@key.label.parent}") # checking 'message/public'
|
||||
members.should have(3).item
|
||||
members.should include('offensive')
|
||||
members.should include('live')
|
||||
members.should include('die')
|
||||
|
||||
key = @key.parent
|
||||
key.children.first.should be_a(Redistat::Key)
|
||||
key.children.should have(3).item
|
||||
key.children.map { |k| k.label.me }.should == members
|
||||
|
||||
members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{key.label.parent}") # checking 'message'
|
||||
members.should have(1).item
|
||||
members.should include('public')
|
||||
|
||||
key = key.parent
|
||||
key.children.should have(1).item
|
||||
key.children.map { |k| k.label.me }.should == members
|
||||
|
||||
members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}") # checking ''
|
||||
members.should have(1).item
|
||||
members.should include('message')
|
||||
|
||||
key.parent.should be_nil
|
||||
key = Redistat::Key.new("PageViews")
|
||||
key.children.should have(1).item
|
||||
key.children.map { |k| k.label.me }.should include('message')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -17,32 +17,42 @@ describe Redistat::Label do
|
||||
it "should store a label hash lookup key" do
|
||||
label = Redistat::Label.new(@name, {:hashed_label => true}).save
|
||||
label.saved?.should be_true
|
||||
db.get("#{Redistat::KEY_LEBELS}#{label.hash}").should == @name
|
||||
db.hget(Redistat::KEY_LEBELS, label.hash).should == @name
|
||||
|
||||
name = "contact_us"
|
||||
label = Redistat::Label.create(name, {:hashed_label => true})
|
||||
label.saved?.should be_true
|
||||
db.get("#{Redistat::KEY_LEBELS}#{label.hash}").should == name
|
||||
db.hget(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" ]
|
||||
describe "Grouping" do
|
||||
before(:each) do
|
||||
@name = "message/public/offensive"
|
||||
@label = Redistat::Label.new(@name)
|
||||
end
|
||||
|
||||
it "should know it's parent label group" do
|
||||
@label.parent.to_s.should == 'message/public'
|
||||
Redistat::Label.new('hello').parent.should be_nil
|
||||
end
|
||||
|
||||
it "should separate label names into groups" do
|
||||
@label.name.should == @name
|
||||
@label.groups.map { |l| l.to_s }.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/public/"
|
||||
@label = Redistat::Label.new(@name)
|
||||
@label.name.should == @name
|
||||
@label.groups.map { |l| l.to_s }.should == [ "message/public",
|
||||
"message" ]
|
||||
|
||||
name = "message"
|
||||
label = Redistat::Label.new(name)
|
||||
label.name.should == name
|
||||
label.groups.should == [ "message" ]
|
||||
@name = "message"
|
||||
@label = Redistat::Label.new(@name)
|
||||
@label.name.should == @name
|
||||
@label.groups.map { |l| l.to_s }.should == [ "message" ]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -22,8 +22,8 @@ describe Redistat::Model do
|
||||
one_hour_ago = 1.hour.ago
|
||||
finder = ModelHelper1.find('label', two_hours_ago, one_hour_ago)
|
||||
finder.should be_a(Redistat::Finder)
|
||||
finder.options[:scope].should == 'ModelHelper1'
|
||||
finder.options[:label].should == 'label'
|
||||
finder.options[:scope].to_s.should == 'ModelHelper1'
|
||||
finder.options[:label].to_s.should == 'label'
|
||||
finder.options[:from].should == two_hours_ago
|
||||
finder.options[:till].should == one_hour_ago
|
||||
end
|
||||
@@ -82,34 +82,71 @@ describe Redistat::Model do
|
||||
stats.first.should == stats.total
|
||||
end
|
||||
|
||||
it "should store and fetch grouping enabled stats" do
|
||||
ModelHelper1.store("sheep/black", {:count => 6, :weight => 461}, @time.hours_ago(4))
|
||||
ModelHelper1.store("sheep/black", {:count => 2, :weight => 156}, @time)
|
||||
ModelHelper1.store("sheep/white", {:count => 5, :weight => 393}, @time.hours_ago(4))
|
||||
ModelHelper1.store("sheep/white", {:count => 4, :weight => 316}, @time)
|
||||
|
||||
stats = ModelHelper1.fetch("sheep/black", @time.hours_ago(2), @time.hours_since(1))
|
||||
stats.total["count"].should == 2
|
||||
stats.total["weight"].should == 156
|
||||
stats.first.should == stats.total
|
||||
|
||||
stats = ModelHelper1.fetch("sheep/black", @time.hours_ago(5), @time.hours_since(1))
|
||||
stats.total[:count].should == 8
|
||||
stats.total[:weight].should == 617
|
||||
stats.first.should == stats.total
|
||||
|
||||
stats = ModelHelper1.fetch("sheep/white", @time.hours_ago(2), @time.hours_since(1))
|
||||
stats.total[:count].should == 4
|
||||
stats.total[:weight].should == 316
|
||||
stats.first.should == stats.total
|
||||
|
||||
stats = ModelHelper1.fetch("sheep/white", @time.hours_ago(5), @time.hours_since(1))
|
||||
stats.total[:count].should == 9
|
||||
stats.total[:weight].should == 709
|
||||
stats.first.should == stats.total
|
||||
|
||||
stats = ModelHelper1.fetch("sheep", @time.hours_ago(2), @time.hours_since(1))
|
||||
stats.total[:count].should == 6
|
||||
stats.total[:weight].should == 472
|
||||
stats.first.should == stats.total
|
||||
|
||||
stats = ModelHelper1.fetch("sheep", @time.hours_ago(5), @time.hours_since(1))
|
||||
stats.total[:count].should == 17
|
||||
stats.total[:weight].should == 1326
|
||||
stats.first.should == stats.total
|
||||
end
|
||||
|
||||
it "should connect to different Redis servers on a per-model basis" do
|
||||
ModelHelper3.redis.client.db.should == 14
|
||||
|
||||
ModelHelper3.store("sheep.black", {:count => 6, :weight => 461}, @time.hours_ago(4))
|
||||
ModelHelper3.store("sheep.black", {:count => 2, :weight => 156}, @time)
|
||||
ModelHelper3.store("sheep.black", {:count => 6, :weight => 461}, @time.hours_ago(4), :label_indexing => false)
|
||||
ModelHelper3.store("sheep.black", {:count => 2, :weight => 156}, @time, :label_indexing => false)
|
||||
|
||||
db.keys("*").should be_empty
|
||||
ModelHelper1.redis.keys("*").should be_empty
|
||||
db("ModelHelper3").keys("*").should have(5).items
|
||||
ModelHelper3.redis.keys("*").should have(5).items
|
||||
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(2), @time.hours_since(1))
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(2), @time.hours_since(1), :label_indexing => false)
|
||||
stats.total["count"].should == 2
|
||||
stats.total["weight"].should == 156
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1))
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1), :label_indexing => false)
|
||||
stats.total[:count].should == 8
|
||||
stats.total[:weight].should == 617
|
||||
|
||||
ModelHelper3.connect_to(:port => 8379, :db => 13)
|
||||
ModelHelper3.redis.client.db.should == 13
|
||||
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1))
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1), :label_indexing => false)
|
||||
stats.total.should == {}
|
||||
|
||||
ModelHelper3.connect_to(:port => 8379, :db => 14)
|
||||
ModelHelper3.redis.client.db.should == 14
|
||||
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1))
|
||||
stats = ModelHelper3.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1), :label_indexing => false)
|
||||
stats.total[:count].should == 8
|
||||
stats.total[:weight].should == 617
|
||||
end
|
||||
|
||||
36
spec/options_spec.rb
Normal file
36
spec/options_spec.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe Redistat::Options do
|
||||
|
||||
before(:each) do
|
||||
@helper = OptionsHelper.new
|
||||
@helper.parse_options(:wtf => 'dude', :foo => 'booze')
|
||||
end
|
||||
|
||||
it "should #parse_options" do
|
||||
@helper.options[:hello].should == 'world'
|
||||
@helper.options[:foo].should == 'booze'
|
||||
@helper.options[:wtf].should == 'dude'
|
||||
@helper.raw_options.should_not have_key(:hello)
|
||||
end
|
||||
|
||||
it "should create option_accessors" do
|
||||
@helper.hello.should == 'world'
|
||||
@helper.hello('woooo')
|
||||
@helper.hello.should == 'woooo'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class OptionsHelper
|
||||
include Redistat::Options
|
||||
|
||||
option_accessor :hello
|
||||
|
||||
def default_options
|
||||
{ :hello => 'world',
|
||||
:foo => 'bar' }
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -69,7 +69,7 @@ describe Redistat::Summary do
|
||||
|
||||
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)
|
||||
Redistat::Summary.update_all(@key, stats, :hour, {:enable_grouping => false})
|
||||
summary = db.hgetall(@key.to_s(:hour))
|
||||
summary.should have(3).items
|
||||
summary["views"].should == "3"
|
||||
@@ -83,8 +83,8 @@ describe Redistat::Summary do
|
||||
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"
|
||||
key.groups[0].label.to_s.should == "views/about_us"
|
||||
key.groups[1].label.to_s.should == "views"
|
||||
child1 = key.groups[0]
|
||||
parent = key.groups[1]
|
||||
|
||||
@@ -92,8 +92,8 @@ describe Redistat::Summary do
|
||||
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"
|
||||
key.groups[0].label.to_s.should == "views/contact"
|
||||
key.groups[1].label.to_s.should == "views"
|
||||
child2 = key.groups[0]
|
||||
|
||||
summary = db.hgetall(child1.to_s(:hour))
|
||||
|
||||
Reference in New Issue
Block a user