title: setting Devise after_sign_in_path_for with multiple models
body: <p>I have some basic experience with Devise in a previous application that had only one Devise model.  However, I am rewriting it all from scratch to have expanded functionality, and in the second iteration I think it best to have two separate user models (patient_user and staff_user).</p>
  
  <p>I am aware of CanCan and Rolify, and will be using those for one of the models but not the other.</p>
  
  <p>My problem is with setting the after_sign_in_path_for and redirecting each model to a different "home screen".  </p>
  
  <p>I have set each model to a separate after_sign_up_path and that works beautifully.</p>
  
  <pre><code>class RegistrationsController &lt; Devise::RegistrationsController
    protected
  
    # Creating separate after_sign_up_paths for patient_user and staff_user
  
    def after_sign_up_path_for(patient_user)
      flash[:notice] = 'Welcome! You have signed up successfully.'
      privacy_agreement_path
    end
  
    # Add an after_sign_up path for staff_user
    def after_sign_up_path_for(staff_user)
      flash[:notice] = 'Welcome! You have signed up successfully.'
      dashboard_path
    end
  
  end
  </code></pre>
  
  <p>Apparently after_sign_in_path_for should be defined in Application Controller though rather than in Sessions Controller.</p>
  
  <p><a href="https://stackoverflow.com/questions/19451881/devise-after-sign-in-path-for-works-but-not-the-other-ones">Stack Overflow question clarifying this difference</a></p>
  
  <p>Here's my best attempt:</p>
  
  <pre><code>class ApplicationController &lt; ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception
  
    def after_sign_in_path_for(resource)
      case resource
      when patient_user
        privacy_agreement_path  
      when staff_user
        dashboard_path
      end
    end
  
  end
  </code></pre>
  
  <p>which gives error:</p>
  
  <pre><code>undefined local variable or method `patient_user' for #&lt;Devise::SessionsController:0x00000109a40e48&gt;
  </code></pre>
  
  <p>If I capitalise the case select conditions then it seems to recognise the variable but I get a completely different error:</p>
  
  <pre><code>class ApplicationController &lt; ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception
  
    def after_sign_in_path_for(resource)
      case resource
      when Patient_user
        privacy_agreement_path  
      when Staff_user
        dashboard_path
      end
    end
  
  end
  </code></pre>
  
  <p>which gives error</p>
  
  <pre><code>  Circular dependency detected while autoloading constant Patient_user (RuntimeError)
    ./app/controllers/application_controller.rb:11:in `after_sign_in_path_for'
  </code></pre>
  
  <p>I have tried a lot of Googling and looked at various other Devise and Circular Dependency problems but have not been able to find a work around, I guess I'm not good enough at Devise to know what I'm doing.</p>
  
  <p>The one other thing I tried was making patient_user and staff_user separate after_sign_in_path_for calls in Application controller</p>
  
  <pre><code>#application_controller.rb
    def after_sign_in_path_for(patient_user)
       privacy_agreement_path  
    end
  
    def after_sign_in_path_for(staff_user)
       dashboard_path  
    end
  </code></pre>
  
  <p>This works for staff_user, but going to /patient_users/sign_in and giving a valid username and password instead redirects to dashboard_path (not privacy_agreement_path).</p>
  
  <p>The problem seems to be focussed around the use of "resource" and the conditional statement that is intended to redirect "patient_user" accounts to "privacy_agreement_path" and "staff_user" accounts to "dashboard_path".  This works well for after_sign_up_path in RegistrationsController, but not in after_sign_in_path in ApplicationController.</p>
  
  <p>Other files</p>
  
  <pre><code>#routes.rb
  devise_for :patient_users, :controllers =&gt; { :registrations =&gt; :registrations }
  devise_for :staff_users, :controllers =&gt; { :registrations =&gt; :registrations }
  </code></pre>
  
  <p>——</p>
  
  <pre><code>#config/initializers/devise.rb
    # https://stackoverflow.com/questions/8320398/second-devise-model-not-using-generated-views
    # Workaround for having multiple Devise models, used the second answer
    config.scoped_views = true
  </code></pre>
  
  <p>Any help would be much appreciated.</p>
  
  <p><strong>EDIT:</strong></p>
  
  <p>I tried Vapire's solution:</p>
  
  <pre><code>class ApplicationController &lt; ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception
  
    def after_sign_in_path_for(resource)
      # check for the class of the object to determine what type it is
      case resource.class
      when PatientUser
        privacy_agreement_path  
      when StaffUser
        dashboard_path
      end
    end
  end
  </code></pre>
  
  <p>but this started up the error:</p>
  
  <pre><code>      undefined method `staff_user_url' for #&lt;Devise::SessionsController:0x000001047a0198&gt; (NoMethodError)
  </code></pre>
  
  <p>With a bit of Googling I found <a href="https://github.com/plataformatec/devise/issues/1350" rel="nofollow noreferrer">this discussion on Devise github</a>, with a total lack of consensus on whether this is some kind of bug or just a poor implementation.</p>
  
  <p>I followed the suggested solution though, which was to update routes.rb</p>
  
  <pre><code>#routes.rb
    devise_for :patient_users, :controllers =&gt; { :registrations =&gt; :registrations }
    devise_for :staff_users, :controllers =&gt; { :registrations =&gt; :registrations }
  
    resources :patient_users #added as bugfix
    resources :staff_users #added as bug fix
  </code></pre>
  
  <p>This gave a new error:</p>
  
  <pre><code>      uninitialized constant StaffUsersController (ActionController::RoutingError)
  </code></pre>
  
  <p>So I created a new Controller file:</p>
  
  <pre><code>#controllers/staff_users_controller.rb
  class StaffUsersController &lt; ApplicationController
  
  end
  </code></pre>
  
  <p>which gave error</p>
  
  <pre><code>  The action 'show' could not be found for StaffUsersController (AbstractController::ActionNotFound)
  </code></pre>
  
  <p>So I added that to the controller file</p>
  
  <pre><code>#controllers/staff_users_controller.rb
  class StaffUsersController &lt; ApplicationController
  
    def show
    end
  
  end
  </code></pre>
  
  <p>Of course that prompted this error:</p>
  
  <pre><code>  Missing template staff_users/show, application/show with {:locale=&gt;[:en], :formats=&gt;[:html], :handlers=&gt;[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in:
  </code></pre>
  
  <p>So I added that file too (just a blank file at <code>app/views/staff_users.html.erb</code>)</p>
  
  <p>which then works, but redirects to the wrong page <code>/staff_users/1</code></p>
  
  <p>So I modified the controller again</p>
  
  <pre><code>#controllers/staff_users_controller.rb
  class StaffUsersController &lt; ApplicationController
  
    def show
      redirect_to dashboard_path
    end
  
  end
  </code></pre>
  
  <p>And then everything works.  This seems like an enormously overcomplicated solution though, had to do the same thing for PatientUsers, and I've picked up a lot of spare resource routes that I don't need either.</p>
  
  <p><strong>EDIT 2:</strong></p>
  
  <p>Debugger information as requested by Mandeep.</p>
  
  <pre><code>[7, 16] in /Users/Me/Code/medapp/app/controllers/application_controller.rb
     7    # https://github.com/plataformatec/devise/wiki/How-To%3A-Redirect-to-a-specific-page-on-successful-sign-in-and-sign-out
     8    # redirect successfully signed in users to the dashboard
     9    def after_sign_in_path_for(resource)
     10      debugger
     11      # check for the class of the object to determine what type it is
  =&gt; 12      case resource.class
     13      when PatientUser
     14        privacy_agreement_path  
     15      when StaffUser
     16        dashboard_path
  (rdb:2) resource.show
  *** NoMethodError Exception: undefined method `show' for #&lt;PatientUser:0x0000010171c1c0&gt;
  
  (rdb:2) @resource = resource
  #&lt;PatientUser id: 2, email: "patient_user@example.com", encrypted_password: "$2a$10$qY0jBEC8UZHD883ryq69BevPo5oxV.9LPDM8K44gXqcD...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 16, current_sign_in_at: "2014-07-03 08:46:07", last_sign_in_at: "2014-07-03 08:45:06", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2014-07-03 03:05:01", updated_at: "2014-07-03 08:46:07"&gt;
  </code></pre>
  
  <p>Seems exactly as expected to me, should indicate that resource.class would work fine without any huge workarounds, but obviously not.</p>
  
  <p><strong>Solution</strong></p>
  
  <p>So just using an If conditional instead of a case conditional fixed the whole thing, no need for any of that other stuff.  I have no idea why this is, but it's an adequate solution.</p>
  
  <pre><code>#registrations_controller.rb
  class RegistrationsController &lt; Devise::RegistrationsController
    protected
  
    # BUGFIX
    # https://stackoverflow.com/questions/19451881/devise-after-sign-in-path-for-works-but-not-the-other-ones
    # Creating separate after_sign_up_paths for patient_user and staff_user
    def after_sign_up_path_for(resource)
      # check for the class of the object to determine what type it is
      if resource.class == PatientUser
        privacy_agreement_path
      elsif resource.class == StaffUser
        dashboard_path
      end 
    end
  
  end
  </code></pre>
  
  <p>AND</p>
  
  <pre><code>#application_controllers.rb
  class ApplicationController &lt; ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception
  
    def after_sign_in_path_for(resource)
      # check for the class of the object to determine what type it is
      if resource.class == PatientUser
        privacy_agreement_path
      elsif resource.class == StaffUser
        dashboard_path
      end 
    end
  
  end
  </code></pre>
  
  <p>Thanks to Mandeep and Vapire for their help!!</p>