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