ruby on rails - Devise Confirmable with Admin Invite -


working on updating legacy devise code (that doesn't use devise_invitable) , suffering inevitable headache.

the desired functionality stave off delivery of devise's confirmation email until admin invites given user, manually.

there's form on devise/registrations#new user inputs email, , admin backend, ultimately, users#index, there's "invite button".

it seem things have changed in recent years devise (app @ ~> 4.1.1) since legacy business logic in admin dashboard looks users not have confirmation_token present, , asks admin if want send users invite.

<%=(user.confirmation_token.nil? ?    (link_to "send invitation", invite_user_path(user.id), {:class => 'button radius tiny'}) :    (link_to "resend", invite_user_path(user.id), {:class => 'button radius tiny'})) unless user.confirmed_at.present? %> 

here user model, chock full of methods meant override default logic devise uses validate , create user.

class user < activerecord::base   ...   devise :database_authenticatable, :registerable,           :confirmable, :recoverable, :rememberable,           :trackable, :validatable    has_and_belongs_to_many :roles, :join_table => :roles_users    after_create :add_role    def password_required?    if !persisted?      !(password != "")    else      !password.nil? || !password_confirmation.nil?    end   end    # override devise method   def confirmation_required?    false   end    # override devise method   def active_for_authentication?    confirmed? || confirmation_period_valid?   end    def send_reset_password_instructions     if self.confirmed?       super     else      errors.add :base, "you must receive invitation before set password."     end   end    # new function set password   def attempt_set_password(params)     p = {}     p[:password] = params[:password]     p[:password_confirmation] = params[:password_confirmation]     update_attributes(p)   end    # new function determine whether password has been set   def has_no_password?     self.encrypted_password.blank?   end    # new function provide access protected method pending_any_confirmation   def only_if_unconfirmed     pending_any_confirmation {yield}   end    private    def add_role     return if roles.pluck(:name).include? "partner"      free = role.find_by_name "free"     roles << free     save!   end end 

after tinkering on prod, seem confirmation token being created immediately, in when user record created.

2016-06-06t21:49:19.283289+00:00 app[web.1]:      sql (27.9ms)       update `users`      set `created_at` = '2016-06-06 21:49:19.172495',          `updated_at` = '2016-06-06 21:49:19.172495',          `id` = 37,          `unconfirmed_email` = 'adminemail@gmail.com',          `confirmation_token` = 'bdyrkwzg31mptfaskesu',          `confirmation_sent_at` = '2016-06-06 21:49:19.252215'      `users`.`id` = 37 

interestingly, it's not 100% clear email actually being sent, want understand why confirmation token (and confirmation_sent_at) still being updated immediately. bug? new? or wrong?

including confirmationscontroller spaghetti code viewing pleasure:

class confirmationscontroller < devise::passwordscontroller    # remove first skip_before_filter (:require_no_authentication) if   # don't want enable logged users access confirmation page.   # skip_before_filter :require_no_authentication    skip_before_filter :authenticate_user!    # post /resource/confirmation   def create     self.resource = resource_class.send_confirmation_instructions(resource_params)     if successfully_sent?(resource)       respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))     else       respond_with(resource)     end   end    # put /resource/confirmation   def update     with_unconfirmed_confirmable       if @confirmable.has_no_password?         @confirmable.attempt_set_password(resource_params)         if @confirmable.valid?           @confirmable.update_attributes!(resource_params)           do_confirm         else           do_show           @confirmable.errors.clear #so won't render :new         end       else         self.class.add_error_on(self, :email, :password_allready_set)       end     end      if !@confirmable.errors.empty?       render 'devise/confirmations/new'     end   end    # /resource/confirmation?confirmation_token=abcdef   def show     with_unconfirmed_confirmable       if @confirmable.has_no_password?         do_show       else         do_confirm       end     end     if !@confirmable.errors.empty?       render 'devise/confirmations/new'     end   end    protected    def with_unconfirmed_confirmable     @confirmable = user.find_or_initialize_with_error_by(:confirmation_token, params[:confirmation_token])     self.resource = @confirmable     if !@confirmable.new_record?       @confirmable.only_if_unconfirmed {yield}     end   end    def do_show     @confirmation_token = params[:confirmation_token]     @requires_password = true     render 'devise/confirmations/show'   end    def do_confirm     @confirmable.confirm!     set_flash_message :notice, :confirmed     sign_in_and_redirect(resource_name, @confirmable)   end    # path used after resending confirmation instructions.   def after_resending_confirmation_instructions_path_for(resource_name)     new_session_path(resource_name)   end    # path used after confirmation.   def after_confirmation_path_for(resource_name, resource)     after_sign_in_path_for(resource)   end    private    def resource_params     params.require(:user).permit(:first_name, :last_name, :email, :encrypted_password, :reset_password_token, :reset_password_sent_at, :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip, :created_at, :updated_at, :confirmation_token, :confirmed_at, :confirmation_sent_at)   end end 

adding registrationscontroller too:

class users::registrationscontroller < devise::registrationscontroller   def new     super   end    def create     session[:email] = params[:user][:email] if params[:user][:email]      if user.exists?(email: params[:user][:email])       redirect_to :public_feed, flash: { notice: "welcome back." }     else       super     end   end end