thumbsy 1.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/MIT-LICENSE +20 -0
- data/README.md +299 -0
- data/Rakefile +24 -0
- data/config/database.yml +47 -0
- data/lib/generators/thumbsy/api_generator.rb +59 -0
- data/lib/generators/thumbsy/install_generator.rb +53 -0
- data/lib/generators/thumbsy/templates/README +56 -0
- data/lib/generators/thumbsy/templates/create_thumbsy_votes.rb +20 -0
- data/lib/generators/thumbsy/templates/thumbsy.rb +24 -0
- data/lib/generators/thumbsy/templates/thumbsy_api.rb +75 -0
- data/lib/thumbsy/api/configuration.rb +19 -0
- data/lib/thumbsy/api/controllers/application_controller.rb +70 -0
- data/lib/thumbsy/api/controllers/votes_controller.rb +135 -0
- data/lib/thumbsy/api/engine.rb +14 -0
- data/lib/thumbsy/api/routes.rb +26 -0
- data/lib/thumbsy/api/serializers/vote_serializer.rb +36 -0
- data/lib/thumbsy/api.rb +57 -0
- data/lib/thumbsy/configuration.rb +24 -0
- data/lib/thumbsy/engine.rb +20 -0
- data/lib/thumbsy/extension.rb +13 -0
- data/lib/thumbsy/models/thumbsy_vote.rb +75 -0
- data/lib/thumbsy/validators/array_inclusion_validator.rb +20 -0
- data/lib/thumbsy/version.rb +5 -0
- data/lib/thumbsy/votable.rb +82 -0
- data/lib/thumbsy/voter.rb +65 -0
- data/lib/thumbsy.rb +58 -0
- metadata +125 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 98dd99f1f66f7fe37f9255fc170210cad077aea204dc5f5d664a43935c6194a1
|
|
4
|
+
data.tar.gz: d7c9d1404bf89c83a8d0589765dac86158a86670867ebcf162b823b47cc2b1b4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: bc37f9a43af3fb528c384c9b734b135bdd84954a6e01916e3da44af9b9829e2d13314eb51dc39691234134d45ba2ddb99712d2761407d2bd06a682a36167f58d
|
|
7
|
+
data.tar.gz: 458119986851681bcd1d29718323fe0351f0ebad2c2a4917f443b5ae56631a6aa45ffd4cd03b571caa094dd17198db0338d1cbe2362909db78ecc7dbecb18a9e
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2024 Thumbsy
|
|
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.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Thumbsy - Rails Voting Gem
|
|
2
|
+
|
|
3
|
+
[](https://github.com/healthhive/thumbsy/actions)
|
|
4
|
+
|
|
5
|
+
A Rails gem for adding thumbs up/down voting functionality with comments and feedback_options.
|
|
6
|
+
Includes optional JSON API endpoints.
|
|
7
|
+
|
|
8
|
+
**Note**: This library was created with Claude Sonnet 4, based on the requirements specified in ORIGINAL_PROMPT.txt
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- **Ruby**: 3.3.0 or newer
|
|
13
|
+
- **Rails**: 7.1, 7.2, or 8.0+
|
|
14
|
+
- **Database**: SQLite, PostgreSQL, or MySQL (any ActiveRecord-supported database)
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add this line to your application's Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem 'thumbsy'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
And then execute:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
bundle install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Run the install generator:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
# Uses default feedback_options (like, dislike, funny)
|
|
34
|
+
rails generate thumbsy:install
|
|
35
|
+
|
|
36
|
+
# Or specify custom feedback_options
|
|
37
|
+
rails generate thumbsy:install --feedback=helpful,unhelpful,spam
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Note:**
|
|
41
|
+
- The `--feedback` option is **optional**. If omitted, the default feedback_options are `like`, `dislike`, and `funny`.
|
|
42
|
+
- If you pass `--feedback` but do not provide any values (e.g., `--feedback` or `--feedback ""`), the generator will fail with an error.
|
|
43
|
+
|
|
44
|
+
## Basic Setup (ActiveRecord only)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Generate migration and basic functionality
|
|
48
|
+
rails generate thumbsy:install
|
|
49
|
+
rails db:migrate
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### ID Type Configuration
|
|
53
|
+
|
|
54
|
+
Thumbsy supports different primary key types to match your application's needs:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Default ID type is UUID (recommended for distributed systems)
|
|
58
|
+
rails generate thumbsy:install
|
|
59
|
+
|
|
60
|
+
# Use big integers (recommended for high-volume applications)
|
|
61
|
+
rails generate thumbsy:install --id_type=bigint
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The ID type affects:
|
|
65
|
+
- Primary key of the `thumbsy_votes` table
|
|
66
|
+
- Foreign key references to votable and voter models
|
|
67
|
+
|
|
68
|
+
### Custom Feedback Options
|
|
69
|
+
|
|
70
|
+
You can customize the feedback_options when generating the model:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Use default feedback_options (like, dislike, funny)
|
|
74
|
+
rails generate thumbsy:install
|
|
75
|
+
|
|
76
|
+
# Use custom feedback_options
|
|
77
|
+
rails generate thumbsy:install --feedback=helpful,unhelpful,spam
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
# Add to your models
|
|
82
|
+
class Book < ApplicationRecord
|
|
83
|
+
votable # Can receive votes
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class User < ApplicationRecord
|
|
87
|
+
voter # Can vote on other models
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Usage
|
|
91
|
+
@book.vote_up(@user)
|
|
92
|
+
@book.vote_down(@user, comment: 'Not helpful', feedback_options: ['unhelpful'])
|
|
93
|
+
|
|
94
|
+
# Querying
|
|
95
|
+
@book.votes_count # Total votes
|
|
96
|
+
@book.up_votes_count # Up votes
|
|
97
|
+
@book.voted_by?(@user) # Check if user voted
|
|
98
|
+
@book.voters # All voters
|
|
99
|
+
Book.with_votes # Books with votes
|
|
100
|
+
|
|
101
|
+
# Feedback options
|
|
102
|
+
@book.vote_up(@user, feedback_options: ['helpful'])
|
|
103
|
+
@book.vote_down(@user, feedback_options: ['spam'])
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Optional: JSON API Endpoints
|
|
107
|
+
|
|
108
|
+
If you need API endpoints for mobile apps or SPAs:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Generate API configuration
|
|
112
|
+
rails generate thumbsy:api
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This adds:
|
|
116
|
+
|
|
117
|
+
- RESTful JSON endpoints
|
|
118
|
+
- Authentication integration
|
|
119
|
+
- Authorization support
|
|
120
|
+
- Flexible routing
|
|
121
|
+
|
|
122
|
+
### API Routes
|
|
123
|
+
|
|
124
|
+
- `POST /:votable_type/:votable_id/vote_up` - Vote up
|
|
125
|
+
- `POST /:votable_type/:votable_id/vote_down` - Vote down
|
|
126
|
+
- `GET /:votable_type/:votable_id/vote` - Get current user's vote details
|
|
127
|
+
- `DELETE /:votable_type/:votable_id/vote` - Remove vote
|
|
128
|
+
|
|
129
|
+
### API Usage
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Vote up on a post
|
|
133
|
+
curl -X POST /api/v1/books/1/vote_up \
|
|
134
|
+
-H "Authorization: Bearer TOKEN" \
|
|
135
|
+
-d '{"comment": "Great book!", "feedback_options": ["helpful"]}'
|
|
136
|
+
|
|
137
|
+
# Get current user's vote details
|
|
138
|
+
curl -X GET /api/v1/books/1/vote \
|
|
139
|
+
-H "Authorization: Bearer TOKEN"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### API Module Access
|
|
143
|
+
|
|
144
|
+
The `Thumbsy::Api` module is automatically available when you require the gem:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
require 'thumbsy'
|
|
148
|
+
|
|
149
|
+
# API module is immediately accessible
|
|
150
|
+
Thumbsy::Api.configure do |config|
|
|
151
|
+
config.require_authentication = false
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Load full API functionality (controllers and routes)
|
|
155
|
+
Thumbsy.load_api! # Call this in config/application.rb or an initializer
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### API Configuration
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
# config/initializers/thumbsy_api.rb
|
|
162
|
+
Thumbsy::Api.configure do |config|
|
|
163
|
+
# Works with any authentication system
|
|
164
|
+
config.authentication_method = proc do
|
|
165
|
+
authenticate_user! # Your auth method
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
config.current_voter_method = proc do
|
|
169
|
+
current_user # Your current user method
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Core Features
|
|
175
|
+
|
|
176
|
+
✅ **ActiveRecord Integration**: Simple `votable` and `voter` declarations
|
|
177
|
+
|
|
178
|
+
✅ **Polymorphic Design**: Any model can vote on any other model
|
|
179
|
+
|
|
180
|
+
✅ **Comment Support**: Optional comments on every vote
|
|
181
|
+
|
|
182
|
+
✅ **Feedback Options**: Customizable feedback_options (like, dislike, funny, etc.)
|
|
183
|
+
|
|
184
|
+
✅ **Rich Queries**: Comprehensive scopes and helper methods
|
|
185
|
+
|
|
186
|
+
✅ **Performance Optimized**: Proper database indexes
|
|
187
|
+
|
|
188
|
+
✅ **Optional API**: Add JSON endpoints only if needed
|
|
189
|
+
|
|
190
|
+
✅ **Flexible Authentication**: Works with Devise, JWT, API keys, etc.
|
|
191
|
+
|
|
192
|
+
✅ **Test Suite**: Complete RSpec tests included
|
|
193
|
+
|
|
194
|
+
✅ **Gem-Provided Model**: Model is provided directly by the gem for consistency
|
|
195
|
+
|
|
196
|
+
## Use Cases
|
|
197
|
+
|
|
198
|
+
### ActiveRecord Only
|
|
199
|
+
|
|
200
|
+
- Traditional Rails apps with server-rendered views
|
|
201
|
+
- Internal voting systems
|
|
202
|
+
- Simple like/dislike functionality
|
|
203
|
+
- Content moderation with feedback_options
|
|
204
|
+
|
|
205
|
+
### With API
|
|
206
|
+
|
|
207
|
+
- Mobile applications
|
|
208
|
+
- Single Page Applications (SPAs)
|
|
209
|
+
- Microservices architecture
|
|
210
|
+
- Third-party integrations
|
|
211
|
+
|
|
212
|
+
## Documentation
|
|
213
|
+
|
|
214
|
+
- **Basic Usage**: This README
|
|
215
|
+
- **API Guide**: [docs/api-guide.md](docs/api-guide.md) - Complete API documentation and integration examples
|
|
216
|
+
- **Architecture Guide**: [docs/architecture-guide.md](docs/architecture-guide.md) - Technical details and design decisions
|
|
217
|
+
- **Release Guide**: [docs/release-guide.md](docs/release-guide.md) - How to release new versions to RubyGems
|
|
218
|
+
|
|
219
|
+
## Releases
|
|
220
|
+
|
|
221
|
+
Thumbsy uses automated releases with semantic versioning. To release a new version:
|
|
222
|
+
|
|
223
|
+
### Quick Release
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Automatically determine version bump from commits
|
|
227
|
+
ruby script/bump_version.rb
|
|
228
|
+
|
|
229
|
+
# Or manually specify bump type
|
|
230
|
+
ruby script/bump_version.rb minor
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Release Process
|
|
234
|
+
|
|
235
|
+
1. **Version Bump**: The script automatically updates version files
|
|
236
|
+
2. **Git Tag**: Creates a version tag (e.g., `v1.1.0`)
|
|
237
|
+
3. **Push Tag**: `git push origin v1.1.0`
|
|
238
|
+
4. **Automated Release**: CI/CD pipeline publishes to RubyGems and creates GitHub release
|
|
239
|
+
|
|
240
|
+
### Version Bumping Rules
|
|
241
|
+
|
|
242
|
+
- **Patch** (`1.0.0` → `1.0.1`): Bug fixes, docs, performance
|
|
243
|
+
- **Minor** (`1.0.0` → `1.1.0`): New features (backward compatible)
|
|
244
|
+
- **Major** (`1.0.0` → `2.0.0`): Breaking changes
|
|
245
|
+
|
|
246
|
+
See [docs/release-guide.md](docs/release-guide.md) for complete release documentation.
|
|
247
|
+
|
|
248
|
+
## Development & Testing
|
|
249
|
+
|
|
250
|
+
### Running Tests Locally
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Run the full test suite
|
|
254
|
+
bundle exec rspec
|
|
255
|
+
|
|
256
|
+
# Run tests with coverage report
|
|
257
|
+
COVERAGE=true bundle exec rspec
|
|
258
|
+
|
|
259
|
+
# Run specific test files
|
|
260
|
+
bundle exec rspec spec/thumbsy_spec.rb
|
|
261
|
+
bundle exec rspec spec/api_integration_spec.rb
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Testing Across Rails Versions
|
|
265
|
+
|
|
266
|
+
Thumbsy is tested against multiple Rails versions (7.1, 7.2, 8.0) and Ruby versions (3.3, 3.4):
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
|
|
270
|
+
RAILS_VERSION=8.0 bundle update rails
|
|
271
|
+
RAILS_VERSION=8.0 bundle exec rspec
|
|
272
|
+
|
|
273
|
+
# Use the automated test script
|
|
274
|
+
ruby script/test_rails_versions.rb
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Continuous Integration
|
|
278
|
+
|
|
279
|
+
Our CI pipeline automatically tests all supported combinations:
|
|
280
|
+
|
|
281
|
+
- **Ruby versions**: 3.3, 3.4
|
|
282
|
+
- **Rails versions**: 7.1, 7.2, 8.0
|
|
283
|
+
- **Total combinations**: 9 test matrices
|
|
284
|
+
- **Coverage requirement**: 78%+
|
|
285
|
+
|
|
286
|
+
All tests must pass across all combinations before any changes are merged.
|
|
287
|
+
|
|
288
|
+
### Contributing
|
|
289
|
+
|
|
290
|
+
1. Fork the repository
|
|
291
|
+
2. Create a feature branch
|
|
292
|
+
3. Add tests for your changes
|
|
293
|
+
4. Ensure all tests pass: `bundle exec rspec`
|
|
294
|
+
5. Test across Rails versions: `ruby script/test_rails_versions.rb`
|
|
295
|
+
6. Submit a pull request
|
|
296
|
+
|
|
297
|
+
## License
|
|
298
|
+
|
|
299
|
+
MIT
|
data/Rakefile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
puts "You must gem install bundler and bundle install to run rake tasks"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require "rdoc/task"
|
|
10
|
+
|
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
12
|
+
rdoc.rdoc_dir = "rdoc"
|
|
13
|
+
rdoc.title = "Thumbsy"
|
|
14
|
+
rdoc.options << "--line-numbers"
|
|
15
|
+
rdoc.rdoc_files.include("README.md")
|
|
16
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
require "bundler/gem_tasks"
|
|
20
|
+
|
|
21
|
+
require "rspec/core/rake_task"
|
|
22
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
23
|
+
|
|
24
|
+
task default: :spec
|
data/config/database.yml
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Database configuration for Thumbsy gem testing
|
|
2
|
+
default: &default
|
|
3
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
|
4
|
+
timeout: 5000
|
|
5
|
+
|
|
6
|
+
development:
|
|
7
|
+
<<: *default
|
|
8
|
+
adapter: sqlite3
|
|
9
|
+
database: db/development.sqlite3
|
|
10
|
+
|
|
11
|
+
test:
|
|
12
|
+
<<: *default
|
|
13
|
+
adapter: sqlite3
|
|
14
|
+
database: db/test.sqlite3
|
|
15
|
+
|
|
16
|
+
# PostgreSQL configuration for CI testing
|
|
17
|
+
test_postgresql:
|
|
18
|
+
<<: *default
|
|
19
|
+
adapter: postgresql
|
|
20
|
+
encoding: unicode
|
|
21
|
+
database: thumbsy_test
|
|
22
|
+
username: <%= ENV.fetch("POSTGRES_USER", "postgres") %>
|
|
23
|
+
password: <%= ENV.fetch("POSTGRES_PASSWORD", "postgres") %>
|
|
24
|
+
host: <%= ENV.fetch("POSTGRES_HOST", "localhost") %>
|
|
25
|
+
port: <%= ENV.fetch("POSTGRES_PORT", 5432) %>
|
|
26
|
+
|
|
27
|
+
# MySQL configuration for CI testing
|
|
28
|
+
test_mysql:
|
|
29
|
+
<<: *default
|
|
30
|
+
adapter: mysql2
|
|
31
|
+
encoding: utf8mb4
|
|
32
|
+
database: thumbsy_test
|
|
33
|
+
username: <%= ENV.fetch("MYSQL_USER", "root") %>
|
|
34
|
+
password: <%= ENV.fetch("MYSQL_PASSWORD", "root") %>
|
|
35
|
+
host: <%= ENV.fetch("MYSQL_HOST", "localhost") %>
|
|
36
|
+
port: <%= ENV.fetch("MYSQL_PORT", 3306) %>
|
|
37
|
+
|
|
38
|
+
# SQLite configuration for CI testing
|
|
39
|
+
test_sqlite:
|
|
40
|
+
<<: *default
|
|
41
|
+
adapter: sqlite3
|
|
42
|
+
database: db/test.sqlite3
|
|
43
|
+
|
|
44
|
+
production:
|
|
45
|
+
<<: *default
|
|
46
|
+
adapter: sqlite3
|
|
47
|
+
database: db/production.sqlite3
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Thumbsy
|
|
6
|
+
module Generators
|
|
7
|
+
class ApiGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
desc "Generate Thumbsy API configuration and routes"
|
|
10
|
+
|
|
11
|
+
def create_api_initializer
|
|
12
|
+
initializer_path = "config/initializers/thumbsy.rb"
|
|
13
|
+
require_line = "require 'thumbsy/api'"
|
|
14
|
+
load_line = "Thumbsy::Api.load!"
|
|
15
|
+
insert_lines = "# Load Thumbsy API if you want to use the API endpoints\n#{require_line}\n#{load_line}\n\n"
|
|
16
|
+
|
|
17
|
+
if File.exist?(initializer_path)
|
|
18
|
+
content = File.read(initializer_path)
|
|
19
|
+
if content.include?(require_line) && content.include?(load_line)
|
|
20
|
+
say "API require and load lines already present in #{initializer_path}"
|
|
21
|
+
else
|
|
22
|
+
new_content = insert_lines + content
|
|
23
|
+
File.write(initializer_path, new_content)
|
|
24
|
+
say "Added API require and load lines to #{initializer_path}"
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
create_file initializer_path, insert_lines
|
|
28
|
+
say "Created #{initializer_path} with API require and load lines"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def add_api_require
|
|
33
|
+
inject_into_file "config/application.rb", after: "require \"rails/all\"\n" do
|
|
34
|
+
"require \"thumbsy/api\"\n"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_routes
|
|
39
|
+
route_content = <<~RUBY
|
|
40
|
+
# Thumbsy API routes
|
|
41
|
+
mount Thumbsy::Api::Engine => "/api/v1", as: :thumbsy_api
|
|
42
|
+
RUBY
|
|
43
|
+
|
|
44
|
+
inject_into_file "config/routes.rb", route_content, after: "Rails.application.routes.draw do\n"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def show_instructions
|
|
48
|
+
say "Thumbsy API has been configured!"
|
|
49
|
+
say ""
|
|
50
|
+
say "Next steps:"
|
|
51
|
+
say "1. Configure authentication in config/initializers/thumbsy_api.rb"
|
|
52
|
+
say "2. API routes are mounted at /api/v1"
|
|
53
|
+
say "3. Test with: curl -X POST /api/v1/posts/1/vote_up"
|
|
54
|
+
say ""
|
|
55
|
+
say "See API_DOCUMENTATION.md for complete usage examples."
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/migration"
|
|
5
|
+
|
|
6
|
+
module Thumbsy
|
|
7
|
+
module Generators
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
include Rails::Generators::Migration
|
|
10
|
+
source_root File.expand_path("../../../lib/generators/thumbsy/templates", __dir__)
|
|
11
|
+
desc "Installs Thumbsy and sets up ThumbsyVote support."
|
|
12
|
+
|
|
13
|
+
class_option :feedback, type: :array, desc: "Feedback options for votes (e.g. --feedback like dislike funny)"
|
|
14
|
+
|
|
15
|
+
def self.next_migration_number(path)
|
|
16
|
+
next_migration_number = current_migration_number(path) + 1
|
|
17
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class_option :id_type, type: :string, default: :uuid, desc: "ID type for primary keys (uuid, bigint, or integer)"
|
|
21
|
+
|
|
22
|
+
def create_migration_file
|
|
23
|
+
@id_type = options[:id_type].to_sym
|
|
24
|
+
|
|
25
|
+
if options.key?(:feedback) && (options[:feedback].nil? || options[:feedback].empty?)
|
|
26
|
+
say "\nERROR: --feedback option must have at least one value if provided (e.g. --feedback=like,dislike)", :red
|
|
27
|
+
exit(1)
|
|
28
|
+
end
|
|
29
|
+
@feedback_options = options[:feedback]
|
|
30
|
+
migration_template "create_thumbsy_votes.rb", "db/migrate/create_thumbsy_votes.rb"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def create_initializer_file
|
|
34
|
+
return unless options[:feedback].present?
|
|
35
|
+
|
|
36
|
+
feedback_options = if options[:feedback].is_a?(Array)
|
|
37
|
+
options[:feedback]
|
|
38
|
+
else
|
|
39
|
+
options[:feedback].to_s.split(",")
|
|
40
|
+
end
|
|
41
|
+
feedback_options.map!(&:strip)
|
|
42
|
+
template "thumbsy.rb", "config/initializers/thumbsy.rb"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def show_readme
|
|
46
|
+
readme "README" if behavior == :invoke
|
|
47
|
+
say ""
|
|
48
|
+
say "Optional: Generate API endpoints with:"
|
|
49
|
+
say " rails generate thumbsy:api"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
===============================================================================
|
|
2
|
+
|
|
3
|
+
Thumbsy has been installed!
|
|
4
|
+
|
|
5
|
+
Next steps:
|
|
6
|
+
|
|
7
|
+
1. Run the migration:
|
|
8
|
+
rails db:migrate
|
|
9
|
+
|
|
10
|
+
2. Add thumbsy methods to your models:
|
|
11
|
+
|
|
12
|
+
# For models that can be voted on:
|
|
13
|
+
class Post < ApplicationRecord
|
|
14
|
+
votable
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# For models that can vote:
|
|
18
|
+
class User < ApplicationRecord
|
|
19
|
+
voter
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
3. Usage examples:
|
|
23
|
+
|
|
24
|
+
# Voting
|
|
25
|
+
@post.vote_up(@user)
|
|
26
|
+
@post.vote_down(@user, comment: "Not helpful")
|
|
27
|
+
|
|
28
|
+
# Or from voter perspective:
|
|
29
|
+
@user.vote_up_for(@post)
|
|
30
|
+
@user.vote_down_for(@post, comment: "Great post!")
|
|
31
|
+
|
|
32
|
+
# Checking votes
|
|
33
|
+
@post.voted_by?(@user)
|
|
34
|
+
@post.up_voted_by?(@user)
|
|
35
|
+
@post.votes_count
|
|
36
|
+
@post.up_votes_count
|
|
37
|
+
@post.votes_score
|
|
38
|
+
|
|
39
|
+
# Getting voters
|
|
40
|
+
@post.voters
|
|
41
|
+
@post.up_voters
|
|
42
|
+
@post.down_voters
|
|
43
|
+
|
|
44
|
+
# Scopes
|
|
45
|
+
Post.with_votes
|
|
46
|
+
Post.with_up_votes
|
|
47
|
+
Post.with_down_votes
|
|
48
|
+
Post.with_comments
|
|
49
|
+
|
|
50
|
+
# Comments on votes
|
|
51
|
+
@post.votes_with_comments
|
|
52
|
+
@post.up_votes_with_comments
|
|
53
|
+
|
|
54
|
+
For more information, visit: https://github.com/healthhive/thumbsy
|
|
55
|
+
|
|
56
|
+
===============================================================================
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class CreateThumbsyVotes < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :thumbsy_votes, id: :<%= @id_type %> do |t|
|
|
4
|
+
t.references :votable, null: false, type: :<%= @id_type %>, polymorphic: true, index: false
|
|
5
|
+
t.references :voter, null: false, type: :<%= @id_type %>, polymorphic: true, index: false
|
|
6
|
+
t.boolean :vote, null: false, default: false
|
|
7
|
+
t.text :comment
|
|
8
|
+
<% if defined?(@feedback_options) && @feedback_options.present? %>
|
|
9
|
+
t.text :feedback_options, default: <%= [].to_yaml.inspect %>
|
|
10
|
+
<% end %>
|
|
11
|
+
t.timestamps null: false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index :thumbsy_votes, [:votable_type, :votable_id, :voter_type, :voter_id],
|
|
15
|
+
unique: true, name: "index_thumbsy_votes_on_voter_and_votable"
|
|
16
|
+
|
|
17
|
+
add_index :thumbsy_votes, [:votable_type, :votable_id, :vote]
|
|
18
|
+
add_index :thumbsy_votes, [:voter_type, :voter_id, :vote]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Thumbsy Configuration
|
|
2
|
+
# All Thumbsy and Thumbsy API settings are centralized here.
|
|
3
|
+
|
|
4
|
+
Thumbsy.configure do |config|
|
|
5
|
+
config.feedback_options = %w[unclear confusing incorrect_info other]
|
|
6
|
+
|
|
7
|
+
config.api do |api_config|
|
|
8
|
+
# Uncomment and customize the following as needed:
|
|
9
|
+
|
|
10
|
+
# Authentication settings
|
|
11
|
+
# api_config.require_authentication = true
|
|
12
|
+
|
|
13
|
+
# Example for Devise:
|
|
14
|
+
# api_config.authentication_method = proc { authenticate_user! }
|
|
15
|
+
# api_config.current_voter_method = proc { current_user }
|
|
16
|
+
|
|
17
|
+
# Authorization (optional)
|
|
18
|
+
# api_config.require_authorization = true
|
|
19
|
+
# api_config.authorization_method = proc { |votable, voter| ... }
|
|
20
|
+
|
|
21
|
+
# Custom voter serialization for API responses
|
|
22
|
+
# api_config.voter_serializer = proc { |voter| { id: voter.id, ... } }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Thumbsy API Configuration
|
|
2
|
+
# Configure the voting API to work with your authentication system
|
|
3
|
+
|
|
4
|
+
require 'thumbsy/api'
|
|
5
|
+
require 'thumbsy/api/engine'
|
|
6
|
+
|
|
7
|
+
# Load API components
|
|
8
|
+
Thumbsy::Api.load!
|
|
9
|
+
|
|
10
|
+
Thumbsy::Api.configure do |config|
|
|
11
|
+
# Authentication settings
|
|
12
|
+
config.require_authentication = true
|
|
13
|
+
|
|
14
|
+
# Define how to authenticate users
|
|
15
|
+
# Example for Devise:
|
|
16
|
+
config.authentication_method = proc do
|
|
17
|
+
authenticate_user! # Your authentication method
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Define how to get the current voter
|
|
21
|
+
# Example for Devise:
|
|
22
|
+
config.current_voter_method = proc do
|
|
23
|
+
current_user # Your current user method
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Authorization (optional)
|
|
27
|
+
# config.require_authorization = true
|
|
28
|
+
# config.authorization_method = proc do |votable, voter|
|
|
29
|
+
# # Return true/false for permission to vote
|
|
30
|
+
# case votable.class.name
|
|
31
|
+
# when "Book"
|
|
32
|
+
# votable.published? && !votable.archived?
|
|
33
|
+
# when "Comment"
|
|
34
|
+
# votable.@book.published? && voter.can_vote?
|
|
35
|
+
# else
|
|
36
|
+
# true
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
|
|
40
|
+
# Custom voter serialization for API responses
|
|
41
|
+
# config.voter_serializer = proc do |voter|
|
|
42
|
+
# {
|
|
43
|
+
# id: voter.id,
|
|
44
|
+
# name: voter.name,
|
|
45
|
+
# avatar: voter.avatar.attached? ? rails_blob_url(voter.avatar) : nil
|
|
46
|
+
# }
|
|
47
|
+
# end
|
|
48
|
+
|
|
49
|
+
# Vote model name (if you want to customize)
|
|
50
|
+
# config.vote_model_name = "CustomVote"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Examples for different authentication systems:
|
|
54
|
+
|
|
55
|
+
# JWT Authentication:
|
|
56
|
+
# config.authentication_method = proc do
|
|
57
|
+
# token = request.headers["Authorization"]&.split(" ")&.last
|
|
58
|
+
# @decoded_token = JWT.decode(token, Rails.application.secret_key_base).first
|
|
59
|
+
# @current_user = User.find(@decoded_token["user_id"])
|
|
60
|
+
# rescue JWT::DecodeError
|
|
61
|
+
# head :unauthorized
|
|
62
|
+
# end
|
|
63
|
+
|
|
64
|
+
# API Key Authentication:
|
|
65
|
+
# config.authentication_method = proc do
|
|
66
|
+
# api_key = request.headers["X-API-Key"]
|
|
67
|
+
# @current_user = User.find_by(api_key: api_key)
|
|
68
|
+
# head :unauthorized unless @current_user
|
|
69
|
+
# end
|
|
70
|
+
|
|
71
|
+
# Session-based Authentication:
|
|
72
|
+
# config.authentication_method = proc do
|
|
73
|
+
# @current_user = User.find(session[:user_id]) if session[:user_id]
|
|
74
|
+
# head :unauthorized unless @current_user
|
|
75
|
+
# end
|