Display name of user all users who have liked a particular post - ruby-on-rails

I want to name(s) of all user who have liked a post.
I have 4 models: user,post,like and review.
A user can have many posts.
Post can reviews && post can have many likes.
I am able to count likes on post but i am not able to display name of users who have liked a post.
Like Model:
class Like < ApplicationRecord
belongs_to :post
belongs_to :user
end
Post Model
class Post < ApplicationRecord
belongs_to :user
has_many :reviews, dependent: :destroy
has_many :likes, dependent: :destroy
validates :title, presence: true
validates :body, presence: true
end
Review Model:
class Review < ApplicationRecord
belongs_to :user
belongs_to :post
end
User Model:
class User < ApplicationRecord
before_create { generate_token(:auth_token) }
before_save { self.email = email.downcase }
has_secure_password
has_many :posts
has_many :reviews
has_many :likes
validates :name, presence: true
validates :email, presence: true, uniqueness: true
validates :password, confirmation: true
validates :password_confirmation, presence: true, unless: Proc.new { |a| !a.new_record? && a.password.blank? }
def send_password_reset
generate_token(:reset_password_token)
self.reset_password_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end[![enter image description here][1]][1]
_post.html.erb
<h1> Posts ( <%= #posts.count %> ) </h1>
<% #posts.each do |p| %>
<div class="form-field">
<h4>
<b><%= p.title %></b>
<%= link_to "Edit", edit_post_path(p.id), class: "btn btn-warning col-md-2-offset", style: "align-right" %>
<%= link_to "Delete", p, method: :delete, class: "btn btn-danger col-md-2-offset" ,data: { confirm: "You sure? "}, style: "align-right" %>
<% if p.reviews.count > 0 %>
<%= link_to "View Comments", review_path(p.id), class: "btn btn-primary" %>
<% end %>
</h4>
<%= p.body %>
<br>
<% if p.likes.count > 0 %>
Likes: <%= p.likes.count %>
<% end %>
<br>
<% if Like.find_by(user_id: current_user.id,post_id: p.id) %>
<%= link_to " UnLike ", like_path(p.id), method: :delete,class: "btn btn-
primary fa fa-thumbs-down" %>
<% else %>
<%= link_to " Like ", post_likes_path(p.id), method: :post, class: "btn btn-
primary fa fa-thumbs-up" %>
<% end %>
<br>
</div>
<% end %>
I was trying something like this,
I need to connect user,like and post table, for post which has been liked by some user.
I somehow need to merge two-three queries.

#like is an array,
instead use
#like = #liked.where(post_id: 7).first
Then try #like.user

First I hope you use eager loading to prevent the N+1 issue, when loading likes of a post, and their users
Second
I would suggest changing this peace of code
<% if Like.find_by(user_id: current_user.id,post_id: p.id) %>
<%= link_to " UnLike ", like_path(p.id), method: :delete,class: "btn btn-primary fa fa-thumbs-down" %>
<% else %>
<%= link_to " Like ", post_likes_path(p.id), method: :post, class: "btn btn-primary fa fa-thumbs-up" %>
<% end %>
With
<% if p.likes.find_by(user_id: current_user.id) %>
<%= link_to " UnLike ", like_path(p.id), method: :delete, class: "btn btn-primary fa fa-thumbs-down" %>
<% else %>
<%= link_to " Like ", post_likes_path(p.id), method: :post, class: "btn btn-primary fa fa-thumbs-up" %>
<% end %>
This will not perform additional DB request
And finnally how to shows user name of each user that has liked the post
I would suggest creating a helper method for this, as it may be need for multiple places
def liked_by_users(post)
post.likes.map do |like|
like.user.username
end.join(', ')
end

Are you just trying to get the names of users that have liked a post to display them? If that's the case you can add a function to one of your controllers called something like get_likes_for(post_id) that will return an array of all the users that liked a post:
def get_likes_for(pid)
users = []
Like.where(post_id: pid).each do |l|
users << User.find_by(id: l.user_id).name
end
users
end
Then you can list them in your post controller like this:
<% if p.likes.count > 0 %>
Likes: <%= p.likes.count %>
Liked By: <%= get_likes_for(p.id).to_s %>
<% end %>
This won't exactly connect the three models like the last part of your posts asks, but it will at least allow you to display the users who have liked a post.

Related

Edit and Update for assosiated model Rails

I tried to create edit and update action for my assosiated model named as Entity.
But When edit pages pop up no saved data shown. Means it is showing all field as empty and when I put values in it, creates a another object.
And also validation messages are not showing
Entities Controller
class EntitiesController < ApplicationController
def edit
#schema = Schema.find(params[:schema_id])
#entity = #schema.entities.find(params[:id])
end
def update
#schema = Schema.find(params[:schema_id])
#entity = #schema.entities.find(params[:id])
if #entity.update(entity_params)
redirect_to schema_entities_path(#schema)
else
render 'edit'
end
end
private
def entity_params
params.require(:entity).permit(:clientId, :name, :description, :mnemonic)
end
end
edit form for it:
<%= form_for([#schema, #schema.entities.build], data: { turbo: false }) do |f| %>
<%= f.text_field :clientId, placeholder:"ClientId" %>
<%= f.text_area :name, placeholder: "Name" %>
<%= f.text_area :description, placeholder: "Description" %>
<%= f.text_area :mnemonic, placeholder: "Mnemonic" %>
<%= f.submit 'Submit' %>
<% if #entity.errors.any? %>
<div id="error_explanation">
<% #entity.errors.full_messages.each do |msg| %>
<p class="error_msg"><%= msg %></p>
<% end %>
</div>
<% end %>
<% end %>
Its model:
class Entity < ApplicationRecord
belongs_to :schema
has_many :fields, dependent: :destroy
has_many :joins, through: :fields
validates :name, presence: true, uniqueness: true
def self.search(search)
where("name LIKE ?", "%#{search}%")
end
end
This is how I am passing values from index page:
<%= link_to 'Edit', edit_schema_entity_path(schema_id: #schema.id, id: data.id) if data.id %>

How can I make a comment visible on user's dashboard after they approve it?

I have a user, they can login and add books to a platform. They also can see when other people add books. They can click 'Show' button and see the details of a book and can comment in the show section. I want this comments to be seen only if the user approves it.
So I need
1- a notification on user's dashboard when someone commented on one of
their books
2- when they click on the notification there would be a page that has
a 'approve the comment' button and when the user clicks that
3- I want this comment to be visible under the book (in show.html.erb)
and in the user's dashboard (like a comments list).
What I did so far: I have my models; book.rb
class Book < ApplicationRecord
validates :title, presence: true
validates :author, presence: true
belongs_to :user
has_many :comments
end
comment.rb:
class Comment < ApplicationRecord
belongs_to :book
belongs_to :user
end
user.rb (dont mind the extra code I'm gonna clean it up later. I'm new to this so I'm having trouble understanding what does what for now):
class User < ApplicationRecord
before_create :set_username
has_many :books
has_many :comments
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
attr_writer :login
def login
#login || self.username || self.email
end
# validates_length_of :username,
# :within => 5..50,
# :too_short => " is too short, must be at least 5 characters.",
# :presence => true
private
def set_username
self.username = self.email.split("#").first
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
if conditions[:username].nil?
where(conditions).first
else
where(username: conditions[:username]).first
end
end
end
end
here's books show.html.erb:
<p style="text-align:center">
<%= link_to 'Shared Books', root_path %>
<%= link_to 'New Book', new_book_path %>
</p>
<p>
<strong>Title:</strong>
<%= #book.title %>
</p>
<p>
<strong>Text:</strong>
<%= #book.author %>
</p>
<p>
<strong>Page Count:</strong>
<%= #book.page_count %>
</p>
<p>
<strong>Share with others:</strong>
<%= #book.hide_from_others %>
</p>
<h2>Comments</h2>
<% #book.comments.each do |comment| %>
<p>
<strong>Commenter:</strong>
<%= comment.title %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.content %>
</p>
<% end %>
<h2>Add a comment:</h2>
<%= form_with(model: [ #book, #book.comments.build ], local: true) do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :content %><br>
<%= form.text_area :content %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<% if current_user.id == #book.user.id %>
<p style="text-align:center">
<%= link_to 'Edit', edit_book_path(#book) %>
</p>
<% end %>
<p style="text-align:center">
<%= link_to 'Back', books_path %>
</p>
dashboard index.html.erb:
<h1>Dashboard</h1>
<table border='4', align="center">
<tr>
<th>Title</th>
<th>Author</th>
<th>Page Count</th>
<th>Username</th>
<th>Status</th>
<th>Action</th>
</tr>
<% #books.each do |book| %>
<tr>
<td><%= book.title %></td>
<td><%= book.author %></td>
<td><%= book.page_count %></td>
<td><%= book.user.username %></td>
<td><%= book.hide_from_others ? 'Shared' : 'Hidden' %></td>
<td><%= link_to 'Show', book_path(book) %>
<%= link_to 'Edit', edit_book_path(book) %>
<%= link_to 'Destroy', book_path(book),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<%= link_to 'New Book', new_book_path %>
<%= link_to 'Shared Books', root_path %>
comments controller:
class CommentsController < ApplicationController
def create
#book = Book.find(params[:book_id])
#comment = #book.comments.build(comment_params)
#comment.user_id = current_user.id
#comment.save
redirect_to book_path(#book)
end
private
def comment_params
params.require(:comment).permit(:title, :content)
end
end
dashboard controller:
class DashboardController < ApplicationController
def index
#books = current_user.books
end
end
routes.rb:
Rails.application.routes.draw do
devise_for :users
root 'welcome#index'
resources :dashboard
resources :books do
resources :comments
end
end
finally this is generate model Comment migration:
class CreateComments < ActiveRecord::Migration[6.0]
def change
create_table :comments do |t|
t.string :title
t.text :content
t.boolean :status
t.references :book, null: false, foreign_key: { to_table: :books }
t.references :user, null: false, foreign_key: { to_table: :users }
t.timestamps
end
end
end
I thought I add a status boolean, and somehow bind it with approve button. When status == true, comment would be approved and seen under the book. But I'm not sure how.
First you need to add scope approved to your Comment model to select only approved comments.
scope :approved, -> {where(status: true)}
Then in your view books/show.html.erb change #book.comments to #book.comments.approved
To update a comment, you'll need to add update action to your CommentsController

Rails4: How to pass dynamic array from view to controller

I have a problem creating Vote which contains value of points that user decided to give for a candidate (Borda Count Method). Values are in number_fields and I want them to be saved in Vote objects.
In my case I am only able to get value from the last number_field and I have no idea how to fix it. Other values are being overwritten so I came up with idea to create an array of points which contains values taken from many number_field_tag and pass them from view to controller.
Something about survey structure:
There is a Surv (survey) which contains many Polls (questions).
One Poll contains many Vote Options.
There are also Votes which save votes of user.
OUTPUT from params
{"utf8"=>"✓", "poll"=>{"id"=>"11"}, "9"=>"17", "arr"=>"1212", "pnts"=>"", "10"=>"20", "11"=>"22", "commit"=>"Zagłosuj", "controller"=>"votes", "action"=>"create"}
VIEW:
show.html.erb
<p id="notice"><%= notice %></p>
<h2>Twoja ankieta:</h2>
<h1>
<%= #surv.topic %>
</h1>
<h2>
<%= election_type %>
</h2>
<%= render 'voting_form' %>
<%= link_to 'Edit', edit_surv_path(#surv) %> |
_voting_form.html.erb
<%= form_tag votes_path, method: :post, remote: true, id: 'voting_form' do %>
<% if #surv.votingtype == "default" %>
<%# here is unnecessary code ... %>
<% elsif #surv.votingtype == "bordy" %> <%# BORDY %>
<% #polls.each do |poll| %>
<h2><%= poll.topic %></h2>
<h2>POLL ID: <%= poll.id %></h2>
<%= hidden_field_tag 'poll[id]', poll.id %>
<%= arr = [] %>
<%= i = 0 %>
<% poll.vote_options.each do |option| %>
<div class="form-group">
<%= content_tag(:label) do %>
<div class="select">
<%= option.title %>
<%= radio_button_tag option.poll_id, option.id %>
<%# if selected then vote is created %>
<%= number_field_tag :point %>
<%#= arr << :point %>
<%= hidden_field_tag 'pnts', arr %>
<%# some tries to pass params %>
</div>
<% end %>
<%= visualize_votes_for option %>
</div>
<% end %>
<p><b>Głosów razem: <%= poll.votes_summary %></b></p>
<% end %>
<% if current_user && current_user.voted_for3?(#surv) %>
<p>Twój głos został DO ANKIETY został zapisany.</p>
<% else %>
<%= submit_tag 'Zagłosuj', class: 'btn btn-lg btn-primary' %>
<% end %>
<% end %>
<% end %>
CONTROLLER:
votes_controller.rb
class VotesController < ApplicationController
def create
#poll = Poll.find_by_id(params[:poll][:id])
#surv = Surv.find_by_id(#poll.surv_id)
puts "Surv id:"
puts #surv.id
#surv.polls.each do |p|
puts "Poll ID (poll voted)."
puts p.id
puts "Vote option ID (vote option voted)"
puts params[p.id.to_s]
#option = p.vote_options.find_by_id(params[p.id.to_s])
if p && !current_user.voted_for?(p)
#here i would like to grab params specially specified for each poll
#vote = #option.votes.create({user_id: current_user.id, points: params[:pnts]})
##vote = #option.votes.create({user_id: current_user.id, points: params[:point]})
puts 'POINTS'
puts params.inspect
else
render js: 'alert(\'Glosowales juz w tej ankiecie!\');'
return
end
# end
end
#surv.update_attribute(:actual_votes, #surv.actual_votes+1)
if (#surv.maximum_votes > 0 && #surv.actual_votes >= #surv.maximum_votes)
respond_to do |format|
format.html { redirect_to terminate_surv_path(#surv), notice: 'Ostatni głos został oddany' }
format.js {render inline: "location.reload();" }
end
end
end
def vote_params
params.require(:vote).permit(:points)
end
end
MODEL:
vote_option.rb
class VoteOption < ActiveRecord::Base
belongs_to :surv
accepts_nested_attributes_for :surv
belongs_to :poll
accepts_nested_attributes_for :poll
has_many :votes, dependent: :destroy
has_many :users, through: :votes
validates :title, presence: true
end
vote.rb
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :vote_option
end
def surv_params
params.require(:surv).permit(:topic, :votingtype,
polls_attributes: [:id, :topic, :endtime, :endontime, :_destroy,
vote_options_attributes: [:id, :title, :_destroy]
])
end
Please let me know if you need some additional code or informations to help me fix my problem. Thank you in advance

How to pass an :id to a link_to through a has_many :through relationship to delete a join table column. (Rails)

This is my first post on StackOverflow, and I'm relatively new to coding in general, so I apologize in advance if I accidentally make some breach of etiquette.
I'm trying to delete a collaborator to a wiki that has been added by the owner, but now he wants to remove their permissions.
This is done from within my wiki#edit view:
<div class="row">
<%= render partial: 'wikis/form' %>
<br>
<%= render partial: 'collaborators/collaborator'%>
<h3> Collaborators: </h3>
<% #wiki.users.each do |c| %>
<li><%= c.username %></li>
<% if #wiki.user == current_user && current_user.premium? %>
<%= link_to "", wiki_collaborator_path(#wiki, #collaborators), method: :delete, remote: true, class: "glyphicon glyphicon-remove" %>
<% end %>
<% end %>
The wikis/_form partial is pretty standard:
<div class="col-md-8">
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control', placeholder: "Enter wiki title" %>
</div>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, rows: 8, class: 'form-control', placeholder: "Enter wiki body" %>
</div>
<% if current_user.admin? || current_user.premium? %>
<div class="form-group">
<%= f.label :private, class: 'checkbox' do %>
<%= f.check_box :private %> Private wiki
<% end %>
</div>
<% end %>
<div class="form-group">
<%= f.submit "Save", class: 'btn btn-primary' %>
</div>
<% end %>
</div>
and the collaborators/_collaborator partial creates the initial collaborator:
<% if #wiki.user == current_user && current_user.premium? %>
<%= form_for [#wiki, Collaborator.new] do |f| %>
<%= email_field_tag :collaborator %>
<%= f.submit "Add collaborator", class: 'btn btn-primary' %>
<% end %>
<% end %>
This is the Collaborator controller:
before_action :set_wiki
def create
#collaborator = User.find_by_email(params[:collaborator]) #pulled from email tag in collaborator form_for partial
Collaborator.create(user_id: #collaborator.id, wiki_id: #wiki.id)
if #collaborator.save
flash[:notice] = "#{#collaborator.username} at #{#collaborator.email} has been added as a collaborator to #{#wiki.title}"
else
flash[:error] = "Adding of collaborator failed"
end
redirect_to #wiki
end
def delete
#collaborator = #wiki.users.find(params[:id])
if #collaborator.destroy
flash[:notice] = "#{#collaborator.username} at #{#collaborator.email} has been removed from wiki: #{#wiki.title}"
else
flash[:error] = "Removal of collaborator failed"
end
redirect_to #wiki
end
private
def set_wiki
#wiki = Wiki.find(params[:wiki_id])
end
end
The Collaborator Model just belongs_to both User and Wiki, and The Wiki model looks like this:
class Wiki < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
#Final refactoring eliminates need for delegate method
has_many :collaborators, dependent: :destroy
has_many :users, through: :collaborators
scope :visible_to, -> (user) { user ? all : where(private: false) }
validates :title, length: { minimum: 5 }, presence: true
validates :body, length: { minimum: 20 }, presence: true
end
The error I'm getting back from my local server is
No route matches {:action=>"destroy", :controller=>"collaborators", :id=>nil, :wiki_id=>"104"} missing required keys: [:id]
I've looked everywhere to solve this problem, but I inherently have some trouble understanding the nested routing for has_many :through associations and when I look for answers it seems like everyone has written their method calls differently than mine. Everything in my app functions except this link_to method, so I'd rather not rewrite it just to match someone else's code unless I really messed up somewhere.
I've tried putting #collaborator.id as well as a few other things just to try to brute force a solution, but nothing is working and I have no idea what I'm looking for here.
Any help at all would be greatly appreciated. Thank you ^^
No route matches {:action=>"destroy", :controller=>"collaborators", :id=>nil, :wiki_id=>"104"} missing required keys: [:id]
The base problem is here:
<%= link_to "", wiki_collaborator_path(#wiki, #collaborators), method: :delete, remote: true, class: "glyphicon glyphicon-remove" %>
The error states that your #collaborators var does not pass the singular id to the link_to method. This is because I presume #collaborators to be a collection.
--
You need to pass a single collaborator object:
<%= link_to "", wiki_collaborator_path(#wiki, c) ...
Since you're using a .each loop with #wiki.users, each user (which I presume to be a collaborator) has the local variable of c assigned:
<% #wiki.users.each do |c| %>
Since you're new, I'd strongly recommend using font-awesome-rails over glyphicon. The main benefit is the fa_icon helper, which would allow you to use:
<%= link_to fa_icon("close"), wiki_collaborator_path(#wiki, c), method: :delete, remote: true %>
Try to iterate through the collaborators of the #wiki to get the correct link. The collaborators of a wiki are in your case the wiki's users, because they are connected via the collaborators relation. So you just do:
<% #wiki.users.each do |collaborator| %>
<%= link_to "", wiki_collaborator_path(#wiki, collaborator), method: :delete, remote: true, class: "glyphicon glyphicon-remove" %>
<% end %>

setting user alias as message recipient

I have two models, user and treating (which you can think of as a message).
User:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
has_many :sent_treatings, :foreign_key => "requestor_id", :class_name => "Treating", dependent: :destroy
has_many :received_treatings, :foreign_key => "requestee_id", :class_name => "Treating", dependent: :destroy
end
Treating:
class Treating < ActiveRecord::Base
attr_accessible :intro, :proposed_date, :proposed_location
validates :intro, presence: true, length: { maximum: 190 }
validates :requestor_id, presence: true
validates :requestee_id, presence: true
belongs_to :requestor, class_name: "User"
belongs_to :requestee, class_name: "User"
default_scope order: 'treatings.created_at DESC'
end
I'm having trouble in my treatings controller setting 'requestee':
class TreatingsController < ApplicationController
before_filter :signed_in_user
def create
requestee = ?
requestor = current_user
#received_treating = requestee.received_treatings.build(params[:treating])
#received_treating.requestor = requestor
if #received_treating.save
flash[:success] = "Treating request sent!"
redirect_to users_path
else
render 'static_pages/home'
end
end
end
The question mark I tried to replace with: User.find(params[:id]), hoping that the user in the current 'users/show' view would be found, but I get this error:
Couldn't find User without an ID
I also tried User.find(params[:treating][:requestee_id]), but this didn't work either. Any ideas?
Thanks!
EDIT:
views/shared/_treating_form.html.erb (this references #received_treating in the users controller, show action):
<div class="field">
<%= f.text_area :intro, placeholder: "Write your introduction here..." %>
</div>
<%= f.submit "Send", class: "btn btn-large btn-primary" %>
EDIT: adding other user profile page:
<% provide(:title, #user.name) %>
<div class="row">
<aside class="span4">
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
<% if signed_in? %>
<section>
<%= render 'shared/treating_form' %>
</section>
<% end %>
</aside>
<div class="span8">
<% if #user.received_treatings.any? %>
<h3>Treating requests (<%= #user.received_treatings.count %> received)</h3>
<ol class="treatings">
<%= render #received_treatings %>
</ol>
<%= will_paginate #received_treatings %>
<% end %>
</div>
</div>
I think just adding #user.id in a hidden_field would work.
<%= f.hidden_field :requestee_id, :input_html => { :value => #user.id }
<div class="field">
<%= f.text_area :intro, placeholder: "Write your introduction here..." %>
</div>
<%= f.submit "Send", class: "btn btn-large btn-primary" %>
You could also populate the field doing
<%= form_for #user.received_treatings.build do %>
<%= f.hidden_field :requestee_id %>
....
On the controller just do
#received_treating = current_user.sent_treatings.build params[:treating]
I have some doubts if this works but I think it should help you out.

Resources