require 'rack' require 'json' module Mixpanel class Middleware class << self attr_accessor :skip_request def skip_this_request @skip_request = true end end @skip_request = false def initialize(app, mixpanel_token, options={}) @app = app @token = mixpanel_token @options = { :insert_mixpanel_scripts=> true, :insert_js_last => false, :persist => false, :config => {} }.merge(options) end def call(env) @env = env @status, @headers, @response = @app.call(env) if is_trackable_response? && !Mixpanel::Middleware.skip_request merge_queue! if @options[:persist] update_response! update_content_length! delete_event_queue! end Mixpanel::Middleware.skip_request = false [@status, @headers, @response] end private def update_response! @response.each do |part| if is_regular_request? && is_html_response? insert_at = part.index(@options[:insert_js_last] ? ' new_size.to_s) end def is_regular_request? !is_ajax_request? && !is_turbolink_request? end def is_turbolink_request? @env.has_key?("HTTP_X_XHR_REFERER") end def is_ajax_request? @env.has_key?("HTTP_X_REQUESTED_WITH") && @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" end def is_html_response? @headers["Content-Type"].include?("text/html") if @headers.has_key?("Content-Type") end def is_javascript_response? @headers["Content-Type"].include?("text/javascript") if @headers.has_key?("Content-Type") end def is_trackable_response? return false if @status == 302 return false if @env.has_key?("HTTP_SKIP_MIXPANEL_MIDDLEWARE") is_html_response? || is_javascript_response? end def render_mixpanel_scripts <<-EOT EOT end def delete_event_queue! if @options[:persist] (@env['rack.session']).delete('mixpanel_events') else @env.delete('mixpanel_events') end end def queue if @options[:persist] return [] if !(@env['rack.session']).has_key?('mixpanel_events') || @env['rack.session']['mixpanel_events'].empty? @env['rack.session']['mixpanel_events'] else return [] if !@env.has_key?('mixpanel_events') || @env['mixpanel_events'].empty? @env['mixpanel_events'] end end def merge_queue! present_hash = {} special_events = ['identify', 'name_tag', 'people.set', 'register'] queue.uniq! queue.reverse_each do |item| is_special = special_events.include?(item[0]) if present_hash[item[0]] and is_special queue.delete(item) else present_hash[item[0]] = true if is_special end end end def render_event_tracking_scripts(include_script_tag=true) return "" if queue.empty? output = queue.map {|type, arguments| %(mixpanel.#{type}(#{arguments.join(', ')});) }.join("\n") output = "try {#{output}} catch(err) {};" include_script_tag ? "" : output end end end