counterman 0.0.1
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/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +30 -0
- data/README.md +1 -0
- data/Rakefile +14 -0
- data/counterman.gemspec +18 -0
- data/lib/counterman.rb +130 -0
- data/lib/counterman/hash_operations.rb +11 -0
- data/lib/counterman/time_events.rb +18 -0
- data/lib/counterman/time_span.rb +36 -0
- data/lib/counterman/time_spans.rb +7 -0
- data/lib/counterman/time_spans/day.rb +17 -0
- data/lib/counterman/time_spans/hour.rb +19 -0
- data/lib/counterman/time_spans/minute.rb +19 -0
- data/lib/counterman/time_spans/month.rb +17 -0
- data/lib/counterman/time_spans/week.rb +18 -0
- data/lib/counterman/time_spans/year.rb +17 -0
- metadata +118 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 54385171cb60d92865307932d22b25e205eb06a0
|
|
4
|
+
data.tar.gz: 8e67a8f47b43988d73bfcc79fa777a66ae1c7534
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c83b01b5c2e9c581f8cdfbcd112af488bbb735cee79c53fa6785506eca3af975b86a0b30ce08d079886ca29103af583da87617c9f90029c3b333dc64d32e0dc4
|
|
7
|
+
data.tar.gz: 704364f2fcc14088612000bb4de09caba7ffcaac6cf86931b92be969a8c12aba940416b645f692c17fbbefc7804de28638b4d85066034298504d931ed4cbfe8e
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pkg
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
counterman (0.0.1)
|
|
5
|
+
redis (~> 3.0.3)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: http://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
given_core (3.0.0)
|
|
11
|
+
sorcerer (>= 0.3.7)
|
|
12
|
+
minitest (4.3.2)
|
|
13
|
+
minitest-given (3.0.0)
|
|
14
|
+
given_core (= 3.0.0)
|
|
15
|
+
minitest (> 4.3)
|
|
16
|
+
rake (0.9.2.2)
|
|
17
|
+
redis (3.0.3)
|
|
18
|
+
redis-namespace (1.2.1)
|
|
19
|
+
redis (~> 3.0.0)
|
|
20
|
+
sorcerer (1.0.0)
|
|
21
|
+
|
|
22
|
+
PLATFORMS
|
|
23
|
+
ruby
|
|
24
|
+
|
|
25
|
+
DEPENDENCIES
|
|
26
|
+
counterman!
|
|
27
|
+
minitest (~> 4.3.0)
|
|
28
|
+
minitest-given (~> 3.0.0)
|
|
29
|
+
rake
|
|
30
|
+
redis-namespace (~> 1.2.1)
|
data/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rake/testtask"
|
|
3
|
+
|
|
4
|
+
Rake::TestTask.new("spec") do |t|
|
|
5
|
+
t.pattern = "test/**/*_test.rb"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Rake::TestTask.new("bench") do |t|
|
|
9
|
+
t.pattern = "test/bench/*_bench.rb"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
task :default => [:test]
|
|
13
|
+
task :all => [:test, :bench]
|
|
14
|
+
task :test => [:spec]
|
data/counterman.gemspec
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = "counterman"
|
|
3
|
+
s.version = "0.0.1"
|
|
4
|
+
s.summary = "Counter Analytics"
|
|
5
|
+
s.description = "Fast and furious tracking system using Redis hash operations"
|
|
6
|
+
s.authors = ["maccman"]
|
|
7
|
+
s.licenses = ["MIT"]
|
|
8
|
+
s.email = ["info@eribium.org"]
|
|
9
|
+
s.homepage = "http://github.com/maccman/counterman"
|
|
10
|
+
s.files = `git ls-files`.split("\n")
|
|
11
|
+
s.test_files = `git ls-files test`.split("\n")
|
|
12
|
+
|
|
13
|
+
s.add_dependency("redis", "~> 3.0.3")
|
|
14
|
+
|
|
15
|
+
s.add_development_dependency("minitest", "~> 4.3.0")
|
|
16
|
+
s.add_development_dependency("minitest-given", "~> 3.0.0")
|
|
17
|
+
s.add_development_dependency("redis-namespace", "~> 1.2.1")
|
|
18
|
+
end
|
data/lib/counterman.rb
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require "redis"
|
|
2
|
+
require "time"
|
|
3
|
+
require "forwardable"
|
|
4
|
+
require "counterman/time_events"
|
|
5
|
+
|
|
6
|
+
# Public: Counterman core classs
|
|
7
|
+
#
|
|
8
|
+
class Counterman
|
|
9
|
+
extend Forwardable
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
attr_accessor :redis, :options
|
|
13
|
+
|
|
14
|
+
# Public: Prevents a fatal error if the options are set to silent
|
|
15
|
+
#
|
|
16
|
+
def safe(&block)
|
|
17
|
+
yield if block
|
|
18
|
+
rescue Redis::BaseError => e
|
|
19
|
+
raise e unless options[:silent]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
PREFIX = "counterman"
|
|
24
|
+
|
|
25
|
+
def_delegators self, :redis, :redis=, :options, :options=, :safe
|
|
26
|
+
|
|
27
|
+
# Public: Initializes Counterman
|
|
28
|
+
#
|
|
29
|
+
# options - An options hash to change how Counterman behaves
|
|
30
|
+
#
|
|
31
|
+
def initialize(options = {})
|
|
32
|
+
|
|
33
|
+
self.options = default_options.merge!(options)
|
|
34
|
+
|
|
35
|
+
spans = self.options.fetch(:time_spans, %w[year month week day hour minute])
|
|
36
|
+
@time_spans = generate_spans(spans)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Public: Lazily Instantiate and memoize the Redis connection
|
|
40
|
+
#
|
|
41
|
+
def redis
|
|
42
|
+
@redis ||= case options[:redis]
|
|
43
|
+
when nil
|
|
44
|
+
Redis.new
|
|
45
|
+
when Hash
|
|
46
|
+
Redis.new options[:redis]
|
|
47
|
+
else
|
|
48
|
+
options[:redis]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Public: Marks an id to a given event on a given time
|
|
54
|
+
#
|
|
55
|
+
# event_name - The event name to be searched for
|
|
56
|
+
# ids - The ids to be tracked
|
|
57
|
+
#
|
|
58
|
+
# Examples
|
|
59
|
+
#
|
|
60
|
+
# analytics = Counterman.new
|
|
61
|
+
# analytics.track("login", 1)
|
|
62
|
+
# analytics.track("login", [2, 3, 4])
|
|
63
|
+
#
|
|
64
|
+
def track(event_name, ids, time = Time.now.utc)
|
|
65
|
+
event_time = time.kind_of?(Time) ? time : Time.parse(time.to_s)
|
|
66
|
+
time_events = TimeEvents.start(@time_spans, event_name, event_time)
|
|
67
|
+
|
|
68
|
+
track_events(time_events, Array(ids))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Public: List all the events given the counterman namespace
|
|
72
|
+
#
|
|
73
|
+
def events
|
|
74
|
+
keys = safe { redis.keys([PREFIX, "*", "????"].join("_")) }
|
|
75
|
+
keys.map { |key| key.split("_")[1] }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Public: Resets all the used keys
|
|
79
|
+
#
|
|
80
|
+
def reset_all
|
|
81
|
+
keys = safe { redis.keys([PREFIX, "*"].join("_")) }
|
|
82
|
+
safe { redis.del(keys) } if keys.any?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# Private: Generates the methods to fech data
|
|
88
|
+
#
|
|
89
|
+
# spans: An array of timespans corresponding to a TimeSpan class
|
|
90
|
+
#
|
|
91
|
+
def generate_spans(spans)
|
|
92
|
+
spans.map do |method_name|
|
|
93
|
+
constructor = self.class.const_get(method_name.capitalize)
|
|
94
|
+
|
|
95
|
+
define_singleton_method(method_name) do |*args|
|
|
96
|
+
event_name, date = *args
|
|
97
|
+
date ||= Time.now.utc
|
|
98
|
+
|
|
99
|
+
constructor.new(event_name, date)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
constructor
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Private: Default configuration options
|
|
107
|
+
#
|
|
108
|
+
def default_options
|
|
109
|
+
{ cache: true, silent: false }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Private: Marks ids for a given time events
|
|
113
|
+
#
|
|
114
|
+
# time_events: A set of TimeEvents
|
|
115
|
+
# ids: The ids to be tracked
|
|
116
|
+
#
|
|
117
|
+
def track_events(time_events, ids)
|
|
118
|
+
safe_multi do
|
|
119
|
+
time_events.each do |event|
|
|
120
|
+
ids.each { |id| safe { redis.hincrby(event.key, id, 1) } }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Private: Executes a block within a safe connection using redis.multi
|
|
126
|
+
#
|
|
127
|
+
def safe_multi(&block)
|
|
128
|
+
safe { redis.multi(&block) }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require "counterman/time_spans"
|
|
2
|
+
|
|
3
|
+
# Public: Counterman core classs
|
|
4
|
+
#
|
|
5
|
+
class Counterman
|
|
6
|
+
module TimeEvents
|
|
7
|
+
# Public: Helper to get all the time trackers ready
|
|
8
|
+
#
|
|
9
|
+
# event_name - The event to be tracked
|
|
10
|
+
# date - A given Time object
|
|
11
|
+
#
|
|
12
|
+
def self.start(time_spans, event_name, time)
|
|
13
|
+
time_spans.map do |t|
|
|
14
|
+
t.new(event_name, time)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "counterman/hash_operations"
|
|
2
|
+
|
|
3
|
+
# Public: Counterman core classs
|
|
4
|
+
#
|
|
5
|
+
class Counterman
|
|
6
|
+
# Public: The timespan class. All the time span classes inherit from this one
|
|
7
|
+
#
|
|
8
|
+
class TimeSpan
|
|
9
|
+
include HashOperations
|
|
10
|
+
|
|
11
|
+
attr_reader :key
|
|
12
|
+
|
|
13
|
+
DATE_FORMAT = "%s-%02d-%02d"
|
|
14
|
+
TIME_FORMAT = "%02d:%02d"
|
|
15
|
+
|
|
16
|
+
# Public: Initializes the base TimeSpan class
|
|
17
|
+
#
|
|
18
|
+
# event_name - The event to be tracked
|
|
19
|
+
# date - A given Time object
|
|
20
|
+
#
|
|
21
|
+
def initialize(event_name, date)
|
|
22
|
+
@key = build_key(event_name, time_format(date))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# Private: The redis key that's going to be used
|
|
28
|
+
#
|
|
29
|
+
# event_name - The event to be tracked
|
|
30
|
+
# date - A given Time object
|
|
31
|
+
#
|
|
32
|
+
def build_key(event_name, date)
|
|
33
|
+
[Counterman::PREFIX, event_name, date.join("-")].join("_")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Public: Counterman core classs
|
|
2
|
+
#
|
|
3
|
+
class Counterman
|
|
4
|
+
# Public: Day TimeSpan class
|
|
5
|
+
#
|
|
6
|
+
class Day < TimeSpan
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Private: The format that's going the be used for the date part of the key
|
|
10
|
+
#
|
|
11
|
+
# date - A given Time object
|
|
12
|
+
#
|
|
13
|
+
def time_format(date)
|
|
14
|
+
[DATE_FORMAT % [date.year, date.month, date.day]]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Public: Counterman core classs
|
|
2
|
+
#
|
|
3
|
+
class Counterman
|
|
4
|
+
# Public: Hour TimeSpan class
|
|
5
|
+
#
|
|
6
|
+
class Hour < TimeSpan
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Private: The format that's going the be used for the date part of the key
|
|
10
|
+
#
|
|
11
|
+
# date - A given Time object
|
|
12
|
+
#
|
|
13
|
+
def time_format(date)
|
|
14
|
+
full_date = DATE_FORMAT % [date.year, date.month, date.day]
|
|
15
|
+
time = TIME_FORMAT % [date.hour, 0]
|
|
16
|
+
[full_date + " " + time]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Public: Counterman core classs
|
|
2
|
+
#
|
|
3
|
+
class Counterman
|
|
4
|
+
# Public: Minute TimeSpan class
|
|
5
|
+
#
|
|
6
|
+
class Minute < TimeSpan
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Private: The format that's going the be used for the date part of the key
|
|
10
|
+
#
|
|
11
|
+
# date - A given Time object
|
|
12
|
+
#
|
|
13
|
+
def time_format(date)
|
|
14
|
+
full_date = DATE_FORMAT % [date.year, date.month, date.day]
|
|
15
|
+
time = TIME_FORMAT % [date.hour, date.min]
|
|
16
|
+
[full_date + " " + time]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Public: Counterman core classs
|
|
2
|
+
#
|
|
3
|
+
class Counterman
|
|
4
|
+
# Public: Month TimeSpan class
|
|
5
|
+
#
|
|
6
|
+
class Month < TimeSpan
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Private: The format that's going the be used for the date part of the key
|
|
10
|
+
#
|
|
11
|
+
# date - A given Time object
|
|
12
|
+
#
|
|
13
|
+
def time_format(date)
|
|
14
|
+
[date.year, "%02d" % date.month]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Public: Counterman core classs
|
|
2
|
+
#
|
|
3
|
+
class Counterman
|
|
4
|
+
# Public: Month TimeSpan class
|
|
5
|
+
#
|
|
6
|
+
class Week < TimeSpan
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Private: The format that's going the be used for the date part of the key
|
|
10
|
+
#
|
|
11
|
+
# date - A given Time object
|
|
12
|
+
#
|
|
13
|
+
def time_format(date)
|
|
14
|
+
week = date.strftime("%W")
|
|
15
|
+
[date.year, "W" + week]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Public: Counterman core classs
|
|
2
|
+
#
|
|
3
|
+
class Counterman
|
|
4
|
+
# Public: Year TimeSpan class
|
|
5
|
+
#
|
|
6
|
+
class Year < TimeSpan
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Private: The format that's going the be used for the date part of the key
|
|
10
|
+
#
|
|
11
|
+
# date - A given Time object
|
|
12
|
+
#
|
|
13
|
+
def time_format(date)
|
|
14
|
+
[date.year]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: counterman
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- maccman
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-06-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: redis
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 3.0.3
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 3.0.3
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: minitest
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 4.3.0
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 4.3.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: minitest-given
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 3.0.0
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 3.0.0
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: redis-namespace
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 1.2.1
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 1.2.1
|
|
69
|
+
description: Fast and furious tracking system using Redis hash operations
|
|
70
|
+
email:
|
|
71
|
+
- info@eribium.org
|
|
72
|
+
executables: []
|
|
73
|
+
extensions: []
|
|
74
|
+
extra_rdoc_files: []
|
|
75
|
+
files:
|
|
76
|
+
- ".gitignore"
|
|
77
|
+
- ".travis.yml"
|
|
78
|
+
- Gemfile
|
|
79
|
+
- Gemfile.lock
|
|
80
|
+
- README.md
|
|
81
|
+
- Rakefile
|
|
82
|
+
- counterman.gemspec
|
|
83
|
+
- lib/counterman.rb
|
|
84
|
+
- lib/counterman/hash_operations.rb
|
|
85
|
+
- lib/counterman/time_events.rb
|
|
86
|
+
- lib/counterman/time_span.rb
|
|
87
|
+
- lib/counterman/time_spans.rb
|
|
88
|
+
- lib/counterman/time_spans/day.rb
|
|
89
|
+
- lib/counterman/time_spans/hour.rb
|
|
90
|
+
- lib/counterman/time_spans/minute.rb
|
|
91
|
+
- lib/counterman/time_spans/month.rb
|
|
92
|
+
- lib/counterman/time_spans/week.rb
|
|
93
|
+
- lib/counterman/time_spans/year.rb
|
|
94
|
+
homepage: http://github.com/maccman/counterman
|
|
95
|
+
licenses:
|
|
96
|
+
- MIT
|
|
97
|
+
metadata: {}
|
|
98
|
+
post_install_message:
|
|
99
|
+
rdoc_options: []
|
|
100
|
+
require_paths:
|
|
101
|
+
- lib
|
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: '0'
|
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0'
|
|
112
|
+
requirements: []
|
|
113
|
+
rubyforge_project:
|
|
114
|
+
rubygems_version: 2.2.2
|
|
115
|
+
signing_key:
|
|
116
|
+
specification_version: 4
|
|
117
|
+
summary: Counter Analytics
|
|
118
|
+
test_files: []
|