bit_analytics 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: 8978bfb9dd2bf699d69081776c673859b7893687
4
+ data.tar.gz: 22dce94749ec582804d3fba2de027effa55de4b9
5
+ !binary "U0hBNTEy":
6
+ metadata.gz: a24a71ba813bdb1b5daf2177450a91bd7fb544d322cbb7e10dd58e82006751678cf6a74a79781606b034b61703f5672b16584c7cbd39aae612076bdbe1a4f039
7
+ data.tar.gz: c5fdc49e40652c6eb5529f1cb23106ce3bf1f3cf5f0557d0112157da15aaf7f1cc4da956ab09c829ed63d08692c03ff6e534686db5add1e01e25a738fdbef3df
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bit_analytics.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,28 @@
1
+ Copyright: 2012 by Doist Ltd., Amir Salihefendic
2
+ Copyright (c) 2014 Jure Triglav.
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification,
6
+ are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice,
9
+ this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of BitAnalytics nor the names of its contributors may be used
16
+ to endorse or promote products derived from this software without
17
+ specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # bit_analytics: a powerful analytics library for Redis for Ruby
2
+
3
+ This Ruby gem (a port of [@amix's bitmapist library for Python](https://github.com/Doist/bitmapist)) makes it possible to implement real-time, highly scalable analytics that can answer following questions:
4
+
5
+ * Has user 123 been online today? This week? This month?
6
+ * Has user 123 performed action "X"?
7
+ * How many users have been active have this month? This hour?
8
+ * How many unique users have performed action "X" this week?
9
+ * How many % of users that were active last week are still active?
10
+ * How many % of users that were active last month are still active this month?
11
+
12
+ This gem is very easy to use and enables you to create your own reports easily.
13
+
14
+ Using Redis bitmaps you can store events for millions of users in a very little amount of memory (megabytes).
15
+ You should be careful about using huge ids (e.g. 2^32 or bigger) as this could require larger amounts of memory.
16
+
17
+ If you want to read more about bitmaps please read following:
18
+
19
+ * http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/
20
+ * http://redis.io/commands/setbit
21
+ * http://en.wikipedia.org/wiki/Bit_array
22
+ * http://www.slideshare.net/crashlytics/crashlytics-on-redis-analytics
23
+ * http://amix.dk/blog/post/19714 [my blog post]
24
+
25
+ Requires Redis 2.6+.
26
+
27
+ # Installation
28
+
29
+ ```
30
+ $ gem install bit_analytics
31
+ ```
32
+
33
+ # Ports
34
+
35
+ * Original Python library: https://github.com/Doist/bitmapist
36
+ * PHP port: https://github.com/jeremyFreeAgent/Bitter
37
+
38
+ # Examples
39
+
40
+ Setting things up:
41
+
42
+ ```ruby
43
+ require 'date'
44
+ now = Time.now.getutc
45
+ last_month = (Date.today << 1).to_time
46
+
47
+ # This connects to the default Redis (localhost:6379)
48
+ @bit_analytics = BitAnalytics.new
49
+
50
+ # You can also use custom Redis options
51
+ @bit_analytics = BitAnalytics.new(host: "10.0.1.1", port: 6380)
52
+ ```
53
+
54
+ Mark user 123 as active and has played a song:
55
+
56
+ ```ruby
57
+ @bit_anaytics.mark_event('active', 123)
58
+ @bit_anaytics.mark_event('song:played', 123)
59
+ ```
60
+
61
+ Answer if user 123 has been active this month:
62
+
63
+ ```ruby
64
+ @bit_analytics.month_events('active', now.year, now.month).includes?(123)
65
+ @bit_analytics.month_events('song:played', now.year, now.month).includes?(123)
66
+ @bit_analytics.month_events('active', now.year, now.month).has_events_marked == true
67
+ ```
68
+
69
+ How many users have been active this week?:
70
+
71
+ ```ruby
72
+ this_week = Time.now.strftime('%V')
73
+ @bit_analytics.week_events('active', now.year, this_week)
74
+ ```
75
+
76
+ Perform bit operations. How many users that have been active last month are still active this month?
77
+
78
+ ```ruby
79
+ active_2_months = @bit_analytics.bit_op_and(
80
+ @bit_analytics.month_events('active', last_month.year, last_month.month),
81
+ @bit_analytics.month_events('active', now.year, now.month)
82
+ )
83
+ puts active_2_months.length
84
+
85
+ # Is 123 active for 2 months?
86
+ active_2_months.includes?(123)
87
+ ```
88
+
89
+ Work with nested bit operations (imagine what you can do with this ;))!
90
+
91
+ ```ruby
92
+ active_2_months = @bit_analytics.bit_op_and(
93
+ @bit_analytics.bit_op_and(
94
+ @bit_analytics.month_events('active', last_month.year, last_month.month),
95
+ @bit_analytics.month_events('active', now.year, now.month)
96
+ ),
97
+ @bit_analytics.month_events('active', now.year, now.month)
98
+ )
99
+ puts active_2_months.length
100
+ active_2_months.includes?(123)
101
+
102
+ # Delete the temporary AND operation
103
+ active_2_months.delete
104
+ ```
105
+
106
+ Tracking hourly is disabled by default to save memory, but you can supply an extra argument to `mark_event` to track events hourly:
107
+
108
+ ```ruby
109
+ @bit_analytics.mark_event('active', 123, track_hourly: true)
110
+ ```
111
+
112
+ Original library: Copyright: 2012 by Doist Ltd. Developer: Amir Salihefendic ( http://amix.dk ) License: BSD
113
+
114
+ Ruby port: Copyright 2014 Jure Triglav License: BSD
115
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.options = "-v"
6
+ t.test_files = FileList["test/*_test.rb"]
7
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bit_analytics/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bit_analytics"
8
+ spec.version = BitAnalytics::VERSION
9
+ spec.authors = ["Jure Triglav"]
10
+ spec.email = ["juretriglav@gmail.com"]
11
+ spec.description = %q{Analytics library built with Redis bitmaps}
12
+ spec.summary = %q{Implements a powerful analytics library on top of Redis's support for bitmaps and bitmap operations.}
13
+ spec.homepage = "http://www.github.com/jure/bit_analytics"
14
+ spec.license = "BSD"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "redis", "~> 3.0"
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "pry"
25
+ end
@@ -0,0 +1,3 @@
1
+ class BitAnalytics
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,259 @@
1
+ require "bit_analytics/version"
2
+ require 'redis'
3
+
4
+ class BitAnalytics
5
+ attr_accessor :redis
6
+
7
+ def initialize(options=nil)
8
+ @redis = if options
9
+ Redis.new(options)
10
+ else
11
+ Redis.new
12
+ end
13
+ end
14
+
15
+ #--- Events marking and deleting ---
16
+ # Marks an event for hours, days, weeks and months.
17
+ # :param :event_name The name of the event, could be "active" or "new_signups"
18
+ # :param :uuid An unique id, typically user id. The id should not be huge, read Redis documentation why (bitmaps)
19
+ # :param :now Which date should be used as a reference point, default is `datetime.utcnow`
20
+ # :param :track_hourly Should hourly stats be tracked, defaults to bitanalytics.TRACK_HOURLY, but an be changed
21
+ # Examples:
22
+ # Mark id 1 as active
23
+ # mark_event('active', 1)
24
+ # Mark task completed for id 252
25
+ # mark_event('tasks:completed', 252)
26
+ def mark_event(event_name, uuid, now: nil, track_hourly: nil)
27
+ # Has memory applications
28
+ track_hourly ||= false
29
+ now ||= Time.now.getutc
30
+ # E.g. ['2014', '03'] for 17th of January 2014
31
+ iso_date = now.strftime('%Y-%V').split('-')
32
+
33
+ events = [
34
+ MonthEvents.new(event_name, now.year, now.month),
35
+ WeekEvents.new(event_name, iso_date[0], iso_date[1]),
36
+ DayEvents.new(event_name, now.year, now.month, now.day),
37
+ ]
38
+
39
+ if track_hourly
40
+ events << HourEvents.new(event_name, now.year, now.month, now.day, now.hour)
41
+ end
42
+
43
+ @redis.pipelined do
44
+ events.each do |event|
45
+ @redis.setbit(event.redis_key, uuid, 1)
46
+ end
47
+ end
48
+ end
49
+
50
+ # Delete all events from the database.
51
+ def delete_all_events
52
+ keys = @redis.keys('bitanalytics_*')
53
+ @redis.del(*keys) unless keys.empty?
54
+ end
55
+
56
+ # Delete all temporary keys that are used when using bit operations.
57
+ def delete_temporary_bitop_keys
58
+ keys = @redis.keys('bitanalytics_bitop_*')
59
+ @redis.del(keys) unless keys.empty?
60
+ end
61
+
62
+ #--- Events ---
63
+
64
+ def month_events(event_name, year, month)
65
+ month_events = MonthEvents.new(event_name, year, month)
66
+ month_events.redis = @redis
67
+ month_events
68
+ end
69
+
70
+ def week_events(event_name, year, week)
71
+ week_events = WeekEvents.new(event_name, year, week)
72
+ week_events.redis = @redis
73
+ week_events
74
+ end
75
+
76
+ def day_events(event_name, year, month, day)
77
+ day_events = DayEvents.new(event_name, year, month, day)
78
+ day_events.redis = @redis
79
+ day_events
80
+ end
81
+
82
+ def hour_events(event_name, year, month, day, hour)
83
+ hour_events = HourEvents.new(event_name, year, month, day, hour)
84
+ hour_events.redis = @redis
85
+ hour_events
86
+ end
87
+
88
+ #--- BitOps ---
89
+
90
+ def bit_op_and(event, *events)
91
+ bit_operation = BitOperation.new('AND', event, *events)
92
+ bit_operation.redis = @redis
93
+ bit_operation.execute
94
+ bit_operation
95
+ end
96
+
97
+ def bit_op_or(event, *events)
98
+ bit_operation = BitOperation.new('OR', event, *events)
99
+ bit_operation.redis = @redis
100
+ bit_operation.execute
101
+ bit_operation
102
+ end
103
+
104
+ def bit_op_xor(event, *events)
105
+ bit_operation = BitOperation.new('XOR', event, *events)
106
+ bit_operation.redis = @redis
107
+ bit_operation.execute
108
+ bit_operation
109
+ end
110
+
111
+ #--- Private ---
112
+
113
+ def _prefix_key(event_name, date)
114
+ return 'bitanalytics_%s_%s' % [event_name, date]
115
+ end
116
+
117
+ #--- Events ---
118
+
119
+ # Extends with an obj.has_events_marked()
120
+ # that returns `True` if there are any events marked,
121
+ # otherwise `False` is returned.
122
+
123
+ # Extens also with a obj.delete()
124
+ # (useful for deleting temporary calculations).
125
+
126
+ module MixinEventsMisc
127
+ def has_events_marked
128
+ @redis.get(@redis_key) != nil
129
+ end
130
+ def delete
131
+ @redis.del(@redis_key)
132
+ end
133
+ end
134
+
135
+ # Extends with an obj.get_count() that uses BITCOUNT to
136
+ # count all the events. Supports also __len__
137
+
138
+ module MixinCounts
139
+ def get_count
140
+ @redis.bitcount(@redis_key)
141
+ end
142
+
143
+ def length
144
+ return get_count
145
+ end
146
+ end
147
+
148
+ # Makes it possible to see if an uuid has been marked.
149
+ # Example:
150
+ # user_active_today = 123 in DayEvents('active', 2012, 10, 23)
151
+ module MixinContains
152
+ def includes?(uuid)
153
+ if @redis.getbit(self.redis_key, uuid) == 1
154
+ true
155
+ else
156
+ false
157
+ end
158
+ end
159
+ end
160
+
161
+ module RedisConnection
162
+ attr_accessor :redis
163
+ attr_reader :redis_key
164
+ end
165
+ end
166
+
167
+
168
+ # Events for a month.
169
+ # Example:
170
+ # MonthEvents('active', 2012, 10)
171
+ class MonthEvents < BitAnalytics
172
+ include RedisConnection
173
+ include MixinCounts
174
+ include MixinContains
175
+ include MixinEventsMisc
176
+
177
+ attr_reader :redis_key
178
+
179
+ def initialize(event_name, year, month)
180
+ @redis_key = _prefix_key(event_name,'%s-%s' % [year, month])
181
+ end
182
+ end
183
+
184
+ # Events for a week.
185
+ # Example:
186
+ # WeekEvents('active', 2012, 48)
187
+ class WeekEvents < BitAnalytics
188
+ include RedisConnection
189
+ include MixinCounts
190
+ include MixinContains
191
+ include MixinEventsMisc
192
+
193
+ def initialize(event_name, year, week)
194
+ @redis_key = _prefix_key(event_name,'W%s-%s' % [year, week])
195
+ end
196
+ end
197
+
198
+ # Events for a day.
199
+ # Example:
200
+ # DayEvents('active', 2012, 10, 23)
201
+ class DayEvents < BitAnalytics
202
+ include RedisConnection
203
+ include MixinCounts
204
+ include MixinContains
205
+ include MixinEventsMisc
206
+
207
+ def initialize(event_name, year, month, day)
208
+ @redis_key = _prefix_key(event_name,'%s-%s-%s' % [year, month, day])
209
+ end
210
+ end
211
+
212
+ # Events for a hour.
213
+ # Example:
214
+ # HourEvents('active', 2012, 10, 23, 13)
215
+ class HourEvents < BitAnalytics
216
+ include RedisConnection
217
+ include MixinCounts
218
+ include MixinContains
219
+ include MixinEventsMisc
220
+
221
+ def initialize(event_name, year, month, day, hour)
222
+ @redis_key = _prefix_key(event_name,'%s-%s-%s-%s' % [year, month, day, hour])
223
+ end
224
+ end
225
+
226
+ #--- Bit operations ---
227
+ # Base class for bit operations (AND, OR, XOR).
228
+ # Please note that each bit operation creates a new key prefixed with `bitanalytics_bitop_`.
229
+ # These temporary keys can be deleted with `delete_temporary_bitop_keys`.
230
+
231
+ # You can even nest bit operations.
232
+ # Example:
233
+ # active_2_months = BitOpAnd(
234
+ # MonthEvents('active', last_month.year, last_month.month),
235
+ # MonthEvents('active', now.year, now.month)
236
+ # )
237
+ # active_2_months = BitOpAnd(
238
+ # BitOpAnd(
239
+ # MonthEvents('active', last_month.year, last_month.month),
240
+ # MonthEvents('active', now.year, now.month)
241
+ # ),
242
+ # MonthEvents('active', now.year, now.month)
243
+ # )
244
+ class BitOperation < BitAnalytics
245
+ include MixinContains
246
+ include MixinCounts
247
+ include MixinEventsMisc
248
+ include RedisConnection
249
+
250
+ def initialize(op_name, *events)
251
+ @op_name = op_name
252
+ @event_redis_keys = events.map(&:redis_key)
253
+ @redis_key = 'bitanalytics_bitop_%s_%s' % [@op_name, @event_redis_keys.join('-')]
254
+ end
255
+
256
+ def execute
257
+ @redis.bitop(@op_name, @redis_key, @event_redis_keys)
258
+ end
259
+ end
@@ -0,0 +1,142 @@
1
+ require 'date'
2
+ require 'test/unit'
3
+ require 'bit_analytics'
4
+
5
+ class BitAnalyticsTest < Test::Unit::TestCase
6
+ def setup
7
+ @bit_analytics = BitAnalytics.new
8
+ end
9
+
10
+ def test_mark_with_diff_days
11
+ @bit_analytics.delete_all_events
12
+ @bit_analytics.mark_event('active', 123, track_hourly: true)
13
+
14
+ now = Time.now.getutc
15
+
16
+ # E.g. ['2014', '03'] for 17th of January 2014
17
+ iso_date = now.strftime('%Y-%V').split('-')
18
+
19
+ # Month
20
+ assert @bit_analytics.month_events('active', now.year, now.month).includes?(123)
21
+ assert !@bit_analytics.month_events('active', now.year, now.month).includes?(124)
22
+
23
+ # Week
24
+ assert @bit_analytics.week_events('active', now.year, iso_date[1]).includes?(123)
25
+ assert !@bit_analytics.week_events('active', now.year, iso_date[1]).includes?(124)
26
+
27
+ # Day
28
+ assert @bit_analytics.day_events('active', now.year, now.month, now.day).includes?(123)
29
+ assert !@bit_analytics.day_events('active', now.year, now.month, now.day).includes?(124)
30
+
31
+ # Hour
32
+ assert @bit_analytics.hour_events('active', now.year, now.month, now.day, now.hour).includes?(123)
33
+ assert !@bit_analytics.hour_events('active', now.year, now.month, now.day, now.hour).includes?(124)
34
+ assert !@bit_analytics.hour_events('active', now.year, now.month, now.day, now.hour-1).includes?(124)
35
+ end
36
+
37
+ def test_mark_counts
38
+ @bit_analytics.delete_all_events
39
+
40
+ now = Time.now.getutc
41
+ assert @bit_analytics.month_events('active', now.year, now.month).get_count == 0
42
+
43
+ @bit_analytics.mark_event('active', 123)
44
+ @bit_analytics.mark_event('active', 23232)
45
+
46
+ assert @bit_analytics.month_events('active', now.year, now.month).length == 2
47
+ end
48
+
49
+ def test_different_dates
50
+ @bit_analytics.delete_all_events
51
+
52
+ now = Time.now.getutc
53
+ yesterday = (Date.today - 1).to_time
54
+
55
+ @bit_analytics.mark_event('active', 123, now: now)
56
+ @bit_analytics.mark_event('active', 23232, now: yesterday)
57
+
58
+ assert @bit_analytics.day_events('active',
59
+ now.year,
60
+ now.month,
61
+ now.day).get_count == 1
62
+
63
+ assert @bit_analytics.day_events('active',
64
+ yesterday.year,
65
+ yesterday.month,
66
+ yesterday.day).get_count == 1
67
+ end
68
+
69
+ def test_different_buckets
70
+ @bit_analytics.delete_all_events
71
+
72
+ now = Time.now.getutc
73
+
74
+ @bit_analytics.mark_event('active', 123)
75
+ @bit_analytics.mark_event('tasks:completed', 23232)
76
+
77
+ assert @bit_analytics.month_events('active', now.year, now.month).get_count == 1
78
+ assert @bit_analytics.month_events('tasks:completed', now.year, now.month).get_count == 1
79
+ end
80
+
81
+ def test_bit_operations
82
+ @bit_analytics.delete_all_events
83
+
84
+ now = Time.now.getutc
85
+ last_month = (Date.today << 1).to_time
86
+
87
+ # 123 has been active for two months
88
+ @bit_analytics.mark_event('active', 123, now: now)
89
+ @bit_analytics.mark_event('active', 123, now: last_month)
90
+
91
+ # 224 has only been active last_month
92
+ @bit_analytics.mark_event('active', 224, now: last_month)
93
+
94
+ # Assert basic premises
95
+ assert @bit_analytics.month_events('active', last_month.year, last_month.month).get_count == 2
96
+ assert @bit_analytics.month_events('active', now.year, now.month).get_count == 1
97
+
98
+ # Try out with bit AND operation
99
+ active_2_months = @bit_analytics.bit_op_and(
100
+ @bit_analytics.month_events('active', last_month.year, last_month.month),
101
+ @bit_analytics.month_events('active', now.year, now.month)
102
+ )
103
+ assert active_2_months.get_count == 1
104
+ assert active_2_months.includes?(123)
105
+ assert !(active_2_months).includes?(224)
106
+ active_2_months.delete
107
+
108
+ # Try out with bit OR operation
109
+ assert @bit_analytics.bit_op_or(
110
+ @bit_analytics.month_events('active', last_month.year, last_month.month),
111
+ @bit_analytics.month_events('active', now.year, now.month)
112
+ ).get_count == 2
113
+
114
+ # Try nested operations
115
+ active_2_months = @bit_analytics.bit_op_and(
116
+ @bit_analytics.bit_op_and(
117
+ @bit_analytics.month_events('active', last_month.year, last_month.month),
118
+ @bit_analytics.month_events('active', now.year, now.month)
119
+ ),
120
+ @bit_analytics.month_events('active', now.year, now.month)
121
+ )
122
+
123
+ assert active_2_months.includes?(123)
124
+ assert !active_2_months.includes?(224)
125
+ active_2_months.delete
126
+ end
127
+
128
+ def test_events_marked
129
+ @bit_analytics.delete_all_events
130
+
131
+ now = Time.now.getutc
132
+
133
+ assert @bit_analytics.month_events('active', now.year, now.month).get_count == 0
134
+ assert @bit_analytics.month_events('active', now.year, now.month).has_events_marked == false
135
+
136
+ @bit_analytics.mark_event('active', 123, now: now)
137
+
138
+ assert @bit_analytics.month_events('active', now.year, now.month).get_count == 1
139
+ assert @bit_analytics.month_events('active', now.year, now.month).has_events_marked == true
140
+ end
141
+ end
142
+
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bit_analytics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jure Triglav
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-18 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'
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'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Analytics library built with Redis bitmaps
70
+ email:
71
+ - juretriglav@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - bit_analytics.gemspec
82
+ - lib/bit_analytics.rb
83
+ - lib/bit_analytics/version.rb
84
+ - test/bit_analytics_test.rb
85
+ homepage: http://www.github.com/jure/bit_analytics
86
+ licenses:
87
+ - BSD
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.0.14
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Implements a powerful analytics library on top of Redis's support for bitmaps
109
+ and bitmap operations.
110
+ test_files:
111
+ - test/bit_analytics_test.rb