minuteman 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.
- data/Gemfile +4 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +8 -0
- data/lib/minuteman.rb +84 -0
- data/lib/minuteman/bit_operations.rb +87 -0
- data/lib/minuteman/time_events.rb +17 -0
- data/lib/minuteman/time_span.rb +34 -0
- data/lib/minuteman/time_spans.rb +7 -0
- data/lib/minuteman/time_spans/day.rb +13 -0
- data/lib/minuteman/time_spans/hour.rb +15 -0
- data/lib/minuteman/time_spans/minute.rb +15 -0
- data/lib/minuteman/time_spans/month.rb +13 -0
- data/lib/minuteman/time_spans/week.rb +14 -0
- data/lib/minuteman/time_spans/year.rb +13 -0
- data/minuteman.gemspec +15 -0
- data/test/test_helper.rb +7 -0
- data/test/unit/minuteman_test.rb +92 -0
- metadata +98 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
minuteman (0.0.1)
|
5
|
+
redis (~> 3.0.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
minitest (4.1.0)
|
11
|
+
rake (0.9.2.2)
|
12
|
+
redis (3.0.2)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
minitest (~> 4.1.0)
|
19
|
+
minuteman!
|
20
|
+
rake
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Bruno Aguirre
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Minuteman
|
2
|
+
|
3
|
+
_Wikipedia_: Minutemen were members of teams from Massachusetts that were well-prepared
|
4
|
+
militia companies of select men from the American colonial partisan militia
|
5
|
+
during the American Revolutionary War. _They provided a highly mobile, rapidly
|
6
|
+
deployed force that allowed the colonies to respond immediately to war threats,
|
7
|
+
hence the name._
|
8
|
+
|
9
|
+

|
10
|
+
|
11
|
+
Fast analytics using Redis bitwise operations
|
12
|
+
|
13
|
+
## Origin
|
14
|
+
Freenode - #cuba.rb - 2012/10/30 15:20 UYT
|
15
|
+
|
16
|
+
**conanbatt:** anyone here knows some good web app metrics tool ?
|
17
|
+
|
18
|
+
**conanbatt:** i use google analytics for the page itself, and its good, but for the webapp its really not useful
|
19
|
+
|
20
|
+
**tizoc: conanbatt:** [http://amix.dk/blog/post/19714]() you can port this (if an equivalent doesn't exist already)
|
21
|
+
|
22
|
+
**conanbatt:** the metrics link is excellent but its python and released 5 days ago lol
|
23
|
+
|
24
|
+
**elcuervo: tizoc:** the idea it's awesome
|
25
|
+
|
26
|
+
**elcuervo:** interesting...
|
27
|
+
|
28
|
+
|
29
|
+
## Inspiration
|
30
|
+
|
31
|
+
* [http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/]()
|
32
|
+
* [http://amix.dk/blog/post/19714]()
|
33
|
+
* [http://en.wikipedia.org/wiki/Bit_array]()
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
|
37
|
+
### Important!
|
38
|
+
|
39
|
+
Depends on Redis 2.6 for the `bitop` operation. You can install it using:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
brew install --devel redis
|
43
|
+
```
|
44
|
+
|
45
|
+
or upgrading your current version:
|
46
|
+
|
47
|
+
```bash
|
48
|
+
brew upgrade --devel redis
|
49
|
+
```
|
50
|
+
|
51
|
+
And then install the gem
|
52
|
+
|
53
|
+
```bash
|
54
|
+
gem install minuteman
|
55
|
+
```
|
56
|
+
|
57
|
+
## Usage
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
require "minuteman"
|
61
|
+
|
62
|
+
# Accepts an options hash that will be sent as is to Redis.new
|
63
|
+
analytics = Minuteman.new
|
64
|
+
|
65
|
+
# Mark an event for a given id
|
66
|
+
analytics.mark("login:successful", user.id)
|
67
|
+
analytics.mark("login:successful", other_user.id)
|
68
|
+
|
69
|
+
# Mark in bulk
|
70
|
+
analytics.mark("programming:love:ruby", User.where(favorite: "ruby").map(&:id))
|
71
|
+
|
72
|
+
# Fetch events for a given time
|
73
|
+
today_events = analytics.day("login:successful", Time.now.utc)
|
74
|
+
|
75
|
+
# This also exists
|
76
|
+
analytics.year("login:successful", Time.now.utc)
|
77
|
+
analytics.month("login:successful", Time.now.utc)
|
78
|
+
analytics.week("login:successful", Time.now.utc)
|
79
|
+
analytics.day("login:successful", Time.now.utc)
|
80
|
+
analytics.hour("login:successful", Time.now.utc)
|
81
|
+
analytics.minute("login:successful", Time.now.utc)
|
82
|
+
|
83
|
+
# Check event length
|
84
|
+
today_events.length
|
85
|
+
#=> 2
|
86
|
+
|
87
|
+
# Check for existance
|
88
|
+
today_events.include?(user.id)
|
89
|
+
#=> true
|
90
|
+
today_events.include?(admin.id)
|
91
|
+
#=> false
|
92
|
+
|
93
|
+
# Bulk check
|
94
|
+
today_events.include?(User.all.map(&:id))
|
95
|
+
#=> [true, true, false, false]
|
96
|
+
```
|
data/Rakefile
ADDED
data/lib/minuteman.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require "redis"
|
2
|
+
require "time"
|
3
|
+
require "minuteman/time_events"
|
4
|
+
|
5
|
+
# Until redis gem gets updated
|
6
|
+
class Redis
|
7
|
+
def bitop(operation, destkey, *keys)
|
8
|
+
synchronize do |client|
|
9
|
+
client.call([:bitop, operation, destkey] + keys)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def bitcount(key, start = 0, stop = -1)
|
14
|
+
synchronize do |client|
|
15
|
+
client.call([:bitcount, key, start, stop])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Minuteman
|
21
|
+
attr_reader :redis
|
22
|
+
|
23
|
+
PREFIX = "minuteman"
|
24
|
+
|
25
|
+
# Public: Initializes Minuteman
|
26
|
+
#
|
27
|
+
# options - The hash to be sent to Redis.new
|
28
|
+
#
|
29
|
+
def initialize(options = {})
|
30
|
+
@redis = Redis.new(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public: Generates the methods to fech data
|
34
|
+
#
|
35
|
+
# event_name - The event name to be searched for
|
36
|
+
# date - A Time object used to do the search
|
37
|
+
#
|
38
|
+
%w[year month week day hour minute].each do |method_name|
|
39
|
+
define_method(method_name) do |event_name, date|
|
40
|
+
constructor = self.class.const_get(method_name.capitalize)
|
41
|
+
constructor.new(@redis, event_name, date)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Marks an id to a given event on a given time
|
46
|
+
#
|
47
|
+
# event_name - The event name to be searched for
|
48
|
+
# ids - The ids to be tracked
|
49
|
+
#
|
50
|
+
# Examples
|
51
|
+
#
|
52
|
+
# analytics = Minuteman.new
|
53
|
+
# analytics.mark("login", 1)
|
54
|
+
# analytics.mark("login", [2, 3, 4])
|
55
|
+
#
|
56
|
+
def mark(event_name, ids, time = Time.now.utc)
|
57
|
+
event_time = time.kind_of?(Time) ? time : Time.parse(time.to_s)
|
58
|
+
time_events = TimeEvents.start(redis, event_name, event_time)
|
59
|
+
|
60
|
+
@redis.multi do
|
61
|
+
time_events.each do |event|
|
62
|
+
Array(ids).each { |id| redis.setbit(event.key, id, 1) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: Resets the bit operation cache keys
|
68
|
+
#
|
69
|
+
def reset_operations_cache
|
70
|
+
prefix = [
|
71
|
+
PREFIX, Minuteman::BitOperations::BIT_OPERATION_PREFIX
|
72
|
+
].join("_")
|
73
|
+
|
74
|
+
keys = @redis.keys([prefix, "*"].join("_"))
|
75
|
+
@redis.del(keys) if keys.any?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Resets all the used keys
|
79
|
+
#
|
80
|
+
def reset_all
|
81
|
+
keys = @redis.keys([PREFIX, "*"].join("_"))
|
82
|
+
@redis.del(keys) if keys.any?
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class Minuteman
|
2
|
+
module BitOperations
|
3
|
+
BIT_OPERATION_PREFIX = "bitop"
|
4
|
+
|
5
|
+
# Public: Checks for the existance of ids on a given set
|
6
|
+
#
|
7
|
+
# ids - Array of ids
|
8
|
+
#
|
9
|
+
def include?(*ids)
|
10
|
+
result = ids.map { |id| redis.getbit(key, id) == 1 }
|
11
|
+
result.size == 1 ? result.first : result
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Resets the current key
|
15
|
+
#
|
16
|
+
def reset
|
17
|
+
redis.rem(key)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Cheks for the amount of ids stored on the current key
|
21
|
+
#
|
22
|
+
def length
|
23
|
+
redis.bitcount(key)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Calculates the NOT of the current key
|
27
|
+
#
|
28
|
+
def -@
|
29
|
+
bit_operation("NOT", key)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Calculates the XOR against another timespan
|
33
|
+
#
|
34
|
+
# timespan: Another BitOperations enabled class
|
35
|
+
#
|
36
|
+
def ^(timespan)
|
37
|
+
bit_operation("XOR", [key, timespan.key])
|
38
|
+
end
|
39
|
+
|
40
|
+
# Public: Calculates the OR against another timespan
|
41
|
+
#
|
42
|
+
# timespan: Another BitOperations enabled class
|
43
|
+
#
|
44
|
+
def |(timespan)
|
45
|
+
bit_operation("OR", [key, timespan.key])
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Calculates the AND against another timespan
|
49
|
+
#
|
50
|
+
# timespan: Another BitOperations enabled class
|
51
|
+
#
|
52
|
+
def &(timespan)
|
53
|
+
bit_operation("AND", [key, timespan.key])
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Private: The destination key for the operation
|
59
|
+
#
|
60
|
+
# type - The bitwise operation
|
61
|
+
# events - The events to permuted
|
62
|
+
#
|
63
|
+
def destination_key(type, events)
|
64
|
+
[
|
65
|
+
Minuteman::PREFIX,
|
66
|
+
BIT_OPERATION_PREFIX,
|
67
|
+
type,
|
68
|
+
events.join("-")
|
69
|
+
].join("_")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Private: Executes a bit operation
|
73
|
+
#
|
74
|
+
# type - The bitwise operation
|
75
|
+
# events - The events to permuted
|
76
|
+
#
|
77
|
+
def bit_operation(type, events)
|
78
|
+
key = destination_key(type, Array(events))
|
79
|
+
@redis.bitop(type, key, events)
|
80
|
+
BitOperation.new(@redis, key)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class BitOperation < Struct.new(:redis, :key)
|
85
|
+
include BitOperations
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "minuteman/time_spans"
|
2
|
+
|
3
|
+
class Minuteman
|
4
|
+
module TimeEvents
|
5
|
+
# Public: Helper to get all the time trakers ready
|
6
|
+
#
|
7
|
+
# redis - The Redis connection
|
8
|
+
# event_name - The event to be tracked
|
9
|
+
# date - A given Time object
|
10
|
+
#
|
11
|
+
def self.start(redis, event_name, time)
|
12
|
+
[Year, Month, Week, Day, Hour, Minute].map do |t|
|
13
|
+
t.new(redis, event_name, time)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "minuteman/bit_operations"
|
2
|
+
|
3
|
+
class Minuteman
|
4
|
+
class TimeSpan
|
5
|
+
include BitOperations
|
6
|
+
|
7
|
+
attr_reader :key, :redis
|
8
|
+
|
9
|
+
DATE_FORMAT = "%s-%02d-%02d"
|
10
|
+
TIME_FORMAT = "%02d:%02d"
|
11
|
+
|
12
|
+
# Public: Initializes the base TimeSpan class
|
13
|
+
#
|
14
|
+
# redis - The Redis connection
|
15
|
+
# event_name - The event to be tracked
|
16
|
+
# date - A given Time object
|
17
|
+
#
|
18
|
+
def initialize(redis, event_name, date)
|
19
|
+
@redis = redis
|
20
|
+
@key = build_key(event_name, time_format(date))
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Private: The redis key that's going to be used
|
26
|
+
#
|
27
|
+
# event_name - The event to be tracked
|
28
|
+
# date - A given Time object
|
29
|
+
#
|
30
|
+
def build_key(event_name, date)
|
31
|
+
[Minuteman::PREFIX, event_name, date.join("-")].join("_")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Minuteman
|
2
|
+
class Day < TimeSpan
|
3
|
+
private
|
4
|
+
|
5
|
+
# Private: The format that's going the be used for the date part of the key
|
6
|
+
#
|
7
|
+
# date - A given Time object
|
8
|
+
#
|
9
|
+
def time_format(date)
|
10
|
+
[DATE_FORMAT % [date.year, date.month, date.day]]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Minuteman
|
2
|
+
class Hour < TimeSpan
|
3
|
+
private
|
4
|
+
|
5
|
+
# Private: The format that's going the be used for the date part of the key
|
6
|
+
#
|
7
|
+
# date - A given Time object
|
8
|
+
#
|
9
|
+
def time_format(date)
|
10
|
+
full_date = DATE_FORMAT % [date.year, date.month, date.day]
|
11
|
+
time = TIME_FORMAT % [date.hour, 0]
|
12
|
+
[full_date + " " + time]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Minuteman
|
2
|
+
class Minute < TimeSpan
|
3
|
+
private
|
4
|
+
|
5
|
+
# Private: The format that's going the be used for the date part of the key
|
6
|
+
#
|
7
|
+
# date - A given Time object
|
8
|
+
#
|
9
|
+
def time_format(date)
|
10
|
+
full_date = DATE_FORMAT % [date.year, date.month, date.day]
|
11
|
+
time = TIME_FORMAT % [date.hour, date.min]
|
12
|
+
[full_date + " " + time]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Minuteman
|
2
|
+
class Week < TimeSpan
|
3
|
+
private
|
4
|
+
|
5
|
+
# Private: The format that's going the be used for the date part of the key
|
6
|
+
#
|
7
|
+
# date - A given Time object
|
8
|
+
#
|
9
|
+
def time_format(date)
|
10
|
+
week = date.strftime("%W")
|
11
|
+
[date.year, "W" + week]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/minuteman.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "minuteman"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "Bit Analytics"
|
5
|
+
s.description = "Fast and furious tracking system using Redis bitwise operations"
|
6
|
+
s.authors = ["elcuervo"]
|
7
|
+
s.email = ["yo@brunoaguirre.com"]
|
8
|
+
s.homepage = "http://github.com/elcuervo/minuteman"
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.test_files = `git ls-files test`.split("\n")
|
11
|
+
|
12
|
+
s.add_dependency("redis", "~> 3.0.2")
|
13
|
+
|
14
|
+
s.add_development_dependency("minitest", "~> 4.1.0")
|
15
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
describe Minuteman do
|
4
|
+
before do
|
5
|
+
@analytics = Minuteman.new
|
6
|
+
|
7
|
+
today = Time.now.utc
|
8
|
+
last_month = today - (3600 * 24 * 30)
|
9
|
+
last_week = today - (3600 * 24 * 7)
|
10
|
+
last_minute = today - 61
|
11
|
+
|
12
|
+
@analytics.mark("login", 12)
|
13
|
+
@analytics.mark("login", [2, 42])
|
14
|
+
@analytics.mark("login", 2, last_week)
|
15
|
+
@analytics.mark("login:successful", 567, last_month)
|
16
|
+
|
17
|
+
@year_events = @analytics.year("login", today)
|
18
|
+
@week_events = @analytics.week("login", today)
|
19
|
+
@month_events = @analytics.month("login", today)
|
20
|
+
@day_events = @analytics.day("login", today)
|
21
|
+
@hour_events = @analytics.hour("login", today)
|
22
|
+
@minute_events = @analytics.minute("login", today)
|
23
|
+
|
24
|
+
@last_week_events = @analytics.week("login", last_week)
|
25
|
+
@last_minute_events = @analytics.minute("login", last_minute)
|
26
|
+
@last_month_events = @analytics.month("login:successful", last_month)
|
27
|
+
end
|
28
|
+
|
29
|
+
after { @analytics.reset_all }
|
30
|
+
|
31
|
+
it "should initialize correctly" do
|
32
|
+
assert @analytics.redis
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should track an event on a time" do
|
36
|
+
assert_equal 3, @year_events.length
|
37
|
+
assert_equal 3, @week_events.length
|
38
|
+
assert_equal 1, @last_week_events.length
|
39
|
+
assert_equal 1, @last_month_events.length
|
40
|
+
assert_equal [true, true, false], @week_events.include?(12, 2, 1)
|
41
|
+
|
42
|
+
assert @year_events.include?(12)
|
43
|
+
assert @month_events.include?(12)
|
44
|
+
assert @day_events.include?(12)
|
45
|
+
assert @hour_events.include?(12)
|
46
|
+
assert @minute_events.include?(12)
|
47
|
+
|
48
|
+
assert @last_week_events.include?(2)
|
49
|
+
assert !@month_events.include?(5)
|
50
|
+
assert !@last_minute_events.include?(12)
|
51
|
+
assert @last_month_events.include?(567)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should accept the AND bitwise operations" do
|
55
|
+
and_operation = @week_events & @last_week_events
|
56
|
+
|
57
|
+
assert @week_events.include?(2)
|
58
|
+
assert @week_events.include?(12)
|
59
|
+
|
60
|
+
assert @last_week_events.include?(2)
|
61
|
+
assert !@last_week_events.include?(12)
|
62
|
+
|
63
|
+
assert_equal 1, and_operation.length
|
64
|
+
|
65
|
+
assert !and_operation.include?(12)
|
66
|
+
assert and_operation.include?(2)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should accept the OR bitwise operations" do
|
70
|
+
or_operation = @week_events | @last_week_events
|
71
|
+
|
72
|
+
assert @week_events.include?(2)
|
73
|
+
assert @last_week_events.include?(2)
|
74
|
+
assert !@last_week_events.include?(12)
|
75
|
+
|
76
|
+
assert_equal 3, or_operation.length
|
77
|
+
|
78
|
+
assert or_operation.include?(12)
|
79
|
+
assert or_operation.include?(2)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should accept the NOT bitwise operations" do
|
83
|
+
not_operation = -@week_events
|
84
|
+
|
85
|
+
assert @week_events.include?(2)
|
86
|
+
assert @week_events.include?(12)
|
87
|
+
|
88
|
+
assert !not_operation.include?(12)
|
89
|
+
assert !not_operation.include?(2)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: minuteman
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- elcuervo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 4.1.0
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 4.1.0
|
46
|
+
description: Fast and furious tracking system using Redis bitwise operations
|
47
|
+
email:
|
48
|
+
- yo@brunoaguirre.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- Gemfile
|
54
|
+
- Gemfile.lock
|
55
|
+
- LICENSE
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- lib/minuteman.rb
|
59
|
+
- lib/minuteman/bit_operations.rb
|
60
|
+
- lib/minuteman/time_events.rb
|
61
|
+
- lib/minuteman/time_span.rb
|
62
|
+
- lib/minuteman/time_spans.rb
|
63
|
+
- lib/minuteman/time_spans/day.rb
|
64
|
+
- lib/minuteman/time_spans/hour.rb
|
65
|
+
- lib/minuteman/time_spans/minute.rb
|
66
|
+
- lib/minuteman/time_spans/month.rb
|
67
|
+
- lib/minuteman/time_spans/week.rb
|
68
|
+
- lib/minuteman/time_spans/year.rb
|
69
|
+
- minuteman.gemspec
|
70
|
+
- test/test_helper.rb
|
71
|
+
- test/unit/minuteman_test.rb
|
72
|
+
homepage: http://github.com/elcuervo/minuteman
|
73
|
+
licenses: []
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.8.23
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Bit Analytics
|
96
|
+
test_files:
|
97
|
+
- test/test_helper.rb
|
98
|
+
- test/unit/minuteman_test.rb
|