Search code examples
ruby-on-railsdevisecancancan

Update information of different user with extension of Devise


I setup a platform that uses Devise to create and manage users and CanCanCan to manage permissions. Users have a role (admin or client) that allows them access to certain areas. Admin users have the ability to can :manage, :all

I extended Devise so that I have a users/registrations and users/sessions controller.

Admin users have access to views in a ManagerController dashboard that allows them to manage other users. I've figured out how to show the details of a different user, but I can't figure out how to save the information. I get errors that indicate it either can't find the User or that it requires a POST route.

I'm fairly new to Rails, so it could be something simple. I was able to find solutions that included the regular Devise installation, but not the extension. I feel like the problem may be in my routes.

I also can't create new users from here either.

Routes.rb

  get 'users' => 'manager#users'
  get 'users/edit' => 'manager#edit'
  post 'users/edit' => 'manager#edit'
  get 'users/new' => 'manager#new'

  devise_for :users, :controllers => { registrations: 'users/registrations', sessions: 'users/sessions' }

  devise_scope :user do
      authenticated :user do
        root 'users/registrations#edit', as: :authenticated_root
      end

      unauthenticated do
        root 'users/sessions#new', as: :unauthenticated_root
      end
  end

Manager Controller

class ManagerController < ApplicationController
  authorize_resource :class => false
  include ManagerHelper

  def users
     @users = User.where.not(:id => current_user.id)
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      flash[:notice] = "Successfully created User." 
      redirect_to root_path
    else
      render :action => 'new'
    end
  end

  def edit
    @user = User.find(params[:id])
    @companies = Company.all

  end

  def update
    @user = User.find(params[:id])
    params[:user][:id].delete(:password) if params[:user][:password].blank?
    params[:user][:id].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?

    if @user.update(user_params)
      flash[:notice] = "Successfully updated User."
      redirect_to root_path
    else
      render :action => 'edit'
    end
  end

  end 
  private

  def user_params
     params.require(:user).permit(:email, :first_name, :last_name, :company_id, :role, :password, :password_confirmation)
  end
end

ManagerHelper

module ManagerHelper
  def resource_name
    :user
  end

  def resource
    @resource ||= User.new
  end

  def devise_mapping
    @devise_mapping ||= Devise.mappings[:user]
  end
end

Manager/Edit.html.erb

<h2>Edit <%= resource_name.to_s.humanize %></h2>



<%= form_for(resource, :as => @user, :url => edit_user_registration_path(resource_name))  do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

  <%= f.hidden_field :id %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :first_name %><br />
    <%= f.text_field :first_name%>
  </div>

  <div class="field">
    <%= f.label :last_name %><br />
    <%= f.text_field :last_name %>
  </div>




    <div class="field">
      <%= f.label :company %><br />
      <%= f.collection_select :company_id, @companies, :id, :name, prompt: true %>
    </div>

    <div class="field">
      <%= f.label :role %><br />
      <%= f.text_field :role %>
    </div>



  <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="actions">
    <%= f.submit "Update" %>
  </div>
<% end %>


I would like the updated information to be saved to the User database.

Currently, I get this error when I click update after changing the user info.

ActiveRecord::RecordNotFound in ManagerController#edit Couldn't find User without an ID Weirdly, when I see the User info, the url is: http://localhost:3000/users/edit?id=2 But when I update (and get the error), the url changes to: http://localhost:3000/users/edit.user

I would also like to be able to create New Users from this screen. Currently, the user form populates with the information of the user who is logged in. (To be fair, I have been trying to get the edit user correct before tackling this issue.)


Solution

  • Many tries later, I've found a solution. There were a lot of errors, so bear with me.

    Routes.rb -- Was using the wrong URL in the form submission, so needed to add the routes for the Update function in the Manager controller.

      get 'users/edit' => 'manager#edit'
      post 'users/edit' => 'manager#edit'
      get 'users/update' => 'manager#update'
      post 'users/update' => 'manager#update'
    

    Manager Controller -- I had to change the password rules so that I could update without a password. Similar concept, slightly different execution.

      def update
        @user = User.find(params[:user][:id])
        @companies = Company.all
        if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
          params[:user].delete(:password)
          params[:user].delete(:password_confirmation)
        end
        if @user.update(user_params)
          flash[:notice] = "Successfully updated User."
          redirect_to root_path
        else
          render :action => 'edit'
        end
      end
    

    Rendered Form.html.erb - Updated with newly created URL path.

    <%= form_for(resource, as: @user, url: users_update_path(resource_name)) do |f| %>
      <%= render "devise/shared/error_messages", resource: resource %>