tabs 0.9.1 → 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +3 -0
- data/README.md +85 -5
- data/lib/tabs/config.rb +37 -2
- data/lib/tabs/metrics/counter.rb +14 -5
- data/lib/tabs/metrics/task.rb +8 -6
- data/lib/tabs/metrics/task/token.rb +25 -10
- data/lib/tabs/metrics/value.rb +35 -27
- data/lib/tabs/resolution.rb +26 -10
- data/lib/tabs/resolutionable.rb +36 -13
- data/lib/tabs/resolutions/day.rb +9 -1
- data/lib/tabs/resolutions/hour.rb +9 -1
- data/lib/tabs/resolutions/minute.rb +9 -1
- data/lib/tabs/resolutions/month.rb +9 -1
- data/lib/tabs/resolutions/week.rb +13 -7
- data/lib/tabs/resolutions/year.rb +9 -1
- data/lib/tabs/storage.rb +39 -17
- data/lib/tabs/tabs.rb +12 -4
- data/lib/tabs/version.rb +1 -1
- data/spec/lib/tabs/config_spec.rb +60 -0
- data/spec/lib/tabs/metrics/counter_spec.rb +44 -1
- data/spec/lib/tabs/{task_spec.rb → metrics/task_spec.rb} +31 -3
- data/spec/lib/tabs/metrics/value_spec.rb +36 -0
- data/spec/lib/tabs/resolution_spec.rb +26 -3
- data/spec/lib/tabs/resolutionable_spec.rb +53 -0
- data/spec/lib/tabs/resolutions/day_spec.rb +23 -0
- data/spec/lib/tabs/resolutions/hour_spec.rb +23 -0
- data/spec/lib/tabs/resolutions/minute_spec.rb +23 -0
- data/spec/lib/tabs/resolutions/month_spec.rb +23 -0
- data/spec/lib/tabs/resolutions/week_spec.rb +24 -0
- data/spec/lib/tabs/resolutions/year_spec.rb +23 -0
- data/spec/lib/tabs/storage_spec.rb +138 -0
- data/spec/lib/tabs_spec.rb +28 -1
- data/spec/spec_helper.rb +9 -1
- data/spec/support/custom_resolutions.rb +10 -2
- data/tabs.gemspec +6 -21
- metadata +48 -81
data/lib/tabs/resolution.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolution
|
3
|
-
|
3
|
+
include Resolutionable
|
4
4
|
extend self
|
5
5
|
|
6
|
-
def register(
|
6
|
+
def register(klass)
|
7
7
|
@@resolution_classes ||= {}
|
8
|
-
@@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) }
|
9
14
|
end
|
10
15
|
|
11
16
|
def serialize(resolution, timestamp)
|
@@ -32,18 +37,29 @@ module Tabs
|
|
32
37
|
@@resolution_classes.keys
|
33
38
|
end
|
34
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
|
+
Tabs::Resolution.register(Tabs::Resolutions::Minute)
|
47
|
+
Tabs::Resolution.register(Tabs::Resolutions::Hour)
|
48
|
+
Tabs::Resolution.register(Tabs::Resolutions::Day)
|
49
|
+
Tabs::Resolution.register(Tabs::Resolutions::Week)
|
50
|
+
Tabs::Resolution.register(Tabs::Resolutions::Month)
|
51
|
+
Tabs::Resolution.register(Tabs::Resolutions::Year)
|
52
|
+
end
|
53
|
+
|
35
54
|
private
|
36
55
|
|
37
56
|
def resolution_klass(resolution)
|
38
|
-
@@resolution_classes[resolution]
|
57
|
+
klass = @@resolution_classes[resolution]
|
58
|
+
raise Tabs::ResolutionMissingError.new(resolution) unless klass
|
59
|
+
klass
|
39
60
|
end
|
40
61
|
|
41
62
|
end
|
42
63
|
end
|
43
64
|
|
44
|
-
Tabs::Resolution.
|
45
|
-
Tabs::Resolution.register(:hour, Tabs::Resolutions::Hour)
|
46
|
-
Tabs::Resolution.register(:day, Tabs::Resolutions::Day)
|
47
|
-
Tabs::Resolution.register(:week, Tabs::Resolutions::Week)
|
48
|
-
Tabs::Resolution.register(:month, Tabs::Resolutions::Month)
|
49
|
-
Tabs::Resolution.register(:year, Tabs::Resolutions::Year)
|
65
|
+
Tabs::Resolution.reset_default_resolutions
|
data/lib/tabs/resolutionable.rb
CHANGED
@@ -1,25 +1,48 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolutionable
|
3
|
-
extend self
|
4
3
|
|
5
|
-
def
|
6
|
-
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
7
6
|
end
|
8
7
|
|
9
|
-
|
10
|
-
raise "Must implement deserialize in the concrete resolution module"
|
11
|
-
end
|
8
|
+
module ClassMethods
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
16
37
|
|
17
|
-
def add
|
18
|
-
raise "Must implement to_seconds in the concrete resolution module"
|
19
38
|
end
|
20
39
|
|
21
|
-
def
|
22
|
-
|
40
|
+
def expire(key, timestamp)
|
41
|
+
return unless Tabs::Config.expires?(name)
|
42
|
+
resolution_ends_at = timestamp.utc.to_i + to_seconds
|
43
|
+
expires_at = resolution_ends_at + Tabs::Config.expires_in(name)
|
44
|
+
Storage.expireat(key, expires_at)
|
23
45
|
end
|
46
|
+
|
24
47
|
end
|
25
48
|
end
|
data/lib/tabs/resolutions/day.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolutions
|
3
3
|
module Day
|
4
|
-
|
4
|
+
include Tabs::Resolutionable
|
5
5
|
extend self
|
6
6
|
|
7
7
|
PATTERN = "%Y-%m-%d"
|
8
8
|
|
9
|
+
def name
|
10
|
+
:day
|
11
|
+
end
|
12
|
+
|
9
13
|
def serialize(timestamp)
|
10
14
|
timestamp.strftime(PATTERN)
|
11
15
|
end
|
@@ -19,6 +23,10 @@ module Tabs
|
|
19
23
|
s / 1.day
|
20
24
|
end
|
21
25
|
|
26
|
+
def to_seconds
|
27
|
+
1.day
|
28
|
+
end
|
29
|
+
|
22
30
|
def add(ts, num)
|
23
31
|
ts + num.days
|
24
32
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolutions
|
3
3
|
module Hour
|
4
|
-
|
4
|
+
include Tabs::Resolutionable
|
5
5
|
extend self
|
6
6
|
|
7
7
|
PATTERN = "%Y-%m-%d-%H"
|
8
8
|
|
9
|
+
def name
|
10
|
+
:hour
|
11
|
+
end
|
12
|
+
|
9
13
|
def serialize(timestamp)
|
10
14
|
timestamp.strftime(PATTERN)
|
11
15
|
end
|
@@ -19,6 +23,10 @@ module Tabs
|
|
19
23
|
s / 1.hour
|
20
24
|
end
|
21
25
|
|
26
|
+
def to_seconds
|
27
|
+
1.hour
|
28
|
+
end
|
29
|
+
|
22
30
|
def add(ts, num)
|
23
31
|
ts + num.hours
|
24
32
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolutions
|
3
3
|
module Minute
|
4
|
-
|
4
|
+
include Tabs::Resolutionable
|
5
5
|
extend self
|
6
6
|
|
7
7
|
PATTERN = "%Y-%m-%d-%H-%M"
|
8
8
|
|
9
|
+
def name
|
10
|
+
:minute
|
11
|
+
end
|
12
|
+
|
9
13
|
def serialize(timestamp)
|
10
14
|
timestamp.strftime(PATTERN)
|
11
15
|
end
|
@@ -19,6 +23,10 @@ module Tabs
|
|
19
23
|
s / 1.minute
|
20
24
|
end
|
21
25
|
|
26
|
+
def to_seconds
|
27
|
+
1.minute
|
28
|
+
end
|
29
|
+
|
22
30
|
def add(ts, num)
|
23
31
|
ts + num.minutes
|
24
32
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolutions
|
3
3
|
module Month
|
4
|
-
|
4
|
+
include Tabs::Resolutionable
|
5
5
|
extend self
|
6
6
|
|
7
7
|
PATTERN = "%Y-%m"
|
8
8
|
|
9
|
+
def name
|
10
|
+
:month
|
11
|
+
end
|
12
|
+
|
9
13
|
def serialize(timestamp)
|
10
14
|
timestamp.strftime(PATTERN)
|
11
15
|
end
|
@@ -19,6 +23,10 @@ module Tabs
|
|
19
23
|
s / 1.month
|
20
24
|
end
|
21
25
|
|
26
|
+
def to_seconds
|
27
|
+
1.month
|
28
|
+
end
|
29
|
+
|
22
30
|
def add(ts, num)
|
23
31
|
ts + num.months
|
24
32
|
end
|
@@ -1,26 +1,32 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolutions
|
3
3
|
module Week
|
4
|
-
|
4
|
+
include Tabs::Resolutionable
|
5
5
|
extend self
|
6
6
|
|
7
|
-
PATTERN = "%Y-%
|
7
|
+
PATTERN = "%Y-%m-%d"
|
8
|
+
|
9
|
+
def name
|
10
|
+
:week
|
11
|
+
end
|
8
12
|
|
9
13
|
def serialize(timestamp)
|
10
|
-
timestamp.strftime(PATTERN)
|
14
|
+
normalize(timestamp).strftime(PATTERN)
|
11
15
|
end
|
12
16
|
|
13
17
|
def deserialize(str)
|
14
|
-
|
15
|
-
week = 1 if week == 0
|
16
|
-
dt = DateTime.strptime("#{year}-#{week}", PATTERN)
|
18
|
+
dt = DateTime.strptime(str, PATTERN)
|
17
19
|
self.normalize(dt)
|
18
20
|
end
|
19
21
|
|
20
|
-
def
|
22
|
+
def from_seconds(s)
|
21
23
|
s / 1.week
|
22
24
|
end
|
23
25
|
|
26
|
+
def to_seconds
|
27
|
+
1.week
|
28
|
+
end
|
29
|
+
|
24
30
|
def add(ts, num)
|
25
31
|
ts + num.weeks
|
26
32
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module Tabs
|
2
2
|
module Resolutions
|
3
3
|
module Year
|
4
|
-
|
4
|
+
include Tabs::Resolutionable
|
5
5
|
extend self
|
6
6
|
|
7
7
|
PATTERN = "%Y"
|
8
8
|
|
9
|
+
def name
|
10
|
+
:year
|
11
|
+
end
|
12
|
+
|
9
13
|
def serialize(timestamp)
|
10
14
|
timestamp.strftime(PATTERN)
|
11
15
|
end
|
@@ -19,6 +23,10 @@ module Tabs
|
|
19
23
|
s / 1.year
|
20
24
|
end
|
21
25
|
|
26
|
+
def to_seconds
|
27
|
+
1.year
|
28
|
+
end
|
29
|
+
|
22
30
|
def add(ts, num)
|
23
31
|
ts + num.years
|
24
32
|
end
|
data/lib/tabs/storage.rb
CHANGED
@@ -6,77 +6,99 @@ module Tabs
|
|
6
6
|
@redis ||= Config.redis
|
7
7
|
end
|
8
8
|
|
9
|
+
def tabs_key(key)
|
10
|
+
if Tabs::Config.prefix.blank?
|
11
|
+
"tabs:#{key}"
|
12
|
+
else
|
13
|
+
"tabs:#{Tabs::Config.prefix}:#{key}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
9
17
|
def exists(key)
|
10
|
-
redis.
|
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))
|
11
27
|
end
|
12
28
|
|
13
29
|
def get(key)
|
14
|
-
redis.get(
|
30
|
+
redis.get(tabs_key(key))
|
15
31
|
end
|
16
32
|
|
17
33
|
def mget(*keys)
|
18
|
-
prefixed_keys = keys.map { |k|
|
34
|
+
prefixed_keys = keys.map { |k| tabs_key(k) }
|
19
35
|
redis.mget(*prefixed_keys)
|
20
36
|
end
|
21
37
|
|
22
38
|
def set(key, value)
|
23
|
-
redis.set(
|
39
|
+
redis.set(tabs_key(key), value)
|
24
40
|
end
|
25
41
|
|
26
42
|
def del(*keys)
|
27
43
|
return 0 if keys.empty?
|
28
|
-
prefixed_keys = keys.map { |k|
|
44
|
+
prefixed_keys = keys.map { |k| tabs_key(k) }
|
29
45
|
redis.del(*prefixed_keys)
|
30
46
|
end
|
31
47
|
|
32
48
|
def del_by_prefix(pattern)
|
33
|
-
keys = redis.keys("
|
49
|
+
keys = redis.keys("#{tabs_key(pattern)}*")
|
34
50
|
return 0 if keys.empty?
|
35
51
|
redis.del(*keys)
|
36
52
|
end
|
37
53
|
|
38
54
|
def incr(key)
|
39
|
-
redis.incr(
|
55
|
+
redis.incr(tabs_key(key))
|
40
56
|
end
|
41
57
|
|
42
58
|
def rpush(key, value)
|
43
|
-
redis.rpush(
|
59
|
+
redis.rpush(tabs_key(key), value)
|
44
60
|
end
|
45
61
|
|
46
62
|
def sadd(key, *values)
|
47
|
-
redis.sadd(
|
63
|
+
redis.sadd(tabs_key(key), *values)
|
48
64
|
end
|
49
65
|
|
50
66
|
def smembers(key)
|
51
|
-
redis.smembers(
|
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
|
52
74
|
end
|
53
75
|
|
54
76
|
def sismember(key, value)
|
55
|
-
redis.sismember(
|
77
|
+
redis.sismember(tabs_key(key), value)
|
56
78
|
end
|
57
79
|
|
58
80
|
def hget(key, field)
|
59
|
-
redis.hget(
|
81
|
+
redis.hget(tabs_key(key), field)
|
60
82
|
end
|
61
83
|
|
62
84
|
def hset(key, field, value)
|
63
|
-
redis.hset(
|
85
|
+
redis.hset(tabs_key(key), field, value)
|
64
86
|
end
|
65
87
|
|
66
88
|
def hdel(key, field)
|
67
|
-
redis.hdel(
|
89
|
+
redis.hdel(tabs_key(key), field)
|
68
90
|
end
|
69
91
|
|
70
92
|
def hkeys(key)
|
71
|
-
redis.hkeys(
|
93
|
+
redis.hkeys(tabs_key(key))
|
72
94
|
end
|
73
95
|
|
74
96
|
def hincrby(key, field, value)
|
75
|
-
redis.hincrby(
|
97
|
+
redis.hincrby(tabs_key(key), field, value)
|
76
98
|
end
|
77
99
|
|
78
100
|
def hgetall(key)
|
79
|
-
redis.hgetall(
|
101
|
+
redis.hgetall(tabs_key(key))
|
80
102
|
end
|
81
103
|
|
82
104
|
end
|
data/lib/tabs/tabs.rb
CHANGED
@@ -2,10 +2,11 @@ module Tabs
|
|
2
2
|
extend self
|
3
3
|
extend Tabs::Storage
|
4
4
|
|
5
|
-
class UnknownTypeError <
|
6
|
-
class DuplicateMetricError <
|
7
|
-
class UnknownMetricError <
|
8
|
-
class MetricTypeMismatchError <
|
5
|
+
class UnknownTypeError < StandardError; end
|
6
|
+
class DuplicateMetricError < StandardError; end
|
7
|
+
class UnknownMetricError < StandardError; end
|
8
|
+
class MetricTypeMismatchError < StandardError; end
|
9
|
+
class ResolutionMissingError < StandardError; end
|
9
10
|
|
10
11
|
METRIC_TYPES = ["counter", "value", "task"]
|
11
12
|
|
@@ -94,6 +95,13 @@ module Tabs
|
|
94
95
|
metrics.each { |key| self.drop_metric! key }
|
95
96
|
end
|
96
97
|
|
98
|
+
def drop_resolution_for_metric!(key, resolution)
|
99
|
+
raise UnknownMetricError.new("Unknown metric: #{key}") unless metric_exists?(key)
|
100
|
+
raise ResolutionMissingError.new(resolution) unless Tabs::Resolution.all.include? resolution
|
101
|
+
metric = get_metric(key)
|
102
|
+
metric.drop_by_resolution!(resolution) unless metric_type(key) == "task"
|
103
|
+
end
|
104
|
+
|
97
105
|
private
|
98
106
|
|
99
107
|
def metric_klass(type)
|
data/lib/tabs/version.rb
CHANGED