Editing a comment that belongs to a post - ruby-on-rails

So I have created a blog that users can create posts and others can comment on that post.
I want users to be able to edit the comments.
What I did so far is the following:
My routes are like this:
resources :posts do
resources :comments
end
and when I rake route I get this:
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
on my view file I use this which is obviously wrong
<%= link_to 'Edit Comment', edit_post_comment_path(#comment) %>
and my controller is like that:
def edit
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
def update
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
if #comment.update(comments_params)
redirect_to post_path(#post)
else
render 'edit'
end
end
When I try to visit the page (of the post) I get this error:
No route matches {:action=>"edit", :controller=>"comments", :id=>nil, :post_id=>nil, :format=>nil} missing required keys: [:post_id, :id]
That's the first time I work on nested routes. I read some tutorials but had nothing really helped me to fix this. I am not sure how can I pass these keys...
(if you need any other info please let me know and I will provide them)
Any help is greatly appreciated!

You need to pass both the #post and #comment instance.
edit_post_comment_path(#post, #comment)
Generally speaking, you need to pass every instance matching the resource the route is part of. So, in a single route like
edit_post_path(#post)
you pass only the post. In a post/comment nested route you pass both. In a post/comment/whatever you will need to pass the three resources, etc.

Related

Error when removing controller's name from url's

Ok so I have a rails app where user can create pins. Then they can comments those pins. What I what to do is to remove the controller name in pin url.
So instead of: http://localhost:3000/pins/name I have http://localhost:3000/name
**I did that by using this in my **config/routes.rb****
Rails.application.routes.draw do
resources :pins, :only => [:index, :new, :create], :path => '' do
resources :comments
member do
put 'upvote'
end
end
But now, when I try to comment a pin I have this error:
wrong constant name 'pin name'
and the error come fom this lines from my comments_controller.rb:
def load_commentable
resource, id = request.path.split('/')[1, 2]
#commentable = resource.singularize.classify.constantize.friendly.find(id)
end
Any ideas how I could fix this ?
EDIT:
**rake routes** output:
pin_comments GET /:pin_id/comments(.:format) comments#index
POST /:pin_id/comments(.:format) comments#create
new_pin_comment GET /:pin_id/comments/new(.:format) comments#new
edit_pin_comment GET /:pin_id/comments/:id/edit(.:format) comments#edit
pin_comment GET /:pin_id/comments/:id(.:format) comments#show
PATCH /:pin_id/comments/:id(.:format) comments#update
PUT /:pin_id/comments/:id(.:format) comments#update
DELETE /:pin_id/comments/:id(.:format) comments#destroy
Use params (Hash with request parameters) instead of resource, id = request.path.split('/')[1, 2]. This should fix your second problem.
I think you probably came from a php background or something like that, cause I used to think like that back then when I used php my self, but in rails you don't touch the URI or try to parse it or anything, that's the router's job, and if it reached that part of your code then the job was already done.
If you are using the pin's name as a url then you should either use a friendly_id gem, or set the to_param method of the model.
The id of the pin will always be in the params[:pin_id] because that's how it's named in the routes, and the id of the comment will be in the params[:id], mind the variable names in the route
/:pin_id/comments/:id
I'm not sure what you mean by the resource, but if you mean the model name, well you're in a pin's controller, so it's safe to assume it's a pin model, but if you want the name of the controller then you could access params[:controller]
Your load_commentable method would probably look like this after fixing everything
def load_commentable
#commentable = Pin.find(params[:pin_id]).comments.where(id: params[:id])
end

redirect_to in action destroy ruby on rails

I have the following resources in db/config
resources :posts do
resources :comments
end
I have the following action destroy inside of my controller called Comment
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
When I eliminate the comment rails redirect to the url of #post
example /posts/1
But I don't want to redirect to that url but to
posts/1/comments
Use
redirect_to post_comments_path(#post)
From the command line, if you do rake routes it will show you:
Prefix Verb URI Pattern Controller#Action
post_comments GET /posts/:post_id/comments(.:format) comments#index
The post_comments prefix lets you know that you have the two helpers available: post_comments_path and post_comments_url for generating the associated path or url.

adding delete option for product rails member routes

My problem is that I need to add the option for delete product and I'm stock with that....
First I added this line in the index.html.erb:
<% glyph_to "Delete", product, method: :delete, data: (confirm: "Are you sure that product" ##(product.id) is not used?") if can? (:destroy, product)%>
but I don't now in that line what is the difference between "method: :delete" and "if can? (:destroy, product)", destroy is the controller action I guess and delete? I don't know....
another thing is my controller definition for that action:
def destroy
#product = Product.find(params[:id])
#product.destroy
redirect_to product_path
end
but when I press "delete" it doesn't redirect to product_path
and my routes definition are the following:
resources products do
member do
get :destroy
end
end
I will be very grateful of any help with this,
thank you for your time :D!
Yes , destroy is controller's delete method , to redirect to the product listing page you should use redirect_to action: 'index'. You can check all actions and method through command rake routes
You should redirect_to products_path(this translates to index) since currently it says redirect_to product path(translating to show) which will throw error
method: :delete is for posting to destroy action and can? is a cancan gem method by Ryan Bates. The if checks if user has the permissions to delete the product or not.
The permissions can be set in app/models/ability.rb
Lastly change your routes to:
resources: :products
destroy action must be called on HTTP DELETE request and never on GET. You should definitely fix the following code from your routes:
resources products do ## missing : here, it should be :products
## member block not required
member do
get :destroy ## destroy should never be GET request
end
end
You just need to define your routes as:
resources :products ## Notice :products and not products
This will generate following routes for you which you can check by running command rake routes
products GET /products(.:format) products#index
POST /products(.:format) products#create
new_product GET /products/new(.:format) products#new
edit_product GET /products/:id/edit(.:format) products#edit
product GET /products/:id(.:format) products#show
PATCH /products/:id(.:format) products#update
PUT /products/:id(.:format) products#update
DELETE /products/:id(.:format) products#destroy
Notice the last route generated.
destroy action should be called as HTTP Delete method which is why on your link you need to specify method: :delete as
<% glyph_to "Delete", product, method: :delete, data: (confirm: "Are you sure that product" ##(product.id) is not used?") if can? (:destroy, product)%>
so when you click on this link a DELETE request would be sent which would be mapped to your route for products#destroy, i.e.,
DELETE /products/:id(.:format) products#destroy
Also, you need to update the destroy action as below:
def destroy
#product = Product.find(params[:id])
#product.destroy
redirect_to products_url ## products_url notice plural products
end
products_url would redirect you to index page of products.
As product_path is for show, i.e., to show a particular product. You can't show a deleted product that too without providing an id to product_path. So, logically you should be redirecting to index action with products_url (NOTE using **_url for redirection is advisable but you could also use products_path)

Rails 3 Engine Problem with Routes

I have an engine, with this routes file:
Rails.application.routes.draw do
resources :comments, :controller => 'opinio/comments'
end
When I run the rake routes task, I get the correct output
comments GET /comments(.:format) {:action=>"index", :controller=>"opinio/comments"}
POST /comments(.:format) {:action=>"create", :controller=>"opinio/comments"}
new_comment GET /comments/new(.:format) {:action=>"new", :controller=>"opinio/comments"}
edit_comment GET /comments/:id/edit(.:format) {:action=>"edit", :controller=>"opinio/comments"}
comment GET /comments/:id(.:format) {:action=>"show", :controller=>"opinio/comments"}
PUT /comments/:id(.:format) {:action=>"update", :controller=>"opinio/comments"}
DELETE /comments/:id(.:format) {:action=>"destroy", :controller=>"opinio/comments"}
My controller is pretty simple:
class Opinio::CommentsController < ApplicationController
include Opinio::Controllers::InternalHelpers
def index
resource.comments.page(params[:page])
end
def create
#comment = resource.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
flash[:notice] = I18n.translate('opinio.comment.sent', :default => "Comment sent successfully.")
else
flash[:error] = I18n.translate('opinio.comment.error', :default => "Error sending the comment.")
end
end
end
But when I try using any action that goes to the engine's controller I get the following error:
uninitialized constant Comment::CommentsController
I sincerely don't know where Rails is magically adding this Comment namespace on the controller, and I don't have a clue of how to solve this.
Wow, this deserves an answer so nobody ever do such stupidity like I did.
Basically, I added this to my engine's module:
mattr_accessor :name
##name = "Comment"
and internally, there is already a method name on every module, which I accidentally overrided, and causing all the errors. AS tried to load the missing constant, but when called for name inside my Opinio model, it got "Comment" instead of Opinio.
A reminder for myself and any others out there.
Don`t use obvious names and attributes without checking if they already exist first.

Rails Resources are giving me headache

I am a .NET developer moving to Ruby on Rails. I have been programming in ASP.NET MVC and now trying to apply the same concepts to Rails. I created index action and now when I say home/index it automatically redirects me the "show" action which does not exists. My routes.rb file has this particular line:
resources :home
home is the home_controller.
What am I doing wrong?
class HomeController < ApplicationController
def show
end
# show all the articles
def index
#articles = Array.new[Article.new,Article.new,Article.new]
respond_to do |format|
format.html
format.xml { render :xml => #articles }
end
end
def new
#article = Article.new
respond_to do |format|
format.html
format.xml { render :xml => #article }
end
end
def create
#article = Article.new(params[:post]);
if #article.save
format.html { redirect_to(#post,
:notice => 'Post was successfully created.') }
end
end
def confirm
end
end
You can run "rake routes" to check out what rails thinks about your routes and which urls will be dispatched to which controllers.
In your case, I get:
home_index GET /home(.:format) {:action=>"index", :controller=>"home"}
home_index POST /home(.:format) {:action=>"create", :controller=>"home"}
new_home GET /home/new(.:format) {:action=>"new", :controller=>"home"}
edit_home GET /home/:id/edit(.:format) {:action=>"edit", :controller=>"home"}
home GET /home/:id(.:format) {:action=>"show", :controller=>"home"}
home PUT /home/:id(.:format) {:action=>"update", :controller=>"home"}
home DELETE /home/:id(.:format) {:action=>"destroy", :controller=>"home"}
So to get to the index action, you need to go to "/home". If you go to "/home/index", it will think "index" is the ID of the resource, thus dispatching to the show action.
However, in Rails, it's custom to use plural names for controllers, and naming them after the resource they represent (this is usually a model, but it doesn't have to be). So in your case the name of the controller should be "ArticlesController" and your routes.rb should contain "resources :articles". Rails is very anal about plural and singular names.
The big advantage of using the plural name of the resource you're accessing, is that you can now use short notations, like "redirect_to #article", "form_for #article do |f|", etc.
So, resources in Rails are supposed to be telling about what you want your actually getting. This helps maintenance too, since other developers have to guess less. If you find yourself needing more than one ArticlesController, consider using namespaces, or try to figure out if one of those controllers are actually another resource (even though they store their data in the same database table).
More information about the routers can be found in the Rails Guide: http://guides.rubyonrails.org/routing.html

Resources