├── lib ├── make_voteable │ ├── version.rb │ ├── voting.rb │ ├── voteable.rb │ ├── exceptions.rb │ └── voter.rb ├── generators │ └── make_voteable │ │ ├── make_voteable_generator.rb │ │ └── templates │ │ └── migration.rb └── make_voteable.rb ├── .gitignore ├── spec ├── database.yml ├── database.yml.sample ├── models.rb ├── generators │ └── make_voteable_generator_spec.rb ├── spec_helper.rb ├── schema.rb └── lib │ └── make_voteable_spec.rb ├── Gemfile ├── Rakefile ├── MIT-LICENSE ├── make_voteable.gemspec └── README.rdoc /lib/make_voteable/version.rb: -------------------------------------------------------------------------------- 1 | module MakeVoteable 2 | VERSION = "0.1.1" 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | pkg/* 3 | doc/* 4 | *.gem 5 | *.log 6 | *.sqlite3 7 | .bundle 8 | -------------------------------------------------------------------------------- /spec/database.yml: -------------------------------------------------------------------------------- 1 | sqlite3: 2 | adapter: sqlite3 3 | database: make_voteable.sqlite3 4 | -------------------------------------------------------------------------------- /spec/database.yml.sample: -------------------------------------------------------------------------------- 1 | sqlite3: 2 | adapter: sqlite3 3 | database: make_voteable.sqlite3 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | # Specify your gem's dependencies in make_voteable.gemspec 3 | gemspec 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rspec/core/rake_task' 5 | RSpec::Core::RakeTask.new(:spec) 6 | 7 | task :default => :spec 8 | -------------------------------------------------------------------------------- /lib/make_voteable/voting.rb: -------------------------------------------------------------------------------- 1 | module MakeVoteable 2 | class Voting < ActiveRecord::Base 3 | belongs_to :voteable, :polymorphic => true 4 | belongs_to :voter, :polymorphic => true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/models.rb: -------------------------------------------------------------------------------- 1 | class VoteableModel < ActiveRecord::Base 2 | make_voteable 3 | end 4 | 5 | class VoterModel < ActiveRecord::Base 6 | make_voter 7 | end 8 | 9 | class InvalidVoteableModel < ActiveRecord::Base 10 | end 11 | 12 | class User < ActiveRecord::Base 13 | make_voter 14 | end 15 | 16 | class Admin < User 17 | end 18 | -------------------------------------------------------------------------------- /lib/make_voteable/voteable.rb: -------------------------------------------------------------------------------- 1 | module MakeVoteable 2 | module Voteable 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | has_many :votings, :class_name => "MakeVoteable::Voting", :as => :voteable 7 | end 8 | 9 | module ClassMethods 10 | def voteable? 11 | true 12 | end 13 | end 14 | 15 | # Return the difference of down and up votes. 16 | # May be negative if there are more down than up votes. 17 | def votes 18 | self.up_votes - self.down_votes 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/make_voteable/make_voteable_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/migration' 2 | require 'rails/generators/active_record' 3 | 4 | class MakeVoteableGenerator < Rails::Generators::Base 5 | include Rails::Generators::Migration 6 | 7 | desc "Generates a migration for the Vote model" 8 | 9 | def self.source_root 10 | @source_root ||= File.dirname(__FILE__) + '/templates' 11 | end 12 | 13 | def self.next_migration_number(path) 14 | ActiveRecord::Generators::Base.next_migration_number(path) 15 | end 16 | 17 | def generate_migration 18 | migration_template 'migration.rb', 'db/migrate/create_make_voteable_tables' 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/make_voteable/exceptions.rb: -------------------------------------------------------------------------------- 1 | module MakeVoteable 2 | module Exceptions 3 | class AlreadyVotedError < StandardError 4 | attr_reader :up_vote 5 | 6 | def initialize(up_vote) 7 | vote = if up_vote 8 | "up voted" 9 | else 10 | "down voted" 11 | end 12 | 13 | super "The voteable was already #{vote} by the voter." 14 | end 15 | end 16 | 17 | class NotVotedError < StandardError 18 | def initialize 19 | super "The voteable was not voted by the voter." 20 | end 21 | end 22 | 23 | class InvalidVoteableError < StandardError 24 | def initialize 25 | super "Invalid voteable." 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/make_voteable.rb: -------------------------------------------------------------------------------- 1 | require 'make_voteable/voting' 2 | require 'make_voteable/voteable' 3 | require 'make_voteable/voter' 4 | require 'make_voteable/exceptions' 5 | 6 | module MakeVoteable 7 | def voteable? 8 | false 9 | end 10 | 11 | def voter? 12 | false 13 | end 14 | 15 | # Specify a model as voteable. 16 | # 17 | # Example: 18 | # class Question < ActiveRecord::Base 19 | # make_voteable 20 | # end 21 | def make_voteable 22 | include Voteable 23 | end 24 | 25 | # Specify a model as voter. 26 | # 27 | # Example: 28 | # class User < ActiveRecord::Base 29 | # make_voter 30 | # end 31 | def make_voter 32 | include Voter 33 | end 34 | end 35 | 36 | ActiveRecord::Base.extend MakeVoteable 37 | -------------------------------------------------------------------------------- /spec/generators/make_voteable_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'action_controller' 3 | require 'generator_spec/test_case' 4 | require 'generators/make_voteable/make_voteable_generator' 5 | 6 | describe MakeVoteableGenerator do 7 | include GeneratorSpec::TestCase 8 | destination File.expand_path("/tmp", __FILE__) 9 | tests MakeVoteableGenerator 10 | 11 | before do 12 | prepare_destination 13 | run_generator 14 | end 15 | 16 | specify do 17 | destination_root.should have_structure { 18 | directory "db" do 19 | directory "migrate" do 20 | migration "create_make_voteable_tables" do 21 | contains "class CreateMakeVoteableTables" 22 | contains "create_table :votings" 23 | end 24 | end 25 | end 26 | } 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/generators/make_voteable/templates/migration.rb: -------------------------------------------------------------------------------- 1 | class CreateMakeVoteableTables < ActiveRecord::Migration 2 | def self.up 3 | create_table :votings do |t| 4 | t.string :voteable_type 5 | t.integer :voteable_id 6 | t.string :voter_type 7 | t.integer :voter_id 8 | t.boolean :up_vote, :null => false 9 | 10 | t.timestamps 11 | end 12 | 13 | add_index :votings, [:voteable_type, :voteable_id] 14 | add_index :votings, [:voter_type, :voter_id] 15 | add_index :votings, [:voteable_type, :voteable_id, :voter_type, :voter_id], :name => "unique_voters", :unique => true 16 | end 17 | 18 | def self.down 19 | remove_index :votings, :column => [:voteable_type, :voteable_id] 20 | remove_index :votings, :column => [:voter_type, :voter_id] 21 | remove_index :votings, :name => "unique_voters" 22 | 23 | drop_table :votings 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | require 'logger' 4 | require 'rspec' 5 | require 'active_record' 6 | require 'database_cleaner' 7 | 8 | $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') 9 | require 'make_voteable' 10 | 11 | ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log') 12 | ActiveRecord::Base.configurations = YAML::load_file(File.dirname(__FILE__) + '/database.yml') 13 | ActiveRecord::Base.establish_connection(ENV['DB'] || 'sqlite3') 14 | 15 | ActiveRecord::Base.silence do 16 | ActiveRecord::Migration.verbose = false 17 | 18 | load(File.dirname(__FILE__) + '/schema.rb') 19 | load(File.dirname(__FILE__) + '/models.rb') 20 | end 21 | 22 | RSpec.configure do |config| 23 | config.filter_run :focus => true 24 | config.run_all_when_everything_filtered = true 25 | config.filter_run_excluding :exclude => true 26 | 27 | config.mock_with :rspec 28 | 29 | config.before(:suite) do 30 | DatabaseCleaner.strategy = :truncation 31 | DatabaseCleaner.clean 32 | end 33 | 34 | config.after(:each) do 35 | DatabaseCleaner.clean 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2011 by Kai Schlamp 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. -------------------------------------------------------------------------------- /make_voteable.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path("../lib/make_voteable/version", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "make_voteable" 6 | s.version = MakeVoteable::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ["Kai Schlamp"] 9 | s.email = ["schlamp@gmx.de"] 10 | s.homepage = "http://github.com/medihack/make_voteable" 11 | s.summary = "Rails 3 voting extension" 12 | s.description = "A user-centric voting extension for Rails 3 applications." 13 | 14 | s.required_rubygems_version = ">= 1.3.6" 15 | s.rubyforge_project = "make_voteable" 16 | 17 | s.add_dependency "activerecord", ">= 3.0" 18 | s.add_development_dependency "bundler", ">= 1.0.0" 19 | s.add_development_dependency "rspec", "~> 2.5.0" 20 | s.add_development_dependency "database_cleaner", "0.6.7" 21 | s.add_development_dependency "sqlite3-ruby", "~> 1.3.0" 22 | s.add_development_dependency "generator_spec", "~> 0.8.2" 23 | 24 | s.files = `git ls-files`.split("\n") 25 | s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact 26 | s.require_path = 'lib' 27 | end 28 | -------------------------------------------------------------------------------- /spec/schema.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Schema.define :version => 0 do 2 | create_table :voteable_models, :force => true do |t| 3 | t.string :name 4 | t.integer :up_votes, :null => false, :default => 0 5 | t.integer :down_votes, :null => false, :default => 0 6 | end 7 | 8 | create_table :voter_models, :force => true do |t| 9 | t.string :name 10 | t.integer :up_votes, :null => false, :default => 0 11 | t.integer :down_votes, :null => false, :default => 0 12 | end 13 | 14 | create_table :invalid_voteable_models, :force => true do |t| 15 | t.string :name 16 | end 17 | 18 | create_table :votings, :force => true do |t| 19 | t.string :voteable_type 20 | t.integer :voteable_id 21 | t.string :voter_type 22 | t.integer :voter_id 23 | t.boolean :up_vote, :null => false 24 | 25 | t.timestamps 26 | end 27 | 28 | create_table :users, :force => true do |t| 29 | t.integer :up_votes, :null => false, :default => 0 30 | t.integer :down_votes, :null => false, :default => 0 31 | t.string :type 32 | end 33 | 34 | add_index :votings, [:voteable_type, :voteable_id] 35 | add_index :votings, [:voter_type, :voter_id] 36 | add_index :votings, [:voteable_type, :voteable_id, :voter_type, :voter_id], :name => "unique_voters", :unique => true 37 | end 38 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = MakeVoteable 2 | 3 | MakeVoteable is an extension for building a user-centric voting system for Rails 3 applications. 4 | It currently supports ActiveRecord models. 5 | 6 | * This repository is unfortunately no longer maintained. If this library is still relevant and you want to maintain it, I am happy to hand this repository over. 7 | 8 | == Installation 9 | 10 | add MakeVoteable to your Gemfile 11 | 12 | gem 'make_voteable' 13 | 14 | afterwards execute 15 | 16 | bundle install 17 | 18 | generate the required migration file 19 | 20 | rails generate make_voteable 21 | 22 | also add +up_votes+ and +down_votes+ columns to the voter (e.g. User) and voteable (e.g. Question) model migrations 23 | 24 | add_column :users, :up_votes, :integer, :null => false, :default => 0 25 | add_column :users, :down_votes, :integer, :null => false, :default => 0 26 | add_column :questions, :up_votes, :integer, :null => false, :default => 0 27 | add_column :questions, :down_votes, :integer, :null => false, :default => 0 28 | 29 | migrate the database 30 | 31 | rake db:migrate 32 | 33 | == Usage 34 | 35 | # Specify a voteable model. 36 | class Question < ActiveRecord::Base 37 | make_voteable 38 | end 39 | 40 | # Specify a voter model. 41 | class User < ActiveRecord::Base 42 | make_voter 43 | end 44 | 45 | # Votes up the question by the user. 46 | # If the user already voted the question up then an AlreadyVotedError is raised. 47 | # If the same user already voted the question down then the vote is changed to an up vote. 48 | user.up_vote(question) 49 | 50 | # Votes the question up, but without raising an AlreadyVotedError when the user 51 | # already voted the question up (it just ignores the vote). 52 | user.up_vote!(question) 53 | 54 | # Votes down the question by the user. 55 | # If the user already voted the question down then an AlreadyVotedError is raised. 56 | # If the same user already voted the question up then the vote is changed to an down vote. 57 | user.down_vote(question) 58 | 59 | # Votes the question down, but without raising an AlreadyVotedError when the user 60 | # already voted the question down (it just ignores the vote). 61 | user.down_vote!(question) 62 | 63 | # Clears a already done vote by an user. 64 | # If the user didn't vote for the question then a NotVotedError is raised. 65 | user.unvote(question) 66 | 67 | # Does not raise a NotVotedError if the user didn't vote for the question 68 | # (it just ignores the unvote). 69 | user.unvote!(question) 70 | 71 | # The number of up votes for this question. 72 | question.up_votes 73 | 74 | # The number of down votes for this question. 75 | question.down_votes 76 | 77 | # The number of up votes the user did. 78 | user.up_votes 79 | 80 | # The number of down votes the user did. 81 | user.down_votes 82 | 83 | # up votes - down votes (may also be negative if there are more down votes than up votes) 84 | question.votes 85 | 86 | # Returns true if the question was voted by the user 87 | user.voted?(question) 88 | 89 | # Returns true if the question was up voted by the user, false otherwise 90 | user.up_voted?(question) 91 | 92 | # Returns true if the question was down voted by the user, false otherwise 93 | user.down_voted?(question) 94 | 95 | # Access votings through voter 96 | voting = user.votings.first 97 | voting.up_vote? # true if up vote, false if down vote 98 | 99 | # Access votings through voteable 100 | voting = question.votings.first 101 | voting.up_vote? # true if up vote, false if down vote 102 | 103 | == Testing 104 | 105 | MakeVoteable uses RSpec for testing and has a rake task for executing the provided specs 106 | 107 | rake spec 108 | 109 | 110 | Copyright © 2010-2011 Kai Schlamp (http://www.medihack.org), released under the MIT license 111 | -------------------------------------------------------------------------------- /lib/make_voteable/voter.rb: -------------------------------------------------------------------------------- 1 | module MakeVoteable 2 | module Voter 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | has_many :votings, :class_name => "MakeVoteable::Voting", :as => :voter 7 | end 8 | 9 | module ClassMethods 10 | def voter? 11 | true 12 | end 13 | end 14 | 15 | # Up vote a +voteable+. 16 | # Raises an AlreadyVotedError if the voter already up voted the voteable. 17 | # Changes a down vote to an up vote if the the voter already down voted the voteable. 18 | def up_vote(voteable) 19 | check_voteable(voteable) 20 | 21 | voting = fetch_voting(voteable) 22 | 23 | if voting 24 | if voting.up_vote 25 | raise Exceptions::AlreadyVotedError.new(true) 26 | else 27 | voting.up_vote = true 28 | voteable.down_votes -= 1 29 | self.down_votes -= 1 if has_attribute?(:down_votes) 30 | end 31 | else 32 | voting = Voting.create(:voteable => voteable, :voter_id => self.id, :voter_type => self.class.to_s, :up_vote => true) 33 | end 34 | 35 | voteable.up_votes += 1 36 | self.up_votes += 1 if has_attribute?(:up_votes) 37 | 38 | Voting.transaction do 39 | save 40 | voteable.save 41 | voting.save 42 | end 43 | 44 | true 45 | end 46 | 47 | # Up votes the +voteable+, but doesn't raise an error if the votelable was already up voted. 48 | # The vote is simply ignored then. 49 | def up_vote!(voteable) 50 | begin 51 | up_vote(voteable) 52 | success = true 53 | rescue Exceptions::AlreadyVotedError 54 | success = false 55 | end 56 | success 57 | end 58 | 59 | # Down vote a +voteable+. 60 | # Raises an AlreadyVotedError if the voter already down voted the voteable. 61 | # Changes an up vote to a down vote if the the voter already up voted the voteable. 62 | def down_vote(voteable) 63 | check_voteable(voteable) 64 | 65 | voting = fetch_voting(voteable) 66 | 67 | if voting 68 | unless voting.up_vote 69 | raise Exceptions::AlreadyVotedError.new(false) 70 | else 71 | voting.up_vote = false 72 | voteable.up_votes -= 1 73 | self.up_votes -= 1 if has_attribute?(:up_votes) 74 | end 75 | else 76 | voting = Voting.create(:voteable => voteable, :voter_id => self.id, :voter_type => self.class.to_s, :up_vote => false) 77 | end 78 | 79 | voteable.down_votes += 1 80 | self.down_votes += 1 if has_attribute?(:down_votes) 81 | 82 | Voting.transaction do 83 | save 84 | voteable.save 85 | voting.save 86 | end 87 | 88 | true 89 | end 90 | 91 | # Down votes the +voteable+, but doesn't raise an error if the votelable was already down voted. 92 | # The vote is simply ignored then. 93 | def down_vote!(voteable) 94 | begin 95 | down_vote(voteable) 96 | success = true 97 | rescue Exceptions::AlreadyVotedError 98 | success = false 99 | end 100 | success 101 | end 102 | 103 | # Clears an already done vote on a +voteable+. 104 | # Raises a NotVotedError if the voter didn't voted for the voteable. 105 | def unvote(voteable) 106 | check_voteable(voteable) 107 | 108 | voting = fetch_voting(voteable) 109 | 110 | raise Exceptions::NotVotedError unless voting 111 | 112 | if voting.up_vote 113 | voteable.up_votes -= 1 114 | self.up_votes -= 1 if has_attribute?(:up_votes) 115 | else 116 | voteable.down_votes -= 1 117 | self.down_votes -= 1 if has_attribute?(:down_votes) 118 | end 119 | 120 | Voting.transaction do 121 | save 122 | voteable.save 123 | voting.destroy 124 | end 125 | 126 | true 127 | end 128 | 129 | # Clears an already done vote on a +voteable+, but doesn't raise an error if 130 | # the voteable was not voted. It ignores the unvote then. 131 | def unvote!(voteable) 132 | begin 133 | unvote(voteable) 134 | success = true 135 | rescue Exceptions::NotVotedError 136 | success = false 137 | end 138 | success 139 | end 140 | 141 | # Returns true if the voter voted for the +voteable+. 142 | def voted?(voteable) 143 | check_voteable(voteable) 144 | voting = fetch_voting(voteable) 145 | !voting.nil? 146 | end 147 | 148 | # Returns true if the voter up voted the +voteable+. 149 | def up_voted?(voteable) 150 | check_voteable(voteable) 151 | voting = fetch_voting(voteable) 152 | return false if voting.nil? 153 | return true if voting.has_attribute?(:up_vote) && voting.up_vote 154 | false 155 | end 156 | 157 | # Returns true if the voter down voted the +voteable+. 158 | def down_voted?(voteable) 159 | check_voteable(voteable) 160 | voting = fetch_voting(voteable) 161 | return false if voting.nil? 162 | return true if voting.has_attribute?(:up_vote) && !voting.up_vote 163 | false 164 | end 165 | 166 | private 167 | 168 | def fetch_voting(voteable) 169 | Voting.where( 170 | :voteable_type => voteable.class.to_s, 171 | :voteable_id => voteable.id, 172 | :voter_type => self.class.to_s, 173 | :voter_id => self.id).try(:first) 174 | end 175 | 176 | def check_voteable(voteable) 177 | raise Exceptions::InvalidVoteableError unless voteable.class.voteable? 178 | end 179 | end 180 | end 181 | -------------------------------------------------------------------------------- /spec/lib/make_voteable_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | describe "Inheritance" do 4 | let(:voteable) { VoteableModel.create(:name => "Votable 1") } 5 | 6 | describe "user (User < ActiveRecord::Base)" do 7 | let(:user) { User.create } 8 | 9 | before(:each) do 10 | user.up_vote(voteable) 11 | end 12 | 13 | it { MakeVoteable::Voting.first.voter_type.should == 'User' } 14 | end 15 | 16 | describe "admin (Admin < User)" do 17 | let(:admin) { Admin.create } 18 | 19 | before(:each) do 20 | admin.up_vote(voteable) 21 | end 22 | 23 | it { MakeVoteable::Voting.first.voter_type.should == 'Admin' } 24 | it { MakeVoteable::Voting.first.voter_type.should_not == 'User' } 25 | end 26 | end 27 | 28 | describe "Make Voteable" do 29 | before(:each) do 30 | @voteable = VoteableModel.create(:name => "Votable 1") 31 | @voter = VoterModel.create(:name => "Voter 1") 32 | end 33 | 34 | it "should create a voteable instance" do 35 | @voteable.class.should == VoteableModel 36 | @voteable.class.voteable?.should == true 37 | end 38 | 39 | it "should create a voter instance" do 40 | @voter.class.should == VoterModel 41 | @voter.class.voter?.should == true 42 | end 43 | 44 | it "should get correct vote summary" do 45 | @voter.up_vote(@voteable).should == true 46 | @voteable.votes.should == 1 47 | @voter.down_vote(@voteable).should == true 48 | @voteable.votes.should == -1 49 | @voter.unvote(@voteable).should == true 50 | @voteable.votes.should == 0 51 | end 52 | 53 | it "voteable should have up vote votings" do 54 | @voteable.votings.length.should == 0 55 | @voter.up_vote(@voteable) 56 | @voteable.votings.reload.length.should == 1 57 | @voteable.votings[0].up_vote?.should be_true 58 | end 59 | 60 | it "voter should have up vote votings" do 61 | @voter.votings.length.should == 0 62 | @voter.up_vote(@voteable) 63 | @voter.votings.reload.length.should == 1 64 | @voter.votings[0].up_vote?.should be_true 65 | end 66 | 67 | it "voteable should have down vote votings" do 68 | @voteable.votings.length.should == 0 69 | @voter.down_vote(@voteable) 70 | @voteable.votings.reload.length.should == 1 71 | @voteable.votings[0].up_vote?.should be_false 72 | end 73 | 74 | it "voter should have down vote votings" do 75 | @voter.votings.length.should == 0 76 | @voter.down_vote(@voteable) 77 | @voter.votings.reload.length.should == 1 78 | @voter.votings[0].up_vote?.should be_false 79 | end 80 | 81 | describe "up vote" do 82 | it "should increase up votes of voteable by one" do 83 | @voteable.up_votes.should == 0 84 | @voter.up_vote(@voteable) 85 | @voteable.up_votes.should == 1 86 | end 87 | 88 | it "should increase up votes of voter by one" do 89 | @voter.up_votes.should == 0 90 | @voter.up_vote(@voteable) 91 | @voter.up_votes.should == 1 92 | end 93 | 94 | it "should create a voting" do 95 | MakeVoteable::Voting.count.should == 0 96 | @voter.up_vote(@voteable) 97 | MakeVoteable::Voting.count.should == 1 98 | voting = MakeVoteable::Voting.first 99 | voting.voteable.should == @voteable 100 | voting.voter.should == @voter 101 | voting.up_vote.should == true 102 | end 103 | 104 | it "should only allow a voter to up vote a voteable once" do 105 | @voter.up_vote(@voteable) 106 | lambda { @voter.up_vote(@voteable) }.should raise_error(MakeVoteable::Exceptions::AlreadyVotedError) 107 | end 108 | 109 | it "should only allow a voter to up vote a voteable once without raising an error" do 110 | @voter.up_vote!(@voteable) 111 | lambda { 112 | @voter.up_vote!(@voteable).should == false 113 | }.should_not raise_error(MakeVoteable::Exceptions::AlreadyVotedError) 114 | MakeVoteable::Voting.count.should == 1 115 | end 116 | 117 | it "should change a down vote to an up vote" do 118 | @voter.down_vote(@voteable) 119 | @voteable.up_votes.should == 0 120 | @voteable.down_votes.should == 1 121 | @voter.up_votes.should == 0 122 | @voter.down_votes.should == 1 123 | MakeVoteable::Voting.count.should == 1 124 | MakeVoteable::Voting.first.up_vote.should be_false 125 | @voter.up_vote(@voteable) 126 | @voteable.up_votes.should == 1 127 | @voteable.down_votes.should == 0 128 | @voter.up_votes.should == 1 129 | @voter.down_votes.should == 0 130 | MakeVoteable::Voting.count.should == 1 131 | MakeVoteable::Voting.first.up_vote.should be_true 132 | end 133 | 134 | it "should allow up votes from different voters" do 135 | @voter2 = VoterModel.create(:name => "Voter 2") 136 | @voter.up_vote(@voteable) 137 | @voter2.up_vote(@voteable) 138 | @voteable.up_votes.should == 2 139 | MakeVoteable::Voting.count.should == 2 140 | end 141 | 142 | it "should raise an error for an invalid voteable" do 143 | invalid_voteable = InvalidVoteableModel.create 144 | lambda { @voter.up_vote(invalid_voteable) }.should raise_error(MakeVoteable::Exceptions::InvalidVoteableError) 145 | end 146 | 147 | it "should check if voter up voted voteable" do 148 | @voter.up_vote(@voteable) 149 | @voter.voted?(@voteable).should be_true 150 | @voter.up_voted?(@voteable).should be_true 151 | @voter.down_voted?(@voteable).should be_false 152 | end 153 | end 154 | 155 | describe "vote down" do 156 | it "should decrease down votes of voteable by one" do 157 | @voteable.down_votes.should == 0 158 | @voter.down_vote(@voteable) 159 | @voteable.down_votes.should == 1 160 | end 161 | 162 | it "should decrease down votes of voter by one" do 163 | @voter.down_votes.should == 0 164 | @voter.down_vote(@voteable) 165 | @voter.down_votes.should == 1 166 | end 167 | 168 | it "should create a voting" do 169 | MakeVoteable::Voting.count.should == 0 170 | @voter.down_vote(@voteable) 171 | MakeVoteable::Voting.count.should == 1 172 | voting = MakeVoteable::Voting.first 173 | voting.voteable.should == @voteable 174 | voting.voter.should == @voter 175 | voting.up_vote.should == false 176 | end 177 | 178 | it "should only allow a voter to down vote a voteable once" do 179 | @voter.down_vote(@voteable) 180 | lambda { @voter.down_vote(@voteable) }.should raise_error(MakeVoteable::Exceptions::AlreadyVotedError) 181 | end 182 | 183 | it "should only allow a voter to down vote a voteable once without raising an error" do 184 | @voter.down_vote!(@voteable) 185 | lambda { 186 | @voter.down_vote!(@voteable).should == false 187 | }.should_not raise_error(MakeVoteable::Exceptions::AlreadyVotedError) 188 | MakeVoteable::Voting.count.should == 1 189 | end 190 | 191 | it "should change an up vote to a down vote" do 192 | @voter.up_vote(@voteable) 193 | @voteable.up_votes.should == 1 194 | @voteable.down_votes.should == 0 195 | @voter.up_votes.should == 1 196 | @voter.down_votes.should == 0 197 | MakeVoteable::Voting.count.should == 1 198 | MakeVoteable::Voting.first.up_vote.should be_true 199 | @voter.down_vote(@voteable) 200 | @voteable.up_votes.should == 0 201 | @voteable.down_votes.should == 1 202 | @voter.up_votes.should == 0 203 | @voter.down_votes.should == 1 204 | MakeVoteable::Voting.count.should == 1 205 | MakeVoteable::Voting.first.up_vote.should be_false 206 | end 207 | 208 | it "should allow down votes from different voters" do 209 | @voter2 = VoterModel.create(:name => "Voter 2") 210 | @voter.down_vote(@voteable) 211 | @voter2.down_vote(@voteable) 212 | @voteable.down_votes.should == 2 213 | MakeVoteable::Voting.count.should == 2 214 | end 215 | 216 | it "should raise an error for an invalid voteable" do 217 | invalid_voteable = InvalidVoteableModel.create 218 | lambda { @voter.down_vote(invalid_voteable) }.should raise_error(MakeVoteable::Exceptions::InvalidVoteableError) 219 | end 220 | 221 | it "should check if voter down voted voteable" do 222 | @voter.down_vote(@voteable) 223 | @voter.voted?(@voteable).should be_true 224 | @voter.up_voted?(@voteable).should be_false 225 | @voter.down_voted?(@voteable).should be_true 226 | end 227 | end 228 | 229 | describe "unvote" do 230 | it "should decrease the up votes if up voted before" do 231 | @voter.up_vote(@voteable) 232 | @voteable.up_votes.should == 1 233 | @voter.up_votes.should == 1 234 | @voter.unvote(@voteable) 235 | @voteable.up_votes.should == 0 236 | @voter.up_votes.should == 0 237 | end 238 | 239 | it "should remove the voting" do 240 | @voter.up_vote(@voteable) 241 | MakeVoteable::Voting.count.should == 1 242 | @voter.unvote(@voteable) 243 | MakeVoteable::Voting.count.should == 0 244 | end 245 | 246 | it "should raise an error if voter didn't vote for the voteable" do 247 | lambda { @voter.unvote(@voteable) }.should raise_error(MakeVoteable::Exceptions::NotVotedError) 248 | end 249 | 250 | it "should not raise error if voter didn't vote for the voteable and unvote! is called" do 251 | lambda { 252 | @voter.unvote!(@voteable).should == false 253 | }.should_not raise_error(MakeVoteable::Exceptions::NotVotedError) 254 | end 255 | 256 | it "should raise an error for an invalid voteable" do 257 | invalid_voteable = InvalidVoteableModel.create 258 | lambda { @voter.unvote(invalid_voteable) }.should raise_error(MakeVoteable::Exceptions::InvalidVoteableError) 259 | end 260 | end 261 | end 262 | --------------------------------------------------------------------------------