DeviseInvitable - thuy-econsys/rails_app GitHub Wiki

Install devise_invitable gem and run bundle install.

Run rails generate devise_invitable:install to insert DeviseInvitable configurations into the Devise configuration file as well as create a yaml internationalization file for DeviseInvitable.

Run rails generate devise_invitable User to auto-generate a DeviseInvitable User model and migration file to add DeviseInvitable to User model.

Or, manually spin up your own migration and add the :invitable flag to your User model devise modules because that's what the generator basically does.

rails g migration AddColumnsToUsers invitation_token:string:uniq invitation_created_at:datetime invitation_sent_at:datetime invitation_accepted_at:datetime invitation_limit:integer invited_by_id:integer invited_by_type:string

...should give you a migration file to run rails db:migrate and update your schema:

class AddColumnsToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :invitation_token, :string
    add_column :users, :invitation_created_at, :datetime
    add_column :users, :invitation_sent_at, :datetime
    add_column :users, :invitation_accepted_at, :datetime
    add_column :users, :invitation_limit, :integer
    add_column :users, :invited_by_id, :integer
    add_column :users, :invited_by_type, :string
    add_index :users, :invitation_token, unique: true
  end
end

Here are the routes created for DeviseInvitable:

                  Prefix Verb   URI Pattern                        Controller#Action
cancel_user_registration GET    /users/cancel(.:format)            devise_invitable/registrations#cancel
   new_user_registration GET    /users/sign_up(.:format)           devise_invitable/registrations#new
  edit_user_registration GET    /users/edit(.:format)              devise_invitable/registrations#edit
       user_registration PATCH  /users(.:format)                   devise_invitable/registrations#update
                         PUT    /users(.:format)                   devise_invitable/registrations#update
                         DELETE /users(.:format)                   devise_invitable/registrations#destroy
                         POST   /users(.:format)                   devise_invitable/registrations#create

  accept_user_invitation GET    /users/invitation/accept(.:format) devise/invitations#edit
  remove_user_invitation GET    /users/invitation/remove(.:format) devise/invitations#destroy

     new_user_invitation GET    /users/invitation/new(.:format)    devise/invitations#new
         user_invitation PATCH  /users/invitation(.:format)        devise/invitations#update
                         PUT    /users/invitation(.:format)        devise/invitations#update
                         POST   /users/invitation(.:format)        devise/invitations#create

Inside the rails console, run User.invite!(:email => "[email protected]") to test out DeviseInvitable

This will generate a simulation of the Devise::Mailer#invitation_instructions process. Inside the email body, there will be a link, http://localhost:3000/users/invitation/accept?invitation_token=<random_token_here> which will take you to a page for setting a password for the user.

In the rails server, you can follow along the routes that the process takes:

HTTP                                                     Controller#action                      Rendered View paths 
GET "/users/sign_in"                                     Devise::SessionsController#new         devise/shared/_links.html.erb
                                                                                                devise/sessions/new.html.erb within layouts/application

GET "/users/invitation/accept?invitation_token=<token>   Devise::InvitationsController#edit     devise/invitations/edit.html.erb
  Parameters: {"invitation_token"=>"<token>"}

PUT "/users/invitation"                                  Devise::InvitationsController#update   devise/invitations/edit.html.erb
                                                                                                Redirected to http://localhost:3000/  
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"<auth_token>", "user"=>{"invitation_token"=>"<token>", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Set my password"}

Note that for the HTTP PUT action, there are 2 updates:

  User Update (0.6ms)  UPDATE "users" SET "invitation_token" = $1, "encrypted_password" = $2, "invitation_accepted_at" = $3, "updated_at" = $4 WHERE "users"."id" = $5  
  [["invitation_token", nil], ["encrypted_password", "<hash>"], ["invitation_accepted_at", "2020-10-05 22:26:26.373091"], ["updated_at", "2020-10-05 22:26:26.374717"], ["id", 74]]

  User Update (0.6ms)  UPDATE "users" SET "sign_in_count" = $1, "current_sign_in_at" = $2, "last_sign_in_at" = $3, "current_sign_in_ip" = $4, "last_sign_in_ip" = $5, "updated_at" = $6 WHERE "users"."id" = $7  
  [["sign_in_count", 1], ["current_sign_in_at", "2020-10-05 22:26:26.380368"], ["last_sign_in_at", "2020-10-05 22:26:26.380368"], ["current_sign_in_ip", "::1/128"], ["last_sign_in_ip", "::1/128"], ["updated_at", "2020-10-05 22:26:26.381536"], ["id", 74]]

Those are attributes that you can access in the rails console, if you're interested:

2.5.1 :033 > User.last.last_sign_in_ip
  User Load (0.6ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
 => #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff> 

DB

seed some data but skip the invitation process, u.skip_invitation = true:

    def adduser(email, password)
      @user = User.invite!(:email => email) do |u|
        u.skip_invitation = true
      end
      token = Devise::VERSION >= "3.1.0" ? @user.instance_variable_get(:@raw_invitation_token) : @user.invitation_token
      User.accept_invitation!(:invitation_token => token, :password => password, :password_confirmation => password)

      puts "Created User #{email} with password #{password}"
      @user
    end
⚠️ **GitHub.com Fallback** ⚠️