Rails 3 devise_for and STI - ruby-on-rails-3

I have the following models:
User
Athlete < User
Coach < User
In my routes, I have the following:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks", :registrations => "registrations" }
I am wanting to create a route for each sign_up path (ie: /sign_up/coach, /sign_up/athlete)... should I create two different registration controllers or is there a way to do this with just one registration controller?

You can use a single registration controller doing something like this:
class UsersController < Devise::RegistrationsController
private
def resource_class
params[:type].present? ? params[:type].classify.constantize : super
end
end
There are some other tweeks you would need to do. Whether that is worth it or not, I guess it depends on how similar the models actually are.
Hope it helps!

It would be good if you create separate controllers for coach and athlete:
devise_for :coach, :controllers => { :registrations => "coach_registrations" }
devise_for :athlete, :controllers => { :registrations => "athlete_registrations" }
class CoachRegistrationsController < Devise::RegistrationsController
end
class AthleteRegistrationsController < Devise::RegistrationsController
end
Thanks.

Related

How to use both Omniauth_callbacks controller and custom devise registrations controller

I have a custom registrations controller for devise set up, which is this:
devise_for :users, controllers: {registrations: "registrations"}
and in the controller:
class RegistrationsController < Devise::RegistrationsController
protected
def after_update_path_for(resource)
user_path(resource)
end
end
It works great.
However I also have omniauth authentication, which again works great...by itself:
devise_for :users, controllers: {omniauth_callbacks: "omniauth_callbacks"}
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to sign_up_path
end
end
alias_method :linkedin, :all
alias_method :twitter, :all
end
However as you can probably already see my problem - I'm not sure how to get them to work together, as they both start with 'devise_for :users' and so whichever way round I place them in the routes file, one won't work.
How can I get them both working at the same time, so that the registrations controller only overrides the 'edit' and 'update' actions, while the omniauth_callbacks controller handles authentication?
Thanks
In routes.rb, you can put comma seperated paths for devise_for like this -
devise_for :users, controllers: {registrations: "registrations", omniauth_callbacks: "omniauth_callbacks"}
This will work.

Devise redirect after signin with username in url

I'm struggling with getting devise to redirect to a user's profile page after signin. My routes file looks like this:
get "profiles/index"
get "users/index"
get "users/show"
authenticated :user do
root :to => 'home#index'
end
root :to => "home#index"
devise_for :users
resources :users
scope ":username", :as => "user" do
match '/', :to => 'profiles#index'
end
I would like it to redirect to /myusername which the user's profile page. Thanks for your guys' help.
If you want to change the sign in redirect you can override the after_sign_in_path_for method by adding a new SessionsController as so:
class SessionsController < Devise::SessionsController
#after_sign_in_path_for is called by devise
def after_sign_in_path_for(user)
"/users/#{user.username}" #adjust the returned path as needed
end
end
As Ashikata mentioned you need to change the devise routing to the following if you're changing the session controller.
devise_for :users, :controllers => { :sessions => 'sessions' }
Alternatively, adding that modified after_sign_in_path_for method to your application controller should do the trick.

rails 3.1, why I can't override 'create' at Devise::SessionsController?

I have devise 1.4.8, and I am trying to override create at Devise::SessionsController to execute some logic once user signed in.
Here is my class: (stored at \app\controllers\students\sessions_controller.rb)
class Students::SessionsController < Devise::SessionsController
def create
super
end
end
here is my routes.rb
devise_for :students, :controllers => { :sessions => "students/sessions" }
but, overridden create is never called! instead, only the create at super class is called
any idea ?
it was a problem in the routes.rb,
I have this line, and it worked:
devise_for :students do post '/students/sign_in' => 'students/sessions#create' end
but, I've deleted all lines for :students at routes.rb

RSpec tests with devise: could not find valid mapping

I'm trying to run controller specs with devise 1.3.4. (and factory girl)
I followed the instructions in the git wiki for the project. I am able to log in as a user using the login_user method created in the macro, but login_admin fails with the following error:
...
sign_in Factory.create(:admin)
Could not find a valid mapping for #<User id: 2023, email: "admin1#gmail.com", .... >
Factory:
Factory.define :user do |f|
f.sequence(:username) {|n| "user#{n}"}
f.sequence(:email) {|n| "user#{n}#gmail.com"}
f.email_confirmation {|fac| fac.email }
f.password "a12345Den123"
f.password_confirmation "a12345Den123"
# f.admin 0
end
Factory.define :admin, :class => User do |f|
f.sequence(:username) {|n| "admin#{n}"}
f.sequence(:email) {|n| "admin#{n}#gmail.com"}
f.email_confirmation {|fac| fac.email }
f.password "a12345Den123"
f.password_confirmation "a12345Den123"
f.admin 1
end
Controller macros module:
module ControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user] #it should map to user because admin is not a model of its own. It produces the same result either way.
#admin = Factory.create(:admin)
sign_in #admin
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
sign_in #user
end
end
end
routes
devise_for :users
devise_for :admins, :class_name => 'User'
One solution is to set cache_classes = false, however that isn't ideal as I use spork and don't want to have to restart it after changing a model.
Any help?
I have something like this in my routes:
devise_for :accounts, :controllers => {:confirmations => "confirmations"} do
put "confirm_account", :to => "confirmations#confirm_account"
get "login" => "devise/sessions#new", :as => :login
delete "logout" => "devise/sessions#destroy", :as => :logout
get "register" => "devise/registrations#new", :as => :register
end
so in my spec/support/controller_macros.rb I needed to change from:
def login_account
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:account]
#account = Factory.create(:account)
sign_in(#account)
end
end
to
def login_account
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:account]
#account = Factory.create(:account)
sign_in(:account, #account)
end
end
note the sign_in(scope, resource)
I hope this helps.
This is from the devise readme:
Devise also ships with default routes.
If you need to customize them, you
should probably be able to do it
through the devise_for method. It
accepts several options like
:class_name, :path_prefix and so on,
including the possibility to change
path names for I18n
So I would check your routes file and make sure this is in there:
devise_for :admins, :class_name => 'User'
You may want to check your code for multiple devise_for :admins declarations in different places. This has been the cause of such an exception in my case, as it surely confuses Devise.

declarative_authorization: control access to nested resource that doesn't have an explicit model

I have a model that allows a User to mark other Users as Favorites.
This HABTM relationship is defined in the User model:
class User < ActiveRecord::Base
has_and_belongs_to_many :favorites, :class_name => "User", :join_table => "favorites", :association_foreign_key => "favorite_id", :foreign_key => "user_id"
end
The FavoritesController only requires three actions (index, create,
destroy) to manage a User's Favorites.
Rule: Only an authenticated user (current_user) is allowed to manage
their Favorites.
Initially, I tried to represent this rule in the authorization_rule.rb
file:
# allow authenticated user to update profile
has_permission_on :users, :to => :change do
if_attribute :id => is { user.id }
has_permission_on :favorites, :to => [:index,:create,:destroy]
end
This didn't work, probably because the Favorite doesn't have an
explicit model (i.e. favorite.rb). Though I could be wrong about
this.
It seems like the correct approach would be to represent the rule in
the FavoritesController:
filter_access_to :all, :nested_in => :users
...
But I'm not certain how to represent the rule properly here.
Assistance is really appreciated.
** edit **
It was suggested that I use a context to control access in this situation:
setting permissions for a no-model controller .
I tried modifying the FavoritesController:
filter_access_to :all, :context => :favorites
This change had no effect.
** /edit **