test_track_rails_client 3.0.1 → 4.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -40
- data/app/models/concerns/test_track/identity.rb +10 -10
- data/app/models/test_track/analytics/mixpanel_client.rb +2 -10
- data/app/models/test_track/analytics/safe_wrapper.rb +2 -2
- data/app/models/test_track/analytics_event.rb +28 -0
- data/app/models/test_track/assignment.rb +12 -0
- data/app/models/test_track/fake/visitor.rb +2 -2
- data/app/models/test_track/{identity_session_discriminator.rb → identity_session_locator.rb} +3 -7
- data/app/models/test_track/notify_assignment_job.rb +10 -7
- data/app/models/test_track/session.rb +21 -0
- data/lib/test_track_rails_client/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15d95f4893c2f57f54d755bc1c8bb7130537767c
|
4
|
+
data.tar.gz: 5a26c66732114f90119b54636ae6e7122cb6efeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 654863b402ff1e8a8ae2257999afb947c1ac3875fbb205c6b8de628b439f7dfafc84066f51d167aa4feb9082ec97466a7bb3928a6111e7d4673eb63020278683
|
7
|
+
data.tar.gz: 6704876202051c7d04dec540c5adb74a6d20d8fc9344d616d014d9355959e542a317dbeb1d66facc5e28434ba1ac57a5669a52ae8097c82caa7b35dc852d7ec7
|
data/README.md
CHANGED
@@ -349,9 +349,10 @@ Your client must implement the following methods:
|
|
349
349
|
```ruby
|
350
350
|
# Called when a new Split has been Assigned
|
351
351
|
#
|
352
|
-
# @param
|
353
|
-
#
|
354
|
-
|
352
|
+
# @param analytics_event [TestTrack::AnalyticsEvent] An object
|
353
|
+
# representing an analytics event providing name and properties
|
354
|
+
# values you can send to your analytics backend
|
355
|
+
def track(analytics_event)
|
355
356
|
|
356
357
|
# Called after TestTrack.sign_up!
|
357
358
|
#
|
@@ -361,7 +362,7 @@ def sign_up!(visitor_id)
|
|
361
362
|
|
362
363
|
### Using TestTrack with a new analytics tool
|
363
364
|
|
364
|
-
TestTrack manages its own visitor identifier which is different from the identifier of your analytics tool. We recommend using TestTrack's visitor identifier as your analytics identifier when possible. Within TestTrack Rails Client, assignment events will trigger a call to `TestTrack.analytics.
|
365
|
+
TestTrack manages its own visitor identifier which is different from the identifier of your analytics tool. We recommend using TestTrack's visitor identifier as your analytics identifier when possible. Within TestTrack Rails Client, assignment events will trigger a call to `TestTrack.analytics.track` with a TestTrack visitor identifier. To ensure that analytics events coming from within the browser have the right identifier, you must set the identifier when your analytics javascript library is loaded.
|
365
366
|
|
366
367
|
Here's an example for how to do it with Mixpanel:
|
367
368
|
|
@@ -373,44 +374,15 @@ mixpanel.init('YOUR MIXPANEL TOKEN', {
|
|
373
374
|
});
|
374
375
|
```
|
375
376
|
|
376
|
-
|
377
|
-
|
378
|
-
In cases where you have already established identities with your analytics tool, you can use TestTrack's hooks to link TestTrack's visitor identifier to your analytics identifier.
|
379
|
-
|
380
|
-
Using Mixpanel as an example, you must call [`mixpanel.alias`](https://mixpanel.com/help/questions/articles/assigning-your-own-unique-ids-to-users) with the visitor's analytics identifier and their TestTrack visitor identifier. This will establish a link between those two identifiers within your analytics tool, allowing you to analyze your funnels. We recommend using a thread-local storage like [`RequestStore`](https://github.com/steveklabnik/request_store) to make your identifier available to the TestTrack analytics client.
|
381
|
-
|
382
|
-
In your controller, store the Mixpanel distinct ID in the RequestStore.
|
383
|
-
|
384
|
-
```ruby
|
385
|
-
class ApplicationController < ActionController::Base
|
386
|
-
before_action :store_mixpanel_distinct_id
|
387
|
-
|
388
|
-
def store_mixpanel_distinct_id
|
389
|
-
RequestStore[:mixpanel_distinct_id] = cookies["mp_#{ENV['MIXPANEL_TOKEN']}_mixpanel"]
|
390
|
-
end
|
391
|
-
end
|
392
|
-
```
|
377
|
+
## Upgrading
|
393
378
|
|
394
|
-
|
379
|
+
### From 3.0 to 4.0
|
395
380
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
SplitVariant: assignment.variant,
|
402
|
-
SplitContext: assignment.context
|
403
|
-
}
|
404
|
-
|
405
|
-
mixpanel.track(identifier, 'SplitAssigned', properties)
|
406
|
-
end
|
407
|
-
|
408
|
-
def sign_up!(visitor_id)
|
409
|
-
mixpanel.alias(visitor_id, RequestStore[:mixpanel_distinct_id])
|
410
|
-
end
|
411
|
-
```
|
412
|
-
|
413
|
-
## Upgrading
|
381
|
+
The contract of custom analytics plugins has changed. Instead of
|
382
|
+
implementing `track_assignment` you now must implement `track`. It's
|
383
|
+
easier and more conventional, though, and takes care of differentiating
|
384
|
+
between expiriment assignments and feature gate experiences, which are
|
385
|
+
no longer recorded server-side.
|
414
386
|
|
415
387
|
### From 2.0 to 3.0
|
416
388
|
|
@@ -16,36 +16,36 @@ module TestTrack::Identity
|
|
16
16
|
end
|
17
17
|
|
18
18
|
define_method :test_track_ab do |*args|
|
19
|
-
|
20
|
-
|
19
|
+
locator = TestTrack::IdentitySessionLocator.new(self)
|
20
|
+
locator.with_visitor do |v|
|
21
21
|
v.ab(*args)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
define_method :test_track_vary do |*args, &block|
|
26
|
-
|
27
|
-
|
26
|
+
locator = TestTrack::IdentitySessionLocator.new(self)
|
27
|
+
locator.with_visitor do |v|
|
28
28
|
v.vary(*args, &block)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
define_method :test_track_visitor_id do
|
33
|
-
|
34
|
-
|
33
|
+
locator = TestTrack::IdentitySessionLocator.new(self)
|
34
|
+
locator.with_visitor do |v|
|
35
35
|
v.id
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
define_method :test_track_sign_up! do
|
40
|
-
|
41
|
-
|
40
|
+
locator = TestTrack::IdentitySessionLocator.new(self)
|
41
|
+
locator.with_session do |session|
|
42
42
|
session.sign_up! self
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
define_method :test_track_log_in! do |opts = {}|
|
47
|
-
|
48
|
-
|
47
|
+
locator = TestTrack::IdentitySessionLocator.new(self)
|
48
|
+
locator.with_session do |session|
|
49
49
|
session.log_in! self, opts
|
50
50
|
end
|
51
51
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module TestTrack::Analytics
|
2
2
|
class MixpanelClient
|
3
|
-
def
|
4
|
-
mixpanel.track(visitor_id,
|
3
|
+
def track(analytics_event)
|
4
|
+
mixpanel.track(analytics_event.visitor_id, analytics_event.name, analytics_event.properties)
|
5
5
|
end
|
6
6
|
|
7
7
|
private
|
@@ -10,13 +10,5 @@ module TestTrack::Analytics
|
|
10
10
|
raise "ENV['MIXPANEL_TOKEN'] must be set" unless ENV['MIXPANEL_TOKEN']
|
11
11
|
@mixpanel ||= Mixpanel::Tracker.new(ENV['MIXPANEL_TOKEN'])
|
12
12
|
end
|
13
|
-
|
14
|
-
def split_properties(assignment)
|
15
|
-
{
|
16
|
-
SplitName: assignment.split_name,
|
17
|
-
SplitVariant: assignment.variant,
|
18
|
-
SplitContext: assignment.context
|
19
|
-
}
|
20
|
-
end
|
21
13
|
end
|
22
14
|
end
|
@@ -12,8 +12,8 @@ module TestTrack::Analytics
|
|
12
12
|
@error_handler = handler
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
safe_action { underlying.
|
15
|
+
def track(analytics_event)
|
16
|
+
safe_action { underlying.track(analytics_event) }
|
17
17
|
end
|
18
18
|
|
19
19
|
def sign_up!(visitor_id)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module TestTrack
|
2
|
+
class AnalyticsEvent
|
3
|
+
attr_reader :assignment
|
4
|
+
|
5
|
+
delegate :visitor_id, to: :assignment
|
6
|
+
|
7
|
+
def initialize(assignment)
|
8
|
+
@assignment = assignment
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
if assignment.feature_gate?
|
13
|
+
'FeatureGateExperienced'
|
14
|
+
else
|
15
|
+
'SplitAssigned'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def properties
|
20
|
+
{
|
21
|
+
TTVisitorID: visitor_id,
|
22
|
+
SplitName: assignment.split_name,
|
23
|
+
SplitVariant: assignment.variant,
|
24
|
+
SplitContext: assignment.context
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -19,6 +19,18 @@ class TestTrack::Assignment
|
|
19
19
|
true
|
20
20
|
end
|
21
21
|
|
22
|
+
def feature_gate?
|
23
|
+
split_name.end_with?('_enabled')
|
24
|
+
end
|
25
|
+
|
26
|
+
def analytics_event
|
27
|
+
@analytics_event ||= AnalyticsEvent.new(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def visitor_id
|
31
|
+
visitor.id
|
32
|
+
end
|
33
|
+
|
22
34
|
private
|
23
35
|
|
24
36
|
def _variant
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class TestTrack::Fake::Visitor
|
2
2
|
attr_reader :id
|
3
3
|
|
4
|
-
Assignment = Struct.new(:split_name, :variant, :unsynced, :context)
|
4
|
+
Assignment = Struct.new(:split_name, :variant, :unsynced, :context, :visitor_id)
|
5
5
|
|
6
6
|
def self.instance
|
7
7
|
@instance ||= new(TestTrack::FakeServer.seed)
|
@@ -28,7 +28,7 @@ class TestTrack::Fake::Visitor
|
|
28
28
|
def _assignments
|
29
29
|
split_registry.keys.map do |split_name|
|
30
30
|
variant = TestTrack::VariantCalculator.new(visitor: self, split_name: split_name).variant
|
31
|
-
Assignment.new(split_name, variant, false, "the_context")
|
31
|
+
Assignment.new(split_name, variant, false, "the_context", id)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
data/app/models/test_track/{identity_session_discriminator.rb → identity_session_locator.rb}
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
class TestTrack::
|
1
|
+
class TestTrack::IdentitySessionLocator
|
2
2
|
attr_reader :identity
|
3
3
|
|
4
4
|
def initialize(identity)
|
@@ -8,8 +8,8 @@ class TestTrack::IdentitySessionDiscriminator
|
|
8
8
|
def with_visitor
|
9
9
|
raise ArgumentError, "must provide block to `with_visitor`" unless block_given?
|
10
10
|
|
11
|
-
if
|
12
|
-
yield session.
|
11
|
+
if web_context?
|
12
|
+
yield session.visitor_dsl_for(identity)
|
13
13
|
else
|
14
14
|
TestTrack::OfflineSession.with_visitor_for(identity.test_track_identifier_type, identity.test_track_identifier_value) do |v|
|
15
15
|
yield v
|
@@ -29,10 +29,6 @@ class TestTrack::IdentitySessionDiscriminator
|
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
|
-
def matching_identity?
|
33
|
-
session.present? && session.has_matching_identity?(identity)
|
34
|
-
end
|
35
|
-
|
36
32
|
def web_context?
|
37
33
|
session.present?
|
38
34
|
end
|
@@ -12,19 +12,22 @@ class TestTrack::NotifyAssignmentJob
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def perform
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
tracking_result = track
|
16
|
+
unless assignment.feature_gate?
|
17
|
+
TestTrack::Remote::AssignmentEvent.create!(
|
18
|
+
visitor_id: visitor_id,
|
19
|
+
split_name: assignment.split_name,
|
20
|
+
context: assignment.context,
|
21
|
+
mixpanel_result: tracking_result
|
22
|
+
)
|
23
|
+
end
|
21
24
|
end
|
22
25
|
|
23
26
|
private
|
24
27
|
|
25
28
|
def track
|
26
29
|
return "failure" unless TestTrack.enabled?
|
27
|
-
result = TestTrack.analytics.
|
30
|
+
result = TestTrack.analytics.track(assignment.analytics_event)
|
28
31
|
result ? "success" : "failure"
|
29
32
|
end
|
30
33
|
end
|
@@ -16,6 +16,27 @@ class TestTrack::Session
|
|
16
16
|
notify_unsynced_assignments! if sync_assignments?
|
17
17
|
end
|
18
18
|
|
19
|
+
def visitor_dsl_for(identity)
|
20
|
+
if has_matching_identity?(identity)
|
21
|
+
visitor_dsl
|
22
|
+
else
|
23
|
+
TestTrack::VisitorDSL.new(visitors_by_identity[identity])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def visitors_by_identity
|
28
|
+
@visitors_by_identity ||= Hash.new do |visitors_by_identity, identity|
|
29
|
+
remote_visitor = TestTrack::Remote::Visitor.from_identifier(
|
30
|
+
identity.test_track_identifier_type,
|
31
|
+
identity.test_track_identifier_value
|
32
|
+
)
|
33
|
+
visitors_by_identity[identity] = TestTrack::Visitor.new(
|
34
|
+
id: remote_visitor.id,
|
35
|
+
assignments: remote_visitor.assignments
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
19
40
|
def visitor_dsl
|
20
41
|
@visitor_dsl ||= TestTrack::VisitorDSL.new(visitor)
|
21
42
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test_track_rails_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0.alpha1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan O'Neill
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2018-04-
|
16
|
+
date: 2018-04-28 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: airbrake
|
@@ -259,6 +259,7 @@ files:
|
|
259
259
|
- app/models/test_track/ab_configuration.rb
|
260
260
|
- app/models/test_track/analytics/mixpanel_client.rb
|
261
261
|
- app/models/test_track/analytics/safe_wrapper.rb
|
262
|
+
- app/models/test_track/analytics_event.rb
|
262
263
|
- app/models/test_track/assignment.rb
|
263
264
|
- app/models/test_track/config_updater.rb
|
264
265
|
- app/models/test_track/fake/split_detail.rb
|
@@ -266,7 +267,7 @@ files:
|
|
266
267
|
- app/models/test_track/fake/visitor.rb
|
267
268
|
- app/models/test_track/fake/visitor_detail.rb
|
268
269
|
- app/models/test_track/fake_server.rb
|
269
|
-
- app/models/test_track/
|
270
|
+
- app/models/test_track/identity_session_locator.rb
|
270
271
|
- app/models/test_track/misconfiguration_notifier.rb
|
271
272
|
- app/models/test_track/notify_assignment_job.rb
|
272
273
|
- app/models/test_track/offline_session.rb
|
@@ -356,9 +357,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
356
357
|
version: 2.1.0
|
357
358
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
358
359
|
requirements:
|
359
|
-
- - "
|
360
|
+
- - ">"
|
360
361
|
- !ruby/object:Gem::Version
|
361
|
-
version:
|
362
|
+
version: 1.3.1
|
362
363
|
requirements: []
|
363
364
|
rubyforge_project:
|
364
365
|
rubygems_version: 2.5.1
|