Rails 5, Rolify - removing role from user
<p>I am really struggling to understand something that is fundamental to writing code in rails. I don't know what it is to ask a more fundamental question, but i seem to be having similar problems repeatedly.</p>

<p>I have managed to setup rolify in my Rails 5 app. I use rolify to assign roles to users.</p>

<p>Now, I'm trying to setup a function to remove roles from users after they are assigned.</p>

<p>In my user index, I have;</p>

<pre><code>&lt;% user.roles.each do |role| %&gt;
  &lt;%= role.name.titleize %&gt; &lt;br&gt;
&lt;% end %&gt;  
</code></pre>

<p>That shows the roles that have been assigned.</p>

<p>Then, Im trying to add:</p>

<pre><code>&lt;%= link_to 'Destroy', role, method: :delete, data: { confirm: 'Are you sure?' } %&gt;
</code></pre>

<p>My destroy method is defined in my assign_roles controller. It has:</p>

<pre><code>def destroy

    user = User.find(params[:users])
    # role = AppRole.find(params[:roles])
    assigned_role = user.roles
    # user_roles = user.roles
    # organisation = Organisation.first
    # byebug

    user.remove_role assigned_role.name, @current_user.organisation

    flash[:notice] = "Successfully removed"
    redirect_to action: :index
  end
</code></pre>

<p>The routes are:</p>

<pre><code>resources :users, shallow: true do
    scope module: :users do
      resources :assign_roles
    end
</code></pre>

<p>There isn't an assign_role.rb model. I just use a controller and a view.</p>

<p>When I try to use the destroy role link in my user index, I get an error that says:</p>

<pre><code>Couldn't find User with 'id'=
</code></pre>

<p>Can anyone see what I need to do to get the assign_roles#destroy action to work?</p>

<p>My create action in the assign_roles controller works. It has:</p>

<pre><code>def create
    user = User.find(params[:users])
    role = AppRole.find(params[:roles])
    # organisation = Organisation.first
    @organisation = Organisation.find(@current_user.organisation)
     # byebug

    user.add_role role.display_name, organisation

    flash[:notice] = "Successfully created"
    redirect_to action: :index
  end
</code></pre>

<p><strong>Entire assign roles controller:</strong></p>

<pre><code>class Users::AssignRolesController &lt; ApplicationController

  before_action :authenticate_user!

  def index
    # if @current_user.is_admin?
      @app_roles = AppRole.all
    # else
    #   @app_roles = AppRole.where(category: relevant_category)
    # end

    # if @current_user.is_admin?
        @users = User.all
   #  else 
      #   @users = current_user.organisation.users.select { |u| u.id != current_user.organisation.owner_id }
    # end  
  end


  def create
    user = User.find(params[:users])
    role = AppRole.find(params[:roles])
    # organisation = Organisation.first
    @organisation = Organisation.find(@current_user.organisation)
     # byebug

    user.add_role role.display_name, organisation

    flash[:notice] = "Successfully created"
    redirect_to action: :index
  end

  def show
    # @users = User.joins(:profiles).where('profiles.organisation_id = ?' @current_user.organisation.id)
    # @users = User.all
    @current_user.organisation.users
  end

  def update
  end

  def destroy

    user = User.find(params[:users])
    # role = AppRole.find(params[:roles])
    assigned_role = user.roles
    # user_roles = user.roles
    # organisation = Organisation.first
    # byebug

    user.remove_role assigned_role.name, @current_user.organisation

    flash[:notice] = "Successfully created"
    redirect_to action: :index
  end

end
</code></pre>

<p><strong>I CAN DO WHAT I WANT TO DO FROM THE CONSOLE - I CANT FIGURE OUT HOW TO DO IT FROM THE CODE</strong></p>

<p>In the console, I can write:</p>

<pre><code>u.remove_role :fff, Organisation.first
</code></pre>

<p>That then successfully removes the role.</p>

<p>This is fine as a test, but in the code I'm trying to use current_user.organisation instead of Organisation.first. My objective is to allow a user to manage roles on its own organisation only.</p>

<p>In my assign roles controller, I have the destroy action copied above.</p>

<p>In my users index, I have an attempt to delete the assigned role from the user as:</p>

<pre><code> &lt;% user.roles.each do |role| %&gt;
              &lt;table class="table table-bordered"&gt;
              &lt;tr&gt;
                &lt;td&gt;&lt;%= role.name.titleize %&gt;&lt;/td&gt;
                &lt;td&gt;&lt;%= link_to 'Remove role', role,  method: :delete, data: { confirm: 'Are you sure?' } %&gt;&lt;/td&gt;
</code></pre>

<p>The error says:</p>

<pre><code>undefined method `role_path' for #&lt;#
</code></pre>

<p>I wonder if I need to give it something else in the path so that it can find the assign roles#destroy action?</p>

<p>When I rake routes | grep assign, I can see:</p>

<pre><code> DELETE   /assign_roles/:id(.:format)                                             users/assign_roles#destroy
</code></pre>

<p>I tried changing the path in my users index to:</p>

<pre><code>&lt;% user.roles.each do |role| %&gt;
              &lt;table class="table table-bordered"&gt;
              &lt;tr&gt;
                &lt;td&gt;&lt;%= role.name.titleize %&gt;&lt;/td&gt;
                &lt;td&gt;&lt;%= link_to 'Remove role', assign_role_path(user),  method: :delete, data: { confirm: 'Are you sure?' } %&gt;&lt;/td&gt;
              &lt;/tr&gt;  
              &lt;/table&gt;
             &lt;% end %&gt;
</code></pre>

<p>But then that gives this error:</p>

<pre><code>Couldn't find User with 'id'=
</code></pre>

<p>I don't understand what this error means.</p>

<p><strong>Another clue</strong></p>

<p>Right, so I can see where the problem arises. I'm out of ideas for how to fix it.</p>

<p>In the console, I can successfully remove a user's role in the way I want to by doing this:</p>

<pre><code>u.remove_role :sdfddd, Organisation.first
  Organisation Load (2.0ms)  SELECT  "organisations".* FROM "organisations" ORDER BY "organisations"."id" ASC LIMIT $1  [["LIMIT", 1]]
  Role Load (0.7ms)  SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND "roles"."name" = $2 AND "roles"."resource_type" = $3 AND "roles"."resource_id" = $4  [["user_id", 4], ["name", "sdfddd"], ["resource_type", "Organisation"], ["resource_id", 1]]
   (0.1ms)  BEGIN
  SQL (0.4ms)  DELETE FROM "users_roles" WHERE "users_roles"."user_id" = $1 AND "users_roles"."role_id" = 6  [["user_id", 4]]
   (1.6ms)  COMMIT
</code></pre>

<p>In the code, my current destroy action from the assign roles controller, which says:</p>

<pre><code>def destroy

    # user = User.find(params[:users])
    user = User.find(params[:id])
    # role = AppRole.find(params[:roles])
    assigned_role = user.roles
    # user_roles = user.roles
    organisation = Organisation.first
    # organisation = Organisation.find(current_user.organisation)

    # byebug

    user.remove_role assigned_role.name, organisation

    flash[:notice] = "Successfully created"
    redirect_to root_path
  end
</code></pre>

<p>shows this process in the log:</p>

<pre><code>User Load (1.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 4], ["LIMIT", 1]]
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
  Organisation Load (0.7ms)  SELECT  "organisations".* FROM "organisations" ORDER BY "organisations"."id" ASC LIMIT $1  [["LIMIT", 1]]
  Role Load (1.0ms)  SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND "roles"."name" = $2 AND "roles"."resource_type" = $3 AND "roles"."resource_id" = $4  [["user_id", 4], ["name", "Role"], ["resource_type", "Organisation"], ["resource_id", 1]]
</code></pre>

<p>The second parameter is a "Role", where maybe it should be the name of the role (i think). Role is a table name.</p>

<p>I'm not sure how to plug this in to make this work. Can anyone see how I can get code to process in the way I can do it in the console?</p>

<p><strong>NEXT ATTEMPT</strong></p>

<p>My new attempt at the destroy action in the assign_roles controller has:</p>

<pre><code>def destroy

    # user = User.find(params[:users])
    user = User.find(params[:id])
    # role = AppRole.find(params[:roles])
    assigned_role = user.roles
    # user_roles = user.roles
    # organisation = Organisation.first
    organisation = Organisation.find(current_user.organisation)

    # byebug

    user.remove_role assigned_role, organisation

    flash[:notice] = "Successfully created"
    redirect_to root_path
  end
</code></pre>

<p>I think the problematic line is this one:</p>

<pre><code>assigned_role = user.roles
</code></pre>

<p>It should be the specific role that I am trying to destroy (from the user's array of assigned roles).</p>

<p>The log shows:</p>

<pre><code>[["user_id", 4], ["name", "#&lt;Role::ActiveRecord_Associations_CollectionProxy:0x007f887ddb9c98&gt;"], ["resource_type", "Organisation"], ["resource_id", 1]]
</code></pre>

<p>The role shouldn't be an array. It should be a specific role. So, my next guess is to try adding the role to the link in my users index, which is now:</p>

<pre><code>&lt;%= link_to 'Remove role', assign_role_path(user, role),  method: :delete, data: { confirm: 'Are you sure?' } %&gt;
</code></pre>

<p>Then, I need ideas for how I can rewrite the assigned_role line in my destroy action so that it knows which role I am trying to un-assign from the user.</p>

<p>I don't have any good ideas for how to do that.</p>

<pre><code># role = AppRole.find(params[:roles])
assigned_role = user.role
# user_roles = user.roles
</code></pre>

<p>Any ideas that might help push this thought forward?</p>