boffin 0.3.0 → 1.0.0
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 +7 -0
- data/CHANGELOG.md +20 -6
- data/Gemfile +1 -3
- data/README.md +28 -24
- data/boffin.gemspec +23 -20
- data/lib/boffin/hit.rb +14 -22
- data/lib/boffin/trackable.rb +3 -15
- data/lib/boffin/tracker.rb +32 -42
- data/lib/boffin/utils.rb +7 -7
- data/lib/boffin/version.rb +1 -1
- data/spec/boffin/hit_spec.rb +5 -5
- data/spec/boffin/trackable_spec.rb +6 -6
- data/spec/boffin/tracker_spec.rb +16 -18
- data/spec/boffin/utils_spec.rb +8 -8
- metadata +76 -48
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1390bed0369dd43eb7dbe07d1ecf8742ffdaf134
|
4
|
+
data.tar.gz: 2cdd53fe1d9598b296302340dc4bebe0f016f450
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c9f6ca3b08a95c6ff48915a9d68c94b405769fe0767f7ed4eef42e0cb6cdc2e4d5ccfe61f8cf58c68c1e20380d2c24400bbdb4c7249720bb32498ee7049cc401
|
7
|
+
data.tar.gz: becf97f999b9050ede5bf07f85329440ffa4ce5d244fa595f0f60543142c427da6689fd0af79d83c9124bbfa42d74db9a73f09fcdf5f50d65d5761e4d6f0f90b
|
data/CHANGELOG.md
CHANGED
@@ -1,15 +1,29 @@
|
|
1
|
-
|
1
|
+
1.0.0
|
2
|
+
|
3
|
+
* Support for redis-rb 3.0, no backwards compatibility is maintained
|
4
|
+
* Ruby 2.0 is now officially supported, 1.9.3, and 1.8.7 are still supported
|
5
|
+
* Removed `Tracker#uhit_count` in favour of `Tracker#count(..., unique: true)`
|
6
|
+
* Renamed `Utils#object_as_session_identifier` to `Utils#object_as_uid`
|
7
|
+
* Renamed `Tracker#hit_count` to `Tracker#count`
|
8
|
+
* Renamed `Utils#uniquenesses_as_session_identifier` to `Utils#uniquenesses_as_uid`
|
9
|
+
* Changed `Utils#uniquenesses_as_uid` to splat arguments, this allows for uses
|
10
|
+
like `Tracker.hit(:thing, unique: current_user)` instead of
|
11
|
+
`Tracker.hit(:thing, unique: [current_user])`
|
12
|
+
* Removed `Tracker#hit_count_for_session_id` in favour of
|
13
|
+
`Tracker#count(..., unique: unique_object)`
|
14
|
+
|
15
|
+
0.3.0
|
2
16
|
|
3
17
|
* `Hit` can now accept a custom increment, this allows values such as cents to
|
4
|
-
be tracked
|
18
|
+
be tracked (Justin Giancola)
|
5
19
|
* Unique qualities are now passed in as a member in an options hash for
|
6
20
|
`Tracker#hit`, this deprecates the unique qualities argument and allows for
|
7
|
-
other options such as `:increment` to be passed as well
|
21
|
+
other options such as `:increment` to be passed as well (Justin Giancola)
|
8
22
|
|
9
|
-
|
23
|
+
0.2.0
|
10
24
|
|
11
|
-
* Support for Ruby 1.8.7
|
25
|
+
* Support for Ruby 1.8.7 (Justin Giancola)
|
12
26
|
|
13
|
-
|
27
|
+
0.1.0
|
14
28
|
|
15
29
|
* Initial public release
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -36,14 +36,6 @@ probably that of a Rails or Sinatra application. Just add `boffin` to your
|
|
36
36
|
gem 'boffin'
|
37
37
|
```
|
38
38
|
|
39
|
-
For utmost performance on *nix-based systems, require
|
40
|
-
[hiredis](https://github.com/pietern/hiredis-rb) before you require Boffin:
|
41
|
-
|
42
|
-
```ruby
|
43
|
-
gem 'hiredis'
|
44
|
-
gem 'boffin'
|
45
|
-
```
|
46
|
-
|
47
39
|
Configuration
|
48
40
|
-------------
|
49
41
|
|
@@ -118,7 +110,7 @@ Now to track hits on instances of the Listing model, simply:
|
|
118
110
|
get '/listings/:id' do
|
119
111
|
@listing = Listing[params[:id]]
|
120
112
|
@listing.hit(:views)
|
121
|
-
|
113
|
+
erb :'listings/show'
|
122
114
|
end
|
123
115
|
```
|
124
116
|
|
@@ -129,13 +121,13 @@ identify hits from particular users or sessions:
|
|
129
121
|
get '/listings/:id' do
|
130
122
|
@listing = Listing[params[:id]]
|
131
123
|
@listing.hit(:views, unique: [current_user, session[:id]])
|
132
|
-
|
124
|
+
erb :'listings/show'
|
133
125
|
end
|
134
126
|
```
|
135
127
|
|
136
128
|
Boffin now adds uniqueness to the hit in the form of `current_user.id` if
|
137
129
|
available. If `current_user` is nil, Boffin then uses `session[:id]`. You can
|
138
|
-
provide as many
|
130
|
+
provide as many unique factors as you'd like, the first one that is not blank
|
139
131
|
(`nil`, `false`, `[]`, `{}`, or `''`) will be used.
|
140
132
|
|
141
133
|
It could get a bit tedious having to add `[current_user, session[:id]]` whenever
|
@@ -167,7 +159,7 @@ You get the idea, now storing a hit is as easy as:
|
|
167
159
|
get '/listings/:id' do
|
168
160
|
@listing = Listing[params[:id]]
|
169
161
|
hit @listing, :views
|
170
|
-
|
162
|
+
erb :'listings/show'
|
171
163
|
end
|
172
164
|
```
|
173
165
|
|
@@ -182,10 +174,16 @@ After some hits have been tracked, you can start to do some queries:
|
|
182
174
|
@listing.hit_count(:views)
|
183
175
|
```
|
184
176
|
|
185
|
-
**Get count of unique views for an instance**
|
177
|
+
**Get count of all unique views for an instance**
|
186
178
|
|
187
179
|
```ruby
|
188
|
-
@listing.
|
180
|
+
@listing.hit_count(:views, unique: true)
|
181
|
+
```
|
182
|
+
|
183
|
+
**Get count of unique views for a specific user**
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
@listing.hit_count(:views, unique: current_user)
|
189
187
|
```
|
190
188
|
|
191
189
|
**Get IDs of the most viewed listings in the past 5 days**
|
@@ -197,7 +195,7 @@ Listing.top_ids(:views, days: 5)
|
|
197
195
|
**Get IDs of the least viewed listings (that were viewed) in the past 8 hours**
|
198
196
|
|
199
197
|
```ruby
|
200
|
-
Listing.top_ids(:views, hours: 8, order:
|
198
|
+
Listing.top_ids(:views, hours: 8, order: :asc)
|
201
199
|
```
|
202
200
|
|
203
201
|
**Get IDs and hit counts of the most liked listings in the past 5 days**
|
@@ -238,12 +236,12 @@ track your friends' favourite and least favourite colours:
|
|
238
236
|
```ruby
|
239
237
|
@tracker = Boffin::Tracker.new(:colours, [:faves, :unfaves])
|
240
238
|
|
241
|
-
@tracker.hit(:faves, 'red', unique:
|
242
|
-
@tracker.hit(:unfaves, 'blue', unique:
|
243
|
-
@tracker.hit(:faves, 'green', unique:
|
244
|
-
@tracker.hit(:unfaves, 'red', unique:
|
245
|
-
@tracker.hit(:faves, 'green', unique:
|
246
|
-
@tracker.hit(:unfaves, 'yellow', unique:
|
239
|
+
@tracker.hit(:faves, 'red', unique: 'lena')
|
240
|
+
@tracker.hit(:unfaves, 'blue', unique: 'lena')
|
241
|
+
@tracker.hit(:faves, 'green', unique: 'soren')
|
242
|
+
@tracker.hit(:unfaves, 'red', unique: 'soren')
|
243
|
+
@tracker.hit(:faves, 'green', unique: 'jens')
|
244
|
+
@tracker.hit(:unfaves, 'yellow', unique: 'jens')
|
247
245
|
|
248
246
|
@tracker.top(:faves, days: 1)
|
249
247
|
```
|
@@ -257,7 +255,7 @@ WordsTracker = Boffin::Tracker.new(:words, [:searches, :tweets])
|
|
257
255
|
get '/search' do
|
258
256
|
@tweets = Tweet.search(params[:q])
|
259
257
|
params[:q].split.each { |word| WordsTracker.hit(:searches, word) }
|
260
|
-
|
258
|
+
erb :'search/show'
|
261
259
|
end
|
262
260
|
|
263
261
|
post '/tweets' do
|
@@ -266,13 +264,13 @@ post '/tweets' do
|
|
266
264
|
@tweet.words.each { |word| WordsTracker.hit(:tweets, word) }
|
267
265
|
redirect to("/tweets/#{@tweet.id}")
|
268
266
|
else
|
269
|
-
|
267
|
+
erb :'tweets/form'
|
270
268
|
end
|
271
269
|
end
|
272
270
|
|
273
271
|
get '/trends' do
|
274
272
|
@words = WordsTracker.top({ tweets: 3, searches: 1 }, hours: 5)
|
275
|
-
|
273
|
+
erb :'trends/index'
|
276
274
|
end
|
277
275
|
```
|
278
276
|
_*This is a joke._
|
@@ -327,6 +325,12 @@ The Future™
|
|
327
325
|
FAQ
|
328
326
|
---
|
329
327
|
|
328
|
+
### OMG haven't you heard of page caching?! How am I supposed to use this if my Ruby app doesn't get hit?
|
329
|
+
|
330
|
+
Make an XHR request to an endpoint which is soley responsible for tracking hits
|
331
|
+
to stuff whenever a specific thing loads. My above examples are just to
|
332
|
+
demonstrate how to use the APIs, you can use them wherever you want.
|
333
|
+
|
330
334
|
### What's with the name?
|
331
335
|
|
332
336
|
Well, it means [this](http://en.wikipedia.org/wiki/Boffin). For the purposes of
|
data/boffin.gemspec
CHANGED
@@ -1,25 +1,16 @@
|
|
1
|
-
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'boffin/version'
|
2
4
|
|
3
5
|
Gem::Specification.new do |s|
|
4
|
-
s.name
|
5
|
-
s.version
|
6
|
-
s.
|
7
|
-
s.
|
8
|
-
s.
|
9
|
-
s.
|
10
|
-
s.
|
11
|
-
s.
|
12
|
-
s.has_rdoc = 'yard'
|
13
|
-
s.rubyforge_project = 'boffin'
|
14
|
-
s.files = `git ls-files`.split(/\n/)
|
15
|
-
s.test_files = `git ls-files -- spec/*`.split(/\n/)
|
16
|
-
s.require_paths = ['lib']
|
17
|
-
|
18
|
-
s.add_dependency 'redis', '>= 2.2'
|
19
|
-
s.add_development_dependency 'rspec', '~> 2.6'
|
20
|
-
s.add_development_dependency 'timecop'
|
21
|
-
s.add_development_dependency 'bundler', '>= 1.0.14'
|
22
|
-
|
6
|
+
s.name = 'boffin'
|
7
|
+
s.version = Boffin::VERSION
|
8
|
+
s.homepage = 'http://github.com/heycarsten/boffin'
|
9
|
+
s.authors = ['Carsten Nielsen']
|
10
|
+
s.email = ['heycarsten@gmail.com']
|
11
|
+
s.summary = 'Hit tracking library for Ruby using Redis'
|
12
|
+
s.has_rdoc = 'yard'
|
13
|
+
s.license = 'MIT'
|
23
14
|
s.description = <<-END
|
24
15
|
Boffin is a library for tracking hits to things in your Ruby application. Things
|
25
16
|
can be IDs of records in a database, strings representing tags or topics, URLs
|
@@ -27,4 +18,16 @@ of webpages, names of places, whatever you desire. Boffin is able to provide
|
|
27
18
|
lists of those things based on most hits, least hits, it can even report on
|
28
19
|
weighted combinations of different types of hits.
|
29
20
|
END
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split($/)
|
23
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
24
|
+
s.require_paths = ['lib']
|
25
|
+
|
26
|
+
s.add_development_dependency 'bundler', '~> 1.3'
|
27
|
+
s.add_development_dependency 'rake'
|
28
|
+
s.add_development_dependency 'yard'
|
29
|
+
s.add_development_dependency 'rspec', '~> 2.13'
|
30
|
+
s.add_development_dependency 'timecop'
|
31
|
+
|
32
|
+
s.add_dependency 'redis', '~> 3.0'
|
30
33
|
end
|
data/lib/boffin/hit.rb
CHANGED
@@ -12,27 +12,19 @@ module Boffin
|
|
12
12
|
# @param [Object] instance
|
13
13
|
# The instance that is being hit, any object that responds to
|
14
14
|
# `#to_member`, `#id`, or `#to_s`
|
15
|
-
# @param [Hash]
|
16
|
-
# @option
|
15
|
+
# @param [Hash] opts
|
16
|
+
# @option opts [Array] :unique ([]) An array of which the first
|
17
17
|
# object is used to generate a session identifier for hit uniqueness
|
18
|
-
# @option
|
18
|
+
# @option opts [Fixnum] :increment (1) The hit increment
|
19
19
|
def initialize(tracker, type, instance, opts = {})
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@increment = opts.delete(:increment) || 1
|
29
|
-
end
|
30
|
-
@now = Time.now
|
31
|
-
@sessid = Utils.uniquenesses_as_session_identifier(uniquenesses)
|
32
|
-
@type = type
|
33
|
-
@tracker = tracker
|
34
|
-
@instance = instance
|
35
|
-
@member = Utils.object_as_member(@instance)
|
20
|
+
uniquenesses = opts.delete(:unique) || []
|
21
|
+
@increment = opts.delete(:increment) || 1
|
22
|
+
@now = Time.now
|
23
|
+
@sessid = Utils.uniquenesses_as_uid(uniquenesses)
|
24
|
+
@type = type
|
25
|
+
@tracker = tracker
|
26
|
+
@instance = instance
|
27
|
+
@member = Utils.object_as_member(@instance)
|
36
28
|
store
|
37
29
|
freeze
|
38
30
|
end
|
@@ -65,11 +57,11 @@ module Boffin
|
|
65
57
|
# `true` if this hit is unique, `false` if it has been made before by the
|
66
58
|
# same session identifer.
|
67
59
|
def track_hit
|
68
|
-
redis.
|
69
|
-
redis.zincrby(keyspace.hits(@type, @instance), 1, @sessid) ==
|
60
|
+
redis.incrbyfloat(keyspace.hit_count(@type, @instance), @increment)
|
61
|
+
redis.zincrby(keyspace.hits(@type, @instance), 1, @sessid) == 1.0
|
70
62
|
end
|
71
63
|
|
72
|
-
# Store the hit member across all time
|
64
|
+
# Store the hit member across all time intervals for the current window
|
73
65
|
# @param [true, false] uniq
|
74
66
|
# If `true` the hit is also added to the keys scoped for unique hits
|
75
67
|
def set_windows(uniq)
|
data/lib/boffin/trackable.rb
CHANGED
@@ -45,21 +45,9 @@ module Boffin
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# @see Tracker#hit_count
|
48
|
-
# @return [
|
49
|
-
def hit_count(type)
|
50
|
-
self.class.boffin.
|
51
|
-
end
|
52
|
-
|
53
|
-
# @see Tracker#uhit_count
|
54
|
-
# @return [Fixnum]
|
55
|
-
def uhit_count(type)
|
56
|
-
self.class.boffin.uhit_count(type, self)
|
57
|
-
end
|
58
|
-
|
59
|
-
# @see Tracker#hit_count_for_session_id
|
60
|
-
# @return [Fixnum]
|
61
|
-
def hit_count_for_session_id(type, sess_obj)
|
62
|
-
self.class.boffin.hit_count_for_session_id(type, self, sess_obj)
|
48
|
+
# @return [Float]
|
49
|
+
def hit_count(type, opts = {})
|
50
|
+
self.class.boffin.count(type, self, opts)
|
63
51
|
end
|
64
52
|
|
65
53
|
end
|
data/lib/boffin/tracker.rb
CHANGED
@@ -25,9 +25,9 @@ module Boffin
|
|
25
25
|
|
26
26
|
# @param [Symbol] hit_type
|
27
27
|
# @param [#as_member, #id, #to_s] instance
|
28
|
-
# @param [Hash]
|
29
|
-
# @option
|
30
|
-
# @option
|
28
|
+
# @param [Hash] opts
|
29
|
+
# @option opts [Array] :unique ([]) uniquenesses
|
30
|
+
# @option opts [Fixnum] :increment (1) hit increment
|
31
31
|
# @return [Hit]
|
32
32
|
# @raise Boffin::UndefinedHitTypeError
|
33
33
|
# Raised if a list of hit types is available and the provided hit type is
|
@@ -38,45 +38,37 @@ module Boffin
|
|
38
38
|
end
|
39
39
|
|
40
40
|
# @param [Symbol] hit_type
|
41
|
+
# Type of hit.
|
41
42
|
# @param [#as_member, #id, #to_s] instance
|
42
|
-
#
|
43
|
-
# @
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
# @param [Symbol] hit_type
|
52
|
-
# @param [#as_member, #id, #to_s] instance
|
53
|
-
# @return [Fixnum]
|
54
|
-
# @raise Boffin::UndefinedHitTypeError
|
55
|
-
# Raised if a list of hit types is available and the provided hit type is
|
56
|
-
# not in the list.
|
57
|
-
def uhit_count(hit_type, instance)
|
58
|
-
validate_hit_type(hit_type)
|
59
|
-
redis.zcard(keyspace.hits(hit_type, instance)).to_i
|
60
|
-
end
|
61
|
-
|
62
|
-
# @param [Symbol] hit_type
|
63
|
-
# @param [#as_member, #id, #to_s] instance
|
64
|
-
# @param [#as_member, #id, #to_s] sess_obj
|
65
|
-
# @return [Fixnum]
|
43
|
+
# Object to track.
|
44
|
+
# @param [Hash] opts
|
45
|
+
# @option opts [true, #as_member, #id, #to_s] :unique (false)
|
46
|
+
# If `true` will return a count of unique hits. If passed an object, will
|
47
|
+
# use that object as a unique identifier and return the score associated
|
48
|
+
# with it.
|
49
|
+
# @return [Float]
|
66
50
|
# @raise Boffin::UndefinedHitTypeError
|
67
51
|
# Raised if a list of hit types is available and the provided hit type is
|
68
52
|
# not in the list.
|
69
|
-
def
|
53
|
+
def count(hit_type, instance, opts = {})
|
70
54
|
validate_hit_type(hit_type)
|
71
|
-
|
72
|
-
|
55
|
+
count = case
|
56
|
+
when opts[:unique] == true
|
57
|
+
redis.zcard(keyspace.hits(hit_type, instance))
|
58
|
+
when opts[:unique]
|
59
|
+
uid = Utils.object_as_uid(opts[:unique])
|
60
|
+
redis.zscore(keyspace.hits(hit_type, instance), uid)
|
61
|
+
else
|
62
|
+
redis.get(keyspace.hit_count(hit_type, instance))
|
63
|
+
end
|
64
|
+
(count && count.to_f) || 0.0
|
73
65
|
end
|
74
66
|
|
75
67
|
# Performs set union across the specified number of hours, days, or months
|
76
68
|
# to calculate the members with the highest hit counts. The operation can
|
77
69
|
# be performed on one hit type, or multiple hit types with weights.
|
78
70
|
# @param [Symbol, Hash] type_or_weights
|
79
|
-
# When Hash the set union is calculated
|
71
|
+
# When Hash the set union is calculated
|
80
72
|
# @param [Hash] opts
|
81
73
|
# @option opts [true, false] :unique (false)
|
82
74
|
# If `true` then only unique hits are considered in the calculation
|
@@ -169,7 +161,6 @@ module Boffin
|
|
169
161
|
# @param [Keyspace] ks
|
170
162
|
# Keyspace to perform the union on
|
171
163
|
# @param [Hash] weights
|
172
|
-
# @param [Symbol] hit_type
|
173
164
|
# @param [:hours, :days, :months] unit
|
174
165
|
# @param [Fixnum] size
|
175
166
|
# Number of intervals to include in the union
|
@@ -200,7 +191,7 @@ module Boffin
|
|
200
191
|
zrangeopts = {
|
201
192
|
:counts => opts.delete(:counts),
|
202
193
|
:order => (opts.delete(:order) || :desc).to_sym }
|
203
|
-
if redis.zcard(storkey)
|
194
|
+
if redis.zcard(storkey) < 1
|
204
195
|
redis.zunionstore(storkey, keys, opts)
|
205
196
|
redis.expire(storkey, @config.cache_expire_secs)
|
206
197
|
end
|
@@ -217,17 +208,16 @@ module Boffin
|
|
217
208
|
# option is `true` it returns an array of pairs where the first value is
|
218
209
|
# the member, and the second value is the member's score.
|
219
210
|
def zrange(key, opts)
|
220
|
-
args = [key, 0, -1
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
211
|
+
args = [key, 0, -1]
|
212
|
+
args << { :with_scores => true } if opts[:counts]
|
213
|
+
case opts[:order]
|
214
|
+
when :asc
|
215
|
+
redis.zrange(*args)
|
216
|
+
when :desc
|
217
|
+
redis.zrevrange(*args)
|
227
218
|
else
|
228
|
-
|
219
|
+
raise ArgumentError, "unknown order type: #{opts[:order].inspect}"
|
229
220
|
end
|
230
221
|
end
|
231
|
-
|
232
222
|
end
|
233
223
|
end
|
data/lib/boffin/utils.rb
CHANGED
@@ -114,16 +114,16 @@ module Boffin
|
|
114
114
|
times
|
115
115
|
end
|
116
116
|
|
117
|
-
# Generates a set member based off the first object in the provided
|
118
|
-
# that is not `nil`. If the array is empty or only contains `nil`
|
119
|
-
# then {Boffin::NIL_SESSION_MEMBER} is returned.
|
117
|
+
# Generates a unique set member based off the first object in the provided
|
118
|
+
# array that is not `nil`. If the array is empty or only contains `nil`
|
119
|
+
# element then {Boffin::NIL_SESSION_MEMBER} is returned.
|
120
120
|
# @param [Array] aspects
|
121
121
|
# An array of which the first non-nil element is passed to
|
122
|
-
# {#
|
122
|
+
# {#object_as_uid}
|
123
123
|
# @return [String]
|
124
|
-
def
|
124
|
+
def uniquenesses_as_uid(*aspects)
|
125
125
|
if (obj = aspects.flatten.reject { |u| blank?(u) }.first)
|
126
|
-
|
126
|
+
object_as_uid(obj)
|
127
127
|
else
|
128
128
|
NIL_SESSION_MEMBER
|
129
129
|
end
|
@@ -172,7 +172,7 @@ module Boffin
|
|
172
172
|
# @param [#as_member, #id, #to_s] obj
|
173
173
|
# @return [String] A string that can be used as a member in {Keyspace#hits}.
|
174
174
|
# @see #object_as_identifier
|
175
|
-
def
|
175
|
+
def object_as_uid(obj)
|
176
176
|
object_as_identifier(obj, :namespace => true)
|
177
177
|
end
|
178
178
|
|
data/lib/boffin/version.rb
CHANGED
data/spec/boffin/hit_spec.rb
CHANGED
@@ -21,8 +21,8 @@ describe Boffin::Hit, '::new' do
|
|
21
21
|
@tracker.top(:tests, interval => 1, :counts => true, :unique => true).
|
22
22
|
should == [['1', 1]]
|
23
23
|
end
|
24
|
-
@tracker.
|
25
|
-
@tracker.
|
24
|
+
@tracker.count(:tests, @ditty).should == 1
|
25
|
+
@tracker.count(:tests, @ditty, :unique => true).should == 1
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'does not store data under unique keys if the hit is not unique' do
|
@@ -34,9 +34,9 @@ describe Boffin::Hit, '::new' do
|
|
34
34
|
@tracker.top(:tests, interval => 1, :counts => true, :unique => true).
|
35
35
|
should == [['1', 1]]
|
36
36
|
end
|
37
|
-
@tracker.
|
38
|
-
@tracker.
|
39
|
-
@tracker.
|
37
|
+
@tracker.count(:tests, @ditty, :unique => @user).should == 2
|
38
|
+
@tracker.count(:tests, @ditty).should == 2
|
39
|
+
@tracker.count(:tests, @ditty, :unique => true).should == 1
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'allows arbitrary hit increments' do
|
@@ -4,8 +4,8 @@ describe Boffin::Trackable do
|
|
4
4
|
before :all do
|
5
5
|
SpecHelper.flush_keyspace!
|
6
6
|
@mock = MockTrackableInjected.new(1)
|
7
|
-
@mock.hit(:views, :unique =>
|
8
|
-
@mock.hit(:views, :unique =>
|
7
|
+
@mock.hit(:views, :unique => 'sess.1')
|
8
|
+
@mock.hit(:views, :unique => 'sess.1')
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'can be included' do
|
@@ -28,11 +28,11 @@ describe Boffin::Trackable do
|
|
28
28
|
@mock.hit_count(:views).should == 2
|
29
29
|
end
|
30
30
|
|
31
|
-
it 'delegates #
|
32
|
-
@mock.
|
31
|
+
it 'delegates #hit_count { unique: true } to the Tracker instance' do
|
32
|
+
@mock.hit_count(:views, :unique => true).should == 1
|
33
33
|
end
|
34
34
|
|
35
|
-
it 'delegates #
|
36
|
-
@mock.
|
35
|
+
it 'delegates #hit_count { unique: obj } to the Tracker instance' do
|
36
|
+
@mock.hit_count(:views, :unique => 'sess.1').should == 2
|
37
37
|
end
|
38
38
|
end
|
data/spec/boffin/tracker_spec.rb
CHANGED
@@ -55,64 +55,57 @@ describe Boffin::Tracker do
|
|
55
55
|
|
56
56
|
describe '#hit' do
|
57
57
|
it 'throws an error if the hit type is not in the list' do
|
58
|
-
lambda
|
58
|
+
lambda { @tracker.hit(:view, @instance1) }.
|
59
59
|
should raise_error Boffin::UndefinedHitTypeError
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
describe '#hit_count' do
|
64
64
|
it 'throws an error if the hit type is not in the list' do
|
65
|
-
lambda
|
65
|
+
lambda { @tracker.count(:view, @instance1) }.
|
66
66
|
should raise_error Boffin::UndefinedHitTypeError
|
67
67
|
end
|
68
68
|
|
69
69
|
it 'returns the raw hit count for the instance' do
|
70
|
-
@tracker.
|
70
|
+
@tracker.count(:views, @instance1).should == 8
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'returns 0 for an instance that was never hit' do
|
74
|
-
@tracker.
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
describe '#uhit_count' do
|
79
|
-
it 'throws an error if the hit type is not in the list' do
|
80
|
-
lambda { @tracker.uhit_count(:view, @instance1) }.
|
81
|
-
should raise_error Boffin::UndefinedHitTypeError
|
74
|
+
@tracker.count(:views, 'neverhit').should == 0
|
82
75
|
end
|
83
76
|
|
84
77
|
it 'returns the unique hit count for the instance' do
|
85
|
-
@tracker.
|
78
|
+
@tracker.count(:views, @instance1, :unique => true).should == 5
|
86
79
|
end
|
87
80
|
|
88
81
|
it 'returns 0 for an instance that was never hit' do
|
89
|
-
@tracker.
|
82
|
+
@tracker.count(:likes, @instance4, :unique => true).should == 0
|
90
83
|
end
|
91
84
|
end
|
92
85
|
|
93
86
|
describe '#hit_count_for_session_id' do
|
94
87
|
it 'throws an error if the hit type is not in the list' do
|
95
|
-
lambda
|
88
|
+
lambda { @tracker.count(:view, @instance1, :unique => 'sess.1') }.
|
96
89
|
should raise_error Boffin::UndefinedHitTypeError
|
97
90
|
end
|
98
91
|
|
99
92
|
it 'returns the number of times the instance was hit by the session id' do
|
100
|
-
@tracker.
|
93
|
+
@tracker.count(:views, @instance3, :unique => 'sess.1').should == 3
|
101
94
|
end
|
102
95
|
|
103
96
|
it 'returns a count of 0 if the session id never hit the instance' do
|
104
|
-
@tracker.
|
97
|
+
@tracker.count(:views, @instance1, :unique => 'nohit').should == 0
|
105
98
|
end
|
106
99
|
end
|
107
100
|
|
108
101
|
describe '#top' do
|
109
102
|
it 'throws an error if passed hit type is invalid' do
|
110
|
-
lambda
|
103
|
+
lambda { @tracker.top(:view, :days => 3) }.
|
111
104
|
should raise_error Boffin::UndefinedHitTypeError
|
112
105
|
end
|
113
106
|
|
114
107
|
it 'throws an error if passed weights with hit type that is invalid' do
|
115
|
-
lambda
|
108
|
+
lambda { @tracker.top({ :view => 1 }, :days => 3) }.
|
116
109
|
should raise_error Boffin::UndefinedHitTypeError
|
117
110
|
end
|
118
111
|
|
@@ -131,6 +124,11 @@ describe Boffin::Tracker do
|
|
131
124
|
ids.should == ['200', '100', '300']
|
132
125
|
end
|
133
126
|
|
127
|
+
it 'throws an error if the specified order is not valid' do
|
128
|
+
lambda { @tracker.top(:views, :days => 3, :order => 'desk') }.
|
129
|
+
should raise_error ArgumentError
|
130
|
+
end
|
131
|
+
|
134
132
|
it 'returns ids and counts when passed { counts: true } as an option' do
|
135
133
|
ids = @tracker.top(:views, :days => 3, :counts => true)
|
136
134
|
ids.should == [
|
data/spec/boffin/utils_spec.rb
CHANGED
@@ -99,29 +99,29 @@ describe Boffin::Utils do
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
-
describe '::
|
102
|
+
describe '::uniquenesses_as_uid' do
|
103
103
|
specify do
|
104
|
-
subject.
|
104
|
+
subject.uniquenesses_as_uid([]).
|
105
105
|
should == Boffin::NIL_SESSION_MEMBER
|
106
106
|
end
|
107
107
|
|
108
108
|
specify do
|
109
|
-
subject.
|
109
|
+
subject.uniquenesses_as_uid([nil, 'hi']).
|
110
110
|
should == 'hi'
|
111
111
|
end
|
112
112
|
|
113
113
|
specify do
|
114
|
-
subject.
|
114
|
+
subject.uniquenesses_as_uid([MockDitty.new]).
|
115
115
|
should == 'mock_ditty:1'
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
-
describe '::
|
120
|
-
specify { subject.
|
121
|
-
specify { subject.
|
119
|
+
describe '::object_as_uid' do
|
120
|
+
specify { subject.object_as_uid(nil).should == '' }
|
121
|
+
specify { subject.object_as_uid(3.14).should == '3.14' }
|
122
122
|
|
123
123
|
specify do
|
124
|
-
subject.
|
124
|
+
subject.object_as_uid(MockDitty.new).
|
125
125
|
should == 'mock_ditty:1'
|
126
126
|
end
|
127
127
|
end
|
metadata
CHANGED
@@ -1,73 +1,107 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boffin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Carsten Nielsen
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-05-08 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement:
|
17
|
-
none: false
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
22
|
-
type: :
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: yard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
23
49
|
prerelease: false
|
24
|
-
version_requirements:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
25
55
|
- !ruby/object:Gem::Dependency
|
26
56
|
name: rspec
|
27
|
-
requirement:
|
28
|
-
none: false
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
29
58
|
requirements:
|
30
59
|
- - ~>
|
31
60
|
- !ruby/object:Gem::Version
|
32
|
-
version: '2.
|
61
|
+
version: '2.13'
|
33
62
|
type: :development
|
34
63
|
prerelease: false
|
35
|
-
version_requirements:
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.13'
|
36
69
|
- !ruby/object:Gem::Dependency
|
37
70
|
name: timecop
|
38
|
-
requirement:
|
39
|
-
none: false
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
40
72
|
requirements:
|
41
|
-
- -
|
73
|
+
- - '>='
|
42
74
|
- !ruby/object:Gem::Version
|
43
75
|
version: '0'
|
44
76
|
type: :development
|
45
77
|
prerelease: false
|
46
|
-
version_requirements:
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
47
83
|
- !ruby/object:Gem::Dependency
|
48
|
-
name:
|
49
|
-
requirement:
|
50
|
-
none: false
|
84
|
+
name: redis
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
51
86
|
requirements:
|
52
|
-
- -
|
87
|
+
- - ~>
|
53
88
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
type: :
|
89
|
+
version: '3.0'
|
90
|
+
type: :runtime
|
56
91
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
|
59
|
-
|
60
|
-
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description: |
|
98
|
+
Boffin is a library for tracking hits to things in your Ruby application. Things
|
61
99
|
can be IDs of records in a database, strings representing tags or topics, URLs
|
62
|
-
|
63
100
|
of webpages, names of places, whatever you desire. Boffin is able to provide
|
64
|
-
|
65
101
|
lists of those things based on most hits, least hits, it can even report on
|
66
|
-
|
67
102
|
weighted combinations of different types of hits.
|
68
|
-
|
69
|
-
|
70
|
-
email: heycarsten@gmail.com
|
103
|
+
email:
|
104
|
+
- heycarsten@gmail.com
|
71
105
|
executables: []
|
72
106
|
extensions: []
|
73
107
|
extra_rdoc_files: []
|
@@ -98,34 +132,28 @@ files:
|
|
98
132
|
- spec/boffin_spec.rb
|
99
133
|
- spec/spec_helper.rb
|
100
134
|
homepage: http://github.com/heycarsten/boffin
|
101
|
-
licenses:
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
102
138
|
post_install_message:
|
103
139
|
rdoc_options: []
|
104
140
|
require_paths:
|
105
141
|
- lib
|
106
142
|
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
-
none: false
|
108
143
|
requirements:
|
109
|
-
- -
|
144
|
+
- - '>='
|
110
145
|
- !ruby/object:Gem::Version
|
111
146
|
version: '0'
|
112
|
-
segments:
|
113
|
-
- 0
|
114
|
-
hash: -2879744131493119363
|
115
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
-
none: false
|
117
148
|
requirements:
|
118
|
-
- -
|
149
|
+
- - '>='
|
119
150
|
- !ruby/object:Gem::Version
|
120
151
|
version: '0'
|
121
|
-
segments:
|
122
|
-
- 0
|
123
|
-
hash: -2879744131493119363
|
124
152
|
requirements: []
|
125
|
-
rubyforge_project:
|
126
|
-
rubygems_version:
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 2.0.3
|
127
155
|
signing_key:
|
128
|
-
specification_version:
|
156
|
+
specification_version: 4
|
129
157
|
summary: Hit tracking library for Ruby using Redis
|
130
158
|
test_files:
|
131
159
|
- spec/boffin/config_spec.rb
|