tabs 0.8.2 → 0.9.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 +67 -3
- data/lib/tabs.rb +2 -1
- data/lib/tabs/config.rb +4 -0
- data/lib/tabs/metrics/counter.rb +1 -1
- data/lib/tabs/metrics/task.rb +2 -2
- data/lib/tabs/metrics/task/token.rb +22 -13
- data/lib/tabs/metrics/value.rb +1 -1
- data/lib/tabs/resolution.rb +17 -1
- data/lib/tabs/resolutionable.rb +23 -0
- data/lib/tabs/resolutions/day.rb +1 -0
- data/lib/tabs/resolutions/hour.rb +1 -0
- data/lib/tabs/resolutions/minute.rb +1 -0
- data/lib/tabs/resolutions/month.rb +1 -0
- data/lib/tabs/resolutions/week.rb +1 -0
- data/lib/tabs/resolutions/year.rb +1 -0
- data/lib/tabs/tabs.rb +0 -2
- data/lib/tabs/version.rb +1 -1
- data/spec/lib/tabs/metrics/counter_spec.rb +2 -2
- data/spec/lib/tabs/metrics/value_spec.rb +2 -2
- data/spec/lib/tabs/resolution_spec.rb +29 -0
- data/spec/lib/tabs/task_spec.rb +3 -3
- data/spec/support/custom_resolutions.rb +28 -0
- data/tabs.gemspec +0 -1
- metadata +7 -18
    
        data/README.md
    CHANGED
    
    | @@ -201,6 +201,67 @@ When tabs increments a counter or records a value it does so for each of the fol | |
| 201 201 |  | 
| 202 202 | 
             
            It automatically aggregates multiple events for the same period.  For instance when you increment a counter metric, 1 will be added for each of the resolutions for the current time.  Repeating the event 5 minutes later will increment a different minute slot, but the same hour, date, week, etc.  When you retrieve metrics, all timestamps will be in UTC.
         | 
| 203 203 |  | 
| 204 | 
            +
            #### Custom Resolutions
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            If the built-in resolutions above don't work you can add your own.  All
         | 
| 207 | 
            +
            that's necessary is a module that conforms to the following protocol:
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            ```ruby
         | 
| 210 | 
            +
            module SecondResolution
         | 
| 211 | 
            +
              extend Tabs::Resolutionable
         | 
| 212 | 
            +
              extend self
         | 
| 213 | 
            +
             | 
| 214 | 
            +
              PATTERN = "%Y-%m-%d-%H-%M-%S"
         | 
| 215 | 
            +
             | 
| 216 | 
            +
              def serialize(timestamp)
         | 
| 217 | 
            +
                timestamp.strftime(PATTERN)
         | 
| 218 | 
            +
              end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
              def deserialize(str)
         | 
| 221 | 
            +
                dt = DateTime.strptime(str, PATTERN)
         | 
| 222 | 
            +
                self.normalize(dt)
         | 
| 223 | 
            +
              end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
              def from_seconds(s)
         | 
| 226 | 
            +
                s / 1
         | 
| 227 | 
            +
              end
         | 
| 228 | 
            +
             | 
| 229 | 
            +
              def normalize(ts)
         | 
| 230 | 
            +
                Time.utc(ts.year, ts.month, ts.day, ts.hour, ts.min, ts.sec)
         | 
| 231 | 
            +
              end
         | 
| 232 | 
            +
            end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
            ```
         | 
| 235 | 
            +
             | 
| 236 | 
            +
            A little description on each of the above methods:
         | 
| 237 | 
            +
             | 
| 238 | 
            +
            *`serialize`*: converts the timestamp to a string.  The return value
         | 
| 239 | 
            +
            here will be used as part of the Redis key storing values associated
         | 
| 240 | 
            +
            with a given metric.
         | 
| 241 | 
            +
             | 
| 242 | 
            +
            *`deserialize`*: converts the string representation of a timestamp back
         | 
| 243 | 
            +
            into an actual DateTime value.
         | 
| 244 | 
            +
             | 
| 245 | 
            +
            *`from_seconds`*: should return the number of periods in the given
         | 
| 246 | 
            +
            number of seconds.  For example, there are 60 seconds in a minute.
         | 
| 247 | 
            +
             | 
| 248 | 
            +
            *`normalize`*: should simply return the first timestamp for the period.
         | 
| 249 | 
            +
            For example, the week resolution returns the first hour of the first day
         | 
| 250 | 
            +
            of the week.
         | 
| 251 | 
            +
             | 
| 252 | 
            +
            *NOTE: If you're doing a custom resolution you should probably look into
         | 
| 253 | 
            +
            the code a bit.*
         | 
| 254 | 
            +
             | 
| 255 | 
            +
            Once you have a module that conforms to the resolution protocol you need
         | 
| 256 | 
            +
            to register it with Tabs.  You can do this in one of two ways:
         | 
| 257 | 
            +
             | 
| 258 | 
            +
            ```ruby
         | 
| 259 | 
            +
            # This call can be anywhere before you start using tabs
         | 
| 260 | 
            +
            Tabs::Resolution.register(:second, SecondResolution)
         | 
| 261 | 
            +
             | 
| 262 | 
            +
            # or, you can use the config block described below
         | 
| 263 | 
            +
            ```
         | 
| 264 | 
            +
             | 
| 204 265 | 
             
            ### Inspecting Metrics
         | 
| 205 266 |  | 
| 206 267 | 
             
            You can list all metrics using `list_metrics`:
         | 
| @@ -255,12 +316,15 @@ Tabs.configure do |config| | |
| 255 316 | 
             
              # affects stat averages and task completion rate
         | 
| 256 317 | 
             
              config.decimal_precision = 2
         | 
| 257 318 |  | 
| 319 | 
            +
              # registers a custom resolution
         | 
| 320 | 
            +
              config.register_resolution :second, SecondResolution
         | 
| 321 | 
            +
             | 
| 258 322 | 
             
            end
         | 
| 259 323 | 
             
            ```
         | 
| 260 324 |  | 
| 261 325 | 
             
            ## Breaking Changes
         | 
| 262 326 |  | 
| 263 | 
            -
            ###  | 
| 327 | 
            +
            ### v0.6.0
         | 
| 264 328 |  | 
| 265 329 | 
             
            Please note that when the library version went from v0.5.6 to v0.6.0 some of
         | 
| 266 330 | 
             
            the key patterns used to store metrics in Redis were changed.  If you upgrade
         | 
| @@ -268,7 +332,7 @@ an app to v0.6.0 the previous set of data will not be picked up by tabs. | |
| 268 332 | 
             
            Please use v0.6.x on new applications only.  However, the 'Task' metric
         | 
| 269 333 | 
             
            type will only be available in v0.6.0 and above.
         | 
| 270 334 |  | 
| 271 | 
            -
            ###  | 
| 335 | 
            +
            ### v0.8.0
         | 
| 272 336 |  | 
| 273 337 | 
             
            In version 0.8.0 and higher the get_stats method returns a more robust
         | 
| 274 338 | 
             
            object instead of just an array of hashes.  These stats objects are
         | 
| @@ -276,7 +340,7 @@ enumerable and most existing code utilizing tabs should continue to | |
| 276 340 | 
             
            function.  However, please review the docs for more information if you
         | 
| 277 341 | 
             
            encounter issues when upgrading.
         | 
| 278 342 |  | 
| 279 | 
            -
            ###  | 
| 343 | 
            +
            ### v0.8.2
         | 
| 280 344 |  | 
| 281 345 | 
             
            In version 0.8.2 and higher the storage keys for value metrics have been
         | 
| 282 346 | 
             
            changed.  Originally the various pieces (avg, sum, count, etc) were
         | 
    
        data/lib/tabs.rb
    CHANGED
    
    | @@ -7,13 +7,14 @@ require "tabs/config" | |
| 7 7 | 
             
            require "tabs/storage"
         | 
| 8 8 | 
             
            require "tabs/helpers"
         | 
| 9 9 |  | 
| 10 | 
            -
            require "tabs/ | 
| 10 | 
            +
            require "tabs/resolutionable"
         | 
| 11 11 | 
             
            require "tabs/resolutions/minute"
         | 
| 12 12 | 
             
            require "tabs/resolutions/hour"
         | 
| 13 13 | 
             
            require "tabs/resolutions/day"
         | 
| 14 14 | 
             
            require "tabs/resolutions/week"
         | 
| 15 15 | 
             
            require "tabs/resolutions/month"
         | 
| 16 16 | 
             
            require "tabs/resolutions/year"
         | 
| 17 | 
            +
            require "tabs/resolution"
         | 
| 17 18 |  | 
| 18 19 | 
             
            require "tabs/metrics/counter/stats"
         | 
| 19 20 | 
             
            require "tabs/metrics/counter"
         | 
    
        data/lib/tabs/config.rb
    CHANGED
    
    
    
        data/lib/tabs/metrics/counter.rb
    CHANGED
    
    
    
        data/lib/tabs/metrics/task.rb
    CHANGED
    
    | @@ -34,10 +34,10 @@ module Tabs | |
| 34 34 | 
             
                    range = timestamp_range(period, resolution)
         | 
| 35 35 | 
             
                    started_tokens = tokens_for_period(range, resolution, "started")
         | 
| 36 36 | 
             
                    completed_tokens = tokens_for_period(range, resolution, "completed")
         | 
| 37 | 
            -
                    matching_tokens = started_tokens  | 
| 37 | 
            +
                    matching_tokens = started_tokens.select { |token| completed_tokens.include? token }
         | 
| 38 38 | 
             
                    completion_rate = (matching_tokens.size.to_f / range.size).round(Config.decimal_precision)
         | 
| 39 39 | 
             
                    elapsed_times = matching_tokens.map { |t| t.time_elapsed(resolution) }
         | 
| 40 | 
            -
                    average_completion_time = (elapsed_times. | 
| 40 | 
            +
                    average_completion_time = (elapsed_times.sum) / matching_tokens.size
         | 
| 41 41 | 
             
                    Stats.new(
         | 
| 42 42 | 
             
                      started_tokens.size,
         | 
| 43 43 | 
             
                      completed_tokens.size,
         | 
| @@ -1,62 +1,71 @@ | |
| 1 1 | 
             
            module Tabs
         | 
| 2 2 | 
             
              module Metrics
         | 
| 3 3 | 
             
                class Task
         | 
| 4 | 
            -
                  class Token | 
| 4 | 
            +
                  class Token
         | 
| 5 5 | 
             
                    include Storage
         | 
| 6 6 |  | 
| 7 7 | 
             
                    attr_reader :key
         | 
| 8 | 
            +
                    attr_reader :token
         | 
| 8 9 |  | 
| 9 10 | 
             
                    def initialize(token, key)
         | 
| 10 11 | 
             
                      @key = key
         | 
| 11 | 
            -
                       | 
| 12 | 
            +
                      @token = token
         | 
| 12 13 | 
             
                    end
         | 
| 13 14 |  | 
| 14 15 | 
             
                    def start(timestamp=Time.now)
         | 
| 15 16 | 
             
                      self.start_time = timestamp.utc
         | 
| 16 | 
            -
                      sadd("stat:task:#{key}:tokens",  | 
| 17 | 
            -
                      Tabs:: | 
| 17 | 
            +
                      sadd("stat:task:#{key}:tokens", token)
         | 
| 18 | 
            +
                      Tabs::Resolution.all.each { |res| record_start(res, start_time) }
         | 
| 18 19 | 
             
                    end
         | 
| 19 20 |  | 
| 20 21 | 
             
                    def complete(timestamp=Time.now)
         | 
| 21 22 | 
             
                      self.complete_time = timestamp.utc
         | 
| 22 | 
            -
                      unless sismember("stat:task:#{key}:tokens",  | 
| 23 | 
            -
                        raise UnstartedTaskMetricError.new("No task for metric '#{key}' was started with token '#{ | 
| 23 | 
            +
                      unless sismember("stat:task:#{key}:tokens", token)
         | 
| 24 | 
            +
                        raise UnstartedTaskMetricError.new("No task for metric '#{key}' was started with token '#{token}'")
         | 
| 24 25 | 
             
                      end
         | 
| 25 | 
            -
                      Tabs:: | 
| 26 | 
            +
                      Tabs::Resolution.all.each { |res| record_complete(res, complete_time) }
         | 
| 26 27 | 
             
                    end
         | 
| 27 28 |  | 
| 28 29 | 
             
                    def time_elapsed(resolution)
         | 
| 29 30 | 
             
                      Tabs::Resolution.from_seconds(resolution, complete_time - start_time)
         | 
| 30 31 | 
             
                    end
         | 
| 31 32 |  | 
| 33 | 
            +
                    def ==(other_token)
         | 
| 34 | 
            +
                      self.token == other_token.token
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    def to_s
         | 
| 38 | 
            +
                      "#{super}:#{token}"
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 32 41 | 
             
                    private
         | 
| 33 42 |  | 
| 34 43 | 
             
                    def record_start(resolution, timestamp)
         | 
| 35 44 | 
             
                      formatted_time = Tabs::Resolution.serialize(resolution, timestamp)
         | 
| 36 | 
            -
                      sadd("stat:task:#{key}:started:#{formatted_time}",  | 
| 45 | 
            +
                      sadd("stat:task:#{key}:started:#{formatted_time}", token)
         | 
| 37 46 | 
             
                    end
         | 
| 38 47 |  | 
| 39 48 | 
             
                    def record_complete(resolution, timestamp)
         | 
| 40 49 | 
             
                      formatted_time = Tabs::Resolution.serialize(resolution, timestamp)
         | 
| 41 | 
            -
                      sadd("stat:task:#{key}:completed:#{formatted_time}",  | 
| 50 | 
            +
                      sadd("stat:task:#{key}:completed:#{formatted_time}", token)
         | 
| 42 51 | 
             
                    end
         | 
| 43 52 |  | 
| 44 53 | 
             
                    def start_time=(timestamp)
         | 
| 45 | 
            -
                      set("stat:task:#{key}:#{ | 
| 54 | 
            +
                      set("stat:task:#{key}:#{token}:started_time", timestamp)
         | 
| 46 55 | 
             
                      @start_time = timestamp
         | 
| 47 56 | 
             
                    end
         | 
| 48 57 |  | 
| 49 58 | 
             
                    def start_time
         | 
| 50 | 
            -
                       | 
| 59 | 
            +
                      Time.parse(get("stat:task:#{key}:#{token}:started_time"))
         | 
| 51 60 | 
             
                    end
         | 
| 52 61 |  | 
| 53 62 | 
             
                    def complete_time=(timestamp)
         | 
| 54 | 
            -
                      set("stat:task:#{key}:#{ | 
| 63 | 
            +
                      set("stat:task:#{key}:#{token}:completed_time", timestamp)
         | 
| 55 64 | 
             
                      @complete_time = timestamp
         | 
| 56 65 | 
             
                    end
         | 
| 57 66 |  | 
| 58 67 | 
             
                    def complete_time
         | 
| 59 | 
            -
                       | 
| 68 | 
            +
                      Time.parse(get("stat:task:#{key}:#{token}:completed_time"))
         | 
| 60 69 | 
             
                    end
         | 
| 61 70 |  | 
| 62 71 | 
             
                  end
         | 
    
        data/lib/tabs/metrics/value.rb
    CHANGED
    
    | @@ -12,7 +12,7 @@ module Tabs | |
| 12 12 |  | 
| 13 13 | 
             
                  def record(value, timestamp=Time.now)
         | 
| 14 14 | 
             
                    timestamp.utc
         | 
| 15 | 
            -
                    Tabs:: | 
| 15 | 
            +
                    Tabs::Resolution.all.each do |resolution|
         | 
| 16 16 | 
             
                      formatted_time = Tabs::Resolution.serialize(resolution, timestamp)
         | 
| 17 17 | 
             
                      stat_key = "stat:value:#{key}:data:#{formatted_time}"
         | 
| 18 18 | 
             
                      update_values(stat_key, value)
         | 
    
        data/lib/tabs/resolution.rb
    CHANGED
    
    | @@ -2,6 +2,11 @@ module Tabs | |
| 2 2 | 
             
              module Resolution
         | 
| 3 3 | 
             
                extend self
         | 
| 4 4 |  | 
| 5 | 
            +
                def register(resolution, klass)
         | 
| 6 | 
            +
                  @@resolution_classes ||= {}
         | 
| 7 | 
            +
                  @@resolution_classes[resolution] = klass
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 5 10 | 
             
                def serialize(resolution, timestamp)
         | 
| 6 11 | 
             
                  resolution_klass(resolution).serialize(timestamp)
         | 
| 7 12 | 
             
                end
         | 
| @@ -18,11 +23,22 @@ module Tabs | |
| 18 23 | 
             
                  resolution_klass(resolution).normalize(timestamp)
         | 
| 19 24 | 
             
                end
         | 
| 20 25 |  | 
| 26 | 
            +
                def all
         | 
| 27 | 
            +
                  @@resolution_classes.keys
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 21 30 | 
             
                private
         | 
| 22 31 |  | 
| 23 32 | 
             
                def resolution_klass(resolution)
         | 
| 24 | 
            -
                   | 
| 33 | 
            +
                  @@resolution_classes[resolution]
         | 
| 25 34 | 
             
                end
         | 
| 26 35 |  | 
| 27 36 | 
             
              end
         | 
| 28 37 | 
             
            end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            Tabs::Resolution.register(:minute, Tabs::Resolutions::Minute)
         | 
| 40 | 
            +
            Tabs::Resolution.register(:hour, Tabs::Resolutions::Hour)
         | 
| 41 | 
            +
            Tabs::Resolution.register(:day, Tabs::Resolutions::Day)
         | 
| 42 | 
            +
            Tabs::Resolution.register(:week, Tabs::Resolutions::Week)
         | 
| 43 | 
            +
            Tabs::Resolution.register(:month, Tabs::Resolutions::Month)
         | 
| 44 | 
            +
            Tabs::Resolution.register(:year, Tabs::Resolutions::Year)
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module Tabs
         | 
| 2 | 
            +
              module Resolutionable
         | 
| 3 | 
            +
                extend self
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                class MethodNotImplementedException < Exception; end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def serialize
         | 
| 8 | 
            +
                  raise MethodNotImplementedException
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def deserialize
         | 
| 12 | 
            +
                  raise MethodNotImplementedException
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def from_seconds
         | 
| 16 | 
            +
                  raise MethodNotImplementedException
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def normalize
         | 
| 20 | 
            +
                  raise MethodNotImplementedException
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
    
        data/lib/tabs/resolutions/day.rb
    CHANGED
    
    
    
        data/lib/tabs/tabs.rb
    CHANGED
    
    
    
        data/lib/tabs/version.rb
    CHANGED
    
    
| @@ -126,7 +126,7 @@ describe Tabs::Metrics::Counter do | |
| 126 126 | 
             
                before do
         | 
| 127 127 | 
             
                  3.times { metric.increment }
         | 
| 128 128 | 
             
                  expect(exists("stat:counter:foo:total")).to be_true
         | 
| 129 | 
            -
                  @count_keys = (Tabs:: | 
| 129 | 
            +
                  @count_keys = (Tabs::Resolution.all.map do |res|
         | 
| 130 130 | 
             
                    smembers("stat:counter:foo:keys:#{res}")
         | 
| 131 131 | 
             
                  end).flatten
         | 
| 132 132 | 
             
                  metric.drop!
         | 
| @@ -143,7 +143,7 @@ describe Tabs::Metrics::Counter do | |
| 143 143 | 
             
                end
         | 
| 144 144 |  | 
| 145 145 | 
             
                it "deletes all resolution key collection keys" do
         | 
| 146 | 
            -
                  Tabs:: | 
| 146 | 
            +
                  Tabs::Resolution.all.each do |res|
         | 
| 147 147 | 
             
                    expect(exists("stat:counter:foo:keys:#{res}")).to be_false
         | 
| 148 148 | 
             
                  end
         | 
| 149 149 | 
             
                end
         | 
| @@ -101,7 +101,7 @@ describe Tabs::Metrics::Value do | |
| 101 101 |  | 
| 102 102 | 
             
                before do
         | 
| 103 103 | 
             
                  3.times { metric.record(rand(30)) }
         | 
| 104 | 
            -
                  @count_keys = (Tabs:: | 
| 104 | 
            +
                  @count_keys = (Tabs::Resolution.all.map do |res|
         | 
| 105 105 | 
             
                    smembers("stat:value:foo:keys:#{res}")
         | 
| 106 106 | 
             
                  end).flatten
         | 
| 107 107 | 
             
                  metric.drop!
         | 
| @@ -114,7 +114,7 @@ describe Tabs::Metrics::Value do | |
| 114 114 | 
             
                end
         | 
| 115 115 |  | 
| 116 116 | 
             
                it "deletes all resolution key collection keys" do
         | 
| 117 | 
            -
                  Tabs:: | 
| 117 | 
            +
                  Tabs::Resolution.all.each do |res|
         | 
| 118 118 | 
             
                    expect(exists("stat:value:foo:keys:#{res}")).to be_false
         | 
| 119 119 | 
             
                  end
         | 
| 120 120 | 
             
                end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
            require File.expand_path('../../../support/custom_resolutions', __FILE__)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe Tabs::Resolution do
         | 
| 5 | 
            +
              describe '#register' do
         | 
| 6 | 
            +
                it 'registers a new resolution' do
         | 
| 7 | 
            +
                  Tabs::Resolution.register(:test, Tabs::Resolutions::Minute)
         | 
| 8 | 
            +
                  expect(Tabs::Resolution.all).to include :test
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                context 'with a custom resolution' do
         | 
| 12 | 
            +
                  it 'does not return nil' do
         | 
| 13 | 
            +
                    expect(WellFormedResolution.serialize(Time.now)).to_not be_nil
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  it 'gets stats for custom resolution' do
         | 
| 17 | 
            +
                    Tabs::Resolution.register(:seconds, WellFormedResolution)
         | 
| 18 | 
            +
                    Timecop.freeze(Time.now)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    Tabs.increment_counter('foo')
         | 
| 21 | 
            +
                    expect(Tabs.get_stats('foo', (Time.now - 5.seconds..Time.now), :seconds).values.size).to eq(6)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  it 'raises an error when method not implemented' do
         | 
| 25 | 
            +
                    expect{BadlyFormedResolution.normalize}.to raise_error Tabs::Resolutionable::MethodNotImplementedException
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
    
        data/spec/lib/tabs/task_spec.rb
    CHANGED
    
    | @@ -4,9 +4,9 @@ describe Tabs::Metrics::Task do | |
| 4 4 |  | 
| 5 5 | 
             
              let(:metric) { Tabs.create_metric("foo", "task") }
         | 
| 6 6 | 
             
              let(:now) { Time.utc(2000, 1, 1, 0, 0) }
         | 
| 7 | 
            -
              let(:token_1) { " | 
| 8 | 
            -
              let(:token_2) { " | 
| 9 | 
            -
              let(:token_3) { " | 
| 7 | 
            +
              let(:token_1) { "aaaa" }
         | 
| 8 | 
            +
              let(:token_2) { "bbbb" }
         | 
| 9 | 
            +
              let(:token_3) { "cccc" }
         | 
| 10 10 |  | 
| 11 11 | 
             
              describe ".start" do
         | 
| 12 12 |  | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module BadlyFormedResolution
         | 
| 2 | 
            +
              extend Tabs::Resolutionable
         | 
| 3 | 
            +
              extend self
         | 
| 4 | 
            +
            end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module WellFormedResolution
         | 
| 7 | 
            +
              extend Tabs::Resolutionable
         | 
| 8 | 
            +
              extend self
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              PATTERN = "%Y-%m-%d-%H-%M-%S"
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def serialize(timestamp)
         | 
| 13 | 
            +
                timestamp.strftime(PATTERN)
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def deserialize(str)
         | 
| 17 | 
            +
                dt = DateTime.strptime(str, PATTERN)
         | 
| 18 | 
            +
                self.normalize(dt)
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def from_seconds(s)
         | 
| 22 | 
            +
                s / 1
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              def normalize(ts)
         | 
| 26 | 
            +
                Time.utc(ts.year, ts.month, ts.day, ts.hour, ts.min, ts.sec)
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
    
        data/tabs.gemspec
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: tabs
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.9.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2013-08- | 
| 12 | 
            +
            date: 2013-08-26 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: activesupport
         | 
| @@ -27,22 +27,6 @@ dependencies: | |
| 27 27 | 
             
                - - ! '>='
         | 
| 28 28 | 
             
                  - !ruby/object:Gem::Version
         | 
| 29 29 | 
             
                    version: '3.2'
         | 
| 30 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 31 | 
            -
              name: json
         | 
| 32 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 33 | 
            -
                none: false
         | 
| 34 | 
            -
                requirements:
         | 
| 35 | 
            -
                - - ! '>='
         | 
| 36 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 37 | 
            -
                    version: '1.7'
         | 
| 38 | 
            -
              type: :runtime
         | 
| 39 | 
            -
              prerelease: false
         | 
| 40 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 41 | 
            -
                none: false
         | 
| 42 | 
            -
                requirements:
         | 
| 43 | 
            -
                - - ! '>='
         | 
| 44 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 45 | 
            -
                    version: '1.7'
         | 
| 46 30 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 47 31 | 
             
              name: redis
         | 
| 48 32 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -164,6 +148,7 @@ files: | |
| 164 148 | 
             
            - lib/tabs/metrics/value.rb
         | 
| 165 149 | 
             
            - lib/tabs/metrics/value/stats.rb
         | 
| 166 150 | 
             
            - lib/tabs/resolution.rb
         | 
| 151 | 
            +
            - lib/tabs/resolutionable.rb
         | 
| 167 152 | 
             
            - lib/tabs/resolutions/day.rb
         | 
| 168 153 | 
             
            - lib/tabs/resolutions/hour.rb
         | 
| 169 154 | 
             
            - lib/tabs/resolutions/minute.rb
         | 
| @@ -178,9 +163,11 @@ files: | |
| 178 163 | 
             
            - spec/lib/tabs/metrics/task/token_spec.rb
         | 
| 179 164 | 
             
            - spec/lib/tabs/metrics/value/stats_spec.rb
         | 
| 180 165 | 
             
            - spec/lib/tabs/metrics/value_spec.rb
         | 
| 166 | 
            +
            - spec/lib/tabs/resolution_spec.rb
         | 
| 181 167 | 
             
            - spec/lib/tabs/task_spec.rb
         | 
| 182 168 | 
             
            - spec/lib/tabs_spec.rb
         | 
| 183 169 | 
             
            - spec/spec_helper.rb
         | 
| 170 | 
            +
            - spec/support/custom_resolutions.rb
         | 
| 184 171 | 
             
            - tabs.gemspec
         | 
| 185 172 | 
             
            homepage: https://github.com/thegrubbsian/tabs
         | 
| 186 173 | 
             
            licenses: []
         | 
| @@ -251,7 +238,9 @@ test_files: | |
| 251 238 | 
             
            - spec/lib/tabs/metrics/task/token_spec.rb
         | 
| 252 239 | 
             
            - spec/lib/tabs/metrics/value/stats_spec.rb
         | 
| 253 240 | 
             
            - spec/lib/tabs/metrics/value_spec.rb
         | 
| 241 | 
            +
            - spec/lib/tabs/resolution_spec.rb
         | 
| 254 242 | 
             
            - spec/lib/tabs/task_spec.rb
         | 
| 255 243 | 
             
            - spec/lib/tabs_spec.rb
         | 
| 256 244 | 
             
            - spec/spec_helper.rb
         | 
| 245 | 
            +
            - spec/support/custom_resolutions.rb
         | 
| 257 246 | 
             
            has_rdoc: 
         |