Advertisement
Scroll to top
Read Time: 7 min
This post is part of a series called Exploring Devise.
Exploring Devise, Part 1

Introduction

In the first part of the tutorial, we learned how to install Devise and set it up in our Rails application. In this part, we will look at how to integrate DeviseInvitable.

DeviseInvitable is an extension that works with Devise. With DeviseInvitable in your application, your users can invite their friends via emails. This is a great feature to include in your application if you are building a collaboration app.

Setting Up DeviseInvitable

Open your Gemfile and add the gem:

1
#Gemfile
2
...
3
gem 'devise_invitable'

Run the command to install bundle install.

Run the generator command to add DeviseInvitable's configuration option to the Devise configuration file.

1
rails generate devise_invitable:install

You can see the new changes by checking out config/initializers/devise.rb with your text editor.

Next, let's add DeviseInvitable to our User model.

1
rails generate devise_invitable User

This will add the :invitable flag to your model, thus your User model will look like this:

1
#app/models/user.rb
2
3
class User < ActiveRecord::Base
4
  # Include default devise modules. Others available are:
5
  # :confirmable, :lockable, :timeoutable and :omniauthable
6
  devise :invitable, :database_authenticatable, :registerable,
7
         :recoverable, :rememberable, :trackable, :validatable
8
end

Running the above command also generated a migration file that looks like what I have below:

1
class DeviseInvitableAddToUsers < ActiveRecord::Migration
2
  def up
3
    change_table :users do |t|
4
      t.string     :invitation_token
5
      t.datetime   :invitation_created_at
6
      t.datetime   :invitation_sent_at
7
      t.datetime   :invitation_accepted_at
8
      t.integer    :invitation_limit
9
      t.references :invited_by, polymorphic: true
10
      t.integer    :invitations_count, default: 0
11
      t.index      :invitations_count
12
      t.index      :invitation_token, unique: true # for invitable
13
      t.index      :invited_by_id
14
    end
15
  end
16
17
  def down
18
    change_table :users do |t|
19
      t.remove_references :invited_by, polymorphic: true
20
      t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
21
    end
22
  end
23
end

Now migrate your database by running rake db:migrate.

Configuring the Controller for DeviseInvitable

DeviseInvitable is required to pass some parameters when sending an invite. For this to work, we need to whitelist the necessary parameter that will be used. Using your text editor, navigate to app/controllers/application_controller.rb and make yours look like what I have below:

1
#app/controllers/application_controller.rb
2
3
class ApplicationController < ActionController::Base
4
  # Prevent CSRF attacks by raising an exception.
5
  # For APIs, you may want to use :null_session instead.
6
  protect_from_forgery with: :exception
7
8
  before_action :configure_permitted_parameters, if: :devise_controller?
9
10
  protected
11
12
  def configure_permitted_parameters
13
    added_attrs = [:username, :email, :password, :password_confirmation, :remember_me]
14
    devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
15
    devise_parameter_sanitizer.permit :account_update, keys: added_attrs
16
    devise_parameter_sanitizer.permit :accept_invitation, keys: [:email]
17
  end
18
end

From the above, you can see that :email  has been whitelisted for DeviseInvitable.

Now let's see what we have via our console. On your terminal, run rails console and enter what you have below.

1
[1] pry(main)> User.invite!(:email => "johndoe@example.com")

It should produce the output that looks like what I have below, though there will be differences.

1
[2] pry(main)> User Load (78.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ?  ORDER BY "users"."id" ASC LIMIT 1  [["email", "johndoe@example.com"]]
2
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."invitation_token" = ?  ORDER BY "users"."id" ASC LIMIT 1  [["invitation_token", "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92"]]
3
   (0.1ms)  begin transaction
4
  SQL (67.8ms)  INSERT INTO "users" ("email", "encrypted_password", "invitation_token", "invitation_created_at", "invitation_sent_at", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?)  [["email", "johndoe@example.com"], ["encrypted_password", "$2a$11$0sLfqvfFDsebcmcQTUXzlOuqNIooL5z8niXeza8OUwNK3gZY/iRum"], ["invitation_token", "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92"], ["invitation_created_at", "2016-10-07 07:41:51.254047"], ["invitation_sent_at", "2016-10-07 07:41:51.254047"], ["created_at", "2016-10-07 07:41:51.255700"], ["updated_at", "2016-10-07 07:41:51.255700"]]
5
   (220.5ms)  commit transaction
6
  Rendered /home/kinsomicrote/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/devise_invitable-1.7.0/app/views/devise/mailer/invitation_instructions.html.erb (2.5ms)
7
  Rendered /home/kinsomicrote/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/devise_invitable-1.7.0/app/views/devise/mailer/invitation_instructions.text.erb (88.0ms)
8
9
Devise::Mailer#invitation_instructions: processed outbound mail in 247.1ms
10
11
Sent mail to johndoe@example.com (74.3ms)
12
Date: Fri, 07 Oct 2016 08:41:51 +0100
13
From: please-change-me-at-config-initializers-devise@example.com
14
Reply-To: please-change-me-at-config-initializers-devise@example.com
15
To: johndoe@example.com
16
Message-ID: <57f751bfce8d6_18022ac6c272b12840661@kinsomicrote-X553MA.mail>
17
Subject: Invitation instructions
18
Mime-Version: 1.0
19
Content-Type: multipart/alternative;
20
 boundary="--==_mimepart_57f751bfcc725_18022ac6c272b12840524";
21
 charset=UTF-8
22
Content-Transfer-Encoding: 7bit
23
24
25
----==_mimepart_57f751bfcc725_18022ac6c272b12840524
26
Content-Type: text/plain;
27
 charset=UTF-8
28
Content-Transfer-Encoding: 7bit
29
30
Hello johndoe@example.com
31
32
Someone has invited you to https://localhost:3000/, you can accept it through the link below.
33
34
http://localhost:3000/users/invitation/accept?invitation_token=xmW9uRfyafptmeFMmFBy
35
36
37
If you don't want to accept the invitation, please ignore this email.

38
Your account won't be created until you access the link above and set your password.
39
40
----==_mimepart_57f751bfcc725_18022ac6c272b12840524
41
Content-Type: text/html;
42
 charset=UTF-8
43
Content-Transfer-Encoding: 7bit
44
45
<p>Hello johndoe@example.com</p>
46
47
<p>Someone has invited you to http://localhost:3000/, you can accept it through the link below.</p>
48
49
<p><a href="http://localhost:3000/users/invitation/accept?invitation_token=xmW9uRfyafptmeFMmFBy">Accept invitation</a></p>
50
51
52
<p>If you don't want to accept the invitation, please ignore this email.<br />

53
Your account won't be created until you access the link above and set your password.</p>
54
55
----==_mimepart_57f751bfcc725_18022ac6c272b12840524--
56
57
=> #<User:0x00558d875fa798

58
 id: 4,
59
 email: "johndoe@example.com",
60
 encrypted_password: "$2a$11$0sLfqvfFDsebcmcQTUXzlOuqNIooL5z8niXeza8OUwNK3gZY/iRum",
61
 reset_password_token: nil,
62
 reset_password_sent_at: nil,
63
 remember_created_at: nil,
64
 sign_in_count: 0,
65
 current_sign_in_at: nil,
66
 last_sign_in_at: nil,
67
 current_sign_in_ip: nil,
68
 last_sign_in_ip: nil,
69
 created_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00,
70
 updated_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00,
71
 username: nil,
72
 invitation_token: "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92",
73
 invitation_created_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00,
74
 invitation_sent_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00,
75
 invitation_accepted_at: nil,
76
 invitation_limit: nil,
77
 invited_by_id: nil,
78
 invited_by_type: nil,
79
 invitations_count: 0>
80
[3] pry(main)> 

That worked as planned.

You do not want our users to send invitations via the command line, so it is important we set up DeviseInvitable to work on the front end. Doing this is very simple; run the generator command to generate the views for DeviseInvitable.

rails generate devise_invitable:views users

You will also need to add a link somewhere in your application that points to the page for sending invites (app/views/users/invitations/new.html.erb).

For this application, you can go ahead and add the link to your navigation file. Here is how I did mine:

1
#app/views/shared/_navigation.html.erb
2
3
<nav class="navbar navbar-inverse">
4
  <div class="container">
5
    <div class="navbar-header">
6
      <%= link_to 'Tutsplus Devise', root_path, class: 'navbar-brand' %>
7
    </div>
8
    <div id="navbar">
9
      <ul class="nav navbar-nav">
10
        <li><%= link_to 'Home', root_path %></li>
11
      </ul>
12
      <ul class="nav navbar-nav pull-right">
13
        <% if user_signed_in? %>
14
          <li class="dropdown">
15
            <a class="dropdown-toggle" data-toggle="dropdown" href="#">
16
              <%= current_user.username %>
17
              <span class="caret"></span>
18
            </a>
19
            <ul class="dropdown-menu" role="menu">
20
              <li><%= link_to 'Invite', new_user_invitation_path %></li>
21
              <li><%= link_to 'Profile', edit_user_registration_path %></li>
22
              <li><%= link_to 'Log out', destroy_user_session_path, method: :delete %></li>
23
            </ul>
24
          </li>
25
        <% else %>
26
          <li><%= link_to 'Log In', new_user_session_path %></li>
27
          <li><%= link_to 'Sign Up', new_user_registration_path %></li>
28
        <% end %>
29
      </ul>
30
    </div>
31
  </div>
32
</nav>

To see the routes made available by DeviseInvitable, run the command rake routes | invit. Here is what the output will look like.

1
cancel_user_registration GET    /users/cancel(.:format)            devise_invitable/registrations#cancel
2
       user_registration POST   /users(.:format)                   devise_invitable/registrations#create
3
   new_user_registration GET    /users/sign_up(.:format)           devise_invitable/registrations#new
4
  edit_user_registration GET    /users/edit(.:format)              devise_invitable/registrations#edit
5
                         PATCH  /users(.:format)                   devise_invitable/registrations#update
6
                         PUT    /users(.:format)                   devise_invitable/registrations#update
7
                         DELETE /users(.:format)                   devise_invitable/registrations#destroy
8
  accept_user_invitation GET    /users/invitation/accept(.:format) devise/invitations#edit
9
  remove_user_invitation GET    /users/invitation/remove(.:format) devise/invitations#destroy
10
         user_invitation POST   /users/invitation(.:format)        devise/invitations#create
11
     new_user_invitation GET    /users/invitation/new(.:format)    devise/invitations#new
12
                         PATCH  /users/invitation(.:format)        devise/invitations#update
13
                         PUT    /users/invitation(.:format)        devise/invitations#update

Let us see what we have at this moment. Run the command to start your server; rails server.

Point your browser to http://localhost:3000/users/invitation/new. Enter an email address in the form shown, and click on the button. That should work! If you go to the logs of your server, you should see an output that was created when you sent the invite. In the output, you will see a link to accept the invite.

You will agree with me that it will be better if you can view the email sent in your browser. Let us see how to make that work.

Integrating Letter_Opener

Letter Opener allows you preview emails in your default browser. With it, you do not have to set up a mail delivery system while working in the development environment.

Open your Gemfile and add the gem below:

gem 'letter_opener'

Run bundle install.

Using your text editor, navigate to config/environments/development.rb and add the line below.

1
#config/environments/development.rb
2
...
3
  config.action_mailer.delivery_method = :letter_opener
4
end

Restart your rails server. Now point your browser to http://localhost:3000/users/invitation/new. Fill and submit the form displayed. This time, a new page pops up containing the invite email.

Change Default Sign In and Sign Out Routes

By default, the sign_in and sign_out routes when using Devise look like this:

sign_in: http://localhost:3000/users/sign_in

sign_out: http://localhost:3000/users/sign_out

To change it, go to config/routes.rb and add the following:

1
#config/routes.rb
2
  as :user do
3
    get 'signin' => 'devise/sessions#new'
4
    post 'signin' => 'devise/sessions#create'
5
    delete 'signout' => 'devise/sessions#destroy'
6
  end

You can point your browser to http://localhost:3000/signin.

Conclusion

Now you know how to make use of DeviseInvitable. You also learned about the gem letter_opener. There are lots of things you can do with Devise, so check out the Wiki to learn more. Thanks for staying with me.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.