snappy_stats 0.0.3 → 0.0.4

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/.travis.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.2"
4
3
  - "1.9.3"
5
4
  - "2.0.0"
6
5
  script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in snappy_stats.gemspec
4
4
  gemspec
5
+ group :development, :test do
6
+ gem 'fakeredis', :git => 'git://github.com/guilleiguaran/fakeredis.git', :ref =>'c2ad4ee'
7
+ end
@@ -1,15 +1,14 @@
1
- module SnappyStats
2
- module Config
3
- extend self
1
+ class SnappyStats
2
+ class Config
4
3
 
5
4
  attr_accessor :namespace
6
5
  attr_accessor :raise_connection_errors
7
6
 
8
- def init!
7
+ def initialize(options = {})
9
8
  # all keys are prefixed with this namespace
10
- self.namespace = 'stats'
9
+ @namespace = 'stats'
11
10
  # rescue Redis connection errors
12
- self.raise_connection_errors = false
11
+ @raise_connection_errors = false
13
12
  end
14
13
 
15
14
  # Set the Redis connection to use
@@ -1,3 +1,3 @@
1
- module SnappyStats
2
- VERSION = "0.0.3"
1
+ class SnappyStats
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/snappy_stats.rb CHANGED
@@ -4,7 +4,7 @@ require 'redis'
4
4
  require 'active_support/time'
5
5
  require 'snappy_stats/config'
6
6
 
7
- module SnappyStats
7
+ class SnappyStats
8
8
 
9
9
  GRANULARITIES = {
10
10
  # Available for 24 hours
@@ -12,90 +12,94 @@ module SnappyStats
12
12
  size: 1440,
13
13
  ttl: 172800,
14
14
  factor: 60
15
- },
16
- hour: {
15
+ },
16
+ hour: {
17
17
  # Available for 7 days
18
- size: 168,
19
- ttl: 1209600,
20
- factor: 3600
21
- },
18
+ size: 168,
19
+ ttl: 1209600,
20
+ factor: 3600
21
+ },
22
22
  day: {
23
23
  # Available for 24 months
24
- size: 365,
25
- ttl: 63113880,
26
- factor: 86400
27
- }
28
- }
29
-
30
- def self.hash_key
31
- "#{SnappyStats.config.namespace}"
32
- end
33
-
34
- def self.configure
35
- yield(config)
36
- end
24
+ size: 365,
25
+ ttl: 63113880,
26
+ factor: 86400
27
+ }
28
+ }
37
29
 
38
- def self.config
39
- Config
40
- end
30
+ attr_accessor :config
41
31
 
42
- def self.connection
43
- @connection ||= config.redis
44
- end
45
-
46
- def self.getSecondsTimestamp
47
- Time.now.getutc.to_i
48
- end
32
+ def initialize(options = {})
33
+ @config = SnappyStats::Config.new
34
+ @config.redis = options[:redis]
35
+ end
49
36
 
50
- def self.getRoundedTimestamp( ts , precision )
51
- ts = ts || self.getSecondsTimestamp
52
- precision = precision || 1
53
- ( ts / precision ).floor * precision
54
- end
55
-
56
- def self.getFactoredTimestamp( ts_seconds, factor )
57
- ts_seconds = ts_seconds || self.getSecondsTimestamp
58
- ( ts_seconds / factor ).floor * factor
59
- end
60
-
61
- def self.recordHitNow(key)
62
- self.recordHit(Time.now.utc.to_i, key)
37
+ def hash_key
38
+ "#{config.namespace}"
39
+ end
40
+
41
+ def configure
42
+ yield(config)
43
+ end
44
+
45
+ def connection
46
+ @connection ||= config.redis
47
+ end
48
+
49
+ def getSecondsTimestamp
50
+ Time.now.getutc.to_i
51
+ end
52
+
53
+ def getRoundedTimestamp( ts , precision )
54
+ ts = ts || self.getSecondsTimestamp
55
+ precision = precision || 1
56
+ ( ts / precision ).floor * precision
57
+ end
58
+
59
+ def getFactoredTimestamp( ts_seconds, factor )
60
+ ts_seconds = ts_seconds || self.getSecondsTimestamp
61
+ ( ts_seconds / factor ).floor * factor
62
+ end
63
+
64
+ def recordHitNow(key)
65
+ recordHit(Time.now.utc.to_i, key)
66
+ end
67
+
68
+ def recordHit( time, key )
69
+ GRANULARITIES.keys.each do | gran |
70
+ granularity = GRANULARITIES[gran]
71
+ size = granularity[:size]
72
+ factor = granularity[:factor]
73
+ ttl = granularity[:ttl]
74
+ tsround = getRoundedTimestamp(time, size * factor)
75
+ redis_key = "#{hash_key}:#{key}:#{gran}:#{tsround}"
76
+ ts = getFactoredTimestamp time, factor
77
+ connection.pipelined{
78
+ connection.hincrby redis_key, ts, 1
79
+ connection.expireat redis_key, tsround + ttl
80
+ }
63
81
  end
82
+ end
83
+
84
+ def get(gran, from, to, key)
85
+ granularity = GRANULARITIES[gran]
86
+ size = granularity[:size]
87
+ factor = granularity[:factor]
88
+
89
+ from = getFactoredTimestamp( from, factor )
90
+ to = getFactoredTimestamp( to, factor )
64
91
 
65
- def self.recordHit( time, key )
66
- GRANULARITIES.keys.each do | gran |
67
- granularity = GRANULARITIES[gran]
68
- size = granularity[:size]
69
- factor = granularity[:factor]
70
- ttl = granularity[:ttl]
71
- tsround = SnappyStats.getRoundedTimestamp(time, size * factor)
72
- redis_key = "#{hash_key}:#{key}:#{gran}:#{tsround}"
73
- ts = getFactoredTimestamp time, factor
74
- SnappyStats.connection.hincrby redis_key, ts, 1
75
- SnappyStats.connection.expireat redis_key, tsround + ttl
76
- end
92
+ ts = from
93
+ i = 0
94
+ results = {}
95
+ while ts <= to
96
+ tsround = getRoundedTimestamp( ts, size * factor )
97
+ redis_key = "#{hash_key}:#{key}:#{gran}:#{tsround}"
98
+ results[ts] = connection.hget( redis_key, ts )
99
+ i = i+1
100
+ ts = ts + GRANULARITIES[gran][:factor]
77
101
  end
102
+ results
103
+ end
78
104
 
79
- def self.get(gran, from, to, key)
80
- granularity = GRANULARITIES[gran]
81
- size = granularity[:size]
82
- factor = granularity[:factor]
83
-
84
- from = self.getFactoredTimestamp( from, factor )
85
- to = self.getFactoredTimestamp( to, factor )
86
-
87
- ts = from
88
- i = 0
89
- results = {}
90
- while ts <= to
91
- tsround = getRoundedTimestamp( ts, size * factor )
92
- redis_key = "#{hash_key}:#{key}:#{gran}:#{tsround}"
93
- results[ts] = SnappyStats.connection.hget( redis_key, ts )
94
- i = i+1
95
- ts = ts + GRANULARITIES[gran][:factor]
96
- end
97
- results
98
- end
99
-
100
- config.init!
101
105
  end
@@ -1,50 +1,69 @@
1
1
  require 'spec_helper'
2
2
 
3
+ require_relative '../lib/snappy_stats'
3
4
  describe SnappyStats do
4
5
 
5
- before(:all) do
6
+ before(:each) do
7
+ redis = Redis.new
8
+ $snappy_stats = SnappyStats.new(:redis => redis)
6
9
  #Redis::Connection.drivers.delete_if {|x| Redis::Connection::Memory == x }
7
- keys = SnappyStats.connection.keys("stats:*")
8
- keys.each { | key | SnappyStats.connection.del(key) }
10
+ keys = $snappy_stats.connection.keys("stats:*")
11
+ keys.each { | key | $snappy_stats.connection.del(key) }
9
12
  end
10
13
 
11
14
  it "Calculates ts in seconds" do
12
15
  Timecop.freeze(Time.local(2013, 01, 01, 01, 01))
13
- time = SnappyStats.getSecondsTimestamp
16
+ time = $snappy_stats.getSecondsTimestamp
14
17
  expect(time).to be 1357002060
15
- default_rounded = SnappyStats.getRoundedTimestamp(nil, 60)
18
+ default_rounded = $snappy_stats.getRoundedTimestamp(nil, 60)
16
19
  expect(default_rounded).to be 1357002060
17
20
  time = 1364833411
18
- rounded = SnappyStats.getRoundedTimestamp(time, 60)
21
+ rounded = $snappy_stats.getRoundedTimestamp(time, 60)
19
22
  expect(rounded).to be 1364833380
20
23
  end
21
24
 
22
- it "Records a hit at all granularities" do
23
- pending("Need updated version of fakeredis with correct hincrby implementation")
25
+ it "Records a hit at all granularities" do
24
26
  Timecop.freeze
25
27
  now = Time.now
28
+ starting_hour = Time.now.beginning_of_hour.to_i
26
29
  5.times do
27
- SnappyStats.recordHitNow("test:counter1")
30
+ $snappy_stats.recordHitNow("test:counter1")
28
31
  end
29
32
  3.times do
30
- SnappyStats.recordHitNow("test:counter2")
33
+ $snappy_stats.recordHitNow("test:counter2")
31
34
  end
32
35
  Timecop.freeze(now + 2.days)
33
36
  2.times do
34
- SnappyStats.recordHitNow("test:counter1")
37
+ $snappy_stats.recordHitNow("test:counter1")
35
38
  end
36
39
  from = now.midnight.to_i
37
40
  to = (now.midnight + 2.day).to_i
38
- daily_user1_stat = SnappyStats.get(:day,from,to,"test:counter1")
41
+ daily_user1_stat = $snappy_stats.get(:day,from,to,"test:counter1")
39
42
  Timecop.freeze(now + 1.day)
40
- puts daily_user1_stat
41
43
  expect(daily_user1_stat[from]).to eq("5")
42
- hourly_user1_stat = SnappyStats.get(:hour,from,to,"test:counter1")
43
- expect(hourly_user1_stat[ Time.now.beginning_of_hour.to_i]).to eq("5")
44
- daily_user2_stat = SnappyStats.get(:day,from,to,"test:counter2")
44
+ hourly_user1_stat = $snappy_stats.get(:hour,from,to,"test:counter1")
45
+ expect(hourly_user1_stat[ starting_hour.to_i]).to eq("5")
46
+ daily_user2_stat = $snappy_stats.get(:day,from,to,"test:counter2")
45
47
  expect(daily_user2_stat[from]).to eq("3")
46
48
  end
47
49
 
50
+ it "can have two versions at same time" do
51
+ redis1 = Redis.new(db: 1)
52
+ $snappy_stats_eu = SnappyStats.new(:redis => redis1)
53
+ from = Time.now.midnight.to_i
54
+ to = (Time.now.midnight + 2.day).to_i
55
+ 500.times do
56
+ $snappy_stats.recordHitNow("test:counter1")
57
+ end
58
+ 250.times do
59
+ $snappy_stats_eu.recordHitNow("test:counter2")
60
+ end
61
+ user1_stats = $snappy_stats.get(:day,from,to,"test:counter1")
62
+ expect(user1_stats[from]).to eq("500")
63
+ user2_stats = $snappy_stats_eu.get(:day,from,to,"test:counter2")
64
+ expect(user2_stats[from]).to eq("250")
65
+ end
66
+
48
67
  after(:each) do
49
68
  Timecop.return
50
69
  end
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  # loaded once.
5
5
  #
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
- require 'snappy_stats' # and any other gems you need
7
+ # and any other gems you need
8
8
  require 'fakeredis'
9
9
  require 'timecop'
10
10
  RSpec.configure do |config|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snappy_stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
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-10-18 00:00:00.000000000 Z
12
+ date: 2013-11-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport