Started to learn and develop rails - while it is pretty straight forward I got stuck changing the implementation of the /users/edit view and endpoint in the default configuration of devise.
My approach is pretty simple, all routes and logic for authentication, password resetting etc. get handled by devise and it's routes. My problem is that I want users to change their passwords and email via my custom /settings route and view. For me /users/edit just doesn't make sense and I want to remove it from the accessible routes and ideally use its logic for my custom route.
routes.rb
Rails.application.routes.draw do
devise_for :users
# Defines the root path route ("/")
root "home#index"
# SETTINGS
get "settings/profile" => "settings#profile"
get "settings/account" => "settings#account"
get "settings/billing" => "settings#billing"
get "settings/notifications" => "settings#notifications"
end
user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable,
:trackable, :omniauthable
def remember_me
true
end
end
settings_controller.rb
class SettingsController < ApplicationController
before_action :authenticate_user!
def profile
end
def account
end
def billing
end
def notifications
end
end
Related
Im currently learning ROR and have bumped into a small issue regarding different root pages depending on the user type. In my user model i have an attribute for admin which is either true or false depending on the user.
Please see my routes file below, currently all users aways go to 'houses#index', even if they have admin set as false.
I cannot see where i am going wrong in my routes file.
Is there anyway to add a condition for attribute admin on authenicated user in my routes file?
Routes.rb
Rails.application.routes.draw do
devise_for :user
get 'welcome/index'
resources :houses
resources :tenants
resources :requests
authenticated :user do
root 'houses#index', as: "authenticated_root"
end
authenticated :user, {admin:'false'} do
root 'requests#index', as: "authenticated_root1"
end
root 'welcome#index'
end
model\User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :houses
has_many :tenants
has_many :requests
def admin?
admin
end
end
Thanks.
You should use after_sign_in_path_for devise's method in your application_controller.
https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in
For instance:
def after_sign_in_path_for(resource)
stored_location_for(resource) || (resource.admin? ? admin_dashboard_url : user_dashboard_url(resource))
end
(Please note that resource is like an alias for the user instance. By default it's the first devise role declared in your routes as said here: https://stackoverflow.com/a/40825885/10347572)
For all other requests, if you're not using Cancan, create a new method (pretty similar to the one above) to redirect the user according to his type (but if a user wants to access to an admin page, you should raise a 404 and not just a root redirection)
I'm having some troubles to set up Devise correctly with Engines.
In my engine I have:
config/initializers/devise.rb
config.router_name = :portal_core
config/routes.rb
devise_for :users, class_name: 'PortalCore::User', module: :devise
app/models/portal_core/user.rb
module PortalCore
class User < ApplicationRecord
...
devise :database_authenticatable, :trackable, :confirmable,
:recoverable, :rememberable, :validatable, :lockable
end
end
All my tests pass here.
Then, in one of my host apps I have:
config/routes.rb
devise_for :admin_users, class_name: 'AdminUser'
app/models/admin_user.rb
class User < ApplicationRecord
...
devise :database_authenticatable, :trackable,
:recoverable, :rememberable, :validatable
end
When I ran this test in the host app:
context "When not logged in" do
it "redirect to new user session when try to access index" do
process :index, method: :get, params:{}
expect(response).to redirect_to(new_admin_user_session_path)
end
end
I got this error: NoMethodError:
undefined method `portal_core' for # Devise::FailureApp:0x0000000008c9f1f0>
If I mount the engine routes in the host app:
mount PortalCore::Engine => '/portal_core'
Then I got this error: Expected response to be a redirect to http://test.host/admin_users/sign_in but was a redirect to http://test.host/.
And if I try to start the server, it keeps redirecting with 401 Unauthorized
Probably some configuration is missing, but I can't figure out what.
So, I don't know if this is the solution or just a workaround.
In the host app devise initializer I set the router_name to nil:
config.router_name = nil
And it worked.
Any other solution is welcome!
Thanks!
I am using Device for my authentication. Using rails 4.1. I have it setup as follows:
The root of my project is defined as root 'projects#index' When first hitting my server, it goes to the url /users/sign_in. When I type in a username and password and click sign in, redirects me to the same sign in page with a bubble that says successfully logged in at the top. When I click sign in again, it goes to the routes error:
No route matches [PATCH] "/users/sign_in"
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :projects do
member do
post 'base_images'
get 'base_images'
end
end
root 'projects#index'
end
ApplicationController.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
before_action :authenticate_user!
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
If you then change the url manually to /projects it works as expected.
How come after sign in, it won't redirect to the root of my rails application? Any help would be much appreciated.
You can specify a path to be redirected to after a successful login, just add this to your Application Controller:
def after_sign_in_path_for(resource)
your_custom_path
end
I am trying to override my devise registration controller, with no luck.
I finally got the routes working, but now I'm getting an superclass mismatch for class error.
Heres my setup:
Registration Controller (app/controllers/users/registrations_controller.rb)
class RegistrationsController < Devise::RegistrationsController
def sign_up_params
devise_parameter_sanitizer.sanitize(:sign_up)
params.require(:user).permit(:email, :password, profile_attributes: [:username])
end
def new
super
end
def create
end
def update
super
end
end
Routes
root 'welcome#index'
devise_for :users, :controllers => {:registrations => "users/registrations"}
Views
--edit.html.erb && new.html.erb exist in the folder (app/views/users/registrations)
User Model (just in case)
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
accepts_nested_attributes_for :profile
def profile
super || build_profile
end
end
Any idea why this error is appearing?
Thanks!
Your controller is underneath the users directory but does not have a Users module (it is not in the Users namespace, you might say). Either change the controller to this:
module Users
class RegistrationsController < Devise::RegistrationsController
...
end
end
Or move your controller up a directory
app/controllers/registrations_controller.rb
Define your RegistrationsController as below
class Users::RegistrationsController < Devise::RegistrationsController
...
end
By defining the controller as suggested above, you don't need to define the module explicitly.
You get the error because you have placed the RegistrationsController inside users folder.
So, rails expects that RegistrationsController is a class belonging to Users module.
My sessions_controller is as follows:
class SessionsController < ApplicationController
require 'omniauth-facebook'
require 'omniauth'
def create
#user = User.find_or_create_from_auth_hash(auth_hash)
self.current_user = #user
redirect_to '/'
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
So... it's THERE isn't it?
Here's my users.rb file:
class User < ActiveRecord::Base
acts_as_voter
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
has_many :posts
has_many :tasks
end
And my routes file:
LiquidAdmin::Application.routes.draw do
devise_for :users
get '/auth/:provider/callback', to: 'sessions#create'
resource :sessions, :only => :create
get "home/bandwall"
get "home/index"
root :to => "home#index"
So where's the problem? "auth_hash" is clearly defined... The SessionsController is loading... so why is it complaining about no method for find_or_create_from_auth_hash?
In Rails 3.2, the method you'll want to use is called first_or_create:
User.where(auth_hash: auth_hash).first_or_create
According to the Rails 3.2 release notes:
Add first_or_create, first_or_create!, first_or_initialize methods to
Active Record. This is a better approach over the old
find_or_create_by dynamic methods because it's clearer which arguments
are used to find the record and which are used to create it.
Given this, the following is made possible through the query:
User.where(auth_hash: auth_hash).first_or_create(foo: 'bar') # Assuming no other User entries exist
#=> #<User id: 1, auth_hash: "your_auth_hash", foo: "bar">
User.where(auth_hash: auth_hash).first_or_create(foo: 'baz')
#=> #<User id: 1, auth_hash: "your_auth_hash", foo: "bar">
Given that this question is based on OmniAuth, I think a better answer would be:
#user = User.where(auth_hash).first_or_create
or if in Rails version greater than 4.1.14
#user = User.where(uid: auth_hash[:uid], provider: auth_hash[:provider]).first_or_create
auth_hash is not a field in the User model, but rather a Hash of fields that could be on the User model.