sojourn 0.1.1 → 1.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/README.md +41 -66
- data/Rakefile +0 -8
- data/lib/generators/sojourn/install_generator.rb +1 -1
- data/lib/generators/sojourn/templates/create_sojourn_browsers.rb +13 -0
- data/lib/generators/sojourn/templates/create_sojourn_campaigns.rb +9 -0
- data/lib/generators/sojourn/templates/create_sojourn_events.rb +2 -0
- data/lib/generators/sojourn/templates/create_sojourn_requests.rb +19 -0
- data/lib/sojourn/browser.rb +27 -0
- data/lib/sojourn/campaign.rb +16 -0
- data/lib/sojourn/configuration.rb +2 -0
- data/lib/sojourn/controller.rb +2 -0
- data/lib/sojourn/event.rb +4 -1
- data/lib/sojourn/request.rb +34 -90
- data/lib/sojourn/serializers/symbol.rb +13 -0
- data/lib/sojourn/session_stores/cookie.rb +2 -0
- data/lib/sojourn/tracker.rb +4 -5
- data/lib/sojourn/version.rb +1 -1
- data/lib/sojourn.rb +1 -1
- data/sojourn.gemspec +0 -6
- metadata +24 -117
- data/.codeclimate.yml +0 -11
- data/.rubocop.yml +0 -6
- data/.travis.yml +0 -8
- data/spec/mocks/controller.rb +0 -15
- data/spec/mocks/cookie.rb +0 -30
- data/spec/mocks/request.rb +0 -56
- data/spec/mocks/user.rb +0 -9
- data/spec/spec_helper.rb +0 -47
- data/spec/tracker_spec.rb +0 -110
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9caf4fdd9129f3391d00292849ea95096e2e7bb8
|
|
4
|
+
data.tar.gz: 3cae47fe7eea5430c668cb8d63993ab9eca5a89f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cb52529bf9c8f7623ad5e35ace62dc89933c7c11b086a39241d92b98bfaebf4fc1d7de7f4b62f009f107fce7e8cad8d8c1548bbbdc31b61b6b5b8d1a581b6567
|
|
7
|
+
data.tar.gz: 6db0dd0268c3fd39d40340b6e9254b436f367f03bfb013259c80920e445b239adc4e2db329b76cd11e362153d1ef78de43b3545ee472a781170b53dce192ade3
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Sojourn
|
|
1
|
+
# Sojourn
|
|
2
2
|
|
|
3
3
|
Simple source & event tracking for Rails. This gem automatically tracks *sojourners*
|
|
4
4
|
(i.e. unique visitors) based on:
|
|
@@ -11,22 +11,18 @@ Simple source & event tracking for Rails. This gem automatically tracks *sojourn
|
|
|
11
11
|
|
|
12
12
|
## How It Works
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
whenever a user logs in, logs out, or visits again from an external site. In addition,
|
|
17
|
-
you can track a custom event anytime a visitor does something of interest to you.
|
|
14
|
+
**Everything is tracked in the form of events.** Yep, events all the way down.
|
|
15
|
+
(See 'Why Events?' below for the reasoning behind this.)
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
for the reasoning behind this.)
|
|
17
|
+
Sojourn assigns each *sojourner* a UUID, which is tracked across requests. All events are
|
|
18
|
+
associated with this UUID and with the current user's ID (if logged-in).
|
|
22
19
|
|
|
23
|
-
Sojourn
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
Events (`Sojourn::Event`) consist of a name, a set of properties (key-value hash) and information
|
|
21
|
+
about the request. In the current ActiveRecord implementation, requests (`Sojourn::Request`) can
|
|
22
|
+
be queried separately and may have many events. Requests also track browser (`Sojourn::Browser`)
|
|
23
|
+
and campaign (`Sojourn::Campaign`) info as unique models. See 'Usage' below for the details
|
|
24
|
+
of these models.
|
|
26
25
|
|
|
27
|
-
Events consist of an event name (defining a collection of events), a session UUID,
|
|
28
|
-
and a set of properties (key-value data) which includes information about the request.
|
|
29
|
-
In the PostgreSQL implementation, we use a `JSONB` column to store the key-value data.
|
|
30
26
|
|
|
31
27
|
## Usage
|
|
32
28
|
|
|
@@ -34,16 +30,37 @@ In the PostgreSQL implementation, we use a `JSONB` column to store the key-value
|
|
|
34
30
|
# Track a custom event (highly encouraged!):
|
|
35
31
|
sojourn.track! 'clicked call-to-action', plan_choice: 'enterprise'
|
|
36
32
|
|
|
37
|
-
# Read events using ActiveRecord
|
|
38
33
|
e = Sojourn::Event.last
|
|
39
34
|
e.name # event name (e.g. 'clicked call-to-action')
|
|
40
35
|
e.sojourner_uuid # uuid tracked across requests, stored in cookie
|
|
41
36
|
e.user # User or nil
|
|
42
37
|
e.properties # key-value hash (e.g. "{ plan_choice: 'enterprise' }")
|
|
38
|
+
e.request # Sojourn::Request object
|
|
39
|
+
|
|
40
|
+
r = Sojourn::Request.last
|
|
41
|
+
r.referer
|
|
42
|
+
r.host
|
|
43
|
+
r.path
|
|
44
|
+
r.controller
|
|
45
|
+
r.action
|
|
46
|
+
r.params
|
|
47
|
+
r.method
|
|
48
|
+
r.ip_address
|
|
49
|
+
r.campaign # Sojourn::Campaign object (nil if no campaign detected)
|
|
50
|
+
r.browser # Sojourn::Browser object
|
|
51
|
+
|
|
52
|
+
b = Sojourn::Browser.last
|
|
53
|
+
b.known? # whether or not the browser was detected successfully
|
|
54
|
+
b.user_agent
|
|
55
|
+
b.name
|
|
56
|
+
b.version
|
|
57
|
+
b.platform
|
|
58
|
+
b.bot?
|
|
59
|
+
|
|
60
|
+
c = Sojourn::Campaign.last
|
|
61
|
+
c.path # Base path (e.g. '/posts/2')
|
|
62
|
+
c.params # Notable (tracked) params, sorted. (Typically utm-style, but configurable.)
|
|
43
63
|
|
|
44
|
-
# If you don't have access to a controller context (i.e. the event is not occurring during a web
|
|
45
|
-
# request), you can still track a raw event like this:
|
|
46
|
-
Sojourn.track_raw_event! 'subscription expired', plan: 'enterprise', customer_id: 'xyb123'
|
|
47
64
|
```
|
|
48
65
|
|
|
49
66
|
## Default Events
|
|
@@ -56,7 +73,7 @@ The three built-in events are as follows:
|
|
|
56
73
|
'!logged_out' # The sojourner has logged-out.
|
|
57
74
|
```
|
|
58
75
|
|
|
59
|
-
A `'!sojourning'` event takes place whenever any of the following
|
|
76
|
+
A `'!sojourning'` event takes place whenever any of the following is true:
|
|
60
77
|
|
|
61
78
|
* The sojourner has never been seen before (i.e. direct traffic of some kind)
|
|
62
79
|
* The referer is from an external source (i.e. not the current `request.host`)
|
|
@@ -64,42 +81,6 @@ A `'!sojourning'` event takes place whenever any of the following conditions is
|
|
|
64
81
|
initializer.)
|
|
65
82
|
|
|
66
83
|
|
|
67
|
-
## Properties
|
|
68
|
-
|
|
69
|
-
In addition to properties that you manually add, events will automatically include data about
|
|
70
|
-
the current web request. An example looks like this:
|
|
71
|
-
|
|
72
|
-
```json
|
|
73
|
-
{
|
|
74
|
-
"custom_property":"value",
|
|
75
|
-
"request":{
|
|
76
|
-
"uuid":"5e698f6ca74a016c49ca6b91a79cada7",
|
|
77
|
-
"host":"example.com",
|
|
78
|
-
"path":"/my-news",
|
|
79
|
-
"controller":"news",
|
|
80
|
-
"action":"index",
|
|
81
|
-
"method":"get",
|
|
82
|
-
"params":{
|
|
83
|
-
"utm_campaign":"daily_updates",
|
|
84
|
-
"page":"1"
|
|
85
|
-
},
|
|
86
|
-
"referer":"https://mail.google.com",
|
|
87
|
-
"ip_address":"42.42.42.42",
|
|
88
|
-
"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36"
|
|
89
|
-
},
|
|
90
|
-
"browser":{
|
|
91
|
-
"bot":false,
|
|
92
|
-
"name":"Chrome",
|
|
93
|
-
"known":true,
|
|
94
|
-
"version":"48",
|
|
95
|
-
"platform":"mac"
|
|
96
|
-
},
|
|
97
|
-
"campaign":{
|
|
98
|
-
"utm_campaign":"daily_updates"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
84
|
## Installation
|
|
104
85
|
|
|
105
86
|
Add this line to your application's Gemfile:
|
|
@@ -121,17 +102,10 @@ To install migrations and the `sojourn.rb` initializer, execute:
|
|
|
121
102
|
The idea is that, at a certain scale, this kind of tracking should be dumped directly into
|
|
122
103
|
append-only logs (or an event bus / messaging queue) for asynchronous processing.
|
|
123
104
|
|
|
124
|
-
This is made easier when everything can be represented, at a basic level, as
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
Kafka combined with Samza or Storm).
|
|
129
|
-
|
|
130
|
-
An added benfit of storing the start of each visit as its own event in the series (i.e. the
|
|
131
|
-
built-in `!sojourning` event) is that you can change the length of your visit window after
|
|
132
|
-
the fact and re-run your analysis. The more traditional approach is to tag each event with
|
|
133
|
-
some kind of incrementing visit ID, which forces you into defining what a "unique visit"
|
|
134
|
-
means for your product before you've even collected any data.
|
|
105
|
+
This is made easier when everything can be represented, at a basic level, as discrete events.
|
|
106
|
+
In theory, it works with just about any data store, and makes for easy time series and funnel
|
|
107
|
+
analysis. I'd like to move away from ActiveRecord at some point and open up the door for other,
|
|
108
|
+
highly scalable data backends.
|
|
135
109
|
|
|
136
110
|
## Current Limitations (i.e. the 'todo' list)
|
|
137
111
|
|
|
@@ -141,3 +115,4 @@ means for your product before you've even collected any data.
|
|
|
141
115
|
website.
|
|
142
116
|
* Relies solely on cookies to track visitor UUID across requests (no JS, fingerprinting, etc)
|
|
143
117
|
* Relies on ActiveRecord for storage. (At a bigger scale, append-only logs are preferred)
|
|
118
|
+
* There are no tests.
|
data/Rakefile
CHANGED
|
@@ -21,7 +21,7 @@ module Sojourn
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def create_migrations
|
|
24
|
-
%w(events).map { |m| "create_sojourn_#{m}" }.each do |name|
|
|
24
|
+
%w(campaigns browsers requests events).map { |m| "create_sojourn_#{m}" }.each do |name|
|
|
25
25
|
if self.class.migration_exists?('db/migrate', name)
|
|
26
26
|
say " #{set_color('skip', :yellow)} #{name}.rb (migration already exists)"
|
|
27
27
|
else
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateSojournBrowsers < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :sojourn_browsers do |t|
|
|
4
|
+
t.text :user_agent, unique: true
|
|
5
|
+
t.string :name
|
|
6
|
+
t.string :version
|
|
7
|
+
t.string :platform
|
|
8
|
+
t.boolean :known
|
|
9
|
+
t.boolean :bot
|
|
10
|
+
end
|
|
11
|
+
add_index :sojourn_browsers, [:user_agent]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -4,10 +4,12 @@ class CreateSojournEvents < ActiveRecord::Migration
|
|
|
4
4
|
t.string :sojourner_uuid, limit: 36, null: false
|
|
5
5
|
t.string :name
|
|
6
6
|
t.column :properties, :jsonb
|
|
7
|
+
t.references :sojourn_request
|
|
7
8
|
t.references :user
|
|
8
9
|
t.timestamp :created_at
|
|
9
10
|
end
|
|
10
11
|
add_index :sojourn_events, [:sojourner_uuid]
|
|
12
|
+
add_index :sojourn_events, [:sojourn_request_id]
|
|
11
13
|
add_index :sojourn_events, [:user_id]
|
|
12
14
|
add_index :sojourn_events, [:created_at]
|
|
13
15
|
add_index :sojourn_events, [:name]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class CreateSojournRequests < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :sojourn_requests do |t|
|
|
4
|
+
t.references :sojourn_campaign
|
|
5
|
+
t.references :sojourn_browser
|
|
6
|
+
t.string :host, limit: 2048
|
|
7
|
+
t.string :path, limit: 2048
|
|
8
|
+
t.string :method
|
|
9
|
+
t.string :controller
|
|
10
|
+
t.string :action
|
|
11
|
+
t.string :ip_address
|
|
12
|
+
t.text :params
|
|
13
|
+
t.text :referer
|
|
14
|
+
t.timestamp :created_at
|
|
15
|
+
end
|
|
16
|
+
add_index :sojourn_requests, [:sojourn_campaign_id]
|
|
17
|
+
add_index :sojourn_requests, [:sojourn_browser_id]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'browser'
|
|
2
|
+
|
|
3
|
+
module Sojourn
|
|
4
|
+
class Browser < ActiveRecord::Base
|
|
5
|
+
has_many :requests, foreign_key: :sojourn_browser_id
|
|
6
|
+
has_many :events, through: :requests
|
|
7
|
+
|
|
8
|
+
def self.from_request(request)
|
|
9
|
+
where(user_agent: request.user_agent).first_or_initialize
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
before_validation do
|
|
13
|
+
self.name ||= browser.name
|
|
14
|
+
self.version ||= browser.version
|
|
15
|
+
self.platform ||= browser.platform
|
|
16
|
+
self.known ||= browser.known?
|
|
17
|
+
self.bot ||= browser.bot?
|
|
18
|
+
true # otherwise .valid? will return false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def browser
|
|
24
|
+
@browser ||= ::Browser.new(user_agent: user_agent)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Sojourn
|
|
2
|
+
class Campaign < ActiveRecord::Base
|
|
3
|
+
|
|
4
|
+
has_many :requests, foreign_key: :sojourn_campaign_id
|
|
5
|
+
has_many :events, through: :requests
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
|
|
9
|
+
def from_request(request)
|
|
10
|
+
return unless (request.tracked_params).any?
|
|
11
|
+
where(params: request.tracked_params.to_param.try(:truncate, 2048)).first_or_initialize
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require_relative 'session_stores/cookie'
|
|
2
2
|
|
|
3
3
|
class Configuration
|
|
4
|
+
|
|
4
5
|
attr_accessor :campaign_params
|
|
5
6
|
attr_accessor :session_store
|
|
6
7
|
attr_accessor :cookie_name
|
|
@@ -23,4 +24,5 @@ private
|
|
|
23
24
|
self.cookie_name = :_sojourn
|
|
24
25
|
self.tracking_enabled = true
|
|
25
26
|
end
|
|
27
|
+
|
|
26
28
|
end
|
data/lib/sojourn/controller.rb
CHANGED
|
@@ -2,6 +2,7 @@ require_relative 'tracker'
|
|
|
2
2
|
|
|
3
3
|
module Sojourn
|
|
4
4
|
module Controller
|
|
5
|
+
|
|
5
6
|
def self.included(base)
|
|
6
7
|
base.before_filter :track_sojourning
|
|
7
8
|
base.before_filter :save_sojourn_session
|
|
@@ -20,5 +21,6 @@ module Sojourn
|
|
|
20
21
|
def save_sojourn_session
|
|
21
22
|
sojourn.update_session!
|
|
22
23
|
end
|
|
24
|
+
|
|
23
25
|
end
|
|
24
26
|
end
|
data/lib/sojourn/event.rb
CHANGED
|
@@ -2,9 +2,12 @@ require_relative 'serializers/indifferent_json'
|
|
|
2
2
|
|
|
3
3
|
module Sojourn
|
|
4
4
|
class Event < ActiveRecord::Base
|
|
5
|
-
DEFAULT_FIELDS =
|
|
5
|
+
DEFAULT_FIELDS = [:id, :sojourner_uuid, :name, :properties, :sojourn_request_id, :user_id, :created_at]
|
|
6
6
|
|
|
7
|
+
belongs_to :request, foreign_key: :sojourn_request_id
|
|
7
8
|
belongs_to :user
|
|
9
|
+
has_one :campaign, through: :request
|
|
10
|
+
has_one :browser, through: :request
|
|
8
11
|
|
|
9
12
|
serialize :properties, Serializers::IndifferentJSON
|
|
10
13
|
|
data/lib/sojourn/request.rb
CHANGED
|
@@ -1,20 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative 'campaign'
|
|
2
|
+
require_relative 'browser'
|
|
3
|
+
require_relative 'serializers/symbol'
|
|
2
4
|
require 'addressable/uri'
|
|
3
|
-
require 'browser'
|
|
4
|
-
require 'referer-parser'
|
|
5
5
|
|
|
6
6
|
module Sojourn
|
|
7
|
-
class Request
|
|
8
|
-
|
|
7
|
+
class Request < ActiveRecord::Base
|
|
8
|
+
attr_accessor :user_agent
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
serialize :method, Serializers::Symbol
|
|
11
|
+
serialize :params
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
belongs_to :campaign, foreign_key: :sojourn_campaign_id
|
|
14
|
+
belongs_to :browser, foreign_key: :sojourn_browser_id
|
|
15
|
+
has_many :events, foreign_key: :sojourn_request_id
|
|
16
|
+
|
|
17
|
+
def self.from_request(request)
|
|
18
|
+
new referer: request.referer.try(:truncate, 2048),
|
|
19
|
+
host: request.host.try(:truncate, 2048),
|
|
20
|
+
path: request.path.try(:truncate, 2048),
|
|
21
|
+
controller: request.params[:controller],
|
|
22
|
+
action: request.params[:action],
|
|
23
|
+
params: request.filtered_parameters.with_indifferent_access.except(:controller, :action),
|
|
24
|
+
method: request.request_method_symbol,
|
|
25
|
+
ip_address: request.remote_ip,
|
|
26
|
+
user_agent: request.user_agent
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
before_validation do
|
|
30
|
+
self.campaign ||= Campaign.from_request(self)
|
|
31
|
+
self.browser ||= Browser.from_request(self) if user_agent
|
|
14
32
|
end
|
|
15
33
|
|
|
16
34
|
def outside_referer?
|
|
17
|
-
referer.present? &&
|
|
35
|
+
referer.present? && Addressable::URI.parse(referer).host != host
|
|
18
36
|
end
|
|
19
37
|
|
|
20
38
|
def any_utm_data?
|
|
@@ -25,90 +43,20 @@ module Sojourn
|
|
|
25
43
|
Hash[downcased_params.slice(*tracked_param_keys).delete_if { |_, v| v.blank? }.sort]
|
|
26
44
|
end
|
|
27
45
|
|
|
28
|
-
def raw_data
|
|
29
|
-
Hash[KEYS.map { |k| [k, send(k)] }].with_indifferent_access
|
|
30
|
-
end
|
|
31
|
-
|
|
32
46
|
def browser_data
|
|
33
47
|
return @browser_data if @browser_data
|
|
34
|
-
|
|
35
|
-
@browser_data
|
|
36
|
-
name:
|
|
37
|
-
version:
|
|
38
|
-
platform:
|
|
39
|
-
bot:
|
|
40
|
-
known:
|
|
48
|
+
b = browser.try(:send, :browser) || ::Browser.new(user_agent: user_agent)
|
|
49
|
+
@browser_data = {
|
|
50
|
+
name: b.name,
|
|
51
|
+
version: b.version,
|
|
52
|
+
platform: b.platform,
|
|
53
|
+
bot: b.bot?,
|
|
54
|
+
known: b.known?
|
|
41
55
|
}
|
|
42
56
|
end
|
|
43
57
|
|
|
44
|
-
def referer_data
|
|
45
|
-
return @referer_data if @referer_data
|
|
46
|
-
p = RefererParser::Parser.new.parse(sanitized_referer)
|
|
47
|
-
@referer_data = {
|
|
48
|
-
known: p[:known],
|
|
49
|
-
host: referer_host,
|
|
50
|
-
source: p[:source],
|
|
51
|
-
medium: p[:medium],
|
|
52
|
-
term: p[:term]
|
|
53
|
-
}
|
|
54
|
-
rescue
|
|
55
|
-
@referer_data = {}
|
|
56
|
-
end
|
|
57
|
-
|
|
58
58
|
private
|
|
59
59
|
|
|
60
|
-
def uuid
|
|
61
|
-
@uuid ||= request.uuid || SecureRandom.uuid
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def referer
|
|
65
|
-
@referer ||= request.referer.try(:truncate, 2048)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def host
|
|
69
|
-
@host ||= request.host.try(:truncate, 2048)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def path
|
|
73
|
-
@path ||= request.path.try(:truncate, 2048)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def controller
|
|
77
|
-
@controller ||= request.params[:controller]
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def action
|
|
81
|
-
@action ||= request.params[:action]
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def params
|
|
85
|
-
@params ||= request.filtered_parameters.with_indifferent_access.except(:controller, :action)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def method
|
|
89
|
-
@method ||= request.request_method_symbol
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def ip_address
|
|
93
|
-
@ip_address ||= request.remote_ip
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def user_agent
|
|
97
|
-
@user_agent ||= request.user_agent
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def referer_host
|
|
101
|
-
@referer_host ||= parsed_referer.host
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def sanitized_referer
|
|
105
|
-
@sanitized_referer ||= parsed_referer.display_uri
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def parsed_referer
|
|
109
|
-
@parsed_referer ||= Addressable::URI.parse(referer)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
60
|
def downcased_params
|
|
113
61
|
params.each_with_object({}) { |(k, v), h| h[k.to_s.downcase] = v }
|
|
114
62
|
end
|
|
@@ -116,9 +64,5 @@ module Sojourn
|
|
|
116
64
|
def tracked_param_keys
|
|
117
65
|
Sojourn.config.campaign_params.map(&:to_s).map(&:downcase)
|
|
118
66
|
end
|
|
119
|
-
|
|
120
|
-
def browser
|
|
121
|
-
@browser ||= Browser.new(user_agent: user_agent) if user_agent
|
|
122
|
-
end
|
|
123
67
|
end
|
|
124
68
|
end
|
data/lib/sojourn/tracker.rb
CHANGED
|
@@ -13,7 +13,8 @@ module Sojourn
|
|
|
13
13
|
def track!(event_name, properties = {}, user_id = current_user_id)
|
|
14
14
|
return unless Sojourn.tables_exist?
|
|
15
15
|
properties = default_event_properties.merge(properties)
|
|
16
|
-
Event.
|
|
16
|
+
Event.reset_column_information if Event.columns_hash['properties'].type != :jsonb
|
|
17
|
+
Event.create! sojourner_uuid: sojourner_uuid, name: event_name, request: request,
|
|
17
18
|
properties: properties, user_id: user_id
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -37,7 +38,7 @@ module Sojourn
|
|
|
37
38
|
private
|
|
38
39
|
|
|
39
40
|
def request
|
|
40
|
-
@request ||= Request.
|
|
41
|
+
@request ||= Request.from_request(ctx.request)
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def session
|
|
@@ -70,10 +71,8 @@ module Sojourn
|
|
|
70
71
|
Sojourn.config.default_properties_block
|
|
71
72
|
end
|
|
72
73
|
@ctx.sojourn_event_properties(properties) if @ctx.respond_to? :sojourn_event_properties
|
|
73
|
-
properties.merge! request
|
|
74
|
-
properties.merge! campaign: request.tracked_params if request.tracked_params.any?
|
|
74
|
+
properties.merge!(campaign: request.tracked_params) if request.tracked_params.any?
|
|
75
75
|
properties.merge! browser: request.browser_data
|
|
76
|
-
properties.merge! referer: request.referer_data if request.referer_data.any?
|
|
77
76
|
properties
|
|
78
77
|
end
|
|
79
78
|
end
|
data/lib/sojourn/version.rb
CHANGED
data/lib/sojourn.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Sojourn
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def self.tables_exist?
|
|
19
|
-
@tables_exist ||= %w(sojourn_events sojourn_requests)
|
|
19
|
+
@tables_exist ||= %w(sojourn_events sojourn_requests sojourn_browsers sojourn_campaigns)
|
|
20
20
|
.map { |t| ActiveRecord::Base.connection.table_exists?(t) }.all?
|
|
21
21
|
end
|
|
22
22
|
|
data/sojourn.gemspec
CHANGED
|
@@ -22,13 +22,7 @@ sojourn tracks the referer, utm data, and logged-in user (if any)).gsub("\n", '
|
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
|
24
24
|
spec.add_development_dependency 'rake', '~> 10.0'
|
|
25
|
-
spec.add_development_dependency 'rspec', '~> 3.4.0'
|
|
26
|
-
spec.add_development_dependency 'rspec-its', '~> 1.2.0'
|
|
27
|
-
spec.add_development_dependency 'sqlite3', '~> 1.3.0'
|
|
28
|
-
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.8'
|
|
29
25
|
|
|
30
26
|
spec.add_dependency 'browser', '>= 0.8.0'
|
|
31
27
|
spec.add_dependency 'addressable', '>= 2.3.1'
|
|
32
|
-
spec.add_dependency 'referer-parser', '~> 0.3.0'
|
|
33
|
-
spec.add_dependency 'rails', '~> 3.2.0'
|
|
34
28
|
end
|
metadata
CHANGED
|
@@ -1,190 +1,103 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sojourn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0.pre
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Smudge
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2015-03-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ~>
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '1.7'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ~>
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.7'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- -
|
|
31
|
+
- - ~>
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '10.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- -
|
|
38
|
+
- - ~>
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '10.0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: rspec
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: 3.4.0
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: 3.4.0
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: rspec-its
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - "~>"
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: 1.2.0
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - "~>"
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: 1.2.0
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: sqlite3
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: 1.3.0
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: 1.3.0
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: codeclimate-test-reporter
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: 0.4.8
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: 0.4.8
|
|
97
41
|
- !ruby/object:Gem::Dependency
|
|
98
42
|
name: browser
|
|
99
43
|
requirement: !ruby/object:Gem::Requirement
|
|
100
44
|
requirements:
|
|
101
|
-
- -
|
|
45
|
+
- - '>='
|
|
102
46
|
- !ruby/object:Gem::Version
|
|
103
47
|
version: 0.8.0
|
|
104
48
|
type: :runtime
|
|
105
49
|
prerelease: false
|
|
106
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
51
|
requirements:
|
|
108
|
-
- -
|
|
52
|
+
- - '>='
|
|
109
53
|
- !ruby/object:Gem::Version
|
|
110
54
|
version: 0.8.0
|
|
111
55
|
- !ruby/object:Gem::Dependency
|
|
112
56
|
name: addressable
|
|
113
57
|
requirement: !ruby/object:Gem::Requirement
|
|
114
58
|
requirements:
|
|
115
|
-
- -
|
|
59
|
+
- - '>='
|
|
116
60
|
- !ruby/object:Gem::Version
|
|
117
61
|
version: 2.3.1
|
|
118
62
|
type: :runtime
|
|
119
63
|
prerelease: false
|
|
120
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
65
|
requirements:
|
|
122
|
-
- -
|
|
66
|
+
- - '>='
|
|
123
67
|
- !ruby/object:Gem::Version
|
|
124
68
|
version: 2.3.1
|
|
125
|
-
|
|
126
|
-
name: referer-parser
|
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
|
128
|
-
requirements:
|
|
129
|
-
- - "~>"
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: 0.3.0
|
|
132
|
-
type: :runtime
|
|
133
|
-
prerelease: false
|
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
-
requirements:
|
|
136
|
-
- - "~>"
|
|
137
|
-
- !ruby/object:Gem::Version
|
|
138
|
-
version: 0.3.0
|
|
139
|
-
- !ruby/object:Gem::Dependency
|
|
140
|
-
name: rails
|
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
|
142
|
-
requirements:
|
|
143
|
-
- - "~>"
|
|
144
|
-
- !ruby/object:Gem::Version
|
|
145
|
-
version: 3.2.0
|
|
146
|
-
type: :runtime
|
|
147
|
-
prerelease: false
|
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
-
requirements:
|
|
150
|
-
- - "~>"
|
|
151
|
-
- !ruby/object:Gem::Version
|
|
152
|
-
version: 3.2.0
|
|
153
|
-
description: " Sojourn tracks site visitors and sources, with the ability to recognise
|
|
69
|
+
description: ' Sojourn tracks site visitors and sources, with the ability to recognise
|
|
154
70
|
multiple sources per visitor. Each time a new source is detected, sojourn tracks
|
|
155
|
-
the referer, utm data, and logged-in user (if any)
|
|
71
|
+
the referer, utm data, and logged-in user (if any)'
|
|
156
72
|
email:
|
|
157
73
|
- nathan@ngriffith.com
|
|
158
74
|
executables: []
|
|
159
75
|
extensions: []
|
|
160
76
|
extra_rdoc_files: []
|
|
161
77
|
files:
|
|
162
|
-
-
|
|
163
|
-
- ".gitignore"
|
|
164
|
-
- ".rubocop.yml"
|
|
165
|
-
- ".travis.yml"
|
|
78
|
+
- .gitignore
|
|
166
79
|
- Gemfile
|
|
167
80
|
- README.md
|
|
168
81
|
- Rakefile
|
|
169
82
|
- lib/generators/sojourn/install_generator.rb
|
|
170
83
|
- lib/generators/sojourn/templates/config_initializer.rb
|
|
84
|
+
- lib/generators/sojourn/templates/create_sojourn_browsers.rb
|
|
85
|
+
- lib/generators/sojourn/templates/create_sojourn_campaigns.rb
|
|
171
86
|
- lib/generators/sojourn/templates/create_sojourn_events.rb
|
|
87
|
+
- lib/generators/sojourn/templates/create_sojourn_requests.rb
|
|
172
88
|
- lib/sojourn.rb
|
|
89
|
+
- lib/sojourn/browser.rb
|
|
90
|
+
- lib/sojourn/campaign.rb
|
|
173
91
|
- lib/sojourn/configuration.rb
|
|
174
92
|
- lib/sojourn/controller.rb
|
|
175
93
|
- lib/sojourn/event.rb
|
|
176
94
|
- lib/sojourn/request.rb
|
|
177
95
|
- lib/sojourn/serializers/indifferent_json.rb
|
|
96
|
+
- lib/sojourn/serializers/symbol.rb
|
|
178
97
|
- lib/sojourn/session_stores/cookie.rb
|
|
179
98
|
- lib/sojourn/tracker.rb
|
|
180
99
|
- lib/sojourn/version.rb
|
|
181
100
|
- sojourn.gemspec
|
|
182
|
-
- spec/mocks/controller.rb
|
|
183
|
-
- spec/mocks/cookie.rb
|
|
184
|
-
- spec/mocks/request.rb
|
|
185
|
-
- spec/mocks/user.rb
|
|
186
|
-
- spec/spec_helper.rb
|
|
187
|
-
- spec/tracker_spec.rb
|
|
188
101
|
homepage: ''
|
|
189
102
|
licenses:
|
|
190
103
|
- ''
|
|
@@ -195,24 +108,18 @@ require_paths:
|
|
|
195
108
|
- lib
|
|
196
109
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
197
110
|
requirements:
|
|
198
|
-
- -
|
|
111
|
+
- - '>='
|
|
199
112
|
- !ruby/object:Gem::Version
|
|
200
113
|
version: '0'
|
|
201
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
202
115
|
requirements:
|
|
203
|
-
- -
|
|
116
|
+
- - '>'
|
|
204
117
|
- !ruby/object:Gem::Version
|
|
205
|
-
version:
|
|
118
|
+
version: 1.3.1
|
|
206
119
|
requirements: []
|
|
207
120
|
rubyforge_project:
|
|
208
|
-
rubygems_version: 2.4.
|
|
121
|
+
rubygems_version: 2.4.1
|
|
209
122
|
signing_key:
|
|
210
123
|
specification_version: 4
|
|
211
124
|
summary: Simple user source tracking for Rails.
|
|
212
|
-
test_files:
|
|
213
|
-
- spec/mocks/controller.rb
|
|
214
|
-
- spec/mocks/cookie.rb
|
|
215
|
-
- spec/mocks/request.rb
|
|
216
|
-
- spec/mocks/user.rb
|
|
217
|
-
- spec/spec_helper.rb
|
|
218
|
-
- spec/tracker_spec.rb
|
|
125
|
+
test_files: []
|
data/.codeclimate.yml
DELETED
data/.rubocop.yml
DELETED
data/.travis.yml
DELETED
data/spec/mocks/controller.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require 'mocks/user'
|
|
2
|
-
require 'mocks/request'
|
|
3
|
-
require 'mocks/cookie'
|
|
4
|
-
|
|
5
|
-
module Mocks
|
|
6
|
-
class Controller
|
|
7
|
-
attr_accessor :current_user, :request, :cookies
|
|
8
|
-
|
|
9
|
-
def initialize(user = User.new, request = Request.new, cookies = Cookie.new)
|
|
10
|
-
self.current_user = user
|
|
11
|
-
self.request = request
|
|
12
|
-
self.cookies = cookies
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
data/spec/mocks/cookie.rb
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Credit: https://kylecrum.wordpress.com/2009/07/19/mock-cookies-in-rails-tests-the-easy-way/
|
|
2
|
-
|
|
3
|
-
module Mocks
|
|
4
|
-
class Cookie < Hash
|
|
5
|
-
def [](name)
|
|
6
|
-
super(name)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def []=(key, options)
|
|
10
|
-
if options.is_a?(Hash)
|
|
11
|
-
options.symbolize_keys!
|
|
12
|
-
else
|
|
13
|
-
options = { value: options }
|
|
14
|
-
end
|
|
15
|
-
super(key, options[:value])
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def delete(key, _)
|
|
19
|
-
super(key)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def permanent
|
|
23
|
-
self
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def signed
|
|
27
|
-
self
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
data/spec/mocks/request.rb
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
require 'securerandom'
|
|
2
|
-
|
|
3
|
-
module Mocks
|
|
4
|
-
class Request
|
|
5
|
-
CHROME_UA = <<-EOF.squish
|
|
6
|
-
Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)
|
|
7
|
-
Chrome/41.0.2228.0 Safari/537.36
|
|
8
|
-
EOF
|
|
9
|
-
|
|
10
|
-
attr_reader :opts
|
|
11
|
-
|
|
12
|
-
def initialize(opts = {})
|
|
13
|
-
@opts = opts
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def uuid
|
|
17
|
-
opts[:uuid] || SecureRandom.uuid
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def referer
|
|
21
|
-
opts[:referer]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def host
|
|
25
|
-
opts[:host] || 'http://example.com'
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def path
|
|
29
|
-
opts[:path] || '/'
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def params
|
|
33
|
-
opts[:params] || {}.with_indifferent_access
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def method
|
|
37
|
-
opts[:method] || 'GET'
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def filtered_parameters
|
|
41
|
-
@filtered_parameters ||= params.merge(filtered: true)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def request_method_symbol
|
|
45
|
-
@request_method_symbol ||= method.downcase.to_sym
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def remote_ip
|
|
49
|
-
opts[:remote_ip] || '192.168.1.1'
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def user_agent
|
|
53
|
-
opts.key?(:user_agent) ? opts[:user_agent] : CHROME_UA
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
data/spec/mocks/user.rb
DELETED
data/spec/spec_helper.rb
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
require 'codeclimate-test-reporter'
|
|
2
|
-
CodeClimate::TestReporter.start
|
|
3
|
-
|
|
4
|
-
require 'bundler/setup'
|
|
5
|
-
Bundler.setup
|
|
6
|
-
|
|
7
|
-
require 'rspec/its'
|
|
8
|
-
require 'rails'
|
|
9
|
-
require 'active_record'
|
|
10
|
-
require 'sojourn'
|
|
11
|
-
|
|
12
|
-
RSpec.configure do |config|
|
|
13
|
-
config.before(:suite) do
|
|
14
|
-
ActiveRecord::Base.establish_connection adapter: 'sqlite3',
|
|
15
|
-
database: ':memory:'
|
|
16
|
-
|
|
17
|
-
ActiveRecord::Schema.define do
|
|
18
|
-
self.verbose = false
|
|
19
|
-
|
|
20
|
-
create_table :sojourn_events do |t|
|
|
21
|
-
t.string :sojourner_uuid, limit: 36, null: false
|
|
22
|
-
t.string :name
|
|
23
|
-
t.text :properties
|
|
24
|
-
t.references :sojourn_request
|
|
25
|
-
t.references :user
|
|
26
|
-
t.timestamp :created_at
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
create_table :sojourn_requests do |t|
|
|
30
|
-
t.string :host, limit: 2048
|
|
31
|
-
t.string :path, limit: 2048
|
|
32
|
-
t.string :method
|
|
33
|
-
t.string :controller
|
|
34
|
-
t.string :action
|
|
35
|
-
t.string :ip_address
|
|
36
|
-
t.text :user_agent
|
|
37
|
-
t.text :params
|
|
38
|
-
t.text :referer
|
|
39
|
-
t.timestamp :created_at
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
config.before do
|
|
45
|
-
Sojourn::Event.delete_all
|
|
46
|
-
end
|
|
47
|
-
end
|
data/spec/tracker_spec.rb
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'securerandom'
|
|
3
|
-
require 'mocks/controller'
|
|
4
|
-
|
|
5
|
-
module Sojourn
|
|
6
|
-
COOKIE_NAME = Sojourn.config.cookie_name
|
|
7
|
-
|
|
8
|
-
describe Tracker do
|
|
9
|
-
let(:user) { Mocks::User.new }
|
|
10
|
-
let(:request) { Mocks::Request.new }
|
|
11
|
-
let(:cookies) { Mocks::Cookie.new }
|
|
12
|
-
let(:ctx) { Mocks::Controller.new(user, request, cookies) }
|
|
13
|
-
let(:tracker) { Tracker.new(ctx) }
|
|
14
|
-
|
|
15
|
-
describe '#track!' do
|
|
16
|
-
let(:event_name) { 'foo' }
|
|
17
|
-
let(:opts) { { bar: true } }
|
|
18
|
-
before { tracker.track!(event_name, opts) }
|
|
19
|
-
subject { Event.last }
|
|
20
|
-
|
|
21
|
-
its(:user_id) { is_expected.to eq(user.id) }
|
|
22
|
-
its(:name) { is_expected.to eq(event_name) }
|
|
23
|
-
|
|
24
|
-
describe 'properties' do
|
|
25
|
-
subject { Event.last.properties }
|
|
26
|
-
|
|
27
|
-
its(:keys) { is_expected.to eq(%w(request browser bar)) }
|
|
28
|
-
its([:request, :params]) { is_expected.to eq('filtered' => true) }
|
|
29
|
-
its([:request, :method]) { is_expected.to eq('get') }
|
|
30
|
-
its([:browser, :name]) { is_expected.to eq('Chrome') }
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
describe '#sojourning!' do
|
|
35
|
-
before { tracker.sojourning! }
|
|
36
|
-
subject { Event.last }
|
|
37
|
-
|
|
38
|
-
its(:name) { is_expected.to eq('!sojourning') }
|
|
39
|
-
|
|
40
|
-
context 'when already tracked once' do
|
|
41
|
-
before do
|
|
42
|
-
tracker.update_session!
|
|
43
|
-
Sojourn::Event.delete_all
|
|
44
|
-
tracker.sojourning!
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it { is_expected.to be_nil }
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
context 'when user changes' do
|
|
51
|
-
before do
|
|
52
|
-
tracker.update_session!
|
|
53
|
-
Sojourn::Event.delete_all
|
|
54
|
-
ctx.current_user = Mocks::User.new
|
|
55
|
-
tracker.sojourning!
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
its(:name) { is_expected.to eq('!logged_in') }
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
context 'when user logs out' do
|
|
62
|
-
before do
|
|
63
|
-
tracker.update_session!
|
|
64
|
-
Sojourn::Event.delete_all
|
|
65
|
-
ctx.current_user = nil
|
|
66
|
-
tracker.sojourning!
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
its(:name) { is_expected.to eq('!logged_out') }
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
context 'when user agent not present' do
|
|
73
|
-
let(:request) { Mocks::Request.new(user_agent: nil) }
|
|
74
|
-
|
|
75
|
-
its(:name) { is_expected.to eq('!sojourning') }
|
|
76
|
-
|
|
77
|
-
describe 'properties' do
|
|
78
|
-
subject { Event.last.properties }
|
|
79
|
-
|
|
80
|
-
its([:browser, :known]) { is_expected.to be(false) }
|
|
81
|
-
its([:browser, :name]) { is_expected.to be_nil }
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
describe 'update_session!' do
|
|
87
|
-
subject { cookies }
|
|
88
|
-
|
|
89
|
-
context 'before' do
|
|
90
|
-
its([COOKIE_NAME]) { is_expected.to be_nil }
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
context 'after running' do
|
|
94
|
-
before { tracker.update_session! }
|
|
95
|
-
|
|
96
|
-
its([COOKIE_NAME, :uuid]) { is_expected.to_not be_nil }
|
|
97
|
-
its([COOKIE_NAME, :user_id]) { is_expected.to eq(user.id) }
|
|
98
|
-
|
|
99
|
-
context 'with existing cookie' do
|
|
100
|
-
let(:sojourner_uuid) { SecureRandom.uuid }
|
|
101
|
-
let(:cookie_data) { { user_id: user.id, uuid: sojourner_uuid } }
|
|
102
|
-
let(:cookies) { Mocks::Cookie[{ COOKIE_NAME => cookie_data }] }
|
|
103
|
-
|
|
104
|
-
its([COOKIE_NAME, :uuid]) { is_expected.to eq(sojourner_uuid) }
|
|
105
|
-
its([COOKIE_NAME, :user_id]) { is_expected.to eq(user.id) }
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|