boffin 0.1.0 → 0.2.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.
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -1
- data/README.md +1 -0
- data/Rakefile +1 -1
- data/boffin.gemspec +3 -2
- data/lib/boffin.rb +3 -4
- data/lib/boffin/tracker.rb +6 -4
- data/lib/boffin/utils.rb +19 -7
- data/lib/boffin/version.rb +1 -1
- data/spec/boffin/config_spec.rb +2 -2
- data/spec/boffin/hit_spec.rb +4 -4
- data/spec/boffin/keyspace_spec.rb +7 -1
- data/spec/boffin/trackable_spec.rb +1 -1
- data/spec/boffin/tracker_spec.rb +12 -12
- data/spec/boffin/utils_spec.rb +14 -14
- data/spec/boffin_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- metadata +21 -10
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -15,6 +15,7 @@ weighted combinations of different types of hits.
|
|
15
15
|
Resources
|
16
16
|
---------
|
17
17
|
|
18
|
+
* [Documentation](http://rubydoc.info/github/heycarsten/boffin/master/frames)
|
18
19
|
* [Source Code](https://github.com/heycarsten/boffin)
|
19
20
|
* [Issue Tracker](https://github.com/heycarsten/boffin/issues)
|
20
21
|
* [Test Suite](https://github.com/heycarsten/boffin/tree/master/spec)
|
data/Rakefile
CHANGED
data/boffin.gemspec
CHANGED
@@ -11,13 +11,14 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.summary = 'Hit tracking library for Ruby using Redis'
|
12
12
|
s.has_rdoc = 'yard'
|
13
13
|
s.rubyforge_project = 'boffin'
|
14
|
-
s.files = `git ls-files`.split(
|
15
|
-
s.test_files = `git ls-files -- spec/*`.split(
|
14
|
+
s.files = `git ls-files`.split(/\n/)
|
15
|
+
s.test_files = `git ls-files -- spec/*`.split(/\n/)
|
16
16
|
s.require_paths = ['lib']
|
17
17
|
|
18
18
|
s.add_dependency 'redis', '>= 2.2'
|
19
19
|
s.add_development_dependency 'rspec', '~> 2.6'
|
20
20
|
s.add_development_dependency 'timecop'
|
21
|
+
s.add_development_dependency 'bundler', '>= 1.0.14'
|
21
22
|
|
22
23
|
s.description = <<-END
|
23
24
|
Boffin is a library for tracking hits to things in your Ruby application. Things
|
data/lib/boffin.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'base64'
|
2
1
|
require 'date'
|
3
2
|
require 'time'
|
4
3
|
require 'redis'
|
@@ -24,9 +23,9 @@ module Boffin
|
|
24
23
|
|
25
24
|
# The way Time should be formatted for each interval type
|
26
25
|
INTERVAL_FORMATS = {
|
27
|
-
hours
|
28
|
-
days
|
29
|
-
months
|
26
|
+
:hours => '%F-%H',
|
27
|
+
:days => '%F',
|
28
|
+
:months => '%Y-%m' }
|
30
29
|
|
31
30
|
# Different interval types
|
32
31
|
INTERVAL_TYPES = INTERVAL_FORMATS.keys
|
data/lib/boffin/tracker.rb
CHANGED
@@ -91,6 +91,8 @@ module Boffin
|
|
91
91
|
# Perform union for hit counts over the last _n_ months.
|
92
92
|
# @example Return IDs of most viewed and liked listings in the past 6 days with scores
|
93
93
|
# @tracker.top({ views: 1, likes: 1 }, counts: true, days: 6)
|
94
|
+
# @example Return IDS of most viewed and liked listings in the past 6 days with scores (Alternate syntax)
|
95
|
+
# @tracker.top([[:views, 1], [:likes, 1]], counts: true, days: 6)
|
94
96
|
# @example Return IDs of most viewed listings in the past 12 hours
|
95
97
|
# @tracker.top(:views, hours: 12)
|
96
98
|
# @note
|
@@ -178,7 +180,7 @@ module Boffin
|
|
178
180
|
weights.keys.each { |t| union(ks, t, unit, size, opts) }
|
179
181
|
keys = weights.keys.map { |t| ks.hits_union(t, unit, size) }
|
180
182
|
zfetch(ks.hits_union_multi(weights, unit, size), keys, {
|
181
|
-
weights
|
183
|
+
:weights => weights.values
|
182
184
|
}.merge(opts))
|
183
185
|
end
|
184
186
|
|
@@ -194,8 +196,8 @@ module Boffin
|
|
194
196
|
# @see #zrange
|
195
197
|
def zfetch(storkey, keys, opts = {})
|
196
198
|
zrangeopts = {
|
197
|
-
counts
|
198
|
-
order
|
199
|
+
:counts => opts.delete(:counts),
|
200
|
+
:order => (opts.delete(:order) || :desc).to_sym }
|
199
201
|
if redis.zcard(storkey) == 0
|
200
202
|
redis.zunionstore(storkey, keys, opts)
|
201
203
|
redis.expire(storkey, @config.cache_expire_secs)
|
@@ -213,7 +215,7 @@ module Boffin
|
|
213
215
|
# option is `true` it returns an array of pairs where the first value is
|
214
216
|
# the member, and the second value is the member's score.
|
215
217
|
def zrange(key, opts)
|
216
|
-
args = [key, 0, -1, opts[:counts] ? { withscores
|
218
|
+
args = [key, 0, -1, opts[:counts] ? { :withscores => true } : {}]
|
217
219
|
result = case opts[:order]
|
218
220
|
when :asc then redis.zrange(*args)
|
219
221
|
when :desc then redis.zrevrange(*args)
|
data/lib/boffin/utils.rb
CHANGED
@@ -13,9 +13,9 @@ module Boffin
|
|
13
13
|
|
14
14
|
# Number of seconds for a single value of each unit
|
15
15
|
SECONDS_IN_UNIT = {
|
16
|
-
hours
|
17
|
-
days
|
18
|
-
months
|
16
|
+
:hours => SECONDS_IN_HOUR,
|
17
|
+
:days => SECONDS_IN_DAY,
|
18
|
+
:months => SECONDS_IN_MONTH
|
19
19
|
}
|
20
20
|
|
21
21
|
module_function
|
@@ -45,6 +45,18 @@ module Boffin
|
|
45
45
|
obj.respond_to?(:empty?) ? obj.empty? : !obj
|
46
46
|
end
|
47
47
|
|
48
|
+
# @param [Object] obj any Ruby object
|
49
|
+
# @return [true, false]
|
50
|
+
# `true` if the provided object responds to :id, other than it's
|
51
|
+
# internal object identifier
|
52
|
+
# `false` if the object does not respond to :id
|
53
|
+
def respond_to_id?(obj)
|
54
|
+
# NOTE: this feels like a hack. I'm sure there is a more elegant way
|
55
|
+
# to determine whether the :id method is the built in Object#id but
|
56
|
+
# I can't think of it
|
57
|
+
obj.respond_to?(:id) and obj.id != obj.object_id
|
58
|
+
end
|
59
|
+
|
48
60
|
# Pulls time interval information from a hash of options.
|
49
61
|
# @example
|
50
62
|
# extract_time_unit(this: 'is ignored', days: 6, so_is: 'this')
|
@@ -134,13 +146,13 @@ module Boffin
|
|
134
146
|
# generated value will be Base64 encoded.
|
135
147
|
# @return [String]
|
136
148
|
def object_as_identifier(obj, opts = {})
|
137
|
-
if obj.respond_to?(:as_member) ||
|
149
|
+
if obj.respond_to?(:as_member) || respond_to_id?(obj)
|
138
150
|
''.tap do |s|
|
139
151
|
s << "#{underscore(obj.class)}:" if opts[:namespace]
|
140
152
|
s << (obj.respond_to?(:as_member) ? obj.as_member : obj.id).to_s
|
141
153
|
end
|
142
154
|
else
|
143
|
-
opts[:encode] ?
|
155
|
+
opts[:encode] ? [obj.to_s].pack("m0").chomp : obj.to_s
|
144
156
|
end
|
145
157
|
end
|
146
158
|
|
@@ -157,14 +169,14 @@ module Boffin
|
|
157
169
|
# @return [String] A string that can be used as a member in {Keyspace#hits}.
|
158
170
|
# @see #object_as_identifier
|
159
171
|
def object_as_session_identifier(obj)
|
160
|
-
object_as_identifier(obj, namespace
|
172
|
+
object_as_identifier(obj, :namespace => true)
|
161
173
|
end
|
162
174
|
|
163
175
|
# @param [#as_member, #id, #to_s] obj
|
164
176
|
# @return [String] A string that can be used as part of a Redis key
|
165
177
|
# @see #object_as_identifier
|
166
178
|
def object_as_key(obj)
|
167
|
-
object_as_identifier(obj, encode
|
179
|
+
object_as_identifier(obj, :encode => true)
|
168
180
|
end
|
169
181
|
|
170
182
|
end
|
data/lib/boffin/version.rb
CHANGED
data/spec/boffin/config_spec.rb
CHANGED
@@ -28,14 +28,14 @@ describe Boffin::Config do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'can be sent a hash' do
|
31
|
-
conf = Boffin::Config.new(namespace
|
31
|
+
conf = Boffin::Config.new(:namespace => 'hello')
|
32
32
|
conf.namespace.should == 'hello'
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
describe '#merge' do
|
37
37
|
it 'copies the existing instance' do
|
38
|
-
newconf = subject.merge(namespace
|
38
|
+
newconf = subject.merge(:namespace => 'carsten')
|
39
39
|
newconf.namespace.should == 'carsten'
|
40
40
|
subject.namespace.should == 'boffin'
|
41
41
|
end
|
data/spec/boffin/hit_spec.rb
CHANGED
@@ -16,9 +16,9 @@ describe Boffin::Hit, '::new' do
|
|
16
16
|
it 'stores hit data under the appropriate keys' do
|
17
17
|
Boffin::Hit.new(@tracker, :tests, @ditty, [nil, @user])
|
18
18
|
[:hours, :days, :months].each do |interval|
|
19
|
-
@tracker.top(:tests, interval => 1, counts
|
19
|
+
@tracker.top(:tests, interval => 1, :counts => true).
|
20
20
|
should == [['1', 1]]
|
21
|
-
@tracker.top(:tests, interval => 1, counts
|
21
|
+
@tracker.top(:tests, interval => 1, :counts => true, :unique => true).
|
22
22
|
should == [['1', 1]]
|
23
23
|
end
|
24
24
|
@tracker.hit_count(:tests, @ditty).should == 1
|
@@ -29,9 +29,9 @@ describe Boffin::Hit, '::new' do
|
|
29
29
|
Boffin::Hit.new(@tracker, :tests, @ditty, [nil, @user])
|
30
30
|
Boffin::Hit.new(@tracker, :tests, @ditty, [nil, @user])
|
31
31
|
[:hours, :days, :months].each do |interval|
|
32
|
-
@tracker.top(:tests, interval => 1, counts
|
32
|
+
@tracker.top(:tests, interval => 1, :counts => true).
|
33
33
|
should == [['1', 2]]
|
34
|
-
@tracker.top(:tests, interval => 1, counts
|
34
|
+
@tracker.top(:tests, interval => 1, :counts => true, :unique => true).
|
35
35
|
should == [['1', 1]]
|
36
36
|
end
|
37
37
|
@tracker.hit_count_for_session_id(:tests, @ditty, @user).should == 2
|
@@ -42,7 +42,13 @@ describe Boffin::Keyspace do
|
|
42
42
|
|
43
43
|
describe '#hits_union_multi' do
|
44
44
|
specify do
|
45
|
-
|
45
|
+
weighted_hit_types =
|
46
|
+
if RUBY_VERSION < '1.9'
|
47
|
+
[[:views, 1], [:likes, 3]]
|
48
|
+
else
|
49
|
+
{ :views => 1, :likes => 3 }
|
50
|
+
end
|
51
|
+
@ks.hits_union_multi(weighted_hit_types, :days, 5).
|
46
52
|
should == 'b:mock_ditty:views_1_likes_3:hits:current.days_5'
|
47
53
|
end
|
48
54
|
end
|
@@ -21,7 +21,7 @@ describe Boffin::Trackable do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'delegates ::top_ids to the Tracker instance' do
|
24
|
-
MockTrackableInjected.top_ids(:views, days
|
24
|
+
MockTrackableInjected.top_ids(:views, :days => 1).should == ['1']
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'delegates #hit_count to the Tracker instance' do
|
data/spec/boffin/tracker_spec.rb
CHANGED
@@ -55,14 +55,14 @@ 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
|
-
|
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
|
-
|
65
|
+
lambda { @tracker.hit_count(:view, @instance1) }.
|
66
66
|
should raise_error Boffin::UndefinedHitTypeError
|
67
67
|
end
|
68
68
|
|
@@ -77,7 +77,7 @@ describe Boffin::Tracker do
|
|
77
77
|
|
78
78
|
describe '#uhit_count' do
|
79
79
|
it 'throws an error if the hit type is not in the list' do
|
80
|
-
|
80
|
+
lambda { @tracker.uhit_count(:view, @instance1) }.
|
81
81
|
should raise_error Boffin::UndefinedHitTypeError
|
82
82
|
end
|
83
83
|
|
@@ -92,7 +92,7 @@ describe Boffin::Tracker do
|
|
92
92
|
|
93
93
|
describe '#hit_count_for_session_id' do
|
94
94
|
it 'throws an error if the hit type is not in the list' do
|
95
|
-
|
95
|
+
lambda { @tracker.hit_count_for_session_id(:view, @instance1, 'sess.1') }.
|
96
96
|
should raise_error Boffin::UndefinedHitTypeError
|
97
97
|
end
|
98
98
|
|
@@ -107,32 +107,32 @@ describe Boffin::Tracker do
|
|
107
107
|
|
108
108
|
describe '#top' do
|
109
109
|
it 'throws an error if passed hit type is invalid' do
|
110
|
-
|
110
|
+
lambda { @tracker.top(:view, :days => 3) }.
|
111
111
|
should raise_error Boffin::UndefinedHitTypeError
|
112
112
|
end
|
113
113
|
|
114
114
|
it 'throws an error if passed weights with hit type that is invalid' do
|
115
|
-
|
115
|
+
lambda { @tracker.top({ :view => 1 }, :days => 3) }.
|
116
116
|
should raise_error Boffin::UndefinedHitTypeError
|
117
117
|
end
|
118
118
|
|
119
119
|
it 'returns ids ordered by hit counts of weighted totals' do
|
120
|
-
ids = @tracker.top({ views
|
120
|
+
ids = @tracker.top({ :views => 1, :likes => 2 }, :days => 3)
|
121
121
|
ids.should == ['300', '200', '100']
|
122
122
|
end
|
123
123
|
|
124
124
|
it 'returns ids ordered by total counts of a specific hit type' do
|
125
|
-
ids = @tracker.top(:views, days
|
125
|
+
ids = @tracker.top(:views, :days => 3)
|
126
126
|
ids.should == ['300', '100', '200']
|
127
127
|
end
|
128
128
|
|
129
129
|
it 'returns ids in ascending order when passed { order: "asc" } as an option' do
|
130
|
-
ids = @tracker.top(:views, days
|
130
|
+
ids = @tracker.top(:views, :days => 3, :order => 'asc')
|
131
131
|
ids.should == ['200', '100', '300']
|
132
132
|
end
|
133
133
|
|
134
134
|
it 'returns ids and counts when passed { counts: true } as an option' do
|
135
|
-
ids = @tracker.top(:views, days
|
135
|
+
ids = @tracker.top(:views, :days => 3, :counts => true)
|
136
136
|
ids.should == [
|
137
137
|
['300', 12],
|
138
138
|
['100', 8],
|
@@ -141,7 +141,7 @@ describe Boffin::Tracker do
|
|
141
141
|
end
|
142
142
|
|
143
143
|
it 'returns ids based on unique hit data when passed { unique: true } as an option' do
|
144
|
-
ids = @tracker.top(:views, days
|
144
|
+
ids = @tracker.top(:views, :days => 3, :counts => true, :unique => true)
|
145
145
|
ids.should == [
|
146
146
|
['300', 7],
|
147
147
|
['200', 5],
|
@@ -159,4 +159,4 @@ describe Boffin::Tracker do
|
|
159
159
|
@tracker.keyspace(true).unique_namespace?.should be_true
|
160
160
|
end
|
161
161
|
end
|
162
|
-
end
|
162
|
+
end
|
data/spec/boffin/utils_spec.rb
CHANGED
@@ -44,12 +44,12 @@ describe Boffin::Utils do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
describe '::extract_time_unit' do
|
47
|
-
specify { subject.extract_time_unit(hours
|
48
|
-
specify { subject.extract_time_unit(days
|
49
|
-
specify { subject.extract_time_unit(months
|
47
|
+
specify { subject.extract_time_unit(:hours => 6).should == [:hours, 6] }
|
48
|
+
specify { subject.extract_time_unit(:days => 2).should == [:days, 2] }
|
49
|
+
specify { subject.extract_time_unit(:months => 3).should == [:months, 3] }
|
50
50
|
|
51
51
|
it 'throws an error if no time unit pair exists in the hash' do
|
52
|
-
lambda { subject.extract_time_unit(fun
|
52
|
+
lambda { subject.extract_time_unit(:fun => 'times') }.
|
53
53
|
should raise_error ArgumentError
|
54
54
|
end
|
55
55
|
end
|
@@ -57,12 +57,12 @@ describe Boffin::Utils do
|
|
57
57
|
describe '::time_ago' do
|
58
58
|
before { @time = Time.local(2011, 2, 15, 12) }
|
59
59
|
|
60
|
-
specify { subject.time_ago(@time, hours
|
61
|
-
specify { subject.time_ago(@time, days
|
62
|
-
specify { subject.time_ago(@time, months
|
60
|
+
specify { subject.time_ago(@time, :hours => 6).should == Time.local(2011, 2, 15, 6) }
|
61
|
+
specify { subject.time_ago(@time, :days => 5).should == Time.local(2011, 2, 10, 12) }
|
62
|
+
specify { subject.time_ago(@time, :months => 1).should == Time.local(2011, 1, 16, 12) } # A "month" is 30 days
|
63
63
|
|
64
64
|
it 'throws an error if no time unit pair exists in the hash' do
|
65
|
-
lambda { subject.time_ago(@time, fun
|
65
|
+
lambda { subject.time_ago(@time, :fun => 'fail') }.
|
66
66
|
should raise_error ArgumentError
|
67
67
|
end
|
68
68
|
end
|
@@ -70,16 +70,16 @@ describe Boffin::Utils do
|
|
70
70
|
describe '::time_ago_range' do
|
71
71
|
before { @time = Time.local(2011, 2, 15, 12) }
|
72
72
|
|
73
|
-
specify { subject.time_ago_range(@time, hours
|
74
|
-
specify { subject.time_ago_range(@time, months
|
73
|
+
specify { subject.time_ago_range(@time, :hours => 6).size.should == 6 }
|
74
|
+
specify { subject.time_ago_range(@time, :months => 1).size.should == 1 }
|
75
75
|
|
76
76
|
specify do
|
77
|
-
subject.time_ago_range(@time, days
|
77
|
+
subject.time_ago_range(@time, :days => 2).
|
78
78
|
should == [Time.local(2011, 2, 14, 12), Time.local(2011, 2, 15, 12)]
|
79
79
|
end
|
80
80
|
|
81
81
|
specify do
|
82
|
-
subject.time_ago_range(@time, days
|
82
|
+
subject.time_ago_range(@time, :days => 3).
|
83
83
|
should == [
|
84
84
|
Time.local(2011, 2, 13, 12),
|
85
85
|
Time.local(2011, 2, 14, 12),
|
@@ -88,13 +88,13 @@ describe Boffin::Utils do
|
|
88
88
|
end
|
89
89
|
|
90
90
|
it 'ranges from n days away upto @time' do
|
91
|
-
times = subject.time_ago_range(@time, days
|
91
|
+
times = subject.time_ago_range(@time, :days => 4)
|
92
92
|
times.first.should == Time.local(2011, 2, 12, 12)
|
93
93
|
times.last.should == @time
|
94
94
|
end
|
95
95
|
|
96
96
|
it 'throws an error if no time unit pair exists in the hash' do
|
97
|
-
lambda { subject.time_ago_range(@time, fun
|
97
|
+
lambda { subject.time_ago_range(@time, :fun => 'crash') }.
|
98
98
|
should raise_error ArgumentError
|
99
99
|
end
|
100
100
|
end
|
data/spec/boffin_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boffin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-08
|
12
|
+
date: 2011-09-08 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
16
|
-
requirement: &
|
16
|
+
requirement: &70347564151120 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '2.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70347564151120
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70347564150300 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '2.6'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70347564150300
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: timecop
|
38
|
-
requirement: &
|
38
|
+
requirement: &70347564149720 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,18 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70347564149720
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: &70347564149100 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.0.14
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70347564149100
|
47
58
|
description: ! 'Boffin is a library for tracking hits to things in your Ruby application.
|
48
59
|
Things
|
49
60
|
|
@@ -100,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
111
|
version: '0'
|
101
112
|
segments:
|
102
113
|
- 0
|
103
|
-
hash:
|
114
|
+
hash: 3845958252376638612
|
104
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
116
|
none: false
|
106
117
|
requirements:
|
@@ -109,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
120
|
version: '0'
|
110
121
|
segments:
|
111
122
|
- 0
|
112
|
-
hash:
|
123
|
+
hash: 3845958252376638612
|
113
124
|
requirements: []
|
114
125
|
rubyforge_project: boffin
|
115
126
|
rubygems_version: 1.8.6
|