tabstabs 2.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.
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