hit_list 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - jruby-19mode
6
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hot_or_not.gemspec
4
+ gemspec
@@ -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.
@@ -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
+ [![Build Status](https://travis-ci.org/krists/hit_list.png?branch=master)](https://travis-ci.org/krists/hit_list)
7
+ [![Gem Version](https://badge.fury.io/rb/hit_list.png)](http://badge.fury.io/rb/hit_list)
8
+ [![Code Climate](https://codeclimate.com/github/krists/hit_list.png)](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')
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ rescue LoadError
7
+ end
8
+
9
+ task :default => :spec
@@ -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
@@ -0,0 +1,6 @@
1
+ require "hit_list/version"
2
+ require "hit_list/counter"
3
+ require "hit_list/rails_model_extension"
4
+
5
+ module HitList
6
+ end
@@ -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,3 @@
1
+ module HitList
2
+ VERSION = "1.1.1"
3
+ 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
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'rspec'
5
+ require "fakeredis"
6
+ require 'timecop'
7
+ require 'hit_list'
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