test_track_rails_client 4.0.0.alpha7 → 4.0.0.alpha8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -48
- data/app/controllers/concerns/test_track/controller.rb +2 -0
- data/app/models/test_track/ab_configuration.rb +3 -1
- data/app/models/test_track/lazy_visitor_by_identity.rb +38 -0
- data/app/models/test_track/session.rb +38 -84
- data/app/models/test_track/session_visitor_repository.rb +52 -0
- data/app/models/test_track/threaded_visitor_notifier.rb +34 -0
- data/app/models/test_track/visitor.rb +28 -7
- data/lib/test_track_rails_client/version.rb +1 -1
- metadata +5 -3
- data/app/models/test_track/session_identity_collection.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c421a570a50e10dbfdff7420aafa19964ee4c01
|
4
|
+
data.tar.gz: 901a202cbb0db2717ba240b6466f6408ddb1b3c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 684fa6562e8a88bd4092bbd2cf0f5527f84c4c4e55b307c5ca9ec88f4d965cfb193aec37fb20a08aefda96588fbc34597f9b1b91198bf3390c3dde7c09484505
|
7
|
+
data.tar.gz: 63edb85ee37e5eba3d15fc2f926eca2aae470bf0f927d2b5cde366206edc48f8b12ebc685849343fd06fb66a54a53aa101e51606e02c4869923bad91694c750c
|
data/README.md
CHANGED
@@ -21,13 +21,15 @@ If you're looking to do client-side assignment, then check out our [JS client](h
|
|
21
21
|
|
22
22
|
## Installation
|
23
23
|
|
24
|
-
Install the gem:
|
24
|
+
### Install the gem:
|
25
25
|
|
26
26
|
```ruby
|
27
27
|
# Gemfile
|
28
28
|
gem 'test_track_rails_client'
|
29
29
|
```
|
30
30
|
|
31
|
+
### Create an app in the TestTrack server
|
32
|
+
|
31
33
|
In every environment (local included) cut an App record via the **TestTrack server** rails console:
|
32
34
|
|
33
35
|
```ruby
|
@@ -40,6 +42,8 @@ README](https://github.com/Betterment/test_track/blob/master/README.md#user-cont
|
|
40
42
|
for additional information on configuring seed apps for local
|
41
43
|
development.
|
42
44
|
|
45
|
+
### Set up ENV vars
|
46
|
+
|
43
47
|
Set up ENV vars in every environment:
|
44
48
|
|
45
49
|
* `MIXPANEL_TOKEN` - By default, TestTrack reports to Mixpanel. If you're using a [custom analytics provider](#custom-analytics) you can omit this.
|
@@ -51,15 +55,28 @@ Set up ENV vars in every environment:
|
|
51
55
|
* `example.org`
|
52
56
|
* etc
|
53
57
|
|
58
|
+
### Prepare your controllers
|
54
59
|
|
55
|
-
Mix `TestTrack::Controller` into any controllers needing access to TestTrack
|
60
|
+
Mix `TestTrack::Controller` into any controllers needing access to TestTrack and configure it with the name of your `:current_user` method.
|
56
61
|
|
57
62
|
```ruby
|
58
63
|
class MyController < ApplicationController
|
59
64
|
include TestTrack::Controller
|
65
|
+
|
66
|
+
self.test_track_identity = :current_user
|
60
67
|
end
|
61
68
|
```
|
62
69
|
|
70
|
+
If your app doesn't support authentication, set
|
71
|
+
`self.test_track_identity` to `:none`.
|
72
|
+
|
73
|
+
### Prepare your identity models (optional)
|
74
|
+
|
75
|
+
If your app supports authentication, You'll need to configure your
|
76
|
+
`User` model as a [TestTrack Identity](#varying-app-behavior-from-within-a-model)
|
77
|
+
|
78
|
+
### Set up the Chrome extension (optional)
|
79
|
+
|
63
80
|
If you'd like to be able to use the [TestTrack Chrome Extension](https://github.com/Betterment/test_track_chrome_extension) which makes it easy for you and your team to change assignments via your browser, you **must** set up the TestTrack JS client.
|
64
81
|
|
65
82
|
1. `testTrack.bundle.min` in your `application.js` file after your reference to jQuery
|
@@ -239,51 +256,9 @@ if test_track_visitor.ab :dark_deployed_feature, context: 'signup'
|
|
239
256
|
end
|
240
257
|
```
|
241
258
|
|
242
|
-
### Varying app behavior in an offline context
|
243
|
-
|
244
|
-
The `OfflineSession` class can be used to load a test track visitor when there is no access to browser cookies. It is perfect for use in a process being run from either a job queue or a scheduler. The visitor object that is yielded to the block is the same as the visitor in a controller context; it has both the `vary` and `ab` methods.
|
245
|
-
|
246
|
-
An `OfflineSession` can be established in one of two ways:
|
247
|
-
|
248
|
-
1. with an `identifier_type`:
|
249
|
-
```ruby
|
250
|
-
OfflineSession.with_visitor_for(:myapp_user_id, 1234) do |test_track_visitor|
|
251
|
-
test_track_visitor.vary :name_of_split, context: 'background_job' do |v|
|
252
|
-
v.when :variant_1, :variant_2 do
|
253
|
-
# Do something
|
254
|
-
end
|
255
|
-
v.when :variant_3 do
|
256
|
-
# Do another thing
|
257
|
-
end
|
258
|
-
v.default :variant_4 do
|
259
|
-
# Do something else
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
```
|
264
|
-
|
265
|
-
2. with a `TestTrack::Visitor#id`:
|
266
|
-
```ruby
|
267
|
-
OfflineSession.with_visitor_id(1234) do |test_track_visitor|
|
268
|
-
test_track_visitor.vary :name_of_split, context: 'background_job' do |v|
|
269
|
-
v.when :variant_1, :variant_2 do
|
270
|
-
# Do something
|
271
|
-
end
|
272
|
-
v.when :variant_3 do
|
273
|
-
# Do another thing
|
274
|
-
end
|
275
|
-
v.default :variant_4 do
|
276
|
-
# Do something else
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
|
-
```
|
281
|
-
|
282
259
|
### Varying app behavior from within a model
|
283
260
|
|
284
|
-
The `TestTrack::Identity` concern can be included in a model and it will add two methods to the model: `test_track_vary` and `test_track_ab`.
|
285
|
-
|
286
|
-
Because these methods may need to stand up an `OfflineSession` the consuming model needs to provide both the identifier type and which column should be used as the identifier value via the `test_track_identifier` method so that the `OfflineSession` can grab the correct visitor.
|
261
|
+
The `TestTrack::Identity` concern can be included in a model and it will add two methods to the model: `test_track_vary` and `test_track_ab`.
|
287
262
|
|
288
263
|
```ruby
|
289
264
|
class User
|
@@ -293,8 +268,6 @@ class User
|
|
293
268
|
end
|
294
269
|
```
|
295
270
|
|
296
|
-
N.B. If you call `test_track_vary` and `test_track_ab` on a model in a web context, but that model is not the currently authenticated model, an `OfflineSession` will be created instead of participating in the existing session.
|
297
|
-
|
298
271
|
## Tracking visitor logins
|
299
272
|
|
300
273
|
The `test_track_visitor.log_in!` is used to ensure a consistent experience across devices. For instance, when a user logs in to your app on their mobile device we can log in to Test Track in order to grab their existing split assignments instead of treating them like a new visitor.
|
@@ -317,7 +290,7 @@ The `test_track_visitor.sign_up!` method tells TestTrack when a new identifier h
|
|
317
290
|
test_track_visitor.sign_up!(:myapp_user_id, 2345)
|
318
291
|
```
|
319
292
|
|
320
|
-
## Testing
|
293
|
+
## Testing your split-dependent application code with RSpec
|
321
294
|
|
322
295
|
Add this line to your `rails_helper.rb`:
|
323
296
|
|
@@ -384,6 +357,14 @@ easier and more conventional, though, and takes care of differentiating
|
|
384
357
|
between expiriment assignments and feature gate experiences, which are
|
385
358
|
no longer recorded server-side.
|
386
359
|
|
360
|
+
You also must add `self.test_track_identity = :current_user` (or
|
361
|
+
whatever your controller uses as a sign-in identity) to your
|
362
|
+
TestTrack-enabled controllers, or set it to `:none` if your app doesn't
|
363
|
+
support authentication.
|
364
|
+
|
365
|
+
If your app supports authentication, You'll need to configure your
|
366
|
+
user model as a [TestTrack Identity](#varying-app-behavior-from-within-a-model)
|
367
|
+
|
387
368
|
### From 2.0 to 3.0
|
388
369
|
|
389
370
|
TestTrack Rails Client no longer manages your Mixpanel cookie. The analytics plugin now provides a callback on `sign_up!` that will allow you to implement this functionality within your application. Please see the [analytics documentation](#analytics) for more details.
|
@@ -19,7 +19,9 @@ class TestTrack::ABConfiguration
|
|
19
19
|
private
|
20
20
|
|
21
21
|
def build_variant_hash
|
22
|
-
|
22
|
+
if split_variants && split_variants.size > 2 # rubocop:disable Style/SafeNavigation
|
23
|
+
notify_because_ab("configures split with more than 2 variants")
|
24
|
+
end
|
23
25
|
{ true: true_variant, false: false_variant }
|
24
26
|
end
|
25
27
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class TestTrack::LazyVisitorByIdentity
|
2
|
+
def initialize(identity)
|
3
|
+
@identity = identity
|
4
|
+
end
|
5
|
+
|
6
|
+
def loaded?
|
7
|
+
@visitor.present?
|
8
|
+
end
|
9
|
+
|
10
|
+
def id_loaded?
|
11
|
+
loaded?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
|
17
|
+
__visitor__.send(method, *args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_missing?(method, include_private = false)
|
21
|
+
super || __visitor__.respond_to?(method, include_private)
|
22
|
+
end
|
23
|
+
|
24
|
+
def __visitor__
|
25
|
+
@visitor ||= __load_visitor__
|
26
|
+
end
|
27
|
+
|
28
|
+
def __load_visitor__
|
29
|
+
remote_visitor = TestTrack::Remote::Visitor.from_identifier(
|
30
|
+
@identity.test_track_identifier_type,
|
31
|
+
@identity.test_track_identifier_value
|
32
|
+
)
|
33
|
+
TestTrack::Visitor.new(
|
34
|
+
id: remote_visitor.id,
|
35
|
+
assignments: remote_visitor.assignments
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
@@ -11,34 +11,19 @@ class TestTrack::Session
|
|
11
11
|
def manage
|
12
12
|
yield
|
13
13
|
ensure
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
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])
|
14
|
+
if current_visitor.id_loaded?
|
15
|
+
manage_cookies!
|
16
|
+
manage_response_headers!
|
24
17
|
end
|
18
|
+
visitors.notify_unsynced_assignments!
|
25
19
|
end
|
26
20
|
|
27
|
-
def
|
28
|
-
|
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
|
21
|
+
def visitor_dsl_for(identity)
|
22
|
+
TestTrack::VisitorDSL.new(visitors.for_identity(identity))
|
38
23
|
end
|
39
24
|
|
40
25
|
def visitor_dsl
|
41
|
-
|
26
|
+
TestTrack::VisitorDSL.new(current_visitor)
|
42
27
|
end
|
43
28
|
|
44
29
|
def state_hash
|
@@ -46,50 +31,55 @@ class TestTrack::Session
|
|
46
31
|
url: TestTrack.url,
|
47
32
|
cookieDomain: cookie_domain,
|
48
33
|
cookieName: visitor_cookie_name,
|
49
|
-
registry:
|
50
|
-
assignments:
|
34
|
+
registry: current_visitor.split_registry,
|
35
|
+
assignments: current_visitor.assignment_json
|
51
36
|
}
|
52
37
|
end
|
53
38
|
|
54
39
|
def log_in!(identity, forget_current_visitor: nil)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@visitor = TestTrack::Visitor.new if forget_current_visitor
|
59
|
-
visitor.link_identifier!(identifier_type, identifier_value)
|
60
|
-
|
61
|
-
identities << identity if identity.present?
|
40
|
+
visitors.forget_unauthenticated! if forget_current_visitor
|
41
|
+
visitors.authenticate!(identity)
|
62
42
|
true
|
63
43
|
end
|
64
44
|
|
65
45
|
def sign_up!(identity)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
visitor.link_identifier!(identifier_type, identifier_value)
|
70
|
-
identities << identity if identity.present?
|
71
|
-
|
72
|
-
TestTrack.analytics.sign_up!(visitor.id)
|
73
|
-
|
46
|
+
visitors.authenticate!(identity)
|
47
|
+
TestTrack.analytics.sign_up!(current_visitor.id)
|
74
48
|
true
|
75
49
|
end
|
76
50
|
|
77
|
-
def has_matching_identity?(identity)
|
78
|
-
identities.include?(identity)
|
79
|
-
end
|
80
|
-
|
81
51
|
private
|
82
52
|
|
83
53
|
attr_reader :controller
|
84
54
|
|
85
|
-
def
|
86
|
-
|
55
|
+
def current_identity
|
56
|
+
raise <<~ERROR unless controller.class.test_track_identity&.is_a?(Symbol)
|
57
|
+
Your controller (or controller base class) must set test_track_identity for
|
58
|
+
TestTrack to work properly. e.g.:
|
59
|
+
|
60
|
+
self.test_track_identity = :current_user
|
61
|
+
|
62
|
+
If your app doesn't support authentication, set it to `:none`.
|
63
|
+
ERROR
|
64
|
+
identity = controller.class.test_track_identity
|
65
|
+
controller.send(identity) unless identity == :none
|
87
66
|
end
|
88
67
|
|
89
|
-
def
|
68
|
+
def unauthenticated_visitor_id
|
90
69
|
cookies[visitor_cookie_name] || request_headers[visitor_request_header_name]
|
91
70
|
end
|
92
71
|
|
72
|
+
def visitors
|
73
|
+
@visitors ||= TestTrack::SessionVisitorRepository.new(
|
74
|
+
current_identity: current_identity,
|
75
|
+
unauthenticated_visitor_id: unauthenticated_visitor_id
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def current_visitor
|
80
|
+
visitors.current
|
81
|
+
end
|
82
|
+
|
93
83
|
def set_cookie(name, value)
|
94
84
|
cookies[name] = {
|
95
85
|
value: value,
|
@@ -131,7 +121,7 @@ class TestTrack::Session
|
|
131
121
|
end
|
132
122
|
|
133
123
|
def manage_cookies!
|
134
|
-
set_cookie(visitor_cookie_name,
|
124
|
+
set_cookie(visitor_cookie_name, current_visitor.id)
|
135
125
|
end
|
136
126
|
|
137
127
|
def request
|
@@ -155,26 +145,7 @@ class TestTrack::Session
|
|
155
145
|
end
|
156
146
|
|
157
147
|
def manage_response_headers!
|
158
|
-
response_headers[visitor_response_header_name] =
|
159
|
-
end
|
160
|
-
|
161
|
-
def notify_unsynced_assignments!
|
162
|
-
payload = {
|
163
|
-
visitor_id: visitor.id,
|
164
|
-
assignments: visitor.unsynced_assignments
|
165
|
-
}
|
166
|
-
ActiveSupport::Notifications.instrument('test_track.notify_unsynced_assignments', payload) do
|
167
|
-
##
|
168
|
-
# This block creates an unbounded number of threads up to 1 per request.
|
169
|
-
# This can potentially cause issues under high load, in which case we should move to a thread pool/work queue.
|
170
|
-
new_thread_with_request_store do
|
171
|
-
TestTrack::UnsyncedAssignmentsNotifier.new(payload).notify
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def sync_assignments?
|
177
|
-
visitor.loaded? && visitor.unsynced_assignments.present?
|
148
|
+
response_headers[visitor_response_header_name] = current_visitor.id if current_visitor.id_overridden_by_existing_visitor?
|
178
149
|
end
|
179
150
|
|
180
151
|
def visitor_cookie_name
|
@@ -192,21 +163,4 @@ class TestTrack::Session
|
|
192
163
|
def fully_qualified_cookie_domain_enabled?
|
193
164
|
ENV['TEST_TRACK_FULLY_QUALIFIED_COOKIE_DOMAIN_ENABLED'] == '1'
|
194
165
|
end
|
195
|
-
|
196
|
-
def new_thread_with_request_store
|
197
|
-
Thread.new(RequestStore.store) do |original_store|
|
198
|
-
begin
|
199
|
-
RequestStore.begin!
|
200
|
-
RequestStore.store.merge!(original_store)
|
201
|
-
yield
|
202
|
-
ensure
|
203
|
-
RequestStore.end!
|
204
|
-
RequestStore.clear!
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def identities
|
210
|
-
@identities ||= TestTrack::SessionIdentityCollection.new(controller)
|
211
|
-
end
|
212
166
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class TestTrack::SessionVisitorRepository
|
2
|
+
attr_reader :current_identity, :unauthenticated_visitor_id
|
3
|
+
|
4
|
+
def initialize(current_identity:, unauthenticated_visitor_id:)
|
5
|
+
@current_identity = current_identity
|
6
|
+
@unauthenticated_visitor_id = unauthenticated_visitor_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def current
|
10
|
+
if current_identity
|
11
|
+
for_identity(current_identity)
|
12
|
+
else
|
13
|
+
unauthenticated
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def for_identity(identity)
|
18
|
+
identity_visitor_map[identity] ||= TestTrack::LazyVisitorByIdentity.new(identity)
|
19
|
+
end
|
20
|
+
|
21
|
+
def forget_unauthenticated!
|
22
|
+
@unauthenticated = TestTrack::Visitor.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def authenticate!(identity)
|
26
|
+
@current_identity = identity
|
27
|
+
identity_visitor_map[identity] = unauthenticated
|
28
|
+
unauthenticated.link_identity!(identity)
|
29
|
+
end
|
30
|
+
|
31
|
+
def all
|
32
|
+
identity_visitor_map.values.to_set << current
|
33
|
+
end
|
34
|
+
|
35
|
+
def notify_unsynced_assignments!
|
36
|
+
all.each do |visitor|
|
37
|
+
if visitor.loaded? && visitor.unsynced_assignments.present?
|
38
|
+
TestTrack::ThreadedVisitorNotifier.new(visitor).notify
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def unauthenticated
|
46
|
+
@unauthenticated ||= TestTrack::Visitor.new(id: unauthenticated_visitor_id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def identity_visitor_map
|
50
|
+
@identity_visitor_map ||= {}
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class TestTrack::ThreadedVisitorNotifier
|
2
|
+
attr_reader :visitor
|
3
|
+
|
4
|
+
def initialize(visitor)
|
5
|
+
@visitor = visitor
|
6
|
+
end
|
7
|
+
|
8
|
+
def notify
|
9
|
+
payload = {
|
10
|
+
visitor_id: visitor.id,
|
11
|
+
assignments: visitor.unsynced_assignments
|
12
|
+
}
|
13
|
+
ActiveSupport::Notifications.instrument('test_track.notify_unsynced_assignments', payload) do
|
14
|
+
new_thread_with_request_store do
|
15
|
+
TestTrack::UnsyncedAssignmentsNotifier.new(payload).notify
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def new_thread_with_request_store
|
23
|
+
Thread.new(RequestStore.store) do |original_store|
|
24
|
+
begin
|
25
|
+
RequestStore.begin!
|
26
|
+
RequestStore.store.merge!(original_store)
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
RequestStore.end!
|
30
|
+
RequestStore.clear!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -6,6 +6,7 @@ class TestTrack::Visitor
|
|
6
6
|
def initialize(opts = {})
|
7
7
|
opts = opts.dup
|
8
8
|
@id = opts.delete(:id)
|
9
|
+
@loaded = true if opts[:assignments]
|
9
10
|
@assignments = opts.delete(:assignments)
|
10
11
|
unless id
|
11
12
|
@id = SecureRandom.uuid
|
@@ -65,17 +66,17 @@ class TestTrack::Visitor
|
|
65
66
|
@split_registry ||= TestTrack::Remote::SplitRegistry.to_hash
|
66
67
|
end
|
67
68
|
|
68
|
-
def
|
69
|
-
|
69
|
+
def link_identity!(identity)
|
70
|
+
opts = identifier_opts(identity)
|
70
71
|
begin
|
71
|
-
identifier = TestTrack::Remote::Identifier.create!(
|
72
|
+
identifier = TestTrack::Remote::Identifier.create!(opts)
|
72
73
|
merge!(identifier.visitor)
|
73
74
|
rescue *TestTrack::SERVER_ERRORS => e
|
74
75
|
Rails.logger.error "TestTrack failed to link identifier, retrying. #{e}"
|
75
76
|
|
76
77
|
# If at first you don't succeed, async it - we may not display 100% consistent UX this time,
|
77
78
|
# but subsequent requests will be better off
|
78
|
-
TestTrack::Remote::Identifier.delay.create!(
|
79
|
+
TestTrack::Remote::Identifier.delay.create!(opts)
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
@@ -83,8 +84,12 @@ class TestTrack::Visitor
|
|
83
84
|
@tt_offline
|
84
85
|
end
|
85
86
|
|
87
|
+
def id_loaded?
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
86
91
|
def loaded?
|
87
|
-
!offline? && @
|
92
|
+
!offline? && @loaded
|
88
93
|
end
|
89
94
|
|
90
95
|
def id_overridden_by_existing_visitor?
|
@@ -93,12 +98,28 @@ class TestTrack::Visitor
|
|
93
98
|
|
94
99
|
private
|
95
100
|
|
101
|
+
def identifier_opts(identity)
|
102
|
+
{
|
103
|
+
identifier_type: identity.test_track_identifier_type,
|
104
|
+
visitor_id: id,
|
105
|
+
value: identity.test_track_identifier_value.to_s
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
96
109
|
def assignments
|
97
|
-
@assignments ||=
|
110
|
+
@assignments ||= remote_visitor&.assignments || []
|
98
111
|
end
|
99
112
|
|
100
113
|
def remote_visitor
|
101
|
-
@remote_visitor ||=
|
114
|
+
@remote_visitor ||= _remote_visitor
|
115
|
+
end
|
116
|
+
|
117
|
+
def _remote_visitor
|
118
|
+
unless tt_offline?
|
119
|
+
TestTrack::Remote::Visitor.find(id).tap do |_|
|
120
|
+
@loaded = true
|
121
|
+
end
|
122
|
+
end
|
102
123
|
rescue *TestTrack::SERVER_ERRORS => e
|
103
124
|
Rails.logger.error "TestTrack failed to load remote visitor. #{e}"
|
104
125
|
@tt_offline = true
|
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.0.0.
|
4
|
+
version: 4.0.0.alpha8
|
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-05-
|
16
|
+
date: 2018-05-08 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: airbrake
|
@@ -268,6 +268,7 @@ files:
|
|
268
268
|
- app/models/test_track/fake/visitor_detail.rb
|
269
269
|
- app/models/test_track/fake_server.rb
|
270
270
|
- app/models/test_track/identity_session_locator.rb
|
271
|
+
- app/models/test_track/lazy_visitor_by_identity.rb
|
271
272
|
- app/models/test_track/misconfiguration_notifier.rb
|
272
273
|
- app/models/test_track/notify_assignment_job.rb
|
273
274
|
- app/models/test_track/offline_session.rb
|
@@ -283,7 +284,8 @@ files:
|
|
283
284
|
- app/models/test_track/remote/visitor.rb
|
284
285
|
- app/models/test_track/remote/visitor_detail.rb
|
285
286
|
- app/models/test_track/session.rb
|
286
|
-
- app/models/test_track/
|
287
|
+
- app/models/test_track/session_visitor_repository.rb
|
288
|
+
- app/models/test_track/threaded_visitor_notifier.rb
|
287
289
|
- app/models/test_track/unsynced_assignments_notifier.rb
|
288
290
|
- app/models/test_track/variant_calculator.rb
|
289
291
|
- app/models/test_track/vary_dsl.rb
|
@@ -1,29 +0,0 @@
|
|
1
|
-
class TestTrack::SessionIdentityCollection
|
2
|
-
def initialize(controller)
|
3
|
-
@controller = controller
|
4
|
-
end
|
5
|
-
|
6
|
-
def include?(identity)
|
7
|
-
found_identity = identities[identity.test_track_identifier_type] || authenticated_resource_for_identity(identity)
|
8
|
-
found_identity.present? && found_identity == identity
|
9
|
-
end
|
10
|
-
|
11
|
-
def <<(identity)
|
12
|
-
identities[identity.test_track_identifier_type] = identity
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
attr_reader :controller
|
18
|
-
|
19
|
-
def identities
|
20
|
-
@identities ||= {}
|
21
|
-
end
|
22
|
-
|
23
|
-
def authenticated_resource_for_identity(identity)
|
24
|
-
authenticated_resource_method_name = "current_#{identity.class.model_name.element}"
|
25
|
-
|
26
|
-
# pass true to `respond_to?` to include private methods
|
27
|
-
controller.respond_to?(authenticated_resource_method_name, true) && controller.send(authenticated_resource_method_name)
|
28
|
-
end
|
29
|
-
end
|