Adding roles to users in rails app? - ruby-on-rails

So summary: I wanted to have two types of users which are teachers and students. They are all users and therefore inherit from the users controller. The problem is no about the controllers or classes existing, it's how to adapt them through a form and whether or not I'm missing anything. I'm not sure how to do a backend to radio buttons and have tried things from previous answers but they result in errors. Also, how do I change routes based on which role they select? I have a registrations controller to override Devise.
Here is my form:
<div class="authform"> <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
<h2>Sign Up</h2>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<div>
<%= f.radio_button :role, 'student' %>
<%= label :role_student, 'Student' %>
<%= f.radio_button :role, 'teacher' %>
<%= label :role_teacher, 'Teacher' %>
</div>
<%= f.submit 'Sign Up', :class => 'button right' %>
<% end %> </div>
I have a migration to add roles to users right here
class AddRoleToUsers < ActiveRecord::Migration
def change
add_column :users, :role, :integer
end
end
This was my original attempt on changing routes when adding roles (from another stack overflow question)
class RegistrationsController < Devise::RegistrationsController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
path_to_route = '/classrooms/index'
elsif [:user][:role] == 'teacher'
path_to_route = '/classrooms/new'
else
path_to_route = '/'
end
redirect_to path_to_route
end
end
Finally, here is my user's controller
class UsersController < ApplicationController
# before_action :authenticate_user!
before_action :logged_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
after_action :verify_authorized
# before_filter :check_role
def show
#user = User.find(params[:id])
authorize #user
#posts = #user.posts
end
def new
#user = User.new
authorize #user
end
#def after_sign_up_path_for(resource)
# if params[:user][:role] == 'student'
# path_to_route = '/classrooms/index'
# elsif [:user][:role] == 'teacher'
# path_to_route = '/classrooms/new'
# else
# path_to_route = '/'
# end
# redirect_to path_to_route
#end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
# authorize #user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
def create_user_type
end
private
def secure_params
params.require(:user).permit(:role)
end
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless #user == current_user
end
end
I know this is a bit of a long post, but I'd appreciate any help. Thanks so much! I love the rails community.

If you use simple_form you can use a standard input and assign the options to collection.
with simple_form
<%= f.input :role, collection: [:admin, :subscriber, :poster] %>
without simple_form
<%= f.select(:role, User.roles.keys.map {|role| [role.titleize,role]}) %>
user.rb
enum role: [:admin, :subscriber, :poster]

Related

Issues with redirecting after signup with Devise. What to do?

I added roles to my users and after sign up devise automatically send me back to the homepage. I tried many things from this site and past questions but nothing seems to work. Maybe I'm over thinking it, I'm new here. Essentially I want people who sign up to choose their role, if they're a teacher they go to create a new class ('classrooms#new') and students go to chose their class ('classrooms#index'). I've created a separate registrations controller as suggested and that hasn't worked.
Sign up form (new.html.erb)
<div class="authform">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
<h2>Sign Up</h2>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<div>
<%= f.radio_button :role, 'student' %>
<%= label :role_student, 'Student' %>
<%= f.radio_button :role, 'teacher' %>
<%= label :role_teacher, 'teacher' %>
</div>
<%= f.submit 'Sign Up', :class => 'button right' %>
<% end %>
</div>
Here is my registrations controller:
class Devise::RegistrationsController < DeviseController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
end
Here is my users controller
class UsersController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
before_filter :check_role
def show
#user = User.find(params[:id])
authorize #user
#posts = #user.posts
end
def new
#user = User.new
authorize #user
end
def check_role
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
# authorize #user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
def create_user_type
end
private
def secure_params
params.require(:user).permit(:role)
end
end
Edit: Here is my routes page
Rails.application.routes.draw do
get 'students/new'
get 'classrooms/new'
get 'teachers/new'
devise_for :users
resources :users
get "edit_profile" => "devise/registrations#edit"
resources :posts
resources :teachers
post 'users/create_user_type' => 'users/create_user_type'
root to: 'visitors#index'
end
I know there is repetitive code with checking roles, I've just been merging answers together (which isn't good I know)
use pry gem with binding.pry to check your code & variable content.
Anyway, I suppose that role is an attribute of user so why aren't you checking
user.teacher?
user.student?
instead of
params['']['']
resource attribute is persistent instead of params content
I think your registrations controller isn't correct.
If you create your own registrations controller, you want it to override the registrations controller from devise:
class RegistrationsController < Devise::RegistrationsController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
end
Then in your routes you must make sure that it also uses your own registrations controller:
devise_for :users, controllers: {registrations: 'registrations'}
Hope this will help you.

Rails: Multi-Step New User Signup Form (First argument in form cannot contain nil or be empty)

I'm a beginner and have a serious problem with making two steps signing up.
I keep getting that error:
First argument in form cannot contain nil or be empty
<%= form_for(#Profile) do |f| %>
Users can sign up with their user information which are on the user table(DB). And all is fine with them. But I really want them to have profiles. So they can put their bio for example.
I tried to create MVC structure for the profiles based on users' MVC but it doesn't work.
I have been trying to find the answer for days. Tried hundreds of variations but nothing worked out. Please help!
views/users/new.html.erb (This is working)( I just wanted to create profile for the users and give them the ability to fill out their information as the second steps of the signing up)
<div class="container">
<% provide(:title, 'Sign up') %>
<h1 class="center">Sign up</h1>
<div class="row">
<div class="col s12 m10 l8 offset-m1 offset-l2">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :genre %>
<%= f.text_field :genre %>
<%= f.label :middle_name %>
<%= f.text_field :middle_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.label :preferred_name %>
<%= f.text_field :preferred_name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<div class="row center">
<%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
models/user.rb
class User < ActiveRecord::Base
has_one :profile
has_many :microposts, dependent: :destroy
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :first_name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
def feed
Micropost.from_users_followed_by(self)
end
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
controllers/users_controller.rb
class UsersController < ApplicationController
before_action :signed_in_user,
only: [:index, :edit, :update, :destroy, :following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Welcome to the my app"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "just got destroyed."
redirect_to users_url
end
def following
#title = "Connections"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "known by"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:first_name, :middle_name, :last_name, :preferred_name, :email, :password,
:password_confirmation)
end
# Before filters
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
second signup page
views/users/signup2.html.erb
<div class="container">
<% provide(:title, 'Sign up2') %>
<h1 class="center">Sign up2</h1>
<div class="row">
<div class="col s12 m10 l8 offset-m1 offset-l2">
<%= form_for(#Profile) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :primary_instrument %>
<%= f.text_field :primary_instrument %>
<%= f.label :bio %>
<%= f.text_field :bio %>
<div class="row center">
<%= f.submit "Create my account2", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
controllers/profiles_controller.rb
class ProfilesController < ApplicationController
before_filter :get_user
def get_user
#profile = User.find(params[:user_id])
end
# generate new-profile form
def new
#user.profile = Profile.new
#profile = #user.profile
end
# process new-profile-form post
def create
#user.profile = Profile.new(params[:profile])
#profile = #user.profile
respond_to do |format|
if #profile.save
flash[:notice] = 'Profile was successfully created.'
format.html { redirect_to(#profile) }
format.xml { render :xml => #profile, :status => :created, :location => #profile }
...
end
end
end
# generate edit-profile form
def edit
#profile = Profile.find(params[:id])
end
# generate edit-profile-form post
def update
#profile = #user.profile
respond_to do |format|
if #profile.update_attributes(params[:profile])
flash[:notice] = 'Profile was successfully updated.'
# format.html { redirect_to(#profile) }
format.html { redirect_to(user_profile(#user)) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end
Routes.rb
root to: 'static_pages#home'
match '/signup', to: 'users#new', via: 'get'
match '/signup2', to: 'profiles#new', via: 'get'
match '/signin', to: 'sessions#new', via: 'get'
match '/signout', to: 'sessions#destroy', via: 'delete'
match '/help', to: 'static_pages#help', via: 'get'
match '/about', to: 'static_pages#about', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
end
Your #Profile variable is capitalized in the markup, while the variable set in your controller is downcased. Rewrite the variable in the markup like so:
<%= form_for(#profile) do |f| %>
Hope it helps!
Apart from #Zoran's answer you can use the following gem which would make your life easy with multi step forms.
Wicked gem

Can't log in to my database. Error: "Param is missing or the value is empty: user"

I'm having a trouble with my logging in. It highlights the user_params function as an error in users controller. What could it be?
/views/sessions/new.html.erb or login form
<h1>Log In</h1>
<%= form_tag users_path do %>
<div class="field">
<%= label_tag :email %><br />
<%= text_field :email, params[:email] %>
</div>
<div class="field">
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</div>
<div class="actions"><%= submit_tag "Log In" %></div>
<% end %>
controllers/sessions_controller.rb
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 = "Email or password is invalid"
render "new"
end
end
def destroy
session[:user_id] = nil
redirect_to signup_path, notice: "Logged out!"
end
end
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 user_tasks, notice: "Thank you for signing up!"
else
render "new"
end
end
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def authorize
redirect_to login_url, alert: "Not authorized" if current_user.nil?
end
end
rake routes
Generated parameters
{"utf8"=>"✓", "authenticity_token"=>"jtdA0rITCiFjU9ECiKnDUMh/MHMpqv6u+Bd65PAfpARwYsmb9Dli32wpULfmYa7wpBQiFYxAGHF6flCfJgPOew==", "email"=>"dsadsa", "password"=>[FILTERED]", "commit"=>"Log In"}
Any help?
change this line and try please.
<%= text_field :email, params[:email] %>
to
<%= text_field_tag :email, params[:email] %>
Also majorly add this in your routes file to redirect it to sessions controller create action.
post '/authenticate' => 'sessions#create'
change this tag
<%= form_tag users_path do %>
to
<%= form_tag /authenticate do %>
When you look into the params, they are not coming from the user. Try changing your user_params like below
def user_params
params.permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
I suggest you keep the :user namespace for params in your form
<%= form_tag users_path do |form| %>
<%= fields_for :user do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email, params[:email] %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="actions"><%= submit_tag "Log In" %></div>
<% end %>
<% end %>
So you can still use params.require(:user) in your controller as usual
Parameters will look like this
{
"utf8"=>"✓",
"authenticity_token"=>"jtdA0rITCiFjU9ECiKnDUMh/MHMpqv6u+Bd65PAfpARwYsmb9Dli32wpULfmYa7wpBQiFYxAGHF6flCfJgPOew==",
"user" => {
"email"=>"dsadsa",
"password"=>[FILTERED],
},
"commit"=>"Log In"
}

The action 'create' could not be found for Admin::UsersController

I am stuck with this issue, and i am not able to identify the problem. I am trying to add a user from admin section. Hence I have created a custom user controller, which calls the devise model. And I am trying to insert and update values using custom forms.
Controller
class Admin::UsersController < ApplicationController
load_and_authorize_resource
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user!
layout 'admin-layout'
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit({ roles: [] }, :email, :password, :password_confirmation) }
end
def index
#user = User.all
end
def new
#user = User.new
end
def create
#user = User.new
if #user.save
redirect_to :action => "index"
else
render :action => "new"
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes
redirect_to :action => "index"
else
render :Action => "update"
end
end
def destroy
User.find(params[:id]).destroy
redirect_to ::Action => "index"
end
end
View File
<div class="container">
<div class="col-md-6 col-md-offset-2">
<h2>Create New User</h2>
<%= form_for #user, url: {action: "create"} do |f| %>
<%= f.text_field :email, :class=>"form-control", :placeholder => "Enter Email", :required => "true" %>
<%= f.text_field :password, :class=>"form-control", :placeholder => "Enter Password", :required => "true" %>
<%= f.text_field :password_confirmation, :class=>"form-control", :placeholder => "Enter Password Again", :required => "true" %>
<p>Roles:</p>
<% for r in Role.all %>
<%= check_box_tag "user[role_ids][]", r.id %>
<%= r.name %>
<% end %><br/>
<%= f.submit "Create", :class=>"btn btn-primary" %>
<% end %>
</div>
<div>
Routes
admin_users GET /admin/users(.:format) admin/users#index
POST /admin/users(.:format) admin/users#create
Can any1 please point me out where am I making a mistake.
You defined new, create etc. methods as protected, they need to be public if you want them to be recognized as actions.
BTW, in Ruby, if you have collection Role.all, it's much more readable to iterate over this with each instead of for (which is rarely used here):
<% Role.all.each do |r| %>

rails blog add comment redirecting to active admin login page

I am creating a blogging application which uses active_admin for admin panel.
When I try to comment on a post it is redirecting me to active_admin login page.
Comment Model:-
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post, :counter_cache => true
attr_accessible :text, :user_id, :post_id
validate :text, :presence => true
end
Active Admin Comment Model:-
ActiveAdmin.register Comment, :as => "PostComment" do
end
Posts Controller:-
def show
#post = Post.find(params[:id])
#showPost = true
respond_with do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
Comments Controller:-
class CommentsController < ApplicationController
before_filter :auth_user
def create
#post = Post.find(params[:post_id])
params[:comment][:user_id] = current_user.id
#comment = #post.comments.create(params[:comment])
redirect_to post_path(#post)
end
end
Comment Form:-
<%= semantic_form_for ([#post, #post.comments.build]), :html => { :id => "comment-form", :class => "metta-form"} do |f| %>
<h5 class="pull-left">Comments</h5>
<div class="clearfix">
</div>
<div class="wrap-comment-form">
<%= f.inputs do %>
<%= f.input :text, :label => false, :input_html => { :class => "comment-text pull-left" } %>
<% end %>
<%= f.actions do %>
<%= f.action :submit, :label => "Add Comment", :button_html => { :placeholder => "Enter Your Comment Here", :class => "btn btn-primary btn-metta pull-right" } %>
<% end %>
</div>
<% end %>
When I click on the add comment button it takes me to active admin login page. Can someone help me out here ??
class ApplicationController < ActionController::Base
protect_from_forgery
helper ApplicationHelper
def after_sign_in_path_for(resource)
'/users/home'
end
def after_sign_up_path_for(resource)
'/users/home'
end
def auth_user
redirect_to new_user_path unless user_signed_in?
end
end
Look here: before_filter :auth_user
So the reason you have been redirected to login page is you have not logged in yet.

Resources