minuteman 1.0.0.pre → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minuteman (1.0.0.pre)
4
+ minuteman (1.0.2)
5
5
  redis (~> 3.0.2)
6
6
 
7
7
  GEM
@@ -10,6 +10,8 @@ GEM
10
10
  minitest (4.2.0)
11
11
  rake (0.9.2.2)
12
12
  redis (3.0.2)
13
+ redis-namespace (1.2.1)
14
+ redis (~> 3.0.0)
13
15
 
14
16
  PLATFORMS
15
17
  ruby
@@ -18,3 +20,4 @@ DEPENDENCIES
18
20
  minitest (~> 4.2.0)
19
21
  minuteman!
20
22
  rake
23
+ redis-namespace (~> 1.2.1)
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![Minuteman](http://elcuervo.github.com/minuteman/img/minuteman-readme.png)
2
+
1
3
  # Minuteman
2
4
  [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/elcuervo/minuteman)
3
5
  [![Build Status](https://secure.travis-ci.org/elcuervo/minuteman.png?branch=master)](https://travis-ci.org/elcuervo/minuteman)
@@ -8,10 +10,6 @@ during the American Revolutionary War. _They provided a highly mobile, rapidly
8
10
  deployed force that allowed the colonies to respond immediately to war threats,
9
11
  hence the name._
10
12
 
11
- ![Minuteman](http://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Minute_Man_Statue_Lexington_Massachusetts_cropped.jpg/220px-Minute_Man_Statue_Lexington_Massachusetts_cropped.jpg)
12
-
13
- Fast analytics using Redis bitwise operations
14
-
15
13
  ## Origin
16
14
  Freenode - #cuba.rb - 2012/10/30 15:20 UYT
17
15
 
@@ -58,29 +56,42 @@ gem install minuteman
58
56
 
59
57
  ## Usage
60
58
 
59
+ Currently Minutemen supports two options `:silent (default: false)` and `:redis
60
+ (default: {})`
61
+
62
+ ### Options
63
+
64
+ **silent**: when `true` the operations will not raise errors to prevent failures
65
+
66
+ **redis**: can be a Hash with the options to be sent to `Redis.new` or a `Redis`
67
+ connection already established (`Redis::Namespace` works as well).
68
+
61
69
  ```ruby
62
70
  require "minuteman"
63
71
 
64
- # Accepts an options hash that will be sent as is to Redis.new
72
+ # Accepts an options[:redis] hash that will be sent as is to Redis.new
65
73
  analytics = Minuteman.new
66
74
 
75
+ # You can also reuse your Redis or Redis::Namespace connection
76
+ analytics = Minuteman.new(redis: Redis::Namespace.new(:mm, redis: Redis.new))
77
+
67
78
  # Mark an event for a given id
68
- analytics.mark("login:successful", user.id)
69
- analytics.mark("login:successful", other_user.id)
79
+ analytics.track("login:successful", user.id)
80
+ analytics.track("login:successful", other_user.id)
70
81
 
71
82
  # Mark in bulk
72
- analytics.mark("programming:love:ruby", User.where(favorite: "ruby").pluck(:id))
83
+ analytics.track("programming:love:ruby", User.where(favorite: "ruby").pluck(:id))
73
84
 
74
- # Fetch events for a given time
85
+ # Fetch events for a given time (default is Time.now.utc)
75
86
  today_events = analytics.day("login:successful", Time.now.utc)
76
87
 
77
88
  # This also exists
78
- analytics.year("login:successful", Time.now.utc)
79
- analytics.month("login:successful", Time.now.utc)
80
- analytics.week("login:successful", Time.now.utc)
81
- analytics.day("login:successful", Time.now.utc)
82
- analytics.hour("login:successful", Time.now.utc)
83
- analytics.minute("login:successful", Time.now.utc)
89
+ analytics.year("login:successful")
90
+ analytics.month("login:successful")
91
+ analytics.week("login:successful")
92
+ analytics.day("login:successful")
93
+ analytics.hour("login:successful")
94
+ analytics.minute("login:successful")
84
95
 
85
96
  # Lists all the tracked events
86
97
  analytics.events
@@ -123,7 +134,7 @@ set1 ^ set2
123
134
  Let's assume this scenario:
124
135
 
125
136
  You have a list of users and want to know which of them have been going throught
126
- some of the marks you made.
137
+ some of the tracks you made.
127
138
 
128
139
  ```ruby
129
140
  paid = analytics.month("buy:complete")
@@ -138,8 +149,8 @@ Currently the supported commands to interact with arrays are `&` and `-`
138
149
  ### Example
139
150
 
140
151
  ```ruby
141
- invited = analytics.month("email:invitation", Time.now.utc)
142
- successful_buys = analytics.month("buy:complete", Time.now.utc)
152
+ invited = analytics.month("email:invitation")
153
+ successful_buys = analytics.month("buy:complete")
143
154
 
144
155
  successful_buys_after_invitation = invited & successful_buys
145
156
  successful_buys_after_invitation.include?(user.id)
data/Rakefile CHANGED
@@ -4,5 +4,10 @@ Rake::TestTask.new("spec") do |t|
4
4
  t.pattern = "test/**/*_test.rb"
5
5
  end
6
6
 
7
+ Rake::TestTask.new("bench") do |t|
8
+ t.pattern = "test/bench/*_bench.rb"
9
+ end
10
+
7
11
  task :default => [:test]
8
- task :test => [:spec]
12
+ task :all => [:test, :bench]
13
+ task :test => [:spec]
@@ -1,5 +1,6 @@
1
1
  require "redis"
2
2
  require "time"
3
+ require "forwardable"
3
4
  require "minuteman/time_events"
4
5
 
5
6
  # Until redis gem gets updated
@@ -17,17 +18,36 @@ class Redis
17
18
  end
18
19
  end
19
20
 
21
+ # Public: Minuteman core classs
22
+ #
20
23
  class Minuteman
21
- attr_reader :redis
24
+ extend Forwardable
25
+
26
+ class << self
27
+ attr_accessor :redis, :options
28
+
29
+ # Public: Prevents a fatal error if the options are set to silent
30
+ #
31
+ def safe(&block)
32
+ yield if block
33
+ rescue Redis::BaseError => e
34
+ raise e unless options[:silent]
35
+ end
36
+ end
22
37
 
23
38
  PREFIX = "minuteman"
24
39
 
40
+ def_delegators self, :redis, :redis=, :options, :options=, :safe
41
+
25
42
  # Public: Initializes Minuteman
26
43
  #
27
- # options - The hash to be sent to Redis.new
44
+ # options - An options hash to change how Minuteman behaves
28
45
  #
29
46
  def initialize(options = {})
30
- @redis = Redis.new(options)
47
+ redis_options = options.delete(:redis) || {}
48
+
49
+ self.options = default_options.merge!(options)
50
+ self.redis = define_connection(redis_options)
31
51
  end
32
52
 
33
53
  # Public: Generates the methods to fech data
@@ -36,9 +56,12 @@ class Minuteman
36
56
  # date - A Time object used to do the search
37
57
  #
38
58
  %w[year month week day hour minute].each do |method_name|
39
- define_method(method_name) do |event_name, date|
59
+ define_method(method_name) do |*args|
60
+ event_name, date = *args
61
+ date ||= Time.now.utc
62
+
40
63
  constructor = self.class.const_get(method_name.capitalize)
41
- constructor.new(@redis, event_name, date)
64
+ constructor.new(event_name, date)
42
65
  end
43
66
  end
44
67
 
@@ -50,49 +73,87 @@ class Minuteman
50
73
  # Examples
51
74
  #
52
75
  # analytics = Minuteman.new
53
- # analytics.mark("login", 1)
54
- # analytics.mark("login", [2, 3, 4])
76
+ # analytics.track("login", 1)
77
+ # analytics.track("login", [2, 3, 4])
55
78
  #
56
- def mark(event_name, ids, time = Time.now.utc)
79
+ def track(event_name, ids, time = Time.now.utc)
57
80
  event_time = time.kind_of?(Time) ? time : Time.parse(time.to_s)
58
- time_events = TimeEvents.start(redis, event_name, event_time)
81
+ time_events = TimeEvents.start(event_name, event_time)
59
82
 
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
83
+ track_events(time_events, Array(ids))
65
84
  end
66
85
 
67
86
  # Public: List all the events given the minuteman namespace
68
87
  #
69
88
  def events
70
- keys = @redis.keys([PREFIX, "*", "????"].join("_"))
89
+ keys = safe { redis.keys([PREFIX, "*", "????"].join("_")) }
71
90
  keys.map { |key| key.split("_")[1] }
72
91
  end
73
92
 
74
93
  # Public: List all the operations executed in a given the minuteman namespace
75
94
  #
76
95
  def operations
77
- @redis.keys([operations_cache_key_prefix, "*"].join("_"))
96
+ safe { redis.keys([operations_cache_key_prefix, "*"].join("_")) }
78
97
  end
79
98
 
80
99
  # Public: Resets the bit operation cache keys
81
100
  #
82
101
  def reset_operations_cache
83
- keys = @redis.keys([operations_cache_key_prefix, "*"].join("_"))
84
- @redis.del(keys) if keys.any?
102
+ keys = safe { redis.keys([operations_cache_key_prefix, "*"].join("_")) }
103
+ safe { redis.del(keys) } if keys.any?
85
104
  end
86
105
 
87
106
  # Public: Resets all the used keys
88
107
  #
89
108
  def reset_all
90
- keys = @redis.keys([PREFIX, "*"].join("_"))
91
- @redis.del(keys) if keys.any?
109
+ keys = safe { redis.keys([PREFIX, "*"].join("_")) }
110
+ safe { redis.del(keys) } if keys.any?
92
111
  end
93
112
 
94
113
  private
95
114
 
115
+ # Private: Default configuration options
116
+ #
117
+ def default_options
118
+ {
119
+ cache: true,
120
+ silent: false
121
+ }
122
+ end
123
+
124
+ # Private: Determines to use or instance a Redis connection
125
+ #
126
+ # object: Can be the options to instance a Redis connection or a connection
127
+ # itself
128
+ #
129
+ def define_connection(object)
130
+ case object
131
+ when Redis, defined?(Redis::Namespace) && Redis::Namespace
132
+ object
133
+ else
134
+ Redis.new(object)
135
+ end
136
+ end
137
+
138
+ # Private: Marks ids for a given time events
139
+ #
140
+ # time_events: A set of TimeEvents
141
+ # ids: The ids to be tracked
142
+ #
143
+ def track_events(time_events, ids)
144
+ safe_multi do
145
+ time_events.each do |event|
146
+ ids.each { |id| safe { redis.setbit(event.key, id, 1) } }
147
+ end
148
+ end
149
+ end
150
+
151
+ # Private: Executes a block within a safe connection using redis.multi
152
+ #
153
+ def safe_multi(&block)
154
+ safe { redis.multi(&block) }
155
+ end
156
+
96
157
  # Private: The prefix key of all the operations
97
158
  #
98
159
  def operations_cache_key_prefix
@@ -1,7 +1,13 @@
1
1
  require "minuteman/bit_operations/operation"
2
2
 
3
+ # Public: Minuteman core classs
4
+ #
3
5
  class Minuteman
4
6
  module BitOperations
7
+ extend Forwardable
8
+
9
+ def_delegators :Minuteman, :safe, :redis
10
+
5
11
  # Public: Checks for the existance of ids on a given set
6
12
  #
7
13
  # ids - Array of ids
@@ -14,13 +20,13 @@ class Minuteman
14
20
  # Public: Resets the current key
15
21
  #
16
22
  def reset
17
- redis.rem(key)
23
+ safe { redis.rem(key) }
18
24
  end
19
25
 
20
26
  # Public: Cheks for the amount of ids stored on the current key
21
27
  #
22
28
  def length
23
- redis.bitcount(key)
29
+ safe { redis.bitcount(key) }
24
30
  end
25
31
 
26
32
  # Public: Calculates the NOT of the current key
@@ -70,7 +76,7 @@ class Minuteman
70
76
  # id: The bit
71
77
  #
72
78
  def getbit(id)
73
- redis.getbit(key, id) == 1
79
+ safe { redis.getbit(key, id) == 1 }
74
80
  end
75
81
 
76
82
  # Private: Cxecutes an operation between the current timespan and another
@@ -85,7 +91,7 @@ class Minuteman
85
91
  # Private: Memoizes the operation class
86
92
  #
87
93
  def operate
88
- @_operate ||= Operation.new(redis, self)
94
+ @_operate ||= Operation.new(self)
89
95
  end
90
96
  end
91
97
  end
@@ -1,14 +1,15 @@
1
1
  require "minuteman/bit_operations"
2
2
 
3
+ # Public: Minuteman core classs
4
+ #
3
5
  class Minuteman
4
6
  module BitOperations
5
7
  # Public: The conversion of an array to an operable class
6
8
  #
7
- # redis - The Redis connection
8
9
  # key - The key where the result it's stored
9
10
  # data - The original data of the intersection
10
11
  #
11
- class Data < Struct.new(:redis, :key, :data)
12
+ class Data < Struct.new(:key, :data)
12
13
  include BitOperations
13
14
  include Enumerable
14
15
 
@@ -1,16 +1,17 @@
1
1
  require "minuteman/bit_operations/plain"
2
2
  require "minuteman/bit_operations/with_data"
3
3
 
4
+ # Public: Minuteman core classs
5
+ #
4
6
  class Minuteman
5
7
  module BitOperations
6
8
  # Public: Handles the operations between two timespans
7
9
  #
8
- # redis: The Redis connection
9
10
  # type: The operation type
10
11
  # timespan: One of the timespans to be permuted
11
12
  # other: The other timespan to be permuted
12
13
  #
13
- class Operation < Struct.new(:redis, :timespan)
14
+ class Operation < Struct.new(:timespan)
14
15
  # Public: Caches operations against Array
15
16
  #
16
17
  class Cache
@@ -68,7 +69,7 @@ class Minuteman
68
69
  return minus_operation if type == "MINUS" && operable?
69
70
  return cache[other] if cache.include?(other)
70
71
 
71
- caching { klass.new(redis, type, other, timespan.key).call }
72
+ caching { klass.new(type, other, timespan.key).call }
72
73
  end
73
74
 
74
75
  private
@@ -83,7 +84,10 @@ class Minuteman
83
84
  #
84
85
  def caching
85
86
  executed_class = yield
86
- cache[other] = executed_class if other.is_a?(Array)
87
+ if other.is_a?(Array) && Minuteman.options[:cache]
88
+ cache[other] = executed_class
89
+ end
90
+
87
91
  executed_class
88
92
  end
89
93
 
@@ -1,18 +1,22 @@
1
1
  require "minuteman/keys_methods"
2
2
  require "minuteman/bit_operations/result"
3
3
 
4
+ # Public: Minuteman core classs
5
+ #
4
6
  class Minuteman
5
7
  module BitOperations
6
8
  # Public: The class to handle operations with others timespans
7
9
  #
8
- # redis: The Redis connection
9
10
  # type: The operation type
10
11
  # timespan: The timespan to be permuted
11
12
  # source_key: The original key to do the operation
12
13
  #
13
- class Plain < Struct.new(:redis, :type, :timespan, :source_key)
14
+ class Plain < Struct.new(:type, :timespan, :source_key)
15
+ extend Forwardable
14
16
  include KeysMethods
15
17
 
18
+ def_delegators :Minuteman, :redis, :safe
19
+
16
20
  def call
17
21
  events = if source_key == timespan
18
22
  Array(source_key)
@@ -21,9 +25,9 @@ class Minuteman
21
25
  end
22
26
 
23
27
  key = destination_key(type, events)
24
- redis.bitop(type, key, events)
28
+ safe { redis.bitop(type, key, events) }
25
29
 
26
- Result.new(redis, key)
30
+ Result.new(key)
27
31
  end
28
32
  end
29
33
  end
@@ -1,13 +1,14 @@
1
1
  require "minuteman/bit_operations"
2
2
 
3
+ # Public: Minuteman core classs
4
+ #
3
5
  class Minuteman
4
6
  module BitOperations
5
7
  # Public: The result of intersecting results
6
8
  #
7
- # redis - The Redis connection
8
9
  # key - The key where the result it's stored
9
10
  #
10
- class Result < Struct.new(:redis, :key)
11
+ class Result < Struct.new(:key)
11
12
  include BitOperations
12
13
  end
13
14
  end
@@ -1,35 +1,55 @@
1
1
  require "minuteman/keys_methods"
2
2
  require "minuteman/bit_operations/data"
3
3
 
4
+ # Public: Minuteman core classs
5
+ #
4
6
  class Minuteman
5
7
  module BitOperations
6
8
  # Public: The class to handle operations with datasets
7
9
  #
8
- # redis: The Redis connection
9
10
  # type: The operation type
10
11
  # data: The data to be permuted
11
12
  # source_key: The original key to do the operation
12
13
  #
13
- class WithData < Struct.new(:redis, :type, :data, :source_key)
14
+ class WithData < Struct.new(:type, :data, :source_key)
15
+ extend Forwardable
14
16
  include KeysMethods
15
17
 
18
+ def_delegators :Minuteman, :redis, :safe
19
+
16
20
  def call
17
- normalized_data = Array(data)
18
21
  key = destination_key("data-#{type}", normalized_data)
19
- command = case type
20
- when "AND" then :select
21
- when "MINUS" then :reject
22
- end
23
22
 
24
- intersected_data = normalized_data.send(command) do |id|
25
- redis.getbit(source_key, id) == 1
23
+ if !safe { redis.exists(key) }
24
+ intersected_data.each { |id| safe { redis.setbit(key, id, 1) } }
26
25
  end
27
26
 
28
- if !redis.exists(key)
29
- intersected_data.each { |id| redis.setbit(key, id, 1) }
27
+ Data.new(key, intersected_data)
28
+ end
29
+
30
+ private
31
+
32
+ # Private: Normalized data
33
+ #
34
+ def normalized_data
35
+ Array(data)
36
+ end
37
+
38
+ # Private: Defines command to get executed based on the type
39
+ #
40
+ def command
41
+ case type
42
+ when "AND" then :select
43
+ when "MINUS" then :reject
30
44
  end
45
+ end
31
46
 
32
- Data.new(redis, key, intersected_data)
47
+ # Private: The intersected data depending on the command executed
48
+ #
49
+ def intersected_data
50
+ normalized_data.send(command) do |id|
51
+ Minuteman.redis.getbit(source_key, id) == 1
52
+ end
33
53
  end
34
54
  end
35
55
  end
@@ -1,3 +1,5 @@
1
+ # Public: Minuteman core classs
2
+ #
1
3
  class Minuteman
2
4
  module KeysMethods
3
5
  BIT_OPERATION_PREFIX = "bitop"
@@ -1,16 +1,17 @@
1
1
  require "minuteman/time_spans"
2
2
 
3
+ # Public: Minuteman core classs
4
+ #
3
5
  class Minuteman
4
6
  module TimeEvents
5
7
  # Public: Helper to get all the time trackers ready
6
8
  #
7
- # redis - The Redis connection
8
9
  # event_name - The event to be tracked
9
10
  # date - A given Time object
10
11
  #
11
- def self.start(redis, event_name, time)
12
+ def self.start(event_name, time)
12
13
  [Year, Month, Week, Day, Hour, Minute].map do |t|
13
- t.new(redis, event_name, time)
14
+ t.new(event_name, time)
14
15
  end
15
16
  end
16
17
  end
@@ -1,22 +1,24 @@
1
1
  require "minuteman/bit_operations"
2
2
 
3
+ # Public: Minuteman core classs
4
+ #
3
5
  class Minuteman
6
+ # Public: The timespan class. All the time span classes inherit from this one
7
+ #
4
8
  class TimeSpan
5
9
  include BitOperations
6
10
 
7
- attr_reader :key, :redis
11
+ attr_reader :key
8
12
 
9
13
  DATE_FORMAT = "%s-%02d-%02d"
10
14
  TIME_FORMAT = "%02d:%02d"
11
15
 
12
16
  # Public: Initializes the base TimeSpan class
13
17
  #
14
- # redis - The Redis connection
15
18
  # event_name - The event to be tracked
16
19
  # date - A given Time object
17
20
  #
18
- def initialize(redis, event_name, date)
19
- @redis = redis
21
+ def initialize(event_name, date)
20
22
  @key = build_key(event_name, time_format(date))
21
23
  end
22
24
 
@@ -1,4 +1,8 @@
1
+ # Public: Minuteman core classs
2
+ #
1
3
  class Minuteman
4
+ # Public: Day TimeSpan class
5
+ #
2
6
  class Day < TimeSpan
3
7
  private
4
8
 
@@ -1,4 +1,8 @@
1
+ # Public: Minuteman core classs
2
+ #
1
3
  class Minuteman
4
+ # Public: Hour TimeSpan class
5
+ #
2
6
  class Hour < TimeSpan
3
7
  private
4
8
 
@@ -1,4 +1,8 @@
1
+ # Public: Minuteman core classs
2
+ #
1
3
  class Minuteman
4
+ # Public: Minute TimeSpan class
5
+ #
2
6
  class Minute < TimeSpan
3
7
  private
4
8
 
@@ -1,4 +1,8 @@
1
+ # Public: Minuteman core classs
2
+ #
1
3
  class Minuteman
4
+ # Public: Month TimeSpan class
5
+ #
2
6
  class Month < TimeSpan
3
7
  private
4
8
 
@@ -1,4 +1,8 @@
1
+ # Public: Minuteman core classs
2
+ #
1
3
  class Minuteman
4
+ # Public: Month TimeSpan class
5
+ #
2
6
  class Week < TimeSpan
3
7
  private
4
8
 
@@ -1,4 +1,8 @@
1
+ # Public: Minuteman core classs
2
+ #
1
3
  class Minuteman
4
+ # Public: Year TimeSpan class
5
+ #
2
6
  class Year < TimeSpan
3
7
  private
4
8
 
@@ -1,9 +1,10 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "minuteman"
3
- s.version = "1.0.0.pre"
3
+ s.version = "1.0.2"
4
4
  s.summary = "Bit Analytics"
5
5
  s.description = "Fast and furious tracking system using Redis bitwise operations"
6
6
  s.authors = ["elcuervo"]
7
+ s.licenses = ["MIT"]
7
8
  s.email = ["yo@brunoaguirre.com"]
8
9
  s.homepage = "http://github.com/elcuervo/minuteman"
9
10
  s.files = `git ls-files`.split("\n")
@@ -11,5 +12,6 @@ Gem::Specification.new do |s|
11
12
 
12
13
  s.add_dependency("redis", "~> 3.0.2")
13
14
 
14
- s.add_development_dependency("minitest", "~> 4.2.0")
15
+ s.add_development_dependency("minitest", "~> 4.2.0")
16
+ s.add_development_dependency("redis-namespace", "~> 1.2.1")
15
17
  end
@@ -0,0 +1,37 @@
1
+ require_relative "../test_helper"
2
+ require "minitest/benchmark"
3
+
4
+ describe Minuteman do
5
+ before do
6
+ today = Time.now.utc
7
+ last_week = today - (3600 * 24 * 7)
8
+
9
+ @analytics = Minuteman.new
10
+ @analytics.mark("login", 12)
11
+ @analytics.mark("login", [2, 42])
12
+ @analytics.mark("login:successful", 567, last_week)
13
+
14
+ @week_events = @analytics.week("login")
15
+ @last_week_events = @analytics.week("login", last_week)
16
+ @last_week_events2 = @analytics.month("login:successful", last_week)
17
+ end
18
+
19
+ bench_performance_constant("AND") { @week_events & @last_week_events }
20
+ bench_performance_constant("OR") { @week_events | @last_week_events }
21
+ bench_performance_constant("XOR") { @week_events ^ @last_week_events }
22
+ bench_performance_constant("NOT") { ~@week_events }
23
+ bench_performance_constant("MINUS") { @week_events - @last_week_events }
24
+
25
+ bench_performance_constant "complex operations" do
26
+ @week_events & (@last_week_events ^ @last_week_events2)
27
+ end
28
+
29
+ bench_performance_constant "intersections using cache" do
30
+ 5.times { @week_events & [2, 12, 43] }
31
+ end
32
+
33
+ bench_performance_constant "intersections not using cache" do
34
+ @analytics.options[:cache] = false
35
+ 5.times { @week_events & [2, 12, 43] }
36
+ end
37
+ end
@@ -5,3 +5,4 @@ require "minitest/spec"
5
5
  require "minitest/pride"
6
6
  require "minitest/autorun"
7
7
  require "minuteman"
8
+ require "redis-namespace"
@@ -5,14 +5,14 @@ describe Minuteman do
5
5
  @analytics = Minuteman.new
6
6
 
7
7
  today = Time.now.utc
8
- last_month = today - (3600 * 24 * 30)
9
- last_week = today - (3600 * 24 * 7)
8
+ last_month = today - (3600 * 24 * 30)
9
+ last_week = today - (3600 * 24 * 7)
10
10
  last_minute = today - 120
11
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)
12
+ @analytics.track("login", 12)
13
+ @analytics.track("login", [2, 42])
14
+ @analytics.track("login", 2, last_week)
15
+ @analytics.track("login:successful", 567, last_month)
16
16
 
17
17
  @year_events = @analytics.year("login", today)
18
18
  @week_events = @analytics.week("login", today)
@@ -160,3 +160,58 @@ describe Minuteman do
160
160
  assert_equal 2, ids.size
161
161
  end
162
162
  end
163
+
164
+ describe "Using options" do
165
+ it "should be able to stop using the cache" do
166
+ minuteman = Minuteman.new
167
+ assert_equal true, minuteman.options[:cache]
168
+
169
+ minuteman.options[:cache] = false
170
+
171
+ assert_equal false, minuteman.options[:cache]
172
+ end
173
+ end
174
+
175
+ describe "Changing Minuteman redis connections" do
176
+ it "should support using a Redis instance" do
177
+ redis = Redis.new
178
+ minuteman = Minuteman.new(redis: redis)
179
+
180
+ assert_equal redis, Minuteman.redis
181
+ assert_equal redis, minuteman.redis
182
+ end
183
+
184
+ it "should support changing the current connection" do
185
+ redis = Redis.new
186
+ minuteman = Minuteman.new
187
+
188
+ assert redis != Minuteman.redis
189
+
190
+ minuteman.redis = redis
191
+
192
+ assert_equal redis, minuteman.redis
193
+ end
194
+
195
+ it "should support Redis::Namespace" do
196
+ namespace = Redis::Namespace.new(:ns, redis: Redis.new)
197
+
198
+ minuteman = Minuteman.new(redis: namespace)
199
+
200
+ assert_equal namespace, Minuteman.redis
201
+ assert_equal namespace, minuteman.redis
202
+ end
203
+
204
+ it "should fail silently" do
205
+ minuteman = Minuteman.new(silent: true, redis: { port: 1234 })
206
+
207
+ minuteman.track("test", 1)
208
+ end
209
+
210
+ it "should fail loudly" do
211
+ minuteman = Minuteman.new(redis: { port: 1234 })
212
+
213
+ assert_raises Redis::CannotConnectError do
214
+ minuteman.track("test", 1)
215
+ end
216
+ end
217
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minuteman
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre
5
- prerelease: 6
4
+ version: 1.0.2
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - elcuervo
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-23 00:00:00.000000000 Z
12
+ date: 2012-11-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -43,6 +43,22 @@ dependencies:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
45
  version: 4.2.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: redis-namespace
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.1
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.1
46
62
  description: Fast and furious tracking system using Redis bitwise operations
47
63
  email:
48
64
  - yo@brunoaguirre.com
@@ -74,10 +90,12 @@ files:
74
90
  - lib/minuteman/time_spans/week.rb
75
91
  - lib/minuteman/time_spans/year.rb
76
92
  - minuteman.gemspec
93
+ - test/bench/minuteman_bench.rb
77
94
  - test/test_helper.rb
78
95
  - test/unit/minuteman_test.rb
79
96
  homepage: http://github.com/elcuervo/minuteman
80
- licenses: []
97
+ licenses:
98
+ - MIT
81
99
  post_install_message:
82
100
  rdoc_options: []
83
101
  require_paths:
@@ -91,9 +109,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
109
  required_rubygems_version: !ruby/object:Gem::Requirement
92
110
  none: false
93
111
  requirements:
94
- - - ! '>'
112
+ - - ! '>='
95
113
  - !ruby/object:Gem::Version
96
- version: 1.3.1
114
+ version: '0'
97
115
  requirements: []
98
116
  rubyforge_project:
99
117
  rubygems_version: 1.8.23
@@ -101,5 +119,6 @@ signing_key:
101
119
  specification_version: 3
102
120
  summary: Bit Analytics
103
121
  test_files:
122
+ - test/bench/minuteman_bench.rb
104
123
  - test/test_helper.rb
105
124
  - test/unit/minuteman_test.rb