12

Sign-out link isn't working in my rails application.

I have checked my routes.rb which is listed below and my application.html.erb looks to follow the right path.

Getting the following error.

ActiveRecord::RecordNotFound in UsersController#show

Couldn't find User with id=sign_out
Rails.root: /Users/patrickwalsh/rails_projects/ytutorial

Application Trace | Framework Trace | Full Trace
app/controllers/users_controller.rb:4:in `show'
lib/disable_assets_logger.rb:11:in `call'

My routes.rb

Refectory::Application.routes.draw do
  devise_for :users, :controllers => { :registrations => "users" }
  devise_scope :user do
    get 'login', to: "devise/sessions#new", as: "login"
    get 'logout', to: "devise/sessions#destroy", as: "logout"
    get 'logout', to: "users/sessions#destroy", as: "logout"
    get 'signup', to: "users#new", as: "signup"
    match '/users/:id', :to => 'users#show', :as => :user
  end
  root :to => 'tutorials#index'

devise_for :users do get '/users/sign_out' => 'devise/sessions#destroy' 
get 'users/:id' => 'users#show'
end
  resources :tutorials
  resources :comments, only: [:show, :create, :update, :destroy]

 resources :tutorials do
    member { post :vote }
  end

  if Rails.env == "development"
    match 'errors/404' => 'errors#error_404'
    match 'errors/500' => 'errors#error_500' 
  end

  unless Rails.application.config.consider_all_requests_local
    match '*not_found', to: 'errors#error_404'
  end

  match 'tagged' => 'tutorials#tagged', :as => 'tagged'
end

and my application.html which seems to be following the right route from what I can see.

Any help is greatly appreciated!

<% if current_user.present? %>
          <li><%= link_to "Log out", destroy_user_session_path, (:method => "delete") %></li>
        <% else %>       
          <li><%= link_to "Log in", new_user_session_path %></li>
          <li><%= link_to "Sign up", new_user_registration_path %></li>
        <% end %>

My users controller as well as I have a suspicion this is where the problem lies but not sure what the error is.

class UsersController < Devise::RegistrationsController
def show
  @user = User.find(params[:id])
  @tutorials = @user.tutorials
end
end
PatGW
  • 369
  • 6
  • 19
  • possible duplicate of [Problem Signing Out with Devise on my App](http://stackoverflow.com/questions/6596936/problem-signing-out-with-devise-on-my-app) – BrokenBinary Sep 25 '15 at 00:01

10 Answers10

13

I had the same issue. My routes were in the correct order, the link_to method was properly used, and Rails kept triggering the users/:id route with :id => :sign_out. I figured out that was because I removed jquery_ujs from my application.js file...

jquery_ujs handles the data-method attribute in the links (generated by link_to when you use the method option), which is used to determine the correct route as explained here: https://thoughtbot.com/blog/a-tour-of-rails-jquery-ujs

So just make sure the you have the following included in your application.js:

//= require jquery
//= require jquery_ujs
Dave Powers
  • 2,051
  • 2
  • 30
  • 34
Rafik
  • 163
  • 1
  • 5
  • 1
    This was absolutely correct. I copied a new bootstrap theme and suddenly this error was appearing. As soon as I added these back, it worked. – Donato Aug 07 '15 at 22:06
  • Awesome answer. Struggled with this for a while, and kept staring at the routes, which looked perfect. Alas, it was javascript! I had 'jquery_ujs', but it was after my bootstrap, require so I changed the order to: //= require jquery //= require jquery_ujs //= require bootstrap – Carson Cole Aug 29 '16 at 20:46
5

I have started noticing this error after removing rails-ujs. This was done as part of the upgrade to Rails 7 with ImportMap and Hotwire suite. Changing link_to to button_to has fixed this error in this case.

<%= button_to 'Log out', destroy_user_session_path, method: :delete %>

https://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html#replacements-for-rails-ujs-functionality

bunufi
  • 654
  • 1
  • 6
  • 16
4

If you are calling /users/sign_out directly from the URL it won't work because the routes is:

destroy_user_session DELETE /users/sign_out(.:format)                      devise/sessions#destroy 

id est, it uses the DELETE method. You need to change your devise initializer to:

config.sign_out_via = :get

other option would be to create a small form and its button with DELETE as method.

aarkerio
  • 2,183
  • 2
  • 20
  • 34
3

This worked for me

#form 

<%= link_to(destroy_user_session_path, {:class => "nav-link", :method => :delete}) do  %>
      <span class="sidebar-normal"> Logout </span>
<% end %>

#routes

devise_scope :user do
    get '/users/sign_out' => 'devise/sessions#destroy'
  end
Maged Makled
  • 1,918
  • 22
  • 25
2

None of this solutions worked for me. Also it happens just in development mode for me... I fixed it by adding

  if params[:id] = "sign_out"
    sign_out current_user
    return
  end

in the set user function. Not the prettiest solution but it works...

Markus Andreas
  • 935
  • 1
  • 13
  • 12
1

You need to move:

devise_for :users do get '/users/sign_out' => 'devise/sessions#destroy' 

over your devise_scope. Rails is looking for routes from top of Routes file. Your sign out url matches users/:id, hence it is trying to render show action with sign_out being an id.

UPDATE:

Actually, do you really need the last line in your devise_scope block?

BroiSatse
  • 44,031
  • 8
  • 61
  • 86
  • Thanks @BroiSatse it was the bottom line in the `devise_scope` block that was breaking it. Appreciate the help! – PatGW Mar 18 '14 at 22:21
  • The better solution is from @bunofi https://stackoverflow.com/a/71108574/1735159 Insteade using link_to use button_to and does not need to change anything. – Carlos Fagiani Jr Jun 03 '22 at 16:36
0

Since non of the other answers worked, I found that you could change the base path for every Devise endpoint as described here. So, what I did was to user devise_for on routes.rb:

devise_for :users,
         path: 'devise'

Then, all my Devise routes started with devise instead of users so the conflict was gone.

0

Sorry to bump this up but the "correct" anwser is not to mess around with routes risking breaking many things.

IF you want a disconnect on a GET then actually configure Devise like so in initializer/devise.rb

  # The default HTTP method used to sign out a resource. Default is :delete.
  config.sign_out_via = :get

As mentionned in the Devise documentation.

Xqua
  • 1
  • 1
0

Short answer: use link_to with method DELETE

<%= link_to 'Logout', destroy_user_session_path(current_user), method: :delete %>

explanation: If you take a look at your Routes you'll see

Helper destroy_user_session_path

the HTTP verb is DELETE

the Path

/users/sign_out(.:format)

Controller#Action

devise/sessions#destroy

link_to defaoult method is get, this is "the why" (in a simplistic way of explaning). However, you can specify by using method: :method name

Wagner Braga
  • 459
  • 4
  • 9
0

I got this error when using devise in a rails 7 app (with bootstrap):

ActiveRecord::RecordNotFound (Couldn't find User with 'id'=sign_out)

Make sure you start your server with ./bin/dev (or else your javascripts won't work properly).

Even still, I also needed to use

<%= link_to "Logout", destroy_user_session_path, data: { turbo_method: "delete" } %>

Then logout worked.

stevec
  • 41,291
  • 27
  • 223
  • 311