counterman 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1 @@
1
+ pkg
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "jruby-19mode"
6
+ services:
7
+ - "redis-server"
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+ gem "rake"
@@ -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)
@@ -0,0 +1 @@
1
+
@@ -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]
@@ -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
@@ -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,11 @@
1
+ class Counterman
2
+ module HashOperations
3
+ extend Forwardable
4
+
5
+ def_delegators :Counterman, :safe, :redis
6
+
7
+ def count(id)
8
+ safe { redis.hget(key, id).to_i }
9
+ end
10
+ end
11
+ 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,7 @@
1
+ require "counterman/time_span"
2
+ require "counterman/time_spans/year"
3
+ require "counterman/time_spans/month"
4
+ require "counterman/time_spans/week"
5
+ require "counterman/time_spans/day"
6
+ require "counterman/time_spans/hour"
7
+ require "counterman/time_spans/minute"
@@ -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: []