minuteman 1.0.0.pre → 1.0.2

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.
@@ -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