Actually I am using two models for my app that is User and Admin and I followed every steps to be dealt while using devise gem.
And I would like to have multiple login. When User logged in, must be redirected to respective profile and when Admin logged in, must be redirected to his respective profile.
I don't know where I am making mistake.
home/index.html.erb
<ul>
<li>User:<%= link_to 'User', new_user_session_path, target: "_blank" %></li>
<li>Admin:<%= link_to 'Admin', new_admin_session_path, target: "_blank" %></li>
</ul>
When I go to User link it gives me routing error which is as below;
And When I go to Admin link it gives me routing error which is as below;
routes.rb
Rails.application.routes.draw do
root "home#index"
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
devise_scope :user do
authenticated do
root to: 'aslani#index', as: 'authenticated_user_root'
end
unauthenticated do
root to: 'aslani#index', as: 'unauthenticated_user_root'
end
end
devise_for :admins, controllers: {
sessions: 'admins/sessions',
registrations: 'admins/registrations'
}
devise_scope :admin do
authenticated do
root to: 'yaseen#index', as: 'authenticated_admin_root'
end
unauthenticated do
root to: 'yaseen#index', as: 'unauthenticated_admin_root'
end
end
end
aslani/index.html.erb
<% if user_signed_in? %>
I am Aslani.
<%= link_to 'Log out', destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to 'Log In', new_user_session_path %>
<%= link_to 'Sign Up', new_user_registration_path %>
<% end %>
kola/index.html.erb
<% if admin_signed_in? %>
I am Kola.
<%= link_to 'Log out', destroy_admin_session_path, method: :delete %>
<% else %>
<%= link_to 'Log In', new_admin_session_path %></li>
<%= link_to 'Sign Up', new_admin_registration_path %>
<% end %>
app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
def new
super
end
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
if !session[:return_to].blank?
redirect_to session[:return_to]
session[:return_to] = nil
else
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
end
Any suggestions are most welcome.
Thank you in advance.
As you are taking the sessions contoller under the users name space,
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
Your sessions_controller path should be,
app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
def new
super
end
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
if !session[:return_to].blank?
redirect_to session[:return_to]
session[:return_to] = nil
else
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
end
The form you have taken in the que should be in app/views/users/sessions/new.html.erb
<h2>Log in</h2>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<% if devise_mapping.rememberable? -%>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end -%>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
<%= render "users/shared/links" %>
If you change your controller in routes as
devise_for :users, controllers: {
registrations: 'registrations',
sessions: 'sessions',
passwords: 'passwords',
confirmations: 'confirmations',
}
and keep your controller as app/controllers/session_controller.rb will do the job.
Related
I am trying to use bcrpyt with has_secure_password but instead of password_digest attribute, I am using key_digest. as the documentation of has_secure_password allows us to use any XXX_digest.( https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password )
I am using it as a key will be generated from the system and the user doesn't need to enter any password. but for this example, I am just trying the key to be encrpted and saved in the database. If I change key to password, it works. I am not sure why key is not working
I am getting NoMethodError in Users#new and undefined method "key" for #<User:0x000000000d0beb38>
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
private
## Strong Parameters
def user_params
params.require(:user).permit(:email, :key_digest, :key)
end
end
users/new.html.erb
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :key %><br />
<%= f.number_field :key %>
</div>
<div class="actions"><%= f.submit "Sign Up" %></div>
<% end %>
20210605052011_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :email
t.string :key_digest
t.timestamps
end
end
end
routes.rb
Rails.application.routes.draw do
root to: 'static#home'
get 'static/home'
get 'static/faq'
get 'signup', to: 'users#new', as: 'signup'
get 'login', to: 'sessions#new', as: 'login'
get 'logout', to: 'sessions#destroy', as: 'logout'
resources :users
resources :sessions
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
In your User model, haven't field key, so error show. Change your code as below to save with hash password!
Edit your app/views/users/new.html.erb to:
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.number_field :password %>
</div>
<div class="actions"><%= f.submit "Sign Up" %></div>
<% end %>
With app/controllers/users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
private
## Strong Parameters
def user_params
params.require(:user).permit(:email, :password)
end
end
Last, don't forget install bcrypt gem and set this content app/models/user.rb to:
class User < ApplicationRecord
has_secure_password
alias_attribute :password_digest, :key_digest
end
Any things will fine! : D
When a user has a failed registration attempt, my app is redirecting them to the root url instead of rendering the registration page.
Routes:
resources :users
devise_for :users, path: '', path_names: { sign_up: 'register', sign_in: 'login', sign_out: 'logout'}, :controllers => { :omniauth_callbacks => "callbacks" }
registrations#new:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), :html => {:class => "col s12 form-text", autocomplete: "off"}) do |f| %>
<%# render 'devise/errors', resource: resource %>
<div class="row">
<div class="input-field col s12">
<%= f.text_field :full_name, :class => "validate", :required => true, :placeholder => "Robin Smith" %>
<%# f.label :full_name, :class => "allcaps active", :data => {:error => 'Name Required'} %>
<%= f.label :full_name, :class => "allcaps" %>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<%= f.email_field :email, :class => "validate", :required => true, placeholder: " " %>
<%= f.label :email, :class => "allcaps" %>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<%= f.password_field :password, :class => "validate", :required => true, placeholder: " " %>
<%= f.label :password, "password (min 6 chars)", :class => "allcaps" %>
</div>
</div>
<div class="button-container">
<button class="waves-effect btn-flat btn-large"type="submit" name="action">Join Now</button>
</div>
<% end %>
I can go to mydomain.com/register and the form appears. But when there's a failed registration attempt, they're redirected to the root url instead of re-rendering the register page with errors.
I feel like there must be something wrong in my routes. Any idea?
You can override the Devise create action and redirect to your url:
# registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message! :notice, :signed_up
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
resource.errors.full_messages.each {|x| flash[x] = x}
#redirect_to your_path
end
end
end
And in routes:
resources :users
devise_for :users, path: '', path_names: { sign_up: 'register', sign_in: 'login', sign_out: 'logout'}, :controllers => { :registrations => "registrations", :omniauth_callbacks => "callbacks" }
I tweaked little from Shannon's answer as following because I did not want to remove super from the code and only intervene when register fails.
def create
super do |user|
unless user.persisted?
clean_up_passwords user
set_minimum_password_length
redirect_back(notice:user.errors.full_messages.to_sentence)
return
end
end
end
So, I got an interesting problem. I am attempting to override and redirect a devise sign_up and sign_in request to their appropriate profile pages but I am running into an error. The profiles and URL work by accessing it with console, link_to or by typing it out. For some reason the devise method won't route to it. I'm not sure why. Anyways, upvotes for all that contribute or solve. Thanks!
Routes:
root :to => 'pages#index'
get "pages/index"
devise_for :users, :path => 'accounts', :controllers => { :registrations => "registrations" }
get 'users/:id/profile' => 'profiles#show', :as => 'current_profile'
get 'users/:id/profile/edit' => 'profiles#edit', :as => 'edit_current_profile'
put 'users/:id/profile' => 'profiles#update'
resources :users do
resources :profiles
end
Registration Controller:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
edit_current_profile_path(resource)
end
def after_sign_in_path_for(resource)
current_profile_path(resource)
end
end
Profile Controller
def show
#user = current_user
#profile = #user.profile
respond_to do |format|
format.html
end
end
Error message:
Routing Error
No route matches {:controller=>"profiles", :action=>"show"}
Try running rake routes for more information on available routes.
View: edit.html.erb
<%= form_for([#profile.user, #profile]) do |f| %>
<% if #profile.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#profile.errors.count, "error") %> prohibited this profile from being saved:</h2>
<ul>
<% #profile.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :real_name %><br />
<%= f.text_field :real_name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You should give an attribute to your route helpers. Try it out
def after_sign_up_path_for(resource)
edit_current_profile_path(resource)
end
def after_sign_in_path_for(resource)
current_profile_path(resource)
end
Sorry for the novice question but I am trying to get to grips with RoR. I have a very basic sign up and login process in place but am having some difficulty getting the routing correct. I am also unsure whether I am actually being logged out successfully when I push my logout button because it isn't then displaying the login button as it should.
My setup is as follows on Rails 3.1:
Sessions Controller
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password!"
render "signup"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged Out!"
end
end
User Controller
class UserController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new (params[:user])
if #user.save
redirect_to root_url, :notice => "Signed Up!"
else
render "user/new"
end
end
end
User Model
class User < ActiveRecord::Base
has_secure_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email, :on => :create
end
Sessions/new.html.erb
<h1>Log In</h1>
<%= form_tag login_path do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class ="field">
<%= label_tag :password %>
<%= password_field_tag :password %>
</div>
<div class="actions"><%= submit_tag "Log in" %></div>
<%end%>
User/new.html.erb
<% if session[:user_id] %>
<!-- user is logged in -->
<%= link_to logout_path %>
<% else %>
<!-- user is not logged in -->
<%= link_to login_path %>
<% end %>
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class = "field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class = "field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class = "field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit %></div>
<% end %>
Finally my Routes file
MadeByV2::Application.routes.draw do
controller :user do
get "signup" => "user#new"
end
resources :users
controller :sessions do
get "login" => "sessions#new"
post "login" => "sessions#create"
delete "logout" => "sessions#destroy"
end
root :to => "user#new"
end
Sorry for the extensive use of code in this post but I figure it's best to give a well rounded view of everything so people can see where I am going wrong.
Any help you can offer really would be much appreciated because I don't seem to be getting it myself
Thanks,
Tom
Your code looks fine, but your routes look a little strange.
I'd try something like this:
resources :users
resources :sessions
match 'login' => 'sessions#new', :as => :login
match 'logout' => 'sessions#destroy', :as => :logout
Then I think everything you've currently got should work.
I'll just try to outline this as high level as I can. I am trying to access http://localhost:3000/login
The error is:
No route matches {:controller=>"user_sessions"}
And it's erroring on this line in the new.html.erb file below:
<%= form_for(#user_session) do |f| %>
The route in routes.rb is:
match 'login' => 'user_sessions#new', :as => :login
The user_sessions_controller.rb is:
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Successfully logged in."
redirect_to root_path
else
render :action => 'new'
end
end
def destroy
#user_session = UserSession.find
#user_session.destroy
flash[:notice] = "Successfully logged out."
redirect_to root_path
end
end
And the actual view for the login page is as follows (new.html.erb):
<h1>Login</h1>
<%= form_for(#user_session) do |f| %>
<% if #user_session.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user_session.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user_session.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :login %><br />
<%= f.text_field :login %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
Thank you.
Using form_for(#user_session) alone will try to build out the path using a resource you have defined in your routes.rb. Which you currently don't have (I'm assuming, as you didn't mention it. Please correct if I'm wrong.).
A few ways to go..
Add a resource and limit to the ones you need
resources :user_sessions, :only => [:create, :destroy]
These will use the default routing namings, but you can custom that up as you need.
Match out the routes you need.
match 'login' => 'user_sessions#create', :as => :post_login, :via => :post
View
= form_for(#user_session), :url => post_login_path do |f|
...