I am using the acts_as_votable gem and am having an issue with the voting.
I have two models - User and Post. A User is able to vote on a post.
The problem I am having is that the User is only currently able to vote on their own post, but not other users' posts.
I think this is because I have done #posts.each and then the user is only being accessed for their own posts, so they can only vote on their own posts. Or the problem is in my index method in my controller.
Index.html.erb File:
<% #posts.each do |post| %>
<div class="user-input">
<% if post.user.voted_up_on? post %>
<%= link_to unlike_post_path(post), method: :put do %>
<%= image_tag("/assets/like.png") %>
<% end %>
<% else %>
<%= link_to like_post_path(post), method: :put do %>
<%= image_tag("/assets/heart.png") %>
<% end %>
<% end %>
</div>
<% end %>
Posts_controller (relevant methods):
def index
#posts = Post.includes(:user).order("created_at DESC") #Possible issue is here??
end
def like
#post.upvote_by current_user
redirect_to :back
end
def unlike
#post.unvote_by current_user
redirect_to :back
end
Routes:
resources :posts do
member do
put "like", to: "posts#like"
put 'unlike', to: 'posts#unlike'
end
end
If someone could help me understand why a User is only able to vote on their own posts that would be great.
Got it.
Had to replace
<% if post.user.voted_up_on? post %>
with:
<% if current_user.liked? post %>
I believe it was asking if the creator of the post has voted on the post, whereas I needed just the current user to see if they have voted on the post.
Related
I have a rails question. I'm building a site where posts have likes, both posts and likes are their own model. A user can only like a post once, and once they like it the like button becomes an "unlike" button, that deletes the like.
I'm trying to create an experience in which the user can like, or unlike a post - and will not be redirected, but the like will update. With my limited rails knowledge, this isn't an easy task. Can anyone point me in the right direction?
Here is my /likes/_likes.html.erb template partial with the like/unlike button:
<% liked = #post.likes.find { |like| like.user_id == current_user.id} %>
<div class="likes">
<% if liked %>
<%= button_to 'Unlike', post_like_path(#post, liked), method: :delete %>
<% else %>
<%= button_to 'Like', post_likes_path(#post), method: :post %>
<% end %>
<%= #post.likes.count %><%= (#post.likes.count) == 1 ? 'Like' : 'Likes'%>
</div>
Here is my Like controller:
class LikesController < ApplicationController
before_action :find_post
before_action :find_like, only: [:destroy]
def create
if (!already_liked?)
#post.likes.create(user_id: current_user.id)
end
end
def destroy
if (already_liked?)
#like.destroy
end
end
private
def already_liked?
Like.where(user_id: current_user.id, post_id:
params[:post_id]).exists?
end
def find_post
#post = Post.find(params[:post_id])
end
def find_like
#like = #post.likes.find(params[:id])
end
end
Here is one of the views in which the _likes partial shows up (although the issue persists everywhere it appears):
<div class="post-display">
<% if #post.title %>
<h1><%= #post.title %></h1>
<% end %>
<% if #post.user %>
Post by <%= #post.user.email %>
<% end %>
<% if #post.price %>
<p>$<%= sprintf "%.2f", #post.price %></p>
<% end %>
<% if #post.description %>
<p><%= #post.description %></p>
<% end %>
<% if #post.image.present? %>
<%= image_tag #post.image.variant(:small) %>
<% end %>
<%= render 'likes/likes' %>
</div>
<% if current_user == #post.user %>
<%= link_to "Edit", edit_post_path(#post) %>
<%= button_to "Delete", #post, method: :delete %>
<% end %>
<% if #post.comments.count > 0 %>
<div class="post-comments">
<h2 class="post-comments-headline">Comments</h2>
<%= render #post.comments %>
</div>
<% end %>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
If you don't have an answer to my question, but have an idea on how to improve my code - let me know either way! I'm trying to learn here...
Thank you,
Jill
Since you're using rails 7, rendering turbo_stream in response to "like" and "unlike" buttons will update the page without refreshing.
# config/routes.rb
resources :posts do
# NOTE: i've used singular `resource`, since there is no need to have `id`
# for the like.
resource :like, only: [:destroy, :create]
end
https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resource
# app/models/post.rb
class Post < ApplicationRecord
has_many :likes
def liked_by? user
likes.where(user: user).exists?
end
end
# app/models/like.rb
class Like < ApplicationRecord
belongs_to :post
belongs_to :user
# NOTE: avoid double likes.
validates_uniqueness_of :user, scope: :post, message: "already liked this post"
# TODO: create a unique index migration, to really make sure no double likes.
# `add_index :likes, [:post_id, :user_id], unique: true`
end
I've simplified LikesController a bit. No need for before_action filters:
# app/controllers/likes_controller.rb
class LikesController < ApplicationController
# POST /posts/:post_id/like
def create
# NOTE: uniqueness validation in `Like` model will prevent creating dup likes.
post.likes.create(user: current_user)
# you can access `like` error if you want to show it:
# like = post.likes.create(user: current_user)
# like.errors.full_messages
# NOTE: that's it, now we render `likes` partial inside a `turbo_stream`
render turbo_stream: turbo_stream.replace(
helpers.dom_id(post, :like), # this is the target div `id` that will be replaced
partial: "posts/likes", # with `likes` partial.
locals: { post: post }
)
end
# DELETE /posts/:post_id/like
def destroy
# NOTE: this will work regardless if there are any likes or not.
post.likes.where(user: current_user).destroy_all
# NOTE: alternatively, we can render the same `turbo_stream` as above
# in a template `likes/likes.turbo_stream.erb`:
render :likes
end
private
def post
#post ||= Post.find params[:post_id]
end
end
<!-- app/views/posts/_likes.html.erb -->
<!-- `dom_id` helps us generate a uniq id: "like_post_1" -->
<div id="<%= dom_id(post, :like) %>">
<!-- yes, there is a rails helper for this -->
<%= pluralize post.likes.count, "like" %>
<% if post.liked_by? current_user %>
<%= button_to "Unlike", post_like_path(post), method: :delete %>
<% else %>
<%= button_to "Like", post_like_path(post) %>
<% end %>
</div>
This turbo_stream is the same as in create action:
<!-- app/views/likes/likes.turbo_stream.erb -->
<%= turbo_stream.replace dom_id(#post, :like) do %>
<%= render "posts/likes", post: #post %>
<% end %>
https://turbo.hotwired.dev/handbook/streams
Try this
views file where likes partial render
<div id='post_likes'>
<%= render 'likes/likes' %>
</div>
/likes/_likes.html.erb
<div class="likes">
<% if liked %>
<%= button_to 'Unlike', post_like_path(#post, liked), method: :delete, remote: true %>
<% else %>
<%= button_to 'Like', post_likes_path(#post), method: :post, remote: true %>
<% end %>
<%= #post.likes.count %><%= pluralize(#post.likes.count, 'Like') %>
</div>
views/likes/create.js.erb
$('#post_likes').html('<%= render 'likes/likes' %>');
views/likes/destroy.js.erb
$('#post_likes').html('<%= render 'likes/likes' %>');
Im trying to get all the Posts from the the Users. I'm following using the act_as_follower gem. The User follows a profile model, and Posts belong to a User.
My User model:
acts_as_follower
My Profile model the user follows:
belongs_to :user
acts_as_followable
Post model:
belongs_to :user
My Post Controller.rb:
def follow
#profile = Profile.find(params[:id])
current_user.follow(#profile)
redirect_to :back
end
def unfollow
#profile = Profile.find(params[:id])
current_user.stop_following(#profile)
redirect_to :back
end
Im trying to implement something like this using follows_by_type method provided:
#posts = current_user.follows_by_type('Post').order("created_at DESC")
But the thing is the User follows the Profile model, but here I'm looking for a type 'Post'.
EDIT
In my index controller i'v set up the following:
#favoritePost = Post.where(user_id: current_user.all_follows.pluck(:id))
And in the view iv implemented this:
<% if user_signed_in? %>
<%#favoritePost.each do |post| %>
<%= post.title %>
<% end %>
<% end %>
The gem lets you follow multiple models and follows_by_type('Post') filters the Posts you follow.
What you're looking to do is to return the posts from the users you follow.
Controller
#posts = Post.where(user_id: current_user.all_following.pluck(:id))
View
<% #posts.each do |post| %>
<%= post.title %>
<% end %>
I worked out this solution, might not be the best, but it works as required.
In my controller i set this up:
#following = current_user.all_following
#followposts = #following.each do |f|
f.user.id
end
And in my view I have set this up:
<% #followposts.each do |f| %>
<% f.user.posts.each do |g| %>
<%= g.title %>
<% end %>
<% end %>
Added an admin view to my rails app (this: http://www.interque.co/). Added a boolean state for approved to each question. I'm trying to toggle that state by clicking a link I've labeled approved.
Here is my controller:
class AdminController < ApplicationController
def index
#questions = Question.all
unless current_user && current_user.administrator?
redirect_to root_path
end
end
end
Here's the admin view I'm working with, which lists all unapproved questions:
<% if current_user %>
<% #questions.each do |question| %>
<% if question.approved == false %>
<div>
Title: <%= question.title %><br>
Description: <%= question.description %><br>
<%= link_to "Approve", question_path(question), :method => :put %>
</div>
<br>
<% end %>
<% end %>
<% end %>
Can I toggle the state by clicking a link_to? I just need to set question.approved = true.
Any help would be appreciated. Thanks in advance.
Add remote: true to your link and add a controller action that sets boolean state. You'll probably want to check that link is changed to 'approved' after that (included in the answer). Another good thing will be displaying an error message in case of error and disabling link during request.
config/routes.rb
resources :questions do
member do
put :approve
end
end
views/admin/index.html.erb
<span class="approve-question">
<%= link_to "Approve", approve_question_path(question), :method => :put, remote: true %>
</span>
views/questions/approve.js.erb
$('.approve-question').html('Approved');
controllers/questions_controller.rb
respond_to :js
def approve
#question = Question.find(params[:id])
if #question.update(approved: true)
render
else
render #question
end
end
Friends I just want to show a single post against its description.
On my home page I have all the descriptions and when a user clicks any description then user should be redirected to its respective post.
I have two models one for Post and Descrip.
Descrip belongs_to Post
and
Post has_one Descrip
I just want to know that if user clicks on any description then on next page a post should appear related to that description.
Kindly help or suggest me how i can do that. At this time I just get all the posts when user click a description link. Any help will be highly appreciated. I have partial file which i render to my home page.This partila file have a link button as below
<% #post.reverse_each do |post| %>
<li>
<p>
<%= descrip.description %>
</p>
<%=link_to 'APPLY TO THIS JOB', descriptions_view_path, :class => 'read_more pull_rigt' %>
<% end %>
View.html.erb where i render my partial file look like this
<%= render partial: 'posts/post', locals: { post: Post.all} %>
and posts controller have two methods
def show
redirect_to post_path(Post.all) and return
end
def index
#posts = Post.all(:order => "created_at DESC")
end
and description controller have a view method
def view
#descrip = Descrip.find_by_id(params[:post_id])
#post = Post.all
end
Do this:
#config/routes.rb
root to: "descriptions#index"
resources :descriptions
#app/controllers/descriptions_controller.rb
def index
#descriptions = Description.all
end
def show
#description = Description.find params[:id]
end
#app/models/description.rb
Class Description < ActiveRecord::Base
has_one :post
end
#app/views/descriptions/index.html.erb
<% for description in #descriptions do %>
<%= link_to description.title, description_path(description.id) %>
<% end %>
#app/views/descriptions/show.html.erb
<%= #description.post %>
In my rails app I have a simple poll system. User checks options he likes and votes. The problem is check_boxes not showing and even if will showing I wouldn't Vote all the same because made some mistakes in my code likely
polls/show.html.haml:
- for question in #poll.questions.each
%span= question.title
%p= question.comment
- form_tag upvote_question_path do
%p= check_box_tag question, question.id
%p= question.title
= submit_tag 'Vote'
questions_controller.rb
class QuestionsController < ApplicationController
def upvote
#question = Question.find(params[:id])
#question.increment!(:votes_count)
redirect_to :back
end
end
If you need more information please comment!
Try this
<% #poll.questions.each do |question| %>
<span> <%= question.title %></span>
<%= form_tag upvote_question_path do %>
<p><%= check_box_tag :question_id, question.id %></p>
<p><%= question.title %></p>
<%= submit_tag 'Vote' %>
<% end %>
<% end %>
In controller
def upvote
#question = Question.find(params[:question_id])
#question.increment!(:votes_count)
redirect_to :back
end
Several things:
Your update_vote_path doesn't seem to be a member route?
You could easily achieve this with a button
You're looping through questions -- maybe this is empty?
Code
I would try this:
#config/routes.rb
resources :questions do
#/questions/2/vote
post :vote
end
#app/views/your_view
- for question in #poll.questions.each
%span= question.title
%p= question.comment
- button_to "Vote", vote_questions_path(question), method: :post
#app/controllers/questions_controller.rb
class QuestionsController < ApplicationController
def vote
#question = Question.find(params[:question_id])
#question.increment!(:votes_count)
redirect_to :back
end
end
Modularity
You're trying to manually submit a form with specific params, whereas you can handle all of that with the routes
I think your main issue will be how you're calling the #poll.questions variable. I don't believe it will be loading correctly, but without seeing your other code, this is just speculation