cheer 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +158 -0
- data/Rakefile +5 -0
- data/cheer.gemspec +25 -0
- data/lib/cheer.rb +7 -0
- data/lib/cheer/argument.rb +56 -0
- data/lib/cheer/error.rb +23 -0
- data/lib/cheer/leaderboard.rb +48 -0
- data/lib/cheer/model_additions.rb +34 -0
- data/lib/cheer/rank_evaluator.rb +67 -0
- data/lib/cheer/version.rb +4 -0
- data/spec/cheer_spec.rb +362 -0
- data/spec/spec_helper.rb +96 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3fbc7d10a59452417daeb9b6556e483a239e5cb4
|
4
|
+
data.tar.gz: 625f081e1de184703ec2b8b22f60b0ccce6c6e59
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: be88f6e3d1d420ee199a771d06000fcf8c5ff190fd9aef2c3020f880a524caaf4ddbafcee90ba61162ae3153a86e7e8d956e2b3c5f624d4933e44445ee7a950e
|
7
|
+
data.tar.gz: b17a8987079615013178485f2cf4ac75af61149e3bbca52c23a34a0feeea5613e50a9f543489347601305f6f6702291059624736b810d16c12698e7c21191c83
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
cheer
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0-p353
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 TODO: Write your name
|
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,158 @@
|
|
1
|
+
# Cheer
|
2
|
+
|
3
|
+
A ruby gem to quickly add leaderboard functionality to any existing model in a rails application. This gem makes it easy to add leaderboards not only in games, where they are usually used but also into any other application which contains rankable models.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'cheer'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```
|
22
|
+
$ gem install cheer
|
23
|
+
```
|
24
|
+
|
25
|
+
## Basic Usage
|
26
|
+
|
27
|
+
Consider a `Movie` model which has a `views` column that stores the number of times users have viewed the movie and a `profit` column that contains the total profit made by that movie.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class Movie < ActiveRecord::Base
|
31
|
+
extend Cheer::ModelAdditions
|
32
|
+
|
33
|
+
leaderboard :viewership_leaderboard, column_name: :views
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
For example consider the following movies in the database:
|
38
|
+
|
39
|
+
<table>
|
40
|
+
<tr>
|
41
|
+
<th>Name</th>
|
42
|
+
<th>Views</th>
|
43
|
+
<th>Profit</th>
|
44
|
+
</tr>
|
45
|
+
<tr>
|
46
|
+
<td>Pulp Fiction</td>
|
47
|
+
<td>50</td>
|
48
|
+
<td>$500</td>
|
49
|
+
</tr>
|
50
|
+
<tr>
|
51
|
+
<td>Reservoir Dogs</td>
|
52
|
+
<td>40</td>
|
53
|
+
<td>$600</td>
|
54
|
+
</tr>
|
55
|
+
<tr>
|
56
|
+
<td>Kill Bill</td>
|
57
|
+
<td>30</td>
|
58
|
+
<td>$200</td>
|
59
|
+
</tr>
|
60
|
+
<tr>
|
61
|
+
<td>Death Proof</td>
|
62
|
+
<td>20</td>
|
63
|
+
<td>$100</td>
|
64
|
+
</tr>
|
65
|
+
<tr>
|
66
|
+
<td>Jackie Brown</td>
|
67
|
+
<td>10</td>
|
68
|
+
<td>$400</td>
|
69
|
+
</tr>
|
70
|
+
</table>
|
71
|
+
|
72
|
+
## leaderboard class method
|
73
|
+
|
74
|
+
The gem will automatically add the `leaderboard` class method to the Movie class which will allow
|
75
|
+
you to configure custom leaderboards.
|
76
|
+
This method takes these arguments: `name`, `column_name`, `sort_order`, `around_limit`.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class Movie < ActiveRecord::Base
|
80
|
+
extend Cheer::ModelAdditions
|
81
|
+
|
82
|
+
leaderboard :profitability_leaderboard, column_name: :profit,
|
83
|
+
sort_order: %w(name),
|
84
|
+
around_limit: 1
|
85
|
+
|
86
|
+
# This will add the :profitability_leaderboard instance method.
|
87
|
+
# It also takes an optional argument(integer) to limit the number of records returned from `top_movies` method.
|
88
|
+
end
|
89
|
+
|
90
|
+
pulp_fiction = Movie.find_by_name("Pulp Fiction")
|
91
|
+
reservoir_dogs = Movie.find_by_name("Reservoir Dogs")
|
92
|
+
kill_bill = Movie.find_by_name("Kill Bill")
|
93
|
+
death_proof = Movie.find_by_name("Death Proof")
|
94
|
+
jackie_brown = Movie.find_by_name("Jackie Brown")
|
95
|
+
|
96
|
+
pulp_fiction.profitability_leaderboard # => #<Cheer::Leaderboard:0xb121a14>
|
97
|
+
pulp_fiction.profitability_leaderboard.current_movie_rank # => 2
|
98
|
+
pulp_fiction.profitability_leaderboard.movies_around # => [reservoir_dogs, pulp_fiction, jackie_brown]
|
99
|
+
pulp_fiction.profitability_leaderboard.top_movies # => [pulp_fiction, reservoir_dogs, kill_bill]
|
100
|
+
pulp_fiction.profitability_leaderboard.top_movies(2) # => [pulp_fiction, reservoir_dogs]
|
101
|
+
|
102
|
+
pulp_fiction.profitability_leaderboard.to_hash # => {current_movie_rank: 2, movies_around: [reservoir_dogs, pulp_fiction, jackie_brown], top_movies: [pulp_fiction, reservoir_dogs, kill_bill]}
|
103
|
+
|
104
|
+
death_proof.profitability_leaderboard.to_hash(2) # => {current_movie_rank: 6, movies_around: [kill_bill, death_proof], top_movies: [pulp_fiction, reservoir_dogs]}
|
105
|
+
```
|
106
|
+
|
107
|
+
|
108
|
+
## Additional Options
|
109
|
+
|
110
|
+
### :sort_order
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
class Movie < ActiveRecord::Base
|
114
|
+
extend Cheer::ModelAdditions
|
115
|
+
|
116
|
+
leaderboard :viewership_leaderboard, column_name: :views,
|
117
|
+
sort_order: ["released_on asc", "number_of_awards desc"]
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
The gem also allows you to specify additional sort orders to resolve conflicts when there are a bunch of movies with the same number of views. The additional sort order can be specified as shown above. In this case, if two movies have the same number of views, the one released earlier will have a higher ranking. In case the number of views and the release date is the same, the one with more awards will have a higher ranking.
|
122
|
+
|
123
|
+
If the `:sort_order` is not specified, the conflicts will be resolved using the `id asc` sort order.
|
124
|
+
|
125
|
+
### :around_limit
|
126
|
+
|
127
|
+
The default number of objects returned by `movies_around` can be overwritten using the option `around_limit`. In the example below, we set the option to 1 forcing the gem to return at most 1 movie before and after the current movie:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class Movie < ActiveRecord::Base
|
131
|
+
extend Cheer::ModelAdditions
|
132
|
+
|
133
|
+
leaderboard :viewership_leaderboard, column_name: :views,
|
134
|
+
around_limit: 1
|
135
|
+
end
|
136
|
+
|
137
|
+
kill_bill = Movie.find_by_name("Kill Bill")
|
138
|
+
kill_bill.viewership_leaderboard.movies_around # => [reservoir_dogs, kill_bill, death_proof]
|
139
|
+
```
|
140
|
+
|
141
|
+
|
142
|
+
## Roadmap
|
143
|
+
* Scopes - Calculate leaderboards and ranking for a specific scope. For example, this will help us generate leaderboards for all movies released in 2012 or for all movies produced by DreamWorks, etc.
|
144
|
+
|
145
|
+
|
146
|
+
## Authors
|
147
|
+
* Karthik C: https://github.com/karthikc
|
148
|
+
* Nitin Misra: https://github.com/nitinstp23
|
149
|
+
* Rakesh Verma: https://github.com/rakesh87
|
150
|
+
|
151
|
+
|
152
|
+
## Contributing
|
153
|
+
|
154
|
+
1. Fork it
|
155
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
156
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
157
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
158
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/cheer.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/cheer/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'cheer'
|
6
|
+
gem.version = Cheer::VERSION
|
7
|
+
gem.authors = ['Eos Software Systems Pvt Ltd']
|
8
|
+
gem.email = ['info@eossys.com']
|
9
|
+
gem.description = %q{A ruby gem to quickly add rankings & leaderboards to existing models in a rails application}
|
10
|
+
gem.summary = gem.description
|
11
|
+
gem.homepage = 'https://github.com/eossys/cheer'
|
12
|
+
gem.license = 'MIT'
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
|
19
|
+
# Runtime Dependencies
|
20
|
+
gem.add_runtime_dependency 'activerecord', '>= 3.0'
|
21
|
+
|
22
|
+
# Development dependencies
|
23
|
+
gem.add_development_dependency 'rspec', '~> 2.14'
|
24
|
+
gem.add_development_dependency 'sqlite3', '~> 1.3'
|
25
|
+
end
|
data/lib/cheer.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Cheer
|
2
|
+
class Argument
|
3
|
+
|
4
|
+
attr_reader :column_name, :sort_order, :around_limit, :model_klass
|
5
|
+
|
6
|
+
def initialize(args = {})
|
7
|
+
@model_klass = args[:model_klass]
|
8
|
+
@column_name = parse_column_name(args[:column_name])
|
9
|
+
@sort_order = parse_sort_order(args[:sort_order])
|
10
|
+
@around_limit = parse_around_limit(args[:around_limit])
|
11
|
+
|
12
|
+
validate_column_name
|
13
|
+
validate_sort_order
|
14
|
+
validate_around_limit
|
15
|
+
|
16
|
+
merge_sort_order
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_column_name(column_name)
|
20
|
+
[String, Symbol].include?(column_name.class) ? column_name.to_s : ''
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_sort_order(sort_order)
|
24
|
+
sort_order.present? ? sort_order : ['id']
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_around_limit(around_limit)
|
28
|
+
around_limit ? around_limit : 2
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_column_name
|
32
|
+
return if column_name.present? && column_exists_in_db?
|
33
|
+
raise Error::InvalidColumnName
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate_around_limit
|
37
|
+
return if around_limit.to_i > 0
|
38
|
+
raise Error::InvalidAroundLimit
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate_sort_order
|
42
|
+
return if sort_order.is_a?(Array)
|
43
|
+
raise Error::InvalidSortOrder
|
44
|
+
end
|
45
|
+
|
46
|
+
def column_exists_in_db?
|
47
|
+
model_klass.column_names.include?(column_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def merge_sort_order
|
51
|
+
# Ensure 'id' is always present in Sort Order.
|
52
|
+
@sort_order | ['id']
|
53
|
+
@sort_order = @sort_order.join(',')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/cheer/error.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Cheer
|
2
|
+
module Error
|
3
|
+
|
4
|
+
class InvalidColumnName < ArgumentError
|
5
|
+
def message
|
6
|
+
':column_name option is not a symbol or string'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class InvalidSortOrder < ArgumentError
|
11
|
+
def message
|
12
|
+
':sort_order option is not an array of symbols or strings'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvalidAroundLimit < ArgumentError
|
17
|
+
def message
|
18
|
+
':around_limit option is not an integer'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Cheer
|
2
|
+
class Leaderboard
|
3
|
+
|
4
|
+
def initialize(attributes = {})
|
5
|
+
@model_klass = attributes[:model_klass]
|
6
|
+
@rank_evaluator = attributes[:rank_evaluator]
|
7
|
+
|
8
|
+
define_public_methods
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_hash(user_limit = 3)
|
12
|
+
{
|
13
|
+
standing_methods[:current_rank] => self.public_send(standing_methods[:current_rank]),
|
14
|
+
standing_methods[:rank_around] => self.public_send(standing_methods[:rank_around]),
|
15
|
+
standing_methods[:top_rankers] => self.public_send(standing_methods[:top_rankers], user_limit)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :model_klass, :rank_evaluator
|
22
|
+
|
23
|
+
def standing_methods
|
24
|
+
@standing_methods ||= model_klass.standing_methods
|
25
|
+
end
|
26
|
+
|
27
|
+
def define_public_methods
|
28
|
+
current_rank_method = standing_methods[:current_rank]
|
29
|
+
rank_around_method = standing_methods[:rank_around]
|
30
|
+
top_rankers_method = standing_methods[:top_rankers]
|
31
|
+
|
32
|
+
self.class_eval do
|
33
|
+
define_method current_rank_method do
|
34
|
+
rank_evaluator.current_rank
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method rank_around_method do
|
38
|
+
rank_evaluator.rank_around
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method top_rankers_method do |user_limit = 3|
|
42
|
+
rank_evaluator.top_rankers(user_limit)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Cheer
|
2
|
+
module ModelAdditions
|
3
|
+
|
4
|
+
# Hookup event for ModelAdditions Module
|
5
|
+
def self.extended(base)
|
6
|
+
base.class_eval do
|
7
|
+
model_name = self.name.underscore
|
8
|
+
|
9
|
+
define_singleton_method :standing_methods do
|
10
|
+
{
|
11
|
+
current_rank: "current_#{model_name}_rank".to_sym,
|
12
|
+
rank_around: "#{model_name.pluralize}_around".to_sym,
|
13
|
+
top_rankers: "top_#{model_name.pluralize}".to_sym
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def leaderboard(name, args = {})
|
20
|
+
define_method name.to_sym do |user_limit = 3|
|
21
|
+
rank_evaluator = RankEvaluator.new(
|
22
|
+
model_object: self,
|
23
|
+
config: Argument.new(args.merge(model_klass: self.class))
|
24
|
+
)
|
25
|
+
|
26
|
+
Leaderboard.new(
|
27
|
+
model_klass: self.class,
|
28
|
+
rank_evaluator: rank_evaluator
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Cheer
|
2
|
+
class RankEvaluator
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegator :@config, :column_name, :column_name
|
6
|
+
def_delegator :@config, :sort_order, :sort_order
|
7
|
+
def_delegator :@config, :around_limit, :around_limit
|
8
|
+
def_delegator :@config, :model_klass, :model_klass
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
@model_object = attributes[:model_object]
|
12
|
+
@config = attributes[:config]
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_rank
|
16
|
+
return high_rankers unless equal_rankers?
|
17
|
+
high_rankers + position_amongst_equal_rankers
|
18
|
+
end
|
19
|
+
|
20
|
+
def rank_around
|
21
|
+
offset = current_rank - (around_limit + 1)
|
22
|
+
limit_setting = around_limit * 2 + 1
|
23
|
+
|
24
|
+
if offset < 0
|
25
|
+
limit_setting += offset
|
26
|
+
offset = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
top_rankers(limit_setting).offset(offset)
|
30
|
+
end
|
31
|
+
|
32
|
+
def top_rankers(user_limit)
|
33
|
+
model_klass.order("#{column_name} DESC, #{sort_order}")
|
34
|
+
.limit(user_limit)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :model_object
|
40
|
+
|
41
|
+
def column_value
|
42
|
+
@column_value ||= model_object.public_send(column_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def high_rankers
|
46
|
+
rankers = model_klass.where("#{column_name} > ?", column_value)
|
47
|
+
.order("#{column_name} DESC")
|
48
|
+
|
49
|
+
@high_rankers ||= rankers.count + 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def equal_rankers?
|
53
|
+
equal_rankers && equal_rankers.count > 1
|
54
|
+
end
|
55
|
+
|
56
|
+
def equal_rankers
|
57
|
+
@equal_rankers ||= model_klass.where("#{column_name} = ?", column_value)
|
58
|
+
.order(sort_order)
|
59
|
+
.select(:id)
|
60
|
+
end
|
61
|
+
|
62
|
+
def position_amongst_equal_rankers
|
63
|
+
equal_rankers.index(model_object).to_i
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/spec/cheer_spec.rb
ADDED
@@ -0,0 +1,362 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cheer do
|
4
|
+
|
5
|
+
context "Students With Equal Score" do
|
6
|
+
before do
|
7
|
+
Student.delete_all
|
8
|
+
@subrat = Student.create!(name: 'Subrat Behera', score: 23)
|
9
|
+
@rakesh = Student.create!(name: 'Rakesh Verma', score: 22)
|
10
|
+
@girish = Student.create!(name: 'Girish Kumar', score: 21)
|
11
|
+
@nitin = Student.create!(name: 'Nitin Misra', score: 22)
|
12
|
+
@bhanu = Student.create!(name: 'Bhanu Chander', score: 19)
|
13
|
+
@prateeth = Student.create!(name: 'Prateeth S', score: 20)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns leaderboard objects" do
|
17
|
+
@subrat.topers.should be_an_instance_of(Cheer::Leaderboard)
|
18
|
+
@rakesh.topers.should be_an_instance_of(Cheer::Leaderboard)
|
19
|
+
@girish.topers.should be_an_instance_of(Cheer::Leaderboard)
|
20
|
+
@nitin.topers.should be_an_instance_of(Cheer::Leaderboard)
|
21
|
+
@bhanu.topers.should be_an_instance_of(Cheer::Leaderboard)
|
22
|
+
@prateeth.topers.should be_an_instance_of(Cheer::Leaderboard)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns the current student rank if students with equal score exists" do
|
26
|
+
# For students with equal scores, sorting should be done on the basis of id,
|
27
|
+
# since the Student model doesn't define sort_order array.
|
28
|
+
@bhanu.topers.current_student_rank.should == 6
|
29
|
+
@nitin.topers.current_student_rank.should == 3
|
30
|
+
@subrat.topers.current_student_rank.should == 1
|
31
|
+
@rakesh.topers.current_student_rank.should == 2
|
32
|
+
@girish.topers.current_student_rank.should == 4
|
33
|
+
@prateeth.topers.current_student_rank.should == 5
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "GameUsers" do
|
38
|
+
before do
|
39
|
+
GameUser.delete_all
|
40
|
+
@dark_lord = GameUser.create!(name: 'Dark Lord', score: 11)
|
41
|
+
@tom_riddle = GameUser.create!(name: 'Tom Riddle', score: 25)
|
42
|
+
@ron_weasely = GameUser.create!(name: 'Ron Weasely', score: 23)
|
43
|
+
@harry_potter = GameUser.create!(name: 'Harry Potter', score: 14)
|
44
|
+
@jack_sparrow = GameUser.create!(name: 'Jack Sparrow', score: 12)
|
45
|
+
@albus_dumbelldore = GameUser.create!(name: 'Albus Dumbelldore', score: 2)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "returns leaderboard objects" do
|
49
|
+
@dark_lord.high_scorers.should be_an_instance_of(Cheer::Leaderboard)
|
50
|
+
@tom_riddle.high_scorers.should be_an_instance_of(Cheer::Leaderboard)
|
51
|
+
@ron_weasely.high_scorers.should be_an_instance_of(Cheer::Leaderboard)
|
52
|
+
@harry_potter.high_scorers.should be_an_instance_of(Cheer::Leaderboard)
|
53
|
+
@jack_sparrow.high_scorers.should be_an_instance_of(Cheer::Leaderboard)
|
54
|
+
@albus_dumbelldore.high_scorers.should be_an_instance_of(Cheer::Leaderboard)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns the current user rank" do
|
58
|
+
@dark_lord.high_scorers.current_game_user_rank.should == 5
|
59
|
+
@tom_riddle.high_scorers.current_game_user_rank.should == 1
|
60
|
+
@ron_weasely.high_scorers.current_game_user_rank.should == 2
|
61
|
+
@jack_sparrow.high_scorers.current_game_user_rank.should == 4
|
62
|
+
@harry_potter.high_scorers.current_game_user_rank.should == 3
|
63
|
+
@albus_dumbelldore.high_scorers.current_game_user_rank.should == 6
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns the top rank users" do
|
67
|
+
@dark_lord.high_scorers.top_game_users.should == [@tom_riddle, @ron_weasely, @harry_potter]
|
68
|
+
@tom_riddle.high_scorers.top_game_users(2).should == [@tom_riddle, @ron_weasely]
|
69
|
+
@ron_weasely.high_scorers.top_game_users(5).should == [@tom_riddle, @ron_weasely, @harry_potter, @jack_sparrow, @dark_lord]
|
70
|
+
@jack_sparrow.high_scorers.top_game_users(4).should == [@tom_riddle, @ron_weasely, @harry_potter, @jack_sparrow]
|
71
|
+
@harry_potter.high_scorers.top_game_users(1).should == [@tom_riddle]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns user around to current user according score descending" do
|
75
|
+
@tom_riddle.high_scorers.game_users_around.should == [@tom_riddle, @ron_weasely, @harry_potter]
|
76
|
+
@harry_potter.high_scorers.game_users_around.should == [@tom_riddle, @ron_weasely, @harry_potter, @jack_sparrow, @dark_lord]
|
77
|
+
@albus_dumbelldore.high_scorers.game_users_around.should == [@jack_sparrow, @dark_lord, @albus_dumbelldore]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "GameUsers With Equal Score" do
|
82
|
+
before do
|
83
|
+
GameUser.delete_all
|
84
|
+
@dark_lord = GameUser.create!(name: 'Dark Lord', score: 11)
|
85
|
+
@tom_riddle = GameUser.create!(name: 'Tom Riddle', score: 25)
|
86
|
+
@ivan_potter = GameUser.create!(name: 'Ivan Potter', score: 14)
|
87
|
+
@ron_weasely = GameUser.create!(name: 'Ron Weasely', score: 23)
|
88
|
+
@harry_potter = GameUser.create!(name: 'Harry Potter', score: 14)
|
89
|
+
@jerry_potter = GameUser.create!(name: 'Jerry Potter', score: 14)
|
90
|
+
@jack_sparrow_1 = GameUser.create!(name: 'Jack Sparrow', score: 12, age: 22)
|
91
|
+
@jack_sparrow_2 = GameUser.create!(name: 'Jack Sparrow', score: 12, age: 23)
|
92
|
+
@albus_dumbelldore = GameUser.create!(name: 'Albus Dumbelldore', score: 2)
|
93
|
+
@duglous_dumbelldore = GameUser.create!(name: 'Duglous Dumbelldore', score: 2)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "returns the current user rank if users with equal score exists" do
|
97
|
+
@dark_lord.high_scorers.current_game_user_rank.should == 8
|
98
|
+
@tom_riddle.high_scorers.current_game_user_rank.should == 1
|
99
|
+
@ivan_potter.high_scorers.current_game_user_rank.should == 4
|
100
|
+
@ron_weasely.high_scorers.current_game_user_rank.should == 2
|
101
|
+
@harry_potter.high_scorers.current_game_user_rank.should == 3
|
102
|
+
@jerry_potter.high_scorers.current_game_user_rank.should == 5
|
103
|
+
@jack_sparrow_1.high_scorers.current_game_user_rank.should == 7
|
104
|
+
@jack_sparrow_2.high_scorers.current_game_user_rank.should == 6
|
105
|
+
@albus_dumbelldore.high_scorers.current_game_user_rank.should == 9
|
106
|
+
@duglous_dumbelldore.high_scorers.current_game_user_rank.should == 10
|
107
|
+
end
|
108
|
+
|
109
|
+
it "returns the top rank users if equal score users exists" do
|
110
|
+
@dark_lord.high_scorers.top_game_users.should == [@tom_riddle, @ron_weasely, @harry_potter]
|
111
|
+
@tom_riddle.high_scorers.top_game_users(1).should == [@tom_riddle]
|
112
|
+
@ivan_potter.high_scorers.top_game_users(2).should == [@tom_riddle, @ron_weasely]
|
113
|
+
@ron_weasely.high_scorers.top_game_users(4).should == [@tom_riddle, @ron_weasely, @harry_potter, @ivan_potter]
|
114
|
+
@harry_potter.high_scorers.top_game_users(5).should == [@tom_riddle, @ron_weasely, @harry_potter, @ivan_potter, @jerry_potter]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "returns user around current user if users with equal score exists" do
|
118
|
+
@dark_lord.high_scorers.game_users_around.should == [@jack_sparrow_2, @jack_sparrow_1, @dark_lord, @albus_dumbelldore, @duglous_dumbelldore]
|
119
|
+
@tom_riddle.high_scorers.game_users_around.should == [@tom_riddle, @ron_weasely, @harry_potter]
|
120
|
+
@ivan_potter.high_scorers.game_users_around.should == [@ron_weasely, @harry_potter, @ivan_potter, @jerry_potter, @jack_sparrow_2]
|
121
|
+
@ron_weasely.high_scorers.game_users_around.should == [@tom_riddle, @ron_weasely, @harry_potter, @ivan_potter]
|
122
|
+
@harry_potter.high_scorers.game_users_around.should == [@tom_riddle, @ron_weasely, @harry_potter, @ivan_potter, @jerry_potter]
|
123
|
+
@jerry_potter.high_scorers.game_users_around.should == [@harry_potter, @ivan_potter, @jerry_potter, @jack_sparrow_2, @jack_sparrow_1]
|
124
|
+
@jack_sparrow_1.high_scorers.game_users_around.should == [@jerry_potter, @jack_sparrow_2, @jack_sparrow_1, @dark_lord, @albus_dumbelldore]
|
125
|
+
@jack_sparrow_2.high_scorers.game_users_around.should == [@ivan_potter, @jerry_potter, @jack_sparrow_2, @jack_sparrow_1, @dark_lord]
|
126
|
+
@albus_dumbelldore.high_scorers.game_users_around.should == [@jack_sparrow_1, @dark_lord, @albus_dumbelldore, @duglous_dumbelldore]
|
127
|
+
@duglous_dumbelldore.high_scorers.game_users_around.should == [@dark_lord, @albus_dumbelldore, @duglous_dumbelldore]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "Products" do
|
132
|
+
before do
|
133
|
+
Product.delete_all
|
134
|
+
@cell = Product.create!(name: 'cell', price: 122)
|
135
|
+
@ring = Product.create!(name: 'ring', price: 14)
|
136
|
+
@shoe = Product.create!(name: 'shoe', price: 235)
|
137
|
+
@belt = Product.create!(name: 'belt', price: 21)
|
138
|
+
@watch = Product.create!(name: 'watch', price: 267)
|
139
|
+
@food = Product.create!(name: 'food', price: 9)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "returns leaderboard objects" do
|
143
|
+
@cell.costliest.should be_an_instance_of(Cheer::Leaderboard)
|
144
|
+
@ring.costliest.should be_an_instance_of(Cheer::Leaderboard)
|
145
|
+
@shoe.costliest.should be_an_instance_of(Cheer::Leaderboard)
|
146
|
+
@belt.costliest.should be_an_instance_of(Cheer::Leaderboard)
|
147
|
+
@watch.costliest.should be_an_instance_of(Cheer::Leaderboard)
|
148
|
+
@food.costliest.should be_an_instance_of(Cheer::Leaderboard)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "returns the current product rank" do
|
152
|
+
@cell.costliest.current_product_rank.should == 3
|
153
|
+
@ring.costliest.current_product_rank.should == 5
|
154
|
+
@shoe.costliest.current_product_rank.should == 2
|
155
|
+
@belt.costliest.current_product_rank.should == 4
|
156
|
+
@watch.costliest.current_product_rank.should == 1
|
157
|
+
@food.costliest.current_product_rank.should == 6
|
158
|
+
end
|
159
|
+
|
160
|
+
it "returns the Top products" do
|
161
|
+
@cell.costliest.top_products(2).should == [@watch, @shoe]
|
162
|
+
@cell.costliest.top_products(3).should == [@watch, @shoe, @cell]
|
163
|
+
@cell.costliest.top_products(4).should == [@watch, @shoe, @cell, @belt]
|
164
|
+
@cell.costliest.top_products(5).should == [@watch, @shoe, @cell, @belt, @ring]
|
165
|
+
@cell.costliest.top_products(6).should == [@watch, @shoe, @cell, @belt, @ring, @food]
|
166
|
+
@cell.costliest.top_products(7).should == [@watch, @shoe, @cell, @belt, @ring, @food]
|
167
|
+
end
|
168
|
+
|
169
|
+
it "returns product around the current product" do
|
170
|
+
@cell.costliest.products_around.should == [@watch, @shoe, @cell, @belt, @ring, @food]
|
171
|
+
@ring.costliest.products_around.should == [@shoe, @cell, @belt, @ring, @food]
|
172
|
+
@shoe.costliest.products_around.should == [@watch, @shoe, @cell, @belt, @ring]
|
173
|
+
@belt.costliest.products_around.should == [@watch, @shoe, @cell, @belt, @ring, @food]
|
174
|
+
@watch.costliest.products_around.should == [@watch, @shoe, @cell, @belt]
|
175
|
+
@food.costliest.products_around.should == [@cell, @belt, @ring, @food]
|
176
|
+
end
|
177
|
+
|
178
|
+
it "returns leaderboard for the current product" do
|
179
|
+
@cell.costliest.to_hash.should == {
|
180
|
+
current_product_rank: 3,
|
181
|
+
products_around: [@watch, @shoe, @cell, @belt, @ring, @food],
|
182
|
+
top_products: [@watch, @shoe, @cell]
|
183
|
+
}
|
184
|
+
|
185
|
+
@ring.costliest.to_hash(5).should == {
|
186
|
+
current_product_rank: 5,
|
187
|
+
products_around: [@shoe, @cell, @belt, @ring, @food],
|
188
|
+
top_products: [@watch, @shoe, @cell, @belt, @ring]
|
189
|
+
}
|
190
|
+
|
191
|
+
@shoe.costliest.to_hash(2).should == {
|
192
|
+
current_product_rank: 2,
|
193
|
+
products_around: [@watch, @shoe, @cell, @belt, @ring],
|
194
|
+
top_products: [@watch, @shoe]
|
195
|
+
}
|
196
|
+
|
197
|
+
@belt.costliest.to_hash.should == {
|
198
|
+
current_product_rank: 4,
|
199
|
+
products_around: [@watch, @shoe, @cell, @belt, @ring, @food],
|
200
|
+
top_products: [@watch, @shoe, @cell]
|
201
|
+
}
|
202
|
+
|
203
|
+
@watch.costliest.to_hash(4).should == {
|
204
|
+
current_product_rank: 1,
|
205
|
+
products_around: [@watch, @shoe, @cell, @belt],
|
206
|
+
top_products: [@watch, @shoe, @cell, @belt]
|
207
|
+
}
|
208
|
+
|
209
|
+
@food.costliest.to_hash(3).should == {
|
210
|
+
current_product_rank: 6,
|
211
|
+
products_around: [@cell, @belt, @ring, @food],
|
212
|
+
top_products: [@watch, @shoe, @cell]
|
213
|
+
}
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Bad Configurations
|
218
|
+
context "Bad Configuration Options" do
|
219
|
+
before do
|
220
|
+
@klass = Class.new do
|
221
|
+
include ActiveModel::Model
|
222
|
+
|
223
|
+
def self.name; "anonymus"; end
|
224
|
+
|
225
|
+
attr_accessor :score
|
226
|
+
|
227
|
+
extend Cheer::ModelAdditions
|
228
|
+
|
229
|
+
def self.column_names; ["score"]; end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
it "fails if column_name is blank" do
|
234
|
+
@klass.class_eval do
|
235
|
+
# Without primary column name
|
236
|
+
leaderboard :bad_config, column_name: '',
|
237
|
+
sort_order: ["name", "age DESC"],
|
238
|
+
around_limit: 2
|
239
|
+
end
|
240
|
+
|
241
|
+
lambda {
|
242
|
+
@klass.new(score: 5).bad_config
|
243
|
+
}.should raise_error(Cheer::Error::InvalidColumnName)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "fails if column_name is not a database column" do
|
247
|
+
@klass.class_eval do
|
248
|
+
leaderboard :bad_config, column_name: :im_not_in_db,
|
249
|
+
sort_order: ["name", "age DESC"],
|
250
|
+
around_limit: 2
|
251
|
+
end
|
252
|
+
|
253
|
+
lambda {
|
254
|
+
@klass.new(score: 5).bad_config
|
255
|
+
}.should raise_error(Cheer::Error::InvalidColumnName)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "fails if sort_order is not an array" do
|
259
|
+
@klass.class_eval do
|
260
|
+
# Invalid sort order
|
261
|
+
leaderboard :bad_config, column_name: :score,
|
262
|
+
sort_order: "name",
|
263
|
+
around_limit: 2
|
264
|
+
end
|
265
|
+
|
266
|
+
lambda {
|
267
|
+
@klass.new(score: 5).bad_config
|
268
|
+
}.should raise_error(Cheer::Error::InvalidSortOrder)
|
269
|
+
end
|
270
|
+
|
271
|
+
it "fails if around_limit is zero or less" do
|
272
|
+
@klass.class_eval do
|
273
|
+
# Invalid around limit
|
274
|
+
leaderboard :bad_config, column_name: :score,
|
275
|
+
sort_order: ["name", "age DESC"],
|
276
|
+
around_limit: [0, -1, -2].sample
|
277
|
+
end
|
278
|
+
|
279
|
+
lambda {
|
280
|
+
@klass.new(score: 5).bad_config
|
281
|
+
}.should raise_error(Cheer::Error::InvalidAroundLimit)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe ".leaderboard" do
|
286
|
+
before do
|
287
|
+
Developer.delete_all
|
288
|
+
@aaron = Developer.create!(name: 'Aaron Patterson', ruby_gems_created: 100, total_experience: 15)
|
289
|
+
@corey = Developer.create!(name: 'Corey Haines', ruby_gems_created: 90, total_experience: 20)
|
290
|
+
@jim = Developer.create!(name: 'Jim Weirich', ruby_gems_created: 50, total_experience: 30)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "returns leaderboard objects" do
|
294
|
+
@aaron.ruby_heroes.should be_an_instance_of(Cheer::Leaderboard)
|
295
|
+
@corey.ruby_heroes.should be_an_instance_of(Cheer::Leaderboard)
|
296
|
+
@jim.ruby_heroes.should be_an_instance_of(Cheer::Leaderboard)
|
297
|
+
end
|
298
|
+
|
299
|
+
context "#current_developer_rank" do
|
300
|
+
it "returns current rank" do
|
301
|
+
@aaron.ruby_heroes.current_developer_rank == 1
|
302
|
+
@corey.ruby_heroes.current_developer_rank == 2
|
303
|
+
@jim.ruby_heroes.current_developer_rank == 3
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context "#developers_around" do
|
308
|
+
it "returns developers around" do
|
309
|
+
@aaron.ruby_heroes.developers_around == [ @aaron, @corey, @jim ]
|
310
|
+
@corey.ruby_heroes.developers_around == [ @aaron, @corey, @jim ]
|
311
|
+
@jim.ruby_heroes.developers_around == [ @aaron, @corey, @jim ]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
context "#top_developers" do
|
316
|
+
it "returns top developers" do
|
317
|
+
@aaron.ruby_heroes.top_developers == [ @aaron, @corey, @jim ]
|
318
|
+
@corey.ruby_heroes.top_developers(2) == [ @aaron, @corey ]
|
319
|
+
@jim.ruby_heroes.top_developers(1) == [ @aaron ]
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "#to_hash method" do
|
324
|
+
it "returns leaderboard hash" do
|
325
|
+
@aaron.ruby_heroes.to_hash.should == {
|
326
|
+
current_developer_rank: 1,
|
327
|
+
developers_around: [ @aaron, @corey, @jim ],
|
328
|
+
top_developers: [ @aaron, @corey, @jim ]
|
329
|
+
}
|
330
|
+
@corey.ruby_heroes.to_hash(1).should == {
|
331
|
+
current_developer_rank: 2,
|
332
|
+
developers_around: [ @aaron, @corey, @jim ],
|
333
|
+
top_developers: [ @aaron ]
|
334
|
+
}
|
335
|
+
@jim.ruby_heroes.to_hash(2).should == {
|
336
|
+
current_developer_rank: 3,
|
337
|
+
developers_around: [ @aaron, @corey, @jim ],
|
338
|
+
top_developers: [ @aaron, @corey ]
|
339
|
+
}
|
340
|
+
end
|
341
|
+
|
342
|
+
it "returns leaderboard hash" do
|
343
|
+
@aaron.veterans.to_hash(2).should == {
|
344
|
+
current_developer_rank: 3,
|
345
|
+
developers_around: [ @corey, @aaron ],
|
346
|
+
top_developers: [ @jim, @corey ]
|
347
|
+
}
|
348
|
+
@corey.veterans.to_hash.should == {
|
349
|
+
current_developer_rank: 2,
|
350
|
+
developers_around: [ @jim, @corey, @aaron ],
|
351
|
+
top_developers: [ @jim, @corey, @aaron ]
|
352
|
+
}
|
353
|
+
@jim.veterans.to_hash(1).should == {
|
354
|
+
current_developer_rank: 1,
|
355
|
+
developers_around: [ @jim, @corey ],
|
356
|
+
top_developers: [ @jim ]
|
357
|
+
}
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
8
|
+
|
9
|
+
require 'active_record'
|
10
|
+
require 'sqlite3'
|
11
|
+
require 'cheer'
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection(
|
14
|
+
adapter: "sqlite3",
|
15
|
+
database: ":memory:"
|
16
|
+
)
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
20
|
+
config.order = 'random'
|
21
|
+
config.filter_run :focus
|
22
|
+
config.run_all_when_everything_filtered = true
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::Migration.verbose = false
|
26
|
+
|
27
|
+
ActiveRecord::Schema.define do
|
28
|
+
create_table :game_users do |t|
|
29
|
+
t.string :name
|
30
|
+
t.float :score, default: 0.0
|
31
|
+
t.integer :age, default: 20
|
32
|
+
t.timestamps
|
33
|
+
end
|
34
|
+
|
35
|
+
create_table :products do |t|
|
36
|
+
t.string :name
|
37
|
+
t.float :price, default: 0.0
|
38
|
+
t.integer :review, default: 1
|
39
|
+
t.timestamps
|
40
|
+
end
|
41
|
+
|
42
|
+
create_table :students do |t|
|
43
|
+
t.string :name
|
44
|
+
t.float :score, default: 0.0
|
45
|
+
t.integer :age, default: 5
|
46
|
+
t.timestamps
|
47
|
+
end
|
48
|
+
|
49
|
+
create_table :developers do |t|
|
50
|
+
t.string :name
|
51
|
+
t.float :total_experience, default: 0.0
|
52
|
+
t.integer :ruby_gems_created, default: 0
|
53
|
+
t.timestamps
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class GameUser < ActiveRecord::Base
|
58
|
+
extend Cheer::ModelAdditions
|
59
|
+
|
60
|
+
# leaderboard leaderboard_name, {:column_name, :sort_order, :around_limit}
|
61
|
+
# Default Sort Order will be ID for game_users with equal score.
|
62
|
+
leaderboard :high_scorers, column_name: :score,
|
63
|
+
sort_order: ["name", "age DESC"],
|
64
|
+
around_limit: 2
|
65
|
+
end
|
66
|
+
|
67
|
+
class Product < ActiveRecord::Base
|
68
|
+
extend Cheer::ModelAdditions
|
69
|
+
|
70
|
+
# leaderboard leaderboard_name, {:column_name, :sort_order, :around_limit}
|
71
|
+
# Default Sort Order will be ID for products with equal price.
|
72
|
+
leaderboard :costliest, column_name: :price,
|
73
|
+
sort_order: %w(name),
|
74
|
+
around_limit: 3
|
75
|
+
end
|
76
|
+
|
77
|
+
class Student < ActiveRecord::Base
|
78
|
+
extend Cheer::ModelAdditions
|
79
|
+
|
80
|
+
# leaderboard leaderboard_name, {:column_name, :sort_order, :around_limit}
|
81
|
+
# Default Sort Order will be ID for students with equal score.
|
82
|
+
leaderboard :topers, column_name: :score
|
83
|
+
end
|
84
|
+
|
85
|
+
class Developer < ActiveRecord::Base
|
86
|
+
extend Cheer::ModelAdditions
|
87
|
+
|
88
|
+
# leaderboard leaderboard_name, {:column_name, :sort_order, :around_limit}
|
89
|
+
leaderboard :ruby_heroes, column_name: :ruby_gems_created,
|
90
|
+
sort_order: %w(name),
|
91
|
+
around_limit: 3
|
92
|
+
|
93
|
+
leaderboard :veterans, column_name: 'total_experience',
|
94
|
+
sort_order: %w(name),
|
95
|
+
around_limit: 1
|
96
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cheer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eos Software Systems Pvt Ltd
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.14'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
description: A ruby gem to quickly add rankings & leaderboards to existing models
|
56
|
+
in a rails application
|
57
|
+
email:
|
58
|
+
- info@eossys.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- .rspec
|
65
|
+
- .ruby-gemset
|
66
|
+
- .ruby-version
|
67
|
+
- Gemfile
|
68
|
+
- LICENSE
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- cheer.gemspec
|
72
|
+
- lib/cheer.rb
|
73
|
+
- lib/cheer/argument.rb
|
74
|
+
- lib/cheer/error.rb
|
75
|
+
- lib/cheer/leaderboard.rb
|
76
|
+
- lib/cheer/model_additions.rb
|
77
|
+
- lib/cheer/rank_evaluator.rb
|
78
|
+
- lib/cheer/version.rb
|
79
|
+
- spec/cheer_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
homepage: https://github.com/eossys/cheer
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 2.2.1
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: A ruby gem to quickly add rankings & leaderboards to existing models in a
|
105
|
+
rails application
|
106
|
+
test_files:
|
107
|
+
- spec/cheer_spec.rb
|
108
|
+
- spec/spec_helper.rb
|