acts_as_votable 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +88 -0
- data/Rakefile +2 -0
- data/acts_as_votable.gemspec +28 -0
- data/lib/acts_as_votable.rb +21 -0
- data/lib/acts_as_votable/alias.rb +14 -0
- data/lib/acts_as_votable/init/init.rb +5 -0
- data/lib/acts_as_votable/init/votable.rb +31 -0
- data/lib/acts_as_votable/init/voter.rb +27 -0
- data/lib/acts_as_votable/version.rb +3 -0
- data/lib/acts_as_votable/votable.rb +128 -0
- data/lib/acts_as_votable/vote.rb +40 -0
- data/lib/acts_as_votable/voter.rb +47 -0
- data/lib/generators/acts_as_votable/migration/migration_generator.rb +31 -0
- data/lib/generators/acts_as_votable/migration/templates/active_record/migration.rb +20 -0
- data/spec/alias_spec.rb +31 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/votable_spec.rb +165 -0
- data/spec/vote_spec.rb +34 -0
- data/spec/voter_spec.rb +79 -0
- metadata +116 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
acts_as_votable (0.0.4)
|
5
|
+
rails (>= 3.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
abstract (1.0.0)
|
11
|
+
actionmailer (3.0.3)
|
12
|
+
actionpack (= 3.0.3)
|
13
|
+
mail (~> 2.2.9)
|
14
|
+
actionpack (3.0.3)
|
15
|
+
activemodel (= 3.0.3)
|
16
|
+
activesupport (= 3.0.3)
|
17
|
+
builder (~> 2.1.2)
|
18
|
+
erubis (~> 2.6.6)
|
19
|
+
i18n (~> 0.4)
|
20
|
+
rack (~> 1.2.1)
|
21
|
+
rack-mount (~> 0.6.13)
|
22
|
+
rack-test (~> 0.5.6)
|
23
|
+
tzinfo (~> 0.3.23)
|
24
|
+
activemodel (3.0.3)
|
25
|
+
activesupport (= 3.0.3)
|
26
|
+
builder (~> 2.1.2)
|
27
|
+
i18n (~> 0.4)
|
28
|
+
activerecord (3.0.3)
|
29
|
+
activemodel (= 3.0.3)
|
30
|
+
activesupport (= 3.0.3)
|
31
|
+
arel (~> 2.0.2)
|
32
|
+
tzinfo (~> 0.3.23)
|
33
|
+
activeresource (3.0.3)
|
34
|
+
activemodel (= 3.0.3)
|
35
|
+
activesupport (= 3.0.3)
|
36
|
+
activesupport (3.0.3)
|
37
|
+
arel (2.0.6)
|
38
|
+
builder (2.1.2)
|
39
|
+
diff-lcs (1.1.2)
|
40
|
+
erubis (2.6.6)
|
41
|
+
abstract (>= 1.0.0)
|
42
|
+
i18n (0.5.0)
|
43
|
+
mail (2.2.13)
|
44
|
+
activesupport (>= 2.3.6)
|
45
|
+
i18n (>= 0.4.0)
|
46
|
+
mime-types (~> 1.16)
|
47
|
+
treetop (~> 1.4.8)
|
48
|
+
mime-types (1.16)
|
49
|
+
polyglot (0.3.1)
|
50
|
+
rack (1.2.1)
|
51
|
+
rack-mount (0.6.13)
|
52
|
+
rack (>= 1.0.0)
|
53
|
+
rack-test (0.5.6)
|
54
|
+
rack (>= 1.0)
|
55
|
+
rails (3.0.3)
|
56
|
+
actionmailer (= 3.0.3)
|
57
|
+
actionpack (= 3.0.3)
|
58
|
+
activerecord (= 3.0.3)
|
59
|
+
activeresource (= 3.0.3)
|
60
|
+
activesupport (= 3.0.3)
|
61
|
+
bundler (~> 1.0)
|
62
|
+
railties (= 3.0.3)
|
63
|
+
railties (3.0.3)
|
64
|
+
actionpack (= 3.0.3)
|
65
|
+
activesupport (= 3.0.3)
|
66
|
+
rake (>= 0.8.7)
|
67
|
+
thor (~> 0.14.4)
|
68
|
+
rake (0.8.7)
|
69
|
+
rspec (2.3.0)
|
70
|
+
rspec-core (~> 2.3.0)
|
71
|
+
rspec-expectations (~> 2.3.0)
|
72
|
+
rspec-mocks (~> 2.3.0)
|
73
|
+
rspec-core (2.3.1)
|
74
|
+
rspec-expectations (2.3.0)
|
75
|
+
diff-lcs (~> 1.1.2)
|
76
|
+
rspec-mocks (2.3.0)
|
77
|
+
thor (0.14.6)
|
78
|
+
treetop (1.4.9)
|
79
|
+
polyglot (>= 0.3.1)
|
80
|
+
tzinfo (0.3.23)
|
81
|
+
|
82
|
+
PLATFORMS
|
83
|
+
ruby
|
84
|
+
|
85
|
+
DEPENDENCIES
|
86
|
+
acts_as_votable!
|
87
|
+
rails (>= 3.0.0)
|
88
|
+
rspec
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "acts_as_votable/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "acts_as_votable"
|
7
|
+
s.version = ActsAsVotable::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Ryan"]
|
10
|
+
s.email = ["ryanto"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/acts_as_votable"
|
12
|
+
s.summary = %q{Rails gem to allowing records to be votable}
|
13
|
+
s.description = %q{Rails gem to allowing records to be votable}
|
14
|
+
|
15
|
+
s.rubyforge_project = "acts_as_votable"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
|
26
|
+
s.add_dependency "rails", '>=3.0.0'
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
require 'acts_as_votable/init/init'
|
7
|
+
require 'acts_as_votable/init/votable'
|
8
|
+
require 'acts_as_votable/init/voter'
|
9
|
+
require 'acts_as_votable/votable'
|
10
|
+
require 'acts_as_votable/voter'
|
11
|
+
require 'acts_as_votable/alias'
|
12
|
+
|
13
|
+
module ActsAsVotable
|
14
|
+
|
15
|
+
if defined?(ActiveRecord::Base)
|
16
|
+
require 'acts_as_votable/vote'
|
17
|
+
ActiveRecord::Base.extend ActsAsVotable::Init::Votable
|
18
|
+
ActiveRecord::Base.extend ActsAsVotable::Init::Voter
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActsAsVotable::Alias
|
2
|
+
|
3
|
+
def self.words_to_alias object, words, call_function
|
4
|
+
words.each do |word|
|
5
|
+
if word.is_a?(String)
|
6
|
+
function = word.pluralize.to_sym
|
7
|
+
if !object.respond_to?(function)
|
8
|
+
object.send(:alias_method, function, call_function)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActsAsVotable::Init
|
2
|
+
|
3
|
+
module Votable
|
4
|
+
|
5
|
+
def votable?
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
def acts_as_votable(*args)
|
10
|
+
|
11
|
+
class_eval do
|
12
|
+
belongs_to :votable, :polymorphic => true
|
13
|
+
|
14
|
+
def self.votable?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
include ActsAsVotable::Votable
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# aliasing
|
23
|
+
ActsAsVotable::Alias::words_to_alias self, ActsAsVotable::Vote.true_votes, :count_votes_true
|
24
|
+
ActsAsVotable::Alias::words_to_alias self, ActsAsVotable::Vote.false_votes, :count_votes_false
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActsAsVotable::Init
|
2
|
+
|
3
|
+
# voter
|
4
|
+
module Voter
|
5
|
+
|
6
|
+
def voter?
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
def acts_as_voter(*args)
|
11
|
+
|
12
|
+
class_eval do
|
13
|
+
belongs_to :voter, :polymorphic => true
|
14
|
+
|
15
|
+
def self.voter?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
include ActsAsVotable::Voter
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module ActsAsVotable
|
2
|
+
module Votable
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.send :include, ActsAsVotable::Votable::InstanceMethods
|
6
|
+
# base.extend ActsAsVotable::Votable::ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
|
15
|
+
attr_accessor :vote_registered
|
16
|
+
|
17
|
+
def vote_registered?
|
18
|
+
return self.vote_registered
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_conditions
|
22
|
+
{
|
23
|
+
:votable_id => self.id,
|
24
|
+
:votable_type => self.class.name
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def vote args = {}
|
29
|
+
|
30
|
+
options = ActsAsVotable::Vote.default_voting_args.merge(args)
|
31
|
+
self.vote_registered = false
|
32
|
+
|
33
|
+
if options[:voter].nil?
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
# find the vote
|
38
|
+
votes = find_votes({
|
39
|
+
:voter_id => options[:voter].id,
|
40
|
+
:voter_type => options[:voter].class.name
|
41
|
+
})
|
42
|
+
|
43
|
+
if votes.count == 0
|
44
|
+
# this voter has never voted
|
45
|
+
vote = ActsAsVotable::Vote.new(
|
46
|
+
:votable => self,
|
47
|
+
:voter => options[:voter]
|
48
|
+
)
|
49
|
+
else
|
50
|
+
# this voter is potentially changing his vote
|
51
|
+
vote = votes.first
|
52
|
+
end
|
53
|
+
|
54
|
+
last_update = vote.updated_at
|
55
|
+
|
56
|
+
vote.vote_flag = ActsAsVotable::Vote.word_is_a_vote_for(options[:vote])
|
57
|
+
|
58
|
+
if vote.save
|
59
|
+
self.vote_registered = true if last_update != vote.updated_at
|
60
|
+
update_cached_votes
|
61
|
+
return true
|
62
|
+
else
|
63
|
+
self.vote_registered = false
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
# caching
|
71
|
+
def update_cached_votes
|
72
|
+
|
73
|
+
updates = {}
|
74
|
+
|
75
|
+
if self.respond_to?(:cached_votes_total=)
|
76
|
+
updates[:cached_votes_total] = count_votes_total(true)
|
77
|
+
end
|
78
|
+
|
79
|
+
if self.respond_to?(:cached_votes_up=)
|
80
|
+
updates[:cached_votes_up] = count_votes_true(true)
|
81
|
+
end
|
82
|
+
|
83
|
+
if self.respond_to?(:cached_votes_down=)
|
84
|
+
updates[:cached_votes_down] = count_votes_false(true)
|
85
|
+
end
|
86
|
+
|
87
|
+
self.update_attributes(updates) if updates.size > 0
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# results
|
93
|
+
def find_votes extra_conditions = {}
|
94
|
+
ActsAsVotable::Vote.find(:all, :conditions => default_conditions.merge(extra_conditions))
|
95
|
+
end
|
96
|
+
|
97
|
+
def count_votes_total skip_cache = false
|
98
|
+
if !skip_cache && self.respond_to?(:cached_votes_total)
|
99
|
+
return self.send(:cached_votes_total)
|
100
|
+
end
|
101
|
+
find_votes.size
|
102
|
+
end
|
103
|
+
alias :votes :count_votes_total
|
104
|
+
|
105
|
+
def count_votes_true skip_cache = false
|
106
|
+
if !skip_cache && self.respond_to?(:cached_votes_up)
|
107
|
+
return self.send(:cached_votes_up)
|
108
|
+
end
|
109
|
+
find_votes(:vote_flag => true).size
|
110
|
+
end
|
111
|
+
|
112
|
+
def count_votes_false skip_cache = false
|
113
|
+
if !skip_cache && self.respond_to?(:cached_votes_down)
|
114
|
+
return self.send(:cached_votes_down)
|
115
|
+
end
|
116
|
+
find_votes(:vote_flag => false).size
|
117
|
+
end
|
118
|
+
|
119
|
+
# voters
|
120
|
+
def voted_on_by? voter
|
121
|
+
votes = find_votes :voter_id => voter.id, :voter_type => voter.class.name
|
122
|
+
votes.size > 0
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ActsAsVotable
|
2
|
+
class Vote < ::ActiveRecord::Base
|
3
|
+
|
4
|
+
attr_accessible :votable_id, :votable_type,
|
5
|
+
:voter_id, :voter_type,
|
6
|
+
:votable, :voter,
|
7
|
+
:vote_flag
|
8
|
+
|
9
|
+
belongs_to :votable, :polymorphic => true
|
10
|
+
belongs_to :voter, :polymorphic => true
|
11
|
+
|
12
|
+
validates_presence_of :votable_id
|
13
|
+
validates_presence_of :voter_id
|
14
|
+
|
15
|
+
def self.true_votes
|
16
|
+
['up', 'upvote', 'like', 'yes', 'good', 'true', 1, true]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.false_votes
|
20
|
+
['down', 'downvote', 'dislike', 'no', 'bad', 'false', 0, false]
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# check is word is a true or bad vote
|
25
|
+
# if the word is unknown, then it counts it as a true/good
|
26
|
+
# vote. this exists to allow all voting to be good by default
|
27
|
+
def self.word_is_a_vote_for word
|
28
|
+
!false_votes.include? word
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.default_voting_args
|
32
|
+
{
|
33
|
+
:vote => true,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ActsAsVotable
|
2
|
+
module Voter
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.send :include, ActsAsVotable::Voter::InstanceMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
|
14
|
+
def default_conditions
|
15
|
+
{
|
16
|
+
:voter_id => self.id,
|
17
|
+
:voter_type => self.class.name
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def voted_on? votable
|
22
|
+
votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.name)
|
23
|
+
votes.size > 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def voted_as_when_voting_on votable
|
27
|
+
votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.name)
|
28
|
+
return nil if votes.size == 0
|
29
|
+
return votes.first.vote_flag
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_votes extra_conditions = {}
|
33
|
+
ActsAsVotable::Vote.find(:all, :conditions => default_conditions.merge(extra_conditions))
|
34
|
+
end
|
35
|
+
|
36
|
+
def total_votes_for_class klass
|
37
|
+
find_votes({:votable_type => klass.name}).size
|
38
|
+
end
|
39
|
+
|
40
|
+
def vote args
|
41
|
+
args[:votable].vote args.merge({:voter => self})
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rails/generators/migration'
|
2
|
+
|
3
|
+
module ActsAsVotable
|
4
|
+
class MigrationGenerator < Rails::Generators::Base
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
|
7
|
+
desc "Generates migration for votable (votes table)"
|
8
|
+
|
9
|
+
def self.orm
|
10
|
+
Rails::Generators.options[:rails][:orm]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.source_root
|
14
|
+
File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) )
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.orm_has_migration?
|
18
|
+
[:active_record].include? orm
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.next_migration_number(path)
|
22
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_migration_file
|
26
|
+
if self.class.orm_has_migration?
|
27
|
+
migration_template 'migration.rb', 'db/migrate/acts_as_votable_migration'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ActsAsVotableMigration < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :votes do |t|
|
4
|
+
|
5
|
+
t.references :votable, :polymorphic => true
|
6
|
+
t.references :voter, :polymorphic => true
|
7
|
+
|
8
|
+
t.boolean :vote_flag
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :votes, [:votable_id, :votable_type]
|
14
|
+
add_index :votes, [:voter_id, :voter_type]
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.down
|
18
|
+
drop_table :votes
|
19
|
+
end
|
20
|
+
end
|
data/spec/alias_spec.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'acts_as_votable'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ActsAsVotable::Alias do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
clean_database
|
8
|
+
@votable = Votable.new(:name => 'votable with aliases')
|
9
|
+
@votable.save
|
10
|
+
|
11
|
+
@voter = Voter.new(:name => 'a voter')
|
12
|
+
@voter.save
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should alias a bunch of functions" do
|
16
|
+
@votable.respond_to?(:upvotes).should be true
|
17
|
+
@votable.respond_to?(:ups).should be true
|
18
|
+
@votable.respond_to?(:dislikes).should be true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should only alias voting words that are strings" do
|
22
|
+
@votable.respond_to?('1s'.to_sym).should be false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should add callable functions" do
|
26
|
+
@votable.vote :voter => @voter
|
27
|
+
@votable.likes.should == 1
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'acts_as_votable'
|
3
|
+
|
4
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
5
|
+
|
6
|
+
ActiveRecord::Schema.define(:version => 1) do
|
7
|
+
create_table :votes do |t|
|
8
|
+
t.references :votable, :polymorphic => true
|
9
|
+
t.references :voter, :polymorphic => true
|
10
|
+
|
11
|
+
t.boolean :vote_flag
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index :votes, [:votable_id, :votable_type]
|
17
|
+
add_index :votes, [:voter_id, :voter_type]
|
18
|
+
|
19
|
+
create_table :voters do |t|
|
20
|
+
t.string :name
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :not_voters do |t|
|
24
|
+
t.string :name
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :votables do |t|
|
28
|
+
t.string :name
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table :not_votables do |t|
|
32
|
+
t.string :name
|
33
|
+
end
|
34
|
+
|
35
|
+
create_table :votable_caches do |t|
|
36
|
+
t.string :name
|
37
|
+
t.integer :cached_votes_total
|
38
|
+
t.integer :cached_votes_up
|
39
|
+
t.integer :cached_votes_down
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
class Voter < ActiveRecord::Base
|
46
|
+
acts_as_voter
|
47
|
+
end
|
48
|
+
|
49
|
+
class NotVoter < ActiveRecord::Base
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Votable < ActiveRecord::Base
|
54
|
+
acts_as_votable
|
55
|
+
validates_presence_of :name
|
56
|
+
end
|
57
|
+
|
58
|
+
class NotVotable < ActiveRecord::Base
|
59
|
+
end
|
60
|
+
|
61
|
+
class VotableCache < ActiveRecord::Base
|
62
|
+
acts_as_votable
|
63
|
+
validates_presence_of :name
|
64
|
+
end
|
65
|
+
|
66
|
+
class ABoringClass
|
67
|
+
def self.hw
|
68
|
+
'hello world'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def clean_database
|
74
|
+
models = [ActsAsVotable::Vote, Voter, NotVoter, Votable, NotVotable, VotableCache]
|
75
|
+
models.each do |model|
|
76
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{model.table_name}"
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'acts_as_votable'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ActsAsVotable::Votable do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
clean_database
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not be votable" do
|
11
|
+
NotVotable.should_not be_votable
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be votable" do
|
15
|
+
Votable.should be_votable
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "voting on a votable object" do
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
clean_database
|
22
|
+
@voter = Voter.new(:name => 'i can vote!')
|
23
|
+
@voter.save
|
24
|
+
|
25
|
+
@voter2 = Voter.new(:name => 'a new person')
|
26
|
+
@voter2.save
|
27
|
+
|
28
|
+
@votable = Votable.new(:name => 'a voting model')
|
29
|
+
@votable.save
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return false when a vote with no voter is saved" do
|
33
|
+
@votable.vote.should be false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should have one vote when saved" do
|
37
|
+
@votable.vote :voter => @voter, :vote => 'yes'
|
38
|
+
@votable.votes.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should have one vote when voted on twice by the same person" do
|
42
|
+
@votable.vote :voter => @voter, :vote => 'yes'
|
43
|
+
@votable.vote :voter => @voter, :vote => 'no'
|
44
|
+
@votable.votes.should == 1
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should have 2 votes when voted on once by two different people" do
|
48
|
+
@votable.vote :voter => @voter
|
49
|
+
@votable.vote :voter => @voter2
|
50
|
+
@votable.votes.should == 2
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have one true vote" do
|
54
|
+
@votable.vote :voter => @voter
|
55
|
+
@votable.vote :voter => @voter2, :vote => 'dislike'
|
56
|
+
@votable.count_votes_true.should == 1
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should have 2 false votes" do
|
60
|
+
@votable.vote :voter => @voter, :vote => 'no'
|
61
|
+
@votable.vote :voter => @voter2, :vote => 'dislike'
|
62
|
+
@votable.count_votes_false.should == 2
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should have been voted on by voter2" do
|
66
|
+
@votable.vote :voter => @voter2, :vote => true
|
67
|
+
@votable.find_votes.first.voter.id.should be @voter2.id
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should count the vote as registered if this is the voters first vote" do
|
71
|
+
@votable.vote :voter => @voter
|
72
|
+
@votable.vote_registered?.should be true
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should not count the vote as being registered if that voter has already voted and the vote has not changed" do
|
76
|
+
@votable.vote :voter => @voter, :vote => true
|
77
|
+
@votable.vote :voter => @voter, :vote => 'yes'
|
78
|
+
@votable.vote_registered?.should be false
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should count the vote as registered if the voter has voted and the vote has changed" do
|
82
|
+
@votable.vote :voter => @voter, :vote => true
|
83
|
+
@votable.vote :voter => @voter, :vote => 'dislike'
|
84
|
+
@votable.vote_registered?.should be true
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should be voted on by voter" do
|
88
|
+
@votable.vote :voter => @voter
|
89
|
+
@votable.voted_on_by?(@voter).should be true
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be thread safe" do
|
93
|
+
votable2 = Votable.new(:name => '2nd votable')
|
94
|
+
votable2.save
|
95
|
+
|
96
|
+
@votable.vote :voter => @voter, :vote => false
|
97
|
+
votable2.vote :voter => @voter, :vote => true
|
98
|
+
votable2.vote :voter => @voter, :vote => true
|
99
|
+
|
100
|
+
@votable.vote_registered?.should be true
|
101
|
+
votable2.vote_registered?.should be false
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
describe "with cached votes" do
|
106
|
+
|
107
|
+
before(:each) do
|
108
|
+
clean_database
|
109
|
+
@voter = Voter.new(:name => 'i can vote!')
|
110
|
+
@voter.save
|
111
|
+
|
112
|
+
@votable = Votable.new(:name => 'a voting model without a cache')
|
113
|
+
@votable.save
|
114
|
+
|
115
|
+
@votable_cache = VotableCache.new(:name => 'voting model with cache')
|
116
|
+
@votable_cache.save
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should not update cached votes if there are no columns" do
|
120
|
+
@votable.vote :voter => @voter
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should update cached total votes if there is a total column" do
|
124
|
+
@votable_cache.cached_votes_total = 50
|
125
|
+
@votable_cache.vote :voter => @voter
|
126
|
+
@votable_cache.cached_votes_total.should == 1
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should update cached up votes if there is an up vote column" do
|
130
|
+
@votable_cache.cached_votes_up = 50
|
131
|
+
@votable_cache.vote :voter => @voter
|
132
|
+
@votable_cache.vote :voter => @voter
|
133
|
+
@votable_cache.cached_votes_up.should == 1
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should update cached down votes if there is a down vote column" do
|
137
|
+
@votable_cache.cached_votes_down = 50
|
138
|
+
@votable_cache.vote :voter => @voter, :vote => 'false'
|
139
|
+
@votable_cache.cached_votes_down.should == 1
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should select from cached total votes if there a total column" do
|
143
|
+
@votable_cache.vote :voter => @voter
|
144
|
+
@votable_cache.cached_votes_total = 50
|
145
|
+
@votable_cache.votes.should == 50
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should select from cached up votes if there is an up vote column" do
|
149
|
+
@votable_cache.vote :voter => @voter
|
150
|
+
@votable_cache.cached_votes_up = 50
|
151
|
+
@votable_cache.count_votes_true.should == 50
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should select from cached down votes if there is a down vote column" do
|
155
|
+
@votable_cache.vote :voter => @voter, :vote => 'false'
|
156
|
+
@votable_cache.cached_votes_down = 50
|
157
|
+
@votable_cache.count_votes_false.should == 50
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
end
|
data/spec/vote_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'acts_as_votable'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ActsAsVotable::Vote do
|
5
|
+
|
6
|
+
it "should know that like is a true vote" do
|
7
|
+
ActsAsVotable::Vote.true_votes.should include "like"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should know that bad is a false vote" do
|
11
|
+
ActsAsVotable::Vote.false_votes.should include "bad"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be a vote for true when word is good" do
|
15
|
+
ActsAsVotable::Vote.word_is_a_vote_for('good').should be true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be a vote for false when word is down" do
|
19
|
+
ActsAsVotable::Vote.word_is_a_vote_for('down').should be false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be a vote for true when the word is unknown" do
|
23
|
+
ActsAsVotable::Vote.word_is_a_vote_for('lsdhklkadhfs').should be true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should have vote=>true in the default voting args" do
|
27
|
+
ActsAsVotable::Vote.default_voting_args[:vote].should be true
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
end
|
data/spec/voter_spec.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'acts_as_votable'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ActsAsVotable::Voter do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
clean_database
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not be a voter" do
|
11
|
+
NotVotable.should_not be_votable
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be a voter" do
|
15
|
+
Votable.should be_votable
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "voting by a voter" do
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
clean_database
|
22
|
+
@voter = Voter.new(:name => 'i can vote!')
|
23
|
+
@voter.save
|
24
|
+
|
25
|
+
@voter2 = Voter.new(:name => 'a new person')
|
26
|
+
@voter2.save
|
27
|
+
|
28
|
+
@votable = Votable.new(:name => 'a voting model')
|
29
|
+
@votable.save
|
30
|
+
|
31
|
+
@votable2 = Votable.new(:name => 'a 2nd voting model')
|
32
|
+
@votable2.save
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be voted on after a voter has voted" do
|
36
|
+
@votable.vote :voter => @voter
|
37
|
+
@voter.voted_on?(@votable).should be true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not be voted on if a voter has not voted" do
|
41
|
+
@voter.voted_on?(@votable).should be false
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be voted as true when a voter has voted true" do
|
45
|
+
@votable.vote :voter => @voter
|
46
|
+
@voter.voted_as_when_voting_on(@votable).should be true
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be voted as false when a voter has voted false" do
|
50
|
+
@votable.vote :voter => @voter, :vote => false
|
51
|
+
@voter.voted_as_when_voting_on(@votable).should be false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should be voted as nil when a voter has never voted" do
|
55
|
+
@voter.voted_as_when_voting_on(@votable).should be nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return the total number of votes cast against a given class" do
|
59
|
+
@votable.vote :voter => @voter
|
60
|
+
@votable2.vote :voter => @voter
|
61
|
+
@voter.total_votes_for_class(Votable).should == 2
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should provide reserve functionality, voter can vote on votable" do
|
65
|
+
@voter.vote :votable => @votable, :vote => 'bad'
|
66
|
+
@voter.voted_as_when_voting_on(@votable).should be false
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should be thread safe" do
|
70
|
+
@voter.vote :votable => @votable, :vote => false
|
71
|
+
@voter2.vote :votable => @votable
|
72
|
+
|
73
|
+
@voter.voted_as_when_voting_on(@votable).should be false
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_votable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
version: 0.0.4
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Ryan
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-30 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rails
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 3
|
43
|
+
- 0
|
44
|
+
- 0
|
45
|
+
version: 3.0.0
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Rails gem to allowing records to be votable
|
49
|
+
email:
|
50
|
+
- ryanto
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- Gemfile.lock
|
61
|
+
- Rakefile
|
62
|
+
- acts_as_votable.gemspec
|
63
|
+
- lib/acts_as_votable.rb
|
64
|
+
- lib/acts_as_votable/alias.rb
|
65
|
+
- lib/acts_as_votable/init/init.rb
|
66
|
+
- lib/acts_as_votable/init/votable.rb
|
67
|
+
- lib/acts_as_votable/init/voter.rb
|
68
|
+
- lib/acts_as_votable/version.rb
|
69
|
+
- lib/acts_as_votable/votable.rb
|
70
|
+
- lib/acts_as_votable/vote.rb
|
71
|
+
- lib/acts_as_votable/voter.rb
|
72
|
+
- lib/generators/acts_as_votable/migration/migration_generator.rb
|
73
|
+
- lib/generators/acts_as_votable/migration/templates/active_record/migration.rb
|
74
|
+
- spec/alias_spec.rb
|
75
|
+
- spec/spec_helper.rb
|
76
|
+
- spec/votable_spec.rb
|
77
|
+
- spec/vote_spec.rb
|
78
|
+
- spec/voter_spec.rb
|
79
|
+
has_rdoc: true
|
80
|
+
homepage: http://rubygems.org/gems/acts_as_votable
|
81
|
+
licenses: []
|
82
|
+
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project: acts_as_votable
|
107
|
+
rubygems_version: 1.3.7
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Rails gem to allowing records to be votable
|
111
|
+
test_files:
|
112
|
+
- spec/alias_spec.rb
|
113
|
+
- spec/spec_helper.rb
|
114
|
+
- spec/votable_spec.rb
|
115
|
+
- spec/vote_spec.rb
|
116
|
+
- spec/voter_spec.rb
|