von 0.1.0 → 0.2.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/README.md +46 -5
- data/Rakefile +1 -1
- data/lib/von.rb +42 -5
- data/lib/von/config.rb +54 -24
- data/lib/von/counter.rb +41 -83
- data/lib/von/counters/best.rb +68 -0
- data/lib/von/counters/commands.rb +41 -0
- data/lib/von/counters/current.rb +43 -0
- data/lib/von/counters/period.rb +62 -0
- data/lib/von/counters/total.rb +49 -0
- data/lib/von/period.rb +51 -30
- data/lib/von/version.rb +1 -1
- data/test/config_test.rb +31 -11
- data/test/counter_test.rb +40 -65
- data/test/counters/best_test.rb +53 -0
- data/test/counters/current_test.rb +55 -0
- data/test/counters/period_test.rb +78 -0
- data/test/counters/total_test.rb +33 -0
- data/test/period_test.rb +62 -40
- data/test/test_helper.rb +2 -62
- data/test/von_test.rb +25 -3
- data/von.gemspec +1 -0
- metadata +31 -3
- data/lib/von/model_counter.rb +0 -28
    
        data/README.md
    CHANGED
    
    | @@ -19,7 +19,7 @@ Von.increment('downloads')          # bumps the 'downloads' key 1 time | |
| 19 19 | 
             
            Von.increment('downloads:app123')   # bumps the 'downloads:app123' key 1 time AND the 'downloads' key 1 time
         | 
| 20 20 | 
             
            ```
         | 
| 21 21 |  | 
| 22 | 
            -
            ## Time  | 
| 22 | 
            +
            ## Tracking Time Periods
         | 
| 23 23 |  | 
| 24 24 | 
             
            By default Von will only bump a "total" counter for the given key. This is great, but what makes Von really useful is that it can be configured to group certain keys by hour, day, week, month, and year. And you can set limits on how many of each you want to keep around. Here's how it works:
         | 
| 25 25 |  | 
| @@ -34,23 +34,64 @@ Von.configure do |config| | |
| 34 34 | 
             
            end
         | 
| 35 35 | 
             
            ```
         | 
| 36 36 |  | 
| 37 | 
            -
             | 
| 37 | 
            +
            ## Tracking the Current Time Period
         | 
| 38 38 |  | 
| 39 | 
            -
             | 
| 39 | 
            +
            If just wanna track stats on the current minute/hour/day/week/etc, you can set that up pretty easily with Von:
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            ```ruby
         | 
| 42 | 
            +
            Von.configure do |config|
         | 
| 43 | 
            +
                # Track downloads stats for the current hour
         | 
| 44 | 
            +
                config.counter 'downloads', :current => :hour
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                # Track page views for the current day and week
         | 
| 47 | 
            +
                config.counter 'page-views', :current => [ :day, :week ]
         | 
| 48 | 
            +
            end
         | 
| 49 | 
            +
            ```
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            ## Tracking "Bests"
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            Time periods are pretty cool, but sometimes you wanna know when you did your best. You can track these with Von as well:
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            ```ruby
         | 
| 56 | 
            +
            Von.configure do |config|
         | 
| 57 | 
            +
                # Track the best day for downloads
         | 
| 58 | 
            +
                config.counter 'downloads', :best => :day
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                # Track the best hour and week for page views
         | 
| 61 | 
            +
                config.counter 'page-views', :best => [ :hour, :week ]
         | 
| 62 | 
            +
            end
         | 
| 63 | 
            +
            ```
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            ## Incrementing
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            Once you've configured the keys you don't have to do anything special, just increment the key, Von will handle this rest.
         | 
| 40 68 |  | 
| 41 69 | 
             
            ```ruby
         | 
| 42 70 | 
             
            Von.increment('downloads')
         | 
| 43 71 | 
             
            Von.increment('uploads')
         | 
| 72 | 
            +
            Von.increment('page-views')
         | 
| 44 73 | 
             
            ```
         | 
| 45 74 |  | 
| 46 75 | 
             
            ## Getting Stats
         | 
| 47 76 |  | 
| 48 77 | 
             
            ```ruby
         | 
| 49 78 | 
             
            # get the total downloads (returns an Integer)
         | 
| 50 | 
            -
            Von.count('downloads') | 
| 79 | 
            +
            Von.count('downloads').total       #=> 4
         | 
| 80 | 
            +
             | 
| 51 81 | 
             
            # get the monthly counts (returns an Array of Hashes)
         | 
| 52 | 
            -
            Von.count('uploads' | 
| 82 | 
            +
            Von.count('uploads').per(:month)   #=> [ { :timestamp => '2012-03', :count => 3 }, { :timestamp => '2013-04', :count => 1 }, { :timestamp => '2013-05', :count => 0 }]
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            # get the download stats for the hour
         | 
| 85 | 
            +
            Von.count('downloads').this(:hour)          #=> 10
         | 
| 86 | 
            +
            # or
         | 
| 87 | 
            +
            Von.count('downloads').current(:hour)       #=> 10
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            # get the page-views for today
         | 
| 90 | 
            +
            Von.count('page-views').today               #=> 78
         | 
| 91 | 
            +
            Von.count('page-views').current(:day)       #=> 78
         | 
| 53 92 |  | 
| 93 | 
            +
            # get the best day for downloads (returns a Hash)
         | 
| 94 | 
            +
            Von.count('downloads').best(:day)  #=> { :timestamp => '2012-03-01', :count => 10 }
         | 
| 54 95 | 
             
            ```
         | 
| 55 96 |  | 
| 56 97 | 
             
            One nice thing to note, if you're counting a time period and there wasn't a value stored for the particular hour/day/week/etc, it'll be populated with a zero, this ensures that if you want 30 days of stats, you get 30 days of stats.
         | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/lib/von.rb
    CHANGED
    
    | @@ -2,11 +2,18 @@ require 'redis' | |
| 2 2 | 
             
            require 'active_support/time'
         | 
| 3 3 |  | 
| 4 4 | 
             
            require 'von/config'
         | 
| 5 | 
            -
            require 'von/counter'
         | 
| 6 5 | 
             
            require 'von/period'
         | 
| 6 | 
            +
            require 'von/counter'
         | 
| 7 | 
            +
            require 'von/counters/commands'
         | 
| 8 | 
            +
            require 'von/counters/total'
         | 
| 9 | 
            +
            require 'von/counters/period'
         | 
| 10 | 
            +
            require 'von/counters/best'
         | 
| 11 | 
            +
            require 'von/counters/current'
         | 
| 7 12 | 
             
            require 'von/version'
         | 
| 8 13 |  | 
| 9 14 | 
             
            module Von
         | 
| 15 | 
            +
              PARENT_REGEX = /:?[^:]+\z/
         | 
| 16 | 
            +
             | 
| 10 17 | 
             
              def self.connection
         | 
| 11 18 | 
             
                @connection ||= config.redis
         | 
| 12 19 | 
             
              end
         | 
| @@ -20,16 +27,46 @@ module Von | |
| 20 27 | 
             
              end
         | 
| 21 28 |  | 
| 22 29 | 
             
              def self.increment(field)
         | 
| 23 | 
            -
                 | 
| 30 | 
            +
                parents = field.to_s.sub(PARENT_REGEX, '')
         | 
| 31 | 
            +
                total   = increment_counts_for(field)
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                until parents.empty? do
         | 
| 34 | 
            +
                  increment_counts_for(parents)
         | 
| 35 | 
            +
                  parents.sub!(PARENT_REGEX, '')
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                total
         | 
| 24 39 | 
             
              rescue Redis::BaseError => e
         | 
| 25 40 | 
             
                raise e if config.raise_connection_errors
         | 
| 26 41 | 
             
              end
         | 
| 27 42 |  | 
| 28 | 
            -
              def self. | 
| 29 | 
            -
                 | 
| 43 | 
            +
              def self.increment_counts_for(field)
         | 
| 44 | 
            +
                counter = Counters::Total.new(field)
         | 
| 45 | 
            +
                total   = counter.increment
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                if config.periods_defined_for_counter?(counter)
         | 
| 48 | 
            +
                  periods = config.periods[counter.field]
         | 
| 49 | 
            +
                  Counters::Period.new(counter.field, periods).increment
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                if config.bests_defined_for_counter?(counter)
         | 
| 53 | 
            +
                  periods = config.bests[counter.field]
         | 
| 54 | 
            +
                  Counters::Best.new(counter.field, periods).increment
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                if config.currents_defined_for_counter?(counter)
         | 
| 58 | 
            +
                  periods = config.currents[counter.field]
         | 
| 59 | 
            +
                  Counters::Current.new(counter.field, periods).increment
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                total
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              def self.count(field)
         | 
| 66 | 
            +
                Counter.new(field)
         | 
| 30 67 | 
             
              rescue Redis::BaseError => e
         | 
| 31 68 | 
             
                raise e if config.raise_connection_errors
         | 
| 32 69 | 
             
              end
         | 
| 33 70 |  | 
| 34 71 | 
             
              config.init!
         | 
| 35 | 
            -
            end
         | 
| 72 | 
            +
            end
         | 
    
        data/lib/von/config.rb
    CHANGED
    
    | @@ -11,26 +11,33 @@ module Von | |
| 11 11 | 
             
                attr_accessor :weekly_format
         | 
| 12 12 | 
             
                attr_accessor :daily_format
         | 
| 13 13 | 
             
                attr_accessor :hourly_format
         | 
| 14 | 
            +
                attr_accessor :minutely_format
         | 
| 14 15 |  | 
| 15 | 
            -
                attr_reader | 
| 16 | 
            +
                attr_reader :periods
         | 
| 17 | 
            +
                attr_reader :bests
         | 
| 18 | 
            +
                attr_reader :currents
         | 
| 16 19 |  | 
| 17 20 | 
             
                def init!
         | 
| 18 | 
            -
                  @ | 
| 19 | 
            -
                  @ | 
| 21 | 
            +
                  @periods  = {}
         | 
| 22 | 
            +
                  @bests    = {}
         | 
| 23 | 
            +
                  @currents = {}
         | 
| 24 | 
            +
                  @totals   = {}
         | 
| 20 25 | 
             
                  # all keys are prefixed with this namespace
         | 
| 21 26 | 
             
                  self.namespace = 'von'
         | 
| 22 27 | 
             
                  # rescue Redis connection errors
         | 
| 23 28 | 
             
                  self.raise_connection_errors = false
         | 
| 24 29 | 
             
                  # 2013
         | 
| 25 | 
            -
                  self.yearly_format | 
| 30 | 
            +
                  self.yearly_format   = '%Y'
         | 
| 26 31 | 
             
                  # 2013-01
         | 
| 27 | 
            -
                  self.monthly_format | 
| 32 | 
            +
                  self.monthly_format  = '%Y-%m'
         | 
| 28 33 | 
             
                  # 2013-01-02
         | 
| 29 | 
            -
                  self.weekly_format | 
| 34 | 
            +
                  self.weekly_format   = '%Y-%m-%d'
         | 
| 30 35 | 
             
                  # 2013-01-02
         | 
| 31 | 
            -
                  self.daily_format | 
| 36 | 
            +
                  self.daily_format    = '%Y-%m-%d'
         | 
| 32 37 | 
             
                  # 2013-01-02 12:00
         | 
| 33 | 
            -
                  self.hourly_format | 
| 38 | 
            +
                  self.hourly_format   = '%Y-%m-%d %H:00'
         | 
| 39 | 
            +
                  # 2013-01-02 12:05
         | 
| 40 | 
            +
                  self.minutely_format = '%Y-%m-%d %H:%M'
         | 
| 34 41 | 
             
                end
         | 
| 35 42 |  | 
| 36 43 | 
             
                # Set the Redis connection to use
         | 
| @@ -54,28 +61,51 @@ module Von | |
| 54 61 | 
             
                # Configure options for given Counter. Configures length of given time period
         | 
| 55 62 | 
             
                # and any other options for the Counter
         | 
| 56 63 | 
             
                def counter(field, options = {})
         | 
| 57 | 
            -
                   | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
                    end
         | 
| 64 | 
            +
                  field = field.to_sym
         | 
| 65 | 
            +
                  options.each do |option, value|
         | 
| 66 | 
            +
                    set_period(field, option, value) if Period.exists?(option)
         | 
| 67 | 
            +
                    set_best(field, value) if option == :best
         | 
| 68 | 
            +
                    set_current(field, value) if option == :current
         | 
| 63 69 | 
             
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                # Returns a True if a period is defined for the
         | 
| 73 | 
            +
                # given Counter
         | 
| 74 | 
            +
                # TODO: these should just take the key, will fix when renaming field
         | 
| 75 | 
            +
                def periods_defined_for_counter?(counter)
         | 
| 76 | 
            +
                  @periods.has_key?(counter.field)
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                # Returns a True if a best is defined for the
         | 
| 80 | 
            +
                # given counter
         | 
| 81 | 
            +
                def bests_defined_for_counter?(counter)
         | 
| 82 | 
            +
                  @bests.has_key?(counter.field)
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                # Returns a True if a current is defined for the
         | 
| 86 | 
            +
                # given counter
         | 
| 87 | 
            +
                def currents_defined_for_counter?(counter)
         | 
| 88 | 
            +
                  @currents.has_key?(counter.field)
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                private
         | 
| 64 92 |  | 
| 65 | 
            -
             | 
| 93 | 
            +
                def set_period(field, period, length)
         | 
| 94 | 
            +
                  @periods[field] ||= []
         | 
| 95 | 
            +
                  @periods[field] << Period.new(period, length)
         | 
| 66 96 | 
             
                end
         | 
| 67 97 |  | 
| 68 | 
            -
                 | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
                  @periods.has_key?(key) && @periods[key].has_key?(period)
         | 
| 98 | 
            +
                def set_best(field, time_unit)
         | 
| 99 | 
            +
                  @bests[field] = [ time_unit ].flatten.map { |u|
         | 
| 100 | 
            +
                    Period.new(u)
         | 
| 101 | 
            +
                  }
         | 
| 73 102 | 
             
                end
         | 
| 74 103 |  | 
| 75 | 
            -
                 | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 104 | 
            +
                def set_current(field, time_unit)
         | 
| 105 | 
            +
                  @currents[field] = [ time_unit ].flatten.map { |u|
         | 
| 106 | 
            +
                    Period.new(u)
         | 
| 107 | 
            +
                  }
         | 
| 78 108 | 
             
                end
         | 
| 79 109 |  | 
| 80 110 | 
             
              end
         | 
| 81 | 
            -
            end
         | 
| 111 | 
            +
            end
         | 
    
        data/lib/von/counter.rb
    CHANGED
    
    | @@ -1,112 +1,70 @@ | |
| 1 1 | 
             
            module Von
         | 
| 2 2 | 
             
              class Counter
         | 
| 3 | 
            -
                PARENT_REGEX = /:?[^:]+\z/
         | 
| 4 3 |  | 
| 5 | 
            -
                # Initialize a new Counter
         | 
| 6 | 
            -
                #
         | 
| 7 | 
            -
                # field - counter field name
         | 
| 8 4 | 
             
                def initialize(field)
         | 
| 9 5 | 
             
                  @field = field.to_sym
         | 
| 10 6 | 
             
                end
         | 
| 11 7 |  | 
| 12 | 
            -
                 | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 8 | 
            +
                def to_s
         | 
| 9 | 
            +
                  Counters::Total.new(@field).count.to_s
         | 
| 10 | 
            +
                rescue Redis::BaseError => e
         | 
| 11 | 
            +
                  raise e if Von.config.raise_connection_errors
         | 
| 15 12 | 
             
                end
         | 
| 16 13 |  | 
| 17 | 
            -
                 | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 14 | 
            +
                def to_i
         | 
| 15 | 
            +
                  Counters::Total.new(@field).count
         | 
| 16 | 
            +
                rescue Redis::BaseError => e
         | 
| 17 | 
            +
                  raise e if Von.config.raise_connection_errors
         | 
| 20 18 | 
             
                end
         | 
| 21 19 |  | 
| 22 | 
            -
                 | 
| 23 | 
            -
             | 
| 24 | 
            -
                 | 
| 25 | 
            -
             | 
| 26 | 
            -
                def increment
         | 
| 27 | 
            -
                  total = Von.connection.hincrby(hash_key, 'total', 1)
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                  increment_periods
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                  total
         | 
| 20 | 
            +
                def total
         | 
| 21 | 
            +
                  Counters::Total.new(@field).count
         | 
| 22 | 
            +
                rescue Redis::BaseError => e
         | 
| 23 | 
            +
                  raise e if Von.config.raise_connection_errors
         | 
| 32 24 | 
             
                end
         | 
| 33 25 |  | 
| 34 | 
            -
                 | 
| 35 | 
            -
             | 
| 36 | 
            -
                  return unless Von.config.periods.has_key?(@field.to_sym)
         | 
| 26 | 
            +
                def per(unit)
         | 
| 27 | 
            +
                  periods = Von.config.periods[@field]
         | 
| 37 28 |  | 
| 38 | 
            -
                   | 
| 39 | 
            -
                     | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
                    end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                    if Von.connection.llen(period.list_key) > period.length
         | 
| 45 | 
            -
                      expired_counter = Von.connection.lpop(period.list_key)
         | 
| 46 | 
            -
                      Von.connection.hdel(period.hash_key, expired_counter)
         | 
| 47 | 
            -
                    end
         | 
| 29 | 
            +
                  if !Period.time_unit_exists?(unit)
         | 
| 30 | 
            +
                    raise ArgumentError, "`#{unit}' is an unknown time unit"
         | 
| 31 | 
            +
                  else
         | 
| 32 | 
            +
                    Counters::Period.new(@field, periods).count(unit)
         | 
| 48 33 | 
             
                  end
         | 
| 34 | 
            +
                rescue Redis::BaseError => e
         | 
| 35 | 
            +
                  raise e if Von.config.raise_connection_errors
         | 
| 49 36 | 
             
                end
         | 
| 50 37 |  | 
| 51 | 
            -
                 | 
| 52 | 
            -
             | 
| 53 | 
            -
                #
         | 
| 54 | 
            -
                # Returns the Integer total for the key
         | 
| 55 | 
            -
                def self.increment(field)
         | 
| 56 | 
            -
                  total   = Counter.new(field).increment
         | 
| 57 | 
            -
                  parents = field.sub(PARENT_REGEX, '')
         | 
| 38 | 
            +
                def best(unit)
         | 
| 39 | 
            +
                  periods = Von.config.bests[@field]
         | 
| 58 40 |  | 
| 59 | 
            -
                   | 
| 60 | 
            -
                     | 
| 61 | 
            -
             | 
| 41 | 
            +
                  if !Period.time_unit_exists?(unit)
         | 
| 42 | 
            +
                    raise ArgumentError, "`#{unit}' is an unknown time unit"
         | 
| 43 | 
            +
                  else
         | 
| 44 | 
            +
                    Counters::Best.new(@field, periods).count(unit)
         | 
| 62 45 | 
             
                  end
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                   | 
| 65 | 
            -
                end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                # Count the "total" field for this Counter.
         | 
| 68 | 
            -
                #
         | 
| 69 | 
            -
                # Returns an Integer count
         | 
| 70 | 
            -
                def count
         | 
| 71 | 
            -
                  Von.connection.hget(hash_key, 'total')
         | 
| 46 | 
            +
                rescue Redis::BaseError => e
         | 
| 47 | 
            +
                  raise e if Von.config.raise_connection_errors
         | 
| 72 48 | 
             
                end
         | 
| 73 49 |  | 
| 74 | 
            -
                 | 
| 75 | 
            -
             | 
| 76 | 
            -
                # Returns an Array of Hashes representing the count
         | 
| 77 | 
            -
                def count_period(period)
         | 
| 78 | 
            -
                  return unless Von.config.period_defined_for?(@field, period)
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                  _counts   = []
         | 
| 81 | 
            -
                  _period   = Von.config.periods[@field][period]
         | 
| 82 | 
            -
                  now       = DateTime.now.beginning_of_hour
         | 
| 50 | 
            +
                def this(unit)
         | 
| 51 | 
            +
                  periods = Von.config.currents[@field]
         | 
| 83 52 |  | 
| 84 | 
            -
                   | 
| 85 | 
            -
                     | 
| 86 | 
            -
             | 
| 87 | 
            -
                     | 
| 53 | 
            +
                  if !Period.time_unit_exists?(unit)
         | 
| 54 | 
            +
                    raise ArgumentError, "`#{unit}' is an unknown time unit"
         | 
| 55 | 
            +
                  else
         | 
| 56 | 
            +
                    Counters::Current.new(@field, periods).count(unit)
         | 
| 88 57 | 
             
                  end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                   | 
| 91 | 
            -
                  _counts.map { |date| { date => keys.fetch(date, 0) }}
         | 
| 58 | 
            +
                rescue Redis::BaseError => e
         | 
| 59 | 
            +
                  raise e if Von.config.raise_connection_errors
         | 
| 92 60 | 
             
                end
         | 
| 93 61 |  | 
| 94 | 
            -
                 | 
| 95 | 
            -
                # If a Period argument is given we lookup the count for
         | 
| 96 | 
            -
                # all of the possible units (not expired), zeroing ones that
         | 
| 97 | 
            -
                # aren't set in Redis already.
         | 
| 98 | 
            -
                #
         | 
| 99 | 
            -
                # period - A Period to lookup
         | 
| 100 | 
            -
                #
         | 
| 101 | 
            -
                # Returns an Integer representing the count or an Array of counts.
         | 
| 102 | 
            -
                def self.count(field, period = nil)
         | 
| 103 | 
            -
                  counter = Counter.new(field)
         | 
| 62 | 
            +
                alias :current :this
         | 
| 104 63 |  | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
                  end
         | 
| 64 | 
            +
                def today
         | 
| 65 | 
            +
                  periods = Von.config.currents[@field]
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  Counters::Current.new(@field, periods).count(:day)
         | 
| 110 68 | 
             
                end
         | 
| 111 69 |  | 
| 112 70 | 
             
              end
         | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            module Von
         | 
| 2 | 
            +
              module Counters
         | 
| 3 | 
            +
                class Best
         | 
| 4 | 
            +
                  include Von::Counters::Commands
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(field, periods = nil)
         | 
| 7 | 
            +
                    @field   = field.to_sym
         | 
| 8 | 
            +
                    @periods = periods || []
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def hash_key
         | 
| 12 | 
            +
                    @hash_key ||= "#{Von.config.namespace}:counters:bests:#{@field}"
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def best_total(time_unit)
         | 
| 16 | 
            +
                    hget("#{hash_key}:#{time_unit}:best", 'total').to_i
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def best_timestamp(time_unit)
         | 
| 20 | 
            +
                    hget("#{hash_key}:#{time_unit}:best", 'timestamp')
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def current_total(time_unit)
         | 
| 24 | 
            +
                    hget("#{hash_key}:#{time_unit}:current", 'total').to_i
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def current_timestamp(time_unit)
         | 
| 28 | 
            +
                    hget("#{hash_key}:#{time_unit}:current", 'timestamp')
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def increment
         | 
| 32 | 
            +
                    return if @periods.empty?
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    @periods.each do |period|
         | 
| 35 | 
            +
                      _current_timestamp = current_timestamp(period.time_unit)
         | 
| 36 | 
            +
                      _current_total     = current_total(period.time_unit)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      if period.timestamp != _current_timestamp
         | 
| 39 | 
            +
                        # changing current period
         | 
| 40 | 
            +
                        hset("#{hash_key}:#{period.time_unit}:current", 'total', 1)
         | 
| 41 | 
            +
                        hset("#{hash_key}:#{period.time_unit}:current", 'timestamp', period.timestamp)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                        if best_total(period) < _current_total
         | 
| 44 | 
            +
                          hset("#{hash_key}:#{period.time_unit}:best", 'total', _current_total)
         | 
| 45 | 
            +
                          hset("#{hash_key}:#{period.time_unit}:best", 'timestamp', _current_timestamp)
         | 
| 46 | 
            +
                        end
         | 
| 47 | 
            +
                      else
         | 
| 48 | 
            +
                        hincrby("#{hash_key}:#{period.time_unit}:current", 'total', 1)
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  def count(time_unit)
         | 
| 54 | 
            +
                    _current_timestamp = current_timestamp(time_unit)
         | 
| 55 | 
            +
                    _current_total     = current_total(time_unit)
         | 
| 56 | 
            +
                    _best_timestamp    = best_timestamp(time_unit)
         | 
| 57 | 
            +
                    _best_total        = best_total(time_unit)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    if _current_total > _best_total
         | 
| 60 | 
            +
                      { :timestamp => _current_timestamp, :count => _current_total }
         | 
| 61 | 
            +
                    else
         | 
| 62 | 
            +
                      { :timestamp => _best_timestamp, :count => _best_total }
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         |