test_track_rails_client 4.0.0.alpha7 → 4.0.0.alpha8
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/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
|