I cannot make a function to delete likes - ruby-on-rails

I try to make simple voting system for comments.
Just one button "voteup", and if the user has already clicked it, it changes to "delete vote". And everything seems to work, except for the voice removal feature. If I click "delete vote" an error Couldn't find Post with 'id'=11 appears.
And I do not understand why this is so, because this is, in fact, one method that is used both for voting and for removing one's vote. Only in one case does everything work, but in the other not.
votes_controller:
class VotesController < ApplicationController
before_action :find_comment
before_action :find_vote, only: [:destroy]
def create
if already_voted?
flash[:notice] = "You can't like more than once"
else
#comment.votes.create(author_id: current_author.id)
end
redirect_to post_path(#post)
end
def destroy
if !(already_voted?)
flash[:notice] = "Cannot unlike"
else
#vote.destroy
end
redirect_to post_path(#post)
end
private
def find_comment
#post = Post.find(params[:post_id])
#comment = Comment.find(params[:comment_id])
end
def already_voted?
Vote.where(author_id: current_author.id, comment_id:
params[:comment_id]).exists?
end
def find_vote
#vote = #comment.votes.find(params[:id])
end
end
Votes elements in _comment.html.erb:
<% pre_vote = comment.votes.find { |vote| vote.author_id == current_author.id} %>
<% if pre_vote %>
<%= button_to 'Delete Vote', post_comment_vote_path(comment, pre_vote), method: :delete %>
<% else %>
<%= button_to 'UpVote', post_comment_votes_path(post, comment), method: :post %>
<% end %>
<p><%= comment.votes.count %> <%= (comment.votes.count) == 1 ? 'Like' : 'Likes'%></p>
UPD
This post has id - 3, not 11.
The comment has id 11.
For some reason, it confused everything during the removal of like.
UPD 2
Migration:
def change
create_table :votes do |t|
t.references :comment, null: false, foreign_key: true
t.references :author, null: false, foreign_key: true
t.timestamps
end
end
vote.rb :
class Vote < ApplicationRecord
belongs_to :comment
belongs_to :author
end
comment.rb and author.rb : has_many :votes, dependent: :destroy

It looks like you're missing an argument from your delete link. Try adding post as the first argument:
<%= button_to 'Delete Vote', post_comment_vote_path(post, comment, pre_vote), method: :delete %>

Related

I need to add a destroy function for decline option in my follow request page

I am new to rails and I am creating a app in which a clone like twitter. The users are connected with each other by sending request first and the corresponding user accepts or deletes the request. I followed michael-hartl book.
User model:
attr_accessible :email, :name, :password, :password_confirmation, :id
has_many :reverse_requests, foreign_key: "requested_id", class_name: "Request", dependent: :destroy
has_many :requesters, through: :reverse_requests, source: :requester
has_many :requests, foreign_key: "requester_id", dependent: :destroy
has_many :requested_users, through: :requests, source: :requested
Accept or decline view:
<ul class="users">
<% #users.each do |user| %>
<%= link_to gravatar_for(user, size: 30), user %>
<%= link_to user.name, user %>
//Accept which invokes create
<%= form_for(current_user.relationships.build(follower_id: user.id)) do |f| %>
<%= f.hidden_field :follower_id %>
<%= f.submit "Accept", class: "btn btn-large btn-primary" %>
<% end %>
//Decline which invokes destroy
<%= form_for(user.requests.find_by_requested_id(current_user),
html: { method: :delete }) do |f| %>
<%= f.submit "Decline", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
</ul>
Requests controller:
def create
#user = User.find(params[:request][:requested_id])
current_user.request!(#user, 2)
#current_user.status!(2)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Request.find(params[:id]).requester
current_user.decline!(#user)
respond_to do |format|
format.html { redirect_to current_user }
format.js
end
end
When decline is clicked the request is removed from the database as well as from the user requests list. Which I need it also after accepting the request it should be removed from both the database and request list.
Can anyone help to achieve this?
Is it possible to call the destroy function after the accept button is clicked?
Or any other suggestions??
Answering to your question, YES we can call the destroy action. But I suggest we go with something more scale and pretty design, here is my rough design (not tested):
# routes.rb
Rails.application.routes.draw do
resources :requests, only: [] do
member do
patch :accept
patch :decline
end
end
end
# request.rb
class Request < ApplicationRecord
belongs_to :user
def accept
# 1. Do accept logic
# 2. Destroy
destroy
end
def decline
# 1. Do decline logic
# 2. Destroy
destroy
end
end
# requests_controller.rb
class RequestsController < ApplicationController
before_action :set_request
def accept
#request.accept
# Redirect somewhere
redirect_to profile_path
end
def decline
#request.decline
# Redirect somewhere
redirect_to profile_path
end
private
def set_request
#request = Request.find(params[:id])
end
end
With the following design you and add more logic easily to decline/accept e.g send email ...

How to get the user that published the comment and time it was created from the database so I can present it to the view

I'm creating a forum. I have successfully created a Post model presenting posts in the html view with the user email and created_at time. I have also created a Comment model for replying to posts. I've been following a tutorial and understand most of it, but am lost in now getting the user and created_at values of the comments from the database so I can display them. Even though I did it with the post model, it's different because I'm using partials that display in the show html view from the Post controller, and it's confusing me that both the post and comments are displaying in the post controller show view. (i.e. the comments don't have their own show view). I'm a newbie. Any help would be appreciated. Thank you.
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :posts do
resources :comments
end
root 'posts#index'
end
Migration for create_comments
class CreateComments < ActiveRecord::Migration[5.0]
def change
create_table :comments do |t|
t.text :comment
t.references :post, foreign_key: true
t.references :user, foreign_key: true
t.timestamps
end
end
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:comment))
#comment.user = current_user
if #comment.save
redirect_to post_path(#post)
else
render 'new'
end
end
end
_form.html.haml
= simple_form_for([#post, #post.comments.new]) do |f|
= f.input :comment
= f.submit
models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
end
show.html.haml
#post_content
%h1= #post.title
- if user_allowed_post
= link_to "Delete", post_path(#post), method: :delete, data: { confirm: "Are you sure you want to delete this?"}, class: "button"
= link_to "Edit", edit_post_path(#post), class: "button"
- else
%br
%br
%br
%p= #post.content
%p
Published
= time_ago_in_words(#post.created_at)
by
= #post.user.email
#comments
%h2
- if #post.comments.size == 1
= #post.comments.size
Comment
- else
= #post.comments.size
Comments
= render #post.comments
%h3 Reply to thread
= render "comments/form"
Let me know if you need any other files or info.
A local variable comment will be available to you in your partial _comment.html.haml when you specify = render #post.comments .
You should be able to do = comment.user and = comment.created_at in the partial.

How to get UsersPublisher join model working

I have three models: User, Publisher and Interest all with many to many relationships linked through three join models but only 2 out of 3 join models record the id's of their 2 parent models. my UsersPublisher model does not link User to Publisher.
My Interestscontroller proccesses a form (see code) through which I ask the user to provide Interest and Publisher. The latter gets processed via the fields_for method which allows you to pass Publisher attributes via the InterestsController. the UsersPublisher join model records the user_id but the publisher_id is nil.
I've tried putting #users_publishers in both the new and create methods of Publishers- and InterestsController. My latest attempt of using after_action in the InterestsController (see code) has also failed. I've also tried the after_action way in the PublishersController
Your helped is highly appreciated!
The UsersPublisher join model
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
end
InterestsController
class InterestsController < ApplicationController
before_action :find_user
after_action :upublisher, only: [:new]
def index
#interests = policy_scope(Interest)
end
def show
#interest = Interest.find(params[:id])
end
def new
#interest = Interest.new
#interest.publishers.build
authorize #interest
end
def create
#interest = Interest.new(interest_params)
#users_interests = UsersInterest.create(user: current_user, interest: #interest)
authorize #interest
if #interest.save
respond_to do |format|
format.js
format.html {redirect_to root_path}
end
flash[:notice] = 'Thank you, we will be in touch soon'
else
respond_to do |format|
format.js { render }
format.html { render :new }
end
end
end
def edit
#interest = Interest.find(params[:id])
authorize #interest
end
def update
#interest = Interest.find(params[:id])
#interest.update(interest_params)
if #interest.save
flash[:notice] = 'Your interest has been added'
else
flash[:notice] = 'Oops something went wrong'
end
end
private
def interest_params
params.require(:interest).permit(:name, publishers_attributes: [:publisher,:id, :feed])
end
def find_user
#user = current_user
end
def upublisher
#users_publishers = UsersPublisher.create(publisher: #publisher, user: current_user)
end
end
Form
<%= form_for [#user, #interest] do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :publishers do |ff| %>
<%= ff.label :publisher %>
<%= ff.text_field :publisher %>
<%= ff.label :feed %>
<%= ff.text_field :feed %>
<%end%>
<%= f.submit "Submit" %>
<%end%>
Since you're using fields_for, you'll want to make sure you have accepts_nested_attributes_for:
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
This should fix your issue (if it's as you outlined).
Your question is pretty broad, so I don't know whether the above will work. Below are my notes...
From the looks of it, your structure is very complicated; you should work to make it as simple as possible. In the case of creating "interests", you may wish to get rid of the form completely:
#config/routes.rb
resources :publishers do
resources :interests, path: "interest", only: [:create, :destroy] #-> url.com/publishers/:publisher_id/interest
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
before_action :set_publisher
def create
current_user.interests.create publisher: #publisher
end
def destroy
#interest = current_user.interests.find_by publisher_id: #publisher.id
current_user.interests.delete #interest
end
private
def set_publisher
#publisher = UserPublisher.find params[:publisher_id]
end
end
You'd be able to use the above as follows:
<%= link_to "Add Interest", publisher_interest_path(#publisher), method: :post %>
<%= link_to "Remove Interest", publisher_interest_path(#publisher), method: :delete %>
Thinking about it properly, you've got a pretty bad structure.
I'd do something like this:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :interests
has_many :publishers, through: :interests
end
#app/models/interest.rb
class Interest < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
#app/models/publisher.rb
class Publisher < ActiveRecord::Base
has_many :interests,
has_many :users, through: :interests
end
This should give you the ability to create interests for any number of users and publishers. If you create a publisher for a specific user, you can use accepts_nested_attributes_for to pass the appropriate data:
#config/routes.rb
resources :users do
resources :interest, only: [:new, :create, :destroy] #-> url.com/users/:user_id/interests/new
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
def new
#user = User.find params[:user_id]
#interest = #user.interests.new
#interest.publisher.build
end
def create
#user = User.find params[:user_id]
#interest = #user.interests.new interest_params
end
private
def interest_params
params.require(:interest).permit(:user, :publisher)
end
end
#app/views/interests/new.html.erb
<%= form_for [#user, #interest] do |f| %>
<%= f.fields_for :publisher do |p| %>
<%= p.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>

"param is missing or the value is empty" in nested form

I'm currently trying to build a really simple nested form app in my quest to learn rails. In this app I have three models legal_form answer and question. I have my my answers.html.erb, set up as follows:
<%= form_for (#legal_form) do |f| %>
<h1>Title <%= #legal_form.title %></h1>
<p><%= #legal_form.description %></p>
<ul>
<% #legal_form.questions.each do |question| %>
<%= fields_for question.answers.build do |q| %>
<li>
<%= question.question_content %>
<%= q.text_field :answer_content %>
</li>
<% end =%>
<% end %>
</ul>
<p><%= f.submit "Submit" %></p>
<% end %>
Which currently grabs the three questions I have stored and renders text input boxes next to them; works without a problem. However, when I submit the values, I get the "param is missing or empty: legal_form".
I figure that this is most likely due to my strong params configuration in the legal_forms controller, see below.
class LegalFormsController < ApplicationController
before_action :find_legal_form, only: [:show, :edit, :update, :destroy, :answers]
def index
#legal_form = LegalForm.all.order("created_at DESC")
end
def show
end
def new
#legal_form=LegalForm.new
end
def create
#legal_form = LegalForm.new(legal_form_params)
if #legal_form.save
redirect_to #legal_form, notice: "Successfully created new legal form."
else
render 'new'
end
end
def edit
end
def update
if #legal_form.update(legal_form_params)
redirect_to #legal_form
else
render 'edit'
end
end
def destroy
#legal_form.destroy
redirect_to root_path, notice: "Successfully deleted form"
end
def answers
#questions=#legal_form.questions
#legal_form=LegalForm.find(params[:id])
end
private
def legal_form_params
params.reqire(:legal_form).permit(:title, :description, :questions_attribute => [:id, :question_number, :question_content, :_destroy, :answer_attributes => [:id, :answer_content, :question_id, :user_id]])
end
def find_legal_form
#legal_form=LegalForm.find(params[:id])
end
end
And, in case it's helpful, here are the models for each.
class Answer < ActiveRecord::Base
belongs_to :question
end
class LegalForm < ActiveRecord::Base
has_many :questions, :dependent => :destroy
has_many :answers, through: :entity_roles
accepts_nested_attributes_for :questions,
reject_if: proc { |attributes| attributes['question_content'].blank? },
allow_destroy: true
end
class Question < ActiveRecord::Base
belongs_to :legal_form
has_many :answers
accepts_nested_attributes_for :answers,
reject_if: proc { |attributes| attributes['question_content'].blank? },
allow_destroy: true
end
Also, as requested here's my routes file:
Rails.application.routes.draw do
resources :legal_forms do
member do
get 'answers'
end
end
resources :answers
root "legal_forms#index"
end
Any help to finally conquer nested forms would be greatly appreciated. I've been banging my head against it off and on for about a week now. Many thanks in advance.
Try in the controller
def legal_form_params
params.require(:legal_form).permit(...)
end
Also question.answers.build add it to the method of your controller and call the object that returns the responses to fields_for
UPDATE
To be sent through this form your results, should probably be like this
form
<%= f.fields_for :answers do |q| %>
...
<% end =%>
in the new method
def new
#legal_form=LegalForm.new
#answers = #legal_form.question.answers.build
end
def legal_form_params
params.require(:legal_form).permit! #temporarily
end
not tried it, but imagine how it works something like this

Comments on multiple models

Within my rails app, I currently have comments setup to work with my posts model, which is functioning properly. How do I add comments to my books model?
Here is what I have so far:
Here is what I have in my schema for the comments:
create_table "comments", force: true do |t|
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "post_id"
t.integer "book_id"
end
In my user model:
class User < ActiveRecord::Base
has_many :comments
acts_as_voter
end
In my post model:
class Post < ActiveRecord::Base
has_many :comments
end
In my book model:
class Book < ActiveRecord::Base
has_many :comments
end
In my comment model:
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :book
belongs_to :user
acts_as_votable
end
In my comments controller:
class CommentsController < ApplicationController
def create
post.comments.create(new_comment_params) do |comment|
comment.user = current_user
end
respond_to do |format|
format.html {redirect_to post_path(post)}
end
end
def upvote
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.liked_by current_user
respond_to do |format|
format.html {redirect_to #post}
end
end
private
def new_comment_params
params.require(:comment).permit(:body)
end
def post
#post = Post.find(params[:post_id])
end
end
In my routes file:
resources :posts do
resources :comments do
member do
put "like", to: "comments#upvote"
end
end
end
In my view:
<% #post.comments.each do |comment| %>
<%= comment.body %>
<% if user_signed_in? && (current_user != comment.user) && !(current_user.voted_for? comment) %>
<%= link_to “up vote”, like_post_comment_path(#post, comment), method: :put %>
<%= comment.votes.size %>
<% else %>
<%= comment.votes.size %></a>
<% end %>
<% end %>
<br />
<%= form_for([#post, #post.comments.build]) do |f| %>
<p><%= f.text_area :body, :cols => "80", :rows => "10" %></p>
<p><%= f.submit “comment” %></p>
<% end %>
What do I add to my comments controller to get comments working on both posts and books? What do I add to my routes file?
Thanks in advance for any help.
You don't want to specify each type of object that can hold Comment objects. That creates a headache of if-elsif-else blocks all over the place. Instead, you want things to be Commentable, and they all will have .comments on them.
This is called a polymorphic association in Active Record. So you would have your models something like:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Book < ActiveRecord::Base
has_many :comments, as: :commentable
end
And modify your database accordingly, it's all in the linked article. Now when you build a Comment object for a form, it will have pre-populated a commentable_id and commentable_type, which you can toss in hidden fields. Now it doesn't matter what the Comment is associated with, you always treat it the same.
I'd leave User as a separate association, since it's not really the same idea.

Resources