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.
@@ -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: []