diff options
| author | Star Rauchenberger <fefferburbia@gmail.com> | 2023-10-20 21:27:06 -0400 |
|---|---|---|
| committer | Star Rauchenberger <fefferburbia@gmail.com> | 2023-10-20 21:27:06 -0400 |
| commit | 2a7a19c93ee0e0d77e4e388d43f36a721c7ab715 (patch) | |
| tree | c5e775bca4600d111d01d1bd7998d3b6252462e6 | |
| parent | 74b1612e49ebd9cb29efb85f3afeb07f0e8d608a (diff) | |
| download | thoughts-2a7a19c93ee0e0d77e4e388d43f36a721c7ab715.tar.gz thoughts-2a7a19c93ee0e0d77e4e388d43f36a721c7ab715.tar.bz2 thoughts-2a7a19c93ee0e0d77e4e388d43f36a721c7ab715.zip | |
Added post voting
| -rw-r--r-- | app/assets/stylesheets/main/entries.scss | 10 | ||||
| -rw-r--r-- | app/controllers/blogs_controller.rb | 48 | ||||
| -rw-r--r-- | app/models/blog.rb | 1 | ||||
| -rw-r--r-- | app/models/concerns/votable.rb | 43 | ||||
| -rw-r--r-- | app/models/vote.rb | 6 | ||||
| -rw-r--r-- | app/views/blogs/_blog.html.haml | 4 | ||||
| -rw-r--r-- | app/views/blogs/voted.js.erb | 12 | ||||
| -rw-r--r-- | config/routes.rb | 3 | ||||
| -rw-r--r-- | db/migrate/20231020194529_create_votes.rb | 11 | ||||
| -rw-r--r-- | db/migrate/20231020195330_make_blog_votable.rb | 8 | ||||
| -rw-r--r-- | db/schema.rb | 14 |
11 files changed, 159 insertions, 1 deletions
| diff --git a/app/assets/stylesheets/main/entries.scss b/app/assets/stylesheets/main/entries.scss index edf1706..3b7215c 100644 --- a/app/assets/stylesheets/main/entries.scss +++ b/app/assets/stylesheets/main/entries.scss | |||
| @@ -416,3 +416,13 @@ | |||
| 416 | font-weight: bold; | 416 | font-weight: bold; |
| 417 | } | 417 | } |
| 418 | } | 418 | } |
| 419 | |||
| 420 | .post-vote { | ||
| 421 | float: right; | ||
| 422 | position: relative; | ||
| 423 | right: 0.5em; | ||
| 424 | |||
| 425 | a { | ||
| 426 | text-decoration: none; | ||
| 427 | } | ||
| 428 | } | ||
| diff --git a/app/controllers/blogs_controller.rb b/app/controllers/blogs_controller.rb index 0d218ae..2f9df49 100644 --- a/app/controllers/blogs_controller.rb +++ b/app/controllers/blogs_controller.rb | |||
| @@ -38,4 +38,52 @@ class BlogsController < ApplicationController | |||
| 38 | }) | 38 | }) |
| 39 | end | 39 | end |
| 40 | 40 | ||
| 41 | def upvote | ||
| 42 | @blog = Blog.find_by_slug(params[:slug]) | ||
| 43 | |||
| 44 | raise ActiveRecord::RecordNotFound unless @blog | ||
| 45 | raise ActiveRecord::RecordNotFound unless @blog.published | ||
| 46 | |||
| 47 | respond_to do |format| | ||
| 48 | if @blog.upvote! request.remote_ip | ||
| 49 | format.html do | ||
| 50 | flash[:notice] = "You have upvoted the blog post \"#{@blog.title}\"." | ||
| 51 | redirect_to @blog | ||
| 52 | end | ||
| 53 | format.js { render "voted" } | ||
| 54 | format.xml { head :ok } | ||
| 55 | else | ||
| 56 | format.html do | ||
| 57 | flash[:notice] = "You have already voted on the blog post \"#{@blog.title}\"." | ||
| 58 | redirect_to @blog | ||
| 59 | end | ||
| 60 | format.xml { render :xml => { :error => "Someone from your IP address has already voted on this blog post."} } | ||
| 61 | end | ||
| 62 | end | ||
| 63 | end | ||
| 64 | |||
| 65 | def downvote | ||
| 66 | @blog = Blog.find_by_slug(params[:slug]) | ||
| 67 | |||
| 68 | raise ActiveRecord::RecordNotFound unless @blog | ||
| 69 | raise ActiveRecord::RecordNotFound unless @blog.published | ||
| 70 | |||
| 71 | respond_to do |format| | ||
| 72 | if @blog.downvote! request.remote_ip | ||
| 73 | format.html do | ||
| 74 | flash[:notice] = "You have downvoted the blog post \"#{@blog.title}\"." | ||
| 75 | redirect_to @blog | ||
| 76 | end | ||
| 77 | format.js { render "voted" } | ||
| 78 | format.xml { head :ok } | ||
| 79 | else | ||
| 80 | format.html do | ||
| 81 | flash[:notice] = "You have already voted on the blog post \"#{@blog.title}\"." | ||
| 82 | redirect_to @blog | ||
| 83 | end | ||
| 84 | format.xml { render :xml => { :error => "Someone from your IP address has already voted on this blog post."} } | ||
| 85 | end | ||
| 86 | end | ||
| 87 | end | ||
| 88 | |||
| 41 | end | 89 | end |
| diff --git a/app/models/blog.rb b/app/models/blog.rb index 6db75ec..03643bf 100644 --- a/app/models/blog.rb +++ b/app/models/blog.rb | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | class Blog < ApplicationRecord | 1 | class Blog < ApplicationRecord |
| 2 | include Recordable | 2 | include Recordable |
| 3 | include Votable | ||
| 3 | 4 | ||
| 4 | acts_as_taggable | 5 | acts_as_taggable |
| 5 | 6 | ||
| diff --git a/app/models/concerns/votable.rb b/app/models/concerns/votable.rb new file mode 100644 index 0000000..ba6e6d5 --- /dev/null +++ b/app/models/concerns/votable.rb | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | module Votable | ||
| 2 | extend ActiveSupport::Concern | ||
| 3 | |||
| 4 | included do | ||
| 5 | has_many :votes, as: :votable | ||
| 6 | |||
| 7 | def already_upvoted?(ip) | ||
| 8 | !votes.where(ip: ip, upvote: 1).empty? | ||
| 9 | end | ||
| 10 | |||
| 11 | def already_downvoted?(ip) | ||
| 12 | !votes.where(ip: ip, upvote: 0).empty? | ||
| 13 | end | ||
| 14 | |||
| 15 | def upvote!(ip) | ||
| 16 | return false if already_upvoted?(ip) | ||
| 17 | |||
| 18 | if already_downvoted?(ip) | ||
| 19 | votes.where(ip: ip, upvote: 0).first.delete | ||
| 20 | self.downvotes -= 1 | ||
| 21 | save! | ||
| 22 | else | ||
| 23 | votes.create(ip: ip, upvote: 1).save | ||
| 24 | self.upvotes += 1 | ||
| 25 | save! | ||
| 26 | end | ||
| 27 | end | ||
| 28 | |||
| 29 | def downvote!(ip) | ||
| 30 | return false if already_downvoted?(ip) | ||
| 31 | |||
| 32 | if already_upvoted?(ip) | ||
| 33 | votes.where(ip: ip, upvote: 1).first.delete | ||
| 34 | self.upvotes -= 1 | ||
| 35 | save! | ||
| 36 | else | ||
| 37 | votes.create(ip: ip, upvote: 0).save | ||
| 38 | self.downvotes += 1 | ||
| 39 | save! | ||
| 40 | end | ||
| 41 | end | ||
| 42 | end | ||
| 43 | end | ||
| diff --git a/app/models/vote.rb b/app/models/vote.rb new file mode 100644 index 0000000..e2d8386 --- /dev/null +++ b/app/models/vote.rb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | class Vote < ApplicationRecord | ||
| 2 | belongs_to :votable, polymorphic: true | ||
| 3 | |||
| 4 | validates :upvote, presence: true, inclusion: { in: [0, 1] } | ||
| 5 | validates :ip, presence: true | ||
| 6 | end | ||
| diff --git a/app/views/blogs/_blog.html.haml b/app/views/blogs/_blog.html.haml index ec61bb5..1f86ae8 100644 --- a/app/views/blogs/_blog.html.haml +++ b/app/views/blogs/_blog.html.haml | |||
| @@ -19,3 +19,7 @@ | |||
| 19 | %strong= blog.user.login.capitalize | 19 | %strong= blog.user.login.capitalize |
| 20 | on | 20 | on |
| 21 | = blog.visible_date.strftime("%B #{blog.visible_date.day.ordinalize}, %Y at %-I:%M:%S%P") | 21 | = blog.visible_date.strftime("%B #{blog.visible_date.day.ordinalize}, %Y at %-I:%M:%S%P") |
| 22 | .post-vote{ id: "blog-vote-section-#{blog.id}" } | ||
| 23 | %span.vote-link{ id: "blog-upvote-link-#{blog.id}" }= link_to_unless (not blog.published or blog.already_upvoted?(request.remote_ip)), "👍", upvote_blog_path(blog), remote: true, rel: "nofollow", class: "blog-upvote-link", method: :post | ||
| 24 | %span.post-rating{ id: "blog-rating-#{blog.id}" }= blog.upvotes - blog.downvotes | ||
| 25 | %span.vote-link{ id: "blog-downvote-link-#{blog.id}" }= link_to_unless (not blog.published or blog.already_downvoted?(request.remote_ip)), "👎", downvote_blog_path(blog), remote: true, rel: "nofollow", class: "blog-downvote-link", method: :post | ||
| diff --git a/app/views/blogs/voted.js.erb b/app/views/blogs/voted.js.erb new file mode 100644 index 0000000..951c740 --- /dev/null +++ b/app/views/blogs/voted.js.erb | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | $("#blog-rating-<%= @blog.id %>").html('<%= escape_javascript("#{@blog.upvotes - @blog.downvotes}") %>'); | ||
| 2 | |||
| 3 | <% if @blog.already_upvoted? request.remote_ip %> | ||
| 4 | $("#blog-upvote-link-<%= @blog.id %>").html("👍"); | ||
| 5 | <% elsif @blog.already_downvoted? request.remote_ip %> | ||
| 6 | $("#blog-downvote-link-<%= @blog.id %>").html("👎"); | ||
| 7 | <% else %> | ||
| 8 | $("#blog-upvote-link-<%= @blog.id %>").html('<%= escape_javascript(link_to("👍", upvote_blog_path(@blog), remote: true, rel: "nofollow", class: "blog-upvote-link", method: :post)) %>'); | ||
| 9 | $("#blog-downvote-link-<%= @blog.id %>").html('<%= escape_javascript(link_to("👎", downvote_blog_path(@blog), remote: true, rel: "nofollow", class: "blog-downvote-link", method: :post)) %>'); | ||
| 10 | <% end %> | ||
| 11 | |||
| 12 | $("#blog-vote-section-<%= @blog.id %>").effect('highlight', {}, 2000); | ||
| diff --git a/config/routes.rb b/config/routes.rb index 416e939..6363590 100644 --- a/config/routes.rb +++ b/config/routes.rb | |||
| @@ -38,6 +38,9 @@ Rails.application.routes.draw do | |||
| 38 | 38 | ||
| 39 | resources :blogs, only: [:index, :show], param: :slug, path: "blog" do | 39 | resources :blogs, only: [:index, :show], param: :slug, path: "blog" do |
| 40 | member do | 40 | member do |
| 41 | post 'upvote' | ||
| 42 | post 'downvote' | ||
| 43 | |||
| 41 | resources :comments, only: [:create] | 44 | resources :comments, only: [:create] |
| 42 | end | 45 | end |
| 43 | end | 46 | end |
| diff --git a/db/migrate/20231020194529_create_votes.rb b/db/migrate/20231020194529_create_votes.rb new file mode 100644 index 0000000..947652b --- /dev/null +++ b/db/migrate/20231020194529_create_votes.rb | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | class CreateVotes < ActiveRecord::Migration[7.0] | ||
| 2 | def change | ||
| 3 | create_table :votes do |t| | ||
| 4 | t.references :votable, polymorphic: true | ||
| 5 | t.integer :upvote | ||
| 6 | t.string :ip | ||
| 7 | |||
| 8 | t.timestamps | ||
| 9 | end | ||
| 10 | end | ||
| 11 | end | ||
| diff --git a/db/migrate/20231020195330_make_blog_votable.rb b/db/migrate/20231020195330_make_blog_votable.rb new file mode 100644 index 0000000..4d1e42a --- /dev/null +++ b/db/migrate/20231020195330_make_blog_votable.rb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | class MakeBlogVotable < ActiveRecord::Migration[7.0] | ||
| 2 | def change | ||
| 3 | change_table :blogs do |t| | ||
| 4 | t.integer :upvotes, default: 0, null: false | ||
| 5 | t.integer :downvotes, default: 0, null: false | ||
| 6 | end | ||
| 7 | end | ||
| 8 | end | ||
| diff --git a/db/schema.rb b/db/schema.rb index 3a9e111..f8a8c49 100644 --- a/db/schema.rb +++ b/db/schema.rb | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | # | 10 | # |
| 11 | # It's strongly recommended that you check this file into your version control system. | 11 | # It's strongly recommended that you check this file into your version control system. |
| 12 | 12 | ||
| 13 | ActiveRecord::Schema[7.0].define(version: 2023_10_17_153558) do | 13 | ActiveRecord::Schema[7.0].define(version: 2023_10_20_195330) do |
| 14 | create_table "active_storage_attachments", force: :cascade do |t| | 14 | create_table "active_storage_attachments", force: :cascade do |t| |
| 15 | t.string "name", null: false | 15 | t.string "name", null: false |
| 16 | t.string "record_type", null: false | 16 | t.string "record_type", null: false |
| @@ -70,6 +70,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_17_153558) do | |||
| 70 | t.boolean "published", default: false, null: false | 70 | t.boolean "published", default: false, null: false |
| 71 | t.datetime "published_at", precision: nil | 71 | t.datetime "published_at", precision: nil |
| 72 | t.integer "user_id" | 72 | t.integer "user_id" |
| 73 | t.integer "upvotes", default: 0, null: false | ||
| 74 | t.integer "downvotes", default: 0, null: false | ||
| 73 | t.index ["user_id"], name: "index_blogs_on_user_id" | 75 | t.index ["user_id"], name: "index_blogs_on_user_id" |
| 74 | end | 76 | end |
| 75 | 77 | ||
| @@ -393,6 +395,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_17_153558) do | |||
| 393 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true | 395 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true |
| 394 | end | 396 | end |
| 395 | 397 | ||
| 398 | create_table "votes", force: :cascade do |t| | ||
| 399 | t.string "votable_type" | ||
| 400 | t.integer "votable_id" | ||
| 401 | t.integer "upvote" | ||
| 402 | t.string "ip" | ||
| 403 | t.datetime "created_at", null: false | ||
| 404 | t.datetime "updated_at", null: false | ||
| 405 | t.index ["votable_type", "votable_id"], name: "index_votes_on_votable" | ||
| 406 | end | ||
| 407 | |||
| 396 | add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" | 408 | add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" |
| 397 | add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" | 409 | add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" |
| 398 | add_foreign_key "blogs", "users" | 410 | add_foreign_key "blogs", "users" |
