Rails: What's the proper RESTful way to make this route? - ruby-on-rails

So I have a Carts model which has_many Items. When I'm on the Carts#show page I want to have a link to an action that removes an item from that cart, in other words something that will find the item and set its cart_id to nil.
What should I name this action? Which controller should it be in -- CartsController or ItemsController? And lastly... how do I specify this route in my routes.rb (CODE PLEASE FOR THIS ONE)
Edit
I should be a bit more explicit: I don't want to destroy an item when I remove it from a person's cart. Each item is unique, and thus destroying it would destroy the listing... which is undesirable. Note that this also implies that this isn't a HABTM relationship... items can't belong to more than one cart.

Route:
resources :carts do
resources :items
end
This generates the following routes:
cart_items GET /carts/:cart_id/items(.:format) items#index
POST /carts/:cart_id/items(.:format) items#create
new_cart_item GET /carts/:cart_id/items/new(.:format) items#new
edit_cart_item GET /carts/:cart_id/items/:id/edit(.:format) items#edit
cart_item GET /carts/:cart_id/items/:id(.:format) items#show
PUT /carts/:cart_id/items/:id(.:format) items#update
DELETE /carts/:cart_id/items/:id(.:format) items#destroy
carts GET /carts(.:format) carts#index
POST /carts(.:format) carts#create
new_cart GET /carts/new(.:format) carts#new
edit_cart GET /carts/:id/edit(.:format) carts#edit
cart GET /carts/:id(.:format) carts#show
PUT /carts/:id(.:format) carts#update
DELETE /carts/:id(.:format) carts#destroy
So you're really explicit in describing items - carts relationship.
You use your controllers like you normally would.
I don't know why would you want to set the cart_id to nil instead of removing it, but this is how you would do it:
# in items_controller.rb
def destroy
#item = Item.find(params[:id])
#item.update_attributes(:cart_id => params[:cart_id])
respond_to do |format|
format.html { redirect_to items_url }
format.json { head :no_content }
end
end

Related

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)

Editing a comment that belongs to a post

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.

Creating a page that is dependant on the model id

I have a create product page that takes infomation like name description. I want to make another page that allows the user to add a photo. To that product page. So i created a method for this page.
def pics
#product = Product.find(params[:id])
#photo = Photo.new
#product.photos.build
end
And also i added this to the config file
resources :products do
collection do
get :pics
end
end
And my rake routes look like this.
pics_products GET /products/pics(.:format) products#pics
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
PUT /products/:id(.:format) products#update
DELETE /products/:id(.:format) products#destroy
Right now it's not taking the ID of the product it's in. How can i turn it into pics_products GET /products/:id/pics(.:format)?
You should change you routes like this:
resources :products do
member do
get :pics
end
end

Rails 3 - Missing Index Paths?

I'm having a pretty weird problem with one of my rails apps. I think I'm probably doing something really silly that I just haven't been able to identify. My problem is that, I seem to be missing about half of my index paths.
For example, if my controller is "foos" for a model foo, I'll have the:
foos POST /foos(.:format) {:action=>"create", :controller=>"foos"}
But no GET option which would usually be as:
foos GET /foos(.:format) {:action=>"index", :controller=>"foos"}
Below I'll show you my actually code, to help me recover my missing index routes.
routes.rb:
resource :announcements, :controller => "announcements" do
resources :comments
member do
post 'vote'
end
end
routes for the announcements part:
announcements POST /announcements(.:format) {:action=>"create", :controller=>"announcements"}
new_announcements GET /announcements/new(.:format) {:action=>"new", :controller=>"announcements"}
edit_announcements GET /announcements/edit(.:format) {:action=>"edit", :controller=>"announcements"}
GET /announcements(.:format) {:action=>"show", :controller=>"announcements"}
PUT /announcements(.:format) {:action=>"update", :controller=>"announcements"}
DELETE /announcements(.:format) {:action=>"destroy", :controller=>"announcements"}
As you can see there is no get / index. In my controller, I have the simply index method defined...
def index
#announcements = Announcement.all
respond_to do |format|
format.html
format.xml { render :xml => #announcements }
end
end
I really don't understand why I don't have this index path. It's happening on several other controllers as well. Any help would be appreciated.
Edit: In the console, app.announcements_path returns a method missing error, in addition to the others that have missing index paths.
This is because you're using the singularized version of resources (resource). There is no index action route generated for these. You should change this to be the pluralized version, and remove :controller from the line too.

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