lib/rack/tracker.rb
require "rack"
require "tilt"
require "active_support/core_ext/class/attribute"
require "active_support/core_ext/hash"
require "active_support/json"
require "active_support/inflector"
require "rack/tracker/version"
require "rack/tracker/extensions"
require "rack/tracker/javascript_helper"
require 'rack/tracker/railtie' if defined?(Rails)
require "rack/tracker/handler"
require "rack/tracker/handler_delegator"
require "rack/tracker/controller"
require "rack/tracker/google_analytics/google_analytics"
require "rack/tracker/google_global/google_global"
require "rack/tracker/google_tag_manager/google_tag_manager"
require "rack/tracker/google_adwords_conversion/google_adwords_conversion"
require "rack/tracker/facebook/facebook"
require "rack/tracker/facebook_pixel/facebook_pixel"
require "rack/tracker/vwo/vwo"
require "rack/tracker/go_squared/go_squared"
require "rack/tracker/criteo/criteo"
require "rack/tracker/zanox/zanox"
require "rack/tracker/hotjar/hotjar"
require "rack/tracker/bing/bing"
require "rack/tracker/hubspot/hubspot"
require "rack/tracker/drift/drift"
require "rack/tracker/heap/heap"
module Rack
class Tracker
EVENT_TRACKING_KEY = 'tracker'
def initialize(app, &block)
@app = app
@handlers = Rack::Tracker::HandlerSet.new(&block)
end
def call(env)
dup._call(env)
end
def _call(env)
status, headers, body = @app.call(env)
return [status, headers, body] unless headers['Content-Type'] =~ /html/
response = Rack::Response.new([], status, headers)
env[EVENT_TRACKING_KEY] ||= {}
if session = env["rack.session"]
env[EVENT_TRACKING_KEY].deep_merge!(session.delete(EVENT_TRACKING_KEY) || {}) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
end
if response.redirection? && session
session[EVENT_TRACKING_KEY] = env[EVENT_TRACKING_KEY]
end
body.each { |fragment| response.write inject(env, fragment) }
body.close if body.respond_to?(:close)
response.finish
end
private
def inject(env, response)
duplicated_response = response.dup
@handlers.each(env) do |handler|
handler.inject(duplicated_response)
end
duplicated_response
end
class HandlerSet
Handler = Struct.new(:klass, :configuration) do
def init(env)
klass.new(env, configuration)
end
end
def initialize(&block)
@handlers = []
instance_exec(&block) if block_given?
end
# setup the handler class with configuration options and make it ready for receiving the env during injection
#
# usage:
#
# use Rack::Tracker do
# handler :google_analytics, { tracker: 'U-XXXXX-Y' }
# end
#
def handler(name, configuration = {}, &block)
# we need here "something" (which is atm the handler struct)
# to postpone the initialization of the handler,
# to give it the env and configuration options when the result of the handler is injected into the response.
@handlers << Handler.new(Rack::Tracker::HandlerDelegator.handler(name), configuration)
end
def each(env = {}, &block)
@handlers.map { |h| h.init(env) }.each(&block)
end
end
end
end