Add a 'destroyAll' action and route to my RESTful rails app - ruby-on-rails

I want to add a destroyAll action that destroys all posts in a blog app that I am writing. I have my routes.rb like this :
Rails.application.routes.draw do
resources :writings
root "writings#index"
end
And inside my controller I want to add the following action :
def destroyAll
#writing = Writing.all
#writing.each.destroy
end
please guide me through how to add a route to the destroyAll action and how to use it inside my view to make a link to use that action.
Thanks so much in advance

It should be defined as collection route of writings:
resources :writings do
collection do
delete :destroy_all
end
end
Having it, you can link to delete_all action like this:
link_to 'delete all', [:destroy_all, :writings], method: :delete
You should notice that I wrote delete_all instead of deleteAll, because the former sticks to Ruby naming convention.

Very late to the party, but wanted to add that perhaps another way is to consider all of the writings as a kind of resource itself, so you can keep the controllers adhering to the basic RESTful actions Rails typically uses.
resource :writings_collection, only: :destroy
# DELETE /writings_collection --> WritingsCollectionController#destroy
The advantage is the route helpers will not conflict with the writings ones.
You could even name the collection resource something meaningful that represents the collection concept.

I think Marek Lipka's answer is perfectly adequate, but I'd like to offer an alternative, which IMO is a tad bit more RESTful. I'd model the collection of all writings as a separate resource. Since this resource does not have an id and there can only be one instance of this resource, it must be a singular resource:
resource :writings, only: :destroy
The problem with this is, that Rails will map this action to WritingsController#destroy, since Rails uses plural controllers for singular resources (any our singular resource has a plural name anyway), so this will conflict with the destroy action for individual writings. In that case you could either create a custom controller for this and implement a destroy action there:
resource :writings, only: :destroy, controller: 'collections/writings'
or you could still use the old controller, but specify another action:
resource :writings, only: :destroy, action: :destroy_all
In that case, you could use the following in your views:
link_to 'delete all', writings_path, method: :delete

Related

What is the purpose of adding routes for additional new actions?

The guide Rails Routing from the Outside In has a section named Adding Routes for Additional New Actions in chapter 2, Resource Routing: the Rails Default, with this example:
resources :comments do
get 'preview', on: :new
end
which generates the following route:
Prefix Verb URI Pattern Controller#Action
preview_new_comment GET /comments/new/preview(.:format) comments#preview
The same result can be achieved at least in two other ways:
1) Adding a collection route inside resources :comments like get 'new/preview', to: :preview, on: :collection (if you don't bother about the route name)
2) Adding just this route to the corresponding singular resource:
resource :comment, only: :preview do
get 'new/preview', to: :preview, as: 'preview_new', on: :collection
end
which has exactly the same behavior.
So why there is a on: :new option? Is it just a shortcut? If it is, why it's just for 'new actions' and there are not any similar options for the other default REST actions, like on: :edit or on: :delete?
This is a shortcut that is unique to the RESTful action new, in which an object is instantiated but not yet persisted.
For example, if you are filling out a form for a new object, you would provide yourself with a link_to "preview", which sends all of the attributes to your preview action (instead of directly to create). This is similar to a show view, but for an object that only exists in memory.
This functionality isn't logical for destroy, update, index, show or create. I suppose you could use a preview on an edit action after assigning new attributes before sending to update, but you'd have to create the route manually.
Actually there is a subtle difference.
If you add it to the resources :comments the path helper generated has a pluralized resource name so it will be preview_new_comments and the path will be /comments/new/preview. If you add it to the corresponding singular resource it doesn't pluralize the resource name in the path so you get /comment/new/preview but the helper will be preview_new_comment. Using :on will generate the path helper with the singular resource preview_new_comment but the route will have the pluralized resource /comments/new/preview.
To get the same behaviour you could use the following more verbose route outside the resource
get 'collections/new/preview', to: 'collections#preview', as: :preview_new_comment, on: :collection
I'm not sure why there is no on: :<action other than new> option. They advise against having too many new actions in the documentation but if you are going to have a new 'new' it seems a reasonable possibility you might need an new 'edit' to edit your new 'new'. However, things become more complicated because to edit you would need a path like collections/<id>/edit/preview and the path helper of the solution above stops working as you might expect. More importantly at this stage maybe you start veering away from the original intention of the option.

Including attributes in custom Rails routes

I hope the title is not to misleading, as I don't know a better title for the problem I'm working on:
I have a doctor which belongs to location and specialty. I'd like to route to show action of the doc controller like this:
/dentist/berlin/7
I defined my routes like this:
get ':specialty/:location/:id', to: 'docs#show'
And in my views create the following url to link to the show action of the doc controller:
<%= link_to doc.name, "#{doc.specialty.name}/#{doc.location.name}/#{doc.id}" %>
Is this a good solution to the problem? If not, is there a cleaner way to construct urls like this possibly using resources? What the heck is the name for a this problem?
Thank your very much for your help in advance.
For references, you should have a look at this page (especially the end of section 2.6)
If it is only for a single route, it's okay as you did. But then if you want to have more than one route (like /dentist/berlin/7, /dentist/berlin/7/make_appointment, etc.) you might want to structure a bit more your routes so as to take advantage of rails resources.
For example, instead of
get ':specialty/:location/:id', to: 'doctors#show'
get ':specialty/:location/:id/appointment', to: 'doctors#new_appointment'
post ':specialty/:location/:id/appointment', to: 'doctors#post_appointment'
You could have something like this (the code is almost equivalent, see explanation below)
resources :doctors, path: '/:specialty/:location', only: [:show] do
member do
get 'new_appointment'
post 'create_appointment'
end
end
Explanation
resources will generate the RESTful routes (index, show, edit, new, create, destroy) for the specified controller (doctors_controller I assume)
The 'only' means you don't want to add all the RESTful routes, just the ones specified
Then you want to add member actions, ie. actions that can be executed on a particular item of the collection. You can chose different syntaxes
resources :doctors do
member do
# Everything here will have the prefix /:id so the action applies to a particular item
end
end
# OR
resources :doctors do
get 'new_appointement', on: :member
end
By default, the controller action is the same as the path name you give, but you can also override it
member do
get 'appointment', action: 'new_appointment'
post 'appointment', action: 'post_appointment'
end
Rails has some wonderful helpers when it comes to routing !
The correct approach is to give your route a name, like this:
get ':specialty/:location/:id', to: 'docs#show', as: 'docs_show'
Then you can use it like this:
<%= link_to doc.name, docs_show_path(doc.specialty.name, doc.location.name, doc.id) %>
Note 1:
Rails appends _path at the end of the route names you define.
Note 2:
You can see all the available named routes by executing rake routes.

How do I specify destroy path helper in a view of my Rails app?

I have a model Category in my Rails app. According to rails RESTful Routes, I can perform CRUD Operations on model, having resources: categories defined in my routes.rb.
But how do i define destroy path helper in my view to perform DELETE action, just like edit_category_path(#category) to edit the record. I tried like this
destroy_category_path(#category) but getting error as
undefined method `destroy_category_path' for #<#<Class:0x00000005371298>:0x000000053734f8>
The path is exactly the same as the show action ('/categories/:id'), but you also need to specify the DELETE HTTP method:
button_to #category, method: :delete
Note, it is not considered safe to use links having destructive/constructive actions, as those might be visited by robots.
There is a path helper for delete, but Rails defaults to having this route not defined. To activate the helper you need to add delete to your resourceful routes in your routes.rb file.
resources :categories do
member do
get :delete
end
end
Once you've done that you should be able to use delete_category_path(#category).
Then inside your category you can call destroy on the object from your delete action.

Rails Routing question

I'm not very familiar with routing, but here is my dilemma:
I have a photos controller with the usual show, edit, etc. views. I am trying to build a view to moderate photos. I have a moderate.html.erb view under my photos views. I have also defined moderate method in my photos_controller. If I try to access this view like /photos/moderate I get Couldn't find Photo with ID=moderate.
Am I building this the correct way, or does moderate need to have its own separate controller and view? Seems silly to me for that to be the case. Is this just something I need to configure in my routes?
UPDATE:
I've added this to my routes:
resources :photos do
resources :comments
collection do
get 'moderate'
end
end
Still getting the same Couldn't find Photo with ID=moderate message when I go to /photos/moderate...
UPDATE:
Crap! Just figured out the problem. I had a before_filter running that needed to ignore the moderate action... It's now working fine. Sorry for the trouble.
You can add a further restful action. Take a look at the docs
So you're trying to split out the concern of being able to edit and view photos?
If you wanted to keep your actions RESTful, you could create a "moderate" namespace and put all the actions that require authentication for photos in that namespace.
For instance,
#routes.rb
namespace :moderate do
resources :photos # will create paths like '/moderate/photos/', '/moderate/photos/1'
end
#controllers/moderate/photos_controller.rb
class Moderate::PhotosController < ActionController
before_filter :authorize_moderator!
#your standard restful actions like 'index', 'show', 'edit, 'update' would go in here.
end

Router for nested resources in a "not usual" Ruby on Rails way

I am using Ruby on Rails 3.0.7 and I am trying to set nested resource routing to make it to work in a "not regular" RoR way.
In my routes.rb file I have
resources :articles do
resources :categories, :only => [:index], :controller => 'articles/categories' # The related controller is Articles::CategoriesController
end
so that I can browse following URLs:
<my_site>/articles/1/categories
<my_site>/articles/2/categories
...
What I would to do is to access new, edit and show controller actions for categories by using the same articles/categories controller used for the nested resource stated above (that is, Articles::CategoriesController) and by accessing these URLs:
<my_site>/articles/categories/new
<my_site>/articles/categories/edit
<my_site>/articles/categories/1
<my_site>/articles/categories/2
...
How can I do that? How I must code the router?
Maybe I can do something by using the router collection method like this
resources :articles do
collection do
# match something here for the Articles::CategoriesController...
end
resources :categories, :only => [:index], :controller => 'articles/categories'
end
but I don't know how to do that.
I'm not real sure what you're trying to do with those routes, so I'm not quite sure how to answer your questions. If your intent is to be able to add a new category for a particular article, or edit all the categories for a particular article, you have to pass an ID for the article. If you're trying to create a new article and a new category all at once, you don't need category in the route, just the article and you can do something like form_for([#article,#category]) in your form and use the build method in your controller. If you can clarify, I might be able to help you further (in other words, it's not hard to construct those routes -- but it depends on what you want to do with them.

Resources