Alex Sears

Persisting Guest Users in Rails with Devise

Introduction

user

Alex Sears

Developer. Drummer. Ginger.


article

Persisting Guest Users in Rails with Devise

Posted by Alex Sears on .
Featured

article

Persisting Guest Users in Rails with Devise

Posted by Alex Sears on .

Some sites allow guest users to still interact with it. Kandid.ly allows you to like a photo, for example. CodePen and JSFiddle allow you to create pieces of code without having an account. Devise by default doesn’t have anything in it for having guest users on your site. Adding this functionality is not a difficult thing to do.

In fact, the Devise wiki has an article on how to do this exact thing! However, that article talks about how to persist that user only for the current session. This session goes away when the user closes their browser. What if we want to persist those sessions for longer than the current session? We use a cookie!

The Code

module AuthorizationHelper

  # if user is logged in, return current_user, else return guest_user
  def current_or_guest_user
    if current_user
      if cookies.signed[:guest_user_email]
        logging_in
        guest_user.delete
        cookies.delete :guest_user_email
      end
      current_user
    else
      guest_user
    end
  end

  # find guest_user object associated with the current session,
  # creating one as needed
  def guest_user
    # Cache the value the first time it's gotten.
    @cached_guest_user ||=
      User.find_by!(email: (cookies.permanent.signed[:guest_user_email] ||= create_guest_user.email))

  # if cookies.signed[:guest_user_email] invalid
  rescue ActiveRecord::RecordNotFound #
    cookies.delete :guest_user_email
    guest_user
  end

  private

  # called (once) when the user logs in, insert any code your application needs
  # to hand off from guest_user to current_user.
  def logging_in
    # put all your processing for transferring
    # from a guest user to a registered user
    # i.e. update votes, update comments, etc.
  end

  # creates guest user by adding a record to the DB
  # with a guest name and email
  def create_guest_user
    u = User.create(:first_name => "guest", :email => "guest_#{Time.now.to_i}#{rand(99)}@example.com")
    u.skip_confirmation!
    u.save!(:validate => false)
    u
  end

end

If you put this in the app/helpers, the method current_or_guest_user will be available in your views. If you want to use it in your controller, you need to include the module in your application controller like the code below.

class ApplicationController < ActionController::Base
  include AuthorizationHelper

  # ...the rest of your code

I tried to comment the code to explain it a bit. Here’s a quick explanation of whats going on. current_or_guest_user will check if current_user exists. If not, it fires the guest_user method. This method checks if the user has been cached on the controller yet. If so, it returns that. If not, it gets the user email from a signed cookie. If the cookie doesn’t exist, then we create a new user using the create_guest_user and save their email in the cookie. Then we cache that user in case we try to get the guest user again in this request.

If the user is signed in, then we check if the cookie exists. If not, we just return the current user. If the cookie does exist, it fires the logging_in method, deletes the guest user entry in the database, deletes the cookie, and then returns the current user. The logging_in method is an important one. This is where you write your logic to transfer all of the guest user values over to the currently logged in user. For example, if the guest user had liked a bunch of photos on your site, you would want to move all those likes to the current user. It’s just that easy!

Wrapping Up

Adding guest users can be very simple using the Devise gem for Rails. The code Devise gives on its wiki is good, but their implementation only allows for same session persistence. Sometimes we need a bit more! Let me know what you think in the comments!

user

Alex Sears

http://alexsears.com

Developer. Drummer. Ginger.