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:
#Gemfile ... 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.
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.
rails generate devise_invitable User
This will add the :invitable
flag to your model, thus your User model will look like this:
#app/models/user.rb class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :invitable, :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable end
Running the above command also generated a migration file that looks like what I have below:
class DeviseInvitableAddToUsers < ActiveRecord::Migration def up change_table :users do |t| t.string :invitation_token t.datetime :invitation_created_at t.datetime :invitation_sent_at t.datetime :invitation_accepted_at t.integer :invitation_limit t.references :invited_by, polymorphic: true t.integer :invitations_count, default: 0 t.index :invitations_count t.index :invitation_token, unique: true # for invitable t.index :invited_by_id end end def down change_table :users do |t| t.remove_references :invited_by, polymorphic: true t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at end end 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:
#app/controllers/application_controller.rb class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters added_attrs = [:username, :email, :password, :password_confirmation, :remember_me] devise_parameter_sanitizer.permit :sign_up, keys: added_attrs devise_parameter_sanitizer.permit :account_update, keys: added_attrs devise_parameter_sanitizer.permit :accept_invitation, keys: [:email] end 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] pry(main)> User.invite!(:email => "[email protected]")
It should produce the output that looks like what I have below, though there will be differences.
[2] pry(main)> User Load (78.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? ORDER BY "users"."id" ASC LIMIT 1 [["email", "[email protected]"]] User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."invitation_token" = ? ORDER BY "users"."id" ASC LIMIT 1 [["invitation_token", "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92"]] (0.1ms) begin transaction SQL (67.8ms) INSERT INTO "users" ("email", "encrypted_password", "invitation_token", "invitation_created_at", "invitation_sent_at", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["email", "[email protected]"], ["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"]] (220.5ms) commit transaction 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) 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) Devise::Mailer#invitation_instructions: processed outbound mail in 247.1ms Sent mail to [email protected] (74.3ms) Date: Fri, 07 Oct 2016 08:41:51 +0100 From: [email protected] Reply-To: [email protected] To: [email protected] Message-ID: <[email protected]> Subject: Invitation instructions Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="--==_mimepart_57f751bfcc725_18022ac6c272b12840524"; charset=UTF-8 Content-Transfer-Encoding: 7bit ----==_mimepart_57f751bfcc725_18022ac6c272b12840524 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Hello [email protected] Someone has invited you to http://localhost:3000/, you can accept it through the link below. http://localhost:3000/users/invitation/accept?invitation_token=xmW9uRfyafptmeFMmFBy If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password. ----==_mimepart_57f751bfcc725_18022ac6c272b12840524 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit <p>Hello [email protected]</p> <p>Someone has invited you to http://localhost:3000/, you can accept it through the link below.</p> <p><a href="http://localhost:3000/users/invitation/accept?invitation_token=xmW9uRfyafptmeFMmFBy">Accept invitation</a></p> <p>If you don't want to accept the invitation, please ignore this email.<br /> Your account won't be created until you access the link above and set your password.</p> ----==_mimepart_57f751bfcc725_18022ac6c272b12840524-- => #<User:0x00558d875fa798 id: 4, email: "[email protected]", encrypted_password: "$2a$11$0sLfqvfFDsebcmcQTUXzlOuqNIooL5z8niXeza8OUwNK3gZY/iRum", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00, updated_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00, username: nil, invitation_token: "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92", invitation_created_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00, invitation_sent_at: Fri, 07 Oct 2016 07:41:51 UTC +00:00, invitation_accepted_at: nil, invitation_limit: nil, invited_by_id: nil, invited_by_type: nil, invitations_count: 0> [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:
#app/views/shared/_navigation.html.erb <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <%= link_to 'Tutsplus Devise', root_path, class: 'navbar-brand' %> </div> <div id="navbar"> <ul class="nav navbar-nav"> <li><%= link_to 'Home', root_path %></li> </ul> <ul class="nav navbar-nav pull-right"> <% if user_signed_in? %> <li class="dropdown"> <a class="dropdown-toggle" data-toggle="dropdown" href="#"> <%= current_user.username %> <span class="caret"></span> </a> <ul class="dropdown-menu" role="menu"> <li><%= link_to 'Invite', new_user_invitation_path %></li> <li><%= link_to 'Profile', edit_user_registration_path %></li> <li><%= link_to 'Log out', destroy_user_session_path, method: :delete %></li> </ul> </li> <% else %> <li><%= link_to 'Log In', new_user_session_path %></li> <li><%= link_to 'Sign Up', new_user_registration_path %></li> <% end %> </ul> </div> </div> </nav>
To see the routes made available by DeviseInvitable, run the command rake routes | invit
. Here is what the output will look like.
cancel_user_registration GET /users/cancel(.:format) devise_invitable/registrations#cancel user_registration POST /users(.:format) devise_invitable/registrations#create new_user_registration GET /users/sign_up(.:format) devise_invitable/registrations#new edit_user_registration GET /users/edit(.:format) devise_invitable/registrations#edit PATCH /users(.:format) devise_invitable/registrations#update PUT /users(.:format) devise_invitable/registrations#update DELETE /users(.:format) devise_invitable/registrations#destroy accept_user_invitation GET /users/invitation/accept(.:format) devise/invitations#edit remove_user_invitation GET /users/invitation/remove(.:format) devise/invitations#destroy user_invitation POST /users/invitation(.:format) devise/invitations#create new_user_invitation GET /users/invitation/new(.:format) devise/invitations#new PATCH /users/invitation(.:format) devise/invitations#update 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.
#config/environments/development.rb ... config.action_mailer.delivery_method = :letter_opener 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:
#config/routes.rb as :user do get 'signin' => 'devise/sessions#new' post 'signin' => 'devise/sessions#create' delete 'signout' => 'devise/sessions#destroy' 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.
Comments