DEV Community

Colleen
Colleen

Posted on

Set a cookie read a cookie! How to (gently) collect user emails in a Rails application without Devise or requiring a password.

I was asked by a client to "soft-gate" user emails. The client wanted a list of emails from people using the application, but didn't want to discourage anyone from use. This presented a problem - how could I ask users' for their emails without preventing them from using the app (or pissing them off so much they closed the window)? And how could I remember they had visited and not ask for their email address again?

First things first - this solution is in no way even remotely secure. Devise has a "soft" sign up feature that allows users to start with entering only their email address (and then adding a password later) that you may want to check out if you're looking for real user authentication. In this case I was only trying to amass email addresses for a mailing list.

Question:

How to collect email addresses of users without being too annoying or preventing them from using the app?

Problem:

User state must be saved somehow, because you don't want the app requesting an email address every time the user visits the site.

Solution:

Save a cookie on the user's browser, and read the cookie to determine if the user has already visited the site. If they have, let them through. If they haven't, show a pop-up modal asking for their email address, but allow them to click anywhere on the page to close the modal and still use the site.

Step 1: Save a cookie

In the create method in the users controller add
cookies[:user_email] = @user.email if the user successfully saves.

Step 2: Create a modal

I'm lazy, this is almost directly from the bootstrap docs. My modal view renders my users#new form. I send in the locals as User.new because form_for user needs a user. Usually this is automagically done for you if the form is accessed via the create action.:

<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <%= render partial: 'users/form', locals: { user: User.new } %>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div> 
    </div>
  </div>
</div>

Step 3: Check for a cookie

So, the bootstrap modal is designed to be triggered by a button. This is straight from the docs. I want to trigger it ONLY if the cookie isn't set. This tripped me up at first, I tried accessing cookies[:user_email] in the view. No dice. The correct way:

In the root controller index (the homepage) I added this method:
(the index action of the root controller)

  def index
    @user_email = cookies[:user_email]
  end

Then in the view I checked for my cookie:
layouts/application.html.haml (or erb whatever you're into)

- if !@user_email
  #render-modal
  = render 'modals/user_email.html.erb'

So, @user_email is set in the index method. If @user_email does not exist (i.e. the cookie has not been created), I create a div with an id of render-modal.

Step 4: Show the modal

Quick jquery snippet to show the modal if the div created above exists:
app/assets/javascripts/modals.js

$(document).ready(function() {
  if ($('#render-modal').length) {
    $('#myModal').modal({show:true});
  }
});

There you have it! A quick way to render a pop-up modal if the user hasn't visited your site before. Now start collecting email addresses and sending great content to your users!

Top comments (0)