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.
@@ -1,3 +1,7 @@
1
+ 0.2.0
2
+
3
+ * Support for Ruby 1.8.7 thanks to Justin Giancola
4
+
1
5
  0.1.0
2
6
 
3
7
  * Initial public release
data/Gemfile CHANGED
@@ -4,5 +4,5 @@ gemspec
4
4
  group :development do
5
5
  gem 'rake'
6
6
  gem 'redcarpet'
7
- gem 'yard', git: 'https://github.com/lsegal/yard.git'
7
+ gem 'yard', :git => 'https://github.com/lsegal/yard.git'
8
8
  end
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
@@ -11,4 +11,4 @@ end
11
11
  require 'yard'
12
12
  YARD::Rake::YardocTask.new
13
13
 
14
- task default: :spec
14
+ task :default => :spec
@@ -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(?\n)
15
- s.test_files = `git ls-files -- spec/*`.split(?\n)
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
@@ -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: '%F-%H',
28
- days: '%F',
29
- months: '%Y-%m' }
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
@@ -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: weights.values
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: opts.delete(:counts),
198
- order: (opts.delete(:order) || :desc).to_sym }
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: true } : {}]
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)
@@ -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: SECONDS_IN_HOUR,
17
- days: SECONDS_IN_DAY,
18
- months: SECONDS_IN_MONTH
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) || obj.respond_to?(:id)
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] ? Base64.strict_encode64(obj.to_s) : obj.to_s
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: true)
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: true)
179
+ object_as_identifier(obj, :encode => true)
168
180
  end
169
181
 
170
182
  end
@@ -1,4 +1,4 @@
1
1
  module Boffin
2
2
  # Version of this Boffin release
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
@@ -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: 'hello')
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: 'carsten')
38
+ newconf = subject.merge(:namespace => 'carsten')
39
39
  newconf.namespace.should == 'carsten'
40
40
  subject.namespace.should == 'boffin'
41
41
  end
@@ -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: true).
19
+ @tracker.top(:tests, interval => 1, :counts => true).
20
20
  should == [['1', 1]]
21
- @tracker.top(:tests, interval => 1, counts: true, unique: true).
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: true).
32
+ @tracker.top(:tests, interval => 1, :counts => true).
33
33
  should == [['1', 2]]
34
- @tracker.top(:tests, interval => 1, counts: true, unique: true).
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
- @ks.hits_union_multi({ views: 1, likes: 3 }, :days, 5).
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: 1).should == ['1']
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
@@ -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
- -> { @tracker.hit(:view, @instance1) }.
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
- -> { @tracker.hit_count(:view, @instance1) }.
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
- -> { @tracker.uhit_count(:view, @instance1) }.
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
- -> { @tracker.hit_count_for_session_id(:view, @instance1, 'sess.1') }.
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
- -> { @tracker.top(:view, days: 3) }.
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
- -> { @tracker.top({ view: 1 }, days: 3) }.
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: 1, likes: 2 }, days: 3)
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: 3)
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: 3, order: 'asc')
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: 3, counts: true)
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: 3, counts: true, unique: true)
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
@@ -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: 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] }
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: 'times') }.
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: 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
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: 'fail') }.
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: 6).size.should == 6 }
74
- specify { subject.time_ago_range(@time, months: 1).size.should == 1 }
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: 2).
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: 3).
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: 4)
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: 'crash') }.
97
+ lambda { subject.time_ago_range(@time, :fun => 'crash') }.
98
98
  should raise_error ArgumentError
99
99
  end
100
100
  end
@@ -32,7 +32,7 @@ describe Boffin do
32
32
  end
33
33
 
34
34
  it 'accepts a hash' do
35
- Boffin.config(namespace: 'trendy')
35
+ Boffin.config(:namespace => 'trendy')
36
36
  Boffin.config.namespace.should == 'trendy'
37
37
  end
38
38
 
@@ -6,7 +6,7 @@ require 'timecop'
6
6
 
7
7
  $redis = if ENV['DEBUG']
8
8
  require 'logger'
9
- Redis.connect(logger: Logger.new(STDERR))
9
+ Redis.connect(:logger => Logger.new(STDERR))
10
10
  else
11
11
  Redis.connect
12
12
  end
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.1.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-26 00:00:00.000000000Z
12
+ date: 2011-09-08 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
16
- requirement: &70146172825080 !ruby/object:Gem::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: *70146172825080
24
+ version_requirements: *70347564151120
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70146172824200 !ruby/object:Gem::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: *70146172824200
35
+ version_requirements: *70347564150300
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: timecop
38
- requirement: &70146172823600 !ruby/object:Gem::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: *70146172823600
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: -3925281016385501386
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: -3925281016385501386
123
+ hash: 3845958252376638612
113
124
  requirements: []
114
125
  rubyforge_project: boffin
115
126
  rubygems_version: 1.8.6