vote_fu 0.0.11 → 2.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +78 -0
- data/README.md +265 -0
- data/app/assets/stylesheets/vote_fu/votes.css +391 -0
- data/app/channels/vote_fu/application_cable/channel.rb +8 -0
- data/app/channels/vote_fu/application_cable/connection.rb +39 -0
- data/app/channels/vote_fu/votes_channel.rb +99 -0
- data/app/components/vote_fu/like_button_component.rb +136 -0
- data/app/components/vote_fu/reaction_bar_component.rb +208 -0
- data/app/components/vote_fu/star_rating_component.rb +199 -0
- data/app/components/vote_fu/vote_widget_component.rb +181 -0
- data/app/controllers/vote_fu/application_controller.rb +49 -0
- data/app/controllers/vote_fu/votes_controller.rb +223 -0
- data/app/helpers/vote_fu/votes_helper.rb +176 -0
- data/app/javascript/vote_fu/channels/consumer.js +3 -0
- data/app/javascript/vote_fu/channels/index.js +2 -0
- data/app/javascript/vote_fu/channels/votes_channel.js +93 -0
- data/app/javascript/vote_fu/controllers/application.js +9 -0
- data/app/javascript/vote_fu/controllers/index.js +9 -0
- data/app/javascript/vote_fu/controllers/vote_fu_controller.js +148 -0
- data/app/javascript/vote_fu/controllers/vote_fu_reactions_controller.js +92 -0
- data/app/javascript/vote_fu/controllers/vote_fu_stars_controller.js +77 -0
- data/app/models/vote_fu/application_record.rb +7 -0
- data/app/models/vote_fu/vote.rb +90 -0
- data/app/views/vote_fu/votes/_count.html.erb +29 -0
- data/app/views/vote_fu/votes/_downvote_button.html.erb +40 -0
- data/app/views/vote_fu/votes/_error.html.erb +9 -0
- data/app/views/vote_fu/votes/_like_button.html.erb +67 -0
- data/app/views/vote_fu/votes/_upvote_button.html.erb +40 -0
- data/app/views/vote_fu/votes/_widget.html.erb +85 -0
- data/config/importmap.rb +6 -0
- data/config/routes.rb +9 -0
- data/lib/generators/vote_fu/install/install_generator.rb +56 -0
- data/lib/generators/vote_fu/install/templates/initializer.rb +42 -0
- data/lib/generators/vote_fu/install/templates/migration.rb.erb +41 -0
- data/lib/generators/vote_fu/migration/migration_generator.rb +29 -0
- data/lib/generators/vote_fu/migration/templates/create_vote_fu_votes.rb.erb +40 -0
- data/lib/vote_fu/algorithms/hacker_news.rb +54 -0
- data/lib/vote_fu/algorithms/reddit_hot.rb +55 -0
- data/lib/vote_fu/algorithms/wilson_score.rb +69 -0
- data/lib/vote_fu/concerns/karmatic.rb +320 -0
- data/lib/vote_fu/concerns/voteable.rb +291 -0
- data/lib/vote_fu/concerns/voter.rb +275 -0
- data/lib/vote_fu/configuration.rb +53 -0
- data/lib/vote_fu/engine.rb +54 -0
- data/lib/vote_fu/errors.rb +34 -0
- data/lib/vote_fu/version.rb +5 -0
- data/lib/vote_fu.rb +22 -9
- metadata +215 -63
- data/CHANGELOG.markdown +0 -31
- data/README.markdown +0 -220
- data/examples/routes.rb +0 -7
- data/examples/users_controller.rb +0 -76
- data/examples/voteable.html.erb +0 -8
- data/examples/voteable.rb +0 -10
- data/examples/voteables_controller.rb +0 -117
- data/examples/votes/_voteable_vote.html.erb +0 -23
- data/examples/votes/create.rjs +0 -1
- data/examples/votes_controller.rb +0 -110
- data/generators/vote_fu/templates/migration.rb +0 -21
- data/generators/vote_fu/vote_fu_generator.rb +0 -8
- data/init.rb +0 -1
- data/lib/acts_as_voteable.rb +0 -114
- data/lib/acts_as_voter.rb +0 -75
- data/lib/controllers/votes_controller.rb +0 -96
- data/lib/has_karma.rb +0 -68
- data/lib/models/vote.rb +0 -17
- data/rails/init.rb +0 -10
- data/test/vote_fu_test.rb +0 -8
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f74a3d3c293097b15851db854588792c7c584eedc3b16cfc42cf924590564633
|
|
4
|
+
data.tar.gz: b6898c6f0216387b2381ef3ba34ce828db44b8f56bef0df833ff3d2d198b76a7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 136d6018a10d186717a17f8af3efef0fc848a9017c73bad0ad474a24aecc9bb0d923a514e178bb531a60098aac8f85c160d33d6c18587f2d3d173aff8a4f58f7
|
|
7
|
+
data.tar.gz: db1fd3ead94e5038f15f5dc3dd0effe19972f601b49f7ef88c5738ff07aa6681a6aeec7a48f27c6336783920a09d85956e881c994b541da3e5cf41392867ef77
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.0.0] - 2024-11-29
|
|
11
|
+
|
|
12
|
+
Complete rewrite of VoteFu for modern Rails applications.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
#### Core Voting
|
|
17
|
+
- Integer vote values (supports up/down, star ratings 1-5, weighted votes)
|
|
18
|
+
- Scoped voting - multiple independent vote contexts per voteable
|
|
19
|
+
- Counter cache support (`votes_count`, `votes_total`, `upvotes_count`, `downvotes_count`)
|
|
20
|
+
- `acts_as_voteable` and `acts_as_voter` concerns
|
|
21
|
+
- `votes_on` DSL for dynamic method generation
|
|
22
|
+
- `voteable_by` DSL for explicit voter relationships
|
|
23
|
+
|
|
24
|
+
#### Algorithms (Built-in, No External Dependencies)
|
|
25
|
+
- **Wilson Score Lower Bound** - Statistical confidence interval for quality ranking
|
|
26
|
+
- **Reddit Hot** - Time-decaying popularity ranking
|
|
27
|
+
- **Hacker News** - Heavily time-weighted ranking
|
|
28
|
+
|
|
29
|
+
#### Turbo/Hotwire Integration
|
|
30
|
+
- `VotesController` with full Turbo Stream responses
|
|
31
|
+
- View helpers: `vote_widget`, `like_button`, `upvote_button`, `downvote_button`, `vote_count`
|
|
32
|
+
- Turbo Stream partials for seamless DOM updates
|
|
33
|
+
- Stimulus controller for optimistic UI updates
|
|
34
|
+
|
|
35
|
+
#### ViewComponents
|
|
36
|
+
- `VoteWidgetComponent` - Reddit-style upvote/downvote (variants: default, compact, vertical, large)
|
|
37
|
+
- `StarRatingComponent` - 1-5 star rating with averages and counts
|
|
38
|
+
- `LikeButtonComponent` - Simple heart/like button
|
|
39
|
+
- `ReactionBarComponent` - Emoji reactions (Slack/GitHub style)
|
|
40
|
+
|
|
41
|
+
#### ActionCable Real-time Updates
|
|
42
|
+
- `VotesChannel` for live vote broadcasts
|
|
43
|
+
- JavaScript client for subscribing to vote updates
|
|
44
|
+
- Auto-subscribe functionality for widgets on page
|
|
45
|
+
|
|
46
|
+
#### Karma System
|
|
47
|
+
- `has_karma` DSL for reputation tracking
|
|
48
|
+
- Time decay with configurable half-life
|
|
49
|
+
- Weighted karma sources (upvotes vs downvotes)
|
|
50
|
+
- Karma levels with progress tracking (Newcomer -> Legend)
|
|
51
|
+
- Scoped karma calculation
|
|
52
|
+
- Karma caching support for performance
|
|
53
|
+
- `karma_level`, `karma_progress`, `recent_karma` methods
|
|
54
|
+
|
|
55
|
+
#### Styling
|
|
56
|
+
- Default CSS with dark mode support
|
|
57
|
+
- Size variants (compact, default, large, vertical)
|
|
58
|
+
- Fully customizable via CSS
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
- **BREAKING**: Minimum Ruby version is now 3.2
|
|
62
|
+
- **BREAKING**: Minimum Rails version is now 7.2
|
|
63
|
+
- **BREAKING**: Votes use integer `value` instead of boolean
|
|
64
|
+
- **BREAKING**: New namespace `VoteFu::Vote` (was just `Vote`)
|
|
65
|
+
- **BREAKING**: Counter cache columns renamed for clarity
|
|
66
|
+
|
|
67
|
+
### Removed
|
|
68
|
+
- All legacy Rails 2/3/4 compatibility code
|
|
69
|
+
- `named_scope` usage (replaced with modern `scope`)
|
|
70
|
+
- `find(:all)` patterns
|
|
71
|
+
- External dependency on `statistics2` gem
|
|
72
|
+
|
|
73
|
+
## [0.0.11] - 2009-02-11
|
|
74
|
+
|
|
75
|
+
### Legacy
|
|
76
|
+
- Original VoteFu release for Rails 2.x
|
|
77
|
+
- Forked as ThumbsUp in 2010
|
|
78
|
+
- See https://github.com/peteonrails/vote_fu for historical code
|
data/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# VoteFu
|
|
2
|
+
|
|
3
|
+
[](https://votefu.dev)
|
|
4
|
+
|
|
5
|
+
**The original Rails voting gem, reborn for the modern era.**
|
|
6
|
+
|
|
7
|
+
VoteFu was first released in 2008 for Rails 2. Over the years it was forked countless times—most notably as ThumbsUp—as the Rails ecosystem evolved and the original fell behind. While the forks kept pace with Rails 3, 4, and 5, none made the leap to embrace Hotwire.
|
|
8
|
+
|
|
9
|
+
VoteFu 2.0 changes that. This is a complete ground-up rewrite that leapfrogs every fork with first-class Turbo Streams, ViewComponents, Stimulus controllers, and ActionCable support. No more bolting modern UI onto legacy architecture. VoteFu is back, and it's ready for Rails 8.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Flexible voting**: Up/down votes, star ratings (1-5), weighted votes
|
|
14
|
+
- **Scoped voting**: Multiple voting contexts per item (quality, helpfulness, etc.)
|
|
15
|
+
- **Counter caches**: Automatic vote count maintenance for performance
|
|
16
|
+
- **Ranking algorithms**: Wilson Score, Reddit Hot, Hacker News built-in
|
|
17
|
+
- **Karma system**: Calculate user reputation from votes on their content
|
|
18
|
+
- **Turbo-native**: Turbo Streams responses out of the box
|
|
19
|
+
- **Modern Rails**: Designed for Rails 8+, uses Concerns, no legacy patterns
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Add to your Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'vote_fu', '~> 2.0'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Run the installer:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
rails generate vote_fu:install
|
|
33
|
+
rails db:migrate
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### Set up your models
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
# app/models/post.rb
|
|
42
|
+
class Post < ApplicationRecord
|
|
43
|
+
acts_as_voteable
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# app/models/user.rb
|
|
47
|
+
class User < ApplicationRecord
|
|
48
|
+
acts_as_voter
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Cast votes
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
user.upvote(post) # +1 vote
|
|
56
|
+
user.downvote(post) # -1 vote
|
|
57
|
+
user.vote_on(post, value: 5) # 5-star rating
|
|
58
|
+
user.unvote(post) # Remove vote
|
|
59
|
+
user.toggle_vote(post) # Toggle on/off
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Query votes
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# On voteables
|
|
66
|
+
post.votes_for # Upvote count
|
|
67
|
+
post.votes_against # Downvote count
|
|
68
|
+
post.plusminus # Net score
|
|
69
|
+
post.wilson_score # Ranking score (0.0-1.0)
|
|
70
|
+
post.voted_by?(user) # Did user vote?
|
|
71
|
+
|
|
72
|
+
# On voters
|
|
73
|
+
user.voted_on?(post) # Did user vote?
|
|
74
|
+
user.vote_value_for(post) # What value?
|
|
75
|
+
user.voted_items(Post) # All posts user voted on
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Rank items
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
Post.by_votes # Order by vote total
|
|
82
|
+
Post.by_wilson_score # Order by Wilson Score
|
|
83
|
+
Post.trending # Most votes in 24h
|
|
84
|
+
Post.with_positive_score # Net positive only
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Scoped Voting
|
|
88
|
+
|
|
89
|
+
Allow multiple independent votes per item:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
# User can vote separately on quality and helpfulness
|
|
93
|
+
user.vote_on(review, value: 5, scope: :quality)
|
|
94
|
+
user.vote_on(review, value: 1, scope: :helpfulness)
|
|
95
|
+
|
|
96
|
+
review.plusminus(scope: :quality) # => 5
|
|
97
|
+
review.plusminus(scope: :helpfulness) # => 1
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Karma
|
|
101
|
+
|
|
102
|
+
Calculate user reputation:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
class User < ApplicationRecord
|
|
106
|
+
has_many :posts
|
|
107
|
+
|
|
108
|
+
acts_as_voter
|
|
109
|
+
has_karma :posts, as: :author
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
user.karma # Sum of upvotes on user's posts
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Counter Caches
|
|
116
|
+
|
|
117
|
+
Add columns for performance:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
# Migration
|
|
121
|
+
add_column :posts, :votes_count, :integer, default: 0
|
|
122
|
+
add_column :posts, :votes_total, :integer, default: 0
|
|
123
|
+
add_column :posts, :upvotes_count, :integer, default: 0
|
|
124
|
+
add_column :posts, :downvotes_count, :integer, default: 0
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Counters update automatically when `counter_cache: true` (default).
|
|
128
|
+
|
|
129
|
+
## Ranking Algorithms
|
|
130
|
+
|
|
131
|
+
### Wilson Score
|
|
132
|
+
Best for quality ranking. Accounts for statistical confidence.
|
|
133
|
+
```ruby
|
|
134
|
+
post.wilson_score(confidence: 0.95)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Reddit Hot
|
|
138
|
+
Balances popularity with recency.
|
|
139
|
+
```ruby
|
|
140
|
+
post.hot_score(gravity: 1.8)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Hacker News
|
|
144
|
+
Heavily favors recent content.
|
|
145
|
+
```ruby
|
|
146
|
+
VoteFu::Algorithms::HackerNews.call(post, gravity: 1.8)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Turbo Integration
|
|
150
|
+
|
|
151
|
+
VoteFu comes with Turbo Streams support out of the box.
|
|
152
|
+
|
|
153
|
+
### View Helpers
|
|
154
|
+
|
|
155
|
+
```erb
|
|
156
|
+
<%# Reddit-style upvote/downvote widget %>
|
|
157
|
+
<%= vote_widget @post %>
|
|
158
|
+
|
|
159
|
+
<%# Simple like button %>
|
|
160
|
+
<%= like_button @photo %>
|
|
161
|
+
|
|
162
|
+
<%# Scoped voting %>
|
|
163
|
+
<%= vote_widget @review, scope: :quality %>
|
|
164
|
+
<%= vote_widget @review, scope: :helpfulness %>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### ViewComponents
|
|
168
|
+
|
|
169
|
+
For more control, use the ViewComponents directly:
|
|
170
|
+
|
|
171
|
+
```erb
|
|
172
|
+
<%# Vote widget with all options %>
|
|
173
|
+
<%= render VoteFu::VoteWidgetComponent.new(
|
|
174
|
+
voteable: @post,
|
|
175
|
+
voter: current_user,
|
|
176
|
+
variant: :vertical,
|
|
177
|
+
upvote_label: "👍",
|
|
178
|
+
downvote_label: "👎"
|
|
179
|
+
) %>
|
|
180
|
+
|
|
181
|
+
<%# Star rating %>
|
|
182
|
+
<%= render VoteFu::StarRatingComponent.new(
|
|
183
|
+
voteable: @product,
|
|
184
|
+
voter: current_user,
|
|
185
|
+
show_average: true,
|
|
186
|
+
show_count: true
|
|
187
|
+
) %>
|
|
188
|
+
|
|
189
|
+
<%# Emoji reactions (Slack/GitHub style) %>
|
|
190
|
+
<%= render VoteFu::ReactionBarComponent.new(
|
|
191
|
+
voteable: @comment,
|
|
192
|
+
voter: current_user,
|
|
193
|
+
reactions: [
|
|
194
|
+
{ emoji: "👍", label: "Like", scope: "like" },
|
|
195
|
+
{ emoji: "❤️", label: "Love", scope: "love" },
|
|
196
|
+
{ emoji: "🎉", label: "Celebrate", scope: "celebrate" }
|
|
197
|
+
]
|
|
198
|
+
) %>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Controller
|
|
202
|
+
|
|
203
|
+
VoteFu provides a complete controller for handling votes:
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
# config/routes.rb
|
|
207
|
+
Rails.application.routes.draw do
|
|
208
|
+
mount VoteFu::Engine => "/vote_fu"
|
|
209
|
+
end
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The controller responds to:
|
|
213
|
+
- `POST /vote_fu/votes` - Create/update a vote
|
|
214
|
+
- `POST /vote_fu/votes/toggle` - Toggle vote (upvote ↔ remove)
|
|
215
|
+
- `DELETE /vote_fu/votes/:id` - Remove a vote
|
|
216
|
+
|
|
217
|
+
All endpoints return Turbo Streams for seamless updates.
|
|
218
|
+
|
|
219
|
+
## Styles
|
|
220
|
+
|
|
221
|
+
Import the default styles:
|
|
222
|
+
|
|
223
|
+
```css
|
|
224
|
+
/* app/assets/stylesheets/application.css */
|
|
225
|
+
@import "vote_fu/votes";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Or use CSS variables to customize:
|
|
229
|
+
|
|
230
|
+
```css
|
|
231
|
+
:root {
|
|
232
|
+
--vote-fu-upvote-color: #ff6314;
|
|
233
|
+
--vote-fu-downvote-color: #7193ff;
|
|
234
|
+
--vote-fu-like-color: #e0245e;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Configuration
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
# config/initializers/vote_fu.rb
|
|
242
|
+
VoteFu.configure do |config|
|
|
243
|
+
config.allow_recast = true # Change votes?
|
|
244
|
+
config.allow_duplicate_votes = false # Multiple votes per user?
|
|
245
|
+
config.allow_self_vote = false # Vote on yourself?
|
|
246
|
+
config.counter_cache = true # Auto-update counters?
|
|
247
|
+
config.turbo_broadcasts = true # Turbo Stream broadcasts?
|
|
248
|
+
config.default_ranking = :wilson_score
|
|
249
|
+
config.hot_ranking_gravity = 1.8
|
|
250
|
+
end
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## History
|
|
254
|
+
|
|
255
|
+
VoteFu was created in 2008 when Rails 2 was cutting-edge. It became one of the go-to voting solutions for Rails applications, powering upvotes, ratings, and karma systems across thousands of apps.
|
|
256
|
+
|
|
257
|
+
When Rails 3 arrived with breaking changes, VoteFu fell behind. In 2010, it was forked as [ThumbsUp](https://github.com/bouchard/thumbs_up), which carried the torch through Rails 3, 4, 5, and beyond. Other forks emerged too—acts_as_votable, votable, and more—each taking the original idea in different directions.
|
|
258
|
+
|
|
259
|
+
But none of them embraced Hotwire. As Rails evolved toward Turbo and Stimulus, the voting gem ecosystem stayed stuck in the jQuery era, requiring manual JavaScript for real-time updates.
|
|
260
|
+
|
|
261
|
+
**VoteFu 2.0 is a complete rewrite.** Zero legacy code. Built from scratch for Rails 7.2+ with Turbo Streams, ViewComponents, and ActionCable baked in. The original is back—and it's leapfrogged every fork.
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT License. See [LICENSE](MIT-LICENSE).
|