Funneler is a simple gem developed to: group different pages from one or more apps into a cohesive flow.

At Doximity we want to ensure our users get the most out of the medical network they're joining. In order to make that happen, we provide a personalized onboarding experience for each user. Our customization spans the device the user is on (web, mobile web, and our mobile applications) as well as other variations such as taking into account how or when a user is registered. Of course accounting for all these variations is easier said than done.

Funneler provides us the simple tool we need to make that happen:

  1. It groups pages together via a funnel_token parameter
  2. It is light weight - meaning there is very little overhead to modifying a controller to use that parameter when present, or fallback to a default behavior otherwise
  3. The funnel_token is not dependent on any datastore
  4. And thus it can link pages across different applications
  5. It is simply a link, and therefore can easily be used in emails

Let's take a look at how to use funneler. For a moment, pretend your application has two pages you want your user to see /welcome, and /setup before landing at their final destination /home.

Naturally the simple solution would be to setup each controller/action to link to the next page. However, as you know that is brittle, and certainly can't be customized per user without a lot of code. This is exactly what funneler now provides:

funnel = ['/welcome', '/setup', '/home'])
redirect_to funnel.first_page

This will redirect to a route like /welcome?funnel_token=...... The funnel_token is a JWT token encoded with a few simple pieces of data:

  • routes - the array of routes to funnel a user through
  • current_page_index - the current index into the routes array

On subsequent requests, any controller can read the funnel_token and redirect to the next_page such as:

funnel = Funneler.from_token(token: params[:funnel_token])
redirect_to funnel.next_page

The funnel simply builds a new token which has an updated current_page_index, and returns the route at that index with the new funnel_token in the query string.

Of course this is a simple example with a static set of routes. However it shouldn't be hard to imagine customizing the behavior by dynamically generating the routes given different inputs and parameters. The best part about using funneler is that creates a clear separation between controller behavior, and the business logic determining which pages should be included in the funnel.

Funneler has been open sourced on GitHub - feel free to contribute!