├── .github └── FUNDING.yml ├── .gitignore ├── .rspec ├── .ruby-version ├── .travis.yml ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── TODO ├── VERSION ├── gemfiles ├── mongoid_3.gemfile ├── mongoid_3.gemfile.lock ├── mongoid_4.gemfile ├── mongoid_4.gemfile.lock ├── mongoid_5.gemfile ├── mongoid_5.gemfile.lock ├── mongoid_6.gemfile ├── mongoid_6.gemfile.lock ├── mongoid_7.gemfile ├── mongoid_7.gemfile.lock ├── mongoid_8.gemfile └── mongoid_8.gemfile.lock ├── lib ├── mongoid_rateable.rb └── mongoid_rateable │ ├── rateable.rb │ └── rating.rb ├── mongoid_rateable.gemspec └── spec ├── models ├── article.rb ├── comment.rb ├── post.rb └── user.rb ├── rateable_spec.rb ├── spec_helper.rb └── support └── database_cleaner.rb /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: _proton 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | *.gem 3 | /.bundle/ 4 | Gemfile*.lock -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.2.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | before_install: gem update bundler 3 | rvm: 4 | # - 2.0.0 5 | # - 2.1.0 6 | # - 2.2.2 7 | - 2.3.0 8 | - 2.4.0 9 | - 2.5.0 10 | - 2.6.0 11 | - 2.7.0 12 | - 3.0.0 13 | - 3.1.0 14 | - 3.2.0 15 | gemfile: 16 | # - gemfiles/mongoid_3.gemfile 17 | # - gemfiles/mongoid_4.gemfile 18 | # - gemfiles/mongoid_5.gemfile 19 | # - gemfiles/mongoid_6.gemfile 20 | - gemfiles/mongoid_7.gemfile 21 | # - gemfiles/mongoid_8.gemfile 22 | services: 23 | - mongodb 24 | sudo: false 25 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise 'mongoid-3' do 2 | gem 'mongoid', '3.1.7' 3 | end 4 | 5 | appraise 'mongoid-4' do 6 | gem 'mongoid', '4.0.2' 7 | end 8 | 9 | appraise 'mongoid-5' do 10 | gem 'mongoid', '5.2.0' 11 | end 12 | 13 | appraise 'mongoid-6' do 14 | gem 'mongoid', '6.1.0' 15 | end 16 | 17 | appraise 'mongoid-7' do 18 | gem 'mongoid', '7.5.2' 19 | end 20 | 21 | appraise 'mongoid-8' do 22 | gem 'mongoid', '8.0.3' 23 | end 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | *0.4.0* 4 | 5 | Add Mongoid 4,5,6 support 6 | 7 | *0.3.0* 8 | 9 | Removed RATING_RANGE constant. 10 | The RATING_RANGE constant is very inflexible. Much better with a class method, since it allows for reusability, definint the range in a module that is pulled into several classes etc. 11 | 12 | Instead this gem now includes several other config methods. -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'mongoid', '>= 3.0' 4 | 5 | group :development do 6 | gem 'bundler' 7 | gem 'jeweler', '~> 2.1.1' 8 | gem 'simplecov', '~> 0.7', require: false 9 | gem 'yard' 10 | gem 'rspec', '>= 3.0.0' 11 | gem 'appraisal' 12 | gem 'coveralls', require: false 13 | gem 'database_cleaner-mongoid', '~> 2.0', '>= 2.0.1' 14 | end 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Peter Savichev (proton) 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. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mongoid::Rateable 2 | 3 | Provides fields and methods for the **rating** manipulation on Mongoid 4 | documents 5 | 6 | [![travis CI](https://secure.travis-ci.org/proton/mongoid_rateable.png)](http://travis-ci.org/proton/mongoid_rateable) 7 | 8 | Lastest version of Mongoid:Rateable requires mongoid 3, 4, 5, 6, 7 and 8. 9 | 10 | If you need a mongoid 2 support, look at mongoid_rateable 0.1.7. 11 | 12 | ## Support us 13 | 14 | https://www.patreon.com/_proton 15 | 16 | ## Installation 17 | 18 | Add to Gemfile: 19 | 20 | gem 'mongoid_rateable' 21 | 22 | ## Getting Started 23 | 24 | Simply use the `rateable` macro from any class that is a Mongoid Document. 25 | 26 | This macro will include `Mongoid::Rateable` into the class and configure the 27 | rating functionality using the options hash. For any option not present, the 28 | default option value will be used. 29 | 30 | class Post 31 | include Mongoid::Document 32 | 33 | rateable range: (-5..7), raters: [User, Admin] 34 | end 35 | 36 | You can also set the `default_rater` 37 | 38 | class Post 39 | include Mongoid::Document 40 | 41 | # will simply call the 'owner' method to find the default rater 42 | # if no rater given when rating 43 | 44 | rateable range: (-5..7), raters: [User, Admin], default_rater: 'owner' 45 | end 46 | 47 | class Post 48 | include Mongoid::Document 49 | 50 | # if given a block, this will be used as a dynamic way to find 51 | # the a rater in case no rater is passed in as the 2nd argument to 52 | # the rate method 53 | 54 | rateable range: (-5..7), raters: [User, Admin] do 55 | # will by default be rated by the last user 56 | # who made a comment to this post! 57 | comments.last.user 58 | end 59 | end 60 | 61 | Note: For even more control over the configuration, see the `ClassMethods` 62 | module code in `rateable.rb`. 63 | 64 | ## Cast Rates 65 | 66 | You can rate by passing an integer and a rater model to the "rate" method: 67 | 68 | @post = Post.first 69 | @user = User.where(:name => 'Bill') # or more likely, current_user 70 | 71 | @post.rate 1, @user # I like this! 72 | @post.rate -1, @user # I don't like this! 73 | @post.rate 5, @user # I LOVE this! 74 | @post.rate -10, @user # Delete it from the Internet! 75 | 76 | # Many users love this! 77 | @post.rate 5, @users # They LOVIN' it! 78 | 79 | Rates have weight (1 by default) 80 | 81 | @post.rate 5, @user, 3 # Rate @post with weight 3 (@user has high karma) 82 | @post.rate 3, @user, 1 # Rate @post with weight 1 (@user has low karma) 83 | 84 | You can unrate by using "unrate" method: 85 | 86 | @post.unrate @user 87 | 88 | And don't forget to save rateable object: 89 | 90 | @post.save 91 | 92 | Sure, you can rate and save in one function: 93 | 94 | @post.rate_and_save(3, @user) 95 | @post.unrate_and_save(@user) 96 | 97 | ## Additional Functionality 98 | 99 | You'll often want to know if a user already rated post. Simple: 100 | 101 | @post.rated_by? @user # True if it rated by user 102 | 103 | And if someone rated it: 104 | 105 | @post.rated? # True if it rated by someone 106 | 107 | You can get user mark: 108 | 109 | @post.user_mark(@user) # Mark or nil (if not rated by user) 110 | 111 | Or marks: 112 | 113 | @post.user_marks([@user1, @user2]) # Hash {user.id => mark} 114 | 115 | You can also get a tally of the number of rates cast: 116 | 117 | @post.rate_count # Just one so far! 118 | 119 | You can get a total weight of post rates: 120 | 121 | @post.rate_weight # Just one so far! 122 | 123 | And you can get the average rating: 124 | 125 | @post.rating # rates / rate_weight 126 | 127 | And you can get the average rating without weights (It calculates realtime, so 128 | it can be slow): 129 | 130 | @post.unweighted_rating # rates without weights / rate_count 131 | 132 | And you can get the previous rating and delta: 133 | 134 | @post.previous_rating 135 | @post.rating_delta # rating - previous_rating 136 | 137 | ## Scopes 138 | 139 | You can get rated or unrated posts: 140 | 141 | Post.rated 142 | Post.unrated 143 | 144 | You can get posts rated by someone: 145 | 146 | Post.rated_by(@user) 147 | 148 | You can get posts with some rating: 149 | 150 | Post.with_rating(2..5) 151 | Post.with_rating(0..10) 152 | Post.with_rating(-2..2) 153 | 154 | You can get most rated and highest rated posts: (Sorry, this method doesn't 155 | work with embedded documents) 156 | 157 | Post.highest_rated # 10 (or less) highest rated posts 158 | Post.highest_rated(5) # 5 (or less) highest rated posts 159 | 160 | ## Contributing to Mongoid::Rateable 161 | 162 | * Check out the latest master to make sure the feature hasn't been 163 | implemented or the bug hasn't been fixed yet 164 | * Check out the issue tracker to make sure someone already hasn't requested 165 | it and/or contributed it 166 | * Fork the project 167 | * Start a feature/bugfix branch 168 | * Commit and push until you are happy with your contribution 169 | * Make sure to add tests for it. This is important so I don't break it in a 170 | future version unintentionally. 171 | * Please try not to mess with the Rakefile, version, or history. If you want 172 | to have your own version, or is otherwise necessary, that is fine, but 173 | please isolate to its own commit so I can cherry-pick around it. 174 | 175 | 176 | ## Copyright 177 | 178 | Copyright (c) 2011-2023 Petr Savichev (proton). See LICENSE.txt for further 179 | details. 180 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'rubygems' 4 | require 'rake' 5 | require 'bundler' 6 | Bundler::GemHelper.install_tasks 7 | 8 | begin 9 | Bundler.setup(:default, :development) 10 | rescue Bundler::BundlerError => e 11 | $stderr.puts e.message 12 | $stderr.puts "Run `bundle install` to install missing gems" 13 | exit e.status_code 14 | end 15 | 16 | require 'jeweler' 17 | Jeweler::Tasks.new do |gem| 18 | # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options 19 | gem.name = "mongoid_rateable" 20 | gem.homepage = "http://github.com/proton/mongoid_rateable" 21 | gem.license = "MIT" 22 | gem.summary = %q{Rating functionality for Mongoid documents} 23 | gem.description = %q{Provides fields and methods for the rating manipulation on Mongoid documents.} 24 | gem.email = "psavichev@gmail.com" 25 | gem.authors = ["Petr Savichev (proton)"] 26 | # dependencies defined in Gemfile 27 | end 28 | Jeweler::RubygemsDotOrgTasks.new 29 | 30 | require 'rspec/core/rake_task' 31 | 32 | RSpec::Core::RakeTask.new(:spec) do |spec| 33 | spec.pattern = 'spec/**/*_spec.rb' 34 | end 35 | 36 | task :default => :spec 37 | 38 | require 'rdoc/task' 39 | RDoc::Task.new do |rdoc| 40 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 41 | 42 | rdoc.rdoc_dir = 'rdoc' 43 | rdoc.title = "mongoid_rateable #{version}" 44 | rdoc.rdoc_files.include('README*') 45 | rdoc.rdoc_files.include('lib/**/*.rb') 46 | end 47 | 48 | desc "Build gem" 49 | task :build do 50 | puts "Regenerating gemspec" 51 | system "rake gemspec" 52 | puts "Building" 53 | system "gem build mongoid_rateable.gemspec" 54 | end 55 | 56 | desc "Release gem" 57 | task :release => :build do 58 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 59 | end 60 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | improve speed (http://cookbook.mongodb.org/patterns/votes/) 2 | add random values to specs 3 | add task for rating update -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.4.1 2 | -------------------------------------------------------------------------------- /gemfiles/mongoid_3.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "mongoid", "3.1.7" 6 | 7 | group :development do 8 | gem "bundler" 9 | gem "jeweler", "~> 2.1.1" 10 | gem "simplecov", "~> 0.7", require: false 11 | gem "yard" 12 | gem "rspec", ">= 3.0.0" 13 | gem "appraisal" 14 | gem "coveralls", require: false 15 | gem "database_cleaner-mongoid", "~> 2.0", ">= 2.0.1" 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/mongoid_3.gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activemodel (3.2.22.5) 5 | activesupport (= 3.2.22.5) 6 | builder (~> 3.0.0) 7 | activesupport (3.2.22.5) 8 | i18n (~> 0.6, >= 0.6.4) 9 | multi_json (~> 1.0) 10 | addressable (2.5.0) 11 | public_suffix (~> 2.0, >= 2.0.2) 12 | appraisal (2.1.0) 13 | bundler 14 | rake 15 | thor (>= 0.14.0) 16 | builder (3.0.4) 17 | coveralls (0.8.19) 18 | json (>= 1.8, < 3) 19 | simplecov (~> 0.12.0) 20 | term-ansicolor (~> 1.3) 21 | thor (~> 0.19.1) 22 | tins (~> 1.6) 23 | database_cleaner (1.5.3) 24 | descendants_tracker (0.0.4) 25 | thread_safe (~> 0.3, >= 0.3.1) 26 | diff-lcs (1.3) 27 | docile (1.1.5) 28 | faraday (0.9.2) 29 | multipart-post (>= 1.2, < 3) 30 | git (1.3.0) 31 | github_api (0.11.3) 32 | addressable (~> 2.3) 33 | descendants_tracker (~> 0.0.1) 34 | faraday (~> 0.8, < 0.10) 35 | hashie (>= 1.2) 36 | multi_json (>= 1.7.5, < 2.0) 37 | nokogiri (~> 1.6.0) 38 | oauth2 39 | hashie (3.4.6) 40 | highline (1.7.8) 41 | i18n (0.7.0) 42 | jeweler (2.1.2) 43 | builder 44 | bundler (>= 1.0) 45 | git (>= 1.2.5) 46 | github_api (~> 0.11.0) 47 | highline (>= 1.6.15) 48 | nokogiri (>= 1.5.10) 49 | rake 50 | rdoc 51 | semver 52 | json (2.0.3) 53 | jwt (1.5.6) 54 | mini_portile2 (2.1.0) 55 | mongoid (3.1.7) 56 | activemodel (~> 3.2) 57 | moped (~> 1.4) 58 | origin (~> 1.0) 59 | tzinfo (~> 0.3.29) 60 | moped (1.5.3) 61 | multi_json (1.12.1) 62 | multi_xml (0.6.0) 63 | multipart-post (2.0.0) 64 | nokogiri (1.6.8.1) 65 | mini_portile2 (~> 2.1.0) 66 | oauth2 (1.3.0) 67 | faraday (>= 0.8, < 0.11) 68 | jwt (~> 1.0) 69 | multi_json (~> 1.3) 70 | multi_xml (~> 0.5) 71 | rack (>= 1.2, < 3) 72 | origin (1.1.0) 73 | public_suffix (2.0.5) 74 | rack (2.0.1) 75 | rake (12.0.0) 76 | rdoc (5.0.0) 77 | rspec (3.5.0) 78 | rspec-core (~> 3.5.0) 79 | rspec-expectations (~> 3.5.0) 80 | rspec-mocks (~> 3.5.0) 81 | rspec-core (3.5.4) 82 | rspec-support (~> 3.5.0) 83 | rspec-expectations (3.5.0) 84 | diff-lcs (>= 1.2.0, < 2.0) 85 | rspec-support (~> 3.5.0) 86 | rspec-mocks (3.5.0) 87 | diff-lcs (>= 1.2.0, < 2.0) 88 | rspec-support (~> 3.5.0) 89 | rspec-support (3.5.0) 90 | semver (1.0.1) 91 | simplecov (0.12.0) 92 | docile (~> 1.1.0) 93 | json (>= 1.8, < 3) 94 | simplecov-html (~> 0.10.0) 95 | simplecov-html (0.10.0) 96 | term-ansicolor (1.4.0) 97 | tins (~> 1.0) 98 | thor (0.19.4) 99 | thread_safe (0.3.5) 100 | tins (1.13.0) 101 | tzinfo (0.3.52) 102 | yard (0.9.8) 103 | 104 | PLATFORMS 105 | ruby 106 | 107 | DEPENDENCIES 108 | appraisal 109 | bundler 110 | coveralls 111 | database_cleaner (>= 0.8.0) 112 | jeweler (~> 2.1.1) 113 | mongoid (= 3.1.7) 114 | rspec (>= 3.0.0) 115 | simplecov (~> 0.7) 116 | yard -------------------------------------------------------------------------------- /gemfiles/mongoid_4.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "mongoid", "4.0.2" 6 | 7 | group :development do 8 | gem "bundler" 9 | gem "jeweler", "~> 2.1.1" 10 | gem "simplecov", "~> 0.7", require: false 11 | gem "yard" 12 | gem "rspec", ">= 3.0.0" 13 | gem "appraisal" 14 | gem "coveralls", require: false 15 | gem "database_cleaner-mongoid", "~> 2.0", ">= 2.0.1" 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/mongoid_4.gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activemodel (4.2.7.1) 5 | activesupport (= 4.2.7.1) 6 | builder (~> 3.1) 7 | activesupport (4.2.7.1) 8 | i18n (~> 0.7) 9 | json (~> 1.7, >= 1.7.7) 10 | minitest (~> 5.1) 11 | thread_safe (~> 0.3, >= 0.3.4) 12 | tzinfo (~> 1.1) 13 | addressable (2.5.0) 14 | public_suffix (~> 2.0, >= 2.0.2) 15 | appraisal (2.1.0) 16 | bundler 17 | rake 18 | thor (>= 0.14.0) 19 | bson (3.2.6) 20 | builder (3.2.3) 21 | connection_pool (2.2.1) 22 | coveralls (0.8.19) 23 | json (>= 1.8, < 3) 24 | simplecov (~> 0.12.0) 25 | term-ansicolor (~> 1.3) 26 | thor (~> 0.19.1) 27 | tins (~> 1.6) 28 | database_cleaner (1.5.3) 29 | descendants_tracker (0.0.4) 30 | thread_safe (~> 0.3, >= 0.3.1) 31 | diff-lcs (1.3) 32 | docile (1.1.5) 33 | faraday (0.9.2) 34 | multipart-post (>= 1.2, < 3) 35 | git (1.3.0) 36 | github_api (0.11.3) 37 | addressable (~> 2.3) 38 | descendants_tracker (~> 0.0.1) 39 | faraday (~> 0.8, < 0.10) 40 | hashie (>= 1.2) 41 | multi_json (>= 1.7.5, < 2.0) 42 | nokogiri (~> 1.6.0) 43 | oauth2 44 | hashie (3.4.6) 45 | highline (1.7.8) 46 | i18n (0.7.0) 47 | jeweler (2.1.2) 48 | builder 49 | bundler (>= 1.0) 50 | git (>= 1.2.5) 51 | github_api (~> 0.11.0) 52 | highline (>= 1.6.15) 53 | nokogiri (>= 1.5.10) 54 | rake 55 | rdoc 56 | semver 57 | json (1.8.6) 58 | jwt (1.5.6) 59 | mini_portile2 (2.1.0) 60 | minitest (5.10.1) 61 | mongoid (4.0.2) 62 | activemodel (~> 4.0) 63 | moped (~> 2.0.0) 64 | origin (~> 2.1) 65 | tzinfo (>= 0.3.37) 66 | moped (2.0.7) 67 | bson (~> 3.0) 68 | connection_pool (~> 2.0) 69 | optionable (~> 0.2.0) 70 | multi_json (1.12.1) 71 | multi_xml (0.6.0) 72 | multipart-post (2.0.0) 73 | nokogiri (1.6.8.1) 74 | mini_portile2 (~> 2.1.0) 75 | oauth2 (1.3.0) 76 | faraday (>= 0.8, < 0.11) 77 | jwt (~> 1.0) 78 | multi_json (~> 1.3) 79 | multi_xml (~> 0.5) 80 | rack (>= 1.2, < 3) 81 | optionable (0.2.0) 82 | origin (2.3.0) 83 | public_suffix (2.0.5) 84 | rack (2.0.1) 85 | rake (12.0.0) 86 | rdoc (5.0.0) 87 | rspec (3.5.0) 88 | rspec-core (~> 3.5.0) 89 | rspec-expectations (~> 3.5.0) 90 | rspec-mocks (~> 3.5.0) 91 | rspec-core (3.5.4) 92 | rspec-support (~> 3.5.0) 93 | rspec-expectations (3.5.0) 94 | diff-lcs (>= 1.2.0, < 2.0) 95 | rspec-support (~> 3.5.0) 96 | rspec-mocks (3.5.0) 97 | diff-lcs (>= 1.2.0, < 2.0) 98 | rspec-support (~> 3.5.0) 99 | rspec-support (3.5.0) 100 | semver (1.0.1) 101 | simplecov (0.12.0) 102 | docile (~> 1.1.0) 103 | json (>= 1.8, < 3) 104 | simplecov-html (~> 0.10.0) 105 | simplecov-html (0.10.0) 106 | term-ansicolor (1.4.0) 107 | tins (~> 1.0) 108 | thor (0.19.4) 109 | thread_safe (0.3.5) 110 | tins (1.13.0) 111 | tzinfo (1.2.2) 112 | thread_safe (~> 0.1) 113 | yard (0.9.8) 114 | 115 | PLATFORMS 116 | ruby 117 | 118 | DEPENDENCIES 119 | appraisal 120 | bundler 121 | coveralls 122 | database_cleaner (>= 0.8.0) 123 | jeweler (~> 2.1.1) 124 | mongoid (= 4.0.2) 125 | rspec (>= 3.0.0) 126 | simplecov (~> 0.7) 127 | yard -------------------------------------------------------------------------------- /gemfiles/mongoid_5.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "mongoid", "5.2.0" 6 | 7 | group :development do 8 | gem "bundler" 9 | gem "jeweler", "~> 2.1.1" 10 | gem "simplecov", "~> 0.7", require: false 11 | gem "yard" 12 | gem "rspec", ">= 3.0.0" 13 | gem "appraisal" 14 | gem "coveralls", require: false 15 | gem "database_cleaner-mongoid", "~> 2.0", ">= 2.0.1" 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/mongoid_5.gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activemodel (4.2.11.1) 5 | activesupport (= 4.2.11.1) 6 | builder (~> 3.1) 7 | activesupport (4.2.11.1) 8 | i18n (~> 0.7) 9 | minitest (~> 5.1) 10 | thread_safe (~> 0.3, >= 0.3.4) 11 | tzinfo (~> 1.1) 12 | addressable (2.7.0) 13 | public_suffix (>= 2.0.2, < 5.0) 14 | appraisal (2.2.0) 15 | bundler 16 | rake 17 | thor (>= 0.14.0) 18 | bson (4.7.0) 19 | builder (3.2.4) 20 | concurrent-ruby (1.1.5) 21 | coveralls (0.7.2) 22 | multi_json (~> 1.3) 23 | rest-client (= 1.6.7) 24 | simplecov (>= 0.7) 25 | term-ansicolor (= 1.2.2) 26 | thor (= 0.18.1) 27 | database_cleaner (1.7.0) 28 | descendants_tracker (0.0.4) 29 | thread_safe (~> 0.3, >= 0.3.1) 30 | diff-lcs (1.3) 31 | docile (1.3.2) 32 | faraday (0.9.2) 33 | multipart-post (>= 1.2, < 3) 34 | git (1.5.0) 35 | github_api (0.11.3) 36 | addressable (~> 2.3) 37 | descendants_tracker (~> 0.0.1) 38 | faraday (~> 0.8, < 0.10) 39 | hashie (>= 1.2) 40 | multi_json (>= 1.7.5, < 2.0) 41 | nokogiri (~> 1.6.0) 42 | oauth2 43 | hashie (4.0.0) 44 | highline (2.0.3) 45 | i18n (0.9.5) 46 | concurrent-ruby (~> 1.0) 47 | jeweler (2.1.2) 48 | builder 49 | bundler (>= 1.0) 50 | git (>= 1.2.5) 51 | github_api (~> 0.11.0) 52 | highline (>= 1.6.15) 53 | nokogiri (>= 1.5.10) 54 | rake 55 | rdoc 56 | semver 57 | json (2.3.0) 58 | jwt (2.2.1) 59 | mime-types (3.3) 60 | mime-types-data (~> 3.2015) 61 | mime-types-data (3.2019.1009) 62 | mini_portile2 (2.1.0) 63 | minitest (5.13.0) 64 | mongo (2.11.2) 65 | bson (>= 4.6.0, < 5.0.0) 66 | mongoid (5.2.0) 67 | activemodel (~> 4.0) 68 | mongo (>= 2.4.1, < 3.0.0) 69 | origin (~> 2.3) 70 | tzinfo (>= 0.3.37) 71 | multi_json (1.14.1) 72 | multi_xml (0.6.0) 73 | multipart-post (2.1.1) 74 | nokogiri (1.6.8.1) 75 | mini_portile2 (~> 2.1.0) 76 | oauth2 (1.4.2) 77 | faraday (>= 0.8, < 2.0) 78 | jwt (>= 1.0, < 3.0) 79 | multi_json (~> 1.3) 80 | multi_xml (~> 0.5) 81 | rack (>= 1.2, < 3) 82 | origin (2.3.1) 83 | public_suffix (4.0.1) 84 | rack (2.0.8) 85 | rake (13.0.1) 86 | rdoc (6.2.0) 87 | rest-client (1.6.7) 88 | mime-types (>= 1.16) 89 | rspec (3.9.0) 90 | rspec-core (~> 3.9.0) 91 | rspec-expectations (~> 3.9.0) 92 | rspec-mocks (~> 3.9.0) 93 | rspec-core (3.9.0) 94 | rspec-support (~> 3.9.0) 95 | rspec-expectations (3.9.0) 96 | diff-lcs (>= 1.2.0, < 2.0) 97 | rspec-support (~> 3.9.0) 98 | rspec-mocks (3.9.0) 99 | diff-lcs (>= 1.2.0, < 2.0) 100 | rspec-support (~> 3.9.0) 101 | rspec-support (3.9.0) 102 | semver (1.0.1) 103 | simplecov (0.17.1) 104 | docile (~> 1.1) 105 | json (>= 1.8, < 3) 106 | simplecov-html (~> 0.10.0) 107 | simplecov-html (0.10.2) 108 | term-ansicolor (1.2.2) 109 | tins (~> 0.8) 110 | thor (0.18.1) 111 | thread_safe (0.3.6) 112 | tins (0.13.2) 113 | tzinfo (1.2.5) 114 | thread_safe (~> 0.1) 115 | yard (0.9.20) 116 | 117 | PLATFORMS 118 | ruby 119 | 120 | DEPENDENCIES 121 | appraisal 122 | bundler 123 | coveralls 124 | database_cleaner (>= 0.8.0) 125 | jeweler (~> 2.1.1) 126 | mongoid (= 5.2.0) 127 | rspec (>= 3.0.0) 128 | simplecov (~> 0.7) 129 | yard -------------------------------------------------------------------------------- /gemfiles/mongoid_6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "mongoid", "6.1.0" 6 | 7 | group :development do 8 | gem "bundler" 9 | gem "jeweler", "~> 2.1.1" 10 | gem "simplecov", "~> 0.7", require: false 11 | gem "yard" 12 | gem "rspec", ">= 3.0.0" 13 | gem "appraisal" 14 | gem "coveralls", require: false 15 | gem "database_cleaner-mongoid", "~> 2.0", ">= 2.0.1" 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/mongoid_6.gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activemodel (5.2.4.1) 5 | activesupport (= 5.2.4.1) 6 | activesupport (5.2.4.1) 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | i18n (>= 0.7, < 2) 9 | minitest (~> 5.1) 10 | tzinfo (~> 1.1) 11 | addressable (2.7.0) 12 | public_suffix (>= 2.0.2, < 5.0) 13 | appraisal (2.2.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | bson (4.7.0) 18 | builder (3.2.4) 19 | concurrent-ruby (1.1.5) 20 | coveralls (0.7.2) 21 | multi_json (~> 1.3) 22 | rest-client (= 1.6.7) 23 | simplecov (>= 0.7) 24 | term-ansicolor (= 1.2.2) 25 | thor (= 0.18.1) 26 | database_cleaner (1.7.0) 27 | descendants_tracker (0.0.4) 28 | thread_safe (~> 0.3, >= 0.3.1) 29 | diff-lcs (1.3) 30 | docile (1.3.2) 31 | faraday (0.9.2) 32 | multipart-post (>= 1.2, < 3) 33 | git (1.5.0) 34 | github_api (0.11.3) 35 | addressable (~> 2.3) 36 | descendants_tracker (~> 0.0.1) 37 | faraday (~> 0.8, < 0.10) 38 | hashie (>= 1.2) 39 | multi_json (>= 1.7.5, < 2.0) 40 | nokogiri (~> 1.6.0) 41 | oauth2 42 | hashie (4.0.0) 43 | highline (2.0.3) 44 | i18n (1.7.0) 45 | concurrent-ruby (~> 1.0) 46 | jeweler (2.1.2) 47 | builder 48 | bundler (>= 1.0) 49 | git (>= 1.2.5) 50 | github_api (~> 0.11.0) 51 | highline (>= 1.6.15) 52 | nokogiri (>= 1.5.10) 53 | rake 54 | rdoc 55 | semver 56 | json (2.3.0) 57 | jwt (2.2.1) 58 | mime-types (3.3) 59 | mime-types-data (~> 3.2015) 60 | mime-types-data (3.2019.1009) 61 | mini_portile2 (2.1.0) 62 | minitest (5.13.0) 63 | mongo (2.11.2) 64 | bson (>= 4.6.0, < 5.0.0) 65 | mongoid (6.1.0) 66 | activemodel (~> 5.0) 67 | mongo (>= 2.4.1, < 3.0.0) 68 | multi_json (1.14.1) 69 | multi_xml (0.6.0) 70 | multipart-post (2.1.1) 71 | nokogiri (1.6.8.1) 72 | mini_portile2 (~> 2.1.0) 73 | oauth2 (1.4.2) 74 | faraday (>= 0.8, < 2.0) 75 | jwt (>= 1.0, < 3.0) 76 | multi_json (~> 1.3) 77 | multi_xml (~> 0.5) 78 | rack (>= 1.2, < 3) 79 | public_suffix (4.0.1) 80 | rack (2.0.8) 81 | rake (13.0.1) 82 | rdoc (6.2.0) 83 | rest-client (1.6.7) 84 | mime-types (>= 1.16) 85 | rspec (3.9.0) 86 | rspec-core (~> 3.9.0) 87 | rspec-expectations (~> 3.9.0) 88 | rspec-mocks (~> 3.9.0) 89 | rspec-core (3.9.0) 90 | rspec-support (~> 3.9.0) 91 | rspec-expectations (3.9.0) 92 | diff-lcs (>= 1.2.0, < 2.0) 93 | rspec-support (~> 3.9.0) 94 | rspec-mocks (3.9.0) 95 | diff-lcs (>= 1.2.0, < 2.0) 96 | rspec-support (~> 3.9.0) 97 | rspec-support (3.9.0) 98 | semver (1.0.1) 99 | simplecov (0.17.1) 100 | docile (~> 1.1) 101 | json (>= 1.8, < 3) 102 | simplecov-html (~> 0.10.0) 103 | simplecov-html (0.10.2) 104 | term-ansicolor (1.2.2) 105 | tins (~> 0.8) 106 | thor (0.18.1) 107 | thread_safe (0.3.6) 108 | tins (0.13.2) 109 | tzinfo (1.2.5) 110 | thread_safe (~> 0.1) 111 | yard (0.9.20) 112 | 113 | PLATFORMS 114 | ruby 115 | 116 | DEPENDENCIES 117 | appraisal 118 | bundler 119 | coveralls 120 | database_cleaner (>= 0.8.0) 121 | jeweler (~> 2.1.1) 122 | mongoid (= 6.1.0) 123 | rspec (>= 3.0.0) 124 | simplecov (~> 0.7) 125 | yard -------------------------------------------------------------------------------- /gemfiles/mongoid_7.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "mongoid", "7.5.2" 6 | 7 | group :development do 8 | gem "bundler" 9 | gem "jeweler", "~> 2.1.1" 10 | gem "simplecov", "~> 0.7", require: false 11 | gem "yard" 12 | gem "rspec", ">= 3.0.0" 13 | gem "appraisal" 14 | gem "coveralls", require: false 15 | gem "database_cleaner-mongoid", "~> 2.0", ">= 2.0.1" 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/mongoid_7.gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activemodel (5.2.4.1) 5 | activesupport (= 5.2.4.1) 6 | activesupport (5.2.4.1) 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | i18n (>= 0.7, < 2) 9 | minitest (~> 5.1) 10 | tzinfo (~> 1.1) 11 | addressable (2.7.0) 12 | public_suffix (>= 2.0.2, < 5.0) 13 | appraisal (2.2.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | bson (4.7.0) 18 | builder (3.2.4) 19 | concurrent-ruby (1.1.5) 20 | coveralls (0.7.2) 21 | multi_json (~> 1.3) 22 | rest-client (= 1.6.7) 23 | simplecov (>= 0.7) 24 | term-ansicolor (= 1.2.2) 25 | thor (= 0.18.1) 26 | database_cleaner (1.7.0) 27 | descendants_tracker (0.0.4) 28 | thread_safe (~> 0.3, >= 0.3.1) 29 | diff-lcs (1.3) 30 | docile (1.3.2) 31 | faraday (0.9.2) 32 | multipart-post (>= 1.2, < 3) 33 | git (1.5.0) 34 | github_api (0.11.3) 35 | addressable (~> 2.3) 36 | descendants_tracker (~> 0.0.1) 37 | faraday (~> 0.8, < 0.10) 38 | hashie (>= 1.2) 39 | multi_json (>= 1.7.5, < 2.0) 40 | nokogiri (~> 1.6.0) 41 | oauth2 42 | hashie (4.0.0) 43 | highline (2.0.3) 44 | i18n (1.7.0) 45 | concurrent-ruby (~> 1.0) 46 | jeweler (2.1.2) 47 | builder 48 | bundler (>= 1.0) 49 | git (>= 1.2.5) 50 | github_api (~> 0.11.0) 51 | highline (>= 1.6.15) 52 | nokogiri (>= 1.5.10) 53 | rake 54 | rdoc 55 | semver 56 | json (2.3.0) 57 | jwt (2.2.1) 58 | mime-types (3.3) 59 | mime-types-data (~> 3.2015) 60 | mime-types-data (3.2019.1009) 61 | mini_portile2 (2.1.0) 62 | minitest (5.13.0) 63 | mongo (2.11.2) 64 | bson (>= 4.6.0, < 5.0.0) 65 | mongoid (7.0.0) 66 | activemodel (>= 5.1, < 6.0.0) 67 | mongo (>= 2.5.1, < 3.0.0) 68 | multi_json (1.14.1) 69 | multi_xml (0.6.0) 70 | multipart-post (2.1.1) 71 | nokogiri (1.6.8.1) 72 | mini_portile2 (~> 2.1.0) 73 | oauth2 (1.4.2) 74 | faraday (>= 0.8, < 2.0) 75 | jwt (>= 1.0, < 3.0) 76 | multi_json (~> 1.3) 77 | multi_xml (~> 0.5) 78 | rack (>= 1.2, < 3) 79 | public_suffix (4.0.1) 80 | rack (2.0.8) 81 | rake (13.0.1) 82 | rdoc (6.2.0) 83 | rest-client (1.6.7) 84 | mime-types (>= 1.16) 85 | rspec (3.9.0) 86 | rspec-core (~> 3.9.0) 87 | rspec-expectations (~> 3.9.0) 88 | rspec-mocks (~> 3.9.0) 89 | rspec-core (3.9.0) 90 | rspec-support (~> 3.9.0) 91 | rspec-expectations (3.9.0) 92 | diff-lcs (>= 1.2.0, < 2.0) 93 | rspec-support (~> 3.9.0) 94 | rspec-mocks (3.9.0) 95 | diff-lcs (>= 1.2.0, < 2.0) 96 | rspec-support (~> 3.9.0) 97 | rspec-support (3.9.0) 98 | semver (1.0.1) 99 | simplecov (0.17.1) 100 | docile (~> 1.1) 101 | json (>= 1.8, < 3) 102 | simplecov-html (~> 0.10.0) 103 | simplecov-html (0.10.2) 104 | term-ansicolor (1.2.2) 105 | tins (~> 0.8) 106 | thor (0.18.1) 107 | thread_safe (0.3.6) 108 | tins (0.13.2) 109 | tzinfo (1.2.5) 110 | thread_safe (~> 0.1) 111 | yard (0.9.20) 112 | 113 | PLATFORMS 114 | ruby 115 | 116 | DEPENDENCIES 117 | appraisal 118 | bundler 119 | coveralls 120 | database_cleaner (>= 0.8.0) 121 | jeweler (~> 2.1.1) 122 | mongoid (= 7.0.0) 123 | rspec (>= 3.0.0) 124 | simplecov (~> 0.7) 125 | yard -------------------------------------------------------------------------------- /gemfiles/mongoid_8.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "mongoid", "8.0.3" 6 | 7 | group :development do 8 | gem "bundler" 9 | gem "jeweler", "~> 2.1.1" 10 | gem "simplecov", "~> 0.7", require: false 11 | gem "yard" 12 | gem "rspec", ">= 3.0.0" 13 | gem "appraisal" 14 | gem "coveralls", require: false 15 | gem "database_cleaner-mongoid", "~> 2.0", ">= 2.0.1" 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/mongoid_8.gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activemodel (7.0.4.2) 5 | activesupport (= 7.0.4.2) 6 | activesupport (7.0.4.2) 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | i18n (>= 1.6, < 2) 9 | minitest (>= 5.1) 10 | tzinfo (~> 2.0) 11 | addressable (2.8.1) 12 | public_suffix (>= 2.0.2, < 6.0) 13 | appraisal (2.4.1) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | bson (4.15.0) 18 | builder (3.2.4) 19 | concurrent-ruby (1.2.2) 20 | coveralls (0.8.23) 21 | json (>= 1.8, < 3) 22 | simplecov (~> 0.16.1) 23 | term-ansicolor (~> 1.3) 24 | thor (>= 0.19.4, < 2.0) 25 | tins (~> 1.6) 26 | database_cleaner-core (2.0.1) 27 | database_cleaner-mongoid (2.0.1) 28 | database_cleaner-core (~> 2.0.0) 29 | mongoid 30 | descendants_tracker (0.0.4) 31 | thread_safe (~> 0.3, >= 0.3.1) 32 | diff-lcs (1.5.0) 33 | docile (1.4.0) 34 | faraday (0.9.2) 35 | multipart-post (>= 1.2, < 3) 36 | git (1.17.1) 37 | addressable (~> 2.8) 38 | rchardet (~> 1.8) 39 | github_api (0.11.3) 40 | addressable (~> 2.3) 41 | descendants_tracker (~> 0.0.1) 42 | faraday (~> 0.8, < 0.10) 43 | hashie (>= 1.2) 44 | multi_json (>= 1.7.5, < 2.0) 45 | nokogiri (~> 1.6.0) 46 | oauth2 47 | hashie (5.0.0) 48 | highline (2.1.0) 49 | i18n (1.12.0) 50 | concurrent-ruby (~> 1.0) 51 | jeweler (2.1.2) 52 | builder 53 | bundler (>= 1.0) 54 | git (>= 1.2.5) 55 | github_api (~> 0.11.0) 56 | highline (>= 1.6.15) 57 | nokogiri (>= 1.5.10) 58 | rake 59 | rdoc 60 | semver 61 | json (2.6.3) 62 | jwt (2.7.0) 63 | mini_portile2 (2.1.0) 64 | minitest (5.18.0) 65 | mongo (2.18.2) 66 | bson (>= 4.14.1, < 5.0.0) 67 | mongoid (8.0.3) 68 | activemodel (>= 5.1, < 7.1, != 7.0.0) 69 | mongo (>= 2.18.0, < 3.0.0) 70 | ruby2_keywords (~> 0.0.5) 71 | multi_json (1.15.0) 72 | multi_xml (0.6.0) 73 | multipart-post (2.3.0) 74 | nokogiri (1.6.8.1) 75 | mini_portile2 (~> 2.1.0) 76 | oauth2 (1.4.8) 77 | faraday (>= 0.8, < 3.0) 78 | jwt (>= 1.0, < 3.0) 79 | multi_json (~> 1.3) 80 | multi_xml (~> 0.5) 81 | rack (>= 1.2, < 3) 82 | psych (5.1.0) 83 | stringio 84 | public_suffix (5.0.1) 85 | rack (2.2.6.3) 86 | rake (13.0.6) 87 | rchardet (1.8.0) 88 | rdoc (6.5.0) 89 | psych (>= 4.0.0) 90 | rspec (3.12.0) 91 | rspec-core (~> 3.12.0) 92 | rspec-expectations (~> 3.12.0) 93 | rspec-mocks (~> 3.12.0) 94 | rspec-core (3.12.1) 95 | rspec-support (~> 3.12.0) 96 | rspec-expectations (3.12.2) 97 | diff-lcs (>= 1.2.0, < 2.0) 98 | rspec-support (~> 3.12.0) 99 | rspec-mocks (3.12.3) 100 | diff-lcs (>= 1.2.0, < 2.0) 101 | rspec-support (~> 3.12.0) 102 | rspec-support (3.12.0) 103 | ruby2_keywords (0.0.5) 104 | semver (1.0.1) 105 | simplecov (0.16.1) 106 | docile (~> 1.1) 107 | json (>= 1.8, < 3) 108 | simplecov-html (~> 0.10.0) 109 | simplecov-html (0.10.2) 110 | stringio (3.0.5) 111 | sync (0.5.0) 112 | term-ansicolor (1.7.1) 113 | tins (~> 1.0) 114 | thor (1.2.1) 115 | thread_safe (0.3.6) 116 | tins (1.32.1) 117 | sync 118 | tzinfo (2.0.6) 119 | concurrent-ruby (~> 1.0) 120 | webrick (1.7.0) 121 | yard (0.9.28) 122 | webrick (~> 1.7.0) 123 | 124 | PLATFORMS 125 | x86_64-darwin-21 126 | 127 | DEPENDENCIES 128 | appraisal 129 | bundler 130 | coveralls 131 | database_cleaner-mongoid (~> 2.0, >= 2.0.1) 132 | jeweler (~> 2.1.1) 133 | mongoid (= 8.0.3) 134 | rspec (>= 3.0.0) 135 | simplecov (~> 0.7) 136 | yard 137 | 138 | BUNDLED WITH 139 | 2.4.6 140 | -------------------------------------------------------------------------------- /lib/mongoid_rateable.rb: -------------------------------------------------------------------------------- 1 | require 'mongoid_rateable/rateable' 2 | require 'mongoid_rateable/rating' 3 | 4 | Mongoid::Document.send :include, Mongoid::Rateable::Ext 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/mongoid_rateable/rateable.rb: -------------------------------------------------------------------------------- 1 | module Mongoid 2 | module Rateable 3 | extend ActiveSupport::Concern 4 | 5 | module Ext 6 | extend ActiveSupport::Concern 7 | 8 | module ClassMethods 9 | def rateable options = {} 10 | class_eval do 11 | self.send :include, Mongoid::Rateable 12 | self.rate_config options 13 | end 14 | end 15 | end 16 | end 17 | 18 | included do 19 | field :rates, type: Integer, default: 0 20 | field :rating, type: Float, default: nil 21 | field :rating_previous, type: Float, default: nil 22 | field :rating_delta, type: Float, default: 0.0 23 | field :weighted_rate_count, type: Integer, default: 0 24 | 25 | embeds_many :rating_marks, :as => :rateable, cascade_callbacks: true 26 | 27 | index({"rating_marks.rater_id" => 1, "rating_marks.rater_class" => 1}) 28 | 29 | scope :unrated, -> { where(:rating.exists => false) } 30 | scope :rated, -> { where(:rating.exists => true) } 31 | scope :rated_by, ->(rater) { where("rating_marks.rater_id" => rater.id, "rating_marks.rater_class" => rater.class.to_s) } 32 | scope :with_rating, ->(range) { where(:rating.gte => range.begin, :rating.lte => range.end) } 33 | scope :highest_rated, ->(limit=10) { order_by([:rating, :desc]).limit(limit) } 34 | end 35 | 36 | module ClassMethods 37 | def rater_classes 38 | @rater_classes ||= [] 39 | end 40 | 41 | def valid_rater_class? clazz 42 | return true if !rater_classes || rater_classes.empty? 43 | rater_classes.include? clazz 44 | end 45 | 46 | def in_rating_range?(value) 47 | range = rating_range if respond_to?(:rating_range) 48 | range ? range.include?(value.to_i) : true 49 | end 50 | 51 | # macro to create dynamic :rating_range class method! 52 | # can now even take an Array and find the range of values! 53 | def set_rating_range range = nil 54 | raterange = case range 55 | when Array 56 | arr = range.sort 57 | Range.new arr.first, arr.last 58 | when Range 59 | range 60 | when nil 61 | (1..5) 62 | else 63 | raise ArgumentError, "Must be a range, was: #{range}" 64 | end 65 | 66 | (class << self; self; end).send(:define_method, :rating_range) do 67 | raterange 68 | end 69 | end 70 | 71 | def rateable_by *clazzes 72 | @rater_classes = [] 73 | return if clazzes.compact.empty? 74 | clazzes.each do |clazz| 75 | raise ArgumentError, "A rateable must be a class, was: #{clazz}" unless clazz.respond_to?(:new) 76 | @rater_classes << clazz 77 | end 78 | end 79 | 80 | def rate_config options = {}, &block 81 | set_rating_range options[:range] 82 | rateable_by options[:raters] 83 | default_rater options[:default_rater], &block 84 | end 85 | 86 | def default_rater rater=nil, &block 87 | case rater 88 | when Symbol, String 89 | define_method :default_rater do 90 | self.send(rater) # fx to use owner or user relation 91 | end 92 | when nil 93 | return unless block_given? 94 | define_method :default_rater do 95 | self.instance_eval(&block) 96 | end 97 | else 98 | raise ArgumentError, "Must take symbol or block argument" 99 | end 100 | end 101 | end # class methods 102 | 103 | def rate(mark, rater = nil, weight = 1) 104 | case rater 105 | when Array 106 | rater.each{|rater| rate(mark, rater, weight)} 107 | else 108 | if !rater 109 | unless respond_to?(:default_rater) 110 | raise ArgumentError, "No rater argument and no default_rater specified" 111 | end 112 | rater = default_rater 113 | end 114 | validate_rater!(rater) 115 | validate_rating!(mark) 116 | unrate_without_rating_update(rater) 117 | total_mark = mark.to_i*weight.to_i 118 | self.rates += total_mark 119 | self.rating_marks.new(:rater_id => rater.id, :mark => mark, :rater_class => rater.class.to_s, :weight => weight) 120 | self.weighted_rate_count += weight 121 | update_rating 122 | end 123 | end 124 | 125 | def unrate(rater) 126 | case rater 127 | when Array 128 | rater.each{|rater| unrate(mark, rater, weight)} 129 | else 130 | unrate_without_rating_update(rater) 131 | update_rating 132 | end 133 | end 134 | 135 | def rate_and_save(mark, rater, weight = 1) 136 | case rater 137 | when Array 138 | rater.each{|rater| rate_and_save(mark, rater, weight)} 139 | else 140 | rate(mark, rater, weight) 141 | save 142 | end 143 | end 144 | 145 | def unrate_and_save(rater) 146 | case rater 147 | when Array 148 | rater.each{|rater| unrate_and_save(mark, rater, weight)} 149 | else 150 | unrate(rater) 151 | save 152 | end 153 | end 154 | 155 | def rated? 156 | rate_count != 0 157 | end 158 | 159 | def rated_by?(rater) 160 | case rater 161 | when Array 162 | rater.each{|rater| rated_by(mark, rater, weight)} 163 | else 164 | self.rating_marks.where(:rater_id => rater.id, :rater_class => rater.class.to_s).count == 1 165 | end 166 | end 167 | 168 | def rating 169 | read_attribute(:rating) 170 | end 171 | 172 | def previous_rating 173 | read_attribute(:rating_previous) 174 | end 175 | 176 | def rating_delta 177 | read_attribute(:rating_delta) 178 | end 179 | 180 | def unweighted_rating 181 | return nil if self.rating_marks.empty? 182 | total_sum = self.rating_marks.map(&:mark).sum 183 | return total_sum.to_f/self.rating_marks.size 184 | end 185 | 186 | def rate_count 187 | self.rating_marks.size 188 | end 189 | 190 | def rate_weight 191 | check_weighted_rate_count 192 | read_attribute(:weighted_rate_count) 193 | end 194 | 195 | def user_mark(rater) 196 | r = self.rating_marks.where(:rater_id => rater.id, :rater_class => rater.class.to_s).first 197 | r ? r.mark : nil 198 | end 199 | 200 | def user_marks(raters) 201 | if raters.map{|x| x.class}.uniq.count > 1 202 | raise ArgumentError, "Raters all must be of same class." 203 | return 204 | end 205 | r = self.rating_marks.in(:rater_id => raters.map(&:id), :rater_class => raters.first.class.to_s) 206 | r ? r.inject(Hash.new(0)) { |h, e| h[e.rater_id] = e.mark ; h } : nil 207 | end 208 | 209 | protected 210 | 211 | def validate_rater!(rater) 212 | unless self.class.valid_rater_class?(rater.class) 213 | raise ArgumentError, "Not a valid rater: #{rater.class}, must be of one of #{self.class.rater_classes}" 214 | end 215 | end 216 | 217 | def validate_rating!(value) 218 | if !self.class.in_rating_range?(value) 219 | raise ArgumentError, "Rating not in range #{self.class.rating_range}. Rating provided was #{value}." 220 | end 221 | end 222 | 223 | def unrate_without_rating_update(rater) 224 | rmark = self.rating_marks.where(:rater_id => rater.id, :rater_class => rater.class.to_s).first 225 | return unless rmark 226 | 227 | weight = (rmark.weight ||= 1) 228 | total_mark = rmark.mark.to_i*weight.to_i 229 | self.rates -= total_mark 230 | self.weighted_rate_count -= weight 231 | rmark.delete 232 | end 233 | 234 | def update_rating 235 | check_weighted_rate_count 236 | write_attribute(:rating_previous, self.rating) 237 | rt = (self.rates.to_f / self.weighted_rate_count.to_f) unless self.rating_marks.blank? 238 | write_attribute(:rating, rt) 239 | delta = (self.rating && self.previous_rating) ? rating-previous_rating : 0.0 240 | write_attribute(:rating_delta, delta) 241 | end 242 | 243 | def check_weighted_rate_count 244 | #migration from old version 245 | wrc = read_attribute(:weighted_rate_count).to_i 246 | if (wrc==0 && rate_count!=0) 247 | write_attribute(:weighted_rate_count, self.rating_marks.size) 248 | end 249 | end 250 | end 251 | end 252 | -------------------------------------------------------------------------------- /lib/mongoid_rateable/rating.rb: -------------------------------------------------------------------------------- 1 | class RatingMark 2 | include Mongoid::Document 3 | include Mongoid::Timestamps::Created 4 | 5 | embedded_in :rateable, :polymorphic => true 6 | field :mark, :type => Integer 7 | field :rater_class, :type => String 8 | field :rater_id, :type => Mongoid::VERSION.start_with?('3') ? Moped::BSON::ObjectId : BSON::ObjectId 9 | field :weight, :type => Integer, :default => 1 10 | end 11 | -------------------------------------------------------------------------------- /mongoid_rateable.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | # stub: mongoid_rateable 0.4.1 ruby lib 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "mongoid_rateable".freeze 9 | s.version = "0.4.1" 10 | 11 | s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= 12 | s.require_paths = ["lib".freeze] 13 | s.authors = ["Petr Savichev (proton)".freeze] 14 | s.date = "2023-03-07" 15 | s.description = "Provides fields and methods for the rating manipulation on Mongoid documents.".freeze 16 | s.email = "psavichev@gmail.com".freeze 17 | s.extra_rdoc_files = [ 18 | "CHANGELOG.md", 19 | "LICENSE.txt", 20 | "README.md", 21 | "TODO" 22 | ] 23 | s.files = [ 24 | ".github/FUNDING.yml", 25 | ".rspec", 26 | ".ruby-version", 27 | ".travis.yml", 28 | "Appraisals", 29 | "CHANGELOG.md", 30 | "Gemfile", 31 | "LICENSE.txt", 32 | "README.md", 33 | "Rakefile", 34 | "TODO", 35 | "VERSION", 36 | "gemfiles/mongoid_3.gemfile", 37 | "gemfiles/mongoid_3.gemfile.lock", 38 | "gemfiles/mongoid_4.gemfile", 39 | "gemfiles/mongoid_4.gemfile.lock", 40 | "gemfiles/mongoid_5.gemfile", 41 | "gemfiles/mongoid_5.gemfile.lock", 42 | "gemfiles/mongoid_6.gemfile", 43 | "gemfiles/mongoid_6.gemfile.lock", 44 | "gemfiles/mongoid_7.gemfile", 45 | "gemfiles/mongoid_7.gemfile.lock", 46 | "gemfiles/mongoid_8.gemfile", 47 | "gemfiles/mongoid_8.gemfile.lock", 48 | "lib/mongoid_rateable.rb", 49 | "lib/mongoid_rateable/rateable.rb", 50 | "lib/mongoid_rateable/rating.rb", 51 | "mongoid_rateable.gemspec", 52 | "spec/models/article.rb", 53 | "spec/models/comment.rb", 54 | "spec/models/post.rb", 55 | "spec/models/user.rb", 56 | "spec/rateable_spec.rb", 57 | "spec/spec_helper.rb", 58 | "spec/support/database_cleaner.rb" 59 | ] 60 | s.homepage = "http://github.com/proton/mongoid_rateable".freeze 61 | s.licenses = ["MIT".freeze] 62 | s.rubygems_version = "3.4.6".freeze 63 | s.summary = "Rating functionality for Mongoid documents".freeze 64 | 65 | s.specification_version = 4 66 | 67 | s.add_runtime_dependency(%q.freeze, [">= 3.0"]) 68 | s.add_development_dependency(%q.freeze, [">= 0"]) 69 | s.add_development_dependency(%q.freeze, ["~> 2.1.1"]) 70 | s.add_development_dependency(%q.freeze, ["~> 0.7"]) 71 | s.add_development_dependency(%q.freeze, [">= 0"]) 72 | s.add_development_dependency(%q.freeze, [">= 3.0.0"]) 73 | s.add_development_dependency(%q.freeze, [">= 0"]) 74 | s.add_development_dependency(%q.freeze, [">= 0"]) 75 | s.add_development_dependency(%q.freeze, ["~> 2.0", ">= 2.0.1"]) 76 | end 77 | 78 | -------------------------------------------------------------------------------- /spec/models/article.rb: -------------------------------------------------------------------------------- 1 | class Article 2 | include Mongoid::Document 3 | include Mongoid::Rateable 4 | 5 | set_rating_range (1..5) 6 | 7 | field :name 8 | 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/comment.rb: -------------------------------------------------------------------------------- 1 | class Comment 2 | include Mongoid::Document 3 | 4 | rateable range: (-5..7) 5 | 6 | embedded_in :post 7 | 8 | field :content 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/post.rb: -------------------------------------------------------------------------------- 1 | class Post 2 | include Mongoid::Document 3 | include Mongoid::Rateable 4 | include Mongoid::Attributes::Dynamic if Mongoid::VERSION>='4' 5 | 6 | embeds_many :comments 7 | end 8 | -------------------------------------------------------------------------------- /spec/models/user.rb: -------------------------------------------------------------------------------- 1 | class User 2 | include Mongoid::Document 3 | 4 | field :id 5 | field :name 6 | 7 | end 8 | -------------------------------------------------------------------------------- /spec/rateable_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Post do 4 | 5 | before(:each) do 6 | @bob = User.create :name => "Bob" 7 | @alice = User.create :name => "Alice" 8 | @sally = User.create :name => "Sally" 9 | @post = Post.create :name => "Announcement" 10 | @article = Article.create :name => "Article" 11 | end 12 | 13 | it "should have Mongoid::Rateable module" do 14 | #TODO: Refactor this 15 | @post.class.const_get("Mongoid").const_get("Rateable").should_not be nil 16 | end 17 | 18 | subject { @post } 19 | it { should respond_to :rate } 20 | it { should respond_to :unrate } 21 | it { should respond_to :rate_and_save } 22 | it { should respond_to :unrate_and_save } 23 | it { should respond_to :rated? } 24 | it { should respond_to :rate_count } 25 | it { should respond_to :rates } 26 | it { should respond_to :rating } 27 | it { should respond_to :previous_rating } 28 | it { should respond_to :rating_delta } 29 | it { should respond_to :unweighted_rating } 30 | it { should respond_to :rating_marks } 31 | it { should respond_to :user_mark } 32 | it { should respond_to :user_marks } 33 | 34 | describe "#rating_marks" do 35 | it "should be proper Mongoid field" do 36 | @post.rating_marks.should be_an_instance_of Array 37 | end 38 | end 39 | 40 | context "when rated" do 41 | before (:each) do 42 | @post.rate 1, @bob 43 | end 44 | 45 | describe "#rate" do 46 | it "should track #rates properly" do 47 | @post.rate 1, @sally 48 | @post.rates.should eql 2 49 | end 50 | 51 | it "should track weighted #rates properly" do 52 | @post.rate 1, @alice, 4 53 | @post.rates.should eql 5 54 | end 55 | 56 | it "should limit #rates by user properly" do 57 | @post.rate 5, @bob 58 | @post.rates.should eql 5 59 | end 60 | 61 | context "when rate_value in rating range" do 62 | it { expect { @article.rate 1, @sally }.not_to raise_error } 63 | end 64 | 65 | context "when rate_value not in rating range" do 66 | it { expect { @article.rate 7, @sally }.to raise_error(ArgumentError) } 67 | end 68 | 69 | describe "when using negative values" do 70 | let(:num) { -rand(1..100) } 71 | 72 | it { expect { @post.rate num, @sally }.to change { @post.rates }.by(num) } 73 | it { expect { @post.rate -1, @sally, -num }.to change { @post.rates }.by(num) } 74 | end 75 | end 76 | 77 | describe "#rated?" do 78 | describe "for anyone" do 79 | specify { @post.rated?().should be true } 80 | end 81 | describe "for anyone" do 82 | specify { @article.rated?().should be false } 83 | end 84 | 85 | describe "for Bob" do 86 | specify { @post.rated_by?(@bob).should be true } 87 | end 88 | describe "for Bob" do 89 | specify { @article.rated_by?(@bob).should be false } 90 | end 91 | 92 | describe "when rated by someone else" do 93 | before do 94 | @post.rate 1, @alice 95 | end 96 | 97 | describe "for Alice" do 98 | specify { @post.rated_by?(@alice).should be true } 99 | end 100 | end 101 | 102 | describe "when not rated by someone else" do 103 | describe "for Sally" do 104 | specify { @post.rated_by?(@sally).should be false } 105 | end 106 | end 107 | end 108 | 109 | describe "#unrate" do 110 | before { @post.unrate @bob } 111 | 112 | it "should have null #rate_count" do 113 | @post.rate_count.should eql 0 114 | end 115 | 116 | it "should have null #rates" do 117 | @post.rates.should eql 0 118 | end 119 | 120 | it "should be unrated" do 121 | @post.rated?.should be false 122 | end 123 | end 124 | 125 | describe "#rate_count" do 126 | it "should know how many rates have been cast" do 127 | @post.rate 1, @sally 128 | @post.rate_count.should eql 2 129 | end 130 | end 131 | 132 | describe "#rating" do 133 | it "should calculate the average rate" do 134 | @post.rate 4, @sally 135 | @post.rating.should eq 2.5 136 | end 137 | 138 | it "should calculate the average rate if the result is zero" do 139 | @post.rate -1, @sally 140 | @post.rating.should eq 0.0 141 | end 142 | end 143 | 144 | describe "#previous_rating" do 145 | it "should store previous value of the average rate" do 146 | @post.rate 4, @sally 147 | @post.previous_rating.should eq 1.0 148 | end 149 | 150 | it "should store previous value of the average rate after two changes" do 151 | @post.rate -1, @sally 152 | @post.rate 4, @sally 153 | @post.previous_rating.should eq 0.0 154 | end 155 | end 156 | 157 | describe "#rating_delta" do 158 | it "should calculate delta of previous and new ratings" do 159 | @post.rate 4, @sally 160 | @post.rating_delta.should eq 1.5 161 | end 162 | 163 | it "should calculate delta of previous and new ratings" do 164 | @post.rate -1, @sally 165 | @post.rating_delta.should eq -1.0 166 | end 167 | end 168 | 169 | describe "#unweighted_rating" do 170 | it "should calculate the unweighted average rate" do 171 | @post.rate 4, @sally 172 | @post.unweighted_rating.should eq 2.5 173 | end 174 | 175 | it "should calculate the unweighted average rate if the result is zero" do 176 | @post.rate -1, @sally 177 | @post.unweighted_rating.should eq 0.0 178 | end 179 | end 180 | 181 | describe "#user_mark" do 182 | describe "should give mark" do 183 | specify { @post.user_mark(@bob).should eq 1} 184 | end 185 | describe "should give nil" do 186 | specify { @post.user_mark(@alice).should be_nil} 187 | end 188 | describe "should give marks" do 189 | specify { @post.user_marks([@bob, @alice]).should eq Hash[@bob.id,1] } 190 | end 191 | end 192 | end 193 | 194 | context "when not rated" do 195 | describe "#rates" do 196 | specify { @post.rates.should eql 0 } 197 | end 198 | 199 | describe "#rating" do 200 | specify { @post.rating.should be_nil } 201 | end 202 | 203 | describe "#previous_rating" do 204 | specify { @post.previous_rating.should be_nil } 205 | end 206 | 207 | describe "#rating_delta" do 208 | specify { @post.rating_delta.should eq 0.0 } 209 | end 210 | 211 | describe "#unweighted_rating" do 212 | specify { @post.unweighted_rating.should be_nil } 213 | end 214 | 215 | describe "#unrate" do 216 | before do 217 | @post.unrate @sally 218 | end 219 | 220 | it "should have null #rate_count" do 221 | @post.rate_count.should eql 0 222 | end 223 | 224 | it "should have null #rates" do 225 | @post.rates.should eql 0 226 | end 227 | end 228 | end 229 | 230 | context "when saving the collection" do 231 | before (:each) do 232 | @post.rate 8, @bob 233 | @post.rate -10, @sally 234 | @post.save 235 | @f_post = Post.where(:name => "Announcement").first 236 | end 237 | 238 | describe "#rated_by?" do 239 | describe "for Bob" do 240 | specify { @f_post.rated_by?(@bob).should be true } 241 | end 242 | 243 | describe "for Sally" do 244 | specify { @f_post.rated_by?(@sally).should be true } 245 | end 246 | 247 | describe "for Alice" do 248 | specify { @f_post.rated_by?(@alice).should be false} 249 | end 250 | end 251 | 252 | describe "#rates" do 253 | specify { @f_post.rates.should eql -2 } 254 | end 255 | 256 | describe "#rate_count" do 257 | specify { @f_post.rate_count.should eql 2 } 258 | end 259 | 260 | describe "#rating" do 261 | specify { @f_post.rating.should eq -1.0 } 262 | end 263 | 264 | describe "#previous_rating" do 265 | specify { @f_post.previous_rating.should eq 8.0 } 266 | end 267 | 268 | describe "#rating_delta" do 269 | specify { @post.rating_delta.should eq -9.0 } 270 | end 271 | 272 | describe "#unweighted_rating" do 273 | specify { @f_post.unweighted_rating.should eq -1.0 } 274 | end 275 | 276 | describe "rating timestamps" do 277 | specify { @f_post.rating_marks.first.created_at.should_not be_nil } 278 | end 279 | end 280 | 281 | describe "#rate_and_save" do 282 | before (:each) do 283 | @post.rate_and_save 8, @bob, 2 284 | @post.rate_and_save -10, @sally 285 | @f_post = Post.where(:name => "Announcement").first 286 | end 287 | 288 | describe "#rated?" do 289 | it "should be #rated? by Bob" do 290 | @f_post.rated_by?(@bob).should be true 291 | end 292 | 293 | it "should be #rated? by Sally" do 294 | @f_post.rated_by?(@sally).should be true 295 | end 296 | 297 | it "should be not #rated? by Alice" do 298 | @f_post.rated_by?(@alice).should be false 299 | end 300 | end 301 | 302 | it "should have #rates equal 6" do 303 | @f_post.rates.should eql 6 304 | end 305 | 306 | it "should have #rate_count equal 2" do 307 | @f_post.rate_count.should eql 2 308 | end 309 | 310 | it "should have #rate_weight equal 3" do 311 | @f_post.rate_weight.should eql 3 312 | end 313 | 314 | it "should have #rating equal 2.0" do 315 | @f_post.rating.should eq 2.0 316 | end 317 | 318 | it "should have #previous_rating equal 8.0" do 319 | @f_post.previous_rating.should eq 8.0 320 | end 321 | 322 | it "should have #rating_delta equal -6.0" do 323 | @f_post.rating_delta.should eq -6.0 324 | end 325 | 326 | it "should have #unweighted_rating equal -1.0" do 327 | @f_post.unweighted_rating.should eq -1.0 328 | end 329 | 330 | describe "#unrate_and_save" do 331 | before (:each) do 332 | @post.unrate_and_save @bob 333 | @f_post = Post.where(:name => "Announcement").first 334 | end 335 | 336 | describe "#rated?" do 337 | it "should be #rated? by Sally" do 338 | @f_post.rated_by?(@sally).should be true 339 | end 340 | 341 | it "should be not #rated? by Bob" do 342 | @f_post.rated_by?(@bob).should be false 343 | end 344 | 345 | it "should be #rated?" do 346 | @f_post.rated?.should be true 347 | end 348 | end 349 | 350 | it "should have #rates equal -10" do 351 | @f_post.rates.should eql -10 352 | end 353 | 354 | it "should have #rate_count equal 1" do 355 | @f_post.rate_count.should eql 1 356 | end 357 | 358 | it "should have #rate_weight equal 1" do 359 | @f_post.rate_weight.should eql 1 360 | end 361 | 362 | it "should have #rating equal -10.0" do 363 | @f_post.rating.should eq -10.0 364 | end 365 | 366 | it "should have #previous_rating equal 2.0" do 367 | @f_post.previous_rating.should eq 2.0 368 | end 369 | 370 | it "should have #rating_delta equal -12.0" do 371 | @f_post.rating_delta.should eq -12.0 372 | end 373 | 374 | it "should have #unweighted_rating equal -10.0" do 375 | @f_post.unweighted_rating.should eq -10.0 376 | end 377 | end 378 | end 379 | 380 | describe "#scopes" do 381 | before (:each) do 382 | @post.delete 383 | @post1 = Post.create(:name => "Post 1") 384 | @post2 = Post.create(:name => "Post 2") 385 | @post3 = Post.create(:name => "Post 3") 386 | @post4 = Post.create(:name => "Post 4") 387 | @post5 = Post.create(:name => "Post 5") 388 | @post1.rate_and_save 5, @sally 389 | @post1.rate_and_save 3, @bob 390 | @post4.rate_and_save 1, @sally 391 | end 392 | 393 | describe "#unrated" do 394 | it "should return proper count of unrated posts" do 395 | Post.unrated.size.should eql 3 396 | end 397 | end 398 | 399 | describe "#rated" do 400 | it "should return proper count of rated posts" do 401 | Post.rated.size.should eql 2 402 | end 403 | end 404 | 405 | describe "#rated_by" do 406 | it "should return proper count of posts rated by Bob" do 407 | Post.rated_by(@bob).size.should eql 1 408 | end 409 | 410 | it "should return proper count of posts rated by Sally" do 411 | Post.rated_by(@sally).size.should eql 2 412 | end 413 | end 414 | 415 | describe "#with_rating" do 416 | before (:each) do 417 | @post1.rate_and_save 4, @alice 418 | @post2.rate_and_save 2, @alice 419 | @post3.rate_and_save 5, @alice 420 | @post4.rate_and_save 2, @alice 421 | end 422 | 423 | it "should return proper count of posts with rating 4..5" do 424 | Post.with_rating(4..5).size.should eql 2 425 | end 426 | 427 | it "should return proper count of posts with rating 0..2" do 428 | Post.with_rating(0..2).size.should eql 2 429 | end 430 | 431 | it "should return proper count of posts with rating 0..5" do 432 | Post.with_rating(0..5).size.should eql 4 433 | end 434 | end 435 | 436 | describe "#highest_rated" do 437 | it "should return proper count of posts" do 438 | #mongoid has problems with returning count of documents (https://github.com/mongoid/mongoid/issues/817) 439 | posts_count = 0 440 | Post.highest_rated(1).each {|x| posts_count+=1 } 441 | posts_count.should eql 1 442 | end 443 | 444 | it "should return proper count of posts" do 445 | #mongoid has problems with returning count of documents (https://github.com/mongoid/mongoid/issues/817) 446 | posts_count = 0 447 | Post.highest_rated(10).each {|x| posts_count+=1 } 448 | posts_count.should eql 5 449 | end 450 | 451 | it "should return proper document" do 452 | Post.highest_rated(1).first.name.should eql "Post 1" 453 | end 454 | end 455 | end 456 | end 457 | 458 | describe Comment do 459 | 460 | before(:each) do 461 | @bob = User.create :name => "Bob" 462 | @alice = User.create :name => "Alice" 463 | @sally = User.create :name => "Sally" 464 | @post = Post.create :name => "Announcement" 465 | @comment1 = @post.comments.create :content => 'Hello!' 466 | @comment2 = @post.comments.create :content => 'Goodbye!' 467 | end 468 | 469 | it "should have Mongoid::Rateable module" do 470 | #TODO: Refactor this 471 | @comment1.class.const_get("Mongoid").const_get("Rateable").should_not be nil 472 | end 473 | 474 | subject { @comment1 } 475 | it { should respond_to :rate } 476 | it { should respond_to :unrate } 477 | it { should respond_to :rate_and_save } 478 | it { should respond_to :unrate_and_save } 479 | it { should respond_to :rated? } 480 | it { should respond_to :rate_count } 481 | it { should respond_to :rates } 482 | it { should respond_to :rating } 483 | it { should respond_to :previous_rating } 484 | it { should respond_to :rating_delta } 485 | it { should respond_to :unweighted_rating } 486 | it { should respond_to :rating_marks } 487 | it { should respond_to :user_mark } 488 | 489 | describe "#rating_marks" do 490 | it "should be proper Mongoid field" do 491 | @comment1.rating_marks.should be_an_instance_of Array 492 | end 493 | end 494 | 495 | context "when rated" do 496 | before (:each) do 497 | @comment1.rate 2, @bob 498 | end 499 | 500 | describe "#rate" do 501 | it "should track #rates properly" do 502 | @comment1.rate 3, @sally 503 | @comment1.rates.should eql 5 504 | end 505 | 506 | it "should track weighted #rates properly" do 507 | @comment1.rate 1, @alice, 4 508 | @comment1.rates.should eql 6 509 | end 510 | 511 | it "should limit #rates by user properly" do 512 | @comment1.rate 5, @bob 513 | @comment1.rates.should eql 5 514 | end 515 | 516 | context "when rate_value in rating range" do 517 | it { expect { @comment1.rate 1, @sally }.not_to raise_error } 518 | end 519 | 520 | context "when rate_value not in rating range" do 521 | it { expect { @comment1.rate 9, @sally }.to raise_error(ArgumentError) } 522 | end 523 | 524 | describe "when using negative values" do 525 | let(:num) { -rand(1..5) } 526 | 527 | it { expect { @comment1.rate num, @sally }.to change { @comment1.rates }.by(num) } 528 | it { expect { @comment1.rate -1, @sally, -num }.to change { @comment1.rates }.by(num) } 529 | end 530 | end 531 | 532 | describe "#rated?" do 533 | describe "for anyone" do 534 | specify { @comment1.rated?().should be true } 535 | end 536 | describe "for anyone" do 537 | specify { @comment2.rated?().should be false } 538 | end 539 | 540 | describe "for Bob" do 541 | specify { @comment1.rated_by?(@bob).should be true } 542 | end 543 | describe "for Bob" do 544 | specify { @comment2.rated_by?(@bob).should be false } 545 | end 546 | 547 | describe "when rated by someone else" do 548 | before do 549 | @comment1.rate 1, @alice 550 | end 551 | 552 | describe "for Alice" do 553 | specify { @comment1.rated_by?(@alice).should be true } 554 | end 555 | end 556 | 557 | describe "when not rated by someone else" do 558 | describe "for Sally" do 559 | specify { @comment1.rated_by?(@sally).should be false } 560 | end 561 | end 562 | end 563 | 564 | describe "#unrate" do 565 | before { @comment1.unrate @bob } 566 | 567 | it "should have null #rate_count" do 568 | @comment1.rate_count.should eql 0 569 | end 570 | 571 | it "should have null #rates" do 572 | @comment1.rates.should eql 0 573 | end 574 | 575 | it "should be unrated" do 576 | @comment1.rated?.should be false 577 | end 578 | end 579 | 580 | describe "#rate_count" do 581 | it "should know how many rates have been cast" do 582 | @comment1.rate 1, @sally 583 | @comment1.rate_count.should eql 2 584 | end 585 | end 586 | 587 | describe "#rating" do 588 | it "should calculate the average rate" do 589 | @comment1.rate 4, @sally 590 | @comment1.rating.should eq 3.0 591 | end 592 | 593 | it "should calculate the average rate if the result is zero" do 594 | @comment1.rate -2, @sally 595 | @comment1.rating.should eq 0.0 596 | end 597 | end 598 | 599 | describe "#previous_rating" do 600 | it "should store previous value of the average rate" do 601 | @comment1.rate 4, @sally 602 | @comment1.previous_rating.should eq 2.0 603 | end 604 | 605 | it "should store previous value of the average rate after two changes" do 606 | @comment1.rate -2, @sally 607 | @comment1.rate 4, @sally 608 | @comment1.previous_rating.should eq 0.0 609 | end 610 | end 611 | 612 | describe "#rating_delta" do 613 | it "should calculate delta of previous and new ratings" do 614 | @comment1.rate 4, @sally 615 | @comment1.rating_delta.should eq 1.0 616 | end 617 | 618 | it "should calculate delta of previous and new ratings" do 619 | @comment1.rate -1, @sally 620 | @comment1.rating_delta.should eq -1.5 621 | end 622 | end 623 | 624 | describe "#unweighted_rating" do 625 | it "should calculate the unweighted average rate" do 626 | @comment1.rate 4, @sally 627 | @comment1.unweighted_rating.should eq 3.0 628 | end 629 | 630 | it "should calculate the unweighted average rate if the result is zero" do 631 | @comment1.rate -2, @sally 632 | @comment1.unweighted_rating.should eq 0.0 633 | end 634 | end 635 | 636 | describe "#user_mark" do 637 | describe "should give mark" do 638 | specify { @comment1.user_mark(@bob).should eq 2} 639 | end 640 | describe "should give nil" do 641 | specify { @comment1.user_mark(@alice).should be_nil} 642 | end 643 | end 644 | end 645 | 646 | context "when not rated" do 647 | describe "#rates" do 648 | specify { @comment1.rates.should eql 0 } 649 | end 650 | 651 | describe "#rating" do 652 | specify { @comment1.rating.should be_nil } 653 | end 654 | 655 | describe "#previous_rating" do 656 | specify { @comment1.previous_rating.should be_nil } 657 | end 658 | 659 | describe "#rating_delta" do 660 | specify { @comment1.rating_delta.should eq 0.0 } 661 | end 662 | 663 | describe "#unweighted_rating" do 664 | specify { @comment1.unweighted_rating.should be_nil } 665 | end 666 | 667 | describe "#unrate" do 668 | before do 669 | @comment1.unrate @sally 670 | end 671 | 672 | it "should have null #rate_count" do 673 | @comment1.rate_count.should eql 0 674 | end 675 | 676 | it "should have null #rates" do 677 | @comment1 .rates.should eql 0 678 | end 679 | end 680 | end 681 | 682 | context "when saving the collection" do 683 | before (:each) do 684 | @comment1.rate 3, @bob 685 | @comment1.rate -2, @sally 686 | @comment1.save 687 | @f_post = Post.where(:name => "Announcement").first 688 | @f_comment = @f_post.comments.where(:content => "Hello!").first 689 | end 690 | 691 | describe "#rated_by?" do 692 | describe "for Bob" do 693 | specify { @f_comment.rated_by?(@bob).should be true } 694 | end 695 | 696 | describe "for Sally" do 697 | specify { @f_comment.rated_by?(@sally).should be true } 698 | end 699 | 700 | describe "for Alice" do 701 | specify { @f_comment.rated_by?(@alice).should be false} 702 | end 703 | end 704 | 705 | describe "#rates" do 706 | specify { @f_comment.rates.should eql 1 } 707 | end 708 | 709 | describe "#rate_count" do 710 | specify { @f_comment.rate_count.should eql 2 } 711 | end 712 | 713 | describe "#rating" do 714 | specify { @f_comment.rating.should eq 0.5 } 715 | end 716 | 717 | describe "#previous_rating" do 718 | specify { @f_comment.previous_rating.should eq 3.0 } 719 | end 720 | 721 | describe "#rating_delta" do 722 | specify { @f_comment.rating_delta.should eq -2.5 } 723 | end 724 | 725 | describe "#unweighted_rating" do 726 | specify { @f_comment.unweighted_rating.should eq 0.5 } 727 | end 728 | end 729 | 730 | describe "#rate_and_save" do 731 | before (:each) do 732 | @comment1.rate_and_save 4, @bob, 2 733 | @comment1.rate_and_save -2, @sally 734 | @f_post = Post.where(:name => "Announcement").first 735 | @f_comment = @f_post.comments.where(:content => "Hello!").first 736 | end 737 | 738 | describe "#rated?" do 739 | it "should be #rated? by Bob" do 740 | @f_comment.rated_by?(@bob).should be true 741 | end 742 | 743 | it "should be #rated? by Sally" do 744 | @f_comment.rated_by?(@sally).should be true 745 | end 746 | 747 | it "should be not #rated? by Alice" do 748 | @f_comment.rated_by?(@alice).should be false 749 | end 750 | end 751 | 752 | it "should have #rates equal 6" do 753 | @f_comment.rates.should eql 6 754 | end 755 | 756 | it "should have #rate_count equal 2" do 757 | @f_comment.rate_count.should eql 2 758 | end 759 | 760 | it "should have #rate_weight equal 3" do 761 | @f_comment.rate_weight.should eql 3 762 | end 763 | 764 | it "should have #rating equal 2.0" do 765 | @f_comment.rating.should eq 2.0 766 | end 767 | 768 | it "should have #previous_rating equal 4.0" do 769 | @f_comment.previous_rating.should eq 4.0 770 | end 771 | 772 | it "should have #rating_delta equal -2.0" do 773 | @f_comment.rating_delta.should eq -2.0 774 | end 775 | 776 | it "should have #unweighted_rating equal 1.0" do 777 | @f_comment.unweighted_rating.should eq 1.0 778 | end 779 | 780 | describe "#unrate_and_save" do 781 | before (:each) do 782 | @comment1.unrate_and_save @bob 783 | @f_post = Post.where(:name => "Announcement").first 784 | @f_comment = @f_post.comments.where(:content => "Hello!").first 785 | end 786 | 787 | describe "#rated?" do 788 | it "should be #rated? by Sally" do 789 | @f_comment.rated_by?(@sally).should be true 790 | end 791 | 792 | it "should be not #rated? by Bob" do 793 | @f_comment.rated_by?(@bob).should be false 794 | end 795 | 796 | it "should be #rated?" do 797 | @f_comment.rated?.should be true 798 | end 799 | end 800 | 801 | it "should have #rates equal -2" do 802 | @f_comment.rates.should eql -2 803 | end 804 | 805 | it "should have #rate_count equal 1" do 806 | @f_comment.rate_count.should eql 1 807 | end 808 | 809 | it "should have #rate_weight equal 1" do 810 | @f_comment.rate_weight.should eql 1 811 | end 812 | 813 | it "should have #rating equal -2.0" do 814 | @f_comment.rating.should eq -2.0 815 | end 816 | 817 | it "should have #previous_rating equal 2.0" do 818 | @f_comment.previous_rating.should eq 2.0 819 | end 820 | 821 | it "should have #rating_delta equal -4.0" do 822 | @f_comment.rating_delta.should eq -4.0 823 | end 824 | 825 | it "should have #unweighted_rating equal -2.0" do 826 | @f_comment.unweighted_rating.should eq -2.0 827 | end 828 | end 829 | end 830 | 831 | describe "#scopes" do 832 | before (:each) do 833 | @post1 = Post.create(:name => "Post 1") 834 | @c1 = @post1.comments.create(:content => 'c1') 835 | @c2 = @post1.comments.create(:content => 'c2') 836 | @c3 = @post1.comments.create(:content => 'c3') 837 | @c4 = @post1.comments.create(:content => 'c4') 838 | @c5 = @post1.comments.create(:content => 'c5') 839 | @c1.rate_and_save 5, @sally 840 | @c1.rate_and_save 3, @bob 841 | @c4.rate_and_save 1, @sally 842 | end 843 | 844 | describe "#unrated" do 845 | it "should return proper count of unrated comments" do 846 | @post1.comments.unrated.size.should eql 3 847 | end 848 | end 849 | 850 | describe "#rated" do 851 | it "should return proper count of rated comments" do 852 | @post1.comments.rated.size.should eql 2 853 | end 854 | end 855 | 856 | describe "#rated_by" do 857 | it "should return proper count of comments rated by Bob" do 858 | @post1.comments.rated_by(@bob).size.should eql 1 859 | end 860 | 861 | it "should return proper count of comments rated by Sally" do 862 | @post1.comments.rated_by(@sally).size.should eql 2 863 | end 864 | end 865 | 866 | describe "#with_rating" do 867 | before (:each) do 868 | @c1.rate_and_save 4, @alice 869 | @c2.rate_and_save 2, @alice 870 | @c3.rate_and_save 5, @alice 871 | @c4.rate_and_save 2, @alice 872 | end 873 | 874 | it "should return proper count of comments with rating 4..5" do 875 | @post1.comments.with_rating(4..5).size.should eql 2 876 | end 877 | 878 | it "should return proper count of comments with rating 0..2" do 879 | @post1.comments.with_rating(0..2).size.should eql 2 880 | end 881 | 882 | it "should return proper count of comments with rating 0..5" do 883 | @post1.comments.with_rating(0..5).size.should eql 4 884 | end 885 | end 886 | 887 | describe "#highest_rated" do 888 | it "should return proper count of comments" do 889 | #mongoid has problems with returning count of documents (https://github.com/mongoid/mongoid/issues/817) 890 | comments_count = 0 891 | @post1.comments.highest_rated(1).each {|x| comments_count+=1 } 892 | comments_count.should eql 1 893 | end 894 | 895 | it "should return proper count of comments" do 896 | #mongoid has problems with returning count of documents (https://github.com/mongoid/mongoid/issues/817) 897 | comments_count = 0 898 | @post1.comments.highest_rated(10).each {|x| comments_count+=1 } 899 | comments_count.should eql 5 900 | end 901 | 902 | #Don't work! (Mongoid can't sort embedded documents) 903 | # it "should return proper document" do 904 | # @post1.comments.highest_rated(1).first.content.should eql "c1" 905 | # end 906 | end 907 | end 908 | end 909 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 2 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 3 | 4 | MODELS = File.join(File.dirname(__FILE__), 'models') 5 | 6 | require 'rubygems' 7 | require 'mongoid' 8 | require 'mongoid_rateable' 9 | require 'simplecov' 10 | require 'database_cleaner/mongoid' 11 | require 'coveralls' 12 | 13 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ 14 | SimpleCov::Formatter::HTMLFormatter, 15 | Coveralls::SimpleCov::Formatter 16 | ]) 17 | SimpleCov.start 18 | 19 | Mongoid.configure do |config| 20 | config.connect_to "mongoid_rateable_test" 21 | end 22 | 23 | Mongoid.logger = Logger.new($stdout) 24 | 25 | if Mongoid::VERSION>'5' 26 | Mongo::Logger.logger.level = ::Logger::FATAL 27 | end 28 | 29 | Dir["#{MODELS}/*.rb"].each { |f| require f } 30 | 31 | RSpec.configure do |config| 32 | config.before(:all) do 33 | DatabaseCleaner[:mongoid].strategy = :deletion 34 | end 35 | 36 | config.before(:each) do 37 | DatabaseCleaner[:mongoid].start 38 | end 39 | 40 | config.after(:each) do 41 | DatabaseCleaner[:mongoid].clean 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/support/database_cleaner.rb: -------------------------------------------------------------------------------- 1 | class DatabaseCleaner 2 | class << self 3 | def clean 4 | new.clean 5 | end 6 | end 7 | 8 | def clean 9 | if mongoid4? 10 | collections.each { |c| database[c].find.remove_all } 11 | else 12 | collections.each { |c| database[c].find.delete_many } 13 | end 14 | end 15 | 16 | private 17 | 18 | def mongoid4? 19 | Mongoid::VERSION.start_with? '4' 20 | end 21 | 22 | def database 23 | if mongoid4? 24 | Mongoid.default_session 25 | else 26 | Mongoid::Clients.default 27 | end 28 | end 29 | 30 | def collections 31 | database['system.namespaces'].find(name: { '$not' => /\.system\.|\$/ }).to_a.map do |collection| 32 | _, name = collection['name'].split('.', 2) 33 | name 34 | end 35 | end 36 | end 37 | --------------------------------------------------------------------------------