Rails 3 routing - :delete method on :collection - ruby-on-rails-3

I want to create a route to allow deleting all shares. RESTful way would be to use verb DELETE. How can I create a routing that points to:
DELETE /shares
I tried in the routes:
resources :shares do
delete :on => :collection
end
But this yielded an error that rails can't turn nil into a symbol.
For now I have:
resources :shares do
delete 'delete_all', :on => :collection
end
EDIT: I had a typo in controller action name and this latter way works, but produces URL /shares/delete_all which is not very RESTful.
How can I drop the _delete_all_ part?

For Rails 3 you can do it this way and have nice resourceful GET/DELETE collection actions pointing to index and delete_all respectively:
resources :shares do
delete :index, on: :collection, action: :delete_all
end
If you're using Rails 4 you can make use of concerns to DRY this up and apply it to many resources:
concern :deleteallable do
delete :index, on: :collection, action: :delete_all
end
resources :shares, concerns: :deleteallable
resources :widgets, concerns: :deleteallable

What am I missing?
match 'shares', :to => 'shares#delete_all', :via => :delete
more info: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
<subjective opinion>
This is generally a bad idea and a code/design smell. The need to be deleting all records via a RESTful interface should really be behind a protected (authenticated) action and/or the action should be scoped to the user somehow.

The right way for your case, in routes:
resources :shares, except: :destroy
resource :share, only: :destroy
Please, pay attention, that I wrote word resource for destroy action
Then redefine "destroy" in shares_controller:
def destroy
respond_to do |format|
if Share.destroy_all
format.html { redirect_to root_path, notice: 'Share collection successfully deleted' }
else
format.html { redirect_to root_path, notice: 'Share collection cannot be deleted.' }
end
end
end

Here is the non REST way of doing it:
resources :shares do
collection do
delete :destroy_all
end
end
Then in your controller you will need something like this:
def destroy_all
Share.delete_all
end
Then this is what you want to do:
resources :shares do
collection do
delete :index
end
end
Then in your controller you will need something like this:
def index
if request.method == delete #delete might need to be a string here, I don't know
Share.delete_all
else
#shares = Share.all
end
end

There's a slightly simpler syntax for this that works in Rails 4.2 at least:
resources :shares do
delete on: :collection, action: :destroy_all
end

resources :shares do
collection do
delete '/', :to => :delete_all
end
end

Related

Redirect to root_path after failed Devise user registration

I've read & attempted the solutions posted in numerous SO posts (i.e. here, here, and here) as well as Devise's answer on how to change the path after a failed registration, and Devise's RegistrationsController code, all to no avail.
Instead of doing the custom failure method in the /lib/ folder like most suggest, It seems like the easiest place to fix/override this would be in the RegistrationsController#create method at the bottom where it's:
else
clean_up_passwords resource
respond_with resource
end
It's (I assume) correctly responding with the user (i.e. redirecting them to root_path/users), but here's the tricky part: I create a nested model when my user registers, and that was quite difficult hacking into Devise for that. I'm afraid that if I go messing with the RegistrationsController#create method, that I'll break my perfectly-working nested models.
Someone also mentioned that the Devise solution didn't work for them, but then got it to work after changing a routing problem. I doubt that's the case with me, but here's my routes.rb file just in case:
devise_for :users, :controllers => { :registrations => "registrations" }
resources :users
resources :users do
resources :lockers
end
resources :lockers do
resources :products
end
resources :products
resources :lockups
match '/user/:id', :to => 'users#show', :as => :user
root :to => 'home#index'
I completely appreciate any help anyone can provide me. I'm still pretty new to Rails, but I'm always happy to learn.
EDIT: Thanks to Passionate, I've been trying to figure out how to change the logic in the final else block in the registrations controller. Here's what I've tried (along with my noob logic):
# Obviously cleared all the fields since it made no mention of resource
# redirect_to root_path
# Too many arguments since root_path takes 0
# redirect_to root_path resource
# Just bombed, something about a String somethingorother
# render root_path
# Heh, apparently call 'respond_with' and 'redirect_to' multiple times in one action
# respond_with resource
# redirect_to root_path
First you have to fix routes.rb file . Since you're using custom controller , you also have to customize devise_scope block . Change
get "signup", :to => "devise/registrations#new"
to
get "signup", :to => "registrations#new"
And, if you try to override the method and want to set the root_path after devise rails registration, you can do like
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def create
build_resource
// The `build_resource` will build the users .
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_up(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
## replace your logic here
redirect_to root_path
end
end
end
Please note that the above code works with devise 2.2.4 . Currently, the code which is on master branch on github is changed somewhat due to rails 4 and strong_parameter compatibility .

How to create a thank you page?

I'm trying to create a thank you page, my route for this works fine since I test it the url and works just fine, however when I try to redirect in the create action I get:
Routing Error
No route matches {:action=>"thank_you", :locale=>:en, :controller=>"appointments"}
Controller
def create
#appointment = Appointment.new(params[:appointment])
if #appointment.save
#send email
AppointmentMailer.appointment_confirmation(#appointment).deliver
AppointmentMailer.new_appointment(#appointment).deliver
redirect_to :action => "thank_you"
else
render :action => 'new', :alert => #appointment.errors.full_messages.split(', ')
end
end
def thank_you
#appointment = Appointment.find(params[:id])
end
Route
resources :appointments, :except => :new do
member do
get :thank_you
end
end
You need to add it as a RESTful action (or assume a default matching route).
Nutshell:
resources :appointments do
member do
get 'thank_you'
end
end
Or:
resources :appointments do
get 'thank_you', :on => :member
end
For getting a new page, you have to do more than just editing the controller.
Edit config/routes.rb and include
match "/appointments/thank_you" => "appointments#thank_you"
You might want to insert render command in the controller, that brings you to a thank you view you have to create...

Rails 3 Correctly routing the destroy action for a session

I am refactoring my access_controller into a sessions_controller and can't seem to get my destroy action working properly.
Logging in seems to work fine, but I am unable to log out of a session. Here is the link I have for logging out:
<%= link_to("Logout", :controller => "sessions", :action => 'destroy') %>
routes.rb
resources :sessions
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
...
end
def destroy
session[:user_id] = nil
flash[:notice] = "You are now logged out"
redirect_to root_url
end
end
When I click "Logout" I get redirected to "/sessions/destroy" with a message of "The action 'show' could not be found for SessionsController". The destroy actions seems to want an id, but I don't need to pass in an id, I just want to run the action.
Ah, I found the answer here: http://railscasts.com/episodes/250-authentication-from-scratch
I need to set up my routes as follows:
get "log_out" => "sessions#destroy", :as => "log_out"
get "log_in" => "sessions#new", :as => "log_in"
resources :sessions

Rails 3 - Nested Resources Routing - One to One relationship

Having some trouble with some nested resources routing. What I'm trying to do is link to a user's profile page for editing purposes. In my view it is written as:
<%= link_to "Edit Profile", edit_user_profile_path(current_user) %>
Which errors out with:
No route matches {:action=>"edit", :controller=>"profiles", :user_id=>#<User id: 1, email: "EDITEDOUT", hashed_password: "EDITEDOUT", created_at: "2011-01-20 18:30:44", updated_at: "2011-01-20 18:30:44">}
In my routes.rb file, it looks like so:
resources :users do
resources :profiles, :controller => "profiles"
end
I checked my Rake routes, and it gave me this as a valid option:
edit_user_profile GET /users/:user_id/profiles/:id/edit(.:format) {:action=>"edit", :controller=>"profiles"}
Which I am able to manually navigate to. For good measures, here's proof of my controller:
class ProfilesController < ApplicationController
def edit
#user = current_user
#profile = current_user.profile
end
def update
#user = current_user
#profile = current_user.profile
respond_to do |format|
if #profile.update_attributes(params[:profile])
format.html { redirect_to(orders_path, :notice => "Your profile has been updated.") }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end
end
Anyway, I've been having some problem tracking this down. Any pointers would help. For my DB design Profiles belong to Users in a one-to-one relationship. I'm hoping it's just something newbish I'm not noticing a new set of eyes might help.
If you look closely at your route, you'll see that it expects both a :user_id and an :id. The latter, in this case, refers to the user profile.
In order to tell Rails that you want that particular profile, you'll have to specify both the user and the profile in your link, like this:
edit_user_profile_path(current_user, #profile)
Now, Rails will use the first argument (current_user) for the :user_id part of the route, and the second argument (#profile) for the :id.

Rails3 and Respond_with problem

I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end