visitor_sources 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +11 -0
- data/VERSION +1 -0
- data/lib/visitor_sources/search_engine.rb +59 -0
- data/lib/visitor_sources/traffic_source.rb +109 -0
- data/lib/visitor_sources/traffic_source_middleware.rb +16 -0
- data/lib/visitor_sources/traffic_sources.rb +13 -0
- data/lib/visitor_sources.rb +4 -0
- data/test/app.rb +13 -0
- data/test/search_engine_test.rb +18 -0
- data/test/test_helper.rb +47 -0
- data/test/traffic_source_middleware_test.rb +22 -0
- data/test/traffic_source_test.rb +214 -0
- data/test/traffic_sources_test.rb +18 -0
- metadata +81 -0
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Visitor Sources
|
2
|
+
===============
|
3
|
+
|
4
|
+
This code is not finished
|
5
|
+
-------------------------
|
6
|
+
|
7
|
+
Track where a visitor has come from and kep a record of each visit in the cookie. To get around the "last click wins" nature of Google Analytics
|
8
|
+
|
9
|
+
|
10
|
+
# requires the use of Rack::Session::Cookie
|
11
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class SearchEngine
|
2
|
+
SEARCH_ENGINES = {
|
3
|
+
"google" => "q",
|
4
|
+
"yahoo" => "p",
|
5
|
+
"msn" => "q",
|
6
|
+
"aol" => "query",
|
7
|
+
"aol" => "encquery",
|
8
|
+
"lycos" => "query",
|
9
|
+
"ask" => "q",
|
10
|
+
"altavista" => "q",
|
11
|
+
"netscape" => "query",
|
12
|
+
"cnn" => "query",
|
13
|
+
"looksmart" => "qt",
|
14
|
+
"about" => "terms",
|
15
|
+
"mamma" => "query",
|
16
|
+
"alltheweb" => "q",
|
17
|
+
"gigablast" => "q",
|
18
|
+
"voila" => "rdata",
|
19
|
+
"virgilio" => "qs",
|
20
|
+
"live" => "q",
|
21
|
+
"baidu" => "wd",
|
22
|
+
"alice" => "qs",
|
23
|
+
"yandex" => "text",
|
24
|
+
"najdi" => "q",
|
25
|
+
"aol" => "q",
|
26
|
+
"club-internet" => "query",
|
27
|
+
"mama" => "query",
|
28
|
+
"seznam" => "q",
|
29
|
+
"search" => "q",
|
30
|
+
"wp" => "szukaj",
|
31
|
+
"onet" => "qt",
|
32
|
+
"netsprint" => "q",
|
33
|
+
"google.interia" => "q",
|
34
|
+
"szukacz" => "q",
|
35
|
+
"yam" => "k",
|
36
|
+
"pchome" => "q",
|
37
|
+
"kvasir" => "searchExpr",
|
38
|
+
"sesam" => "q",
|
39
|
+
"ozu" => "q",
|
40
|
+
"terra" => "query",
|
41
|
+
"nostrum" => "query",
|
42
|
+
"mynet" => "q",
|
43
|
+
"ekolay" => "q",
|
44
|
+
"search.ilse" => "search_for"}
|
45
|
+
|
46
|
+
attr_accessor :name, :search_parameter
|
47
|
+
|
48
|
+
def self.find(domain)
|
49
|
+
SEARCH_ENGINES.each do |name, search_parameter|
|
50
|
+
if domain =~ /#{name}/
|
51
|
+
engine = SearchEngine.new
|
52
|
+
engine.name = name
|
53
|
+
engine.search_parameter = search_parameter
|
54
|
+
return engine
|
55
|
+
end
|
56
|
+
end
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# To minimise amount of stored data in the cookie (limited to 4KB) a traffic source gets converted to a string with variables
|
2
|
+
# seperated by a pipe(|), different traffic sources are separated by commas(,)
|
3
|
+
#
|
4
|
+
# The string looks like the following:
|
5
|
+
# 1|1266945604|ppc|just%20tv%20stands|google|December%20Campaign|/content
|
6
|
+
# Broken down, this is what the variables mean
|
7
|
+
# 1 => encoder_version makes it easy to change how this works in the future without loosing old data already in peoples cookies
|
8
|
+
# 1266945604 => unix_timestamp at which the TrafficSource was generated
|
9
|
+
# ppc => medium how the person cameto the site (main ones are cpc, direct, organic or referal)
|
10
|
+
# just%20tv%20stands => term The search term that bought the user to the site [not required]
|
11
|
+
# google => source Where the person came from. Could be keyword such as `google` or a domain name [not required]
|
12
|
+
# December%20Campaign => campaign Usually set for ppc traffic in adwords [not required]
|
13
|
+
# /content => content when a referal, this is the path from which they came from [not required]
|
14
|
+
|
15
|
+
require "uri"
|
16
|
+
class TrafficSource
|
17
|
+
attr_accessor :encoder_version, :unix_timestamp, :medium, :term, :source, :campaign, :content, :custom_parameter_mapping, :env
|
18
|
+
|
19
|
+
COOKIE_LINE_PARAMETERS = ['encoder_version', 'unix_timestamp', 'medium', 'term', 'source', 'campaign', 'content']
|
20
|
+
STANDARD_PARAMETER_MAPPING = {:medium => :utm_medium, :term => :utm_term, :source => :utm_source, :campaign => :utm_campaign, :content => :utm_content}
|
21
|
+
|
22
|
+
def initialize(attributes={})
|
23
|
+
attributes.each do |key, value|
|
24
|
+
self.send("#{key}=", value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.updated_rack_environment(old_env, custom_parameter_mapping = {})
|
29
|
+
old_env["rack.session"][:traffic_sources] ||= ''
|
30
|
+
traffic_sources = TrafficSources.new(old_env["rack.session"][:traffic_sources])
|
31
|
+
latest_source = TrafficSource.initialize_with_rack_env(old_env, custom_parameter_mapping)
|
32
|
+
return old_env if latest_source.to_s.nil?
|
33
|
+
if traffic_sources.length > 0
|
34
|
+
return old_env if latest_source.same_as?(traffic_sources.last)
|
35
|
+
end
|
36
|
+
traffic_sources << latest_source
|
37
|
+
old_env["rack.session"][:traffic_sources] = traffic_sources.to_s
|
38
|
+
return old_env
|
39
|
+
end
|
40
|
+
|
41
|
+
def TrafficSource.initialize_with_rack_env(env, custom_parameter_mapping = {})
|
42
|
+
traffic_source = self.new(:env => env, :custom_parameter_mapping => custom_parameter_mapping,
|
43
|
+
:unix_timestamp => Time.now.to_i, :encoder_version => 1)
|
44
|
+
|
45
|
+
COOKIE_LINE_PARAMETERS.last(5).each do |attribute|
|
46
|
+
traffic_source.send("#{attribute}=", traffic_source.query_string_value_for(attribute.to_sym))
|
47
|
+
end
|
48
|
+
|
49
|
+
#special case for adwords auto tagging
|
50
|
+
traffic_source.medium = 'cpc' if traffic_source.medium.nil? && !traffic_source.env["rack.request.query_hash"]["gclid"].nil?
|
51
|
+
|
52
|
+
if traffic_source.medium.nil?
|
53
|
+
if env["HTTP_REFERER"].nil?
|
54
|
+
traffic_source.medium = "direct"
|
55
|
+
else
|
56
|
+
traffic_source.medium = "referal" unless env["HTTP_REFERER"] =~ /#{env['HTTP_HOST']}/
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if traffic_source.source.nil?
|
60
|
+
begin
|
61
|
+
uri = URI.parse(env["HTTP_REFERER"])
|
62
|
+
traffic_source.source = uri.host
|
63
|
+
search_engine = SearchEngine.find(traffic_source.source)
|
64
|
+
if search_engine
|
65
|
+
traffic_source.source = search_engine.name
|
66
|
+
traffic_source.term = Rack::Utils.parse_query(uri.query)[search_engine.search_parameter] if traffic_source.term.nil?
|
67
|
+
traffic_source.medium = "organic" unless traffic_source.medium == 'cpc'
|
68
|
+
end
|
69
|
+
traffic_source.content = uri.path if traffic_source.content.nil? && !search_engine
|
70
|
+
rescue; end
|
71
|
+
end
|
72
|
+
return traffic_source
|
73
|
+
end
|
74
|
+
|
75
|
+
def TrafficSource.initialize_from_string(string)
|
76
|
+
string_attributes = string.split("|")
|
77
|
+
traffic_source = TrafficSource.new(:encoder_version => string_attributes[0].to_i, :unix_timestamp => string_attributes[1].to_i)
|
78
|
+
COOKIE_LINE_PARAMETERS.last(5).each_with_index do |attribute, index|
|
79
|
+
traffic_source.send("#{attribute}=", string_attributes[index+2])
|
80
|
+
end
|
81
|
+
return traffic_source
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_s
|
85
|
+
return nil if medium.nil?
|
86
|
+
# join each element by pipes(|), remove any unnecessary unused pipes from the end
|
87
|
+
COOKIE_LINE_PARAMETERS.collect{|param| self.send(param)}.join("|").gsub(/\|+$/, '')
|
88
|
+
end
|
89
|
+
|
90
|
+
def query_string_value_for(attribute)
|
91
|
+
if custom_parameter_mapping[attribute] && !env["rack.request.query_hash"][custom_parameter_mapping[attribute].to_s].nil?
|
92
|
+
return env["rack.request.query_hash"][custom_parameter_mapping[attribute].to_s]
|
93
|
+
end
|
94
|
+
if env["rack.request.query_hash"][STANDARD_PARAMETER_MAPPING[attribute].to_s]
|
95
|
+
return env["rack.request.query_hash"][STANDARD_PARAMETER_MAPPING[attribute].to_s]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def same_as?(traffic_source)
|
100
|
+
COOKIE_LINE_PARAMETERS.last(5).each do |attribute|
|
101
|
+
return false if self.send(attribute).to_s != traffic_source.send(attribute).to_s
|
102
|
+
end
|
103
|
+
return true
|
104
|
+
end
|
105
|
+
|
106
|
+
def date
|
107
|
+
Time.at(unix_timestamp)
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class TrafficSourceMiddleware
|
2
|
+
def initialize(app, options={})
|
3
|
+
@app = app
|
4
|
+
@options = options
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
@options[:custom_parameter_mapping] ||= {}
|
9
|
+
env["rack.request.query_hash"] = Rack::Utils.parse_query(env["QUERY_STRING"])
|
10
|
+
env = TrafficSource.updated_rack_environment(env, @options[:custom_parameter_mapping])
|
11
|
+
env[:traffic_sources] = TrafficSources.new(env["rack.session"][:traffic_sources])
|
12
|
+
@status, @headers, @response = @app.call(env)
|
13
|
+
[@status, @headers, @response]
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class TrafficSources < Array
|
2
|
+
|
3
|
+
def initialize(string)
|
4
|
+
traffic_source_strings = string.split(",")
|
5
|
+
traffic_source_strings.each do |string|
|
6
|
+
self << TrafficSource.initialize_from_string(string)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
each.collect(&:to_s).join(",")
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/visitor_sources/search_engine')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/visitor_sources/traffic_source')
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/visitor_sources/traffic_sources')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/visitor_sources/traffic_source_middleware')
|
data/test/app.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sinatra'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/visitor_sources')
|
4
|
+
|
5
|
+
use Rack::Session::Cookie
|
6
|
+
use TrafficSourceMiddleware, :custom_parameter_mapping => {:campaign => :campaign}
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
get "/" do
|
11
|
+
request.env[:traffic_sources].to_s
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
|
3
|
+
class SearchEngineTest < Test::Unit::TestCase
|
4
|
+
context "SearchEngineTest" do
|
5
|
+
context "find" do
|
6
|
+
should "return a search engine if one matches" do
|
7
|
+
engine = SearchEngine.find("www.google.com")
|
8
|
+
assert_equal "google", engine.name
|
9
|
+
assert_equal "q", engine.search_parameter
|
10
|
+
end
|
11
|
+
|
12
|
+
should "return nil if no engine found" do
|
13
|
+
engine = SearchEngine.find("www.fakeengine.com")
|
14
|
+
assert_nil engine
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rack/test'
|
4
|
+
require 'shoulda'
|
5
|
+
require 'base64'
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/app')
|
7
|
+
|
8
|
+
EXAMPLE_RACK_ENV = {
|
9
|
+
"rack.session"=>{:traffic_sources=>""},
|
10
|
+
"HTTP_HOST"=>"localhost:9393",
|
11
|
+
"HTTP_ACCEPT"=>"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
12
|
+
"SERVER_NAME"=>"localhost",
|
13
|
+
"rack.request.cookie_hash"=>{"rack.session"=>"BAh7BjoUdHJhZmZpY19zb3VyY2VzIghBQkM=\n"},
|
14
|
+
"rack.url_scheme"=>"http",
|
15
|
+
"REQUEST_PATH"=>"/",
|
16
|
+
"HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6",
|
17
|
+
"HTTP_KEEP_ALIVE"=>"115",
|
18
|
+
"rack.errors"=>{},
|
19
|
+
"HTTP_ACCEPT_LANGUAGE"=>"en-us,en;q=0.5",
|
20
|
+
"SERVER_PROTOCOL"=>"HTTP/1.1",
|
21
|
+
"rack.version"=>[1, 0],
|
22
|
+
"rack.run_once"=>false,
|
23
|
+
"SERVER_SOFTWARE"=>"Mongrel 1.1.5",
|
24
|
+
"PATH_INFO"=>"/",
|
25
|
+
"REMOTE_ADDR"=>"::1",
|
26
|
+
"rack.request.cookie_string"=>"rack.session=BAh7BjoUdHJhZmZpY19zb3VyY2VzIghBQkM%3D%0A",
|
27
|
+
"HTTP_REFERER"=>"http://localhost:9393/",
|
28
|
+
"SCRIPT_NAME"=>"",
|
29
|
+
"rack.multithread"=>true,
|
30
|
+
"HTTP_VERSION"=>"HTTP/1.1",
|
31
|
+
"HTTP_COOKIE"=>"rack.session=BAh7BjoUdHJhZmZpY19zb3VyY2VzIghBQkM%3D%0A",
|
32
|
+
"rack.request.form_vars"=>"",
|
33
|
+
"rack.multiprocess"=>false,
|
34
|
+
"REQUEST_URI"=>"/",
|
35
|
+
"rack.request.form_input"=>{},
|
36
|
+
"rack.request.query_hash"=>{},
|
37
|
+
"HTTP_ACCEPT_CHARSET"=>"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
|
38
|
+
"SERVER_PORT"=>"9393",
|
39
|
+
"rack.session.options"=>{:domain=>nil, :expire_after=>nil, :path=>"/"},
|
40
|
+
"REQUEST_METHOD"=>"GET",
|
41
|
+
"rack.request.form_hash"=>{},
|
42
|
+
"rack.request.query_string"=>"",
|
43
|
+
"QUERY_STRING"=>"",
|
44
|
+
"rack.input"=>{},
|
45
|
+
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate",
|
46
|
+
"HTTP_CONNECTION"=>"keep-alive",
|
47
|
+
"GATEWAY_INTERFACE"=>"CGI/1.2"}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
|
3
|
+
|
4
|
+
class TrafficSourceMiddlewareTest < Test::Unit::TestCase
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
def app
|
8
|
+
Sinatra::Application
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_should_display_traffic_source_line
|
12
|
+
start_time = Time.now.to_i
|
13
|
+
get "/", params = {}, rack_env = {}
|
14
|
+
assert last_response.ok?
|
15
|
+
assert_equal "1|#{start_time}|direct", last_response.body
|
16
|
+
sleep 1
|
17
|
+
get "/", params = {:campaign => "mycampaign", :utm_medium => "cpc"}, rack_env = {}
|
18
|
+
assert_equal "1|#{start_time}|direct,1|#{start_time+1}|cpc|||mycampaign", last_response.body
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
|
3
|
+
# Priority of variables set
|
4
|
+
# custom variables configured when using the plugin take the most priority
|
5
|
+
# if no custom variable matches, then the standard google variables take priority: 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_campaign'
|
6
|
+
# if no match standard google variables then try and work out the required data from referers etc
|
7
|
+
|
8
|
+
class TrafficSourceTest < Test::Unit::TestCase
|
9
|
+
context "TrafficSourceTest" do
|
10
|
+
setup do
|
11
|
+
@rack_env = EXAMPLE_RACK_ENV.dup
|
12
|
+
@custom_variable_matches = {:campaign => :custom_campaign, :term => :custom_keywords, :medium => :custom_medium}
|
13
|
+
end
|
14
|
+
context "initialize_with_rack_env" do
|
15
|
+
|
16
|
+
context "when there are custom variables in the url present" do
|
17
|
+
should "use the custom variables to set the correct TrafficSource" do
|
18
|
+
@rack_env["rack.request.query_hash"] = {"custom_campaign" => "MyCamp1", "custom_keywords" => "Product One", "gclid" => "AutoAdwordsTaggingClid"}
|
19
|
+
@rack_env["HTTP_REFERER"] = "http://www.google.co.uk/search"
|
20
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
21
|
+
assert_equal "cpc", traffic_source.medium
|
22
|
+
assert_equal "MyCamp1", traffic_source.campaign
|
23
|
+
assert_equal "Product One", traffic_source.term
|
24
|
+
assert_equal Time.now.to_i, traffic_source.unix_timestamp
|
25
|
+
assert_equal "1|#{Time.now.to_i}|cpc|Product One|google|MyCamp1", traffic_source.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
should "use presume organic if no gclid and from a search engine" do
|
29
|
+
@rack_env["rack.request.query_hash"] = {"custom_campaign" => "MyCamp1", "custom_keywords" => "Product One"}
|
30
|
+
@rack_env["HTTP_REFERER"] = "http://www.google.co.uk/search"
|
31
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
32
|
+
assert_equal "organic", traffic_source.medium
|
33
|
+
assert_equal "MyCamp1", traffic_source.campaign
|
34
|
+
assert_equal "Product One", traffic_source.term
|
35
|
+
assert_equal Time.now.to_i, traffic_source.unix_timestamp
|
36
|
+
assert_equal "1|#{Time.now.to_i}|organic|Product One|google|MyCamp1", traffic_source.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
should "assign to cpc if a custom variable says thats the case" do
|
40
|
+
@rack_env["rack.request.query_hash"] = {"custom_campaign" => "MyCamp1", "custom_keywords" => "Product One", "custom_medium" => "cpc"}
|
41
|
+
@rack_env["HTTP_REFERER"] = "http://www.google.co.uk/search"
|
42
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
43
|
+
assert_equal "cpc", traffic_source.medium
|
44
|
+
assert_equal "MyCamp1", traffic_source.campaign
|
45
|
+
assert_equal "Product One", traffic_source.term
|
46
|
+
assert_equal Time.now.to_i, traffic_source.unix_timestamp
|
47
|
+
assert_equal "1|#{Time.now.to_i}|cpc|Product One|google|MyCamp1", traffic_source.to_s
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when no custom or standard variables matching" do
|
52
|
+
context "when there is no referer" do
|
53
|
+
should "add create a TrafficSource instance with medium of direct" do
|
54
|
+
@rack_env["HTTP_REFERER"] = nil
|
55
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
56
|
+
assert_equal "direct", traffic_source.medium
|
57
|
+
assert_equal Time.now.to_i, traffic_source.unix_timestamp
|
58
|
+
assert_equal "1|#{Time.now.to_i}|direct", traffic_source.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when there is a referer" do
|
63
|
+
context "when the referer is an interal address" do
|
64
|
+
should "have no medium and to_s return nil" do
|
65
|
+
@rack_env["HTTP_REFERER"] = "http://localhost:9393/some/path"
|
66
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
67
|
+
assert_nil traffic_source.medium
|
68
|
+
assert_nil traffic_source.to_s
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when the referer is external" do
|
73
|
+
should "have a medium of referer with source and content" do
|
74
|
+
@rack_env["HTTP_REFERER"] = "http://matthewfawcett.co.uk/some/path"
|
75
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
76
|
+
assert_equal "referal", traffic_source.medium
|
77
|
+
assert_equal "matthewfawcett.co.uk", traffic_source.source
|
78
|
+
assert_equal "/some/path", traffic_source.content
|
79
|
+
assert_equal "1|#{Time.now.to_i}|referal||matthewfawcett.co.uk||/some/path", traffic_source.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
should "assign to a search engine with correct keywords if referer matches" do
|
83
|
+
@rack_env["HTTP_REFERER"] = "http://google.co.uk/search?q=mysearchterms"
|
84
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
85
|
+
assert_equal "organic", traffic_source.medium
|
86
|
+
assert_equal "google", traffic_source.source
|
87
|
+
assert_equal "mysearchterms", traffic_source.term
|
88
|
+
assert_nil traffic_source.content
|
89
|
+
assert_equal "1|#{Time.now.to_i}|organic|mysearchterms|google", traffic_source.to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "using standard google variables" do
|
96
|
+
should "should assign to ppc if the variables say so" do
|
97
|
+
@rack_env["rack.request.query_hash"] = {"utm_campaign" => "MyCamp1", "utm_term" => "Product One", "utm_medium" => "cpc", "utm_source" => "google", "utm_term" => "Product One"}
|
98
|
+
@rack_env["HTTP_REFERER"] = "http://www.google.co.uk/search"
|
99
|
+
traffic_source = TrafficSource.initialize_with_rack_env(@rack_env, @custom_variable_matches)
|
100
|
+
assert_equal "cpc", traffic_source.medium
|
101
|
+
assert_equal "MyCamp1", traffic_source.campaign
|
102
|
+
assert_equal "Product One", traffic_source.term
|
103
|
+
assert_equal Time.now.to_i, traffic_source.unix_timestamp
|
104
|
+
assert_equal "1|#{Time.now.to_i}|cpc|Product One|google|MyCamp1", traffic_source.to_s
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "query_string_value_for" do
|
110
|
+
setup do
|
111
|
+
@traffic_source = TrafficSource.new
|
112
|
+
@traffic_source.custom_parameter_mapping = {:campaign => :custom_campaign, :keyword => :custom_keywords, :medium => :custom_medium}
|
113
|
+
end
|
114
|
+
|
115
|
+
should "use the custom value if there is a match" do
|
116
|
+
@traffic_source.env = @rack_env.merge("rack.request.query_hash" => {"custom_campaign" => "mattscustomcampaign", "utm_campaign" => "standardcampaign"})
|
117
|
+
assert_equal "mattscustomcampaign", @traffic_source.query_string_value_for(:campaign)
|
118
|
+
end
|
119
|
+
|
120
|
+
should "use the standard value if there is a match and no custom match" do
|
121
|
+
@traffic_source.env = @rack_env.merge("rack.request.query_hash" => {"utm_campaign" => "standardcampaign"})
|
122
|
+
assert_equal "standardcampaign", @traffic_source.query_string_value_for(:campaign)
|
123
|
+
end
|
124
|
+
|
125
|
+
should "use the standard match if we don't even have any custom parameter mapping" do
|
126
|
+
@traffic_source.custom_parameter_mapping = {}
|
127
|
+
@traffic_source.env = @rack_env.merge("rack.request.query_hash" => {"custom_campaign" => "mattscustomcampaign", "utm_campaign" => "standardcampaign"})
|
128
|
+
assert_equal "standardcampaign", @traffic_source.query_string_value_for(:campaign)
|
129
|
+
end
|
130
|
+
|
131
|
+
should "be nil if no match" do
|
132
|
+
@traffic_source.env = @rack_env.merge("rack.request.query_hash" => {})
|
133
|
+
assert_nil @traffic_source.query_string_value_for(:campaign)
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
context "updated_rack_environment" do
|
139
|
+
|
140
|
+
should "not update the enviroment if the current TrafficSource#to_s is nil" do
|
141
|
+
@rack_env["HTTP_REFERER"] = "http://localhost:9393/some/path"
|
142
|
+
rack_env = TrafficSource.updated_rack_environment(@rack_env)
|
143
|
+
assert_equal "", rack_env["rack.session"][:traffic_sources]
|
144
|
+
end
|
145
|
+
|
146
|
+
should "not update the enviroment if the last TrafficSource#to_s is the same as the current traffic source" do
|
147
|
+
@rack_env["HTTP_REFERER"] = nil
|
148
|
+
rack_env = TrafficSource.updated_rack_environment(@rack_env)
|
149
|
+
assert_equal "1|#{Time.now.to_i}|direct", rack_env["rack.session"][:traffic_sources]
|
150
|
+
|
151
|
+
rack_env = TrafficSource.updated_rack_environment(@rack_env)
|
152
|
+
assert_equal "1|#{Time.now.to_i}|direct", rack_env["rack.session"][:traffic_sources]
|
153
|
+
end
|
154
|
+
|
155
|
+
should "update the traffic source if the current one is differenty from the last one" do
|
156
|
+
@rack_env["HTTP_REFERER"] = nil
|
157
|
+
rack_env = TrafficSource.updated_rack_environment(@rack_env)
|
158
|
+
assert_equal "1|#{Time.now.to_i}|direct", rack_env["rack.session"][:traffic_sources]
|
159
|
+
|
160
|
+
rack_env["rack.request.query_hash"] = {"utm_campaign" => "MyCamp1", "utm_term" => "Product One", "utm_medium" => "cpc", "utm_source" => "google", "utm_term" => "Product One"}
|
161
|
+
rack_env["HTTP_REFERER"] = "http://www.google.co.uk/search"
|
162
|
+
rack_env = TrafficSource.updated_rack_environment(@rack_env)
|
163
|
+
assert_equal "1|#{Time.now.to_i}|direct,1|#{Time.now.to_i}|cpc|Product One|google|MyCamp1", rack_env["rack.session"][:traffic_sources]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
context "same_as?" do
|
172
|
+
setup do
|
173
|
+
@source_1 = TrafficSource.new(:encoder_version => 1, :unix_timestamp => Time.now.to_i, :medium => 'cpc',
|
174
|
+
:term => 'myterm', :source => 'google', :campaign => 'mycamp')
|
175
|
+
@source_2 = @source_1.dup
|
176
|
+
end
|
177
|
+
|
178
|
+
should "be true if all the main fields are the same" do
|
179
|
+
assert @source_1.same_as?(@source_2)
|
180
|
+
@source_2.encoder_version = 2
|
181
|
+
@source_2.unix_timestamp = 1234
|
182
|
+
assert @source_1.same_as?(@source_2)
|
183
|
+
end
|
184
|
+
|
185
|
+
should "be false if one of the fields are different" do
|
186
|
+
@source_2.medium = "direct"
|
187
|
+
assert !@source_1.same_as?(@source_2)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "initialize_from_string" do
|
192
|
+
should "create a new TrafficSource using the correct parameters from the string" do
|
193
|
+
string = "1|123456|organic|mysearchterms|google"
|
194
|
+
source = TrafficSource.initialize_from_string(string)
|
195
|
+
assert_equal 1, source.encoder_version
|
196
|
+
assert_equal 123456, source.unix_timestamp
|
197
|
+
assert_equal "organic", source.medium
|
198
|
+
assert_equal "mysearchterms", source.term
|
199
|
+
assert_equal "google", source.source
|
200
|
+
assert_nil source.campaign
|
201
|
+
assert_nil source.content
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "date" do
|
206
|
+
should "return a date object using the timestamp" do
|
207
|
+
@source = TrafficSource.new(:unix_timestamp => 1267804832)
|
208
|
+
assert_equal Time.at(1267804832), @source.date
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
|
214
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
|
3
|
+
class TrafficSourcesTest < Test::Unit::TestCase
|
4
|
+
context "TrafficSourcesTest" do
|
5
|
+
context "new" do
|
6
|
+
should "allow the raw string to be used to build the array of TrafficSources" do
|
7
|
+
assert_equal 2, TrafficSources.new("1|#{Time.now.to_i}|direct,1|#{Time.now.to_i}|cpc|Product One|google|MyCamp1").length
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "to_s" do
|
12
|
+
should "call to_s on each TrafficSource instance and join with commas" do
|
13
|
+
traffic_sources = TrafficSources.new("1|#{Time.now.to_i}|direct,1|#{Time.now.to_i}|cpc|Product One|google|MyCamp1")
|
14
|
+
assert_equal "1|#{Time.now.to_i}|direct,1|#{Time.now.to_i}|cpc|Product One|google|MyCamp1", traffic_sources.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: visitor_sources
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Fawcett
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-05 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thoughtbot-shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: Use a cookie to record the sources that each visitor came to your site
|
26
|
+
email: mail@matthewfawcett.co.uk
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.md
|
33
|
+
files:
|
34
|
+
- README.md
|
35
|
+
- VERSION
|
36
|
+
- lib/visitor_sources.rb
|
37
|
+
- lib/visitor_sources/search_engine.rb
|
38
|
+
- lib/visitor_sources/traffic_source.rb
|
39
|
+
- lib/visitor_sources/traffic_source_middleware.rb
|
40
|
+
- lib/visitor_sources/traffic_sources.rb
|
41
|
+
- test/app.rb
|
42
|
+
- test/search_engine_test.rb
|
43
|
+
- test/test_helper.rb
|
44
|
+
- test/traffic_source_middleware_test.rb
|
45
|
+
- test/traffic_source_test.rb
|
46
|
+
- test/traffic_sources_test.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/mattfawcett/visitor_sources
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options:
|
53
|
+
- --charset=UTF-8
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.3.5
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Use a cookie to record the sources that each visitor came to your site
|
75
|
+
test_files:
|
76
|
+
- test/app.rb
|
77
|
+
- test/search_engine_test.rb
|
78
|
+
- test/test_helper.rb
|
79
|
+
- test/traffic_source_middleware_test.rb
|
80
|
+
- test/traffic_source_test.rb
|
81
|
+
- test/traffic_sources_test.rb
|