Ruby on Rails: Custom Devise Registration Controller, Asking For Create Action - ruby-on-rails

I have a custom registration controller, but I don't want to override a create action from devise. When I try to sign up a user, I get this error:
Unknown action
The action 'create' could not be found for Devise::RegistrationsController
Is it asking for it because I have a custom registration controller? If so, does that mean I need to copy all the actions that I'm not overriding from here: https://github.com/plataformatec/devise/blob/master/app/controllers/devise/registrations_controller.rb
Or its because there's something wrong with my application?
My routes:
devise_for :user, :controllers => { :registrations => "devise/registrations" }, :skip => [:sessions] do
get 'signup' => 'devise/registrations#new', :as => :new_user_registration
post 'signup' => 'devise/registrations#create', :as => :user_registration
end
This is my devise registration controller
class Devise::RegistrationsController < DeviseController
skip_before_filter :require_no_authentication
def edit
#user = User.find(current_user.id)
#profile = Profile.new
end
def update
# required for settings form to submit when password is left blank
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
protected
def after_update_path_for(resource)
user_path(resource)
end
def after_sign_up_path_for(resource)
user_path(resource)
end
end
This is the registration form:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
...
<div>
<%= button_tag :type => :submit, :class => "btn btn-large btn-inverse" do %>
Sign up
<% end %>
</div>
...
<% end %>

Your controller of registration inherits from the wrong class: DeviseController
It is a base class for a registration and has no "create" method, and so does your custom Devise::RegistrationsController class (it has only edit and update methods) - it incurs the error.
In order to create your own custom registration controller for users with fallback to the original devise methods, I suggest you do the following:
1. create "users" folder in controllers folder
2. create there registrations_controller.rb file, and define class there:
Users::RegistrationsController < Devise::RegistrationsController
and override any actions ("edit" and "update")
3. inform the "routes.rb" file about changes:
devise_for :users, :controllers => { registrations: 'users/registrations' }

Related

How to create an account when and assign it to a User when signing up via Clearance in rails

What is the best way with Clearance to automatically create a an "Account" with a unique account_id (doesn't have to be UUID) and assign it to the new user on sign up?
I need to extend the clearance User sign up form and User controller to handle this but I'm having trouble getting this to work.
Should this be a before_filter on signup or part of the User create action?
Update: Adding some code for reference...
# app/controllers/users_controller.rb
class UsersController < Clearance::UsersController
def create
#user = user_from_params
#account = account_from_params
#user.account = #account
if #user.save
sign_in #user
redirect_back_or url_after_create
else
render template: "users/new"
end
end
end
class AccountController < ApplicationController
def create
#account = Account.new(account_params)
end
private
def account_params
params[:account].permit(:id)
end
end
<fieldset>
<%= form.label :email %>
<%= form.text_field :email, type: 'email' %>
</fieldset>
<fieldset>
<%= form.label :password %>
<%= form.password_field :password %>
</fieldset>
# Users & Clearance routes
resources :passwords, controller: 'clearance/passwords', only: [:create, :new]
resource :session, controller: 'clearance/sessions', only: [:create]
resources :users, controller: 'clearance/users', only: [:create] do
resource :password,
controller: 'clearance/passwords',
only: [:create, :edit, :update]
end
get '/login' => 'clearance/sessions#new', as: 'sign_in'
delete '/logout' => 'clearance/sessions#destroy', as: 'sign_out'
get '/signup' => 'clearance/users#new', as: 'sign_up'
get '/users' => 'users#index'
I would hook into their controllers to keep things "in the gem" so to speak. This is how it works. Looks like you would hook into the controllers by overriding them like this:
class UsersController < Clearance::UsersController
def create
#user = user_from_params
#account = account_from_params
#user.account = #account
if #user.save
sign_in #user
redirect_back_or url_after_create
else
render template: "users/new"
end
end
end
Don't forget to update the routes and point them to this controller action. Now, you're sort of unhooking your controller create action from Clearance, but you're doing it in a "Clearance way" which seems most appropriate.

Rails undefined method for nil:NilClass

I get this error while clicking in form Log In button:
undefined method `[]' for nil:NilClass
View with signup form is below:
<%= form_for(:session, url: login_path) do |f| %>
<%= f.text_field :login, :placeholder => "login" %>
<%= f.password_field :password, :placeholder => "Password" %>
<%= f.submit "Log in", class: "btn-submit"%>
<% end %>
SessionsController :
class SessionsController < ApplicationController
def new
end
def create
#user = User.find_by_login(params[:login][:password]) ////ERROR LINE
if #user && #user.authenticate(params[:session][:password])
session[:user_id] = #user.id
redirect_to '/'
else
flash.now[:danger] = 'err'
redirect_to '/login'
end
end
def destroy
session[:user_id] = nil
redirect_to '/'
end
end
User controller:
class UsersController < ApplicationController
def new
#users = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
current_user = #user.id
redirect_to #user
else
redirect_to '/login'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :login)
end
end
Routes:
get 'logout' => 'sessions#destroy'
post 'logout' => 'sessions#destroy'
delete 'logout' => 'sessions#destroy'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
resources :users, :user_types
Also, I do not want to create 'signup' view - there should be no possibility to create user through website
Looking at your form, I don't think you have params[:login]. So it'll be nil and params[:login][:password] is evaluated to nil[:password]. That's why you have the error.
What you have, according to the form, should be params[:session][:login] and params[:session][:password]. So the only remaining question is how you implemented your User.find_by_login method.

Rails : methodology log in in front and admin section

I am trying to build an admin section in my rails application.
I am quite to new rails and I have some difficulties about the methodology.
So the routes
Rails.application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users, except: [:index]
# namespace :admin do
# root 'base#home'
# get 'login' => 'sessions#home'
# post 'login' => 'admin/sessions#create'
# get 'dashboard' => 'base#dashboard'
# resources :users
# end
namespace :admin do
root 'base#home'
end
end
There is a model User avalaible in the front.
So users can login via a login from
views/sessions/new.html.erb
<div id="login_form">
<%= form_for(:session, url: login_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>
</div>
and edit their profile with the SessionController
app/controllers/sessions_controller
class SessionsController < ApplicationController
def new
end
def create
#user = User.find_by(email: params[:session][:email].downcase)
if #user && #user.authenticate(params[:session][:password])
log_in #user
params[:session][:remember_me] == '1' ? remember(#user) : forget(#user)
redirect_to #user
remember #user
else
flash.now[:danger] = 'Invalid email/password combination' # Not quite right!
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
And a base controller app/controllers/admin/base_controller.rb
class Admin::BaseController < ApplicationController
layout 'admin'
before_action :logged_in_user
def home
end
private
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
(redirect_to login_url)
end
end
end
That's where I am confused because in case of a non-admin user, he's redirected to his profile page (it works fine) but the admin user should be redirected to the admin home page.
I tried to have two different login forms but I had an infinite loop redirection because I was redirecting to the login page in case of non logged in but
before_action :require_login
was called for every action event the login page
What would be the right methodology ?
Thanks :)
As far as I understand your admin users log in the same way a regular user would: via SessionsController#create. It also seems like you have a second controller SessionsController in the Admin namespace, which is not used?
That's where I am confused because in case of a non-admin user, he's redirected to his profile page (it works fine) but the admin user should be redirected to the admin home page.
You currently don't differentiate between the "normal" user and an admin, therefore both are redirect_to #user. (the users profile page).
What would be the right methodology?
You could add field to your user model (boolean admin) which you could use to differentiate in your SessionsController.
if #user && #user.authenticate(params[:session][:password])
log_in #user
params[:session][:remember_me] == '1' ? remember(#user) : forget(#user)
if #user.admin?
redirect_to 'admin_home_page'
else
redirect_to #user
end
remember #user
That way you don't need a second SessionsController. Furthermore
you could write an admin "filter" in your controllers which would only allow access to certain controller actions if the #user is an admin.
class Controller
before_filter :admin_only
private
def admin_only
redirect_to :back, unless #user.admin?
end
end
Hope this helps.

Param in URL not passed to a form - Rails 4

When accessing URL localhost:3000/buyers/sign_up?email=abc%40abc.com , the sign-up form is displayed but email field stays empty and is not auto-filled with abc#abc.com.
Could this be related to the implemented Single Table Inheritance (STI) ? -> Buyer inherits from User.
routes.rb
devise_for :users, :controller => {:sessions => 'sessions'}, :skip => :registrations do
delete '/logout', :to => 'sessions#destroy', :as => :destroy_user_session
get '/login', :to => 'sessions#new', :as => :new_user_session
post '/login', :to => 'sessions#create', :as => :new_user_session
end
devise_for :sellers, :skip => :sessions
devise_for :buyers, :skip => :sessions
resources :users
devise/registrations/new.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:first_name, :last_name, :name, :type, :email)
end
end
Just found the answer.
Simply need to add value in the form + a condition to check presence of the e-mail param:
<%= f.email_field :email, value: params[:email] %>
Params
The problem isn't to do with STI - it's to do with how you're passing variables / parameters to your form.
You must remember that when you use form_for, Rails will automatically populate the provided HTML elements with data from the ActiveRecord object - in that if your object has the attribute email, the form will have that data populated
If you want to populate the form with new data (from a URL), you'll either have to be able to populate the #user.email attribute (as suggested by JTG), or take the params from the URL & pass them through to your form
--
The ways I would look at doing this would include:
Controller-based (the best)
Front-end "params" based
The controller method can be accessed as follows:
#app/controllers/users_controller.rb
Class UsersController < ApplicationController
def new
#user = User.new(email: params[:email])
end
end
This should work, as the param should be made available to the controller during the request.
-
The alternative, as you've discovered, is to populate the value of the element on your page:
<%= f.text_field :email, value: params[:email] %>
This will leave the :email field blank if no email parameter was / is defined.
Making a request to new#User (which is what you are requesting with /buyers/sign_up) won't autofill any of the sign_up form by default
However, you can put
def new
#user = User.new
#user.email = params[:email] if params.has_key? "email"
end
for the url
/buyers/sign_up?email=abc%40abc.com
And it should auto complete the email textfield of your form.
if your url was structured like so...
/buyers/sign_up?user[email]=abc%40abc.com
Then you should be able to use the much more elegant
def new
#user = User.new(params[:user])
end
in your new call. That way you can send in more than one parameter

rails: devise update user without password

I've followed the directions in devise's wiki regarding overriding update method to update user information without adding password, but I'm still getting notice saying that I can't leave password blank. I'm stuck and I need some assistance on tracking the problem down.
<%= form_for(resource, :as => resource_name, :url => custom_update_user_registration_path, :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.email_field :email, :autofocus => true %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<%= f.submit "Update" %>
in my routes :
devise_for :user, :controllers => { :registrations => "registrations" }, :skip => [:registrations, :sessions] do
get 'signup' => 'devise/registrations#new', :as => :new_user_registration
post 'signup' => 'devise/registrations#create', :as => :custom_user_registration
get 'users/cancel' => 'devise/registrations#cancel', :as => :cancel_user_registration
get 'account/edit' => 'devise/registrations#edit', :as => :custom_edit_user_registration
put 'account' => 'devise/registrations#update', :as => :custom_update_user_registration
delete 'users/cancel' => 'devise/registrations#destroy'
# devise/sessions
get 'signin' => 'devise/sessions#new', :as => :new_user_session
post 'signin' => 'devise/sessions#create', :as => :user_session
delete 'signout' => 'devise/sessions#destroy', :as => :destroy_user_session
end
Here's my controller:
class RegistrationsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
def update
#user.attributes = params[:user]
# required for settings form to submit when password is left blank
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
end
I'm not sure why I'm getting the error that I need to submit password still? Can someone see something that I'm overlooking?
Thanks
You could handle that in your model normally.
Just edit your valdiations. This way you normally do not need to override the whole controller..just edit the model:
validates :password, :presence => true, :on => :create
I just found out that I've been overriding my devise registration incorrectly.
This was the correct way of having registration controller
Rails 3 /devise seems to ignore custom controller
Try to change
if #user.update_attributes(params[:user])
to device method
if #user.update_without_password(params[:user])
In class RegistrationsController < Devise::RegistrationsController you are missing update parameters for devise.
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, ...) }
end
def update
# Get all params for :account_update
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# Allow user to update without using password.
if account_update_params[:password].blank?
account_update_params.delete("password")
account_update_params.delete("password_confirmation")
end
# Set current_user
#user = User.find(current_user.id)
if #user.update_attributes(account_update_params)
set_flash_message :notice, :updated
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end

Resources