Rails - Devise / User registration route / POST - ruby-on-rails

I'm stuck a bit with the following problem: I use Devise in Rails, when I open the sign up form and submit it with new the data for the new user, no new user is created in the sqlite-db. I think it's because the POST-route for /register is not properly set:
rake routes:
root / static#home
new_user_session GET /login(.:format) devise/sessions#new
user_session POST /login(.:format) devise/sessions#create
destroy_user_session DELETE /logout(.:format) devise/sessions#destroy
user_password POST /password(.:format) devise/passwords#create
new_user_password GET /password/new(.:format) devise/passwords#new
edit_user_password GET /password/edit(.:format) devise/passwords#edit
PUT /password(.:format) devise/passwords#update
cancel_user_registration GET /cancel(.:format) devise/registrations#cancel
user_registration POST / devise/registrations#create
new_user_registration GET /register(.:format) devise/registrations#new
edit_user_registration GET /edit(.:format) devise/registrations#edit
PUT / devise/registrations#update
DELETE / devise/registrations#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
How should I update routes.rb, maybe only :path_names with adding POST for register in some way?:
root to: 'static#home'
devise_for :users, :path => '', :path_names => { :sign_in => "login", :sign_out => "logout", :sign_up => "register" }
RegistrationsController in Devise(from the devise-homepage, just the default one):
class Devise::RegistrationsController < DeviseController
prepend_before_filter :require_no_authentication, :only => [ :new, :create, :cancel ]
prepend_before_filter :authenticate_scope!, :only => [:edit, :update, :destroy]
# GET /resource/sign_up
def new
build_resource({})
respond_with self.resource
end
# POST /resource
def create
build_resource(sign_up_params)
if resource.save
yield resource if block_given?
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
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}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
respond_with resource
end
end
# GET /resource/edit
def edit
render :edit
end
# PUT /resource
# We need to use a copy of the resource because we don't want to change
# the current user in place.
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
if update_resource(resource, account_update_params)
yield resource if block_given?
if is_flashing_format?
flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
:update_needs_confirmation : :updated
set_flash_message :notice, flash_key
end
sign_in resource_name, resource, :bypass => true
respond_with resource, :location => after_update_path_for(resource)
else
clean_up_passwords resource
respond_with resource
end
end
# DELETE /resource
def destroy
resource.destroy
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
set_flash_message :notice, :destroyed if is_flashing_format?
yield resource if block_given?
respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
def cancel
expire_data_after_sign_in!
redirect_to new_registration_path(resource_name)
end
protected
def update_needs_confirmation?(resource, previous)
resource.respond_to?(:pending_reconfirmation?) &&
resource.pending_reconfirmation? &&
previous != resource.unconfirmed_email
end
# By default we want to require a password checks on update.
# You can overwrite this method in your own RegistrationsController.
def update_resource(resource, params)
resource.update_with_password(params)
end
# Build a devise resource passing in the session. Useful to move
# temporary session data to the newly created user.
def build_resource(hash=nil)
self.resource = resource_class.new_with_session(hash || {}, session)
end
# Signs in a user on sign up. You can overwrite this method in your own
# RegistrationsController.
def sign_up(resource_name, resource)
sign_in(resource_name, resource)
end
# The path used after sign up. You need to overwrite this method
# in your own RegistrationsController.
def after_sign_up_path_for(resource)
after_sign_in_path_for(resource)
end
# The path used after sign up for inactive accounts. You need to overwrite
# this method in your own RegistrationsController.
def after_inactive_sign_up_path_for(resource)
respond_to?(:root_path) ? root_path : "/"
end
# The default url to be used after updating a resource. You need to overwrite
# this method in your own RegistrationsController.
def after_update_path_for(resource)
signed_in_root_path(resource)
end
# Authenticates the current scope and gets the current resource from the session.
def authenticate_scope!
send(:"authenticate_#{resource_name}!", :force => true)
self.resource = send(:"current_#{resource_name}")
end
def sign_up_params
devise_parameter_sanitizer.sanitize(:sign_up)
end
def account_update_params
devise_parameter_sanitizer.sanitize(:account_update)
end
end
new.erb.html:
<h2>Sign up</h2>
<%= 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.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>

The problem is in the order in which you are defining routes. Move the root method after devise_for and it should work as intended.
devise_for :users, :path => '', :path_names => { :sign_in => "login", :sign_out => "logout", :sign_up => "register" }
root to: 'static#home'
If you see source, the root method internally uses map. Since you are specifying root path before devise routes, post requests to root path are being processed by home action of static controller.

Related

Rails - is it possible to redirect to a custom path with params?

So I am making an authorisation system. A new registration page is set to be '/signup'. If registration is invalid (username has already been taken, password too short), I want to show the errors. When I do it like this:
#user = User.create(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to stories_path, notice: "Thanks for registration!"
else
render 'new'
end
it works, but it renders in '/users' instead of '/signup'. And when instead of
render 'new'
I write
redirect_to '/signup'
it redirects, but errors are not shown. Is it possible to redirect to '/signup' and keep the errors?
user.rb:
class User < ActiveRecord::Base
has_secure_password
validates :username, presence: true, :uniqueness => { :case_sensitive => false }
validates :password, presence: true, :length => {minimum: 6}
end
users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to stories_path, notice: "Thanks for registration!"
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:username, :password)
end
end
new.html.erb:
<h3>New registration</h3>
<%= simple_form_for #user do |f| %>
<div> <%= f.input :username %>
<%= f.error :username %></div>
<div><%= f.input :password %>
<%= f.error :password %></div>
<%= f.button :submit %>
<% end %>
and routes.rb:
Rails.application.routes.draw do
root 'stories#index'
resources :stories
resources :users
get 'signup' => 'users#new'
end
it works, but it renders in '/users' instead of '/signup'
It's normal behavior. The /signup page is the result of new action from UsersController. This page contains form. After submitting this form, data goes to create action from the same controller, but via POST method.
If validation will be failed controller will render :new template, as you remember create action has /users link. So you will see :new template in /users link.
Here is route map for UsersController:
GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
To achieve your requirements you can add route to routes.rb. And change the url in form.
Something like this:
post 'signup` => 'users#create`
And in form:
<%= simple_form_for(#user, url: HERE_IS_SIGNUP_PATH) do |f| %>

POST login route is not working in production

I am new to ruby on rails. I am developing an application which has authentication system.
my problem is I am getting error when logging in to the application in production(Heroku). It is working in development.
Error
I production after i typing url https://akashpinnaka.herokuapp.com/login,
it is redirecting to https://akashpinnaka.herokuapp.comlogin. I am missing the '/' between root_url and 'login' for POST login.
Note: Working in development environment.
My routes are
Rails.application.routes.draw do
get 'welcome/index'
root 'welcome#index'
resources :articles
resources :projects
resources :users
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
end
Sessions Controller
class SessionsController < ApplicationController
def new
end
def create
#user = User.find_by_email(params[:session][:email])
if #user && #user.authenticate(params[:session][:password])
session[:user_id] = #user.id
redirect_to root_path
else
redirect_to 'login'
end
end
def destroy
session[:user_id] = nil
redirect_to '/'
end
end
Sessions#new
<%= form_for(:session, url: login_path) do |f| %>
<%= f.email_field :email, :placeholder => "Email" %>
<%= f.password_field :password, :placeholder => "Password" %>
<%= f.submit "Log in" %>
<% end %>
Usually, when your form can't be saved, you don't redirect. You show the same form, with error explanation.
def create
#user = User.find_by_email(params[:session][:email])
if #user && #user.authenticate(params[:session][:password])
session[:user_id] = #user.id
redirect_to root_path
else
# should've been login_path
# redirect_to 'login'
render 'new' # this is better
end
end
If you are sure that you want to redirect, by all means, go ahead. But supply the correct path :)
You need use redirect_to '/login' or redirect_to login_path instead of redirect_to 'login'
#Sergio Tulentsev's answer is pretty good.
You should fix your routes:
#config/routes.rb
root "welcome#index"
resources :articles, :projects, :users
resources sessions, only: [:new, :create, :destroy], path_names: { new: "login", create: "login", destroy: "logout" }
Rails has two sets of path helpers - _path and _url
_path, as we know, is there to provide relative routes (/path).
_url is there to provide direct routes (http://domain.com/path)
Thus, when you have:
get "/login" (with leading slash) in your routes, it will almost certainly cause problems with your applications' relative link helpers.
As mentioned by #Sergio Tulentsev, your create method, and the destroy method, should be fixed to use the correct path helpers:
def create
#user = User.find_by email: params[:session][:email]
if #user && #user.authenticate(params[:session][:password])
session[:user_id] = #user.id
redirect_to root_path
else
redirect_to login_path
end
end
def destroy
...
redirect_to root_path
end
It's worth taking #Sergio's advice on the render :new command too!

Rails 4 How Overwrite Devise Respond Path Upon Error

I've been struggling for a while with this one. I have a Rails4/Devise 3.1 app with two users in the system:
Graduates
Employers
and one devise User who can be either a Graduate or Employer via a polymorphic :profile association. I have Graduates sign up via /graduate/sign_up path and employers via /employer/sign_up path both of which route to the same /views/devise/registrations/new.html.erb view (since their signup form is pretty much the same - email and password). Everything works fine, except when there is a validation error, RegistrationsController#create.respond_with resource always redirects both user types to /users path and I need to redirect them back to where they originally came from, e.g. /graduate/sign_up or /employer/sign_up respectively. I've tried replacing respond_with with redirect_to, but I end up loosing the resource object with associated error messages. Any idea guys on how this can be done?
These are my models:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :profile, polymorphic: true
end
class Graduate < ActiveRecord::Base
has_one :user, as: :profile
end
class Employer < ActiveRecord::Base
has_one :user, as: :profile
end
Registrations controller:
class RegistrationsController < Devise::RegistrationsController
def create
build_resource sign_up_params
user_type = params[:user][:user_type]
# This gets set to either 'Graduate' or 'Employer'
child_class_name = user_type.downcase.camelize
resource.profile = child_class_name.constantize.new
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
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}" if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords(resource)
respond_with resource
end
end
end
Registration view (same for both, graduates and employers):
<h2>Sign up</h2>
<%= 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.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<%= hidden_field resource_name, :user_type, value: params[:user][:user_type] %>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
These are my routes:
devise_for :users, :controllers => { :registrations => 'registrations' }
devise_scope :user do
match 'graduate/sign_up', to: 'registrations#new', user: { user_type: 'graduate' }, via: [:get]
match 'employer/sign_up', to: 'registrations#new', user: { user_type: 'employer' }, via: [:get]
end
root to: 'home#index'
Output of rake routes;
$ rake routes
Prefix Verb URI Pattern Controller#Action
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) registrations#cancel
user_registration POST /users(.:format) registrations#create
new_user_registration GET /users/sign_up(.:format) registrations#new
edit_user_registration GET /users/edit(.:format) registrations#edit
PATCH /users(.:format) registrations#update
PUT /users(.:format) registrations#update
DELETE /users(.:format) registrations#destroy
graduate_sign_up GET /graduate/sign_up(.:format) registrations#new {:user=>{:user_type=>"graduate"}}
employer_sign_up GET /employer/sign_up(.:format) registrations#new {:user=>{:user_type=>"employer"}}
root GET /
Apparently, the :location parameter is ignored if you have an existing template called, well, in this case, #index. I still don't understand why Devise redirects to #index or #show when the resource has errors on it.
I'll update this answer as I find out more.
Calling respond_with will render the default action of that resource. This I believe is the index action (/users/) and thus the cause for the redirect.
I think what you want to do is render the new action instead. Try the following:
if resource.save
...
else
clean_up_passwords(resource)
render :action => 'new'
end
In case of respond_with, for an html response - if the request method is get, an exception is raised but for other requests such as post the response depends on whether the resource has any validation errors (i.e. assuming that an attempt has been made to save the resource, e.g. by a create action) -
If there are no errors, i.e. the resource was saved successfully, the
response redirect's to the resource i.e. its show action.
If there are validation errors, the response renders a default action,
which is :new for a post request or :edit for patch or put.
source: http://apidock.com/rails/v4.1.8/ActionController/MimeResponds/respond_with
For your case, something like render 'view_path' in place of respond_with block should work.

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

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' }

Rails 4 routing - No route matches

I have some troubles with routes in Rails 4 (rusty Rails user). I have the following routes for my Session controller:
controller :sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
get "sessions/create"
get "sessions/destroy"
And I have a form that looks like this:
= form_tag do
.form_container
.field
= label_tag :name, "Namn:"
= text_field_tag :name, params[:name]
.field
= label_tag :password, "Lösenord:"
= password_field_tag :password, params[:password]
.actions
= submit_tag 'Login', :class => "submit_button"
And my session#create action looks like this:
def create
user = User.find_by(name: params[:name])
if user and user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_path
else
redirect_to login_url, alert: "Invalid user/password combination"
end
end
And I get the following error:
No route matches [POST] "/login/create"
How should my routes look in this case?
I generally have a
resource :session
post 'login' => 'sessions#create'
get 'login' => 'sessions#new'
that creates
session POST /session(.:format) sessions#create
new_session GET /session/new(.:format) sessions#new
edit_session GET /session/edit(.:format) sessions#edit
GET /session(.:format) sessions#show
PUT /session(.:format) sessions#update
DELETE /session(.:format) sessions#destroy
login POST /login(.:format) sessions#create
GET /login(.:format) sessions#new
And after that just use the correct urls where needed
= form_tag login_path
...
That should do the trick

Resources