acts_as_votable 0.0.5 → 0.1.0

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 CHANGED
File without changes
data/Gemfile CHANGED
File without changes
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- acts_as_votable (0.0.4)
4
+ acts_as_votable (0.0.5)
5
5
  rails (>= 3.0.0)
6
6
 
7
7
  GEM
data/README.markdown ADDED
@@ -0,0 +1,200 @@
1
+ ### Major Updates
2
+
3
+ Version 0.1.0 introduces new and refactored function calls that improve the
4
+ natural language syntax of this gem. Certain calls that were compatible with
5
+ version 0.0.5 will now be broken. Remember to specify a the version in your
6
+ Gemfile to prevent functionality breakdowns between versions.
7
+
8
+ In version 0.1.0 functions like ``@post.votes`` return an array of all of the vote
9
+ records for @post. In order to count the number of votes simply use
10
+ ``@post.votes.size`` now.
11
+
12
+ - - -
13
+
14
+ # Acts As Votable (aka Acts As Likeable)
15
+
16
+ Acts As Votable is a Ruby Gem specifically written for Rails/ActiveRecord models.
17
+ The main goals of this gem are:
18
+
19
+ - Allow any model to be voted on, like/dislike, upvote/downvote, etc.
20
+ - Allow any model to vote. In other words, votes do not have to come from a user,
21
+ they can come from any model (such as a Group or Team).
22
+ - Provide an easy to write natural language syntax.
23
+
24
+ ## Installation
25
+
26
+ ### Rails 3
27
+
28
+ Just add the following to your Gemfile.
29
+
30
+ gem 'acts_as_votable'
31
+
32
+ And follow that up with a ``bundle install``.
33
+
34
+ ### Database Migrations
35
+
36
+ Acts As Votable uses a votes table to store all voting information. To
37
+ generate and run the migration just use.
38
+
39
+ rails generate acts_as_votable:migration
40
+ rake db:migrate
41
+
42
+ You will get a performance increase by adding in cached columns to your model's
43
+ tables. You will have to do this manually through your own migrations. See the
44
+ caching section of this document for more information.
45
+
46
+ ## Usage
47
+
48
+ ### Votable Models
49
+
50
+ class Post < ActiveRecord::Base
51
+ acts_as_votable
52
+ end
53
+
54
+ @post = Post.new(:name => 'my post!')
55
+ @post.save
56
+
57
+ @post.liked_by @user
58
+ @post.votes.size # => 1
59
+
60
+ ### Like/Dislike Yes/No Up/Down
61
+
62
+ Here are some voting examples. All of these calls are valid and acceptable. The
63
+ more natural calls are the first few examples.
64
+
65
+ @post.liked_by @user1
66
+ @post.downvote_from @user2
67
+ @post.vote :voter => @user3
68
+ @post.vote :voter => @user4, :vote => 'bad'
69
+ @post.vote :voter => @user5, :vote => 'like'
70
+
71
+
72
+ By default all votes are positive, so @user3 has cast a 'good' vote for @post.
73
+
74
+ @user1, @user3, and @user5 all voted in favor of @post.
75
+
76
+ @user2 and @user4 on the other had has voted against @post.
77
+
78
+
79
+ Just about any word works for casting a vote in favor or against post. Up/Down,
80
+ Like/Dislike, Positive/Negative... the list goes on-and-on. Boolean flags ``true`` and
81
+ ``false`` are also applicable.
82
+
83
+ Revisiting the previous example of code.
84
+
85
+ # positive votes
86
+ @post.liked_by @user1
87
+ @post.vote :voter => @user3
88
+ @post.vote :voter => @user5, :vote => 'like'
89
+
90
+ # negative votes
91
+ @post.downvote_from @user2
92
+ @post.vote :voter => @user2, :vote => 'bad'
93
+
94
+ # tally them up!
95
+ @post.votes.size # => 5
96
+ @post.likes.size # => 3
97
+ @post.upvotes.size # => 3
98
+ @post.dislikes.size # => 2
99
+ @post.downvotes.size # => 2
100
+
101
+ ### The Voter
102
+
103
+ You can have your voters ``acts_as_voter`` to provide some reserve functionality.
104
+
105
+ class User < ActiveRecord::Base
106
+ acts_as_voter
107
+ end
108
+
109
+ @user.likes @article
110
+
111
+ @article.votes.size # => 1
112
+ @article.likes.size # => 1
113
+ @article.downvotes.size # => 0
114
+
115
+ To check if a voter has voted on a model, you can use ``voted_for?``. You can
116
+ check how the voter voted by using ``voted_as_when_voted_for``.
117
+
118
+ @user.likes @comment1
119
+ @user.up_votes @comment2
120
+ # user has not voted on @comment3
121
+
122
+ @user.voted_for? @comment1 # => true
123
+ @user.voted_for? @comment2 # => true
124
+ @user.voted_for? @comment3 # => false
125
+
126
+ @user.voted_as_when_voted_for @comment1 # => true, he liked it
127
+ @user.voted_as_when_voted_for @comment2 # => false, he didnt like it
128
+ @user.voted_as_when_voted_for @comment3 # => nil, he has yet to vote
129
+
130
+ ### Registered Votes
131
+
132
+ Voters can only vote once per model. In this example the 2nd vote does not count
133
+ because @user has already voted for @shoe.
134
+
135
+ @user.likes @shoe
136
+ @user.upvotes @shoe
137
+
138
+ @shoe.votes # => 1
139
+ @shoe.likes # => 1
140
+
141
+ To check if a vote counted, or registered, use vote_registered? on your model
142
+ directly after voting. For example:
143
+
144
+ @hat.liked_by @user
145
+ @hat.vote_registered? # => true
146
+
147
+ @hat.liked_by => @user
148
+ @hat.vote_registered? # => false, because @user has already voted this way
149
+
150
+ @hat.disliked_by @user
151
+ @hat.vote_registered? # => true, because user changed their vote
152
+
153
+ @hat.votes.size # => 1
154
+ @hat.positives.size # => 0
155
+ @hat.negatives.size # => 1
156
+
157
+ ## Caching
158
+
159
+ To speed up perform you can add cache columns to your votable model's table. These
160
+ columns will automatically be updated after each vote. For example, if we wanted
161
+ to speed up @post we would use the following migration:
162
+
163
+ class AddCachedVotesToPosts < ActiveRecord::Migration
164
+ def self.up
165
+ add_column :posts, :cached_votes_total, :integer, :default => 0
166
+ add_column :posts, :cached_votes_up, :integer, :default => 0
167
+ add_column :posts, :cached_votes_down, :integer, :default => 0
168
+ add_index :posts, :cached_votes_total
169
+ add_index :posts, :cached_votes_up
170
+ add_index :posts, :cached_votes_down
171
+ end
172
+
173
+ def self.down
174
+ remove_column :posts, :cached_votes_total
175
+ remove_column :posts, :cached_votes_up
176
+ remove_column :posts, :cached_votes_down
177
+ end
178
+ end
179
+
180
+ ## Testing
181
+
182
+ All tests follow the RSpec format and are located in the spec directory
183
+
184
+ ## Thanks
185
+
186
+ A huge thank you to Michael Bleigh and his Acts-As-Taggable-On gem. I learned
187
+ how to write gems by following his source code.
188
+
189
+ ## TODO
190
+
191
+ - Smarter language syntax. Example: ``@user.likes`` will return all of the votables
192
+ that the user likes, while ``@user.likes @model`` will cast a vote for @model by
193
+ @user.
194
+
195
+ - Need to test a model that is votable as well as a voter
196
+
197
+ - The aliased functions are referred to by using the terms 'up/down' amd/or
198
+ 'true/false'. Need to come up with guidelines for naming these function.
199
+
200
+ - Create more aliases. Specifically for counting votes and finding votes.
data/Rakefile CHANGED
File without changes
File without changes
File without changes
@@ -3,7 +3,7 @@ module ActsAsVotable::Alias
3
3
  def self.words_to_alias object, words, call_function
4
4
  words.each do |word|
5
5
  if word.is_a?(String)
6
- function = word.pluralize.to_sym
6
+ function = word.to_sym
7
7
  if !object.respond_to?(function)
8
8
  object.send(:alias_method, function, call_function)
9
9
  end
File without changes
@@ -20,8 +20,16 @@ module ActsAsVotable::Init
20
20
  end
21
21
 
22
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
23
+
24
+ # voting
25
+ ActsAsVotable::Alias::words_to_alias self, %w(up_by upvote_by like_by liked_by vote_by), :vote_up
26
+ ActsAsVotable::Alias::words_to_alias self, %w(up_from upvote_from like_from liked_from vote_from), :vote_up
27
+ ActsAsVotable::Alias::words_to_alias self, %w(down_by downvote_by dislike_by disliked_by), :vote_down
28
+ ActsAsVotable::Alias::words_to_alias self, %w(down_from downvote_from dislike_from disliked_from), :vote_down
29
+
30
+ # finding
31
+ ActsAsVotable::Alias::words_to_alias self, %w(true_votes ups upvotes likes positives), :up_votes
32
+ ActsAsVotable::Alias::words_to_alias self, %w(false_votes downs downvotes dislikes negatives), :down_votes
25
33
 
26
34
  end
27
35
 
@@ -18,6 +18,9 @@ module ActsAsVotable::Init
18
18
 
19
19
  include ActsAsVotable::Voter
20
20
 
21
+ ActsAsVotable::Alias::words_to_alias self, %w(likes upvotes up_votes), :vote_up_for
22
+ ActsAsVotable::Alias::words_to_alias self, %w(dislikes downvotes down_votes), :vote_down_for
23
+
21
24
  end
22
25
 
23
26
  end
@@ -1,3 +1,3 @@
1
1
  module ActsAsVotable
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -25,6 +25,7 @@ module ActsAsVotable
25
25
  }
26
26
  end
27
27
 
28
+ # voting
28
29
  def vote args = {}
29
30
 
30
31
  options = ActsAsVotable::Vote.default_voting_args.merge(args)
@@ -67,6 +68,14 @@ module ActsAsVotable
67
68
 
68
69
  end
69
70
 
71
+ def vote_up voter
72
+ self.vote :voter => voter, :vote => true
73
+ end
74
+
75
+ def vote_down voter
76
+ self.vote :voter => voter, :vote => false
77
+ end
78
+
70
79
  # caching
71
80
  def update_cached_votes
72
81
 
@@ -77,11 +86,11 @@ module ActsAsVotable
77
86
  end
78
87
 
79
88
  if self.respond_to?(:cached_votes_up=)
80
- updates[:cached_votes_up] = count_votes_true(true)
89
+ updates[:cached_votes_up] = count_votes_up(true)
81
90
  end
82
91
 
83
92
  if self.respond_to?(:cached_votes_down=)
84
- updates[:cached_votes_down] = count_votes_false(true)
93
+ updates[:cached_votes_down] = count_votes_down(true)
85
94
  end
86
95
 
87
96
  self.update_attributes(updates) if updates.size > 0
@@ -93,27 +102,37 @@ module ActsAsVotable
93
102
  def find_votes extra_conditions = {}
94
103
  ActsAsVotable::Vote.find(:all, :conditions => default_conditions.merge(extra_conditions))
95
104
  end
105
+ alias :votes :find_votes
106
+
107
+ def up_votes
108
+ find_votes(:vote_flag => true)
109
+ end
110
+
111
+ def down_votes
112
+ find_votes(:vote_flag => false)
113
+ end
114
+
96
115
 
116
+ # counting
97
117
  def count_votes_total skip_cache = false
98
118
  if !skip_cache && self.respond_to?(:cached_votes_total)
99
119
  return self.send(:cached_votes_total)
100
120
  end
101
121
  find_votes.size
102
122
  end
103
- alias :votes :count_votes_total
104
123
 
105
- def count_votes_true skip_cache = false
124
+ def count_votes_up skip_cache = false
106
125
  if !skip_cache && self.respond_to?(:cached_votes_up)
107
126
  return self.send(:cached_votes_up)
108
127
  end
109
- find_votes(:vote_flag => true).size
128
+ up_votes.size
110
129
  end
111
130
 
112
- def count_votes_false skip_cache = false
131
+ def count_votes_down skip_cache = false
113
132
  if !skip_cache && self.respond_to?(:cached_votes_down)
114
133
  return self.send(:cached_votes_down)
115
134
  end
116
- find_votes(:vote_flag => false).size
135
+ down_votes.size
117
136
  end
118
137
 
119
138
  # voters
@@ -13,11 +13,11 @@ module ActsAsVotable
13
13
  validates_presence_of :voter_id
14
14
 
15
15
  def self.true_votes
16
- ['up', 'upvote', 'like', 'yes', 'good', 'true', 1, true]
16
+ ['up', 'upvote', 'like', 'liked', 'positive', 'yes', 'good', 'true', 1, true]
17
17
  end
18
18
 
19
19
  def self.false_votes
20
- ['down', 'downvote', 'dislike', 'no', 'bad', 'false', 0, false]
20
+ ['down', 'downvote', 'dislike', 'disliked', 'negative', 'no', 'bad', 'false', 0, false]
21
21
  end
22
22
 
23
23
  ##
@@ -18,6 +18,20 @@ module ActsAsVotable
18
18
  }
19
19
  end
20
20
 
21
+ # voting
22
+ def vote args
23
+ args[:votable].vote args.merge({:voter => self})
24
+ end
25
+
26
+ def vote_up_for model
27
+ vote :votable => model, :vote => true
28
+ end
29
+
30
+ def vote_down_for model
31
+ vote :votable => model, :vote => false
32
+ end
33
+
34
+ # results
21
35
  def voted_on? votable
22
36
  votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.name)
23
37
  votes.size > 0
@@ -31,16 +45,30 @@ module ActsAsVotable
31
45
  end
32
46
  alias :voted_as_when_voting_for :voted_as_when_voting_on
33
47
 
48
+
34
49
  def find_votes extra_conditions = {}
35
50
  ActsAsVotable::Vote.find(:all, :conditions => default_conditions.merge(extra_conditions))
36
51
  end
52
+ alias :votes :find_votes
37
53
 
38
- def total_votes_for_class klass
39
- find_votes({:votable_type => klass.name}).size
54
+ def find_up_votes
55
+ find_votes :vote_flag => true
40
56
  end
41
57
 
42
- def vote args
43
- args[:votable].vote args.merge({:voter => self})
58
+ def find_down_votes
59
+ find_votes :vote_flag => false
60
+ end
61
+
62
+ def find_votes_for_class klass, extra_conditions = {}
63
+ find_votes extra_conditions.merge({:votable_type => klass.name})
64
+ end
65
+
66
+ def find_up_votes_for_class klass
67
+ find_votes_for_class klass, :vote_flag => true
68
+ end
69
+
70
+ def find_down_votes_for_class klass
71
+ find_votes_for_class klass, :vote_flag => false
44
72
  end
45
73
 
46
74
  end
data/spec/alias_spec.rb CHANGED
@@ -5,27 +5,60 @@ describe ActsAsVotable::Alias do
5
5
 
6
6
  before(:each) do
7
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
8
  end
14
9
 
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
10
+ describe "votable models" do
11
+
12
+ before(:each) do
13
+ clean_database
14
+ @votable = Votable.new(:name => 'votable with aliases')
15
+ @votable.save
16
+
17
+ @voter = Voter.new(:name => 'a voter')
18
+ @voter.save
19
+ end
20
+
21
+ it "should alias a bunch of functions" do
22
+
23
+ # voting
24
+ @votable.respond_to?(:disliked_by).should be true
25
+ @votable.respond_to?(:up_from).should be true
26
+
27
+ # results
28
+ @votable.respond_to?(:upvotes).should be true
29
+ @votable.respond_to?(:ups).should be true
30
+ @votable.respond_to?(:dislikes).should be true
20
31
 
21
- it "should only alias voting words that are strings" do
22
- @votable.respond_to?('1s'.to_sym).should be false
32
+ end
33
+
34
+ it "should add callable functions" do
35
+ @votable.vote :voter => @voter
36
+ @votable.likes.size.should == 1
37
+ end
23
38
  end
24
39
 
25
- it "should add callable functions" do
26
- @votable.vote :voter => @voter
27
- @votable.likes.should == 1
40
+ describe "voter models" do
41
+
42
+ before(:each) do
43
+ clean_database
44
+ @votable = Votable.new(:name => 'a votable')
45
+ @votable.save
46
+
47
+ @voter = Voter.new(:name => 'a voter with aliases')
48
+ @voter.save
49
+ end
50
+
51
+ it "should alias a bunch of functions" do
52
+ @voter.respond_to?(:upvotes).should be true
53
+ @voter.respond_to?(:dislikes).should be true
54
+ end
55
+
56
+ it "should add callable functions" do
57
+ @voter.likes @votable
58
+ @votable.likes.size.should == 1
59
+ end
28
60
  end
29
61
 
30
62
 
63
+
31
64
  end
data/spec/spec_helper.rb CHANGED
File without changes
data/spec/votable_spec.rb CHANGED
@@ -35,31 +35,41 @@ describe ActsAsVotable::Votable do
35
35
 
36
36
  it "should have one vote when saved" do
37
37
  @votable.vote :voter => @voter, :vote => 'yes'
38
- @votable.votes.should == 1
38
+ @votable.votes.size.should == 1
39
39
  end
40
40
 
41
41
  it "should have one vote when voted on twice by the same person" do
42
42
  @votable.vote :voter => @voter, :vote => 'yes'
43
43
  @votable.vote :voter => @voter, :vote => 'no'
44
- @votable.votes.should == 1
44
+ @votable.votes.size.should == 1
45
+ end
46
+
47
+ it "should be callable with vote_up" do
48
+ @votable.vote_up @voter
49
+ @votable.up_votes.first.voter.should == @voter
50
+ end
51
+
52
+ it "should be callable with vote_down" do
53
+ @votable.vote_down @voter
54
+ @votable.down_votes.first.voter.should == @voter
45
55
  end
46
56
 
47
57
  it "should have 2 votes when voted on once by two different people" do
48
58
  @votable.vote :voter => @voter
49
59
  @votable.vote :voter => @voter2
50
- @votable.votes.should == 2
60
+ @votable.votes.size.should == 2
51
61
  end
52
62
 
53
63
  it "should have one true vote" do
54
64
  @votable.vote :voter => @voter
55
65
  @votable.vote :voter => @voter2, :vote => 'dislike'
56
- @votable.count_votes_true.should == 1
66
+ @votable.up_votes.size.should == 1
57
67
  end
58
68
 
59
69
  it "should have 2 false votes" do
60
70
  @votable.vote :voter => @voter, :vote => 'no'
61
71
  @votable.vote :voter => @voter2, :vote => 'dislike'
62
- @votable.count_votes_false.should == 2
72
+ @votable.down_votes.size.should == 2
63
73
  end
64
74
 
65
75
  it "should have been voted on by voter2" do
@@ -102,6 +112,7 @@ describe ActsAsVotable::Votable do
102
112
  end
103
113
 
104
114
 
115
+
105
116
  describe "with cached votes" do
106
117
 
107
118
  before(:each) do
@@ -142,19 +153,19 @@ describe ActsAsVotable::Votable do
142
153
  it "should select from cached total votes if there a total column" do
143
154
  @votable_cache.vote :voter => @voter
144
155
  @votable_cache.cached_votes_total = 50
145
- @votable_cache.votes.should == 50
156
+ @votable_cache.count_votes_total.should == 50
146
157
  end
147
158
 
148
159
  it "should select from cached up votes if there is an up vote column" do
149
160
  @votable_cache.vote :voter => @voter
150
161
  @votable_cache.cached_votes_up = 50
151
- @votable_cache.count_votes_true.should == 50
162
+ @votable_cache.count_votes_up.should == 50
152
163
  end
153
164
 
154
165
  it "should select from cached down votes if there is a down vote column" do
155
166
  @votable_cache.vote :voter => @voter, :vote => 'false'
156
167
  @votable_cache.cached_votes_down = 50
157
- @votable_cache.count_votes_false.should == 50
168
+ @votable_cache.count_votes_down.should == 50
158
169
  end
159
170
 
160
171
  end
data/spec/vote_spec.rb CHANGED
File without changes
data/spec/voter_spec.rb CHANGED
@@ -55,17 +55,54 @@ describe ActsAsVotable::Voter do
55
55
  @voter.voted_as_when_voting_on(@votable).should be nil
56
56
  end
57
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
58
  it "should provide reserve functionality, voter can vote on votable" do
65
59
  @voter.vote :votable => @votable, :vote => 'bad'
66
60
  @voter.voted_as_when_voting_on(@votable).should be false
67
61
  end
68
62
 
63
+ it "should allow the voter to vote up a model" do
64
+ @voter.vote_up_for @votable
65
+ @votable.up_votes.first.voter.should == @voter
66
+ end
67
+
68
+ it "should allow the voter to vote down a model" do
69
+ @voter.vote_down_for @votable
70
+ @votable.down_votes.first.voter.should == @voter
71
+ end
72
+
73
+ it "should get all of the voters votes" do
74
+ @voter.vote_up_for @votable
75
+ @voter.find_votes.size.should == 1
76
+ end
77
+
78
+ it "should get all of the voters up votes" do
79
+ @voter.vote_up_for @votable
80
+ @voter.find_up_votes.size.should == 1
81
+ end
82
+
83
+ it "should get all of the voters down votes" do
84
+ @voter.vote_down_for @votable
85
+ @voter.find_down_votes.size.should == 1
86
+ end
87
+
88
+ it "should get all of the votes votes for a class" do
89
+ @votable.vote :voter => @voter
90
+ @votable2.vote :voter => @voter, :vote => false
91
+ @voter.find_votes_for_class(Votable).size.should == 2
92
+ end
93
+
94
+ it "should get all of the voters up votes for a class" do
95
+ @votable.vote :voter => @voter
96
+ @votable2.vote :voter => @voter, :vote => false
97
+ @voter.find_up_votes_for_class(Votable).size.should == 1
98
+ end
99
+
100
+ it "should get all of the voters down votes for a class" do
101
+ @votable.vote :voter => @voter
102
+ @votable2.vote :voter => @voter, :vote => false
103
+ @voter.find_down_votes_for_class(Votable).size.should == 1
104
+ end
105
+
69
106
  it "should be thread safe" do
70
107
  @voter.vote :votable => @votable, :vote => false
71
108
  @voter2.vote :votable => @votable
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 5
9
- version: 0.0.5
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ryan
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-30 00:00:00 -05:00
17
+ date: 2011-02-09 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -58,6 +58,7 @@ files:
58
58
  - .gitignore
59
59
  - Gemfile
60
60
  - Gemfile.lock
61
+ - README.markdown
61
62
  - Rakefile
62
63
  - acts_as_votable.gemspec
63
64
  - lib/acts_as_votable.rb