hit_list 1.1.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 +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +95 -0
- data/Rakefile +9 -0
- data/hit_list.gemspec +27 -0
- data/lib/hit_list.rb +6 -0
- data/lib/hit_list/counter.rb +78 -0
- data/lib/hit_list/rails_model_extension.rb +52 -0
- data/lib/hit_list/version.rb +3 -0
- data/spec/counter_end_to_end_spec.rb +54 -0
- data/spec/counter_spec.rb +88 -0
- data/spec/rails_model_extension_spec.rb +72 -0
- data/spec/spec_helper.rb +7 -0
- metadata +134 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7b66c6daf960a7b4f088908bda841f84826764ae
|
4
|
+
data.tar.gz: bb2ab292705bc84bb727475922897d43bb8ffbe0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 03d341c62daa4992d6d64a9152d27ca358392a0edcafee8315cb07d434d3a8b80e690124df2d53cdddacad1d7d1f7396abe9a663545fde6eb2bca1bfb46d5392
|
7
|
+
data.tar.gz: 52fecd21ee1eaff7cb2d4f6f16f2485e48fa10b7df9f3ef28922fa4678ab25e6e1e4f92c7a2efa1aec2c40f01291b9ab313dcaf8273a18bc98bb4b10ee9fc3c2
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Krists Ozols
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# HitList
|
2
|
+
|
3
|
+
Very simple and fast hit and popularity counter using Redis sorted sets.
|
4
|
+
It solves problem where you need to know most popular article or project in last X days.
|
5
|
+
|
6
|
+
[](https://travis-ci.org/krists/hit_list)
|
7
|
+
[](http://badge.fury.io/rb/hit_list)
|
8
|
+
[](https://codeclimate.com/github/krists/hit_list)
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'hit_list'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install hit_list
|
23
|
+
|
24
|
+
## Usage with Rails
|
25
|
+
|
26
|
+
# Set Redis connection in initializer
|
27
|
+
HitList::RailsModelExtension.redis_connection= Redis.new(host: '127.0.0.1', port: 6380)
|
28
|
+
|
29
|
+
# If you don't do this HitList will attempt to create one on its own with default arguments
|
30
|
+
# Equivalent to:
|
31
|
+
HitList::RailsModelExtension.redis_connection= Redis.new
|
32
|
+
|
33
|
+
# include in models..
|
34
|
+
class Article < ActiveRecord::Base
|
35
|
+
include HitList::RailsModelExtension
|
36
|
+
# ..
|
37
|
+
end
|
38
|
+
|
39
|
+
# Use..
|
40
|
+
Article.top_records(3) # => ["1", "43", "13"]
|
41
|
+
|
42
|
+
article = Article.first
|
43
|
+
article.total_hits # => 4
|
44
|
+
article.increment_hit_counter! # Increments total hits counter and ranking for days
|
45
|
+
article.increment_only_total_hits!
|
46
|
+
article.increment_only_rank!
|
47
|
+
|
48
|
+
# When you want to preserve rank stats for more than default 7 days you have to overwrite method hit_list_day_count
|
49
|
+
class Article < ActiveRecord::Base
|
50
|
+
include HitList::RailsModelExtension
|
51
|
+
|
52
|
+
def hit_list_day_count
|
53
|
+
14
|
54
|
+
end
|
55
|
+
|
56
|
+
# ..
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
## Usage without Rails
|
62
|
+
|
63
|
+
# connect to redis db
|
64
|
+
redis_connection = Redis.new(:host => "10.0.1.1", :port => 6380)
|
65
|
+
|
66
|
+
# pass connection when initializing counter
|
67
|
+
counter = HitList::Counter.new(connection, 'articles')
|
68
|
+
|
69
|
+
# make hits..
|
70
|
+
counter.hit!(2)
|
71
|
+
counter.hit!(2)
|
72
|
+
counter.hit!('bob')
|
73
|
+
|
74
|
+
# see results..
|
75
|
+
counter.total_hits(2) # => 2
|
76
|
+
counter.total_hits('bob') # => 1
|
77
|
+
|
78
|
+
# You can track hits on objects. Just provide different name
|
79
|
+
counter_1 = HitList::Counter.new(connection, 'articles')
|
80
|
+
counter_1.hit!('top-story')
|
81
|
+
counter_2 = HitList::Counter.new(connection, 'users')
|
82
|
+
counter_2.hit!('alice')
|
83
|
+
|
84
|
+
# Finaly you can see top records for any given time.
|
85
|
+
counter_1 = HitList::Counter.new(connection, 'articles')
|
86
|
+
counter_1.top_records(3) # => ["91","5","34"]
|
87
|
+
|
88
|
+
# or rankings 3 days ago..
|
89
|
+
counter_1.top_records(2, Time.now - 3.days) # => ["5","34"]
|
90
|
+
|
91
|
+
If you want to preserve ranking for more than 7 days you have to provide day count when initializing `HitList::Counter` object
|
92
|
+
|
93
|
+
# Preserve rankings for 2 weeks
|
94
|
+
counter = HitList::Counter.new(connection, 'articles', 14)
|
95
|
+
counter.hit!('something')
|
data/Rakefile
ADDED
data/hit_list.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hit_list/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hit_list"
|
8
|
+
spec.version = HitList::VERSION
|
9
|
+
spec.authors = ["Krists Ozols"]
|
10
|
+
spec.email = ["krists.ozols@gmail.com"]
|
11
|
+
spec.description = %q{Very simple and fast hit and popularity counter using Redis sorted sets.}
|
12
|
+
spec.summary = %q{It solves problem where you need to know most popular article or project in last X days.}
|
13
|
+
spec.homepage = "https://github.com/krists/hit_list"
|
14
|
+
spec.license = "MIT"
|
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_development_dependency "bundler"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency 'timecop'
|
25
|
+
spec.add_development_dependency "fakeredis"
|
26
|
+
|
27
|
+
end
|
data/lib/hit_list.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module HitList
|
2
|
+
class Counter
|
3
|
+
DEFAULT_NAMESPACE = 'hit_list'
|
4
|
+
DEFAULT_DAYS_OF_INTEREST = 7
|
5
|
+
SECONDS_IN_DAY = 86400
|
6
|
+
DATE_PARTIAL_FORMAT = "%Y-%m-%d"
|
7
|
+
DEFAULT_INCR_VALUE = 1
|
8
|
+
|
9
|
+
# Creates new instance of counter. It requires a working Redis connection as first argument.
|
10
|
+
#
|
11
|
+
# ==== Examples
|
12
|
+
#
|
13
|
+
# counter = HitList::Counter.new(connection, 'articles', 7)
|
14
|
+
# counter = HitList::Counter.new(connection, 'articles')
|
15
|
+
def initialize(connection, name, days_of_interest = DEFAULT_DAYS_OF_INTEREST)
|
16
|
+
@connection, @name, @days_of_interest = connection, name, days_of_interest
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :namespace, :connection, :name, :days_of_interest
|
20
|
+
|
21
|
+
# Returns namespace used in keys
|
22
|
+
def namespace
|
23
|
+
DEFAULT_NAMESPACE
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns integer representing total hit count for specific id
|
27
|
+
#
|
28
|
+
# ==== Examples
|
29
|
+
#
|
30
|
+
# counter.total_hits(5) # => 12
|
31
|
+
# counter.total_hits('some-slug') # => 3
|
32
|
+
def total_hits(id)
|
33
|
+
connection.get("#{namespace}:#{name}:total:#{id}").to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns array of ids sorted by most hits first
|
37
|
+
#
|
38
|
+
# ==== Examples
|
39
|
+
#
|
40
|
+
# counter.top_records(3) # => ["31", "1", "44"]
|
41
|
+
# counter.top(1, Time.now - 4.days) # => ["5"]
|
42
|
+
def top_records(limit, time_of_interest = nil)
|
43
|
+
time = time_of_interest || Time.now
|
44
|
+
date = time.strftime(DATE_PARTIAL_FORMAT)
|
45
|
+
connection.zrevrange("#{namespace}:#{name}:date:#{date}", 0, limit - 1)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Increments total hits and rank for given id
|
49
|
+
def hit!(id)
|
50
|
+
increment_total_hits!(id)
|
51
|
+
increment_rank!(id)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Increments total hits only for given id
|
55
|
+
def increment_total_hits!(id)
|
56
|
+
connection.incr("#{namespace}:#{name}:total:#{id}")
|
57
|
+
end
|
58
|
+
|
59
|
+
# Increments rank only for given id
|
60
|
+
def increment_rank!(id)
|
61
|
+
@connection.pipelined do
|
62
|
+
date_partials.each do |date_partial|
|
63
|
+
connection.zincrby("#{namespace}:#{name}:date:#{date_partial}", DEFAULT_INCR_VALUE, id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def date_partials
|
71
|
+
partials = []
|
72
|
+
0.upto(days_of_interest - 1) do |index|
|
73
|
+
partials << (Time.now + (index * SECONDS_IN_DAY)).strftime(DATE_PARTIAL_FORMAT)
|
74
|
+
end
|
75
|
+
partials.to_enum
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module HitList::RailsModelExtension
|
2
|
+
|
3
|
+
def self.redis_connection
|
4
|
+
@redis_connection ||= begin
|
5
|
+
puts "HitList::RailsModelExtension: No Redis connection provided. Creating new with default settings"
|
6
|
+
Redis.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.redis_connection=(connection)
|
11
|
+
@redis_connection = connection
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def top_records(count = 5)
|
16
|
+
counter = HitList::Counter.new(HitList::RailsModelExtension.redis_connection, self.name)
|
17
|
+
counter.top_records(count)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module InstanceMethods
|
22
|
+
|
23
|
+
def hit_list_day_count
|
24
|
+
7
|
25
|
+
end
|
26
|
+
|
27
|
+
def total_hits
|
28
|
+
hit_counter.total_hits(self.id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def increment_hit_counter!
|
32
|
+
hit_counter.hit!(self.id)
|
33
|
+
end
|
34
|
+
|
35
|
+
def increment_only_total_hits!
|
36
|
+
hit_counter.increment_total_hits!(self.id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def increment_only_rank!
|
40
|
+
hit_counter.increment_rank!(self.id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def hit_counter
|
44
|
+
@hit_counter ||= HitList::Counter.new(HitList::RailsModelExtension.redis_connection, self.class.name, hit_list_day_count)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.included(receiver)
|
49
|
+
receiver.extend ClassMethods
|
50
|
+
receiver.send :include, InstanceMethods
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe HitList::Counter do
|
4
|
+
let(:real_connection) { Redis.new }
|
5
|
+
let(:name) { 'article' }
|
6
|
+
let(:days_of_interest) { 7 }
|
7
|
+
|
8
|
+
subject { described_class.new(real_connection, name, days_of_interest) }
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
real_connection.flushall
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#total_hits" do
|
15
|
+
it "works as expected" do
|
16
|
+
Timecop.travel(Date.parse('2013-01-21'))
|
17
|
+
subject.hit!(22)
|
18
|
+
subject.hit!(22)
|
19
|
+
subject.hit!(7)
|
20
|
+
subject.hit!(44)
|
21
|
+
subject.hit!(44)
|
22
|
+
subject.hit!(44)
|
23
|
+
subject.total_hits(22).should eq(2)
|
24
|
+
subject.total_hits(7).should eq(1)
|
25
|
+
subject.total_hits(44).should eq(3)
|
26
|
+
Timecop.return
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#top_records" do
|
31
|
+
it 'top records should work' do
|
32
|
+
Timecop.travel(Date.parse('2013-01-21'))
|
33
|
+
subject.hit!(22)
|
34
|
+
subject.hit!(22)
|
35
|
+
subject.hit!(22)
|
36
|
+
subject.hit!(7)
|
37
|
+
Timecop.travel(Date.parse('2013-01-22'))
|
38
|
+
subject.hit!(22)
|
39
|
+
subject.hit!(22)
|
40
|
+
Timecop.travel(Date.parse('2013-01-23'))
|
41
|
+
subject.hit!(22)
|
42
|
+
subject.hit!(7)
|
43
|
+
subject.hit!(7)
|
44
|
+
subject.hit!(7)
|
45
|
+
subject.hit!(7)
|
46
|
+
subject.top_records(2).should eq(["22", "7"])
|
47
|
+
Timecop.travel(Date.parse('2013-01-25'))
|
48
|
+
subject.top_records(2).should eq(["22", "7"])
|
49
|
+
Timecop.travel(Date.parse('2013-01-28'))
|
50
|
+
subject.top_records(2).should eq(["7", "22"])
|
51
|
+
Timecop.return
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe HitList::Counter do
|
4
|
+
let(:connection) { double "connection" }
|
5
|
+
let(:name) { 'article' }
|
6
|
+
let(:days_of_interest) { 7 }
|
7
|
+
|
8
|
+
subject { described_class.new(connection, name, days_of_interest) }
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
connection.stub(:get) { '' }
|
12
|
+
connection.stub(:incr) { '' }
|
13
|
+
connection.stub(:zincrby) { '' }
|
14
|
+
connection.stub(:pipelined).and_yield
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#initialize" do
|
18
|
+
it "requires connection, name and days of interest as arguments" do
|
19
|
+
expect { subject }.not_to raise_error
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#namespace" do
|
24
|
+
it "returns default namespace for counter keys" do
|
25
|
+
subject.namespace.should eq("hit_list")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#total_hits" do
|
30
|
+
it "calls #get to connection with key like '<namespace>:<name>:total:<id>'" do
|
31
|
+
connection.should_receive(:get).with('hit_list:article:total:12')
|
32
|
+
subject.total_hits(12)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#hit!" do
|
37
|
+
it "calls #increment_total_hits!" do
|
38
|
+
subject.should_receive(:increment_total_hits!).with(13)
|
39
|
+
subject.hit!(13)
|
40
|
+
end
|
41
|
+
it "calls #increment_rank!" do
|
42
|
+
subject.should_receive(:increment_rank!).with(13)
|
43
|
+
subject.hit!(13)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#increment_total_hits!" do
|
48
|
+
it "calls #incr to connection with key like '<namespace>:<name>:total:<id>'" do
|
49
|
+
connection.should_receive(:incr).with('hit_list:article:total:14')
|
50
|
+
subject.increment_total_hits!(14)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#increment_rank!" do
|
55
|
+
it "calls #zincrby on connection with key like '<namespace>:<name>:date:<date>:<id>' multiple times with right dates" do
|
56
|
+
Timecop.freeze(Date.parse('2013-07-29')) do
|
57
|
+
connection.should_receive(:zincrby).with("hit_list:article:date:2013-07-29", 1, 66)
|
58
|
+
connection.should_receive(:zincrby).with("hit_list:article:date:2013-07-30", 1, 66)
|
59
|
+
connection.should_receive(:zincrby).with("hit_list:article:date:2013-07-31", 1, 66)
|
60
|
+
connection.should_receive(:zincrby).with("hit_list:article:date:2013-08-01", 1, 66)
|
61
|
+
connection.should_receive(:zincrby).with("hit_list:article:date:2013-08-02", 1, 66)
|
62
|
+
connection.should_receive(:zincrby).with("hit_list:article:date:2013-08-03", 1, 66)
|
63
|
+
connection.should_receive(:zincrby).with("hit_list:article:date:2013-08-04", 1, 66)
|
64
|
+
subject.increment_rank!(66)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#top_records" do
|
70
|
+
context "when time not specified" do
|
71
|
+
it "should get top records in period" do
|
72
|
+
Timecop.freeze(Date.parse('2013-07-29')) do
|
73
|
+
connection.should_receive(:zrevrange).with("hit_list:article:date:2013-07-29", 0, 4)
|
74
|
+
subject.top_records(5)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when time is specified" do
|
80
|
+
it "should get top records in period" do
|
81
|
+
Timecop.freeze(Date.parse('2013-07-29')) do
|
82
|
+
connection.should_receive(:zrevrange).with("hit_list:article:date:2013-02-22", 0, 2)
|
83
|
+
subject.top_records(3, Date.parse('2013-02-22'))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe HitList::RailsModelExtension do
|
4
|
+
|
5
|
+
describe "#module methods" do
|
6
|
+
subject { HitList::RailsModelExtension }
|
7
|
+
describe "#redis_connection" do
|
8
|
+
context "when not specifying any specific connection" do
|
9
|
+
it "returns Redis connection with default configuration" do
|
10
|
+
expect(subject.redis_connection).to be_a_kind_of(Redis)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#redis_connection=" do
|
16
|
+
it "allows to set Redis connection" do
|
17
|
+
subject.redis_connection= 'fake connection'
|
18
|
+
subject.redis_connection.should eq('fake connection')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "Class with RailsModelExtension included" do
|
24
|
+
|
25
|
+
SomethingModelish = Struct.new(:id) do
|
26
|
+
include HitList::RailsModelExtension
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "class methods" do
|
30
|
+
subject { SomethingModelish }
|
31
|
+
|
32
|
+
describe "#top_records" do
|
33
|
+
it "calls counter top_records method" do
|
34
|
+
HitList::Counter.any_instance.should_receive(:top_records).with(3)
|
35
|
+
subject.top_records(3)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "instance methods" do
|
41
|
+
subject { SomethingModelish.new(:the_id) }
|
42
|
+
|
43
|
+
describe "#total_hits" do
|
44
|
+
it "calls counter total_hits method with model id" do
|
45
|
+
HitList::Counter.any_instance.should_receive(:total_hits).with(:the_id)
|
46
|
+
subject.total_hits
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#increment_hit_counter!" do
|
51
|
+
it "calls counter hit! method with record id" do
|
52
|
+
HitList::Counter.any_instance.should_receive(:hit!).with(:the_id)
|
53
|
+
subject.increment_hit_counter!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#increment_only_total_hits!" do
|
58
|
+
it "calls counter hit! method with record id" do
|
59
|
+
HitList::Counter.any_instance.should_receive(:increment_total_hits!).with(:the_id)
|
60
|
+
subject.increment_only_total_hits!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#increment_only_rank!" do
|
65
|
+
it "calls counter hit! method with record id" do
|
66
|
+
HitList::Counter.any_instance.should_receive(:increment_rank!).with(:the_id)
|
67
|
+
subject.increment_only_rank!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hit_list
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Krists Ozols
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
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: rspec
|
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: timecop
|
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
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: fakeredis
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Very simple and fast hit and popularity counter using Redis sorted sets.
|
84
|
+
email:
|
85
|
+
- krists.ozols@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE.txt
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- hit_list.gemspec
|
97
|
+
- lib/hit_list.rb
|
98
|
+
- lib/hit_list/counter.rb
|
99
|
+
- lib/hit_list/rails_model_extension.rb
|
100
|
+
- lib/hit_list/version.rb
|
101
|
+
- spec/counter_end_to_end_spec.rb
|
102
|
+
- spec/counter_spec.rb
|
103
|
+
- spec/rails_model_extension_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
105
|
+
homepage: https://github.com/krists/hit_list
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.6.10
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: It solves problem where you need to know most popular article or project
|
129
|
+
in last X days.
|
130
|
+
test_files:
|
131
|
+
- spec/counter_end_to_end_spec.rb
|
132
|
+
- spec/counter_spec.rb
|
133
|
+
- spec/rails_model_extension_spec.rb
|
134
|
+
- spec/spec_helper.rb
|