tabstabs 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +421 -0
- data/Rakefile +5 -0
- data/lib/tabs_tabs.rb +26 -0
- data/lib/tabs_tabs/config.rb +65 -0
- data/lib/tabs_tabs/helpers.rb +27 -0
- data/lib/tabs_tabs/metrics/counter.rb +69 -0
- data/lib/tabs_tabs/metrics/counter/stats.rb +51 -0
- data/lib/tabs_tabs/metrics/task.rb +72 -0
- data/lib/tabs_tabs/metrics/task/token.rb +89 -0
- data/lib/tabs_tabs/metrics/value.rb +91 -0
- data/lib/tabs_tabs/metrics/value/stats.rb +55 -0
- data/lib/tabs_tabs/resolution.rb +65 -0
- data/lib/tabs_tabs/resolutionable.rb +48 -0
- data/lib/tabs_tabs/resolutions/day.rb +40 -0
- data/lib/tabs_tabs/resolutions/hour.rb +40 -0
- data/lib/tabs_tabs/resolutions/minute.rb +40 -0
- data/lib/tabs_tabs/resolutions/month.rb +40 -0
- data/lib/tabs_tabs/resolutions/week.rb +40 -0
- data/lib/tabs_tabs/resolutions/year.rb +40 -0
- data/lib/tabs_tabs/storage.rb +105 -0
- data/lib/tabs_tabs/tabs_tabs.rb +117 -0
- data/lib/tabs_tabs/version.rb +3 -0
- data/spec/lib/tabs_tabs/config_spec.rb +60 -0
- data/spec/lib/tabs_tabs/metrics/counter/stats_spec.rb +42 -0
- data/spec/lib/tabs_tabs/metrics/counter_spec.rb +196 -0
- data/spec/lib/tabs_tabs/metrics/task/token_spec.rb +18 -0
- data/spec/lib/tabs_tabs/metrics/task_spec.rb +103 -0
- data/spec/lib/tabs_tabs/metrics/value/stats_spec.rb +61 -0
- data/spec/lib/tabs_tabs/metrics/value_spec.rb +160 -0
- data/spec/lib/tabs_tabs/resolution_spec.rb +52 -0
- data/spec/lib/tabs_tabs/resolutionable_spec.rb +53 -0
- data/spec/lib/tabs_tabs/resolutions/day_spec.rb +23 -0
- data/spec/lib/tabs_tabs/resolutions/hour_spec.rb +23 -0
- data/spec/lib/tabs_tabs/resolutions/minute_spec.rb +23 -0
- data/spec/lib/tabs_tabs/resolutions/month_spec.rb +23 -0
- data/spec/lib/tabs_tabs/resolutions/week_spec.rb +24 -0
- data/spec/lib/tabs_tabs/resolutions/year_spec.rb +23 -0
- data/spec/lib/tabs_tabs/storage_spec.rb +138 -0
- data/spec/lib/tabs_tabs_spec.rb +223 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/custom_resolutions.rb +40 -0
- data/tabs_tabs.gemspec +31 -0
- metadata +213 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Metrics
|
3
|
+
class Value
|
4
|
+
class Stats
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
attr_reader :period, :resolution, :values
|
10
|
+
|
11
|
+
def initialize(period, resolution, values)
|
12
|
+
@period, @resolution, @values = period, resolution, values
|
13
|
+
end
|
14
|
+
|
15
|
+
def first
|
16
|
+
values.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def last
|
20
|
+
values.last
|
21
|
+
end
|
22
|
+
|
23
|
+
def count
|
24
|
+
@count ||= values.map { |v| v["count"] }.sum
|
25
|
+
end
|
26
|
+
|
27
|
+
def sum
|
28
|
+
@sum ||= values.map { |v| v["sum"] }.sum
|
29
|
+
end
|
30
|
+
|
31
|
+
def min
|
32
|
+
@min ||= values.min_by { |v| v["min"] }["min"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def max
|
36
|
+
@max ||= values.max_by { |v| v["max"] }["max"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def avg
|
40
|
+
return 0 if count.zero?
|
41
|
+
(self.sum.to_f / self.count.to_f).round(Config.decimal_precision)
|
42
|
+
end
|
43
|
+
|
44
|
+
def each(&block)
|
45
|
+
values.each(&block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_a
|
49
|
+
values
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolution
|
3
|
+
include Resolutionable
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def register(klass)
|
7
|
+
@@resolution_classes ||= {}
|
8
|
+
@@resolution_classes[klass.name] = klass
|
9
|
+
end
|
10
|
+
|
11
|
+
def unregister(resolutions)
|
12
|
+
resolutions = Array[resolutions].flatten
|
13
|
+
resolutions.each{ |res| @@resolution_classes.delete(res) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def serialize(resolution, timestamp)
|
17
|
+
resolution_klass(resolution).serialize(timestamp)
|
18
|
+
end
|
19
|
+
|
20
|
+
def deserialize(resolution, str)
|
21
|
+
resolution_klass(resolution).deserialize(str)
|
22
|
+
end
|
23
|
+
|
24
|
+
def from_seconds(resolution, s)
|
25
|
+
resolution_klass(resolution).from_seconds(s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def add(resolution, ts, num)
|
29
|
+
resolution_klass(resolution).add(ts, num)
|
30
|
+
end
|
31
|
+
|
32
|
+
def normalize(resolution, timestamp)
|
33
|
+
resolution_klass(resolution).normalize(timestamp)
|
34
|
+
end
|
35
|
+
|
36
|
+
def all
|
37
|
+
@@resolution_classes.keys
|
38
|
+
end
|
39
|
+
|
40
|
+
def expire(resolution, key, timestamp)
|
41
|
+
resolution_klass(resolution).expire(key, timestamp)
|
42
|
+
end
|
43
|
+
|
44
|
+
def reset_default_resolutions
|
45
|
+
@@resolution_classes = {}
|
46
|
+
TabsTabs::Resolution.register(TabsTabs::Resolutions::Minute)
|
47
|
+
TabsTabs::Resolution.register(TabsTabs::Resolutions::Hour)
|
48
|
+
TabsTabs::Resolution.register(TabsTabs::Resolutions::Day)
|
49
|
+
TabsTabs::Resolution.register(TabsTabs::Resolutions::Week)
|
50
|
+
TabsTabs::Resolution.register(TabsTabs::Resolutions::Month)
|
51
|
+
TabsTabs::Resolution.register(TabsTabs::Resolutions::Year)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def resolution_klass(resolution)
|
57
|
+
klass = @@resolution_classes[resolution]
|
58
|
+
raise TabsTabs::ResolutionMissingError.new(resolution) unless klass
|
59
|
+
klass
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
TabsTabs::Resolution.reset_default_resolutions
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolutionable
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
def name
|
11
|
+
raise "Must implement #name in the concrete resolution module"
|
12
|
+
end
|
13
|
+
|
14
|
+
def serialize
|
15
|
+
raise "Must implement #serialize in the concrete resolution module"
|
16
|
+
end
|
17
|
+
|
18
|
+
def deserialize
|
19
|
+
raise "Must implement #deserialize in the concrete resolution module"
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_seconds
|
23
|
+
raise "Must implement #from_seconds in the concrete resolution module"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_seconds
|
27
|
+
raise "Must implement #to_seconds in the concrete resolution module"
|
28
|
+
end
|
29
|
+
|
30
|
+
def add
|
31
|
+
raise "Must implement #to_seconds in the concrete resolution module"
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize
|
35
|
+
raise "Must implement #normalize in the concrete resolution module"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def expire(key, timestamp)
|
41
|
+
return unless TabsTabs::Config.expires?(name)
|
42
|
+
resolution_ends_at = timestamp.utc.to_i + to_seconds
|
43
|
+
expires_at = resolution_ends_at + TabsTabs::Config.expires_in(name)
|
44
|
+
Storage.expireat(key, expires_at)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolutions
|
3
|
+
module Day
|
4
|
+
include TabsTabs::Resolutionable
|
5
|
+
extend self
|
6
|
+
|
7
|
+
PATTERN = "%Y-%m-%d"
|
8
|
+
|
9
|
+
def name
|
10
|
+
:day
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(timestamp)
|
14
|
+
timestamp.strftime(PATTERN)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(str)
|
18
|
+
dt = DateTime.strptime(str, PATTERN)
|
19
|
+
self.normalize(dt)
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_seconds(s)
|
23
|
+
s / 1.day
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_seconds
|
27
|
+
1.day
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(ts, num)
|
31
|
+
ts + num.days
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize(ts)
|
35
|
+
Time.utc(ts.year, ts.month, ts.day)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolutions
|
3
|
+
module Hour
|
4
|
+
include TabsTabs::Resolutionable
|
5
|
+
extend self
|
6
|
+
|
7
|
+
PATTERN = "%Y-%m-%d-%H"
|
8
|
+
|
9
|
+
def name
|
10
|
+
:hour
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(timestamp)
|
14
|
+
timestamp.strftime(PATTERN)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(str)
|
18
|
+
dt = DateTime.strptime(str, PATTERN)
|
19
|
+
self.normalize(dt)
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_seconds(s)
|
23
|
+
s / 1.hour
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_seconds
|
27
|
+
1.hour
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(ts, num)
|
31
|
+
ts + num.hours
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize(ts)
|
35
|
+
Time.utc(ts.year, ts.month, ts.day, ts.hour)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolutions
|
3
|
+
module Minute
|
4
|
+
include TabsTabs::Resolutionable
|
5
|
+
extend self
|
6
|
+
|
7
|
+
PATTERN = "%Y-%m-%d-%H-%M"
|
8
|
+
|
9
|
+
def name
|
10
|
+
:minute
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(timestamp)
|
14
|
+
timestamp.strftime(PATTERN)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(str)
|
18
|
+
dt = DateTime.strptime(str, PATTERN)
|
19
|
+
self.normalize(dt)
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_seconds(s)
|
23
|
+
s / 1.minute
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_seconds
|
27
|
+
1.minute
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(ts, num)
|
31
|
+
ts + num.minutes
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize(ts)
|
35
|
+
Time.utc(ts.year, ts.month, ts.day, ts.hour, ts.min)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolutions
|
3
|
+
module Month
|
4
|
+
include TabsTabs::Resolutionable
|
5
|
+
extend self
|
6
|
+
|
7
|
+
PATTERN = "%Y-%m"
|
8
|
+
|
9
|
+
def name
|
10
|
+
:month
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(timestamp)
|
14
|
+
timestamp.strftime(PATTERN)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(str)
|
18
|
+
dt = DateTime.strptime(str, PATTERN)
|
19
|
+
self.normalize(dt)
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_seconds(s)
|
23
|
+
s / 1.month
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_seconds
|
27
|
+
1.month
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(ts, num)
|
31
|
+
ts + num.months
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize(ts)
|
35
|
+
Time.utc(ts.year, ts.month)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolutions
|
3
|
+
module Week
|
4
|
+
include TabsTabs::Resolutionable
|
5
|
+
extend self
|
6
|
+
|
7
|
+
PATTERN = "%Y-%m-%d"
|
8
|
+
|
9
|
+
def name
|
10
|
+
:week
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(timestamp)
|
14
|
+
normalize(timestamp).strftime(PATTERN)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(str)
|
18
|
+
dt = DateTime.strptime(str, PATTERN)
|
19
|
+
self.normalize(dt)
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_seconds(s)
|
23
|
+
s / 1.week
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_seconds
|
27
|
+
1.week
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(ts, num)
|
31
|
+
ts + num.weeks
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize(ts)
|
35
|
+
Time.utc(ts.year, ts.month, ts.day).beginning_of_week
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Resolutions
|
3
|
+
module Year
|
4
|
+
include TabsTabs::Resolutionable
|
5
|
+
extend self
|
6
|
+
|
7
|
+
PATTERN = "%Y"
|
8
|
+
|
9
|
+
def name
|
10
|
+
:year
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(timestamp)
|
14
|
+
timestamp.strftime(PATTERN)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(str)
|
18
|
+
dt = DateTime.strptime(str, PATTERN)
|
19
|
+
self.normalize(dt)
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_seconds(s)
|
23
|
+
s / 1.year
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_seconds
|
27
|
+
1.year
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(ts, num)
|
31
|
+
ts + num.years
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize(ts)
|
35
|
+
Time.utc(ts.year)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module TabsTabs
|
2
|
+
module Storage
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def redis
|
6
|
+
@redis ||= Config.redis
|
7
|
+
end
|
8
|
+
|
9
|
+
def tabs_key(key)
|
10
|
+
if TabsTabs::Config.prefix.blank?
|
11
|
+
"tabstabs:#{key}"
|
12
|
+
else
|
13
|
+
"tabstabs:#{TabsTabs::Config.prefix}:#{key}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def exists(key)
|
18
|
+
redis.exists(tabs_key(key))
|
19
|
+
end
|
20
|
+
|
21
|
+
def expireat(key, unix_timestamp)
|
22
|
+
redis.expireat(tabs_key(key), unix_timestamp)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ttl(key)
|
26
|
+
redis.ttl(tabs_key(key))
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(key)
|
30
|
+
redis.get(tabs_key(key))
|
31
|
+
end
|
32
|
+
|
33
|
+
def mget(*keys)
|
34
|
+
prefixed_keys = keys.map { |k| tabs_key(k) }
|
35
|
+
redis.mget(*prefixed_keys)
|
36
|
+
end
|
37
|
+
|
38
|
+
def set(key, value)
|
39
|
+
redis.set(tabs_key(key), value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def del(*keys)
|
43
|
+
return 0 if keys.empty?
|
44
|
+
prefixed_keys = keys.map { |k| tabs_key(k) }
|
45
|
+
redis.del(*prefixed_keys)
|
46
|
+
end
|
47
|
+
|
48
|
+
def del_by_prefix(pattern)
|
49
|
+
keys = redis.keys("#{tabs_key(pattern)}*")
|
50
|
+
return 0 if keys.empty?
|
51
|
+
redis.del(*keys)
|
52
|
+
end
|
53
|
+
|
54
|
+
def incr(key)
|
55
|
+
redis.incr(tabs_key(key))
|
56
|
+
end
|
57
|
+
|
58
|
+
def rpush(key, value)
|
59
|
+
redis.rpush(tabs_key(key), value)
|
60
|
+
end
|
61
|
+
|
62
|
+
def sadd(key, *values)
|
63
|
+
redis.sadd(tabs_key(key), *values)
|
64
|
+
end
|
65
|
+
|
66
|
+
def smembers(key)
|
67
|
+
redis.smembers(tabs_key(key))
|
68
|
+
end
|
69
|
+
|
70
|
+
def smembers_all(*keys)
|
71
|
+
redis.pipelined do
|
72
|
+
keys.map{ |key| smembers(key)}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def sismember(key, value)
|
77
|
+
redis.sismember(tabs_key(key), value)
|
78
|
+
end
|
79
|
+
|
80
|
+
def hget(key, field)
|
81
|
+
redis.hget(tabs_key(key), field)
|
82
|
+
end
|
83
|
+
|
84
|
+
def hset(key, field, value)
|
85
|
+
redis.hset(tabs_key(key), field, value)
|
86
|
+
end
|
87
|
+
|
88
|
+
def hdel(key, field)
|
89
|
+
redis.hdel(tabs_key(key), field)
|
90
|
+
end
|
91
|
+
|
92
|
+
def hkeys(key)
|
93
|
+
redis.hkeys(tabs_key(key))
|
94
|
+
end
|
95
|
+
|
96
|
+
def hincrby(key, field, value)
|
97
|
+
redis.hincrby(tabs_key(key), field, value)
|
98
|
+
end
|
99
|
+
|
100
|
+
def hgetall(key)
|
101
|
+
redis.hgetall(tabs_key(key))
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|