2014-07-03 131 views
5

我在以前只有一個Devise模型的應用程序中有Devise的一些基本經驗。然而,我從頭開始重寫它以擴展功能,在第二次迭代中,我認爲最好有兩個單獨的用戶模型(patient_user和staff_user)。設置爲多個模型設計after_sign_in_path_for

我知道CanCan和Rolify,並將使用這些模型之一,但不是其他。

我的問題是設置after_sign_in_path_for並將每個模型重定向到不同的「主屏幕」。

我已經將每個模型設置爲單獨的after_sign_up_path,並且工作得很好。

class RegistrationsController < 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 

顯然after_sign_in_path_for應該在應用程序控制器中定義,而不是在Sessions控制器中定義。

Stack Overflow question clarifying this difference

這是我最好的嘗試:

class ApplicationController < 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 

這給錯誤:

undefined local variable or method `patient_user' for #<Devise::SessionsController:0x00000109a40e48> 

如果我把握的情況下選擇條件,那麼它似乎認識到變量,但我得到一個完全不同的錯誤:

class ApplicationController < 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 

這給錯誤

Circular dependency detected while autoloading constant Patient_user (RuntimeError) 
    ./app/controllers/application_controller.rb:11:in `after_sign_in_path_for' 

我已經嘗試了很多的谷歌搜索的,看着其他各種設計和循環依賴問題,但一直沒能找到周圍工作,我想我不夠好,在設計知道我在做什麼。

我試圖在做patient_user和staff_user分離after_sign_in_path_for在應用控制器調用的一件事

#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 

這適用於staff_user,但要去/ patient_users/sign_in,並給予有效的用戶名和密碼,而不是重定向到dashboard_path(不是privacy_agreement_path)。

這個問題似乎集中在使用「資源」和旨在將「patient_user」帳戶重定向到「privacy_agreement_path」和「staff_user」帳戶到「dashboard_path」的條件語句。這適用於RegistrationsController中的after_sign_up_path,但不適用於ApplicationController中的after_sign_in_path。

其他文件

#routes.rb 
devise_for :patient_users, :controllers => { :registrations => :registrations } 
devise_for :staff_users, :controllers => { :registrations => :registrations } 

-

#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 

任何幫助將非常感激。

編輯:

我試圖Vapire的解決方案:

class ApplicationController < 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 

但這種啓動錯誤:

 undefined method `staff_user_url' for #<Devise::SessionsController:0x000001047a0198> (NoMethodError) 

帶着幾分谷歌搜索,我發現this discussion on Devise github的,總對於這是一種錯誤還是僅僅是一個糟糕的實現缺乏共識。

我跟着建議的解決方案,雖然,這是更新的routes.rb

#routes.rb 
    devise_for :patient_users, :controllers => { :registrations => :registrations } 
    devise_for :staff_users, :controllers => { :registrations => :registrations } 

    resources :patient_users #added as bugfix 
    resources :staff_users #added as bug fix 

這給了新的錯誤:

 uninitialized constant StaffUsersController (ActionController::RoutingError) 

所以我創建了一個新的控制器文件:

#controllers/staff_users_controller.rb 
class StaffUsersController < ApplicationController 

end 

哪給了錯誤

The action 'show' could not be found for StaffUsersController (AbstractController::ActionNotFound) 

所以我補充說,控制文件

#controllers/staff_users_controller.rb 
class StaffUsersController < ApplicationController 

    def show 
    end 

end 

當然,這提示這個錯誤:

Missing template staff_users/show, application/show with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in: 

所以我補充說,文件太(只是在app/views/staff_users.html.erb一個空白文件)

然後工作,但重定向到錯誤的頁面/staff_users/1

所以我再次修改控制器

#controllers/staff_users_controller.rb 
class StaffUsersController < ApplicationController 

    def show 
    redirect_to dashboard_path 
    end 

end 

然後一切正常。這似乎是一個極其複雜的解決方案,但必須爲PatientUsers做同樣的事情,並且我已經選擇了許多我不需要的備用資源路由。

編輯2:如通過Mandeep請求

調試信息。

[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 
=> 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 #<PatientUser:0x0000010171c1c0> 

(rdb:2) @resource = resource 
#<PatientUser id: 2, email: "[email protected]", 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"> 

似乎完全按照預期對我來說,應該指出resource.class會工作得很好,沒有任何變通巨大,但顯然不是。

解決方案

因此,只要使用If條件,而不是有條件的情況下,固定了整個事情,不需要任何其他的東西。我不知道這是爲什麼,但它是一個適當的解決方案。

#registrations_controller.rb 
class RegistrationsController < 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 

#application_controllers.rb 
class ApplicationController < 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 

感謝Mandeep和Vapi​​re他們的幫助!

+0

你可以使用一個調試器,並檢查你的after_sign_in_path_for方法獲得什麼資源值? – Mandeep

+0

我該怎麼做?對Rails來說,我還是有點新鮮,並且只是圍繞我的應用程序構建一個完整的Cucumber/RSpec測試套件。我認爲找出資源中的資源是個好主意,但不知道如何去做,我想可能是使用rails控制檯的東西,但是我也沒有太多的經驗。 – user2792268

+0

檢查http://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debugger-gem – Mandeep

回答

6

鑑於您的兩個用戶模型稱爲PatientUserStaffUser你應該這樣做:

class ApplicationController < 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 

要在其他嘗試詳細一點:

設計在用戶簽訂了你那之後只需向您的應用程序控制器撥打after_sign_in_path(resource)即可。無論是調用Devise自己提供的標準實現還是自定義實現對於Devise來說都不是問題。如果你自己實施它,它會給你完全的做什麼的區別。因此,如果你想對它作出反應,你有責任檢查什麼類型將作爲資源參數。

# application_controller.rb 

# you implement the method once 
def after_sign_in_path_for(patient_user) 
    privacy_agreement_path 
end 

# you implement it twice which means 
# this overrides the above method, so a call to after_sign_in_path will always 
# result in calling this method, no matter what type of user it is 
def after_sign_in_path_for(staff_user) 
    dashboard_path 
end 

僅僅因爲你以某種方式給你的參數命名,它並沒有給出它是什麼類型參數的信息。這不是面向對象編程的設計。

+0

Doh,我應該知道重命名資源參數什麼都不做。您的解決方案種類繁多。查看帖子中的更新。 – user2792268

+0

已修復,請參閱OP中的編輯/評論。感謝您的幫助@Vapire。 – user2792268