How to validate a role with devise gem? - ruby-on-rails

Im trying to validate a enum with the gem devise, but it doesn't save in my active record.
this is my model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates :full_name, presence: true
enum role: %i[admin_user regular_user]
end
this is my controller:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit({ roles:[] }, :full_name,:email, :password, :password_confirmation)
end
end
end
and this is my views:
Sign up
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email%><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class= "field">
<%= f.label :full_name %>
<%= f.text_field :full_name, autocomplete: "full_name"%>
</div>
<div class="field">
<%=f.label :roles %>
<%= f.select :role, collection: User.roles.keys.to_a %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
What should i change to save the roles in active record?

You're whitelisting an array when you just need a single param:
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit(:role, :full_name,:email, :password, :password_confirmation)
end
end
You should also fix the view so that you're using the same argument for the label and input:
<div class="field">
<%= f.label :role %>
<%= f.select :role, collection: User.roles.keys.to_a %>
</div>
This is important for assistive technologies such as screenreaders so that they can properly link the label to the input via the for attribute.

Related

custom password digest issue

I am trying to add hashed custom password into User model. What I did is:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user!
before_action :check_domain
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:slug, :custom_password_digest])
devise_parameter_sanitizer.permit(:account_update, keys: [:slug, :custom_password_digest])
end
end
new.html
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :slug %><br />
<%= f.text_field :slug %>
</div>
<div class="field">
<%= f.label :custom_password %><br />
<%= f.password_field :custom_password %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
class User
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
has_secure_password :custom_password
validates :slug, presence: true, uniqueness: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
I am getting error now:
https://i.stack.imgur.com/ILHjc.png
Could you please advise me what i am doing wrong?
Purpose is to save hashed custom_password to User table.
You're missing the field to store your encrypted password.
Create a migration to add it to the database...
rails g migration AddCustomPasswordDigestToUser custom_password_digest:string
then run the migration...
rails db:migrate

Problem saving custom attribute in rails devise gem

The authentication system in my app is handled by devise and now I want each user in my system to belong to an organisation. So each organisation will have multiple users.
When signing up, each user will select which organisation they want to join.
When a user is signing up, and they select and organisation from a combo-box, they get the following error:
ActiveRecord::AssociationTypeMismatch in Devise::RegistrationsController#create
Organisation(#70213198483780) expected, got "1" which is an instance of String(#70213152374240)
The following is what my source code looks like:
app/models/organisation.rb
class Organisation < ApplicationRecord
has_many :users
end
app/models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :activities
belongs_to :organisation
end
app/views/devise/registrations/new.html.erb
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :organisation %><br />
<%= f.select :organisation, Organisation.all.collect { |o| [ o.organisation_name, o.id ] }%>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :configure_sign_up_params, if: :devise_controller?
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:organisation])
end
end
I suggest you should change in your form to
<%= f.select :organisation_id, Organisation.all.collect { |o| [ o.organisation_name, o.id ] }%>
Because the dropdown makes organisation.name as key and organisation.id as value.
Then change devise_parameter_sanitizer.permit(:sign_up, keys: [:organisation_id]) to allow organisation_id to be assigned to user
Instead of using collect on Organisation.all, use Organisation.all.pluck(:name, :id). It will give same result as but a more optimised query.

Rails Devise: "Foreign Key was set to nil" error when Updating nested Attributes on Form

I am creating a form to update a User in my Rails app with Devise.
I have separated my User data into a User model for the email and password, and a profile table for all the other data.
Created a form to update these details. Form renders fine, and parameters are being sent, but I am getting this error when I try to update the first_name for my nested record.
ActiveRecord::RecordNotSaved in Devise::RegistrationsController#update
Failed to remove the existing associated profile. The record failed to save after its foreign key was set to nil.
Extracted source (around line #93):
if target.persisted? && owner.persisted? && !target.save
set_owner_attributes(target)
raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. "
"The record failed to save after its foreign key was set to nil."
end
Models look like
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one :profile
after_create :create_profile
accepts_nested_attributes_for :profile
end
class Profile < ApplicationRecord
belongs_to :user
end
Controllers look like
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u|
u.permit(:email, :password, [profile_attributes: [:id, :first_name, :last_name]])
}
devise_parameter_sanitizer.permit(:account_update) { |u|
u.permit(:email, :password, [profile_attributes: [:id, :first_name, :last_name]])
}
end
end
class UsersController < Devise::RegistrationsController
def create
super
end
def show
#user = current_user
end
def edit
#user = current_user
super
end
def update
#user = current_user
super
end
end
and the View looks like
<h1>Account Details</h1>
<p><strong>Email Address:</strong> <%= #user.email %></p>
<p><strong>First Name:</strong> <%= #user.profile.first_name %></p>
<p><strong>Last Name:</strong> <%= #user.profile.last_name %></p>
<p><strong>Description:</strong> <%= #user.profile.description %></p>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<%= f.fields_for :profile_attributes, {html: { method: :put}} do |p| %>
<div class="field">
<%= p.label :first_name %><br />
<%= p.text_field :first_name %>
</div>
<% end %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="field">
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "new-password" %>
<% if #minimum_password_length %>
<br />
<em><%= #minimum_password_length %> characters minimum</em>
<% end %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "current-password" %>
</div>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>
<br><br>
<%= button_to "Delete Account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
How do I resolve this?
You need to use existing user's profile inside the form. Change line with fields_for to:
<%= f.fields_for resource.profile do |p| %>
Note, you don't need a method here, since it is not a separate form

How to create a profile for devise user?

I'm new to rails and trying to create a profile for devise users when they signup, using nested form in devise signup. I've gone through
Creating Profile for Devise users,
Profile model for Devise users?
and few other articles to achieve the same but after a day in vain, I'm still trying to make it work. Here is my code.
Model - user.rb
class User < ActiveRecord::Base
has_one :user_profile
accepts_nested_attributes_for :user_profile
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Model - user_profile.rb
class UserProfile < ActiveRecord::Base
belongs_to :user
end
Controller - controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) {|u|
u.permit(:email, :password, :password_confirmation, :remember_me,
user_profile_attributes: [:first_name, :last_name])}
end
end
end
View - views/devise/registrations/new.html.erb
<h2>Sign up</h2>
<% resource.build_user_profile %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<%= f.fields_for :user_profile do |profile_form| %>
<%= profile_form.label :first_name %><br/>
<%= profile_form.text_field :first_name %><br/>
<%= profile_form.label :last_name %><br/>
<p><%= profile_form.text_field :last_name %><br/>
<% end %>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
Server Log
Processing by Devise::RegistrationsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"rLuFXwISxiJpWPjpmKzjnjhKr41F5
56sWbtT+8gslAMsFDWRbl7MSitSXUESjLdZccCBGBGvVv+JbhW7G5py5g==", "user"=>{"email"=>
"zebandz#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTER
ED]", "user_profile_attributes"=>{"first_name"=>"Zeban", "last_name"=>"Dezend"}}
, "commit"=>"Sign up"}
Unpermitted parameter: user_profile_attributes
I think, I'm missing the code to fetch the values from params and create a new record. Can someone suggest me the fix ?
please follow the below steps.
devise> reg > new
<div class="row">
<div class="col-md-5 col-md-offset-4">
<h2>Sign up</h2>
<% resource.build_user_profile if resource.user_profile.nil? %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="row">
<%= f.fields_for :user_profile do |profile_form| %>
<div class="col-md-6">
<div class="form-group">
<%= profile_form.label :first_name %>
<%= profile_form.text_field :first_name, class: "form-control" %>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<%= profile_form.label :last_name %>
<%= profile_form.text_field :last_name, class: "form-control" %>
</div>
</div>
<% end %>
</div>
<div class="form-group">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, class: "form-control" %>
</div>
<%= f.fields_for :user_profile do |profile_form| %>
<div class="form-group">
<%= profile_form.label :business_name %>
<%= profile_form.text_field :business_name, class: "form-control" %>
</div>
<div class="form-group">
<%= profile_form.label :business_category %>
<%= profile_form.collection_select :business_category_id, BusinessCategory.all, :id, :name, {prompt: "Select Category"}, {class: "form-control"} %>
</div>
<div class="form-group">
<%= profile_form.label :website %>
<%= profile_form.url_field :website, class: "form-control" %>
</div>
<div class="form-group">
<%= profile_form.label :address %>
<%= profile_form.text_area :address, class: "form-control" %>
</div>
<div class="form-group">
<%= profile_form.label :personal_number %>
<%= profile_form.text_field :phone_number, class: "form-control" %>
</div>
<div class="form-group">
<%= profile_form.label :office_number %>
<%= profile_form.text_field :office_number, class: "form-control" %>
</div>
<% end %>
<div class="form-group">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off", class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off", class: "form-control" %>
</div>
<div class="actions">
<%= f.submit "Sign up", class: "btn btn-primary" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
</div>
user reg controller
def new
# Override Devise default behaviour and create a profile as well
build_resource({})
resource.build_user_profile
respond_with self.resource
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u|
u.permit(:email, :password, :password_confirmation, :user_profile_attributes => [:first_name, :last_name, :business_name, :business_category_id, :website, :address, :phone_number, :office_number])
}
end
Have you tried following these steps from their github page?
They set the permitted parameters in the ApplicationController. Also the structure is a little different. The parameters go into keys.
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
added_attrs = [:username, :email, :password, :password_confirmation, :remember_me]
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
end
end

Avatar (picture) can't be blank after avatar attached and form submitted - carrierwave error

I am a newbie in Rails. I use Rails 4.2 with Ruby 2.0, I've installed the carrierwave gem. I followed the instructions how to setup with devise.
But the validation or the picture does not work correctly, cause I always get the "Avatar can't be blank" error message when I attached a picture and submit the form. I have no idea where is my mistake.
User model:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook, :google_oauth2]
after_initialize :set_default_role, :if => :new_record?
# Validations
validates_presence_of :avatar
validates_integrity_of :avatar
validates_processing_of :avatar
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.name = auth.info.nickname
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
end
end
end
Sign up form
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { multipart: true }) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>
<div class="field">
<%= f.label 'Woman' %><br />
<%= f.radio_button :gender, 'Woman' %>
<br>
<%= f.label 'Man' %><br />
<%= f.radio_button :gender, 'Man' %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.label :phone %><br />
<%= f.text_field :phone %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :city %><br />
<%= f.text_field :city %>
</div>
<div class="field">
<%= f.label :county %><br />
<%= f.text_field :county %>
</div>
<div class="field">
<label>My Avatar</label>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "users/shared/links" %>
Application controller's strong parameters:
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:email) }
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit({ roles: [] }, :name, :email, :password, :password_confirmation, :avatar, :avatar_cache) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit({ roles: [] }, :name, :email, :password, :password_confirmation, :avatar, :avatar_cache) }
end
I really do not see where is my mistake. Maybe do you see?
Because you have this validation:
validates_presence_of :avatar
means, you have to upload an avatar.
You will get this error message: Avatar can't be blank if you try to submit the form without attaching the avatar.
So, make sure you attach an avatar before hitting the Sign Up button.

Resources