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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 783a76b4acdaebee2ff453ba5513d49e804c985c
4
- data.tar.gz: 4f36e75ebc4a144a02daf0fcd3d53d48deb8bcc2
3
+ metadata.gz: 3c421a570a50e10dbfdff7420aafa19964ee4c01
4
+ data.tar.gz: 901a202cbb0db2717ba240b6466f6408ddb1b3c4
5
5
  SHA512:
6
- metadata.gz: 7725742abb3fa4099ef8e2c4571b820a8b28bb987f0d6e7b111d92c1e7f5de5049311a69217ca576ba56c947cacbeb97966d077c930a4b8a1d17576f7e4bcaf1
7
- data.tar.gz: 3155aa14210aca6d48bd44c02a7e71be22d0dac81bc6472c9299459e8969527346ae9f98dd0fe7d3e909ba6acb4aec2b7345868b4d318cea1f3947f05c0a4ab2
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`. Behind the scenes, these methods check to see if they are being used within a web context of a controller that includes `TestTrack::Controller` or not. If called in a web context they will use the `test_track_visitor` that the controller has and participate in the existing session, if not, they will standup an `OfflineSession`.
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 splits
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.
@@ -2,6 +2,8 @@ module TestTrack::Controller
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do
5
+ class_attribute :test_track_identity
6
+
5
7
  helper_method :test_track_session, :test_track_visitor
6
8
  helper TestTrack::ApplicationHelper
7
9
  around_action :manage_test_track_session
@@ -19,7 +19,9 @@ class TestTrack::ABConfiguration
19
19
  private
20
20
 
21
21
  def build_variant_hash
22
- notify_because_ab("configures split with more than 2 variants") if split_variants && split_variants.size > 2
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
- manage_cookies!
15
- manage_response_headers!
16
- notify_unsynced_assignments! if sync_assignments?
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 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
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
- @visitor_dsl ||= TestTrack::VisitorDSL.new(visitor)
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: visitor.split_registry,
50
- assignments: visitor.assignment_json
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
- identifier_type = identity.test_track_identifier_type
56
- identifier_value = identity.test_track_identifier_value
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
- identifier_type = identity.test_track_identifier_type
67
- identifier_value = identity.test_track_identifier_value
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 visitor
86
- @visitor ||= TestTrack::Visitor.new(id: visitor_id)
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 visitor_id
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, visitor.id)
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] = visitor.id if visitor.id_overridden_by_existing_visitor?
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 link_identifier!(identifier_type, identifier_value)
69
- identifier_opts = { identifier_type: identifier_type, visitor_id: id, value: identifier_value.to_s }
69
+ def link_identity!(identity)
70
+ opts = identifier_opts(identity)
70
71
  begin
71
- identifier = TestTrack::Remote::Identifier.create!(identifier_opts)
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!(identifier_opts)
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? && @remote_visitor.present?
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 ||= (remote_visitor && remote_visitor.assignments) || []
110
+ @assignments ||= remote_visitor&.assignments || []
98
111
  end
99
112
 
100
113
  def remote_visitor
101
- @remote_visitor ||= TestTrack::Remote::Visitor.find(id) unless tt_offline?
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
@@ -1,3 +1,3 @@
1
1
  module TestTrackRailsClient
2
- VERSION = "4.0.0.alpha7" # rubocop:disable Style/MutableConstant
2
+ VERSION = "4.0.0.alpha8" # rubocop:disable Style/MutableConstant
3
3
  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.0.0.alpha7
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-01 00:00:00.000000000 Z
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/session_identity_collection.rb
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