Search code examples
ruby-on-railsdevisedevise-confirmable

Confirm Rails Devise user with checkbox


While administering users, I'd like the administrator to be able to confirm a user's account using a checkbox to trigger the user's devise user.confirm method.

I initially thought I'd be able to use attr_accessor to setup a variable called confirm_now to be used as a boolean on the user form, then in the controller update action (or by before/after action callback) evaluate the boolean and confirm the user accordingly.

I'm still not quite sure if I need methods in the model to set and read the attr_accessor variable, I'm still getting my head around that...or perhaps I'm over-complicating this and should call the user.confirm method when clicking save (is this bad practice? - I'm hesitant to add this field into the user_params).

Model:

class User < ApplicationRecord

  attr_accessor :confirm_now
...

Controller:

class UsersController < ApplicationController
...
  def update
    if params[:user][:password].blank?
      params[:user].delete(:password)
      params[:user].delete(:password_confirmation)
    end
    if [email protected]? && @user.confirm_now
      @user.confirm
    end
...
  private

    def user_params
      params.require(:user).permit(:first_name, :last_name, :email, :password, :account_active, :confirm_now)
    end

Form snippet (this evaluates and works as expected):

  <% if [email protected]? %>                   
    <%= f.input :confirm_now, label: 'Confirm User', as: :boolean, checked_value: true, unchecked_value: false %>
  <% end %>

Rails Log (the attr_accessor is set when checked on the form):

Processing by UsersController#update as HTML
  Parameters: {"authenticity_token"=>"...", "user"=>{"first_name"=>"gg", "last_name"=>"gg", "email"=>"[email protected]", "account_active"=>"1", "confirm_now"=>"true", "password"=>"[FILTERED]"}, "commit"=>"Save", "id"=>"45"}
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."first_name" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."first_name" ASC LIMIT $2  [["id", 45], ["LIMIT", 1]]
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."first_name" ASC LIMIT $2  [["id", 45], ["LIMIT", 1]]
Redirected to http://localhost:3000/users
Completed 302 Found in 6ms (ActiveRecord: 0.6ms | Allocations: 2665)

Thanks in advance!


Solution

  • To simplify I would make it a button, and pass the confirmed_at field directly.

    Parameter whitelisting:

    def user_params
      params.require(:user).permit(:first_name, :last_name, :email, :password, :account_active, :confirmed_at)
    end
    

    Form:

    <%=
      if @user.confirmed?
        link_to 'Unconfirm user',
                user_path(@user, { user: { confirmed_at: nil } }),
                method: :patch        
      else
        link_to 'Confirm user',
                user_path(@user, { user: { confirmed_at: Time.current } }),
                method: :patch
      end 
    %>
    

    Please adjust the paths/method types according to your routes.