Last active
December 30, 2022 18:07
-
-
Save jasper502/065a63fde266fac1fbf5 to your computer and use it in GitHub Desktop.
Devise with additional user fields, nested models / attributes and custom routes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The idea here is that I have a single User login that can eventually be tied to multiple Companies via the Role. | |
You signup via Devise on the custom /register route and fillout the Company information etc. The custom registration controller creates the Role during the user creation and sets a few other attributes. | |
It all worked but now it's broken and I have no idea what I did to break it. | |
When I try to create a new user / company the additional user fields (name_first & name_last) always fail validation regardless if they are in fact valid. The nested Company fields do not validate at all. If I enter the email and password field only the form works but only creates the User record. | |
To me it seems like the custom registration controller is not being processed at all. | |
*UPDATE* | |
Added my new routes file - I switched my routes to "user/..." from "devise/..." and that seemed to do the trick. | |
Also changed the logic in my regisitration controller so that the Role and Company updates are after the User is saved. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Company < ActiveRecord::Base | |
has_many :roles, :dependent => :destroy, :inverse_of => :user | |
has_many :users, :through => :roles | |
has_one :user, :inverse_of => :company | |
validates :name, :presence => { :message => "^Company Name Required" } | |
validates :address1, :presence => { :message => "^Company Address Required" } | |
validates :city, :presence => { :message => "^City Required" } | |
validates :province, :presence => { :message => "^Province Required" } | |
validates :postal, :presence => { :message => "^Postal Code Required" } | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%= form_for(resource, :html => {:class => "form-signin" }, as: resource_name, url: user_registration_path) do |f| %> | |
<div class="container-fluid"> | |
<div class="row"> | |
<div class="col-md-6 text-center"> | |
<h3>Account Owner</h3> | |
</div> | |
<div class="col-md-6 text-center"> | |
<h3>Company Information</h3> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-md-6"> | |
<fieldset> | |
<div class="form-group"> | |
<%= f.label "First Name" %> | |
<%= f.text_field :name_first, class: "form-control", placeholder: "First Name", autocomplete: "on" , autofocus: true %> | |
</div> | |
<div class="form-group"> | |
<%= f.label "Last Name" %> | |
<%= f.text_field :name_last, class: "form-control", placeholder: "Last Name", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= f.label "Phone Number" %> | |
<%= f.text_field :phone, class: "form-control", placeholder: "Phone Number", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= f.label "Email" %> | |
<%= f.email_field :email, class: "form-control", placeholder: "Email", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= f.label "Password" %> | |
<%= f.password_field :password, class: "form-control", placeholder: "Password", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= f.label "Confirm Password" %> | |
<%= f.password_field :password_confirmation, class: "form-control", placeholder: "Password Confirmation", autocomplete: "on" %> | |
</div> | |
</fieldset> | |
</div> | |
<div class="col-md-6"> | |
<fieldset> | |
<%= f.fields_for :roles, resource.roles.build do |r| %> | |
<%= r.fields_for :company, resource.roles.build.build_company do |c| %> | |
<div class="form-group"> | |
<%= c.label "Company Name" %> | |
<%= c.text_field :name, class: "form-control", placeholder: "Company Name", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= c.label "Address" %> | |
<%= c.text_field :address1, class: "form-control", placeholder: "Address", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= c.label "Address Continued (if required)" %> | |
<%= c.text_field :address2, class: "form-control", placeholder: "Address Continued", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= c.label "City" %> | |
<%= c.text_field :city, class: "form-control", placeholder: "City", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= c.label "Province" %> | |
<%= c.text_field :province, class: "form-control", placeholder: "Province", autocomplete: "on" %> | |
</div> | |
<div class="form-group"> | |
<%= c.label "Postal Code" %> | |
<%= c.text_field :postal, class: "form-control", placeholder: "Postal Code", autocomplete: "on" %> | |
</div> | |
<% end %> | |
<% end %> | |
</fieldset> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-md-4 col-md-offset-4"> | |
<input class="btn btn-lg btn-primary btn-block" type="submit" value="Register"> | |
</div> | |
</div> | |
</div> | |
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def create | |
build_resource(sign_up_params) | |
resource_saved = resource.save | |
yield resource if block_given? | |
if resource_saved | |
# Update the role to an owner and make default | |
resource_saved.roles.last.assign_attributes(role: "owner", active: 1, default_role: 1) | |
resource_saved = resource_saved.save | |
# Update the company to show the owner - not sure I need this | |
resource_saved.roles.last.company.update_columns( user_id: resource.id ) | |
resource_saved = resource_saved.save | |
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 | |
@validatable = devise_mapping.validatable? | |
if @validatable | |
@minimum_password_length = resource_class.password_length.min | |
end | |
respond_with resource | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Role < ActiveRecord::Base | |
belongs_to :user, :inverse_of => :roles | |
validates_presence_of :user, :message => "user validation" | |
belongs_to :company, :inverse_of => :roles | |
validates_presence_of :company, :message => "company validation" | |
accepts_nested_attributes_for :company | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
devise_for :users, :controllers => { :registrations => 'users/registrations' }, skip: [:sessions, :passwords, :confirmations, :registrations, :unlocks] | |
devise_scope :user do | |
# authentication | |
# unauthenticated :user do | |
# root 'index#landing' | |
# end | |
# authenticated :user do | |
# root to: :dashboard | |
# end | |
get '/signin', to: 'devise/sessions#new', as: 'new_user_session' | |
post '/signin', to: 'devise/sessions#create', as: 'user_session' | |
get '/signout', to: 'devise/sessions#destroy' | |
delete '/signout', to: 'devise/sessions#destroy', as: 'destroy_user_session' | |
# registrations | |
get '/register', to: 'users/registrations#new', as: 'new_user_registration' | |
post '/register', to: 'users/registrations#create', as: 'user_registration' | |
# user accounts | |
scope '/account' do | |
# confirmation | |
get '/verification', to: 'users/confirmations#verification_sent', as: 'user_verification_sent' | |
get '/confirm', to: 'users/confirmations#show', as: 'user_confirmation' | |
get '/confirm/resend', to: 'users/confirmations#new', as: 'new_user_confirmation' | |
post '/confirm', to: 'users/confirmations#create' | |
# passwords | |
get '/reset-password', to: 'users/passwords#new', as: 'new_user_password' | |
get '/reset-password/change', to: 'users/passwords#edit', as: 'edit_user_password' | |
put '/reset-password', to: 'users/passwords#update', as: 'user_password' | |
post '/reset-password', to: 'users/passwords#create' | |
# unlocks | |
post '/unlock', to: 'users/unlocks#create', as: 'user_unlock' | |
get '/unlock/new', to: 'users/unlocks#new', as: 'new_user_unlock' | |
get '/unlock', to: 'users/unlocks#show' | |
# settings & cancellation | |
# get '/cancel', to: 'users/registrations#cancel', as: 'cancel_user_registration' | |
get '/profile', to: 'users/registrations#edit', as: 'edit_user_registration' | |
put '/profile', to: 'users/registrations#update' | |
# account deletion | |
# delete '', to: 'users/registrations#destroy' | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Rails.application.routes.draw do | |
devise_for :users, :skip => [:sessions, :registrations], :controllers => { :registrations => 'users/registrations', :sessions => 'users/sessions' } | |
as :user do | |
#REGISTRATON | |
#resource :registration, only: [:new, :create, :edit, :update] | |
get "/register/cancel" => "devise/registrations#cancel", as: :cancel_user_registration | |
post "/register/" => "devise/registrations#create", as: :user_registration | |
get "/register/" => "devise/registrations#new", as: :new_user_registration | |
get "/account/profile" => "devise/registrations#edit", as: :edit_user_registration | |
patch "/account/profile" => "devise/registrations#update" | |
put "/account/profile" => "devise/registrations#update" | |
#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 | |
devise_for :admin_users, ActiveAdmin::Devise.config | |
ActiveAdmin.routes(self) | |
root 'index#landing' | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class User < ActiveRecord::Base | |
# Include default devise modules. Others available are: | |
# :confirmable, :lockable, :timeoutable and :omniauthable | |
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :timeoutable | |
# http://stackoverflow.com/questions/14417201/how-to-automatically-keep-user-remembered-in-devise | |
def remember_me | |
true | |
end | |
has_many :roles, :dependent => :destroy, :inverse_of => :user | |
has_many :companies, :through => :roles | |
belongs_to :user, :inverse_of => :company | |
accepts_nested_attributes_for :roles, :limit => 1, :allow_destroy => true | |
validates :name_first, presence: { message: "^First Name Required" } | |
validates :name_last, presence: { message: "^Last Name Required" } | |
validates :phone, numericality: { only_integer: true, message: "^Only Numbers Allowed in Phone Number" } | |
validates :phone, length: { is: 10, message: "^Phone number must be 10 digits long i.e. 4035551234" } | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#class RegistrationsController < Devise::RegistrationsController | |
class Users::RegistrationsController < Devise::RegistrationsController | |
# POST /resource | |
### CUSTOM ### | |
def create | |
build_resource(sign_up_params) | |
# The last role should be the one created in the form | |
# We set the values here so they get saved and they aren't passed in the form | |
resource.roles.last.assign_attributes(role: "owner", active: 1, default_role: 1) | |
resource_saved = resource.save | |
resource.roles.last.company.update_columns( user_id: resource.id ) | |
yield resource if block_given? | |
if resource_saved | |
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 | |
@validatable = devise_mapping.validatable? | |
if @validatable | |
@minimum_password_length = resource_class.password_length.min | |
end | |
respond_with resource | |
end | |
end | |
protected | |
### CUSTOM ### | |
def sign_up_params | |
#devise_parameter_sanitizer.sanitize(:sign_up) | |
params.require(:user).permit(:email, :password, :password_confirmation, :name_first, :name_last, :phone, roles_attributes: [ company_attributes: [ :id, :name, :address1, :address2, :city, :province, :postal ] ] ) | |
end | |
def account_update_params | |
params.require(:user).permit(:email, :password, :password_confirmation, :current_password, :name_first, :name_last, :phone, :mobile, :title, :location, :timezone ) | |
end | |
def after_update_path_for(resource) | |
edit_user_registration_path | |
end | |
#def set_minimum_password_length | |
# if devise_mapping.validatable? | |
# @minimum_password_length = resource_class.password_length.min | |
# end | |
#end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment