Set a cookie read a cookie! How to (gently) collect user emails in a Rails application without Devise or requiring a password.
Tags: rails, webdev, modal, popup
Published: 2019-03-18 13:58:42

---
title: Set a cookie read a cookie! How to (gently) collect user emails in a Rails application without Devise or requiring a password.
published: true
description: 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 use. And how could I remember they had visited and not ask for their email address again?
tags: rubyonrails, webdevelopment, modal, popup
---
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!