zevarito / mixpanel

Simple lib to track events in Mixpanel service. It can be used in any rack based framework.
MIT License
273 stars 84 forks source link

Important News

This Gem will not be maintained anymore, there is an Official gem being developed and we encourage you to use that gem from now on.

We will merge PR for bugs for a little period of time, but no new features will be added.

Official Gem repository

Needed updates

Upgrade to version 4.1.0 to avoid XSS Vulnerability

Table of Contents

What is Mixpanel

Mixpanel is a real-time analytics service that helps companies understand how users interact with web applications. http://mixpanel.com

What does this Gem do?

Install

  gem install mixpanel

Rack Middleware

Only needed if you want to track events via Javascript. This setup will allow your backend to have the client browser process the actual requests over JavaScript rather than sending the request yourself.

If you are using Rails you can add this to your specific environment configuration file (located in config/environments/) or create a new initializer for it:

  config.middleware.use "Mixpanel::Middleware", "YOUR_MIXPANEL_API_TOKEN", options

Where options is a hash that accepts the following keys:

Usage

Initialize Mixpanel

  @mixpanel = Mixpanel::Tracker.new YOUR_MIXPANEL_API_TOKEN, options

Where options is a hash that accepts the following keys:

Track Events Directly

  @mixpanel.track event_name, properties, options

event_name is a string denoting how you want this event to appear in your Mixpanel dashboard.

properties is a hash of properties to be associated with the event. The keys in the properties can either be strings or symbols. If you send in a key that matches a special property, it will automatically be converted to the correct form (e.g., { :os => 'Mac' } will be converted to { :$os => 'Mac' }).

options is a hash that accepts the following keys:

Example:

@mixpanel.track 'Purchased credits', { :number => 5, 'First Time Buyer' => true }

If you would like to alias one distinct id to another, you can use the alias helper method:

@mixpanel.alias 'Siddhartha', { distinct_id: previous_distinct_id }

Pixel Based Event Tracking

@mixpanel.tracking_pixel "Opened Email", { :distinct_id => "bob@email.com", :campaign => "Retarget" }

This allows to track events just by loading a pixel. It's usually useful for tracking opened emails. You've got to specify your own distinct_id as it won't be able to retrieve it from cookies.

And you can use it in your views with an image_tag helper:

image_tag @mixpanel.tracking_pixel("Opened Email", { :distinct_id => "bob@email.com", :campaign => "Retarget" }), :width => 1, :height => 1

Mixpanel docs: https://mixpanel.com/docs/api-documentation/pixel-based-event-tracking

Redirect Based Event Tracking

@mixpanel.redirect_url "Opened Email", 'http://www.example.com/' { :distinct_id => "bob@email.com", :campaign => "Retarget" }

This allows to track events just when a user clicks a link. It's usually useful for tracking opened emails.

Import Events

  @mixpanel.import event_name, properties, options

All of these options have the same meaning and same defaults as the track method, except that the default url is http://api.mixpanel.com/import/

Example:

@mixpanel.import 'Purchased credits', { :number => 4, :time => 5.weeks.ago }, { :api_key => MY_API_KEY}

Set Person Attributes Directly

@mixpanel.set distinct_id_or_request_properties, properties, options

distinct_id_or_request_properties is whatever is used to identify the user to Mixpanel or a hash of properties of the engage event that exist outside of the $set. Special properties will be automatically converted to the correct form (e.g., { :ip => '127.0.0.1' } will be converted to { :$ip => '127.0.0.1' }

properties is a hash of properties to be set. The keys in the properties can either be strings or symbols. If you send in a key that matches a special property, it will automatically be converted to the correct form (e.g., { :first_name => 'Chris' } will be converted to { :$first_name => 'Chris' }).

options is a hash that accepts the following keys:

Example using distinct_id to identify the user:

@mixpanel.set 'john-doe', { :age => 31, :email => 'john@doe.com' }

Example using request properties, telling mixpanel to ignore the time:

@mixpanel.set({ :distinct_id => 'john-doe', :ignore_time => true }, { :age => 31, :email => 'john@doe.com' })

Increment Person Attributes Directly

@mixpanel.increment distinct_id, properties, options

All of these options have the same meaning and same defaults as the set method. Note that according to Mixpanel's docs, you cannot combine set and increment requests, and that is why they are split here.

Example:

@mixpanel.increment 'john-doe', { :tokens => 5, :coins => -4 }

Track Charges for Revenue Directly

@mixpanel.track_charge distinct_id, amount, time, options

This allows you to use the Revenue tab in your mixpanel dashboard.

Example:

@mixpanel.track_charge 'john-doe', 20.00

If you need to remove accidental charges for a person, you can use:

@mixpanel.reset_charges distinct_id

Append Events To Be Tracked With Javascript

Note: You should setup the Rack Middleware.

  @mixpanel.append_track event_name, properties

event_name and properties take the same form as tracking the event directly.

Note that you must call mixpanel.identify() in conjunction with People requests like set(). If you make set() requests before you identify the user, the change will not be immediately sent to Mixpanel. Mixpanel will wait for you to call identify() and then send the accumulated changes.

  @mixpanel.append_identify distinct_id
  @mixpanel.append_set properties

Execute Javascript API call

Note: You should setup the Rack Middleware.

  @mixpanel.append("register", {:some => "property"})
  @mixpanel.append("identify", "Unique Identifier")

Give people real names with Javascript

Note: You should setup the Rack Middleware.

This gives names to people tracked in the Stream view:

  @mixpanel.append("name_tag", "John Doe")

Prevent middleware from inserting code

Note: Only applies when Rack Middleware is setup.

Occasionally you may need to send a request for HTML that you don't want the middleware to alter. In your AJAX request include the header "SKIP_MIXPANEL_MIDDLEWARE" to prevent the mixpanel code from being inserted.

  $.ajax("/path/to/api/endpoint", {
    headers: {"Skip-Mixpanel-Middleware": true}, // valid http headers don't allow underscores and get filtered by some webservers
    success: function(data) {
      // Process data here
    }
  });

Alternatively, you can add this line of code to your controller to temporarily disable the middleware:

   Mixpanel::Middleware.skip_this_request

Examples

How to use it from Rails controllers?

In your ApplicationController class add a method to keep track of a Mixpanel instance.

  def mixpanel
    @mixpanel ||= Mixpanel::Tracker.new YOUR_MIXPANEL_API_TOKEN, { :env => request.env }
  end

Then you can call against this method where it makes sense in your controller. For example, in the users#create method:

  def create
    @user = User.create( :name => 'Jane Doe', :gender => 'female', :mixpanel_identifer => 'asdf' )
    mixpanel.track 'User Created', {
      :gender => @user.gender,
      :distinct_id => @user.mixpanel_identifier,
      :time => @user.created_at
    } # Note that passing the time key overwrites the default value of Time.now

    mixpanel.set @user.mixpanel_identifer, { :gender => @user.gender, :created => @user.created_at, :name => @user.name }
  end

How to track events using Resque and Rails

While there is built-in async functionality, other options are more robust (e.g., using a dedicated queue manager). Below is an example of how this might be done with Resque, but the same concepts would apply no matter what queue manager you use.

class MixpanelTrackEventJob
  @queue = :slow

  def self.mixpanel env
    Mixpanel::Tracker.new MIXPANEL_TOKEN, { :env => env }
  end

  def self.perform name, properties, env
    mixpanel(env).track name, properties
  end
end
  class UsersController < ApplicationController
    def create
      @user = User.new(params[:user])

      if @user.save
        env = {
          'REMOTE_ADDR' => request.env['REMOTE_ADDR'],
          'HTTP_X_FORWARDED_FOR' => request.env['HTTP_X_FORWARDED_FOR'],
          'rack.session' => request.env['rack.session'],
          'mixpanel_events' => request.env['mixpanel_events']
        } # Trying to pass request.env to Resque is going to fail (it chokes when trying to conver it to JSON, but no worries...)

        Resque.enqueue MixpanelTrackEventJob, 'Sign up', { :invited => params[:invited] }, env

        redirect_to user_root_path
      else
        render :new
      end
    end
  end

How to track events using Delayed Job and Rails

Below is an example of implementing async even tracking with Delayed Job

Create a new worker

class MixpanelWorker < Struct.new(:name, :properties, :request_env)
  def perform
      if defined?(MIXPANEL_TOKEN)
        @mixpanel = Mixpanel::Tracker.new(MIXPANEL_TOKEN, { :env => request_env })
      else
        @mixpanel = DummyMixpanel.new
      end

      @mixpanel.track(name, properties)
  end
end

Add the following to your Application controller

class ApplicationController < ActionController::Base
  before_filter :initialize_env

  private
  ##
  # Initialize env for mixpanel
  def initialize_env
    # Similar to the Resque problem above, we need to help DJ serialize the
    # request object.
    @request_env = {
      'REMOTE_ADDR' => request.env['REMOTE_ADDR'],
      'HTTP_X_FORWARDED_FOR' => request.env['HTTP_X_FORWARDED_FOR'],
      'rack.session' => request.env['rack.session'].to_hash,
      'mixpanel_events' => request.env['mixpanel_events']
    }
  end

You can optionally create a nice model wrapper to tidy things up

#app/models/mix_panel.rb
class MixPanel
  def self.track(name, properties, env)
    # Notice we are using the 'mixpanel' queue
        Delayed::Job.enqueue MixpanelWorker.new(name, properties, env), queue: 'mixpanel'
    end
end

Sample Usage

MixPanel.track("Front Page Load", {
                url_type: short_url.uid_type,
                page_name: short_url.page.name,
                distinct_id: @client_uid }, @request_env)

Supported Ruby Platforms

Deprecation Notes

Collaborators and Maintainers