jy-acts_as_votable 0.3.1
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 +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +103 -0
- data/README.markdown +227 -0
- data/Rakefile +2 -0
- data/acts_as_votable.gemspec +29 -0
- data/lib/acts_as_votable.rb +16 -0
- data/lib/acts_as_votable/extenders/votable.rb +25 -0
- data/lib/acts_as_votable/extenders/voter.rb +24 -0
- data/lib/acts_as_votable/helpers/words.rb +36 -0
- data/lib/acts_as_votable/version.rb +3 -0
- data/lib/acts_as_votable/votable.rb +195 -0
- data/lib/acts_as_votable/vote.rb +27 -0
- data/lib/acts_as_votable/voter.rb +100 -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/spec_helper.rb +103 -0
- data/spec/votable_spec.rb +250 -0
- data/spec/voter_spec.rb +157 -0
- data/spec/words_spec.rb +30 -0
- metadata +130 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
acts_as_votable (0.3.0)
|
|
5
|
+
rails (>= 3.0.0)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: http://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
actionmailer (3.2.3)
|
|
11
|
+
actionpack (= 3.2.3)
|
|
12
|
+
mail (~> 2.4.4)
|
|
13
|
+
actionpack (3.2.3)
|
|
14
|
+
activemodel (= 3.2.3)
|
|
15
|
+
activesupport (= 3.2.3)
|
|
16
|
+
builder (~> 3.0.0)
|
|
17
|
+
erubis (~> 2.7.0)
|
|
18
|
+
journey (~> 1.0.1)
|
|
19
|
+
rack (~> 1.4.0)
|
|
20
|
+
rack-cache (~> 1.2)
|
|
21
|
+
rack-test (~> 0.6.1)
|
|
22
|
+
sprockets (~> 2.1.2)
|
|
23
|
+
activemodel (3.2.3)
|
|
24
|
+
activesupport (= 3.2.3)
|
|
25
|
+
builder (~> 3.0.0)
|
|
26
|
+
activerecord (3.2.3)
|
|
27
|
+
activemodel (= 3.2.3)
|
|
28
|
+
activesupport (= 3.2.3)
|
|
29
|
+
arel (~> 3.0.2)
|
|
30
|
+
tzinfo (~> 0.3.29)
|
|
31
|
+
activeresource (3.2.3)
|
|
32
|
+
activemodel (= 3.2.3)
|
|
33
|
+
activesupport (= 3.2.3)
|
|
34
|
+
activesupport (3.2.3)
|
|
35
|
+
i18n (~> 0.6)
|
|
36
|
+
multi_json (~> 1.0)
|
|
37
|
+
arel (3.0.2)
|
|
38
|
+
builder (3.0.0)
|
|
39
|
+
diff-lcs (1.1.3)
|
|
40
|
+
erubis (2.7.0)
|
|
41
|
+
hike (1.2.1)
|
|
42
|
+
i18n (0.6.0)
|
|
43
|
+
journey (1.0.3)
|
|
44
|
+
json (1.6.6)
|
|
45
|
+
mail (2.4.4)
|
|
46
|
+
i18n (>= 0.4.0)
|
|
47
|
+
mime-types (~> 1.16)
|
|
48
|
+
treetop (~> 1.4.8)
|
|
49
|
+
mime-types (1.18)
|
|
50
|
+
multi_json (1.2.0)
|
|
51
|
+
polyglot (0.3.3)
|
|
52
|
+
rack (1.4.1)
|
|
53
|
+
rack-cache (1.2)
|
|
54
|
+
rack (>= 0.4)
|
|
55
|
+
rack-ssl (1.3.2)
|
|
56
|
+
rack
|
|
57
|
+
rack-test (0.6.1)
|
|
58
|
+
rack (>= 1.0)
|
|
59
|
+
rails (3.2.3)
|
|
60
|
+
actionmailer (= 3.2.3)
|
|
61
|
+
actionpack (= 3.2.3)
|
|
62
|
+
activerecord (= 3.2.3)
|
|
63
|
+
activeresource (= 3.2.3)
|
|
64
|
+
activesupport (= 3.2.3)
|
|
65
|
+
bundler (~> 1.0)
|
|
66
|
+
railties (= 3.2.3)
|
|
67
|
+
railties (3.2.3)
|
|
68
|
+
actionpack (= 3.2.3)
|
|
69
|
+
activesupport (= 3.2.3)
|
|
70
|
+
rack-ssl (~> 1.3.2)
|
|
71
|
+
rake (>= 0.8.7)
|
|
72
|
+
rdoc (~> 3.4)
|
|
73
|
+
thor (~> 0.14.6)
|
|
74
|
+
rake (0.9.2.2)
|
|
75
|
+
rdoc (3.12)
|
|
76
|
+
json (~> 1.4)
|
|
77
|
+
rspec (2.9.0)
|
|
78
|
+
rspec-core (~> 2.9.0)
|
|
79
|
+
rspec-expectations (~> 2.9.0)
|
|
80
|
+
rspec-mocks (~> 2.9.0)
|
|
81
|
+
rspec-core (2.9.0)
|
|
82
|
+
rspec-expectations (2.9.1)
|
|
83
|
+
diff-lcs (~> 1.1.3)
|
|
84
|
+
rspec-mocks (2.9.0)
|
|
85
|
+
sprockets (2.1.2)
|
|
86
|
+
hike (~> 1.2)
|
|
87
|
+
rack (~> 1.0)
|
|
88
|
+
tilt (~> 1.1, != 1.3.0)
|
|
89
|
+
sqlite3 (1.3.5)
|
|
90
|
+
thor (0.14.6)
|
|
91
|
+
tilt (1.3.3)
|
|
92
|
+
treetop (1.4.10)
|
|
93
|
+
polyglot
|
|
94
|
+
polyglot (>= 0.3.1)
|
|
95
|
+
tzinfo (0.3.33)
|
|
96
|
+
|
|
97
|
+
PLATFORMS
|
|
98
|
+
ruby
|
|
99
|
+
|
|
100
|
+
DEPENDENCIES
|
|
101
|
+
acts_as_votable!
|
|
102
|
+
rspec
|
|
103
|
+
sqlite3
|
data/README.markdown
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Acts As Votable (aka Acts As Likeable)
|
|
2
|
+
|
|
3
|
+
Acts As Votable is a Ruby Gem specifically written for Rails/ActiveRecord models.
|
|
4
|
+
The main goals of this gem are:
|
|
5
|
+
|
|
6
|
+
- Allow any model to be voted on, like/dislike, upvote/downvote, etc.
|
|
7
|
+
- Allow any model to vote. In other words, votes do not have to come from a user,
|
|
8
|
+
they can come from any model (such as a Group or Team).
|
|
9
|
+
- Provide an easy to write/read syntax.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
### Rails 3
|
|
14
|
+
|
|
15
|
+
Just add the following to your Gemfile.
|
|
16
|
+
|
|
17
|
+
gem 'acts_as_votable'
|
|
18
|
+
|
|
19
|
+
And follow that up with a ``bundle install``.
|
|
20
|
+
|
|
21
|
+
### Database Migrations
|
|
22
|
+
|
|
23
|
+
Acts As Votable uses a votes table to store all voting information. To
|
|
24
|
+
generate and run the migration just use.
|
|
25
|
+
|
|
26
|
+
rails generate acts_as_votable:migration
|
|
27
|
+
rake db:migrate
|
|
28
|
+
|
|
29
|
+
You will get a performance increase by adding in cached columns to your model's
|
|
30
|
+
tables. You will have to do this manually through your own migrations. See the
|
|
31
|
+
caching section of this document for more information.
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### Votable Models
|
|
36
|
+
|
|
37
|
+
class Post < ActiveRecord::Base
|
|
38
|
+
acts_as_votable
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@post = Post.new(:name => 'my post!')
|
|
42
|
+
@post.save
|
|
43
|
+
|
|
44
|
+
@post.liked_by @user
|
|
45
|
+
@post.votes.size # => 1
|
|
46
|
+
|
|
47
|
+
### Like/Dislike Yes/No Up/Down
|
|
48
|
+
|
|
49
|
+
Here are some voting examples. All of these calls are valid and acceptable. The
|
|
50
|
+
more natural calls are the first few examples.
|
|
51
|
+
|
|
52
|
+
@post.liked_by @user1
|
|
53
|
+
@post.downvote_from @user2
|
|
54
|
+
@post.vote :voter => @user3
|
|
55
|
+
@post.vote :voter => @user4, :vote => 'bad'
|
|
56
|
+
@post.vote :voter => @user5, :vote => 'like'
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
By default all votes are positive, so @user3 has cast a 'good' vote for @post.
|
|
60
|
+
|
|
61
|
+
@user1, @user3, and @user5 all voted in favor of @post.
|
|
62
|
+
|
|
63
|
+
@user2 and @user4 on the other had has voted against @post.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Just about any word works for casting a vote in favor or against post. Up/Down,
|
|
67
|
+
Like/Dislike, Positive/Negative... the list goes on-and-on. Boolean flags ``true`` and
|
|
68
|
+
``false`` are also applicable.
|
|
69
|
+
|
|
70
|
+
Revisiting the previous example of code.
|
|
71
|
+
|
|
72
|
+
# positive votes
|
|
73
|
+
@post.liked_by @user1
|
|
74
|
+
@post.vote :voter => @user3
|
|
75
|
+
@post.vote :voter => @user5, :vote => 'like'
|
|
76
|
+
|
|
77
|
+
# negative votes
|
|
78
|
+
@post.downvote_from @user2
|
|
79
|
+
@post.vote :voter => @user2, :vote => 'bad'
|
|
80
|
+
|
|
81
|
+
# tally them up!
|
|
82
|
+
@post.votes.size # => 5
|
|
83
|
+
@post.likes.size # => 3
|
|
84
|
+
@post.upvotes.size # => 3
|
|
85
|
+
@post.dislikes.size # => 2
|
|
86
|
+
@post.downvotes.size # => 2
|
|
87
|
+
|
|
88
|
+
Active Record scopes are provided to make life easier.
|
|
89
|
+
|
|
90
|
+
@post.votes.up.by_type(User)
|
|
91
|
+
@post.votes.down
|
|
92
|
+
@user1.votes.up
|
|
93
|
+
@user1.votes.down
|
|
94
|
+
@user1.votes.up.by_type(Post)
|
|
95
|
+
|
|
96
|
+
Once scoping is complete, you can also trigger a get for the
|
|
97
|
+
voter/votable
|
|
98
|
+
|
|
99
|
+
@post.votes.up.by_type(User).voters
|
|
100
|
+
@post.votes.down.by_type(User).voters
|
|
101
|
+
|
|
102
|
+
@user.votes.up.for_type(Post).votables
|
|
103
|
+
@user.votes.up.votables
|
|
104
|
+
|
|
105
|
+
You can also 'unvote' a model to remove a previous vote.
|
|
106
|
+
|
|
107
|
+
@post.liked_by @user1
|
|
108
|
+
@post.unlike_by @user1
|
|
109
|
+
|
|
110
|
+
@post.disliked_by @user1
|
|
111
|
+
@post.undisliked_by @user1
|
|
112
|
+
|
|
113
|
+
Unvoting works for both positive and negative votes.
|
|
114
|
+
|
|
115
|
+
### The Voter
|
|
116
|
+
|
|
117
|
+
You can have your voters ``acts_as_voter`` to provide some reserve functionality.
|
|
118
|
+
|
|
119
|
+
class User < ActiveRecord::Base
|
|
120
|
+
acts_as_voter
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
@user.likes @article
|
|
124
|
+
|
|
125
|
+
@article.votes.size # => 1
|
|
126
|
+
@article.likes.size # => 1
|
|
127
|
+
@article.dislikes.size # => 0
|
|
128
|
+
|
|
129
|
+
To check if a voter has voted on a model, you can use ``voted_for?``. You can
|
|
130
|
+
check how the voter voted by using ``voted_as_when_voted_for``.
|
|
131
|
+
|
|
132
|
+
@user.likes @comment1
|
|
133
|
+
@user.up_votes @comment2
|
|
134
|
+
# user has not voted on @comment3
|
|
135
|
+
|
|
136
|
+
@user.voted_for? @comment1 # => true
|
|
137
|
+
@user.voted_for? @comment2 # => true
|
|
138
|
+
@user.voted_for? @comment3 # => false
|
|
139
|
+
|
|
140
|
+
@user.voted_as_when_voted_for @comment1 # => true, he liked it
|
|
141
|
+
@user.voted_as_when_voted_for @comment2 # => false, he didnt like it
|
|
142
|
+
@user.voted_as_when_voted_for @comment3 # => nil, he has yet to vote
|
|
143
|
+
|
|
144
|
+
You can also check whether the voter has voted up or down.
|
|
145
|
+
|
|
146
|
+
@user.likes @comment1
|
|
147
|
+
@user.dislikes @comment2
|
|
148
|
+
# user has not voted on @comment3
|
|
149
|
+
|
|
150
|
+
@user.voted_up_on? @comment1 # => true
|
|
151
|
+
@user.voted_down_on? @comment1 # => false
|
|
152
|
+
|
|
153
|
+
@user.voted_down_on? @comment2 # => true
|
|
154
|
+
@user.voted_up_on? @comment2 # => false
|
|
155
|
+
|
|
156
|
+
@user.voted_up_on? @comment3 # => false
|
|
157
|
+
@user.voted_down_on? @comment3 # => false
|
|
158
|
+
|
|
159
|
+
### Registered Votes
|
|
160
|
+
|
|
161
|
+
Voters can only vote once per model. In this example the 2nd vote does not count
|
|
162
|
+
because @user has already voted for @shoe.
|
|
163
|
+
|
|
164
|
+
@user.likes @shoe
|
|
165
|
+
@user.likes @shoe
|
|
166
|
+
|
|
167
|
+
@shoe.votes # => 1
|
|
168
|
+
@shoe.likes # => 1
|
|
169
|
+
|
|
170
|
+
To check if a vote counted, or registered, use vote_registered? on your model
|
|
171
|
+
after voting. For example:
|
|
172
|
+
|
|
173
|
+
@hat.liked_by @user
|
|
174
|
+
@hat.vote_registered? # => true
|
|
175
|
+
|
|
176
|
+
@hat.liked_by => @user
|
|
177
|
+
@hat.vote_registered? # => false, because @user has already voted this way
|
|
178
|
+
|
|
179
|
+
@hat.disliked_by @user
|
|
180
|
+
@hat.vote_registered? # => true, because user changed their vote
|
|
181
|
+
|
|
182
|
+
@hat.votes.size # => 1
|
|
183
|
+
@hat.positives.size # => 0
|
|
184
|
+
@hat.negatives.size # => 1
|
|
185
|
+
|
|
186
|
+
## Caching
|
|
187
|
+
|
|
188
|
+
To speed up perform you can add cache columns to your votable model's table. These
|
|
189
|
+
columns will automatically be updated after each vote. For example, if we wanted
|
|
190
|
+
to speed up @post we would use the following migration:
|
|
191
|
+
|
|
192
|
+
class AddCachedVotesToPosts < ActiveRecord::Migration
|
|
193
|
+
def self.up
|
|
194
|
+
add_column :posts, :cached_votes_total, :integer, :default => 0
|
|
195
|
+
add_column :posts, :cached_votes_up, :integer, :default => 0
|
|
196
|
+
add_column :posts, :cached_votes_down, :integer, :default => 0
|
|
197
|
+
add_index :posts, :cached_votes_total
|
|
198
|
+
add_index :posts, :cached_votes_up
|
|
199
|
+
add_index :posts, :cached_votes_down
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def self.down
|
|
203
|
+
remove_column :posts, :cached_votes_total
|
|
204
|
+
remove_column :posts, :cached_votes_up
|
|
205
|
+
remove_column :posts, :cached_votes_down
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
## Testing
|
|
210
|
+
|
|
211
|
+
All tests follow the RSpec format and are located in the spec directory
|
|
212
|
+
|
|
213
|
+
## TODO
|
|
214
|
+
|
|
215
|
+
- Pass in a block of options when creating acts_as. Allow for things
|
|
216
|
+
like disabling the aliasing
|
|
217
|
+
|
|
218
|
+
- Smarter language syntax. Example: ``@user.likes`` will return all of the votables
|
|
219
|
+
that the user likes, while ``@user.likes @model`` will cast a vote for @model by
|
|
220
|
+
@user.
|
|
221
|
+
|
|
222
|
+
- Need to test a model that is votable as well as a voter
|
|
223
|
+
|
|
224
|
+
- The aliased methods are referred to by using the terms 'up/down' and/or
|
|
225
|
+
'true/false'. Need to come up with guidelines for naming these methods.
|
|
226
|
+
|
|
227
|
+
- Create more aliases. Specifically for counting votes and finding votes.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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 = "jy-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
|
+
s.add_development_dependency "sqlite3"
|
|
26
|
+
|
|
27
|
+
s.add_dependency "rails", '>=3.0.0'
|
|
28
|
+
|
|
29
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
require 'active_support/inflector'
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
5
|
+
|
|
6
|
+
module ActsAsVotable
|
|
7
|
+
|
|
8
|
+
if defined?(ActiveRecord::Base)
|
|
9
|
+
require 'acts_as_votable/extenders/votable'
|
|
10
|
+
require 'acts_as_votable/extenders/voter'
|
|
11
|
+
require 'acts_as_votable/vote'
|
|
12
|
+
ActiveRecord::Base.extend ActsAsVotable::Extenders::Votable
|
|
13
|
+
ActiveRecord::Base.extend ActsAsVotable::Extenders::Voter
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module ActsAsVotable
|
|
2
|
+
module Extenders
|
|
3
|
+
|
|
4
|
+
module Votable
|
|
5
|
+
|
|
6
|
+
def votable?
|
|
7
|
+
false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def acts_as_votable
|
|
11
|
+
require 'acts_as_votable/votable'
|
|
12
|
+
include ActsAsVotable::Votable
|
|
13
|
+
|
|
14
|
+
class_eval do
|
|
15
|
+
def self.votable?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module ActsAsVotable
|
|
2
|
+
module Extenders
|
|
3
|
+
|
|
4
|
+
module Voter
|
|
5
|
+
|
|
6
|
+
def voter?
|
|
7
|
+
false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def acts_as_voter(*args)
|
|
11
|
+
require 'acts_as_votable/voter'
|
|
12
|
+
include ActsAsVotable::Voter
|
|
13
|
+
|
|
14
|
+
class_eval do
|
|
15
|
+
def self.voter?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|