tabstabs 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +421 -0
  8. data/Rakefile +5 -0
  9. data/lib/tabs_tabs.rb +26 -0
  10. data/lib/tabs_tabs/config.rb +65 -0
  11. data/lib/tabs_tabs/helpers.rb +27 -0
  12. data/lib/tabs_tabs/metrics/counter.rb +69 -0
  13. data/lib/tabs_tabs/metrics/counter/stats.rb +51 -0
  14. data/lib/tabs_tabs/metrics/task.rb +72 -0
  15. data/lib/tabs_tabs/metrics/task/token.rb +89 -0
  16. data/lib/tabs_tabs/metrics/value.rb +91 -0
  17. data/lib/tabs_tabs/metrics/value/stats.rb +55 -0
  18. data/lib/tabs_tabs/resolution.rb +65 -0
  19. data/lib/tabs_tabs/resolutionable.rb +48 -0
  20. data/lib/tabs_tabs/resolutions/day.rb +40 -0
  21. data/lib/tabs_tabs/resolutions/hour.rb +40 -0
  22. data/lib/tabs_tabs/resolutions/minute.rb +40 -0
  23. data/lib/tabs_tabs/resolutions/month.rb +40 -0
  24. data/lib/tabs_tabs/resolutions/week.rb +40 -0
  25. data/lib/tabs_tabs/resolutions/year.rb +40 -0
  26. data/lib/tabs_tabs/storage.rb +105 -0
  27. data/lib/tabs_tabs/tabs_tabs.rb +117 -0
  28. data/lib/tabs_tabs/version.rb +3 -0
  29. data/spec/lib/tabs_tabs/config_spec.rb +60 -0
  30. data/spec/lib/tabs_tabs/metrics/counter/stats_spec.rb +42 -0
  31. data/spec/lib/tabs_tabs/metrics/counter_spec.rb +196 -0
  32. data/spec/lib/tabs_tabs/metrics/task/token_spec.rb +18 -0
  33. data/spec/lib/tabs_tabs/metrics/task_spec.rb +103 -0
  34. data/spec/lib/tabs_tabs/metrics/value/stats_spec.rb +61 -0
  35. data/spec/lib/tabs_tabs/metrics/value_spec.rb +160 -0
  36. data/spec/lib/tabs_tabs/resolution_spec.rb +52 -0
  37. data/spec/lib/tabs_tabs/resolutionable_spec.rb +53 -0
  38. data/spec/lib/tabs_tabs/resolutions/day_spec.rb +23 -0
  39. data/spec/lib/tabs_tabs/resolutions/hour_spec.rb +23 -0
  40. data/spec/lib/tabs_tabs/resolutions/minute_spec.rb +23 -0
  41. data/spec/lib/tabs_tabs/resolutions/month_spec.rb +23 -0
  42. data/spec/lib/tabs_tabs/resolutions/week_spec.rb +24 -0
  43. data/spec/lib/tabs_tabs/resolutions/year_spec.rb +23 -0
  44. data/spec/lib/tabs_tabs/storage_spec.rb +138 -0
  45. data/spec/lib/tabs_tabs_spec.rb +223 -0
  46. data/spec/spec_helper.rb +17 -0
  47. data/spec/support/custom_resolutions.rb +40 -0
  48. data/tabs_tabs.gemspec +31 -0
  49. 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