Understanding link_to for deleting nested resources - ruby-on-rails

I'm learning ruby and rails too. I understood how the link_to and deleting items worked with a single resource.
<%= link_to 'Destroy', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %>
And this works since it uses the article_path and uses rails magic where it takes takes article.id although article was passed in and there was a route article with DELETE and it needed the :id
Prefix Verb URI Pattern Controller#Action
article GET /articles/:id(.:format) articles#show
DELETE /articles/:id(.:format) articles#destroy
However after nesting a resource inside it say, comments
To delete a comment it becomes
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete, data: { confirm: 'Are you sure?' } %>
Here are the (relevant) routes for the nested resource (note :format is ommitted)
Prefix Verb URI Pattern Controller#Action
article_comment GET /articles/:article_id/comments/:id comments#show
DELETE /articles/:article_id/comments/:id comments#destroy
Controller code
def destroy
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
View code
<h3>Comments</h3>
<% #article.comments.each do |comment| %>
<p>
<strong> <%= comment.username %> </strong>: <%= comment.body %>
<!-- link_to goes here -->
</p>
<% end %>
Q1)
Firstly, is there another syntax for deleting a comment i.e another way of doing [comment.article, comment] in the structure of article_comment_path(comment) (like with non-nested resources in the first code block).
Q2)
What does [comment.article, comment] mean/do and how does it translate to the correct route with
DELETE /articles/:article_id/comments/:id comments#destroy
(Rails has a lot of syntactic sugar (I come from a Java background) so as I code I'm trying not to use syntactic sugar until I fully understand it.)
This code is all from section 5.13 (non nested resources) and section 8 (nested resources) of guides.rubyonrails.org.

For Q1) I found an answer which was
The wanted alternative syntax is
article_comment_path(#article.id, comment.id)
So overall
<%= link_to 'Destroy comment', article_comment_path(#article.id, comment.id), method: :delete, data: {confirm: 'Are you sure?'} %>
This answer was helpful
And the reason why you need the id for the article and the comment is that they are required due to the way the DELETE route is structured. #article.id is needed for articles/:article_id/ andcomment.id is needed for comments/:id
I would also like to add article_comment_path(#article.id, comment.id) in the link_to can be replaced with [#article, comment]. However if using this array input approach you cannot specify specifically the id, the whole object must be passed. So you cannot do [#article.id, comment.id] (Although I somewhat understand this array syntax with link_to im yet to get how q2 works).

Related

Can't get syntax for deleting a commentable comment in Rails 4

Here's the line from my routes config file:
DELETE /posts/:post_id/comments/:id(.:format) posts/comments#destroy
I want to delete a single comment but I can't get the syntax right. Here's what I tried:
<% if current_user == comment.user %>
<span class="edit-delete-line"><p><small><%= link_to "Delete", post_comments_path([#commentable, comment]), method: :delete, data: { confirm: "Are you sure?" } %> </span>
</small></p>
<% end %>
That gives me a routing error. I also tried:
<% if current_user == comment.user %>
<span class="edit-delete-line"><p><small><%= link_to "Delete", post_comments_path(params([:post_id], [:comment_id])), method: :delete, data: { confirm: "Are you sure?" } %> </span>
</small></p>
<% end %>
I got "wrong number of argument" error this time. I know this should be simple, right???
You're trying to delete a single comment, so you need to use post_comment_path(#commentable, comment), not plural post_comments_path, which points at the index. If you have your controller set up to also accept the unnested resource (just /comments/:id), you can just use comment_path directly.
Check out the Rails routing guide for more details.
<%= content_tag :span, link_to("Delete", [#commentable, comment], method: :delete, data: { confirm: "Are you sure?" }), class: "edit-delete-line" if current_user == comment.user %>
This should get it working for you.
To give you a simple synopsis, you have to realize that the Rails routes are basically helper methods which are generated when you define your respective routes.
The route helpers themselves don't do anything except give you a dynamic way to call specific routes. For example, instead of "/posts/<%= #post.id %>", you can call posts_path(#post).
--
The Rails routes work very simply -- they take arguments like any other helper method.
Thus, if you call a route which requires specific values to be passed (for example post_comments_path(post_id, comment_id), you have to pass the respective values to the helper.
Therefore, you can call the following:
post_comments_path(#commentable, comment), method: :delete
... or if you're using link_to, you should be able to pass the respective data objects that you require:
link_to "Destroy", [#commentable, comment], method: :delete

How to call a certain path in delete method Rails

Typically when I am using a delete link it will look something like this
<% #variable_name.each do |block| %>
<%= link_to 'Destroy', block, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
For the current project, I am unable to use the "block" path for delete. I now need to call a path that is related to block, but not defined in it. I was thinking something like this
<% #variable_name.each do |block| %>
<%= link_to 'Destroy', someother_path, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
Is it possible to link to a url instead of using a helper method? If so how would I find that url.
Yes, you can use any other urls as long as you defined the routes in routes.rb
in your routes.rb file
resources :variables do
delete :someother, on: :member
end
then you can check the routes.
To check the urls, please run following command line in terminal.
rake routes
or
rake routes | grep variable
and you can do
<% #variable_name.each do |block| %>
<%= link_to 'Destroy', someother_variable_path(Variable.find_by_name(block)), method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
You can pass arguments to the path helper:
someother_path(first_argument: 'whatever')
Also, if you want the full path of the link (including domain host + port), use this:
someother_url(first_argument: 'whatever')
The last option is to write the path as a string, for example:
link_to 'Destroy', "/posts/#{post.id}/destroy" # etc.
But this last option should not be used as it is complicated to maintain.

Ruby on Rails link_to explanation for a n00b?

I'm doing a Rails blog tutorial and don't fully understand the following link_to code
<%= link_to 'Destroy Comment', [comment.post, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
Why do I have to use:
[comment.post, comment]
and why can't I just write:
#post.comment
My second, related, question is that since I created the "destroy" action in the controller as follows:
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
Why don't I have to mention "destroy" in the link_to code?
<%= link_to 'Destroy Comment', [comment.post, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
The reason why you have to supply both the Post object and the Comment to the link_to helper is because Comment is a nested resource in Post, and both IDs must be known in order to construct the URL. It's actually equivalent to:
link_to 'Destroy Comment', post_comment_path(comment.post, comment), ...
What it's doing is it's resolving the path helper for you, using url_for. See http://guides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects .
You don't have to mention destroy in your link_to because destroy is the name of the action. Your routes file outlines which controllers and actions are associated with which routes.
I assume that you're using resourceful routing, which is shorthand way of defining routes for all of the CRUD actions. See http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions for the mapping between HTTP verb and controller action. You'll see that delete is mapped to destroy, and you're using method: :delete on your link_to.
So there are a lot of things going on here.
1) My guess is that the link_to in the first part is within a loop. Is that true? That would be something like #post.comments.each do |comment|. If that's the case, then likely what's happening is you have comments nested under posts. That documentation can be found here. The brackets are to identify the comment, which you need a post id for. You could probably also do [#post, comment], which would work just as well. You can't just write #post.comment because it's not enough information to identify the correct comment.
2) Rails takes HTTP verbs to identify which action to call from the controller. You're sending an HTTP DELETE request to /posts/:post_id/comments/:id, which the routes file then figures out belongs to the comments controller. That documentation can be found here and here.

Routing Errors on attempts to delete all records in a table using Rails 4 through a link_to

I am using Rails 4, and am trying to delete all the assessments using a link_to helper. The pertinent code is below:
routes.rb
resources :assessments do
collection do
delete :remove_all
end
end
assessment index.html.erb
<p>To delete all assessments in one swoop, click <%= link_to 'Remove ALL Assessments', remove_all_assessments_path, method: :destroy, data: { confirm: 'Are you sure?' } %>
assessments_controller.rb
def remove_all
#assessments = Assessment.all
#assessments.each do |assessment|
assessment.destroy(assessment.id)
end
flash[:notice] = "All assessments have been deleted."
redirect_to assessments_url
end
I run rake routes and
Prefix Verb URI Pattern Controller#Action
remove_all_assessments DELETE /assessments/remove_all(.:format) assessments#remove_all
The HTML source generated for the link:
<p>To delete all assessments in one swoop, click <a data-confirm="Are you sure?" data-method="destroy" href="/assessments/remove_all" rel="nofollow">Remove ALL Assessments</a>
When I click on the 'Remove All Assessments' link, I expect to have the remove_all action run in the AssessmentsController, and delete all the assessments in #assessments, then redirect me to the assessments_url. However, when I click on the 'Remove ALL Assessments', link, I am brought to the url: http://localhost:3000/assessments/remove_all with the error No route matches [POST] "/assessments/remove_all"
What gives?
Verb is DELETE, so you should use the :delete method in your link_to.
There is no method DESTROY in the HTTP spec.
It should be
<p>To delete all assessments in one swoop, click <%= link_to 'Remove ALL Assessments', remove_all_assessments_path, method: :delete, data: { confirm: 'Are you sure?' } %>

Rails - cannot figure out how to destroy a record

I am not able to find the right way to destroy a record. I feel like such a complete newb.
Here are the routes pertaining to the controller (output from rake routes):
contents GET /admin/contents(.:format) {:controller=>"contents", :action=>"index"}
contents POST /admin/contents(.:format) {:controller=>"contents", :action=>"create"}
new_content GET /admin/contents/new(.:format) {:controller=>"contents", :action=>"new"}
edit_content GET /admin/contents/:id/edit(.:format) {:controller=>"contents", :action=>"edit"}
content GET /admin/contents/:id(.:format) {:controller=>"contents", :action=>"show"}
content PUT /admin/contents/:id(.:format) {:controller=>"contents", :action=>"update"}
content DELETE /admin/contents/:id(.:format) {:controller=>"contents", :action=>"destroy"}
What is getting me is the bottom line does not look any different than the get and put.
Here is the link:
<%= link_to 'Destroy', content, :confirm => 'Are you sure?', :method => :delete %>
also tried:
<%= link_to 'Destroy', content, :confirm => 'Are you sure?', :method => :destroy %>
and the output is:
Destroy
Can someone spot what I am doing wrong here? :-/
edit
I did not intially have rails.js loading. I do now.
Here is the contents of my destroy action:
def destroy
#content = Content.find(params[:id])
#content.destroy
respond_to do |format|
format.html { redirect_to(contents_url) }
format.xml { head :ok }
end
end
I am deleting from the content index, like so:
<% #contents.each do |content| %>
<tr>
<td><%= content.name %></td>
<td><%= link_to 'Show', content %></td>
<td><%= link_to 'Edit', edit_content_path(content) %></td>
<td><%= link_to 'Destroy', content, :confirm => 'Are you sure?', :method => :destroy %></td>
</tr>
<% end %>
</table>
The URL looks the same because it is the same. The difference lie within the request method. Your Rails app knows to separate GET, PUT and DELETE requests--even if they are made to the same URL--and route the request to the right action.
However, not all browsers/web servers support all of these methods, so Rails rely upon unobtrusive JavaScript (ujs) to "fake" some of the requests--more specifically, PUT and DELETE. Because of this, you'll need to include one of the bundles for Rails apps (the Prototype comes by default; you can get the jQuery version through this gem). You can find out more through the README (and of course the source) of the jQuery ujs.
If you're experiencing issues, it's probably because you don't have the necessary ujs. It could also be that you haven't included the csrf_meta_tag in your html header.
A few things could be going wrong, but it's hard to narrow down without more information on what you have and what errors/behavior you're getting.
You don't have rails.js loading, hence the data-method="delete" isn't having the effect of sending an AJAX post with a _method argument set to "delete".
Your destroy controller action isn't doing what you are expecting.
content is not a local variable referring to an instance of the model you are trying to destroy (note that you would only use the singular content_path if this were a singular resource you were trying to destroy). Are you looking for #content instead maybe? It's hard to say without some context.
And just to clarify for you, the method option is the HTTP method, not the controller method. There is no destroy HTTP method; Rails just uses destroy over delete as a sort of "delete gracefully".

Resources