From 8fefeee5492c2ce2d5a5bebe1462a714f32d8107 Mon Sep 17 00:00:00 2001 From: blahed Date: Tue, 29 Jan 2013 20:03:49 -0500 Subject: [PATCH] early implementation of "best" counters --- lib/von.rb | 1 + lib/von/best_counter.rb | 43 +++++++++++++++++++++++++++++++++++++++ lib/von/counter.rb | 2 ++ lib/von/period.rb | 21 +++++++++++++++++-- test/best_counter_test.rb | 31 ++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 lib/von/best_counter.rb create mode 100644 test/best_counter_test.rb diff --git a/lib/von.rb b/lib/von.rb index 93a9bd2..b2df578 100644 --- a/lib/von.rb +++ b/lib/von.rb @@ -3,6 +3,7 @@ require 'von/config' require 'von/counter' +require 'von/best_counter' require 'von/period' require 'von/version' diff --git a/lib/von/best_counter.rb b/lib/von/best_counter.rb new file mode 100644 index 0000000..377cc5f --- /dev/null +++ b/lib/von/best_counter.rb @@ -0,0 +1,43 @@ +module Von + class BestCounter + + def initialize(parent) + @parent = parent + @field = parent.field + @periods = Von.config.bests[@field] + end + + # Returns the Redis hash key used for storing counts for this Counter + def hash_key + @hash_key ||= "#{Von.config.namespace}:counters:bests:#{@field}" + end + + def increment + return if @periods.nil? + + @periods.each do |period| + # TODO: subclass counter (or somethin) and add hincrby/etc helpers + + current_timestamp = Von.connection.hget("#{hash_key}:#{period}:current", 'timestamp') + + # TODO: this logic "seems" backwards, rethink current_timestamp + if period.timestamp != current_timestamp + # changing current period + current_total = Von.connection.hget("#{hash_key}:#{period}:current", 'total').to_i + best_total = Von.connection.hget("#{hash_key}:#{period}:best", 'total').to_i + + Von.connection.hset("#{hash_key}:#{period}:current", 'total', 1) + Von.connection.hset("#{hash_key}:#{period}:current", 'timestamp', period.timestamp) + + if best_total < current_total + Von.connection.hset("#{hash_key}:#{period}:best", 'total', current_total) + Von.connection.hset("#{hash_key}:#{period}:best", 'timestamp', current_timestamp) + end + else + Von.connection.hincrby("#{hash_key}:#{period}:current", 'total', 1) + end + end + end + + end +end diff --git a/lib/von/counter.rb b/lib/von/counter.rb index 5ee743d..6609982 100644 --- a/lib/von/counter.rb +++ b/lib/von/counter.rb @@ -23,6 +23,8 @@ def hash_key def increment total = Von.connection.hincrby(hash_key, 'total', 1) + BestCounter.new(self).increment + increment_periods total diff --git a/lib/von/period.rb b/lib/von/period.rb index 425795c..a445c8a 100644 --- a/lib/von/period.rb +++ b/lib/von/period.rb @@ -9,7 +9,9 @@ class Period :monthly => :month, :yearly => :year } - + TIME_PERIODS = TIME_UNITS.invert + + attr_reader :period attr_reader :counter_key attr_reader :length attr_reader :format @@ -19,12 +21,16 @@ class Period # counter - the field name for the counter # period - the time period one of AVAILABLE_PERIODS # length - length of period - def initialize(counter_key, period, length) + def initialize(counter_key, period, length = nil) @counter_key = counter_key @period = period.to_sym @length = length @format = Von.config.send(:"#{@period}_format") end + + def to_s + @period.to_s + end # Returns a Symbol representing the time unit # for the current period. @@ -37,6 +43,15 @@ def hours? @period == :hourly end + def now + # TODO: this may break minutes + DateTime.now.beginning_of_hour + end + + def prev + hours? ? now.ago(3600) : now.send(:"prev_#{_period.time_unit}") + end + # Returns the Redis hash key used for storing counts for this Period def hash_key @hash ||= "#{Von.config.namespace}:counters:#{@counter_key}:#{@period}" @@ -48,9 +63,11 @@ def list_key end # Returns the Redis field representation used for storing the count value + # TODO: rename this def field Time.now.strftime(format) end + alias :timestamp :field def self.exists?(period) AVAILABLE_PERIODS.include?(period) diff --git a/test/best_counter_test.rb b/test/best_counter_test.rb new file mode 100644 index 0000000..4b8bb16 --- /dev/null +++ b/test/best_counter_test.rb @@ -0,0 +1,31 @@ +require 'test_helper' + +describe Von::BestCounter do + Counter = Von::BestCounter + + before :each do + Timecop.freeze(Time.local(2013, 01, 01)) + Von.config.init! + @redis = Redis.new + @redis.flushall + end + + it "increments the best counter for a period" do + Von.configure do |config| + config.counter 'foo', :best => :day + end + + Counter.increment('foo') + + Timecop.freeze(Time.local(2013, 01, 02)) + 10.times { Counter.increment('foo') } + Timecop.freeze(Time.local(2013, 01, 03)) + 3.times { Counter.increment('foo') } + + @redis.hget('von:counters:bests:foo:daily:current', 'timestamp').must_equal '2013-01-03' + @redis.hget('von:counters:bests:foo:daily:current', 'total').must_equal '3' + @redis.hget('von:counters:bests:foo:daily:best', 'timestamp').must_equal '2013-01-02' + @redis.hget('von:counters:bests:foo:daily:best', 'total').must_equal '10' + end + +end