trifle-stats 1.0.0 → 1.1.1
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/trifle/stats/configuration.rb +2 -1
- data/lib/trifle/stats/designator/custom.rb +22 -0
- data/lib/trifle/stats/designator/geometric.rb +25 -0
- data/lib/trifle/stats/designator/linear.rb +24 -0
- data/lib/trifle/stats/driver/README.md +2 -2
- data/lib/trifle/stats/driver/mongo.rb +11 -8
- data/lib/trifle/stats/driver/postgres.rb +11 -7
- data/lib/trifle/stats/driver/process.rb +14 -10
- data/lib/trifle/stats/driver/redis.rb +11 -7
- data/lib/trifle/stats/operations/timeseries/classify.rb +48 -0
- data/lib/trifle/stats/operations/timeseries/increment.rb +9 -7
- data/lib/trifle/stats/operations/timeseries/set.rb +9 -7
- data/lib/trifle/stats/version.rb +1 -1
- data/lib/trifle/stats.rb +13 -0
- metadata +6 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d2ab7e87008ccd472bcbb2f821db69a304c0d2be078827150d828459c62b9ffa
         | 
| 4 | 
            +
              data.tar.gz: 7e6b5b77c70845d2c2d7b5b29f7a9811bba32f166f3a6204fb13e931c29ab90e
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 49870c3899c775ce1a91a857cc39f9cc92c05b6bb7ffe211ecf52adf033436942f5db44942cd4e2f84143b8111e586b25254f88f4d9c4cf8c9123adac20503e8
         | 
| 7 | 
            +
              data.tar.gz: 3dee093eed89e39a2dbc0c867dba0529359a9cba84f11c021fad8a78246e2e0d1115119ee896658e8ed917546757423432078faece8c582f559923b973170938
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
| @@ -6,12 +6,13 @@ module Trifle | |
| 6 6 | 
             
              module Stats
         | 
| 7 7 | 
             
                class Configuration
         | 
| 8 8 | 
             
                  attr_writer :driver
         | 
| 9 | 
            -
                  attr_accessor :track_ranges, :time_zone, :beginning_of_week
         | 
| 9 | 
            +
                  attr_accessor :track_ranges, :time_zone, :beginning_of_week, :designator
         | 
| 10 10 |  | 
| 11 11 | 
             
                  def initialize
         | 
| 12 12 | 
             
                    @ranges = %i[minute hour day week month quarter year]
         | 
| 13 13 | 
             
                    @beginning_of_week = :monday
         | 
| 14 14 | 
             
                    @time_zone = 'GMT'
         | 
| 15 | 
            +
                    @designator = nil
         | 
| 15 16 | 
             
                  end
         | 
| 16 17 |  | 
| 17 18 | 
             
                  def tz
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Trifle
         | 
| 4 | 
            +
              module Stats
         | 
| 5 | 
            +
                class Designator
         | 
| 6 | 
            +
                  class Custom
         | 
| 7 | 
            +
                    attr_reader :buckets
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def initialize(buckets:)
         | 
| 10 | 
            +
                      @buckets = buckets.sort
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def designate(value:)
         | 
| 14 | 
            +
                      return buckets.first.to_s if value <= buckets.first
         | 
| 15 | 
            +
                      return "#{buckets.last}+" if value > buckets.last
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      (buckets.find { |b| value.ceil < b }).to_s
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Trifle
         | 
| 4 | 
            +
              module Stats
         | 
| 5 | 
            +
                class Designator
         | 
| 6 | 
            +
                  class Geometric
         | 
| 7 | 
            +
                    attr_reader :min, :max
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def initialize(min:, max:)
         | 
| 10 | 
            +
                      @min = min.negative? ? 0 : min
         | 
| 11 | 
            +
                      @max = max
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    def designate(value:) # rubocop:disable Metrics/AbcSize
         | 
| 15 | 
            +
                      return min.to_f.to_s if value <= min
         | 
| 16 | 
            +
                      return "#{max.to_f}+" if value > max
         | 
| 17 | 
            +
                      return (10**value.floor.to_s.length).to_f.to_s if value > 1
         | 
| 18 | 
            +
                      return 1.0.to_s if value > 0.1 # ugh?
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      (1.0 / 10**value.to_s.gsub('0.', '').split(/[1-9]/).first.length).to_s
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Trifle
         | 
| 4 | 
            +
              module Stats
         | 
| 5 | 
            +
                class Designator
         | 
| 6 | 
            +
                  class Linear
         | 
| 7 | 
            +
                    attr_reader :min, :max, :step
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def initialize(min:, max:, step:)
         | 
| 10 | 
            +
                      @min = min
         | 
| 11 | 
            +
                      @max = max
         | 
| 12 | 
            +
                      @step = step.to_i
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def designate(value:) # rubocop:disable Metrics/AbcSize
         | 
| 16 | 
            +
                      return min.to_s if value <= min
         | 
| 17 | 
            +
                      return "#{max}+" if value > max
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      (value.ceil / step * step + ((value.ceil % step).zero? ? 0 : step)).to_s
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -2,8 +2,8 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            Driver is a wrapper class that persists and retrieves values from backend. It needs to implement:
         | 
| 4 4 |  | 
| 5 | 
            -
            - `inc( | 
| 6 | 
            -
            - `set( | 
| 5 | 
            +
            - `inc(keys:, **values)` method increment values
         | 
| 6 | 
            +
            - `set(keys:, **values)` method set values
         | 
| 7 7 | 
             
            - `get(keys:)` method to retrieve values
         | 
| 8 8 |  | 
| 9 9 | 
             
            ## Documentation
         | 
| @@ -16,24 +16,27 @@ module Trifle | |
| 16 16 | 
             
                      @separator = '::'
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 | 
            -
                    def inc( | 
| 20 | 
            -
                       | 
| 19 | 
            +
                    def inc(keys:, **values)
         | 
| 20 | 
            +
                      data = self.class.pack(hash: { data: values })
         | 
| 21 21 |  | 
| 22 22 | 
             
                      collection.bulk_write(
         | 
| 23 | 
            -
                         | 
| 23 | 
            +
                        keys.map do |key|
         | 
| 24 | 
            +
                          upsert_operation('$inc', pkey: key.join(separator), data: data)
         | 
| 25 | 
            +
                        end
         | 
| 24 26 | 
             
                      )
         | 
| 25 27 | 
             
                    end
         | 
| 26 28 |  | 
| 27 | 
            -
                    def set( | 
| 28 | 
            -
                       | 
| 29 | 
            +
                    def set(keys:, **values)
         | 
| 30 | 
            +
                      data = self.class.pack(hash: { data: values })
         | 
| 29 31 |  | 
| 30 32 | 
             
                      collection.bulk_write(
         | 
| 31 | 
            -
                         | 
| 33 | 
            +
                        keys.map do |key|
         | 
| 34 | 
            +
                          upsert_operation('$set', pkey: key.join(separator), data: data)
         | 
| 35 | 
            +
                        end
         | 
| 32 36 | 
             
                      )
         | 
| 33 37 | 
             
                    end
         | 
| 34 38 |  | 
| 35 | 
            -
                    def upsert_operation(operation, pkey:,  | 
| 36 | 
            -
                      data = self.class.pack(hash: { data: values })
         | 
| 39 | 
            +
                    def upsert_operation(operation, pkey:, data:)
         | 
| 37 40 | 
             
                      {
         | 
| 38 41 | 
             
                        update_many: {
         | 
| 39 42 | 
             
                          filter: { key: pkey },
         | 
| @@ -16,11 +16,13 @@ module Trifle | |
| 16 16 | 
             
                      @separator = '::'
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 | 
            -
                    def inc( | 
| 20 | 
            -
                       | 
| 19 | 
            +
                    def inc(keys:, **values)
         | 
| 20 | 
            +
                      keys.map do |key|
         | 
| 21 | 
            +
                        pkey = key.join(separator)
         | 
| 21 22 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 23 | 
            +
                        self.class.pack(hash: values).each do |k, c|
         | 
| 24 | 
            +
                          _inc_one(key: pkey, name: k, value: c)
         | 
| 25 | 
            +
                        end
         | 
| 24 26 | 
             
                      end
         | 
| 25 27 | 
             
                    end
         | 
| 26 28 |  | 
| @@ -31,10 +33,12 @@ module Trifle | |
| 31 33 | 
             
                      client.exec(query)
         | 
| 32 34 | 
             
                    end
         | 
| 33 35 |  | 
| 34 | 
            -
                    def set( | 
| 35 | 
            -
                       | 
| 36 | 
            +
                    def set(keys:, **values)
         | 
| 37 | 
            +
                      keys.map do |key|
         | 
| 38 | 
            +
                        pkey = key.join(separator)
         | 
| 36 39 |  | 
| 37 | 
            -
             | 
| 40 | 
            +
                        _set_all(key: pkey, **values)
         | 
| 41 | 
            +
                      end
         | 
| 38 42 | 
             
                    end
         | 
| 39 43 |  | 
| 40 44 | 
             
                    def _set_all(key:, **values)
         | 
| @@ -12,19 +12,23 @@ module Trifle | |
| 12 12 | 
             
                      @separator = '::'
         | 
| 13 13 | 
             
                    end
         | 
| 14 14 |  | 
| 15 | 
            -
                    def inc( | 
| 16 | 
            -
                       | 
| 17 | 
            -
                         | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 15 | 
            +
                    def inc(keys:, **values)
         | 
| 16 | 
            +
                      keys.map do |key|
         | 
| 17 | 
            +
                        self.class.pack(hash: values).each do |k, c|
         | 
| 18 | 
            +
                          d = @data.fetch(key.join(@separator), {})
         | 
| 19 | 
            +
                          d[k] = d[k].to_i + c
         | 
| 20 | 
            +
                          @data[key.join(@separator)] = d
         | 
| 21 | 
            +
                        end
         | 
| 20 22 | 
             
                      end
         | 
| 21 23 | 
             
                    end
         | 
| 22 24 |  | 
| 23 | 
            -
                    def set( | 
| 24 | 
            -
                       | 
| 25 | 
            -
                         | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 25 | 
            +
                    def set(keys:, **values)
         | 
| 26 | 
            +
                      keys.map do |key|
         | 
| 27 | 
            +
                        self.class.pack(hash: values).each do |k, c|
         | 
| 28 | 
            +
                          d = @data.fetch(key.join(@separator), {})
         | 
| 29 | 
            +
                          d[k] = c
         | 
| 30 | 
            +
                          @data[key.join(@separator)] = d
         | 
| 31 | 
            +
                        end
         | 
| 28 32 | 
             
                      end
         | 
| 29 33 | 
             
                    end
         | 
| 30 34 |  | 
| @@ -16,18 +16,22 @@ module Trifle | |
| 16 16 | 
             
                      @separator = '::'
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 | 
            -
                    def inc( | 
| 20 | 
            -
                       | 
| 19 | 
            +
                    def inc(keys:, **values)
         | 
| 20 | 
            +
                      keys.map do |key|
         | 
| 21 | 
            +
                        pkey = ([prefix] + key).join(separator)
         | 
| 21 22 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 23 | 
            +
                        self.class.pack(hash: values).each do |k, c|
         | 
| 24 | 
            +
                          client.hincrby(pkey, k, c)
         | 
| 25 | 
            +
                        end
         | 
| 24 26 | 
             
                      end
         | 
| 25 27 | 
             
                    end
         | 
| 26 28 |  | 
| 27 | 
            -
                    def set( | 
| 28 | 
            -
                       | 
| 29 | 
            +
                    def set(keys:, **values)
         | 
| 30 | 
            +
                      keys.map do |key|
         | 
| 31 | 
            +
                        pkey = ([prefix] + key).join(separator)
         | 
| 29 32 |  | 
| 30 | 
            -
             | 
| 33 | 
            +
                        client.hmset(pkey, *self.class.pack(hash: values))
         | 
| 34 | 
            +
                      end
         | 
| 31 35 | 
             
                    end
         | 
| 32 36 |  | 
| 33 37 | 
             
                    def get(keys:)
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Trifle
         | 
| 4 | 
            +
              module Stats
         | 
| 5 | 
            +
                module Operations
         | 
| 6 | 
            +
                  module Timeseries
         | 
| 7 | 
            +
                    class Classify
         | 
| 8 | 
            +
                      attr_reader :key, :values
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                      def initialize(**keywords)
         | 
| 11 | 
            +
                        @key = keywords.fetch(:key)
         | 
| 12 | 
            +
                        @at = keywords.fetch(:at)
         | 
| 13 | 
            +
                        @values = keywords.fetch(:values)
         | 
| 14 | 
            +
                        @config = keywords[:config]
         | 
| 15 | 
            +
                      end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      def config
         | 
| 18 | 
            +
                        @config || Trifle::Stats.default
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      def deep_classify(hash)
         | 
| 22 | 
            +
                        hash.transform_values do |value|
         | 
| 23 | 
            +
                          next deep_classify(value) if value.is_a?(Hash)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                          { classify(value) => 1 }
         | 
| 26 | 
            +
                        end
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                      def classify(value)
         | 
| 30 | 
            +
                        config.designator.designate(value: value).to_s.gsub('.', '_')
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      def key_for(range:)
         | 
| 34 | 
            +
                        at = Nocturnal.new(@at, config: config).send(range)
         | 
| 35 | 
            +
                        [key, range, at.to_i]
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      def perform
         | 
| 39 | 
            +
                        config.driver.inc(
         | 
| 40 | 
            +
                          keys: config.ranges.map { |range| key_for(range: range) },
         | 
| 41 | 
            +
                          **deep_classify(values)
         | 
| 42 | 
            +
                        )
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| @@ -18,14 +18,16 @@ module Trifle | |
| 18 18 | 
             
                        @config || Trifle::Stats.default
         | 
| 19 19 | 
             
                      end
         | 
| 20 20 |  | 
| 21 | 
            +
                      def key_for(range:)
         | 
| 22 | 
            +
                        at = Nocturnal.new(@at, config: config).send(range)
         | 
| 23 | 
            +
                        [key, range, at.to_i]
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
             | 
| 21 26 | 
             
                      def perform
         | 
| 22 | 
            -
                        config. | 
| 23 | 
            -
                           | 
| 24 | 
            -
                           | 
| 25 | 
            -
             | 
| 26 | 
            -
                            **values
         | 
| 27 | 
            -
                          )
         | 
| 28 | 
            -
                        end
         | 
| 27 | 
            +
                        config.driver.inc(
         | 
| 28 | 
            +
                          keys: config.ranges.map { |range| key_for(range: range) },
         | 
| 29 | 
            +
                          **values
         | 
| 30 | 
            +
                        )
         | 
| 29 31 | 
             
                      end
         | 
| 30 32 | 
             
                    end
         | 
| 31 33 | 
             
                  end
         | 
| @@ -18,14 +18,16 @@ module Trifle | |
| 18 18 | 
             
                        @config || Trifle::Stats.default
         | 
| 19 19 | 
             
                      end
         | 
| 20 20 |  | 
| 21 | 
            +
                      def key_for(range:)
         | 
| 22 | 
            +
                        at = Nocturnal.new(@at, config: config).send(range)
         | 
| 23 | 
            +
                        [key, range, at.to_i]
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
             | 
| 21 26 | 
             
                      def perform
         | 
| 22 | 
            -
                        config. | 
| 23 | 
            -
                           | 
| 24 | 
            -
                           | 
| 25 | 
            -
             | 
| 26 | 
            -
                            **values
         | 
| 27 | 
            -
                          )
         | 
| 28 | 
            -
                        end
         | 
| 27 | 
            +
                        config.driver.set(
         | 
| 28 | 
            +
                          keys: config.ranges.map { |range| key_for(range: range) },
         | 
| 29 | 
            +
                          **values
         | 
| 30 | 
            +
                        )
         | 
| 29 31 | 
             
                      end
         | 
| 30 32 | 
             
                    end
         | 
| 31 33 | 
             
                  end
         | 
    
        data/lib/trifle/stats/version.rb
    CHANGED
    
    
    
        data/lib/trifle/stats.rb
    CHANGED
    
    | @@ -1,5 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'trifle/stats/designator/custom'
         | 
| 4 | 
            +
            require 'trifle/stats/designator/geometric'
         | 
| 5 | 
            +
            require 'trifle/stats/designator/linear'
         | 
| 3 6 | 
             
            require 'trifle/stats/driver/mongo'
         | 
| 4 7 | 
             
            require 'trifle/stats/driver/postgres'
         | 
| 5 8 | 
             
            require 'trifle/stats/driver/process'
         | 
| @@ -7,6 +10,7 @@ require 'trifle/stats/driver/redis' | |
| 7 10 | 
             
            require 'trifle/stats/mixins/packer'
         | 
| 8 11 | 
             
            require 'trifle/stats/nocturnal'
         | 
| 9 12 | 
             
            require 'trifle/stats/configuration'
         | 
| 13 | 
            +
            require 'trifle/stats/operations/timeseries/classify'
         | 
| 10 14 | 
             
            require 'trifle/stats/operations/timeseries/increment'
         | 
| 11 15 | 
             
            require 'trifle/stats/operations/timeseries/set'
         | 
| 12 16 | 
             
            require 'trifle/stats/operations/timeseries/values'
         | 
| @@ -45,6 +49,15 @@ module Trifle | |
| 45 49 | 
             
                  ).perform
         | 
| 46 50 | 
             
                end
         | 
| 47 51 |  | 
| 52 | 
            +
                def self.assort(key:, at:, values:, config: nil)
         | 
| 53 | 
            +
                  Trifle::Stats::Operations::Timeseries::Classify.new(
         | 
| 54 | 
            +
                    key: key,
         | 
| 55 | 
            +
                    at: at,
         | 
| 56 | 
            +
                    values: values,
         | 
| 57 | 
            +
                    config: config
         | 
| 58 | 
            +
                  ).perform
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 48 61 | 
             
                def self.values(key:, from:, to:, range:, config: nil)
         | 
| 49 62 | 
             
                  Trifle::Stats::Operations::Timeseries::Values.new(
         | 
| 50 63 | 
             
                    key: key,
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: trifle-stats
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.1.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Jozef Vaclavik
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022- | 
| 11 | 
            +
            date: 2022-07-12 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         | 
| @@ -183,6 +183,9 @@ files: | |
| 183 183 | 
             
            - bin/setup
         | 
| 184 184 | 
             
            - lib/trifle/stats.rb
         | 
| 185 185 | 
             
            - lib/trifle/stats/configuration.rb
         | 
| 186 | 
            +
            - lib/trifle/stats/designator/custom.rb
         | 
| 187 | 
            +
            - lib/trifle/stats/designator/geometric.rb
         | 
| 188 | 
            +
            - lib/trifle/stats/designator/linear.rb
         | 
| 186 189 | 
             
            - lib/trifle/stats/driver/README.md
         | 
| 187 190 | 
             
            - lib/trifle/stats/driver/mongo.rb
         | 
| 188 191 | 
             
            - lib/trifle/stats/driver/postgres.rb
         | 
| @@ -190,6 +193,7 @@ files: | |
| 190 193 | 
             
            - lib/trifle/stats/driver/redis.rb
         | 
| 191 194 | 
             
            - lib/trifle/stats/mixins/packer.rb
         | 
| 192 195 | 
             
            - lib/trifle/stats/nocturnal.rb
         | 
| 196 | 
            +
            - lib/trifle/stats/operations/timeseries/classify.rb
         | 
| 193 197 | 
             
            - lib/trifle/stats/operations/timeseries/increment.rb
         | 
| 194 198 | 
             
            - lib/trifle/stats/operations/timeseries/set.rb
         | 
| 195 199 | 
             
            - lib/trifle/stats/operations/timeseries/values.rb
         |