snappy_stats 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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