make_voteable 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +100 -0
- data/Rakefile +5 -0
- data/lib/generators/make_voteable/make_voteable_generator.rb +20 -0
- data/lib/generators/make_voteable/templates/migration.rb +25 -0
- data/lib/make_voteable.rb +36 -0
- data/lib/make_voteable/exceptions.rb +33 -0
- data/lib/make_voteable/version.rb +3 -0
- data/lib/make_voteable/voteable.rb +17 -0
- data/lib/make_voteable/voter.rb +159 -0
- data/lib/make_voteable/voting.rb +8 -0
- data/make_voteable.gemspec +26 -0
- data/spec/database.yml +3 -0
- data/spec/database.yml.sample +3 -0
- data/spec/lib/make_voteable_spec.rb +202 -0
- data/spec/models.rb +10 -0
- data/spec/schema.rb +31 -0
- data/spec/spec_helper.rb +41 -0
- metadata +168 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
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.
|
data/README.rdoc
ADDED
@@ -0,0 +1,100 @@
|
|
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
|
+
== Installation
|
7
|
+
|
8
|
+
add MakeVoteable to your Gemfile
|
9
|
+
|
10
|
+
gem 'make_voteable'
|
11
|
+
|
12
|
+
afterwards execute
|
13
|
+
|
14
|
+
bundle install
|
15
|
+
|
16
|
+
generate the required migration file
|
17
|
+
|
18
|
+
rails generate make_voteable
|
19
|
+
|
20
|
+
also add +up_votes+ and +down_votes+ columns to the voter (e.g. User) and voteable (e.g. Question) model migrations
|
21
|
+
|
22
|
+
add_column :users, :up_votes, :integer, :null => false, :default => 0
|
23
|
+
add_column :users, :down_votes, :integer, :null => false, :default => 0
|
24
|
+
add_column :questions, :up_votes, :integer, :null => false, :default => 0
|
25
|
+
add_column :questions, :down_votes, :integer, :null => false, :default => 0
|
26
|
+
|
27
|
+
migrate the database
|
28
|
+
|
29
|
+
rails db:migrate
|
30
|
+
|
31
|
+
== Usage
|
32
|
+
|
33
|
+
# Specify a voteable model.
|
34
|
+
class Question < ActiveRecord::Base
|
35
|
+
make_voteable
|
36
|
+
end
|
37
|
+
|
38
|
+
# Specify a voter model.
|
39
|
+
class User < ActiveRecord::Base
|
40
|
+
make_voter
|
41
|
+
end
|
42
|
+
|
43
|
+
# Votes up the question by the user.
|
44
|
+
# If the user already voted the question up then an AlreadyVotedError is raised.
|
45
|
+
# If the same user already voted the question down then the vote is changed to an up vote.
|
46
|
+
user.up_vote(question)
|
47
|
+
|
48
|
+
# Votes the question up, but without raising an AlreadyVotedError when the user
|
49
|
+
# already voted the question up (it just ignores the vote).
|
50
|
+
user.up_vote!(question)
|
51
|
+
|
52
|
+
# Votes down the question by the user.
|
53
|
+
# If the user already voted the question down then an AlreadyVotedError is raised.
|
54
|
+
# If the same user already voted the question up then the vote is changed to an down vote.
|
55
|
+
user.down_vote(question)
|
56
|
+
|
57
|
+
# Votes the question down, but without raising an AlreadyVotedError when the user
|
58
|
+
# already voted the question down (it just ignores the vote).
|
59
|
+
user.down_vote!(question)
|
60
|
+
|
61
|
+
# Clears a already done vote by an user.
|
62
|
+
# If the user didn't vote for the question then a NotVotedError is raised.
|
63
|
+
user.unvote(question)
|
64
|
+
|
65
|
+
# Does not raise a NotVotedError if the user didn't vote for the question
|
66
|
+
# (it just ignores the unvote).
|
67
|
+
user.unvote!(question)
|
68
|
+
|
69
|
+
# The number of up votes for this question.
|
70
|
+
question.up_votes
|
71
|
+
|
72
|
+
# The number of down votes for this question.
|
73
|
+
question.down_votes
|
74
|
+
|
75
|
+
# The number of up votes the user did.
|
76
|
+
user.up_votes
|
77
|
+
|
78
|
+
# The number of down votes the user did.
|
79
|
+
user.down_votes
|
80
|
+
|
81
|
+
# up votes - down votes (may also be negative if there are more down votes than up votes)
|
82
|
+
question.votes
|
83
|
+
|
84
|
+
# Returns true if the question was voted by the user
|
85
|
+
user.voted?(question)
|
86
|
+
|
87
|
+
# Returns true if the question was up voted by the user, false otherwise
|
88
|
+
user.up_voted?(question)
|
89
|
+
|
90
|
+
# Returns true if the question was down voted by the user, false otherwise
|
91
|
+
user.down_voted?(question)
|
92
|
+
|
93
|
+
== Testing
|
94
|
+
|
95
|
+
MakeVoteable uses RSpec for testing and has a rake task for executing the provided specs
|
96
|
+
|
97
|
+
rake spec
|
98
|
+
|
99
|
+
|
100
|
+
Copyright © 2010-2011 Kai Schlamp (http://www.medihack.org), released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
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
|
@@ -0,0 +1,25 @@
|
|
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
|
@@ -0,0 +1,36 @@
|
|
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
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MakeVoteable
|
2
|
+
module Exceptions
|
3
|
+
class AlreadyVotedError < StandardError
|
4
|
+
attr_reader :up_vote
|
5
|
+
|
6
|
+
def initialize(up_vote)
|
7
|
+
@up_vote = up_vote
|
8
|
+
end
|
9
|
+
|
10
|
+
def message
|
11
|
+
if @up_vote
|
12
|
+
vote = "up voted"
|
13
|
+
else
|
14
|
+
vote = "down voted"
|
15
|
+
end
|
16
|
+
|
17
|
+
"The voteable was already #{vote} by the voter."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class NotVotedError < StandardError
|
22
|
+
def message
|
23
|
+
"The voteable was not voted by the voter."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class InvalidVoteableError < StandardError
|
28
|
+
def message
|
29
|
+
"Invalid voteable."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MakeVoteable
|
2
|
+
module Voteable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def voteable?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return the difference of down and up votes.
|
12
|
+
# May be negative if there are more down than up votes.
|
13
|
+
def votes
|
14
|
+
self.up_votes - self.down_votes
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module MakeVoteable
|
2
|
+
module Voter
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def voter?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Up vote a +voteable+.
|
12
|
+
# Raises an AlreadyVotedError if the voter already up voted the voteable.
|
13
|
+
# Changes a down vote to an up vote if the the voter already down voted the voteable.
|
14
|
+
def up_vote(voteable)
|
15
|
+
check_voteable(voteable)
|
16
|
+
|
17
|
+
voting = fetch_voting(voteable)
|
18
|
+
|
19
|
+
if voting
|
20
|
+
if voting.up_vote
|
21
|
+
raise MakeVoteable::Exceptions::AlreadyVotedError.new(true)
|
22
|
+
else
|
23
|
+
voting.up_vote = true
|
24
|
+
voteable.down_votes -= 1
|
25
|
+
self.down_votes -= 1 if self.has_attribute?(:down_votes)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
voting = Voting.create(:voteable => voteable, :voter => self, :up_vote => true)
|
29
|
+
end
|
30
|
+
|
31
|
+
voteable.up_votes += 1
|
32
|
+
self.up_votes += 1 if self.has_attribute?(:up_votes)
|
33
|
+
|
34
|
+
MakeVoteable::Voting.transaction do
|
35
|
+
self.save
|
36
|
+
voteable.save
|
37
|
+
voting.save
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Up votes the +voteable+, but doesn't raise an error if the votelable was already up voted.
|
42
|
+
# The vote is simply ignored then.
|
43
|
+
def up_vote!(voteable)
|
44
|
+
begin
|
45
|
+
up_vote(voteable)
|
46
|
+
rescue MakeVoteable::Exceptions::AlreadyVotedError
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Down vote a +voteable+.
|
51
|
+
# Raises an AlreadyVotedError if the voter already down voted the voteable.
|
52
|
+
# Changes an up vote to a down vote if the the voter already up voted the voteable.
|
53
|
+
def down_vote(voteable)
|
54
|
+
check_voteable(voteable)
|
55
|
+
|
56
|
+
voting = fetch_voting(voteable)
|
57
|
+
|
58
|
+
if voting
|
59
|
+
unless voting.up_vote
|
60
|
+
raise MakeVoteable::Exceptions::AlreadyVotedError.new(false)
|
61
|
+
else
|
62
|
+
voting.up_vote = false
|
63
|
+
voteable.up_votes -= 1
|
64
|
+
self.up_votes -= 1 if self.has_attribute?(:up_votes)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
voting = Voting.create(:voteable => voteable, :voter => self, :up_vote => false)
|
68
|
+
end
|
69
|
+
|
70
|
+
voteable.down_votes += 1
|
71
|
+
self.down_votes += 1 if self.has_attribute?(:down_votes)
|
72
|
+
|
73
|
+
MakeVoteable::Voting.transaction do
|
74
|
+
self.save
|
75
|
+
voteable.save
|
76
|
+
voting.save
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Down votes the +voteable+, but doesn't raise an error if the votelable was already down voted.
|
81
|
+
# The vote is simply ignored then.
|
82
|
+
def down_vote!(voteable)
|
83
|
+
begin
|
84
|
+
down_vote(voteable)
|
85
|
+
rescue MakeVoteable::Exceptions::AlreadyVotedError
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Clears an already done vote on a +voteable+.
|
90
|
+
# Raises a NotVotedError if the voter didn't voted for the voteable.
|
91
|
+
def unvote(voteable)
|
92
|
+
check_voteable(voteable)
|
93
|
+
|
94
|
+
voting = fetch_voting(voteable)
|
95
|
+
|
96
|
+
raise MakeVoteable::Exceptions::NotVotedError unless voting
|
97
|
+
|
98
|
+
if voting.up_vote
|
99
|
+
voteable.up_votes -= 1
|
100
|
+
self.up_votes -= 1 if self.has_attribute?(:up_votes)
|
101
|
+
else
|
102
|
+
voteable.down_votes -= 1
|
103
|
+
self.down_votes -= 1 if self.has_attribute?(:down_votes)
|
104
|
+
end
|
105
|
+
|
106
|
+
MakeVoteable::Voting.transaction do
|
107
|
+
self.save
|
108
|
+
voteable.save
|
109
|
+
voting.destroy
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Clears an already done vote on a +voteable+, but doesn't raise an error if
|
114
|
+
# the voteable was not voted. It ignores the unvote then.
|
115
|
+
def unvote!(voteable)
|
116
|
+
begin
|
117
|
+
unvote(voteable)
|
118
|
+
rescue MakeVoteable::Exceptions::NotVotedError
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns true if the voter voted for the +voteable+.
|
123
|
+
def voted?(voteable)
|
124
|
+
check_voteable(voteable)
|
125
|
+
voting = fetch_voting(voteable)
|
126
|
+
!voting.nil?
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns true if the voter up voted the +voteable+.
|
130
|
+
def up_voted?(voteable)
|
131
|
+
check_voteable(voteable)
|
132
|
+
voting = fetch_voting(voteable)
|
133
|
+
return true if voting.has_attribute?(:up_vote) && voting.up_vote
|
134
|
+
false
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns true if the voter down voted the +voteable+.
|
138
|
+
def down_voted?(voteable)
|
139
|
+
check_voteable(voteable)
|
140
|
+
voting = fetch_voting(voteable)
|
141
|
+
return true if voting.has_attribute?(:up_vote) && !voting.up_vote
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def fetch_voting(voteable)
|
148
|
+
Voting.where(
|
149
|
+
:voteable_type => voteable.class.to_s,
|
150
|
+
:voteable_id => voteable.id,
|
151
|
+
:voter_type => self.class.to_s,
|
152
|
+
:voter_id => self.id).try(:first)
|
153
|
+
end
|
154
|
+
|
155
|
+
def check_voteable(voteable)
|
156
|
+
raise MakeVoteable::Exceptions::InvalidVoteableError unless voteable.class.voteable?
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,26 @@
|
|
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.0"
|
18
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
19
|
+
s.add_development_dependency "rspec", ">= 2.0.0"
|
20
|
+
s.add_development_dependency "database_cleaner", "0.5.2"
|
21
|
+
s.add_development_dependency "sqlite3-ruby", ">= 1.3.0"
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
25
|
+
s.require_path = 'lib'
|
26
|
+
end
|
data/spec/database.yml
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "Make Voteable" do
|
4
|
+
before(:each) do
|
5
|
+
@voteable = VoteableModel.create(:name => "Votable 1")
|
6
|
+
@voter = VoterModel.create(:name => "Voter 1")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should create a voteable instance" do
|
10
|
+
@voteable.class.should == VoteableModel
|
11
|
+
@voteable.class.voteable?.should == true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should create a voter instance" do
|
15
|
+
@voter.class.should == VoterModel
|
16
|
+
@voter.class.voter?.should == true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should get correct vote summary" do
|
20
|
+
@voter.up_vote(@voteable)
|
21
|
+
@voteable.votes.should == 1
|
22
|
+
@voter.down_vote(@voteable)
|
23
|
+
@voteable.votes.should == -1
|
24
|
+
@voter.unvote(@voteable)
|
25
|
+
@voteable.votes.should == 0
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "up vote" do
|
29
|
+
it "should increase up votes of voteable by one" do
|
30
|
+
@voteable.up_votes.should == 0
|
31
|
+
@voter.up_vote(@voteable)
|
32
|
+
@voteable.up_votes.should == 1
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should increase up votes of voter by one" do
|
36
|
+
@voter.up_votes.should == 0
|
37
|
+
@voter.up_vote(@voteable)
|
38
|
+
@voter.up_votes.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should create a voting" do
|
42
|
+
MakeVoteable::Voting.count.should == 0
|
43
|
+
@voter.up_vote(@voteable)
|
44
|
+
MakeVoteable::Voting.count.should == 1
|
45
|
+
voting = MakeVoteable::Voting.first
|
46
|
+
voting.voteable.should == @voteable
|
47
|
+
voting.voter.should == @voter
|
48
|
+
voting.up_vote.should == true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should only allow a voter to up vote a voteable once" do
|
52
|
+
@voter.up_vote(@voteable)
|
53
|
+
lambda { @voter.up_vote(@voteable) }.should raise_error(MakeVoteable::Exceptions::AlreadyVotedError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should only allow a voter to up vote a voteable once without raising an error" do
|
57
|
+
@voter.up_vote!(@voteable)
|
58
|
+
lambda { @voter.up_vote!(@voteable) }.should_not raise_error(MakeVoteable::Exceptions::AlreadyVotedError)
|
59
|
+
MakeVoteable::Voting.count.should == 1
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should change a down vote to an up vote" do
|
63
|
+
@voter.down_vote(@voteable)
|
64
|
+
@voteable.up_votes.should == 0
|
65
|
+
@voteable.down_votes.should == 1
|
66
|
+
@voter.up_votes.should == 0
|
67
|
+
@voter.down_votes.should == 1
|
68
|
+
MakeVoteable::Voting.count.should == 1
|
69
|
+
MakeVoteable::Voting.first.up_vote.should be_false
|
70
|
+
@voter.up_vote(@voteable)
|
71
|
+
@voteable.up_votes.should == 1
|
72
|
+
@voteable.down_votes.should == 0
|
73
|
+
@voter.up_votes.should == 1
|
74
|
+
@voter.down_votes.should == 0
|
75
|
+
MakeVoteable::Voting.count.should == 1
|
76
|
+
MakeVoteable::Voting.first.up_vote.should be_true
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should allow up votes from different voters" do
|
80
|
+
@voter2 = VoterModel.create(:name => "Voter 2")
|
81
|
+
@voter.up_vote(@voteable)
|
82
|
+
@voter2.up_vote(@voteable)
|
83
|
+
@voteable.up_votes.should == 2
|
84
|
+
MakeVoteable::Voting.count.should == 2
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should raise an error for an invalid voteable" do
|
88
|
+
invalid_voteable = InvalidVoteableModel.create
|
89
|
+
lambda { @voter.up_vote(invalid_voteable) }.should raise_error(MakeVoteable::Exceptions::InvalidVoteableError)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should check if voter up voted voteable" do
|
93
|
+
@voter.up_vote(@voteable)
|
94
|
+
@voter.voted?(@voteable).should be_true
|
95
|
+
@voter.up_voted?(@voteable).should be_true
|
96
|
+
@voter.down_voted?(@voteable).should be_false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "vote down" do
|
101
|
+
it "should decrease down votes of voteable by one" do
|
102
|
+
@voteable.down_votes.should == 0
|
103
|
+
@voter.down_vote(@voteable)
|
104
|
+
@voteable.down_votes.should == 1
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should decrease down votes of voter by one" do
|
108
|
+
@voter.down_votes.should == 0
|
109
|
+
@voter.down_vote(@voteable)
|
110
|
+
@voter.down_votes.should == 1
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should create a voting" do
|
114
|
+
MakeVoteable::Voting.count.should == 0
|
115
|
+
@voter.down_vote(@voteable)
|
116
|
+
MakeVoteable::Voting.count.should == 1
|
117
|
+
voting = MakeVoteable::Voting.first
|
118
|
+
voting.voteable.should == @voteable
|
119
|
+
voting.voter.should == @voter
|
120
|
+
voting.up_vote.should == false
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should only allow a voter to down vote a voteable once" do
|
124
|
+
@voter.down_vote(@voteable)
|
125
|
+
lambda { @voter.down_vote(@voteable) }.should raise_error(MakeVoteable::Exceptions::AlreadyVotedError)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should only allow a voter to down vote a voteable once without raising an error" do
|
129
|
+
@voter.down_vote!(@voteable)
|
130
|
+
lambda { @voter.down_vote!(@voteable) }.should_not raise_error(MakeVoteable::Exceptions::AlreadyVotedError)
|
131
|
+
MakeVoteable::Voting.count.should == 1
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should change an up vote to a down vote" do
|
135
|
+
@voter.up_vote(@voteable)
|
136
|
+
@voteable.up_votes.should == 1
|
137
|
+
@voteable.down_votes.should == 0
|
138
|
+
@voter.up_votes.should == 1
|
139
|
+
@voter.down_votes.should == 0
|
140
|
+
MakeVoteable::Voting.count.should == 1
|
141
|
+
MakeVoteable::Voting.first.up_vote.should be_true
|
142
|
+
@voter.down_vote(@voteable)
|
143
|
+
@voteable.up_votes.should == 0
|
144
|
+
@voteable.down_votes.should == 1
|
145
|
+
@voter.up_votes.should == 0
|
146
|
+
@voter.down_votes.should == 1
|
147
|
+
MakeVoteable::Voting.count.should == 1
|
148
|
+
MakeVoteable::Voting.first.up_vote.should be_false
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should allow down votes from different voters" do
|
152
|
+
@voter2 = VoterModel.create(:name => "Voter 2")
|
153
|
+
@voter.down_vote(@voteable)
|
154
|
+
@voter2.down_vote(@voteable)
|
155
|
+
@voteable.down_votes.should == 2
|
156
|
+
MakeVoteable::Voting.count.should == 2
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should raise an error for an invalid voteable" do
|
160
|
+
invalid_voteable = InvalidVoteableModel.create
|
161
|
+
lambda { @voter.down_vote(invalid_voteable) }.should raise_error(MakeVoteable::Exceptions::InvalidVoteableError)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should check if voter down voted voteable" do
|
165
|
+
@voter.down_vote(@voteable)
|
166
|
+
@voter.voted?(@voteable).should be_true
|
167
|
+
@voter.up_voted?(@voteable).should be_false
|
168
|
+
@voter.down_voted?(@voteable).should be_true
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "unvote" do
|
173
|
+
it "should decrease the up votes if up voted before" do
|
174
|
+
@voter.up_vote(@voteable)
|
175
|
+
@voteable.up_votes.should == 1
|
176
|
+
@voter.up_votes.should == 1
|
177
|
+
@voter.unvote(@voteable)
|
178
|
+
@voteable.up_votes.should == 0
|
179
|
+
@voter.up_votes.should == 0
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should remove the voting" do
|
183
|
+
@voter.up_vote(@voteable)
|
184
|
+
MakeVoteable::Voting.count.should == 1
|
185
|
+
@voter.unvote(@voteable)
|
186
|
+
MakeVoteable::Voting.count.should == 0
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should raise an error if voter didn't vote for the voteable" do
|
190
|
+
lambda { @voter.unvote(@voteable) }.should raise_error(MakeVoteable::Exceptions::NotVotedError)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should not raise error if voter didn't vote for the voteable and unvote! is called" do
|
194
|
+
lambda { @voter.unvote!(@voteable) }.should_not raise_error(MakeVoteable::Exceptions::NotVotedError)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should raise an error for an invalid voteable" do
|
198
|
+
invalid_voteable = InvalidVoteableModel.create
|
199
|
+
lambda { @voter.unvote(invalid_voteable) }.should raise_error(MakeVoteable::Exceptions::InvalidVoteableError)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
data/spec/models.rb
ADDED
data/spec/schema.rb
ADDED
@@ -0,0 +1,31 @@
|
|
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
|
+
add_index :votings, [:voteable_type, :voteable_id]
|
29
|
+
add_index :votings, [:voter_type, :voter_id]
|
30
|
+
add_index :votings, [:voteable_type, :voteable_id, :voter_type, :voter_id], :name => "unique_voters", :unique => true
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
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 = :transaction
|
31
|
+
DatabaseCleaner.clean_with(:truncation)
|
32
|
+
end
|
33
|
+
|
34
|
+
config.before(:each) do
|
35
|
+
DatabaseCleaner.start
|
36
|
+
end
|
37
|
+
|
38
|
+
config.after(:each) do
|
39
|
+
DatabaseCleaner.clean
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: make_voteable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Kai Schlamp
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-10 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activerecord
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 3.0.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: bundler
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 23
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
version: 1.0.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 15
|
62
|
+
segments:
|
63
|
+
- 2
|
64
|
+
- 0
|
65
|
+
- 0
|
66
|
+
version: 2.0.0
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: database_cleaner
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - "="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 15
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
- 5
|
81
|
+
- 2
|
82
|
+
version: 0.5.2
|
83
|
+
type: :development
|
84
|
+
version_requirements: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: sqlite3-ruby
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 27
|
94
|
+
segments:
|
95
|
+
- 1
|
96
|
+
- 3
|
97
|
+
- 0
|
98
|
+
version: 1.3.0
|
99
|
+
type: :development
|
100
|
+
version_requirements: *id005
|
101
|
+
description: A user-centric voting extension for Rails 3 applications.
|
102
|
+
email:
|
103
|
+
- schlamp@gmx.de
|
104
|
+
executables: []
|
105
|
+
|
106
|
+
extensions: []
|
107
|
+
|
108
|
+
extra_rdoc_files: []
|
109
|
+
|
110
|
+
files:
|
111
|
+
- .gitignore
|
112
|
+
- Gemfile
|
113
|
+
- MIT-LICENSE
|
114
|
+
- README.rdoc
|
115
|
+
- Rakefile
|
116
|
+
- lib/generators/make_voteable/make_voteable_generator.rb
|
117
|
+
- lib/generators/make_voteable/templates/migration.rb
|
118
|
+
- lib/make_voteable.rb
|
119
|
+
- lib/make_voteable/exceptions.rb
|
120
|
+
- lib/make_voteable/version.rb
|
121
|
+
- lib/make_voteable/voteable.rb
|
122
|
+
- lib/make_voteable/voter.rb
|
123
|
+
- lib/make_voteable/voting.rb
|
124
|
+
- make_voteable.gemspec
|
125
|
+
- spec/database.yml
|
126
|
+
- spec/database.yml.sample
|
127
|
+
- spec/lib/make_voteable_spec.rb
|
128
|
+
- spec/models.rb
|
129
|
+
- spec/schema.rb
|
130
|
+
- spec/spec_helper.rb
|
131
|
+
has_rdoc: true
|
132
|
+
homepage: http://github.com/medihack/make_voteable
|
133
|
+
licenses: []
|
134
|
+
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
hash: 3
|
146
|
+
segments:
|
147
|
+
- 0
|
148
|
+
version: "0"
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
none: false
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
hash: 23
|
155
|
+
segments:
|
156
|
+
- 1
|
157
|
+
- 3
|
158
|
+
- 6
|
159
|
+
version: 1.3.6
|
160
|
+
requirements: []
|
161
|
+
|
162
|
+
rubyforge_project: make_voteable
|
163
|
+
rubygems_version: 1.4.2
|
164
|
+
signing_key:
|
165
|
+
specification_version: 3
|
166
|
+
summary: Rails 3 voting extension
|
167
|
+
test_files: []
|
168
|
+
|