rating_for 0.2
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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +79 -0
- data/Rakefile +9 -0
- data/lib/generators/rating_for/migration/migration_generator.rb +26 -0
- data/lib/generators/rating_for/migration/templates/migration.rb +32 -0
- data/lib/rating_for/rateable_element.rb +86 -0
- data/lib/rating_for/rating.rb +37 -0
- data/lib/rating_for/version.rb +3 -0
- data/lib/rating_for.rb +88 -0
- data/rating_for.gemspec +24 -0
- data/test/fixtures/hotels.yml +7 -0
- data/test/fixtures/newspapers.yml +4 -0
- data/test/fixtures/users.yml +7 -0
- data/test/rating_for_test.rb +129 -0
- data/test/test_helper.rb +33 -0
- metadata +91 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2010 [name of plugin creator]
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# rating_for
|
|
2
|
+
|
|
3
|
+
## A context-based rating gem for Rails
|
|
4
|
+
This gem allows you to add multiple different rating criteria to your ActiveRecord based model. It provides column-caching, a polymorphic association for raters and works with both Rails 2 and 3.
|
|
5
|
+
|
|
6
|
+
It's a pure model gem and does not provide any view methods. Prototype, jQuery and company provide all excellent methods and plugins for this.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
Rails 2:
|
|
10
|
+
|
|
11
|
+
Not supported anymore.
|
|
12
|
+
|
|
13
|
+
Rails 3:
|
|
14
|
+
|
|
15
|
+
Add the gem to your Gemfile:
|
|
16
|
+
|
|
17
|
+
gem 'rating_for'
|
|
18
|
+
|
|
19
|
+
Generate the migration to create the tables and run it:
|
|
20
|
+
|
|
21
|
+
rails g rating_for:migration
|
|
22
|
+
rake db:migrate
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Example
|
|
26
|
+
Let's say you want to rate hotels by different criteria, like room service or quality.
|
|
27
|
+
Then your model hotel.rb should look like this:
|
|
28
|
+
|
|
29
|
+
class Hotel < ActiveRecord::Base
|
|
30
|
+
rating_for :room_service
|
|
31
|
+
rating_for :quality
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
You can add ratings like this:
|
|
35
|
+
|
|
36
|
+
four_seasons = Hotel.find_by_name("Four Seasons")
|
|
37
|
+
four_seasons.rating_for_quality.add(10)
|
|
38
|
+
four_seasons.rating_for_room_service.add(9)
|
|
39
|
+
|
|
40
|
+
rating = Rating.new(:value => 7)
|
|
41
|
+
marriott = Hotel.find_by_name('Marriott')
|
|
42
|
+
marriott.rating_for_room_service << rating
|
|
43
|
+
|
|
44
|
+
You can also assign raters to your ratings,
|
|
45
|
+
e.g. customers
|
|
46
|
+
|
|
47
|
+
class Customer < ActiveRecord::Base
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
or newspapers
|
|
51
|
+
|
|
52
|
+
class Newspaper < ActiveRecord::Base
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
by simply adding them to your rating:
|
|
56
|
+
|
|
57
|
+
a_customer = Customer.find(1)
|
|
58
|
+
nytimes = Newspaper.find_by_name("New York Times")
|
|
59
|
+
|
|
60
|
+
four_seasons.rating_for_quality.add(10, a_customer)
|
|
61
|
+
marriott.rating_for_room_service << Rating.new(:rater => nytimes, :value => 8)
|
|
62
|
+
|
|
63
|
+
Search over ratings is also possible:
|
|
64
|
+
|
|
65
|
+
hotels = Hotel.find_where_quality_has_average_rating_of(10)
|
|
66
|
+
|
|
67
|
+
hotel1 = hotels.first
|
|
68
|
+
hotel1.name
|
|
69
|
+
=> "Four Seasons"
|
|
70
|
+
hotel1.avg_rating
|
|
71
|
+
=> 10.0
|
|
72
|
+
|
|
73
|
+
## TODO
|
|
74
|
+
* Add more (better) query methods
|
|
75
|
+
* Add more examples
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
Copyright (c) 2012 [Frane Bandov](http://github.com/frane), released under the MIT license
|
|
79
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'rails/generators/migration'
|
|
3
|
+
|
|
4
|
+
module RatingFor
|
|
5
|
+
class MigrationGenerator < Rails::Generators::Base
|
|
6
|
+
include Rails::Generators::Migration
|
|
7
|
+
|
|
8
|
+
desc 'Generates a migration file that contains all tables that are needed by the rating_for gem'
|
|
9
|
+
|
|
10
|
+
def self.source_root
|
|
11
|
+
@source_root ||= File.expand_path('../templates', __FILE__)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.next_migration_number(path)
|
|
15
|
+
if ActiveRecord::Base.timestamped_migrations
|
|
16
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
17
|
+
else
|
|
18
|
+
"%.3d" % (current_migration_number(path) + 1)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def manifest
|
|
23
|
+
migration_template 'migration.rb', 'db/migrate/create_rating_for_tables'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class CreateRatingForTables < ActiveRecord::Migration
|
|
2
|
+
def self.up
|
|
3
|
+
create_table :rateable_elements do |t|
|
|
4
|
+
t.string :element_type
|
|
5
|
+
t.string :element_attribute
|
|
6
|
+
t.integer :element_id
|
|
7
|
+
t.float :avg_rating, :default => 0
|
|
8
|
+
t.integer :total_rating, :default => 0
|
|
9
|
+
t.integer :ratings_count, :default => 0
|
|
10
|
+
|
|
11
|
+
t.timestamps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
create_table :ratings do |t|
|
|
15
|
+
t.integer :value
|
|
16
|
+
t.integer :rateable_element_id
|
|
17
|
+
t.integer :rater_id
|
|
18
|
+
t.string :rater_type
|
|
19
|
+
|
|
20
|
+
t.timestamps
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
add_index :ratings, :rateable_element_id
|
|
24
|
+
add_index :ratings, [:rater_type, :rater_id]
|
|
25
|
+
add_index :rateable_elements, [:element_type, :element_attribute, :element_id], :name => :rateable_elements_index
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.down
|
|
29
|
+
drop_table :ratings
|
|
30
|
+
drop_table :rateable_elements
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
class RateableElement < ActiveRecord::Base
|
|
2
|
+
has_many :ratings, :dependent => :destroy
|
|
3
|
+
belongs_to :element, :polymorphic => true
|
|
4
|
+
before_save :recalculate_avg_rating
|
|
5
|
+
|
|
6
|
+
validates_uniqueness_of :element_id, :scope => [:element_type, :element_attribute]
|
|
7
|
+
validates_associated :element
|
|
8
|
+
validates_presence_of :element_attribute
|
|
9
|
+
|
|
10
|
+
def average_rating
|
|
11
|
+
self.avg_rating
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def average_rating=(value)
|
|
15
|
+
self.avg_rating = value
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def <<(rating)
|
|
19
|
+
self.add_rating(rating)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_rating(rating)
|
|
23
|
+
return self.ratings unless rating.is_a?(Rating)
|
|
24
|
+
self.ratings << rating
|
|
25
|
+
self.total_rating += rating.value
|
|
26
|
+
self.ratings_count += 1
|
|
27
|
+
self.save
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add(value, rater = nil)
|
|
31
|
+
self.add_rating(Rating.new(:rater => rater, :value => value))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def remove_rating(rating)
|
|
35
|
+
return self.ratings unless rating.is_a?(Rating)
|
|
36
|
+
self.total_rating -= rating.value
|
|
37
|
+
self.ratings_count -= 1
|
|
38
|
+
rating.destroy
|
|
39
|
+
self.save
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def remove_by_rater(rater)
|
|
43
|
+
self.ratings.find_all_by_rater_id(rater.id, :conditions => {:rater_type => rater.class.name}).each do |rating|
|
|
44
|
+
self.remove_rating(rating)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def clear
|
|
49
|
+
self.ratings = []
|
|
50
|
+
self.recalculate_rating
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def remove_all
|
|
54
|
+
self.clear
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def rated_by?(rater)
|
|
58
|
+
not self.ratings.find_all_by_rater_id(rater.id, :conditions => {:rater_type => rater.class.name}).empty?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def recalculate_rating
|
|
62
|
+
self.recalculate_rating_without([])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def recalculate_rating_without(ratings)
|
|
66
|
+
return unless (ratings.is_a? Rating or ratings.is_a? Array)
|
|
67
|
+
|
|
68
|
+
self.total_rating = 0
|
|
69
|
+
self.ratings_count = 0
|
|
70
|
+
self.avg_rating = 0
|
|
71
|
+
ratings = self.ratings - [ratings].compact.flatten
|
|
72
|
+
ratings.each do |r|
|
|
73
|
+
self.total_rating += r.value
|
|
74
|
+
self.ratings_count += 1
|
|
75
|
+
end
|
|
76
|
+
self.save
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def recalculate_avg_rating
|
|
82
|
+
self.avg_rating = self.total_rating.to_f / self.ratings_count
|
|
83
|
+
self.avg_rating = 0 if self.avg_rating.nan?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class Rating < ActiveRecord::Base
|
|
2
|
+
belongs_to :rateable_element
|
|
3
|
+
belongs_to :rater, :polymorphic => true
|
|
4
|
+
|
|
5
|
+
validates_associated :rateable_element
|
|
6
|
+
validates_presence_of :value
|
|
7
|
+
|
|
8
|
+
after_destroy :update_rateable_element
|
|
9
|
+
after_update :update_rateable_element
|
|
10
|
+
|
|
11
|
+
def rated_element
|
|
12
|
+
self.rateable_element.element
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def rating_for
|
|
16
|
+
self.rateable_element.element_attribute
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def rating_for?(element)
|
|
20
|
+
element.to_s == self.rating_for
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def is_for
|
|
24
|
+
self.rating_for
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def is_for?(element)
|
|
28
|
+
self.rating_for?(element)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
protected
|
|
32
|
+
|
|
33
|
+
def update_rateable_element
|
|
34
|
+
self.rateable_element.recalculate_rating
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
data/lib/rating_for.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "rating_for/version"
|
|
2
|
+
require "rating_for/rateable_element"
|
|
3
|
+
require "rating_for/rating"
|
|
4
|
+
|
|
5
|
+
module RatingFor
|
|
6
|
+
|
|
7
|
+
def self.included(base)
|
|
8
|
+
base.extend(ClassMethods)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def rating_for(element, options = {})
|
|
13
|
+
has_one "rating_for_#{element}",
|
|
14
|
+
:dependent => :destroy,
|
|
15
|
+
:as => :element,
|
|
16
|
+
:conditions => {:element_attribute => element.to_s}
|
|
17
|
+
|
|
18
|
+
define_method "rating_for_#{element}" do
|
|
19
|
+
rateable_element = instance_variable_get("@_rating_for_#{element}")
|
|
20
|
+
|
|
21
|
+
if rateable_element.nil?
|
|
22
|
+
rateable_element = RateableElement.find_by_element_id(self.id, :conditions => {:element_type => self.class.name, :element_attribute => element.to_s})
|
|
23
|
+
rateable_element = RateableElement.new(:element_id => self.id, :element_type => self.class.name, :element_attribute => element.to_s) if rateable_element.nil?
|
|
24
|
+
instance_variable_set("@_rating_for_#{element}", rateable_element)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
rateable_element
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
(class << self; self; end).module_eval do
|
|
31
|
+
define_method "find_where_#{element}_has_average_rating_of" do |value|
|
|
32
|
+
self.find_with_average_rating_of(element.to_s, value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
(class << self; self; end).module_eval do
|
|
37
|
+
define_method "find_where_#{element}_has_total_rating_of" do |value|
|
|
38
|
+
self.find_with_average_rating_of(element.to_s, value)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
(class << self; self; end).module_eval do
|
|
43
|
+
define_method "find_where_#{element}_has_ratings_count_of" do |value|
|
|
44
|
+
self.find_with_average_rating_of(element.to_s, value)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
(class << self; self; end).module_eval do
|
|
49
|
+
define_method "find_where_#{element}_was_rated_by" do |rater|
|
|
50
|
+
self.find_rated_by(element.to_s, rater)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
extend RatingFor::SingletonMethods
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
module SingletonMethods
|
|
59
|
+
# TODO Check if this is DB-agnostic
|
|
60
|
+
def find_with_average_rating_of(element, value)
|
|
61
|
+
find :all, :conditions => "rateable_elements.element_attribute = \"#{element}\" AND rateable_elements.avg_rating = \"#{value}\"",
|
|
62
|
+
:joins => "INNER JOIN rateable_elements ON rateable_elements.element_id = #{self.name.underscore.pluralize}.id"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def find_with_total_rating_of(element, value)
|
|
66
|
+
find :all, :conditions => "rateable_elements.element_attribute = \"#{element}\" AND rateable_elements.total_rating = \"#{value}\"",
|
|
67
|
+
:joins => "INNER JOIN rateable_elements ON rateable_elements.element_id = #{self.name.underscore.pluralize}.id"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def find_with_ratings_count_of(element, value)
|
|
71
|
+
find :all, :conditions => "rateable_elements.element_attribute = \"#{element}\" AND rateable_elements.ratings_count = \"#{value}\"",
|
|
72
|
+
:joins => "INNER JOIN rateable_elements ON rateable_elements.element_id = #{self.name.underscore.pluralize}.id"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def find_rated_by(element, rater)
|
|
76
|
+
found_objects = []
|
|
77
|
+
Rating.find(:all, :conditions => "rater_id = #{rater.id} AND rater_type = \"#{rater.class}\" AND element_attribute = \"#{element}\"", :joins => [:rateable_element]).each do |rating|
|
|
78
|
+
found_objects << find(:first, :conditions => {:id => rating.rateable_element.element_id})
|
|
79
|
+
end
|
|
80
|
+
found_objects.compact
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if defined?(ActiveRecord::Base)
|
|
87
|
+
ActiveRecord::Base.send(:include, RatingFor)
|
|
88
|
+
end
|
data/rating_for.gemspec
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "rating_for/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "rating_for"
|
|
7
|
+
s.version = RatingFor::VERSION
|
|
8
|
+
s.authors = ["Frane Bandov"]
|
|
9
|
+
s.email = ["frane.bandov@gmail.com"]
|
|
10
|
+
s.homepage = ""
|
|
11
|
+
s.summary = %q{A context-based rating gem for Rails}
|
|
12
|
+
s.description = %q{This plugin allows you to add multiple different rating criteria to your ActiveRecord based model. It provides column-caching and a polymorphic association for raters.}
|
|
13
|
+
|
|
14
|
+
s.rubyforge_project = "rating_for"
|
|
15
|
+
|
|
16
|
+
s.files = `git ls-files`.split("\n")
|
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
|
+
s.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
s.add_dependency 'activerecord', '~> 3.0'
|
|
23
|
+
s.add_development_dependency 'sqlite3'
|
|
24
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class User < ActiveRecord::Base
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class Hotel < ActiveRecord::Base
|
|
7
|
+
rating_for :service
|
|
8
|
+
rating_for :quality
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Newspaper < ActiveRecord::Base
|
|
12
|
+
rating_for :reputation
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RatingForTest < ActiveRecord::TestCase
|
|
17
|
+
include ActiveRecord::TestFixtures
|
|
18
|
+
|
|
19
|
+
self.fixture_path = File.join(File.dirname(__FILE__), 'fixtures')
|
|
20
|
+
#self.fixture_table_names = ActiveRecord::Base.connection.tables
|
|
21
|
+
self.use_transactional_fixtures = true
|
|
22
|
+
fixtures :users, :hotels, :newspapers
|
|
23
|
+
|
|
24
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
25
|
+
|
|
26
|
+
test "add ratings" do
|
|
27
|
+
# init
|
|
28
|
+
hotel = Hotel.find(1)
|
|
29
|
+
user = User.find(1)
|
|
30
|
+
user2 = User.find(2)
|
|
31
|
+
newspaper = Newspaper.find(1)
|
|
32
|
+
|
|
33
|
+
# everything OK?
|
|
34
|
+
assert hotel.rating_for_quality
|
|
35
|
+
assert hotel.rating_for_service
|
|
36
|
+
assert newspaper.rating_for_reputation
|
|
37
|
+
|
|
38
|
+
# add a rating object
|
|
39
|
+
r = Rating.new(:rater => user, :value => 2)
|
|
40
|
+
hotel.rating_for_quality << r
|
|
41
|
+
assert_equal hotel.rating_for_quality.avg_rating, hotel.rating_for_quality.total_rating
|
|
42
|
+
assert_equal r.rated_element, hotel
|
|
43
|
+
assert_equal r.rating_for, "quality"
|
|
44
|
+
assert r.rating_for?(:quality)
|
|
45
|
+
|
|
46
|
+
# add a rating by value - preferred way
|
|
47
|
+
hotel.rating_for_quality.add(10, user2)
|
|
48
|
+
assert_equal hotel.rating_for_quality.avg_rating, 6
|
|
49
|
+
|
|
50
|
+
hotel.rating_for_quality.add(10)
|
|
51
|
+
assert_equal hotel.rating_for_quality.avg_rating.round, 7
|
|
52
|
+
|
|
53
|
+
assert_equal hotel.rating_for_quality.ratings_count, 3
|
|
54
|
+
assert hotel.rating_for_quality.rated_by?(user2)
|
|
55
|
+
|
|
56
|
+
# find by rating
|
|
57
|
+
hotel.rating_for_service.add(10, user)
|
|
58
|
+
assert_equal Hotel.find_where_service_has_average_rating_of(10).first, hotel
|
|
59
|
+
|
|
60
|
+
assert hotel.rating_for_service.rated_by?(user)
|
|
61
|
+
assert_equal hotel.rating_for_service.rated_by?(newspaper), false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
test "remove ratings" do
|
|
65
|
+
# init
|
|
66
|
+
hotel = Hotel.find(1)
|
|
67
|
+
user = User.find(1)
|
|
68
|
+
|
|
69
|
+
hotel.rating_for_quality.add(10)
|
|
70
|
+
hotel.rating_for_quality.add(5)
|
|
71
|
+
hotel.rating_for_quality.add(7)
|
|
72
|
+
hotel.rating_for_quality.add(2, user)
|
|
73
|
+
hotel.rating_for_quality.add(1)
|
|
74
|
+
|
|
75
|
+
hotel.rating_for_service.add(10)
|
|
76
|
+
|
|
77
|
+
avg_rating = hotel.rating_for_quality.avg_rating
|
|
78
|
+
|
|
79
|
+
# everything OK?
|
|
80
|
+
h = Hotel.find_where_quality_has_average_rating_of(avg_rating).first
|
|
81
|
+
assert_equal hotel.rating_for_quality.avg_rating, h.rating_for_quality.avg_rating
|
|
82
|
+
assert_equal hotel.id, h.id
|
|
83
|
+
|
|
84
|
+
r = Rating.find(1)
|
|
85
|
+
assert_equal 10, r.value
|
|
86
|
+
|
|
87
|
+
# remove a rating object by calling its destroy method - not recommended
|
|
88
|
+
r.destroy
|
|
89
|
+
new_avg_rating = (5 + 7 + 2 + 1).to_f / 4
|
|
90
|
+
assert new_avg_rating != avg_rating
|
|
91
|
+
assert new_avg_rating != h.rating_for_quality.avg_rating
|
|
92
|
+
assert_equal avg_rating, h.rating_for_quality.avg_rating
|
|
93
|
+
|
|
94
|
+
# the reference to the rating object is kept until the referring object is reloaded
|
|
95
|
+
h.rating_for_quality.reload
|
|
96
|
+
assert avg_rating != h.rating_for_quality.avg_rating
|
|
97
|
+
assert_equal new_avg_rating, h.rating_for_quality.avg_rating
|
|
98
|
+
|
|
99
|
+
assert hotel.rating_for_quality.avg_rating != new_avg_rating
|
|
100
|
+
assert_equal avg_rating, hotel.rating_for_quality.avg_rating
|
|
101
|
+
|
|
102
|
+
# new objects get their data fresh out of the database - this data is corect
|
|
103
|
+
h2 = Hotel.find(1)
|
|
104
|
+
assert_equal h.rating_for_quality.avg_rating, h2.rating_for_quality.avg_rating
|
|
105
|
+
assert hotel.rating_for_quality.avg_rating != h2.rating_for_quality.avg_rating
|
|
106
|
+
assert_equal new_avg_rating, h2.rating_for_quality.avg_rating
|
|
107
|
+
|
|
108
|
+
# instead of reloading we can racalculate without the destroyed object - not recommended
|
|
109
|
+
hotel.rating_for_quality.recalculate_rating_without(r)
|
|
110
|
+
assert hotel.rating_for_quality.avg_rating != avg_rating
|
|
111
|
+
assert_equal new_avg_rating, hotel.rating_for_quality.avg_rating
|
|
112
|
+
|
|
113
|
+
# preferred way: delete ratings through remove_rating
|
|
114
|
+
avg_rating = hotel.rating_for_quality.avg_rating
|
|
115
|
+
new_avg_rating = (7 + 2 + 1).to_r / 3
|
|
116
|
+
r = Rating.find(2)
|
|
117
|
+
|
|
118
|
+
hotel.rating_for_quality.remove_rating(r)
|
|
119
|
+
assert avg_rating != hotel.rating_for_quality.avg_rating
|
|
120
|
+
assert_equal new_avg_rating, hotel.rating_for_quality.avg_rating
|
|
121
|
+
|
|
122
|
+
# or through remove_by_rater
|
|
123
|
+
avg_rating = hotel.rating_for_quality.avg_rating
|
|
124
|
+
new_avg_rating = (7 + 1).to_r / 2
|
|
125
|
+
hotel.rating_for_quality.remove_by_rater user
|
|
126
|
+
assert avg_rating != hotel.rating_for_quality.avg_rating
|
|
127
|
+
assert_equal new_avg_rating, hotel.rating_for_quality.avg_rating
|
|
128
|
+
end
|
|
129
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
require 'active_record/test_case'
|
|
4
|
+
require 'active_record/fixtures'
|
|
5
|
+
|
|
6
|
+
require 'test/unit'
|
|
7
|
+
require File.dirname(__FILE__) + '/../init'
|
|
8
|
+
require File.dirname(__FILE__) + '/../lib/generators/rating_for/migration/templates/migration'
|
|
9
|
+
|
|
10
|
+
ActiveRecord::Base.establish_connection({
|
|
11
|
+
:adapter => 'sqlite3', :database => ':memory:'
|
|
12
|
+
})
|
|
13
|
+
# this is VERY important! during setting up fixtures there is checked if ActiveRecord::Base.configurations is not blank.
|
|
14
|
+
ActiveRecord::Base.configurations = {:test => true}
|
|
15
|
+
|
|
16
|
+
ActiveRecord::Schema.define do
|
|
17
|
+
create_table :users, :force => true do |t|
|
|
18
|
+
t.string :name
|
|
19
|
+
t.timestamps
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
create_table :hotels, :force => true do |t|
|
|
23
|
+
t.string :name
|
|
24
|
+
t.timestamps
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
create_table :newspapers, :force => true do |t|
|
|
28
|
+
t.string :name
|
|
29
|
+
t.timestamps
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
CreateRatingForTables.up
|
metadata
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rating_for
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: '0.2'
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Frane Bandov
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-02-23 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: activerecord
|
|
16
|
+
requirement: &70173126204620 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '3.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *70173126204620
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: sqlite3
|
|
27
|
+
requirement: &70173126201800 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ! '>='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *70173126201800
|
|
36
|
+
description: This plugin allows you to add multiple different rating criteria to your
|
|
37
|
+
ActiveRecord based model. It provides column-caching and a polymorphic association
|
|
38
|
+
for raters.
|
|
39
|
+
email:
|
|
40
|
+
- frane.bandov@gmail.com
|
|
41
|
+
executables: []
|
|
42
|
+
extensions: []
|
|
43
|
+
extra_rdoc_files: []
|
|
44
|
+
files:
|
|
45
|
+
- .gitignore
|
|
46
|
+
- Gemfile
|
|
47
|
+
- MIT-LICENSE
|
|
48
|
+
- README.md
|
|
49
|
+
- Rakefile
|
|
50
|
+
- lib/generators/rating_for/migration/migration_generator.rb
|
|
51
|
+
- lib/generators/rating_for/migration/templates/migration.rb
|
|
52
|
+
- lib/rating_for.rb
|
|
53
|
+
- lib/rating_for/rateable_element.rb
|
|
54
|
+
- lib/rating_for/rating.rb
|
|
55
|
+
- lib/rating_for/version.rb
|
|
56
|
+
- rating_for.gemspec
|
|
57
|
+
- test/fixtures/hotels.yml
|
|
58
|
+
- test/fixtures/newspapers.yml
|
|
59
|
+
- test/fixtures/users.yml
|
|
60
|
+
- test/rating_for_test.rb
|
|
61
|
+
- test/test_helper.rb
|
|
62
|
+
homepage: ''
|
|
63
|
+
licenses: []
|
|
64
|
+
post_install_message:
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
none: false
|
|
70
|
+
requirements:
|
|
71
|
+
- - ! '>='
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
|
+
none: false
|
|
76
|
+
requirements:
|
|
77
|
+
- - ! '>='
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '0'
|
|
80
|
+
requirements: []
|
|
81
|
+
rubyforge_project: rating_for
|
|
82
|
+
rubygems_version: 1.8.10
|
|
83
|
+
signing_key:
|
|
84
|
+
specification_version: 3
|
|
85
|
+
summary: A context-based rating gem for Rails
|
|
86
|
+
test_files:
|
|
87
|
+
- test/fixtures/hotels.yml
|
|
88
|
+
- test/fixtures/newspapers.yml
|
|
89
|
+
- test/fixtures/users.yml
|
|
90
|
+
- test/rating_for_test.rb
|
|
91
|
+
- test/test_helper.rb
|