fatboy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +2 -0
- data/fatboy.gemspec +34 -0
- data/lib/fatboy/helpers.rb +44 -0
- data/lib/fatboy/popularity.rb +70 -0
- data/lib/fatboy/time_based_popularity.rb +63 -0
- data/lib/fatboy/version.rb +5 -0
- data/lib/fatboy/viewed_item.rb +28 -0
- data/lib/fatboy.rb +56 -0
- data/spec/fatboy_spec.rb +50 -0
- data/spec/mocks/model.rb +8 -0
- data/spec/spec_helper.rb +13 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 14480b9fa576ac0e81318733af2789f82f5caaa8
|
4
|
+
data.tar.gz: 987e8724d2cd01bfad21a66853cf3f53803cd0a6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 78061dc924ed74f6205bfb8715eb3c7edcbf91eb14b2c2c8066e01137396b0bcca307a46d5058e4118c5eb7f5e2cffa51760b869da8195b856371101fbcab939
|
7
|
+
data.tar.gz: 48a20e5e25c67f52e6a0e0631beab08abe409cd512e32e0fc9d995b1948e05ed9fe9de9d5decdfbb800347dfda444b6e4fc171f5a65ef5fcd3a89f4cc2f274b0
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
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
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
*.swp
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Anthony Super
|
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,97 @@
|
|
1
|
+
# Fatboy
|
2
|
+
##### See views, right here, right now
|
3
|
+
Fatboy is a gem which manages view counts on ActiveRecord objects (or things that quack like them).
|
4
|
+
It's great for seeing the most (and least) viewed models on your website.
|
5
|
+
To make things even better, Fatboy will store view counts by day, month, year,
|
6
|
+
and all-time.
|
7
|
+
|
8
|
+
It doesn't touch your SQL database.
|
9
|
+
Fatboy stays slim in Redis.
|
10
|
+
|
11
|
+
NOTE: This is currenlty not production-ready.
|
12
|
+
It will be soon, though.
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'fatboy'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install fatboy
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Fatboy is easy to set up. First, initialize it:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# if you don't provide your own redis, fatoby will create one
|
33
|
+
# with Redis.new
|
34
|
+
fatboy = Fatboy.new(redis: redis)
|
35
|
+
```
|
36
|
+
|
37
|
+
Then, just tell it what your users look at.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# As long as the variable you're passing in responds to
|
41
|
+
# .id, this will work
|
42
|
+
fatboy.view(image)
|
43
|
+
# You can also use the shorthand method:
|
44
|
+
# fatboy[image]
|
45
|
+
```
|
46
|
+
|
47
|
+
Now, managing views is pretty useless if you can't retrieve them later.
|
48
|
+
Thankfully, fatboy makes this easy as well:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
fatboy.views_for(image) # => 1
|
52
|
+
```
|
53
|
+
|
54
|
+
Don't worry if that's a brand-new Fatboy instance---as long as the Redis is the same, Fatboy's view count will be the same.
|
55
|
+
|
56
|
+
#### Getting Top Viewed
|
57
|
+
|
58
|
+
Fatboy makes it easy to retrieve a list of the most-popular records in your database.
|
59
|
+
Check it now now:
|
60
|
+
```ruby
|
61
|
+
fatboy.popular(Image).today.most # => most viewed image today
|
62
|
+
fatboy.popular("Image").all_time.most # => Top viewed image of all time
|
63
|
+
```
|
64
|
+
|
65
|
+
Want it from other days?
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# most popular image a month ago
|
69
|
+
fatboy.popular(Image).day(Time.now << 1).most
|
70
|
+
# Or with active support
|
71
|
+
fatboy.popular(Image).day(1.months.ago).most
|
72
|
+
```
|
73
|
+
Don't want just the most popular?
|
74
|
+
Maybe you'd like the least popular?
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
fatboy.popular(Image).day(Time.now).least
|
78
|
+
fatboy.popular(Image).month(Time.now).least
|
79
|
+
```
|
80
|
+
|
81
|
+
Or perhaps a range?
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
fatboy.popular(Image).this_month.range(10..20)
|
85
|
+
# or, a range for a while ago
|
86
|
+
fatboy.popular(Image).month(3.months.ago).range(10..20)
|
87
|
+
```
|
88
|
+
|
89
|
+
Fatboy makes it easier. See the rdoc for details.
|
90
|
+
## Contributing
|
91
|
+
|
92
|
+
1. Fork it ( https://github.com/[my-github-username]/fatboy/fork )
|
93
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
94
|
+
3. Write functionality and tests
|
95
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
96
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
97
|
+
6. Create a new Pull Request
|
data/Rakefile
ADDED
data/fatboy.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fatboy/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "fatboy"
|
8
|
+
spec.version = Fatboy::VERSION
|
9
|
+
spec.authors = ["Anthony Super"]
|
10
|
+
spec.email = ["anthony@noided.media"]
|
11
|
+
spec.summary = %q{Fatboy keeps track of your models's views, right here, right now.}
|
12
|
+
spec.description = %q{
|
13
|
+
Fatboy is a gem designed to easily keep track of views.
|
14
|
+
It doesn't touch your SQL, and stays slim in redis.
|
15
|
+
It also makes it easy to query based on how viewed something is.
|
16
|
+
}
|
17
|
+
spec.homepage = "http://github.com/AnthonySuper/fatboy"
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "timecop"
|
29
|
+
spec.add_development_dependency "mock_redis"
|
30
|
+
spec.add_development_dependency "simplecov"
|
31
|
+
spec.add_runtime_dependency "redis"
|
32
|
+
spec.required_ruby_version = ">= 2.0"
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Fatboy
|
2
|
+
##
|
3
|
+
# Common helper functions, which are used across many
|
4
|
+
# classes.
|
5
|
+
module Helpers
|
6
|
+
##
|
7
|
+
# Given a model_name and the properly formated hour, day, etc. string,
|
8
|
+
# give the name of the key views are stored in
|
9
|
+
def self.format_store(model_name, store_name)
|
10
|
+
"#{model_name}-#{store_name}"
|
11
|
+
end
|
12
|
+
##
|
13
|
+
# Properly format the time to retrieve or set an hour
|
14
|
+
def self.hour_format(hr)
|
15
|
+
hr.utc.strftime(Fatboy::HOUR_FORMAT_STR)
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Properly format the time to retrieve or set a day
|
20
|
+
def self.day_format(day)
|
21
|
+
day.utc.strftime(Fatboy::DAY_FORMAT_STR)
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Properly format the time to retrieve or set a month
|
26
|
+
def self.month_format(mth)
|
27
|
+
mth.utc.strftime(Fatboy::MONTH_FORMAT_STR)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Properly format the time to retrieve or set a year
|
32
|
+
def self.year_format(yr)
|
33
|
+
yr.utc.strftime(Fatboy::YEAR_FORMAT_STR)
|
34
|
+
end
|
35
|
+
##
|
36
|
+
# Get an array of the hour format, the day format, the month format, and
|
37
|
+
# the year format
|
38
|
+
def self.all_format(time)
|
39
|
+
[:hour_format, :day_format, :month_format, :year_format].map do |func|
|
40
|
+
self.send(func, time)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative './time_based_popularity'
|
2
|
+
class Fatboy
|
3
|
+
##
|
4
|
+
# This class is used to query how popular something is.
|
5
|
+
class Popularity
|
6
|
+
##
|
7
|
+
# We always pass in a redis
|
8
|
+
def initialize(model, redis)
|
9
|
+
@redis = redis
|
10
|
+
@model_name = model.to_s
|
11
|
+
end
|
12
|
+
##
|
13
|
+
# Get a Fatboy::TimeBasedPopularity for a specific hour in time.
|
14
|
+
# Arguments:
|
15
|
+
# * +time+: a DateTime or Time containing the hour in time you wish to query
|
16
|
+
def hour(time)
|
17
|
+
fmt_tim = Fatboy::Helpers.hour_format(time.utc)
|
18
|
+
store_name = Fatboy::Helpers.format_store(@model_name, fmt_tim)
|
19
|
+
Fatboy::TimeBasedPopularity.new(@redis, store_name)
|
20
|
+
end
|
21
|
+
##
|
22
|
+
# Get a Fatboy::TimeBasedPopularity for a specific day in time.
|
23
|
+
# Arguments:
|
24
|
+
# * +time+: A Datetime or Time in the day you wish to query.
|
25
|
+
def day(time)
|
26
|
+
fmt_time = Fatboy::Helpers.day_format(time.utc)
|
27
|
+
store_name = Fatboy::Helpers.format_store(@model_name, fmt_time)
|
28
|
+
|
29
|
+
Fatboy::TimeBasedPopularity.new(@redis, store_name)
|
30
|
+
end
|
31
|
+
##
|
32
|
+
# Get a Fatboy::TimeBasedPopularity for a specific month in time.
|
33
|
+
# Arguments:
|
34
|
+
# * +time+: A time within the month you wish to query.
|
35
|
+
def month(time)
|
36
|
+
fmt_time = Fatboy::Helpers.month_format(time.utc)
|
37
|
+
store_name = Fatboy::Helpers.format_store(@model_name, fmt_time)
|
38
|
+
Fatboy::TimeBasedPopularity.new(@redis, store_name)
|
39
|
+
end
|
40
|
+
##
|
41
|
+
# Get a Fatboy::TimeBasedPopularity for a specific year in time.
|
42
|
+
# Arguments:
|
43
|
+
# * +time+: A DateTime or Time in the year you wish to query.
|
44
|
+
def year(time)
|
45
|
+
fmt_time = Fatboy::Helpers.year_format(time.utc)
|
46
|
+
store_name = Fatboy::Helpers.format_store(@model_name, fmt_time)
|
47
|
+
Fatboy::TimeBasedPopularity.new(@redis, store_name)
|
48
|
+
end
|
49
|
+
##
|
50
|
+
# Get a Fatboy::TimeBasedPopularity for this hour.
|
51
|
+
def this_hour
|
52
|
+
hour(Time.now)
|
53
|
+
end
|
54
|
+
##
|
55
|
+
# Get a Fatboy::TimeBasedPopularity for this day.
|
56
|
+
def today
|
57
|
+
day(Time.now)
|
58
|
+
end
|
59
|
+
##
|
60
|
+
# Get a Fatboy::TimeBasedPopularity for this month.
|
61
|
+
def this_month
|
62
|
+
month(Time.now)
|
63
|
+
end
|
64
|
+
##
|
65
|
+
# Get a Fatboy::TimeBasedPopularity for this year.
|
66
|
+
def this_year
|
67
|
+
year(Time.now)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative './viewed_item'
|
2
|
+
class Fatboy
|
3
|
+
##
|
4
|
+
# TimeBasedPopularity measures the popularity based on a set period of time.
|
5
|
+
# You should probably never initialize this yourself.
|
6
|
+
class TimeBasedPopularity
|
7
|
+
##
|
8
|
+
# What redis to look in, and what sorted set we're using.
|
9
|
+
# Probably don't ever initialize this yourself.
|
10
|
+
def initialize(redis, store)
|
11
|
+
@redis = redis
|
12
|
+
@store = store
|
13
|
+
end
|
14
|
+
##
|
15
|
+
# Get an enumerator of all viewed items, as a Fatboy::ViewedItem, in
|
16
|
+
# rank order.
|
17
|
+
# Pretty useful for lazy operations and such.
|
18
|
+
def enumerator
|
19
|
+
Enumerator.new(size) do |yielder|
|
20
|
+
range(0..(size-1)).each{|x| yielder.yield x}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
##
|
24
|
+
# Get the most viewed item.
|
25
|
+
# Returns a Fatboy::ViewedItem
|
26
|
+
def most
|
27
|
+
range(0..1).first
|
28
|
+
end
|
29
|
+
##
|
30
|
+
# Get the least viewed item.
|
31
|
+
# Returns a Fatboy::ViewedItem
|
32
|
+
def least
|
33
|
+
range(-1..-2).first
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Get the total number of items viewed.
|
38
|
+
def size
|
39
|
+
@redis.zcard(@store)
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Specify a range of ranks, and gets them.
|
44
|
+
# Returns an array of Fatboy::ViewedItem
|
45
|
+
def range(rng)
|
46
|
+
start = rng.first
|
47
|
+
stop = rng.last
|
48
|
+
##
|
49
|
+
# Build up a list of pairs: [id, score]
|
50
|
+
pairs = @redis.zrevrange(@store, start, stop, withscores: true)
|
51
|
+
##
|
52
|
+
# Get rid of nils, zip up list with range of rank
|
53
|
+
triplets = pairs.reject{|p| !p}.zip(start..stop)
|
54
|
+
# After the zip, we have [[[id, score], rank], [[id, score], rank]]
|
55
|
+
# So we flatten out the inner arrays, giving us
|
56
|
+
# [[id, score, rank], [id, score, rank]]
|
57
|
+
triplets.map!(&:flatten)
|
58
|
+
##
|
59
|
+
# Use the array splat to more easily pass in the 3 arguments
|
60
|
+
triplets.map{|trip| Fatboy::ViewedItem.new(*trip)}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Fatboy
|
2
|
+
##
|
3
|
+
# The viewed item class provides a (very) simple interface for interacting
|
4
|
+
# with views on Models.
|
5
|
+
# Almost always obtained from a TimeBasedPopularity, this struct simply
|
6
|
+
# shows the id of the model, the number of views, and the rank.
|
7
|
+
# NOTE: The highest-ranked item has rank 0. Ranks are 0-indexed.
|
8
|
+
class ViewedItem
|
9
|
+
def initialize(id, views, rank)
|
10
|
+
@id = id.to_i
|
11
|
+
@views = views.to_i
|
12
|
+
@rank = rank.to_i
|
13
|
+
end
|
14
|
+
##
|
15
|
+
# Id of the model contained this this ViewedItem
|
16
|
+
attr_reader :id
|
17
|
+
##
|
18
|
+
# Amount this ViewedItem has been viewed in a time period.
|
19
|
+
# This time period was pre-set on construction.
|
20
|
+
attr_reader :views
|
21
|
+
##
|
22
|
+
# The rank, 0 being the most viewed, of this item.
|
23
|
+
# The time period was pre-set on construction, typically from a
|
24
|
+
# Fatboy::TimeBasedView
|
25
|
+
attr_reader :rank
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
data/lib/fatboy.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require "fatboy/version"
|
2
|
+
require 'redis'
|
3
|
+
require_relative './fatboy/popularity'
|
4
|
+
require_relative './fatboy/helpers'
|
5
|
+
##
|
6
|
+
# Fatboy is the main class for interacting with the system.
|
7
|
+
# It provides a variety of functionality.
|
8
|
+
class Fatboy
|
9
|
+
##
|
10
|
+
# Create a new Fatboy.
|
11
|
+
# Options:
|
12
|
+
# * +redis:+ : The redis to store views in. By default, Redis.new
|
13
|
+
def initialize(redis: Redis.new)
|
14
|
+
@redis = redis
|
15
|
+
end
|
16
|
+
##
|
17
|
+
# Say that you have viewed an object, making the proper records for
|
18
|
+
# hour, day, month, and year.
|
19
|
+
# * +model+ - a model of some sort. Should quack like an ActiveRecord model (that is, responding to .id)
|
20
|
+
def view(obj)
|
21
|
+
throw ArgumentError.new("That doesn't quack like a model!") unless obj.respond_to?(:id)
|
22
|
+
stores = Fatboy::Helpers.all_format(Time.now).map do |time|
|
23
|
+
Fatboy::Helpers.format_store(obj.class.to_s, time)
|
24
|
+
end
|
25
|
+
stores.map{|store| inc_member(store, obj.id)}
|
26
|
+
end
|
27
|
+
##
|
28
|
+
# let users view with a shorthand
|
29
|
+
alias :[] view
|
30
|
+
##
|
31
|
+
# This method returns a Fatboy::Popularity, the main interface for
|
32
|
+
# determining the popularity of your models.
|
33
|
+
# Example:
|
34
|
+
# fatboy.popular(Image)
|
35
|
+
# fatboy.popular("Image")
|
36
|
+
# fatboy.popular(model.class)
|
37
|
+
def popular(model)
|
38
|
+
Popularity.new(model, @redis)
|
39
|
+
end
|
40
|
+
##
|
41
|
+
# Format string we use to store the views per hour
|
42
|
+
HOUR_FORMAT_STR = "%Y%m%d%H"
|
43
|
+
##
|
44
|
+
# Format string we use to store the views per day
|
45
|
+
DAY_FORMAT_STR = "%Y%m%d"
|
46
|
+
##
|
47
|
+
# Format string we use to store the views per month
|
48
|
+
MONTH_FORMAT_STR = "%Y%m"
|
49
|
+
##
|
50
|
+
# Format string we use to store the views per year
|
51
|
+
YEAR_FORMAT_STR = "%Y"
|
52
|
+
private
|
53
|
+
def inc_member(store, id)
|
54
|
+
@redis.zincrby(store, 1, id)
|
55
|
+
end
|
56
|
+
end
|
data/spec/fatboy_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
RSpec.describe Fatboy do
|
3
|
+
it "can be initialized" do
|
4
|
+
expect{Fatboy.new}.to_not raise_error
|
5
|
+
end
|
6
|
+
let(:redis){MockRedis.new}
|
7
|
+
let(:f){Fatboy.new(redis: redis)}
|
8
|
+
describe "storage" do
|
9
|
+
before(:each){Timecop.freeze(Date.today)}
|
10
|
+
after(:each){Timecop.return}
|
11
|
+
it "ads to the redis" do
|
12
|
+
l = Fatboy::Helpers.day_format(Time.now)
|
13
|
+
l = Fatboy::Helpers.format_store("Model", l)
|
14
|
+
expect{
|
15
|
+
f.view(Model.new(10))
|
16
|
+
}.to change{redis.zcount(l, -100, 100)}.from(0).to(1)
|
17
|
+
f.view(Model.new(11))
|
18
|
+
expect(redis.zscore(l, 11)).to eq(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
describe "ordering" do
|
23
|
+
|
24
|
+
before(:each){Timecop.freeze(Date.today)}
|
25
|
+
after(:each){Timecop.return}
|
26
|
+
it "orders views by day" do
|
27
|
+
2.times{f.view(Model.new(10))}
|
28
|
+
1.times{f.view(Model.new(11))}
|
29
|
+
expect(f.popular(Model).today.most.id).to eq(10)
|
30
|
+
end
|
31
|
+
it "orders views by month" do
|
32
|
+
2.times{f.view(Model.new(10))}
|
33
|
+
1.times{f.view(Model.new(11))}
|
34
|
+
expect(f.popular(Model).this_month.most.id).to eq(10)
|
35
|
+
end
|
36
|
+
it "orders views by year" do
|
37
|
+
2.times{f.view(Model.new(10))}
|
38
|
+
1.times{f.view(Model.new(11))}
|
39
|
+
expect(f.popular(Model).this_year.most.id).to eq(10)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
describe "popularity finding" do
|
43
|
+
it "doesn't throw an error" do
|
44
|
+
expect{f.popular(Model)}.to_not raise_error
|
45
|
+
end
|
46
|
+
it "returns a popularity" do
|
47
|
+
expect(f.popular(Model)).to be_instance_of(Fatboy::Popularity)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/spec/mocks/model.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.setup
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start
|
5
|
+
require 'fatboy'
|
6
|
+
require_relative './mocks/model.rb'
|
7
|
+
require 'timecop'
|
8
|
+
require 'mock_redis'
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
# Don't need to actually do anything
|
12
|
+
end
|
13
|
+
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fatboy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anthony Super
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-19 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: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
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: mock_redis
|
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
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: redis
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: "\n Fatboy is a gem designed to easily keep track of views.\n It doesn't
|
112
|
+
touch your SQL, and stays slim in redis.\n It also makes it easy to query based
|
113
|
+
on how viewed something is.\n "
|
114
|
+
email:
|
115
|
+
- anthony@noided.media
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".gitignore"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- fatboy.gemspec
|
126
|
+
- lib/fatboy.rb
|
127
|
+
- lib/fatboy/helpers.rb
|
128
|
+
- lib/fatboy/popularity.rb
|
129
|
+
- lib/fatboy/time_based_popularity.rb
|
130
|
+
- lib/fatboy/version.rb
|
131
|
+
- lib/fatboy/viewed_item.rb
|
132
|
+
- spec/fatboy_spec.rb
|
133
|
+
- spec/mocks/model.rb
|
134
|
+
- spec/spec_helper.rb
|
135
|
+
homepage: http://github.com/AnthonySuper/fatboy
|
136
|
+
licenses:
|
137
|
+
- MIT
|
138
|
+
metadata: {}
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '2.0'
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.2.2
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Fatboy keeps track of your models's views, right here, right now.
|
159
|
+
test_files:
|
160
|
+
- spec/fatboy_spec.rb
|
161
|
+
- spec/mocks/model.rb
|
162
|
+
- spec/spec_helper.rb
|
163
|
+
has_rdoc:
|