Couldn't find Item with 'id'=; Destroy action - ruby-on-rails

I needed to customize my controllers so I did this:
routes:
post 'add_item', to: 'walls#create'
delete 'remove_item', to: 'walls#destroy'
destroy action WallsController:
def destroy
#item = Item.find params[:id]
#item.destroy
redirect_to :back
end
view:
<% #items.each do |item| %>
<%= item.name %> <%= link_to "X", remove_item_path(item), method: :delete %>
<% end %>
Create action works just fine but still getting error with destroy action: Couldn't find Item with 'id'=
Many thanks for help

remove_item_path is expecting an item id as argument.
link_to "X", remove_item_path(item.id), method: :delete

This works:
link_to "X", remove_item_path(id: item.id), method: :delete

Related

RoR - how to make an .each loop on views pass a variable to the controller

Im trying to add votes to comments on my blog application. But i cant seem to be able to send the variable within the below loop to the controller:
<% #comments.each do |c| %>
<h1><%= c.title %></h1>
<p><%= c.content %></p>
<p><%= c.user.username %></p>
<% if current_user.id == c.user_id %>
<%= link_to "Edit", edit_comment_path(#post, c)%>
<%= link_to "Delete", delete_comment_path(#post, c), method: :delete, data: { confirm: "Are you sure?" }%>
<% end %>
<% if current_user.id != c.user_id %>
<p>Review this comment</p>
<%= link_to like_path(c), method: :put do %>
Upvote
<%= c.upvote %>
<% end %>
<%= link_to dislike_path(c), method: :put do %>
Downvote
<%= c.downvote %>
<% end %>
<% end %>
<% end %>
controller:
...
def upvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
Routes:
Rails.application.routes.draw do
....
#Comments
post '/post/:post_id', to: 'comment#create', as: 'new_comments'
get '/post/:post_id/:comment_id/edit', to: 'comment#edit', as: 'edit_comment'
put '/post/:post_id/comment/:comment_id', to: 'comment#update', as: 'update_comment'
delete '/post/:id/:comment_id/', to: 'comment#destroy', as: 'delete_comment'
put 'like', to: "comment#upvote", as: 'like'
put 'dislike', to: "comment#downvote", as: 'dislike'
end
I would like to receive the 'c' stated on the each loop as a variable on the upvote and downvote methods to replace the #comment within object_comment = Comment.find(#comment.id), in order to increment the votes. is this possible?
As it is right i obviously receive the following error:
NoMethodError (undefined method `id' for nil:NilClass):
Your *_path methods do not know what to do with that single argument. (If you want to pass an object to your links, you might want to research RESTful routing. However, to solve it with your current routes, I suggest
<%= link_to "Downvote #{c.downvote}" dislike_path(id: c.id) %>
It should then be possible to access this id attribute in the controller via params.
def dislike
#comment = Comment.find(params[:id])
...
end
A couple of other things to think about:
It's very unusual to perform a put action on clicking a link. You might want to consider doing this as a get action instead.
Does the downvote method return something which is different for each comment? If not, it's standard practise to include it in the controller's helper.
You can get id in parameters, please check below methods
def upvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
I think you're doing it wrong. You'll need to save the user's action on the comment with a model CommentVote for example that belongs to user and comment.
Then you'll need to do something like:
class CommentVote
belongs_to :comment
belongs_to :user
enum type: [:upvote, :downvote]
def self.vote(type:, comment_id:, user_id:)
comment_vote = CommentVote.where(comment_id: comment_id, user_id: user_id).first_or_initialize
comment_vote.type = type
comment_vote.save
end
end
And then do a after_save :update_counts in the CommentVote model:
def update_counts
self.comment.upvote = self.comment.comment_votes.upvote.count
self.comment.downvote = self.comment.comment_votes.downvote.count
self.comment.save
end
and you'll call it like: CommentVote.vote(type: :upvote, comment_id: 1, user_id: 1)

Undefined method when updating boolean

I'm currently working on a todo app manager.
My task model have a description and a boolean, which is false by default.
When clicking on 'done', the boolean should switch to true.
However, I have the error undefined local variable or method `tasks'
Here's my task controller :
def index
#tasks = Task.all
#task = Task.new
end
def complete
#task = tasks.find(params[:id])
#task.completed = true
#task.save
redirect_to root_path
end
def incomplete
#task = tasks.find(params[:id])
#task.completed = false
#task.save
redirect_to root_path
end
and my index :
<h3>Tâches à réaliser</h3>
<% #tasks.incomplete.each do |task| %>
<p><%= "#{task.description} | Créé le #{task.created_at.day}/#{task.created_at.month}" %>
<%= link_to "Done", complete_task_path(task), method: :put %><%= link_to ' Modifier', edit_task_path(task) %><%= link_to ' X', task, method: :delete, data: { confirm: 'Are you sure?' } %></p>
<% end %>
<h3>Tâches réalisées</h3>
<% #tasks.completed.each do |task| %>
<p><%= task.description %>|
<%= link_to "Undo", incomplete_task_path(task), method: :delete %></p>
<% end %>
and my routes :
resources :tasks
root 'tasks#index'
match '/tasks/complete/:id' => 'tasks#complete', as: 'complete_task', via: :put
match '/tasks/complete/:id' => 'tasks#incomplete', as: 'incomplete_task', via: :delete
I've tried to change variables but this error is still the same.
Does anyone know what's wrong in here ?
The error here is pretty explicit: you're calling amethod or variable that wasn't defined.
Look at the first line in your complete and incomplete methods:
#task = tasks.find(params[:id])
tasks wasn't defined anywhere, hence the error. The #find method is, in ActiveRecord, a class method, so what you want to do is
#task = Task.find(params[:id])

No route matches [POST] - Rails destroy

I am new to RoR and still don't have enough experience on solving the different errors that may appear to me. In this case I am designing a blog where I can post articles. More specifically, my problem is related to deleting these articles.
As far as I know, writing:
resources :articles
in the routes file is an alternative for writing:
get "/articles" #index
post "/articles" #create
delete "/articles/:id" #delete
get "/articles/:id" #show
get "/articles/new" #new
get "/articles/:id/edit" #edit
patch "/articles/:id" #update
put "/articles/:id" #update
When I try to delete an article I get the following error:
No route matches [POST] "/articles/1"
The code I wrote was:
View
<% #articles.each do |art| %>
<%= art.title %>
<div>
<%= art.body %> - <%= link_to "Delete", art, method: :delete %>
</div>
<% end %>
Controller
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
It sounds like you have this in your view:
<%= art.body %> - <%= link_to "Delete", art, method: :destroy %>
But you actually need:
<%= art.body %> - <%= link_to "Delete", art, method: :delete %>
I'd advise double-checking this in your app based on your reply to a comment from #GonzaloRobaina.
It looks to me like you're missing the correct path in your code. It should work with something like this :)
<%= link_to "Delete, article_path(art), method: :delete %>

Deleting with link_to from within a Rails 4 view

The following Rails 4 link_to is wrong, and thus I'm unable to delete and not sure why. In this project, "bookmarks" is a nested resource under "users" so rake routes gives me:
DELETE /users/:user_id/bookmarks/:id(.:format) bookmarks#destroy
View:
<% #bookmarks.each do |bookmark| %>
<%= link_to "delete", user_bookmarks_path(#user, bookmark.id), method: :delete %>
<% end %>
Controller:
def destroy
#user.bookmarks.find(params[:id]).destroy
redirect_to root_path
end
private
def bookmark_params
params.require(:bookmark).permit(:title, :bookmark_url)
end
def get_user
#user = User.friendly.find(params[:user_id])
end
The result is a link that looks like http://www.example.com/users/jane-doe/bookmarks.6 where 6 is the correct ID of the bookmark to be deleted. But I don't understand why it's not creating /bookmarks/6, which I think would then work fine with destroy in my controller. It feels like there's some big conceptual piece I'm just not understanding. Any tips are appreciated.
Your view should look something like this. It appears your path name is incorrect:
<% #bookmarks.each do |bookmark| %>
<%= link_to "delete", user_bookmark_path(#user, bookmark.id), method: :delete %>
<% end %>

Denying unauthorized users from editing a profile

I am trying to setup so that users will get a "not authorized" message if they click edit for a profile that is not theirs. This message should of course not appear for admins since admins can edit all profiles. I previously done this on Permission.rb, however I got rid of the file to go with a more basic user roles/authorization.
I don't see how I can implement what I had previously on Permission.rb for my current files. I have tried some solutions but they don't add up. If someone could point me in the right direction that will be great. Also I am doing this all from scratch, user authentication/authorization.
index.html.erb:
<% #users.each do |user| %>
<li>
<% if current_user.admin? || current_user == #user %>
<% end %>
<%= link_to "Edit #{user} profile", user %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?"} %>
</li>
<% end %>
Why are you giving the chance for users to edit other peoples profiles?
First, you should have a if statement in your view where you show the link for the edit page. I guess this is showing up on a profile of every user, so i suppose the code in your controller is something like this:
def show
#user = User.find(params[:id])
end
Then in your view you should have something like this:
<% if current_user.admin? || current_user == #user %>
<%= link_to 'Edit Profile' , edit_user_path(#user) %>
<% end %>
There is also a case if someone tries to 'force' their way in, just like trying to type a url www.yourapplication.com/users/6/edit you could write a before_filter method in your controller:
before_filter :check_privileges, only => [:edit, :update]
and then write a method in called check_privileges
def check_privileges
unless current_user.admin? || current_user.id == params[:id]
flash[:warning] = 'not authorized!'
redirect_to root_path
end
end
EDIT: After the questioner edited his code, i'm showing the mistake:
You are putting the end too soon:
<% #users.each do |user| %>
<li>
<%= link_to user.name, user %>
<% if current_user.admin? || current_user == #user %>
<%= link_to "Edit #{user} profile", user %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?"} %>
<% end %>
</li>
<% end %>

Resources