test_track_rails_client 4.0.0.alpha32 → 4.0.0.rc3

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.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +14 -13
  3. data/Rakefile +6 -4
  4. data/app/assets/javascripts/testTrack.bundle.min.js +1 -1
  5. data/app/controllers/concerns/test_track/controller.rb +1 -3
  6. data/app/models/concerns/test_track/required_options.rb +1 -0
  7. data/app/models/test_track/ab_configuration.rb +3 -5
  8. data/app/models/test_track/analytics/mixpanel_client.rb +1 -0
  9. data/app/models/test_track/analytics/safe_wrapper.rb +1 -0
  10. data/app/models/test_track/application_identity.rb +1 -0
  11. data/app/models/test_track/assignment.rb +2 -1
  12. data/app/models/test_track/config_updater.rb +3 -3
  13. data/app/models/test_track/fake/split_registry.rb +4 -6
  14. data/app/models/test_track/job_session.rb +1 -3
  15. data/app/models/test_track/lazy_visitor_by_identity.rb +8 -4
  16. data/app/models/test_track/misconfiguration_notifier.rb +1 -0
  17. data/app/models/test_track/notify_assignment_job.rb +1 -0
  18. data/app/models/test_track/remote/assignment_event.rb +1 -1
  19. data/app/models/test_track/remote/fake_server.rb +1 -0
  20. data/app/models/test_track/remote/identifier.rb +2 -1
  21. data/app/models/test_track/remote/identifier_type.rb +1 -1
  22. data/app/models/test_track/remote/split_config.rb +1 -1
  23. data/app/models/test_track/remote/split_detail.rb +2 -2
  24. data/app/models/test_track/remote/split_registry.rb +2 -2
  25. data/app/models/test_track/remote/visitor.rb +2 -2
  26. data/app/models/test_track/remote/visitor_detail.rb +1 -1
  27. data/app/models/test_track/split_registry.rb +3 -3
  28. data/app/models/test_track/threaded_visitor_notifier.rb +6 -8
  29. data/app/models/test_track/unsynced_assignments_notifier.rb +4 -6
  30. data/app/models/test_track/variant_calculator.rb +1 -0
  31. data/app/models/test_track/vary_dsl.rb +4 -0
  32. data/app/models/test_track/visitor.rb +5 -4
  33. data/app/models/test_track/web_session.rb +5 -3
  34. data/app/models/test_track/web_session_visitor_repository.rb +1 -3
  35. data/config/initializers/set_build_timestamp.rb +1 -0
  36. data/lib/tasks/test_track_rails_client_tasks.rake +21 -0
  37. data/lib/test_track.rb +27 -0
  38. data/lib/test_track_rails_client/engine.rb +4 -2
  39. data/lib/test_track_rails_client/version.rb +1 -1
  40. data/vendor/bin/testtrack-cli/testtrack.darwin +0 -0
  41. data/vendor/bin/testtrack-cli/testtrack.linux +0 -0
  42. metadata +129 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1567c509b4e0cc8688ae66055daf5283fdde67f8
4
- data.tar.gz: 6a00577bafae4d3c1c2587b0bcae5f7154f62300
2
+ SHA256:
3
+ metadata.gz: 5c66d9d6a03b2089f12dc29601ef72d3b24a38c2772f719c84a33561d87c38d9
4
+ data.tar.gz: 6a884499fd36e03da076595c460fd32e5177a1fc8961957d8014a42b3faef8a7
5
5
  SHA512:
6
- metadata.gz: 0c7aa0eec02f9d1396cf7a7616091f5382a2b6c427ea5213b8fb9e9dc82c9752244a185e02c79747cd9faca46f472d72d00c758f543853e34da735fc451c792b
7
- data.tar.gz: 02aa432c35ccb4c9f8edcac7d05d861e13d30256d0e388c7d940107eed3f57527924f713ff3f5687babda5131d5d4573844778eadc677128b76da9ef046fdcd9
6
+ metadata.gz: 13493acef4dfb8e0a74318fa32b8e23636710b07cc9d90544a0d4bea406a41ae69810e78a73323d538b41ba5ee208713dc07c15e387a69fc5c0cb900dc3cf051
7
+ data.tar.gz: 5175ed66bec77901a9206155da9ff7d6a7e9c0ec10d4d98406a8038e8ad1a14834a226321cda18b5ba5a3ce1355afae4950507fbc8cbd5a68e763ca04dc56799
data/README.md CHANGED
@@ -50,7 +50,7 @@ Set up ENV vars in every environment:
50
50
  * `TEST_TRACK_API_URL` - Set this to the URL of your TestTrack instance with your app credentials, e.g. `http://[myapp]:[your new app password]@[your-app-domain]/`
51
51
 
52
52
  [your-app-domain] can be
53
- * `testtrack.dev` ([Pow](pow.cx))
53
+ * `testtrack.test`
54
54
  * `localhost:PORT`
55
55
  * `example.org`
56
56
  * etc
@@ -72,8 +72,8 @@ If your app doesn't support authentication, set
72
72
 
73
73
  ### Prepare your identity models (optional)
74
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)
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
77
 
78
78
  ### Set up the Chrome extension (optional)
79
79
 
@@ -288,7 +288,7 @@ class BackgroundWorkJob
288
288
  end
289
289
  end
290
290
  end
291
- ```
291
+ ```
292
292
 
293
293
  ## Tracking visitor logins
294
294
 
@@ -391,28 +391,29 @@ def notify(message)
391
391
 
392
392
  ### From 3.0 to 4.0
393
393
 
394
- The contract of custom analytics plugins has changed. Instead of
394
+ * The contract of custom analytics plugins has changed. Instead of
395
395
  implementing `track_assignment` you now must implement `track`. It's
396
396
  easier and more conventional, though, and takes care of differentiating
397
- between expiriment assignments and feature gate experiences, which are
397
+ between experiment assignments and feature gate experiences, which are
398
398
  no longer recorded server-side.
399
399
 
400
- You also must add `self.test_track_identity = :current_user` (or
400
+ * You must add `self.test_track_identity = :current_user` (or
401
401
  whatever your controller uses as a sign-in identity) to your
402
402
  TestTrack-enabled controllers, or set it to `:none` if your app doesn't
403
- support authentication.
403
+ support authentication. If your app supports authentication, you'll need to configure your
404
+ user model as a [TestTrack Identity](#varying-app-behavior-from-within-a-model).
404
405
 
405
- If your app supports authentication, You'll need to configure your
406
- user model as a [TestTrack Identity](#varying-app-behavior-from-within-a-model)
406
+ * TestTrack server [introduced a new endpoint](https://github.com/Betterment/test_track/pull/133) for fetching the split registry that requires a timestamp of when the application was built. This prevents dropped splits from breaking a running application. If you use `rake assets:precompile` in your build pipeline, you're all set. If you don't, you'll need to call `rake test_track:generate_build_timestamp` and ensure that the file `testtrack/build_timestamp` is deployed along with your app.
407
407
 
408
408
  ### From 2.0 to 3.0
409
409
 
410
- 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.
411
- The TestTrack.analytics client `#track_assignment` method no longer accepts a properties hash as an argument as `mixpanel_distinct_id` is no longer relevant.
410
+ * 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.
411
+
412
+ * `TestTrack.analytics#track_assignment` no longer accepts a properties hash as an argument as `mixpanel_distinct_id` is no longer relevant.
412
413
 
413
414
  ### From 1.x to 1.3
414
415
 
415
- `TestTrack::Session#log_in!` and `TestTrack:Session#sign_up!` now take a `TestTrack::Identity` instance argument instead of an identity type and identity value.
416
+ * `TestTrack::Session#log_in!` and `TestTrack:Session#sign_up!` now take a `TestTrack::Identity` instance argument instead of an identity type and identity value.
416
417
 
417
418
  ## How to Contribute
418
419
 
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
14
  rdoc.rdoc_files.include('lib/**/*.rb')
15
15
  end
16
16
 
17
- APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
17
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
18
18
  load 'rails/tasks/engine.rake'
19
19
 
20
20
  Bundler::GemHelper.install_tasks
@@ -28,12 +28,14 @@ RuboCop::RakeTask.new
28
28
 
29
29
  desc "Pull the latest versions of all dependencies into the gem for distribution"
30
30
  task :vendor_deps do
31
- TEST_TRACK_JS_CLIENT_VERSION = '1.3.8'.freeze
32
- TEST_TRACK_CLI_VERSION = 'v1.0.2'.freeze
31
+ TEST_TRACK_JS_CLIENT_VERSION = '2.0.0-alpha.6'.freeze
32
+ TEST_TRACK_CLI_VERSION = 'v1.1.3'.freeze
33
33
 
34
34
  # Bundle JS client
35
+ sh 'npm init -y'
35
36
  sh "npm install --no-save test_track_js_client@#{TEST_TRACK_JS_CLIENT_VERSION}"
36
- sh 'cp', 'node_modules/test_track_js_client/dist/testTrack.bundle.min.js', 'app/assets/javascripts/testTrack.bundle.min.js'
37
+ sh 'cp', 'node_modules/test_track_js_client/dist/testTrack.bundle.js', 'app/assets/javascripts/testTrack.bundle.min.js'
38
+ sh 'rm package.json'
37
39
 
38
40
  # Download testtrack-cli
39
41
  FileUtils.module_eval do
@@ -1 +1 @@
1
- !function(t){var e="object"==typeof exports&&exports,i="object"==typeof module&&module&&module.exports==e&&module,n="object"==typeof global&&global;n.global!==n&&n.window!==n||(t=n);var r=function(t){this.message=t};(r.prototype=new Error).name="InvalidCharacterError";var o=function(t){throw new r(t)},s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=/[\t\n\f\r ]/g,u={encode:function(t){t=String(t),/[^\0-\xFF]/.test(t)&&o("The string to be encoded contains characters outside of the Latin1 range.");for(var e,i=t.length%3,n="",r=-1,a=t.length-i;++r<a;)e=(t.charCodeAt(r)<<16)+(t.charCodeAt(++r)<<8)+t.charCodeAt(++r),n+=s.charAt(e>>18&63)+s.charAt(e>>12&63)+s.charAt(e>>6&63)+s.charAt(63&e);return 2==i?(e=(t.charCodeAt(r)<<8)+t.charCodeAt(++r),n+=s.charAt(e>>10)+s.charAt(e>>4&63)+s.charAt(e<<2&63)+"="):1==i&&(e=t.charCodeAt(r),n+=s.charAt(e>>2)+s.charAt(e<<4&63)+"=="),n},decode:function(t){var e=(t=String(t).replace(a,"")).length;e%4==0&&(e=(t=t.replace(/==?$/,"")).length),(e%4==1||/[^+a-zA-Z0-9/]/.test(t))&&o("Invalid character: the string to be decoded is not correctly encoded.");for(var i,n,r=0,u="",f=-1;++f<e;)n=s.indexOf(t.charAt(f)),i=r%4?64*i+n:n,r++%4&&(u+=String.fromCharCode(255&i>>(-2*r&6)));return u},version:"0.1.0"};if("function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return u});else if(e&&!e.nodeType)if(i)i.exports=u;else for(var f in u)u.hasOwnProperty(f)&&(e[f]=u[f]);else t.base64=u}(this),function(t){"use strict";function e(t,e){var i=(65535&t)+(65535&e);return(t>>16)+(e>>16)+(i>>16)<<16|65535&i}function i(t,e){return t<<e|t>>>32-e}function n(t,n,r,o,s,a){return e(i(e(e(n,t),e(o,a)),s),r)}function r(t,e,i,r,o,s,a){return n(e&i|~e&r,t,e,o,s,a)}function o(t,e,i,r,o,s,a){return n(e&r|i&~r,t,e,o,s,a)}function s(t,e,i,r,o,s,a){return n(e^i^r,t,e,o,s,a)}function a(t,e,i,r,o,s,a){return n(i^(e|~r),t,e,o,s,a)}function u(t,i){t[i>>5]|=128<<i%32,t[14+(i+64>>>9<<4)]=i;var n,u,f,c,p,d=1732584193,h=-271733879,l=-1732584194,g=271733878;for(n=0;n<t.length;n+=16)u=d,f=h,c=l,p=g,h=a(h=a(h=a(h=a(h=s(h=s(h=s(h=s(h=o(h=o(h=o(h=o(h=r(h=r(h=r(h=r(h,l=r(l,g=r(g,d=r(d,h,l,g,t[n],7,-680876936),h,l,t[n+1],12,-389564586),d,h,t[n+2],17,606105819),g,d,t[n+3],22,-1044525330),l=r(l,g=r(g,d=r(d,h,l,g,t[n+4],7,-176418897),h,l,t[n+5],12,1200080426),d,h,t[n+6],17,-1473231341),g,d,t[n+7],22,-45705983),l=r(l,g=r(g,d=r(d,h,l,g,t[n+8],7,1770035416),h,l,t[n+9],12,-1958414417),d,h,t[n+10],17,-42063),g,d,t[n+11],22,-1990404162),l=r(l,g=r(g,d=r(d,h,l,g,t[n+12],7,1804603682),h,l,t[n+13],12,-40341101),d,h,t[n+14],17,-1502002290),g,d,t[n+15],22,1236535329),l=o(l,g=o(g,d=o(d,h,l,g,t[n+1],5,-165796510),h,l,t[n+6],9,-1069501632),d,h,t[n+11],14,643717713),g,d,t[n],20,-373897302),l=o(l,g=o(g,d=o(d,h,l,g,t[n+5],5,-701558691),h,l,t[n+10],9,38016083),d,h,t[n+15],14,-660478335),g,d,t[n+4],20,-405537848),l=o(l,g=o(g,d=o(d,h,l,g,t[n+9],5,568446438),h,l,t[n+14],9,-1019803690),d,h,t[n+3],14,-187363961),g,d,t[n+8],20,1163531501),l=o(l,g=o(g,d=o(d,h,l,g,t[n+13],5,-1444681467),h,l,t[n+2],9,-51403784),d,h,t[n+7],14,1735328473),g,d,t[n+12],20,-1926607734),l=s(l,g=s(g,d=s(d,h,l,g,t[n+5],4,-378558),h,l,t[n+8],11,-2022574463),d,h,t[n+11],16,1839030562),g,d,t[n+14],23,-35309556),l=s(l,g=s(g,d=s(d,h,l,g,t[n+1],4,-1530992060),h,l,t[n+4],11,1272893353),d,h,t[n+7],16,-155497632),g,d,t[n+10],23,-1094730640),l=s(l,g=s(g,d=s(d,h,l,g,t[n+13],4,681279174),h,l,t[n],11,-358537222),d,h,t[n+3],16,-722521979),g,d,t[n+6],23,76029189),l=s(l,g=s(g,d=s(d,h,l,g,t[n+9],4,-640364487),h,l,t[n+12],11,-421815835),d,h,t[n+15],16,530742520),g,d,t[n+2],23,-995338651),l=a(l,g=a(g,d=a(d,h,l,g,t[n],6,-198630844),h,l,t[n+7],10,1126891415),d,h,t[n+14],15,-1416354905),g,d,t[n+5],21,-57434055),l=a(l,g=a(g,d=a(d,h,l,g,t[n+12],6,1700485571),h,l,t[n+3],10,-1894986606),d,h,t[n+10],15,-1051523),g,d,t[n+1],21,-2054922799),l=a(l,g=a(g,d=a(d,h,l,g,t[n+8],6,1873313359),h,l,t[n+15],10,-30611744),d,h,t[n+6],15,-1560198380),g,d,t[n+13],21,1309151649),l=a(l,g=a(g,d=a(d,h,l,g,t[n+4],6,-145523070),h,l,t[n+11],10,-1120210379),d,h,t[n+2],15,718787259),g,d,t[n+9],21,-343485551),d=e(d,u),h=e(h,f),l=e(l,c),g=e(g,p);return[d,h,l,g]}function f(t){var e,i="";for(e=0;e<32*t.length;e+=8)i+=String.fromCharCode(t[e>>5]>>>e%32&255);return i}function c(t){var e,i=[];for(i[(t.length>>2)-1]=void 0,e=0;e<i.length;e+=1)i[e]=0;for(e=0;e<8*t.length;e+=8)i[e>>5]|=(255&t.charCodeAt(e/8))<<e%32;return i}function p(t){return f(u(c(t),8*t.length))}function d(t,e){var i,n,r=c(t),o=[],s=[];for(o[15]=s[15]=void 0,r.length>16&&(r=u(r,8*t.length)),i=0;i<16;i+=1)o[i]=909522486^r[i],s[i]=1549556828^r[i];return n=u(o.concat(c(e)),512+8*e.length),f(u(s.concat(n),640))}function h(t){var e,i,n="";for(i=0;i<t.length;i+=1)e=t.charCodeAt(i),n+="0123456789abcdef".charAt(e>>>4&15)+"0123456789abcdef".charAt(15&e);return n}function l(t){return unescape(encodeURIComponent(t))}function g(t){return p(l(t))}function v(t){return h(g(t))}function m(t,e){return d(l(t),l(e))}function y(t,e){return h(m(t,e))}function w(t,e,i){return e?i?m(e,t):y(e,t):i?g(t):v(t)}"function"==typeof define&&define.amd?define(function(){return w}):t.md5=w}(this),function(){function t(t,e){var i=e||0,n=u;return n[t[i++]]+n[t[i++]]+n[t[i++]]+n[t[i++]]+"-"+n[t[i++]]+n[t[i++]]+"-"+n[t[i++]]+n[t[i++]]+"-"+n[t[i++]]+n[t[i++]]+"-"+n[t[i++]]+n[t[i++]]+n[t[i++]]+n[t[i++]]+n[t[i++]]+n[t[i++]]}function e(e,n,r){var o=n&&r||0;"string"==typeof e&&(n="binary"==e?new a(16):null,e=null);var s=(e=e||{}).random||(e.rng||i)();if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,n)for(var u=0;u<16;u++)n[o+u]=s[u];return n||t(s)}var i,n=this;if("function"==typeof n.require)try{var r=n.require("crypto").randomBytes;i=r&&function(){return r(16)}}catch(t){}if(!i&&n.crypto&&crypto.getRandomValues){var o=new Uint8Array(16);i=function(){return crypto.getRandomValues(o),o}}if(!i){var s=new Array(16);i=function(){for(var t,e=0;e<16;e++)0==(3&e)&&(t=4294967296*Math.random()),s[e]=t>>>((3&e)<<3)&255;return s}}for(var a="function"==typeof n.Buffer?n.Buffer:Array,u=[],f={},c=0;c<256;c++)u[c]=(c+256).toString(16).substr(1),f[u[c]]=c;var p=i(),d=[1|p[0],p[1],p[2],p[3],p[4],p[5]],h=16383&(p[6]<<8|p[7]),l=0,g=0,v=e;if(v.v1=function(e,i,n){var r=i&&n||0,o=i||[],s=null!=(e=e||{}).clockseq?e.clockseq:h,a=null!=e.msecs?e.msecs:(new Date).getTime(),u=null!=e.nsecs?e.nsecs:g+1,f=a-l+(u-g)/1e4;if(f<0&&null==e.clockseq&&(s=s+1&16383),(f<0||a>l)&&null==e.nsecs&&(u=0),u>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");l=a,g=u,h=s;var c=(1e4*(268435455&(a+=122192928e5))+u)%4294967296;o[r++]=c>>>24&255,o[r++]=c>>>16&255,o[r++]=c>>>8&255,o[r++]=255&c;var p=a/4294967296*1e4&268435455;o[r++]=p>>>8&255,o[r++]=255&p,o[r++]=p>>>24&15|16,o[r++]=p>>>16&255,o[r++]=s>>>8|128,o[r++]=255&s;for(var v=e.node||d,m=0;m<6;m++)o[r+m]=v[m];return i||t(o)},v.v4=e,v.parse=function(t,e,i){var n=e&&i||0,r=0;for(e=e||[],t.toLowerCase().replace(/[0-9a-f]{2}/g,function(t){r<16&&(e[n+r++]=f[t])});r<16;)e[n+r++]=0;return e},v.unparse=t,v.BufferClass=a,"function"==typeof define&&define.amd)define(function(){return v});else if("undefined"!=typeof module&&module.exports)module.exports=v;else{var m=n.uuid;v.noConflict=function(){return n.uuid=m,v},n.uuid=v}}.call(this),function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t("object"==typeof exports?require("jquery"):jQuery)}(function(t){function e(t){return a.raw?t:encodeURIComponent(t)}function i(t){return a.raw?t:decodeURIComponent(t)}function n(t){return e(a.json?JSON.stringify(t):String(t))}function r(t){0===t.indexOf('"')&&(t=t.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return t=decodeURIComponent(t.replace(s," ")),a.json?JSON.parse(t):t}catch(t){}}function o(e,i){var n=a.raw?e:r(e);return t.isFunction(i)?i(n):n}var s=/\+/g,a=t.cookie=function(r,s,u){if(void 0!==s&&!t.isFunction(s)){if("number"==typeof(u=t.extend({},a.defaults,u)).expires){var f=u.expires,c=u.expires=new Date;c.setTime(+c+864e5*f)}return document.cookie=[e(r),"=",n(s),u.expires?"; expires="+u.expires.toUTCString():"",u.path?"; path="+u.path:"",u.domain?"; domain="+u.domain:"",u.secure?"; secure":""].join("")}for(var p=r?void 0:{},d=document.cookie?document.cookie.split("; "):[],h=0,l=d.length;h<l;h++){var g=d[h].split("="),v=i(g.shift()),m=g.join("=");if(r&&r===v){p=o(m,s);break}r||void 0===(m=o(m))||(p[v]=m)}return p};a.defaults={},t.removeCookie=function(e,i){return void 0!==t.cookie(e)&&(t.cookie(e,"",t.extend({},i,{expires:-1})),!t.cookie(e))}}),function(t,e){if("function"==typeof define&&define.amd)define(["node-uuid","blueimp-md5","jquery","base-64","jquery.cookie"],e);else if("undefined"!=typeof exports){var i=require("node-uuid"),n=require("blueimp-md5"),r=require("jquery"),o=require("base-64");require("jquery.cookie");module.exports=e(i,n,r,o)}else t.TestTrack=e(t.uuid,t.md5,t.jQuery,t.base64)}(this,function(t,e,i,n){"use strict";if(void 0===t)throw new Error('TestTrack depends on node-uuid. Make sure you are including "node_modules/node-uuid/uuid.js"');if(void 0===e)throw new Error('TestTrack depends on blueimp-md5. Make sure you are including "node_modules/blueimp-md5/js/md5.js"');if(void 0===i)throw new Error('TestTrack depends on jquery. You can use your own copy of jquery or the one in "node_modules/jquery/dist/jquery.js"');if("function"!=typeof i.cookie)throw new Error("TestTrack depends on jquery.cookie. You can user your own copy of jquery.cooke or the one in node_modules/jquery.cookie/jquery.cookie.js");if(void 0===n)throw new Error('TestTrack depends on base-64. Make sure you are including "node_modules/base-64/base64.js"');Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},r=function(){return i.apply(this instanceof n?this:t,e.concat(Array.prototype.slice.call(arguments)))};return this.prototype&&(n.prototype=this.prototype),r.prototype=new n,r});var r=function(){var t=function(){};return t.prototype.trackAssignment=function(t,e,i){var n={TTVisitorID:t,SplitName:e.getSplitName(),SplitVariant:e.getVariant(),SplitContext:e.getContext()};window.mixpanel&&window.mixpanel.track("SplitAssigned",n,i)},t.prototype.identify=function(t){window.mixpanel&&window.mixpanel.identify(t)},t.prototype.alias=function(t){window.mixpanel&&window.mixpanel.alias(t)},t}(),o=function(){var t=function(t){if(!t.splitName)throw new Error("must provide splitName");if(!t.hasOwnProperty("variant"))throw new Error("must provide variant");if(!t.hasOwnProperty("isUnsynced"))throw new Error("must provide isUnsynced");this._splitName=t.splitName,this._variant=t.variant,this._context=t.context,this._isUnsynced=t.isUnsynced};return t.fromJsonArray=function(t){for(var e=[],i=0;i<t.length;i++)e.push(new o({splitName:t[i].split_name,variant:t[i].variant,context:t[i].context,isUnsynced:t[i].unsynced}));return e},t.prototype.getSplitName=function(){return this._splitName},t.prototype.getVariant=function(){return this._variant},t.prototype.setVariant=function(t){this._variant=t},t.prototype.getContext=function(){return this._context},t.prototype.setContext=function(t){this._context=t},t.prototype.isUnsynced=function(){return this._isUnsynced},t.prototype.setUnsynced=function(t){this._isUnsynced=t},t}(),s=function(){var t=function(){};return t.prototype.getConfig=function(){return"function"==typeof window.atob?JSON.parse(window.atob(window.TT)):JSON.parse(n.decode(window.TT))},t}(),a=function(){var t,e,i=function(){if(!t){var e=new s;t=e.getConfig()}return t};return{_clear:function(){t=null},getUrl:function(){return i().url},getCookieDomain:function(){return i().cookieDomain},getCookieName:function(){return i().cookieName||"tt_visitor_id"},getSplitRegistry:function(){return i().registry},getAssignments:function(){var t=i().assignments;if(!t)return null;if(!e){e=[];for(var n in t)e.push(new o({splitName:n,variant:t[n],isUnsynced:!1}))}return e}}}(),u=function(){var t=function(t){if(this.visitor=t.visitor,this.splitName=t.splitName,!this.visitor)throw new Error("must provide visitor");if(!this.splitName)throw new Error("must provide splitName")};return t.prototype.getVariant=function(){if(!a.getSplitRegistry())return null;for(var t=0,e=this.getAssignmentBucket(),i=this.getWeighting(),n=this.getSortedVariants(),r=0;r<n.length;r++){var o=n[r];if((t+=i[o])>e)return o}throw new Error("Assignment bucket out of range. "+e+" unmatched in "+this.splitName+": "+JSON.stringify(i))},t.prototype.getSplitVisitorHash=function(){return e(this.splitName+this.visitor.getId())},t.prototype.getHashFixnum=function(){return parseInt(this.getSplitVisitorHash().substr(0,8),16)},t.prototype.getAssignmentBucket=function(){return this.getHashFixnum()%100},t.prototype.getSortedVariants=function(){return this.getVariants().sort()},t.prototype.getVariants=function(){return Object.getOwnPropertyNames(this.getWeighting())},t.prototype.getWeighting=function(){var t=a.getSplitRegistry()[this.splitName];if(!t){var e='Unknown split: "'+this.splitName+'"';throw this.visitor.logError(e),new Error(e)}return t},t}(),f=function(){var t=function(t){if(t=t||{},this._visitor=t.visitor,this._assignment=t.assignment,!this._visitor)throw new Error("must provide visitor");if(!this._assignment)throw new Error("must provide assignment")};return t.prototype.send=function(){this.persistAssignment(),this._visitor.analytics.trackAssignment(this._visitor.getId(),this._assignment,function(t){this.persistAssignment(t?"success":"failure")}.bind(this))},t.prototype.persistAssignment=function(t){return i.ajax(a.getUrl()+"/api/v1/assignment_event",{method:"POST",dataType:"json",crossDomain:!0,data:{visitor_id:this._visitor.getId(),split_name:this._assignment.getSplitName(),context:this._assignment.getContext(),mixpanel_result:t}}).fail(function(t,e,i){var n=t&&t.status,r=t&&t.responseText;this._visitor.logError("test_track persistAssignment error: "+[t,n,r,e,i].join(", "))}.bind(this))},t}(),c=function(){var t=function(t){if(t=t||{},this._visitor=t.visitor,this._assignment=t.assignment,this._username=t.username,this._password=t.password,!this._visitor)throw new Error("must provide visitor");if(!this._assignment)throw new Error("must provide assignment");if(!this._username)throw new Error("must provide username");if(!this._password)throw new Error("must provide password")};return t.prototype.send=function(){this.persistAssignment(),this._visitor.analytics.trackAssignment(this._visitor.getId(),this._assignment,function(t){this.persistAssignment(t?"success":"failure")}.bind(this))},t.prototype.persistAssignment=function(t){return i.ajax(a.getUrl()+"/api/v1/assignment_override",{method:"POST",dataType:"json",crossDomain:!0,headers:{Authorization:"Basic "+btoa(this._username+":"+this._password)},data:{visitor_id:this._visitor.getId(),split_name:this._assignment.getSplitName(),variant:this._assignment.getVariant(),context:this._assignment.getContext(),mixpanel_result:t}}).fail(function(t,e,i){var n=t&&t.status,r=t&&t.responseText;this._visitor.logError("test_track persistAssignment error: "+[t,n,r,e,i].join(", "))}.bind(this))},t}(),p=function(){var e=function(t){if(t=t||{},this._id=t.id,this._assignments=t.assignments,this._ttOffline=t.ttOffline,!this._id)throw new Error("must provide id");if(!this._assignments)throw new Error("must provide assignments");this._errorLogger=function(t){window.console.error(t)},this.analytics=new r};return e.loadVisitor=function(e){var n=i.Deferred(),r=function(t){n.resolve(new p(t))};return e?a.getAssignments()?r({id:e,assignments:a.getAssignments(),ttOffline:!1}):i.ajax(a.getUrl()+"/api/v1/visitors/"+e,{method:"GET",timeout:5e3}).done(function(t){r({id:t.id,assignments:o.fromJsonArray(t.assignments),ttOffline:!1})}).fail(function(){r({id:e,assignments:[],ttOffline:!0})}):r({id:t.v4(),assignments:[],ttOffline:!1}),n.promise()},e.prototype.getId=function(){return this._id},e.prototype.getAssignmentRegistry=function(){if(!this._assignmentRegistry){for(var t={},e=0;e<this._assignments.length;e++){var i=this._assignments[e];t[i.getSplitName()]=i}this._assignmentRegistry=t}return this._assignmentRegistry},e.prototype.vary=function(t,e){if("object"!=typeof e.variants)throw new Error("must provide variants object to `vary` for "+t);if(!e.context)throw new Error("must provide context to `vary` for "+t);if(!e.defaultVariant&&!1!==e.defaultVariant)throw new Error("must provide defaultVariant to `vary` for "+t);var i=e.defaultVariant.toString(),n=e.variants,r=e.context;if(!n.hasOwnProperty(i))throw new Error("defaultVariant: "+i+" must be represented in variants object");var o=this._getAssignmentFor(t,r),s=new l({assignment:o,visitor:this});for(var a in n)n.hasOwnProperty(a)&&(a===i?s.default(a,n[a]):s.when(a,n[a]));s.run(),s.isDefaulted()&&(o.setVariant(s.getDefaultVariant()),o.setUnsynced(!0),o.setContext(r)),this.notifyUnsyncedAssignments()},e.prototype.ab=function(t,e){var i=new g({splitName:t,trueVariant:e.trueVariant,visitor:this}).getVariants(),n={};n[i.true]=function(){e.callback(!0)},n[i.false]=function(){e.callback(!1)},this.vary(t,{context:e.context,variants:n,defaultVariant:i.false})},e.prototype.setErrorLogger=function(t){if("function"!=typeof t)throw new Error("must provide function for errorLogger");this._errorLogger=t},e.prototype.logError=function(t){this._errorLogger.call(null,t)},e.prototype.linkIdentifier=function(t,e){var n=i.Deferred();return new h({visitorId:this.getId(),identifierType:t,value:e}).save().then(function(t){this._merge(t),this.notifyUnsyncedAssignments(),n.resolve()}.bind(this)),n.promise()},e.prototype.setAnalytics=function(t){if("object"!=typeof t)throw new Error("must provide object for setAnalytics");this.analytics=t},e.prototype.notifyUnsyncedAssignments=function(){for(var t=this._getUnsyncedAssignments(),e=0;e<t.length;e++)this._notify(t[e])},e.prototype._getUnsyncedAssignments=function(){var t=[],e=this.getAssignmentRegistry();return Object.keys(e).forEach(function(i){var n=e[i];n.isUnsynced()&&t.push(n)}),t},e.prototype._merge=function(t){var e=this.getAssignmentRegistry(),i=t.getAssignmentRegistry();this._id=t.getId();for(var n in i)i.hasOwnProperty(n)&&(e[n]=i[n])},e.prototype._getAssignmentFor=function(t,e){return this.getAssignmentRegistry()[t]||this._generateAssignmentFor(t,e)},e.prototype._generateAssignmentFor=function(t,e){var i=new u({visitor:this,splitName:t}).getVariant();i||(this._ttOffline=!0);var n=new o({splitName:t,variant:i,context:e,isUnsynced:!0});return this._assignments.push(n),this._assignmentRegistry=null,n},e.prototype._notify=function(t){try{if(this._ttOffline)return;new f({visitor:this,assignment:t}).send(),t.setUnsynced(!1)}catch(t){this.logError("test_track notify error: "+t)}},e}(),d=function(){var t=function(){this._visitorDeferred=i.Deferred()};return t.prototype.initialize=function(t){var e=i.cookie(a.getCookieName());this._visitorDeferred.then(function(t){t.notifyUnsyncedAssignments()}),p.loadVisitor(e).then(function(e){t&&t.analytics&&e.setAnalytics(t.analytics),t&&t.errorLogger&&e.setErrorLogger(t.errorLogger),t&&"function"==typeof t.onVisitorLoaded&&t.onVisitorLoaded.call(null,e),this._visitorDeferred.resolve(e)}.bind(this)),this._setCookie()},t.prototype.vary=function(t,e){this._visitorDeferred.then(function(i){i.vary(t,e)})},t.prototype.ab=function(t,e){this._visitorDeferred.then(function(i){i.ab(t,e)})},t.prototype.logIn=function(t,e){var n=i.Deferred();return this._visitorDeferred.then(function(i){i.linkIdentifier(t,e).then(function(){this._setCookie(),i.analytics.identify(i.getId()),n.resolve()}.bind(this))}.bind(this)),n.promise()},t.prototype.signUp=function(t,e){var n=i.Deferred();return this._visitorDeferred.then(function(i){i.linkIdentifier(t,e).then(function(){this._setCookie(),i.analytics.alias(i.getId()),n.resolve()}.bind(this))}.bind(this)),n.promise()},t.prototype._setCookie=function(){this._visitorDeferred.then(function(t){i.cookie(a.getCookieName(),t.getId(),{expires:365,path:"/",domain:a.getCookieDomain()})})},t.prototype.getPublicAPI=function(){return{vary:this.vary.bind(this),ab:this.ab.bind(this),logIn:this.logIn.bind(this),signUp:this.signUp.bind(this),initialize:this.initialize.bind(this),_crx:{loadInfo:function(){var t=i.Deferred();return this._visitorDeferred.then(function(e){var i={};for(var n in e.getAssignmentRegistry())i[n]=e.getAssignmentRegistry()[n].getVariant();t.resolve({visitorId:e.getId(),splitRegistry:a.getSplitRegistry(),assignmentRegistry:i})}),t.promise()}.bind(this),persistAssignment:function(t,e,n,r){var s=i.Deferred();return this._visitorDeferred.then(function(i){new c({visitor:i,username:n,password:r,assignment:new o({splitName:t,variant:e,context:"chrome_extension",isUnsynced:!0})}).persistAssignment().then(function(){s.resolve()})}),s.promise()}.bind(this)}}},t}(),h=function(){var t=function(t){if(this.visitorId=t.visitorId,this.identifierType=t.identifierType,this.value=t.value,!this.visitorId)throw new Error("must provide visitorId");if(!this.identifierType)throw new Error("must provide identifierType");if(!this.value)throw new Error("must provide value")};return t.prototype.save=function(t,e){var n=i.Deferred();return i.ajax(a.getUrl()+"/api/v1/identifier",{method:"POST",dataType:"json",crossDomain:!0,data:{identifier_type:this.identifierType,value:this.value,visitor_id:this.visitorId}}).then(function(t){var e=new p({id:t.visitor.id,assignments:o.fromJsonArray(t.visitor.assignments)});n.resolve(e)}),n.promise()},t}(),l=function(){var t=function(t){if(!t.assignment)throw new Error("must provide assignment");if(!t.visitor)throw new Error("must provide visitor");this._assignment=t.assignment,this._visitor=t.visitor,this._splitRegistry=a.getSplitRegistry(),this._variantHandlers={}};return t.prototype.when=function(){var t=Array.prototype.slice.call(arguments,0),e=t.length-1,i="function"!=typeof t[0]&&t.length>0?t.slice(0,Math.max(1,e)):[],n=t[e];if(0===i.length)throw new Error("must provide at least one variant");for(var r=0;r<i.length;r++)this._assignHandlerToVariant(i[r],n)},t.prototype.default=function(t,e){if(this._defaultVariant)throw new Error("must provide exactly one `default`");this._defaultVariant=this._assignHandlerToVariant(t,e)},t.prototype.run=function(){this._validate();var t;this._variantHandlers[this._assignment.getVariant()]?t=this._variantHandlers[this._assignment.getVariant()]:(t=this._variantHandlers[this.getDefaultVariant()],this._defaulted=!0),t()},t.prototype.isDefaulted=function(){return this._defaulted||!1},t.prototype.getDefaultVariant=function(){return this._defaultVariant},t.prototype._assignHandlerToVariant=function(t,e){if("function"!=typeof e)throw new Error("must provide handler for "+t);return t=t.toString(),this._getSplit()&&!this._getSplit().hasOwnProperty(t)&&this._visitor.logError("configures unknown variant "+t),this._variantHandlers[t]=e,t},t.prototype._validate=function(){if(!this.getDefaultVariant())throw new Error("must provide exactly one `default`");if(this._getVariants().length<2)throw new Error("must provide at least one `when`");if(this._getSplit()){var t=this._getMissingVariants();if(t.length>0){var e=t.join(", ").replace(/, (.+)$/," and $1");this._visitor.logError("does not configure variants "+e)}}},t.prototype._getSplit=function(){return this._splitRegistry?this._splitRegistry[this._assignment.getSplitName()]:null},t.prototype._getVariants=function(){return Object.getOwnPropertyNames(this._variantHandlers)},t.prototype._getMissingVariants=function(){for(var t=this._getVariants(),e=this._getSplit(),i=Object.getOwnPropertyNames(e),n=[],r=0;r<i.length;r++){var o=i[r];-1===t.indexOf(o)&&n.push(o)}return n},t}(),g=function(){var t=function(t){if(!t.splitName)throw new Error("must provide splitName");if(!t.hasOwnProperty("trueVariant"))throw new Error("must provide trueVariant");if(!t.visitor)throw new Error("must provide visitor");this._splitName=t.splitName,this._trueVariant=t.trueVariant,this._visitor=t.visitor,this._splitRegistry=a.getSplitRegistry()};return t.prototype.getVariants=function(){var t=this._getSplitVariants();return t&&t.length>2&&this._visitor.logError("A/B for "+this._splitName+" configures split with more than 2 variants"),{true:this._getTrueVariant(),false:this._getFalseVariant()}},t.prototype._getTrueVariant=function(){return this._trueVariant||!0},t.prototype._getFalseVariant=function(){var t=this._getNonTrueVariants();return!!t&&t.sort()[0]},t.prototype._getNonTrueVariants=function(){var t=this._getSplitVariants();if(t){var e=this._getTrueVariant(),i=t.indexOf(e);return-1!==i&&t.splice(i,1),t}return null},t.prototype._getSplit=function(){return this._splitRegistry?this._splitRegistry[this._splitName]:null},t.prototype._getSplitVariants=function(){return this._getSplit()&&Object.getOwnPropertyNames(this._getSplit())},t}(),v=(new d).getPublicAPI(),m=function(){window.dispatchEvent(new CustomEvent("tt:lib:loaded",{detail:{TestTrack:v}}))};try{i(document).ready(function(){i(document.body).addClass("_tt");try{window.dispatchEvent(new CustomEvent("tt:class:added"))}catch(t){}}),m(),window.addEventListener("tt:listener:ready",m)}catch(t){}return v});
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).TestTrack=e()}(this,function(){"use strict";var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function e(t,e,r){return t(r={path:e,exports:{},require:function(t,e){return function(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}(null==e&&r.path)}},r.exports),r.exports}var r=e(function(t,e){var r;r=function(){function t(){for(var t=0,e={};t<arguments.length;t++){var r=arguments[t];for(var n in r)e[n]=r[n]}return e}return function e(r){function n(e,i,o){var s;if("undefined"!=typeof document){if(arguments.length>1){if("number"==typeof(o=t({path:"/"},n.defaults,o)).expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*o.expires),o.expires=a}o.expires=o.expires?o.expires.toUTCString():"";try{s=JSON.stringify(i),/^[\{\[]/.test(s)&&(i=s)}catch(t){}i=r.write?r.write(i,e):encodeURIComponent(String(i)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),e=(e=(e=encodeURIComponent(String(e))).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var u="";for(var c in o)o[c]&&(u+="; "+c,!0!==o[c]&&(u+="="+o[c]));return document.cookie=e+"="+i+u}e||(s={});for(var f=document.cookie?document.cookie.split("; "):[],p=/(%[0-9A-Z]{2})+/g,l=0;l<f.length;l++){var d=f[l].split("="),h=d.slice(1).join("=");this.json||'"'!==h.charAt(0)||(h=h.slice(1,-1));try{var g=d[0].replace(p,decodeURIComponent);if(h=r.read?r.read(h,g):r(h,g)||h.replace(p,decodeURIComponent),this.json)try{h=JSON.parse(h)}catch(t){}if(e===g){s=h;break}e||(s[g]=h)}catch(t){}}return s}}return n.set=n,n.get=function(t){return n.call(n,t)},n.getJSON=function(){return n.apply({json:!0},[].slice.call(arguments))},n.defaults={},n.remove=function(e,r){n(e,"",t(r,{expires:-1}))},n.withConverter=e,n}(function(){})},t.exports=r()}),n=function(){function t(t){if(!t.splitName)throw new Error("must provide splitName");if(!t.hasOwnProperty("variant"))throw new Error("must provide variant");if(!t.hasOwnProperty("isUnsynced"))throw new Error("must provide isUnsynced");this._splitName=t.splitName,this._variant=t.variant,this._context=t.context,this._isUnsynced=t.isUnsynced}return t.fromJsonArray=function(e){return e.map(function(e){var r=e.split_name,n=e.variant;return new t({context:e.context,variant:n,splitName:r,isUnsynced:e.unsynced})})},t.prototype.getSplitName=function(){return this._splitName},t.prototype.getVariant=function(){return this._variant},t.prototype.setVariant=function(t){this._variant=t},t.prototype.getContext=function(){return this._context},t.prototype.setContext=function(t){this._context=t},t.prototype.isUnsynced=function(){return this._isUnsynced},t.prototype.setUnsynced=function(t){this._isUnsynced=t},t}(),i=Object.prototype.hasOwnProperty,o=Array.isArray,s=function(){for(var t=[],e=0;e<256;++e)t.push("%"+((e<16?"0":"")+e.toString(16)).toUpperCase());return t}(),a=function(t,e){for(var r=e&&e.plainObjects?Object.create(null):{},n=0;n<t.length;++n)void 0!==t[n]&&(r[n]=t[n]);return r},u={arrayToObject:a,assign:function(t,e){return Object.keys(e).reduce(function(t,r){return t[r]=e[r],t},t)},combine:function(t,e){return[].concat(t,e)},compact:function(t){for(var e=[{obj:{o:t},prop:"o"}],r=[],n=0;n<e.length;++n)for(var i=e[n],s=i.obj[i.prop],a=Object.keys(s),u=0;u<a.length;++u){var c=a[u],f=s[c];"object"==typeof f&&null!==f&&-1===r.indexOf(f)&&(e.push({obj:s,prop:c}),r.push(f))}return function(t){for(;t.length>1;){var e=t.pop(),r=e.obj[e.prop];if(o(r)){for(var n=[],i=0;i<r.length;++i)void 0!==r[i]&&n.push(r[i]);e.obj[e.prop]=n}}}(e),t},decode:function(t,e,r){var n=t.replace(/\+/g," ");if("iso-8859-1"===r)return n.replace(/%[0-9a-f]{2}/gi,unescape);try{return decodeURIComponent(n)}catch(t){return n}},encode:function(t,e,r){if(0===t.length)return t;var n=t;if("symbol"==typeof t?n=Symbol.prototype.toString.call(t):"string"!=typeof t&&(n=String(t)),"iso-8859-1"===r)return escape(n).replace(/%u[0-9a-f]{4}/gi,function(t){return"%26%23"+parseInt(t.slice(2),16)+"%3B"});for(var i="",o=0;o<n.length;++o){var a=n.charCodeAt(o);45===a||46===a||95===a||126===a||a>=48&&a<=57||a>=65&&a<=90||a>=97&&a<=122?i+=n.charAt(o):a<128?i+=s[a]:a<2048?i+=s[192|a>>6]+s[128|63&a]:a<55296||a>=57344?i+=s[224|a>>12]+s[128|a>>6&63]+s[128|63&a]:(o+=1,a=65536+((1023&a)<<10|1023&n.charCodeAt(o)),i+=s[240|a>>18]+s[128|a>>12&63]+s[128|a>>6&63]+s[128|63&a])}return i},isBuffer:function(t){return!(!t||"object"!=typeof t||!(t.constructor&&t.constructor.isBuffer&&t.constructor.isBuffer(t)))},isRegExp:function(t){return"[object RegExp]"===Object.prototype.toString.call(t)},merge:function t(e,r,n){if(!r)return e;if("object"!=typeof r){if(o(e))e.push(r);else{if(!e||"object"!=typeof e)return[e,r];(n&&(n.plainObjects||n.allowPrototypes)||!i.call(Object.prototype,r))&&(e[r]=!0)}return e}if(!e||"object"!=typeof e)return[e].concat(r);var s=e;return o(e)&&!o(r)&&(s=a(e,n)),o(e)&&o(r)?(r.forEach(function(r,o){if(i.call(e,o)){var s=e[o];s&&"object"==typeof s&&r&&"object"==typeof r?e[o]=t(s,r,n):e.push(r)}else e[o]=r}),e):Object.keys(r).reduce(function(e,o){var s=r[o];return i.call(e,o)?e[o]=t(e[o],s,n):e[o]=s,e},s)}},c=String.prototype.replace,f=/%20/g,p={RFC1738:"RFC1738",RFC3986:"RFC3986"},l=u.assign({default:p.RFC3986,formatters:{RFC1738:function(t){return c.call(t,f,"+")},RFC3986:function(t){return String(t)}}},p),d=Object.prototype.hasOwnProperty,h={brackets:function(t){return t+"[]"},comma:"comma",indices:function(t,e){return t+"["+e+"]"},repeat:function(t){return t}},g=Array.isArray,m=Array.prototype.push,y=function(t,e){m.apply(t,g(e)?e:[e])},v=Date.prototype.toISOString,w=l.default,_={addQueryPrefix:!1,allowDots:!1,charset:"utf-8",charsetSentinel:!1,delimiter:"&",encode:!0,encoder:u.encode,encodeValuesOnly:!1,format:w,formatter:l.formatters[w],indices:!1,serializeDate:function(t){return v.call(t)},skipNulls:!1,strictNullHandling:!1},b=function t(e,r,n,i,o,s,a,c,f,p,l,d,h){var m,v=e;if("function"==typeof a?v=a(r,v):v instanceof Date?v=p(v):"comma"===n&&g(v)&&(v=v.join(",")),null===v){if(i)return s&&!d?s(r,_.encoder,h,"key"):r;v=""}if("string"==typeof(m=v)||"number"==typeof m||"boolean"==typeof m||"symbol"==typeof m||"bigint"==typeof m||u.isBuffer(v))return s?[l(d?r:s(r,_.encoder,h,"key"))+"="+l(s(v,_.encoder,h,"value"))]:[l(r)+"="+l(String(v))];var w,b=[];if(void 0===v)return b;if(g(a))w=a;else{var C=Object.keys(v);w=c?C.sort(c):C}for(var S=0;S<w.length;++S){var A=w[S];o&&null===v[A]||(g(v)?y(b,t(v[A],"function"==typeof n?n(r,A):r,n,i,o,s,a,c,f,p,l,d,h)):y(b,t(v[A],r+(f?"."+A:"["+A+"]"),n,i,o,s,a,c,f,p,l,d,h)))}return b},C=(Object.prototype.hasOwnProperty,Array.isArray,function(t,e){var r,n=t,i=function(t){if(!t)return _;if(null!==t.encoder&&void 0!==t.encoder&&"function"!=typeof t.encoder)throw new TypeError("Encoder has to be a function.");var e=t.charset||_.charset;if(void 0!==t.charset&&"utf-8"!==t.charset&&"iso-8859-1"!==t.charset)throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");var r=l.default;if(void 0!==t.format){if(!d.call(l.formatters,t.format))throw new TypeError("Unknown format option provided.");r=t.format}var n=l.formatters[r],i=_.filter;return("function"==typeof t.filter||g(t.filter))&&(i=t.filter),{addQueryPrefix:"boolean"==typeof t.addQueryPrefix?t.addQueryPrefix:_.addQueryPrefix,allowDots:void 0===t.allowDots?_.allowDots:!!t.allowDots,charset:e,charsetSentinel:"boolean"==typeof t.charsetSentinel?t.charsetSentinel:_.charsetSentinel,delimiter:void 0===t.delimiter?_.delimiter:t.delimiter,encode:"boolean"==typeof t.encode?t.encode:_.encode,encoder:"function"==typeof t.encoder?t.encoder:_.encoder,encodeValuesOnly:"boolean"==typeof t.encodeValuesOnly?t.encodeValuesOnly:_.encodeValuesOnly,filter:i,formatter:n,serializeDate:"function"==typeof t.serializeDate?t.serializeDate:_.serializeDate,skipNulls:"boolean"==typeof t.skipNulls?t.skipNulls:_.skipNulls,sort:"function"==typeof t.sort?t.sort:null,strictNullHandling:"boolean"==typeof t.strictNullHandling?t.strictNullHandling:_.strictNullHandling}}(e);"function"==typeof i.filter?n=(0,i.filter)("",n):g(i.filter)&&(r=i.filter);var o,s=[];if("object"!=typeof n||null===n)return"";o=e&&e.arrayFormat in h?e.arrayFormat:e&&"indices"in e?e.indices?"indices":"repeat":"indices";var a=h[o];r||(r=Object.keys(n)),i.sort&&r.sort(i.sort);for(var u=0;u<r.length;++u){var c=r[u];i.skipNulls&&null===n[c]||y(s,b(n[c],c,a,i.strictNullHandling,i.skipNulls,i.encode?i.encoder:null,i.filter,i.sort,i.allowDots,i.serializeDate,i.formatter,i.encodeValuesOnly,i.charset))}var f=s.join(i.delimiter),p=!0===i.addQueryPrefix?"?":"";return i.charsetSentinel&&("iso-8859-1"===i.charset?p+="utf8=%26%2310003%3B&":p+="utf8=%E2%9C%93&"),f.length>0?p+f:""}),S=function(t,e){return function(){for(var r=new Array(arguments.length),n=0;n<r.length;n++)r[n]=arguments[n];return t.apply(e,r)}},A=Object.prototype.toString;function E(t){return"[object Array]"===A.call(t)}function x(t){return null!==t&&"object"==typeof t}function N(t){return"[object Function]"===A.call(t)}function j(t,e){if(null!=t)if("object"!=typeof t&&(t=[t]),E(t))for(var r=0,n=t.length;r<n;r++)e.call(null,t[r],r,t);else for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&e.call(null,t[i],i,t)}var R={isArray:E,isArrayBuffer:function(t){return"[object ArrayBuffer]"===A.call(t)},isBuffer:function(t){return null!=t&&null!=t.constructor&&"function"==typeof t.constructor.isBuffer&&t.constructor.isBuffer(t)},isFormData:function(t){return"undefined"!=typeof FormData&&t instanceof FormData},isArrayBufferView:function(t){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(t):t&&t.buffer&&t.buffer instanceof ArrayBuffer},isString:function(t){return"string"==typeof t},isNumber:function(t){return"number"==typeof t},isObject:x,isUndefined:function(t){return void 0===t},isDate:function(t){return"[object Date]"===A.call(t)},isFile:function(t){return"[object File]"===A.call(t)},isBlob:function(t){return"[object Blob]"===A.call(t)},isFunction:N,isStream:function(t){return x(t)&&N(t.pipe)},isURLSearchParams:function(t){return"undefined"!=typeof URLSearchParams&&t instanceof URLSearchParams},isStandardBrowserEnv:function(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&"undefined"!=typeof window&&"undefined"!=typeof document},forEach:j,merge:function t(){var e={};function r(r,n){"object"==typeof e[n]&&"object"==typeof r?e[n]=t(e[n],r):e[n]=r}for(var n=0,i=arguments.length;n<i;n++)j(arguments[n],r);return e},deepMerge:function t(){var e={};function r(r,n){"object"==typeof e[n]&&"object"==typeof r?e[n]=t(e[n],r):e[n]="object"==typeof r?t({},r):r}for(var n=0,i=arguments.length;n<i;n++)j(arguments[n],r);return e},extend:function(t,e,r){return j(e,function(e,n){t[n]=r&&"function"==typeof e?S(e,r):e}),t},trim:function(t){return t.replace(/^\s*/,"").replace(/\s*$/,"")}};function V(t){return encodeURIComponent(t).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var O=function(t,e,r){if(!e)return t;var n;if(r)n=r(e);else if(R.isURLSearchParams(e))n=e.toString();else{var i=[];R.forEach(e,function(t,e){null!=t&&(R.isArray(t)?e+="[]":t=[t],R.forEach(t,function(t){R.isDate(t)?t=t.toISOString():R.isObject(t)&&(t=JSON.stringify(t)),i.push(V(e)+"="+V(t))}))}),n=i.join("&")}if(n){var o=t.indexOf("#");-1!==o&&(t=t.slice(0,o)),t+=(-1===t.indexOf("?")?"?":"&")+n}return t};function k(){this.handlers=[]}k.prototype.use=function(t,e){return this.handlers.push({fulfilled:t,rejected:e}),this.handlers.length-1},k.prototype.eject=function(t){this.handlers[t]&&(this.handlers[t]=null)},k.prototype.forEach=function(t){R.forEach(this.handlers,function(e){null!==e&&t(e)})};var U=k,T=function(t,e,r){return R.forEach(r,function(r){t=r(t,e)}),t},D=function(t){return!(!t||!t.__CANCEL__)},L=function(t,e){R.forEach(t,function(r,n){n!==e&&n.toUpperCase()===e.toUpperCase()&&(t[e]=r,delete t[n])})},I=function(t,e,r,n,i){return function(t,e,r,n,i){return t.config=e,r&&(t.code=r),t.request=n,t.response=i,t.isAxiosError=!0,t.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},t}(new Error(t),e,r,n,i)},P=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"],B=R.isStandardBrowserEnv()?function(){var t,e=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a");function n(t){var n=t;return e&&(r.setAttribute("href",n),n=r.href),r.setAttribute("href",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:"/"===r.pathname.charAt(0)?r.pathname:"/"+r.pathname}}return t=n(window.location.href),function(e){var r=R.isString(e)?n(e):e;return r.protocol===t.protocol&&r.host===t.host}}():function(){return!0},F=R.isStandardBrowserEnv()?{write:function(t,e,r,n,i,o){var s=[];s.push(t+"="+encodeURIComponent(e)),R.isNumber(r)&&s.push("expires="+new Date(r).toGMTString()),R.isString(n)&&s.push("path="+n),R.isString(i)&&s.push("domain="+i),!0===o&&s.push("secure"),document.cookie=s.join("; ")},read:function(t){var e=document.cookie.match(new RegExp("(^|;\\s*)("+t+")=([^;]*)"));return e?decodeURIComponent(e[3]):null},remove:function(t){this.write(t,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}},H=function(t){return new Promise(function(e,r){var n=t.data,i=t.headers;R.isFormData(n)&&delete i["Content-Type"];var o=new XMLHttpRequest;if(t.auth){var s=t.auth.username||"",a=t.auth.password||"";i.Authorization="Basic "+btoa(s+":"+a)}if(o.open(t.method.toUpperCase(),O(t.url,t.params,t.paramsSerializer),!0),o.timeout=t.timeout,o.onreadystatechange=function(){if(o&&4===o.readyState&&(0!==o.status||o.responseURL&&0===o.responseURL.indexOf("file:"))){var n,i,s,a,u,c="getAllResponseHeaders"in o?(n=o.getAllResponseHeaders(),u={},n?(R.forEach(n.split("\n"),function(t){if(a=t.indexOf(":"),i=R.trim(t.substr(0,a)).toLowerCase(),s=R.trim(t.substr(a+1)),i){if(u[i]&&P.indexOf(i)>=0)return;u[i]="set-cookie"===i?(u[i]?u[i]:[]).concat([s]):u[i]?u[i]+", "+s:s}}),u):u):null,f={data:t.responseType&&"text"!==t.responseType?o.response:o.responseText,status:o.status,statusText:o.statusText,headers:c,config:t,request:o};!function(t,e,r){var n=r.config.validateStatus;!n||n(r.status)?t(r):e(I("Request failed with status code "+r.status,r.config,null,r.request,r))}(e,r,f),o=null}},o.onabort=function(){o&&(r(I("Request aborted",t,"ECONNABORTED",o)),o=null)},o.onerror=function(){r(I("Network Error",t,null,o)),o=null},o.ontimeout=function(){r(I("timeout of "+t.timeout+"ms exceeded",t,"ECONNABORTED",o)),o=null},R.isStandardBrowserEnv()){var u=F,c=(t.withCredentials||B(t.url))&&t.xsrfCookieName?u.read(t.xsrfCookieName):void 0;c&&(i[t.xsrfHeaderName]=c)}if("setRequestHeader"in o&&R.forEach(i,function(t,e){void 0===n&&"content-type"===e.toLowerCase()?delete i[e]:o.setRequestHeader(e,t)}),t.withCredentials&&(o.withCredentials=!0),t.responseType)try{o.responseType=t.responseType}catch(e){if("json"!==t.responseType)throw e}"function"==typeof t.onDownloadProgress&&o.addEventListener("progress",t.onDownloadProgress),"function"==typeof t.onUploadProgress&&o.upload&&o.upload.addEventListener("progress",t.onUploadProgress),t.cancelToken&&t.cancelToken.promise.then(function(t){o&&(o.abort(),r(t),o=null)}),void 0===n&&(n=null),o.send(n)})},q={"Content-Type":"application/x-www-form-urlencoded"};function z(t,e){!R.isUndefined(t)&&R.isUndefined(t["Content-Type"])&&(t["Content-Type"]=e)}var J,M={adapter:("undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?J=H:"undefined"!=typeof XMLHttpRequest&&(J=H),J),transformRequest:[function(t,e){return L(e,"Accept"),L(e,"Content-Type"),R.isFormData(t)||R.isArrayBuffer(t)||R.isBuffer(t)||R.isStream(t)||R.isFile(t)||R.isBlob(t)?t:R.isArrayBufferView(t)?t.buffer:R.isURLSearchParams(t)?(z(e,"application/x-www-form-urlencoded;charset=utf-8"),t.toString()):R.isObject(t)?(z(e,"application/json;charset=utf-8"),JSON.stringify(t)):t}],transformResponse:[function(t){if("string"==typeof t)try{t=JSON.parse(t)}catch(t){}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(t){return t>=200&&t<300}};M.headers={common:{Accept:"application/json, text/plain, */*"}},R.forEach(["delete","get","head"],function(t){M.headers[t]={}}),R.forEach(["post","put","patch"],function(t){M.headers[t]=R.merge(q)});var $=M;function W(t){t.cancelToken&&t.cancelToken.throwIfRequested()}var Q=function(t){var e,r,n;return W(t),t.baseURL&&(n=t.url,!/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(n))&&(t.url=(e=t.baseURL,(r=t.url)?e.replace(/\/+$/,"")+"/"+r.replace(/^\/+/,""):e)),t.headers=t.headers||{},t.data=T(t.data,t.headers,t.transformRequest),t.headers=R.merge(t.headers.common||{},t.headers[t.method]||{},t.headers||{}),R.forEach(["delete","get","head","post","put","patch","common"],function(e){delete t.headers[e]}),(t.adapter||$.adapter)(t).then(function(e){return W(t),e.data=T(e.data,e.headers,t.transformResponse),e},function(e){return D(e)||(W(t),e&&e.response&&(e.response.data=T(e.response.data,e.response.headers,t.transformResponse))),Promise.reject(e)})},X=function(t,e){e=e||{};var r={};return R.forEach(["url","method","params","data"],function(t){void 0!==e[t]&&(r[t]=e[t])}),R.forEach(["headers","auth","proxy"],function(n){R.isObject(e[n])?r[n]=R.deepMerge(t[n],e[n]):void 0!==e[n]?r[n]=e[n]:R.isObject(t[n])?r[n]=R.deepMerge(t[n]):void 0!==t[n]&&(r[n]=t[n])}),R.forEach(["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"],function(n){void 0!==e[n]?r[n]=e[n]:void 0!==t[n]&&(r[n]=t[n])}),r};function G(t){this.defaults=t,this.interceptors={request:new U,response:new U}}G.prototype.request=function(t){"string"==typeof t?(t=arguments[1]||{}).url=arguments[0]:t=t||{},(t=X(this.defaults,t)).method=t.method?t.method.toLowerCase():"get";var e=[Q,void 0],r=Promise.resolve(t);for(this.interceptors.request.forEach(function(t){e.unshift(t.fulfilled,t.rejected)}),this.interceptors.response.forEach(function(t){e.push(t.fulfilled,t.rejected)});e.length;)r=r.then(e.shift(),e.shift());return r},G.prototype.getUri=function(t){return t=X(this.defaults,t),O(t.url,t.params,t.paramsSerializer).replace(/^\?/,"")},R.forEach(["delete","get","head","options"],function(t){G.prototype[t]=function(e,r){return this.request(R.merge(r||{},{method:t,url:e}))}}),R.forEach(["post","put","patch"],function(t){G.prototype[t]=function(e,r,n){return this.request(R.merge(n||{},{method:t,url:e,data:r}))}});var Z=G;function K(t){this.message=t}K.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},K.prototype.__CANCEL__=!0;var Y=K;function tt(t){if("function"!=typeof t)throw new TypeError("executor must be a function.");var e;this.promise=new Promise(function(t){e=t});var r=this;t(function(t){r.reason||(r.reason=new Y(t),e(r.reason))})}tt.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},tt.source=function(){var t;return{token:new tt(function(e){t=e}),cancel:t}};var et=tt;function rt(t){var e=new Z(t),r=S(Z.prototype.request,e);return R.extend(r,Z.prototype,e),R.extend(r,e),r}var nt=rt($);nt.Axios=Z,nt.create=function(t){return rt(X(nt.defaults,t))},nt.Cancel=Y,nt.CancelToken=et,nt.isCancel=D,nt.all=function(t){return Promise.all(t)},nt.spread=function(t){return function(e){return t.apply(null,e)}};var it=nt,ot=nt;it.default=ot;var st=it;for(var at={atob:function(t){if((t=(t=`${t}`).replace(/[ \t\n\f\r]/g,"")).length%4==0&&(t=t.replace(/==?$/,"")),t.length%4==1||/[^+\/0-9A-Za-z]/.test(t))return null;let e="",r=0,n=0;for(let o=0;o<t.length;o++)r<<=6,r|=(i=t[o],/[A-Z]/.test(i)?i.charCodeAt(0)-"A".charCodeAt(0):/[a-z]/.test(i)?i.charCodeAt(0)-"a".charCodeAt(0)+26:/[0-9]/.test(i)?i.charCodeAt(0)-"0".charCodeAt(0)+52:"+"===i?62:"/"===i?63:void 0),24===(n+=6)&&(e+=String.fromCharCode((16711680&r)>>16),e+=String.fromCharCode((65280&r)>>8),e+=String.fromCharCode(255&r),r=n=0);var i;return 12===n?(r>>=4,e+=String.fromCharCode(r)):18===n&&(r>>=2,e+=String.fromCharCode((65280&r)>>8),e+=String.fromCharCode(255&r)),e},btoa:function(t){let e;for(t=`${t}`,e=0;e<t.length;e++)if(t.charCodeAt(e)>255)return null;let r="";for(e=0;e<t.length;e+=3){const i=[void 0,void 0,void 0,void 0];i[0]=t.charCodeAt(e)>>2,i[1]=(3&t.charCodeAt(e))<<4,t.length>e+1&&(i[1]|=t.charCodeAt(e+1)>>4,i[2]=(15&t.charCodeAt(e+1))<<2),t.length>e+2&&(i[2]|=t.charCodeAt(e+2)>>6,i[3]=63&t.charCodeAt(e+2));for(let t=0;t<i.length;t++)void 0===i[t]?r+="=":r+=(n=i[t])<26?String.fromCharCode(n+"A".charCodeAt(0)):n<52?String.fromCharCode(n-26+"a".charCodeAt(0)):n<62?String.fromCharCode(n-52+"0".charCodeAt(0)):62===n?"+":63===n?"/":void 0}var n;return r}},ut=function(){function t(){}return t.prototype.getConfig=function(){var t=at.atob(window.TT);if(t)return JSON.parse(t);throw new Error("Unable to parse configuration")},t}(),ct=function(){function t(t,e,r){this._name=t,this._isFeatureGate=e,this._weighting=r}return t.prototype.getName=function(){return this._name},t.prototype.isFeatureGate=function(){return this._isFeatureGate},t.prototype.getVariants=function(){return Object.keys(this._weighting)},t.prototype.getWeighting=function(){return this._weighting},t.prototype.hasVariant=function(t){return t in this._weighting},t}(),ft=function(){return(ft=Object.assign||function(t){for(var e,r=1,n=arguments.length;r<n;r++)for(var i in e=arguments[r])Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t}).apply(this,arguments)},pt=function(){function t(t){this._splitArray=t||[],this._loaded=null!==t}return t.prototype.getSplit=function(t){return this.getSplits()[t]},t.prototype.isLoaded=function(){return this._loaded},t.prototype.asV1Hash=function(){var t={};for(var e in this.getSplits()){var r=this.getSplits()[e];t[e]=r.getWeighting()}return t},t.prototype.getSplits=function(){return this._loaded?(this._splits||(this._splits=this._splitArray.reduce(function(t,e){var r;return ft(ft({},t),((r={})[e.getName()]=e,r))},{})),this._splits):{}},t}(),lt=null,dt=null,ht=null,gt=function(){if(!lt){var t=new ut;lt=t.getConfig()}return lt},mt={_clear:function(){lt=null},getUrl:function(){return gt().url},getCookieDomain:function(){return gt().cookieDomain},getCookieName:function(){return gt().cookieName||"tt_visitor_id"},getExperienceSamplingWeight:function(){return gt().experienceSamplingWeight},getSplitRegistry:function(){var t=gt().splits;if(!t)return new pt(null);if(!ht){var e=Object.keys(t).map(function(e){var r=t[e];return new ct(e,r.feature_gate,r.weights)});ht=new pt(e)}return ht},getAssignments:function(){var t=gt().assignments;if(!t)return null;if(!dt)for(var e in dt=[],t)dt.push(new n({splitName:e,variant:t[e],isUnsynced:!1}));return dt}},yt=st.create({baseURL:mt.getUrl()+"/api",headers:{"Content-Type":"application/x-www-form-urlencoded"},crossDomain:!0}),vt=function(){function t(t){if(this._visitor=t.visitor,this._assignment=t.assignment,this._username=t.username,this._password=t.password,!this._visitor)throw new Error("must provide visitor");if(!this._assignment)throw new Error("must provide assignment");if(!this._username)throw new Error("must provide username");if(!this._password)throw new Error("must provide password")}return t.prototype.persistAssignment=function(){var t=this;return yt.post("/v1/assignment_override",C({visitor_id:this._visitor.getId(),split_name:this._assignment.getSplitName(),variant:this._assignment.getVariant(),context:this._assignment.getContext(),mixpanel_result:"success"}),{auth:{username:this._username,password:this._password}}).catch(function(e){if(e.response){var r=e.response,n=r.status,i=r.statusText,o=r.data;t._visitor.logError("test_track persistAssignment response error: "+n+", "+i+", "+o)}else t._visitor.logError("test_track persistAssignment other error: "+e)})},t}(),wt=function(){function t(t){if(!t.splitName)throw new Error("must provide splitName");if(!t.hasOwnProperty("trueVariant"))throw new Error("must provide trueVariant");if(!t.visitor)throw new Error("must provide visitor");this._splitName=t.splitName,this._trueVariant=t.trueVariant,this._visitor=t.visitor,this._splitRegistry=mt.getSplitRegistry()}return t.prototype.getVariants=function(){var t=this._getSplitVariants();return t&&t.length>2&&this._visitor.logError("A/B for "+this._splitName+" configures split with more than 2 variants"),{true:this._getTrueVariant(),false:this._getFalseVariant()}},t.prototype._getTrueVariant=function(){return this._trueVariant||"true"},t.prototype._getFalseVariant=function(){var t=this._getNonTrueVariants();return Array.isArray(t)&&0!==t.length?t.sort()[0]:"false"},t.prototype._getNonTrueVariants=function(){var t=this._getSplitVariants();if(t){var e=this._getTrueVariant(),r=t.indexOf(e.toString());return-1!==r&&t.splice(r,1),t}return null},t.prototype._getSplit=function(){return this._splitRegistry.getSplit(this._splitName)},t.prototype._getSplitVariants=function(){return this._getSplit()&&this._getSplit().getVariants()},t}(),_t=function(){function t(t){var e=t.visitor,r=t.assignment;if(this._visitor=e,this._assignment=r,!this._visitor)throw new Error("must provide visitor");if(!this._assignment)throw new Error("must provide assignment")}return t.prototype.send=function(){var t=this,e=this._persistAssignment(),r=new Promise(function(e){t._visitor.analytics.trackAssignment(t._visitor.getId(),t._assignment,function(r){return t._persistAssignment(r?"success":"failure").then(e)})});return Promise.all([e,r])},t.prototype._persistAssignment=function(t){var e=this;return yt.post("/v1/assignment_event",C({visitor_id:this._visitor.getId(),split_name:this._assignment.getSplitName(),context:this._assignment.getContext(),mixpanel_result:t})).catch(function(t){if(t.response){var r=t.response,n=r.status,i=r.statusText,o=r.data;e._visitor.logError("test_track persistAssignment response error: "+n+", "+i+", "+o)}else e._visitor.logError("test_track persistAssignment other error: "+t)})},t}(),bt=function(){function t(t){if(this.visitorId=t.visitorId,this.identifierType=t.identifierType,this.value=t.value,!this.visitorId)throw new Error("must provide visitorId");if(!this.identifierType)throw new Error("must provide identifierType");if(!this.value)throw new Error("must provide value")}return t.prototype.save=function(){return yt.post("/v1/identifier",C({identifier_type:this.identifierType,value:this.value,visitor_id:this.visitorId})).then(function(t){var e=t.data;return new Ot({id:e.visitor.id,assignments:n.fromJsonArray(e.visitor.assignments)})})},t}(),Ct=function(){function t(){}return t.prototype.trackAssignment=function(t,e,r){var n={TTVisitorID:t,SplitName:e.getSplitName(),SplitVariant:e.getVariant(),SplitContext:e.getContext()};window.mixpanel&&window.mixpanel.track("SplitAssigned",n,r)},t.prototype.identify=function(t){window.mixpanel&&window.mixpanel.identify(t)},t.prototype.alias=function(t){window.mixpanel&&window.mixpanel.alias(t)},t}(),St=e(function(t){var e="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof window.msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto);if(e){var r=new Uint8Array(16);t.exports=function(){return e(r),r}}else{var n=new Array(16);t.exports=function(){for(var t,e=0;e<16;e++)0==(3&e)&&(t=4294967296*Math.random()),n[e]=t>>>((3&e)<<3)&255;return n}}}),At=[],Et=0;Et<256;++Et)At[Et]=(Et+256).toString(16).substr(1);var xt=function(t,e){var r=e||0,n=At;return[n[t[r++]],n[t[r++]],n[t[r++]],n[t[r++]],"-",n[t[r++]],n[t[r++]],"-",n[t[r++]],n[t[r++]],"-",n[t[r++]],n[t[r++]],"-",n[t[r++]],n[t[r++]],n[t[r++]],n[t[r++]],n[t[r++]],n[t[r++]]].join("")};var Nt=function(t,e,r){var n=e&&r||0;"string"==typeof t&&(e="binary"===t?new Array(16):null,t=null);var i=(t=t||{}).random||(t.rng||St)();if(i[6]=15&i[6]|64,i[8]=63&i[8]|128,e)for(var o=0;o<16;++o)e[n+o]=i[o];return e||xt(i)},jt=e(function(e){!function(t){function r(t,e){var r=(65535&t)+(65535&e);return(t>>16)+(e>>16)+(r>>16)<<16|65535&r}function n(t,e,n,i,o,s){return r((a=r(r(e,t),r(i,s)))<<(u=o)|a>>>32-u,n);var a,u}function i(t,e,r,i,o,s,a){return n(e&r|~e&i,t,e,o,s,a)}function o(t,e,r,i,o,s,a){return n(e&i|r&~i,t,e,o,s,a)}function s(t,e,r,i,o,s,a){return n(e^r^i,t,e,o,s,a)}function a(t,e,r,i,o,s,a){return n(r^(e|~i),t,e,o,s,a)}function u(t,e){var n,u,c,f,p;t[e>>5]|=128<<e%32,t[14+(e+64>>>9<<4)]=e;var l=1732584193,d=-271733879,h=-1732584194,g=271733878;for(n=0;n<t.length;n+=16)u=l,c=d,f=h,p=g,l=i(l,d,h,g,t[n],7,-680876936),g=i(g,l,d,h,t[n+1],12,-389564586),h=i(h,g,l,d,t[n+2],17,606105819),d=i(d,h,g,l,t[n+3],22,-1044525330),l=i(l,d,h,g,t[n+4],7,-176418897),g=i(g,l,d,h,t[n+5],12,1200080426),h=i(h,g,l,d,t[n+6],17,-1473231341),d=i(d,h,g,l,t[n+7],22,-45705983),l=i(l,d,h,g,t[n+8],7,1770035416),g=i(g,l,d,h,t[n+9],12,-1958414417),h=i(h,g,l,d,t[n+10],17,-42063),d=i(d,h,g,l,t[n+11],22,-1990404162),l=i(l,d,h,g,t[n+12],7,1804603682),g=i(g,l,d,h,t[n+13],12,-40341101),h=i(h,g,l,d,t[n+14],17,-1502002290),l=o(l,d=i(d,h,g,l,t[n+15],22,1236535329),h,g,t[n+1],5,-165796510),g=o(g,l,d,h,t[n+6],9,-1069501632),h=o(h,g,l,d,t[n+11],14,643717713),d=o(d,h,g,l,t[n],20,-373897302),l=o(l,d,h,g,t[n+5],5,-701558691),g=o(g,l,d,h,t[n+10],9,38016083),h=o(h,g,l,d,t[n+15],14,-660478335),d=o(d,h,g,l,t[n+4],20,-405537848),l=o(l,d,h,g,t[n+9],5,568446438),g=o(g,l,d,h,t[n+14],9,-1019803690),h=o(h,g,l,d,t[n+3],14,-187363961),d=o(d,h,g,l,t[n+8],20,1163531501),l=o(l,d,h,g,t[n+13],5,-1444681467),g=o(g,l,d,h,t[n+2],9,-51403784),h=o(h,g,l,d,t[n+7],14,1735328473),l=s(l,d=o(d,h,g,l,t[n+12],20,-1926607734),h,g,t[n+5],4,-378558),g=s(g,l,d,h,t[n+8],11,-2022574463),h=s(h,g,l,d,t[n+11],16,1839030562),d=s(d,h,g,l,t[n+14],23,-35309556),l=s(l,d,h,g,t[n+1],4,-1530992060),g=s(g,l,d,h,t[n+4],11,1272893353),h=s(h,g,l,d,t[n+7],16,-155497632),d=s(d,h,g,l,t[n+10],23,-1094730640),l=s(l,d,h,g,t[n+13],4,681279174),g=s(g,l,d,h,t[n],11,-358537222),h=s(h,g,l,d,t[n+3],16,-722521979),d=s(d,h,g,l,t[n+6],23,76029189),l=s(l,d,h,g,t[n+9],4,-640364487),g=s(g,l,d,h,t[n+12],11,-421815835),h=s(h,g,l,d,t[n+15],16,530742520),l=a(l,d=s(d,h,g,l,t[n+2],23,-995338651),h,g,t[n],6,-198630844),g=a(g,l,d,h,t[n+7],10,1126891415),h=a(h,g,l,d,t[n+14],15,-1416354905),d=a(d,h,g,l,t[n+5],21,-57434055),l=a(l,d,h,g,t[n+12],6,1700485571),g=a(g,l,d,h,t[n+3],10,-1894986606),h=a(h,g,l,d,t[n+10],15,-1051523),d=a(d,h,g,l,t[n+1],21,-2054922799),l=a(l,d,h,g,t[n+8],6,1873313359),g=a(g,l,d,h,t[n+15],10,-30611744),h=a(h,g,l,d,t[n+6],15,-1560198380),d=a(d,h,g,l,t[n+13],21,1309151649),l=a(l,d,h,g,t[n+4],6,-145523070),g=a(g,l,d,h,t[n+11],10,-1120210379),h=a(h,g,l,d,t[n+2],15,718787259),d=a(d,h,g,l,t[n+9],21,-343485551),l=r(l,u),d=r(d,c),h=r(h,f),g=r(g,p);return[l,d,h,g]}function c(t){var e,r="",n=32*t.length;for(e=0;e<n;e+=8)r+=String.fromCharCode(t[e>>5]>>>e%32&255);return r}function f(t){var e,r=[];for(r[(t.length>>2)-1]=void 0,e=0;e<r.length;e+=1)r[e]=0;var n=8*t.length;for(e=0;e<n;e+=8)r[e>>5]|=(255&t.charCodeAt(e/8))<<e%32;return r}function p(t){var e,r,n="";for(r=0;r<t.length;r+=1)e=t.charCodeAt(r),n+="0123456789abcdef".charAt(e>>>4&15)+"0123456789abcdef".charAt(15&e);return n}function l(t){return unescape(encodeURIComponent(t))}function d(t){return function(t){return c(u(f(t),8*t.length))}(l(t))}function h(t,e){return function(t,e){var r,n,i=f(t),o=[],s=[];for(o[15]=s[15]=void 0,i.length>16&&(i=u(i,8*t.length)),r=0;r<16;r+=1)o[r]=909522486^i[r],s[r]=1549556828^i[r];return n=u(o.concat(f(e)),512+8*e.length),c(u(s.concat(n),640))}(l(t),l(e))}function g(t,e,r){return e?r?h(e,t):p(h(e,t)):r?d(t):p(d(t))}e.exports?e.exports=g:t.md5=g}(t)}),Rt=function(){function t(t){if(this.visitor=t.visitor,this.splitName=t.splitName,!this.visitor)throw new Error("must provide visitor");if(!this.splitName)throw new Error("must provide splitName")}return t.prototype.getVariant=function(){if(!mt.getSplitRegistry().isLoaded())return null;for(var t=0,e=this.getAssignmentBucket(),r=this.getWeighting(),n=this.getSortedVariants(),i=0;i<n.length;i++){var o=n[i];if((t+=r[o])>e)return o}throw new Error("Assignment bucket out of range. "+e+" unmatched in "+this.splitName+": "+JSON.stringify(r))},t.prototype.getSplitVisitorHash=function(){return jt(this.splitName+this.visitor.getId())},t.prototype.getHashFixnum=function(){return parseInt(this.getSplitVisitorHash().substr(0,8),16)},t.prototype.getAssignmentBucket=function(){return this.getHashFixnum()%100},t.prototype.getSortedVariants=function(){return this.getVariants().sort()},t.prototype.getVariants=function(){return Object.getOwnPropertyNames(this.getWeighting())},t.prototype.getWeighting=function(){var t=mt.getSplitRegistry().getSplit(this.splitName);if(!t){var e='Unknown split: "'+this.splitName+'"';throw this.visitor.logError(e),new Error(e)}return t.getWeighting()},t}(),Vt=function(){function t(t){if(!t.assignment)throw new Error("must provide assignment");if(!t.visitor)throw new Error("must provide visitor");this._assignment=t.assignment,this._visitor=t.visitor,this._splitRegistry=mt.getSplitRegistry(),this._variantHandlers={}}return t.prototype.when=function(){for(var t=this,e=[],r=0;r<arguments.length;r++)e[r]=arguments[r];var n="function"==typeof e[e.length-1]?e.pop():null;if(0===e.length)throw new Error("must provide at least one variant");e.forEach(function(e){"string"==typeof e&&t._assignHandlerToVariant(e,n)})},t.prototype.default=function(t,e){if(this._defaultVariant)throw new Error("must provide exactly one `default`");this._defaultVariant=this._assignHandlerToVariant(t,e)},t.prototype.run=function(){this._validate();var t=this.getDefaultVariant();if(void 0===t)throw new Error("must provide exactly one `default`");var e=this._variantHandlers[t],r=this._assignment.getVariant();r&&this._variantHandlers[r]?e=this._variantHandlers[r]:this._defaulted=!0,e()},t.prototype.isDefaulted=function(){return this._defaulted||!1},t.prototype.getDefaultVariant=function(){return this._defaultVariant},t.prototype._assignHandlerToVariant=function(t,e){if("function"!=typeof e)throw new Error("must provide handler for "+t);return t=t.toString(),this._getSplit()&&!this._getSplit().hasVariant(t)&&this._visitor.logError("configures unknown variant "+t),this._variantHandlers[t]=e,t},t.prototype._validate=function(){if(!this.getDefaultVariant())throw new Error("must provide exactly one `default`");if(this._getVariants().length<2)throw new Error("must provide at least one `when`");if(this._getSplit()){var t=this._getMissingVariants();if(t.length>0){var e=t.join(", ").replace(/, (.+)$/," and $1");this._visitor.logError("does not configure variants "+e)}}},t.prototype._getSplit=function(){return this._splitRegistry.getSplit(this._assignment.getSplitName())},t.prototype._getVariants=function(){return Object.getOwnPropertyNames(this._variantHandlers)},t.prototype._getMissingVariants=function(){var t=this._getVariants();return this._getSplit().getVariants().filter(function(e){return!t.includes(e)})},t}(),Ot=function(){function t(t){var e=t.id,r=t.assignments,n=t.ttOffline;if(this._id=e,this._assignments=r,this._ttOffline=n,!this._id)throw new Error("must provide id");if(!this._assignments)throw new Error("must provide assignments");this._errorLogger=function(t){window.console.error(t)},this.analytics=new Ct}return t.loadVisitor=function(e){if(e){var r=mt.getAssignments();return r?Promise.resolve(new t({id:e,assignments:r,ttOffline:!1})):yt.get("/v1/visitors/"+e,{timeout:5e3}).then(function(e){var r=e.data;return new t({id:r.id,assignments:n.fromJsonArray(r.assignments),ttOffline:!1})}).catch(function(){return new t({id:e,assignments:[],ttOffline:!0})})}return Promise.resolve(new t({id:Nt(),assignments:[],ttOffline:!1}))},t.prototype.getId=function(){return this._id},t.prototype.getAssignmentRegistry=function(){return this._assignmentRegistry||(this._assignmentRegistry=this._assignments.reduce(function(t,e){var r;return ft(ft({},t),((r={})[e.getSplitName()]=e,r))},{})),this._assignmentRegistry},t.prototype.vary=function(t,e){if("object"!=typeof e.variants)throw new Error("must provide variants object to `vary` for "+t);if(!e.context)throw new Error("must provide context to `vary` for "+t);if(!e.defaultVariant&&!1!==e.defaultVariant)throw new Error("must provide defaultVariant to `vary` for "+t);var r=e.defaultVariant.toString(),n=e.variants,i=e.context;if(!n.hasOwnProperty(r))throw new Error("defaultVariant: "+r+" must be represented in variants object");var o=this._getAssignmentFor(t,i),s=new Vt({assignment:o,visitor:this});for(var a in n)n.hasOwnProperty(a)&&(a===r?s.default(a,n[a]):s.when(a,n[a]));s.run(),s.isDefaulted()&&(o.setVariant(s.getDefaultVariant()),o.setUnsynced(!0),o.setContext(i)),this.notifyUnsyncedAssignments()},t.prototype.ab=function(t,e){var r=new wt({splitName:t,trueVariant:e.trueVariant,visitor:this}).getVariants(),n={};n[r.true.toString()]=function(){e.callback(!0)},n[r.false.toString()]=function(){e.callback(!1)},this.vary(t,{context:e.context,variants:n,defaultVariant:r.false})},t.prototype.setErrorLogger=function(t){if("function"!=typeof t)throw new Error("must provide function for errorLogger");this._errorLogger=t},t.prototype.logError=function(t){this._errorLogger.call(null,t)},t.prototype.linkIdentifier=function(t,e){var r=this;return new bt({identifierType:t,value:e,visitorId:this.getId()}).save().then(function(t){r._merge(t),r.notifyUnsyncedAssignments()})},t.prototype.setAnalytics=function(t){if("object"!=typeof t)throw new Error("must provide object for setAnalytics");this.analytics=t},t.prototype.notifyUnsyncedAssignments=function(){this._getUnsyncedAssignments().forEach(this._notify.bind(this))},t.prototype._getUnsyncedAssignments=function(){var t=this.getAssignmentRegistry();return Object.keys(t).reduce(function(e,r){var n=t[r];return n.isUnsynced()&&e.push(n),e},[])},t.prototype._merge=function(t){var e=this.getAssignmentRegistry(),r=t.getAssignmentRegistry();this._id=t.getId(),Object.assign(e,r)},t.prototype._getAssignmentFor=function(t,e){return this.getAssignmentRegistry()[t]||this._generateAssignmentFor(t,e)},t.prototype._generateAssignmentFor=function(t,e){var r=new Rt({visitor:this,splitName:t}).getVariant();r||(this._ttOffline=!0);var i=new n({splitName:t,variant:r,context:e,isUnsynced:!0});return this._assignments.push(i),this._assignmentRegistry=null,i},t.prototype._notify=function(t){try{if(this._ttOffline)return;new _t({visitor:this,assignment:t}).send(),t.setUnsynced(!1)}catch(t){this.logError("test_track notify error: "+t)}},t}(),kt=null,Ut=(new(function(){function t(){this._visitorLoaded=new Promise(function(t){return kt=t})}return t.prototype.initialize=function(t){var e=r.get(mt.getCookieName());return Ot.loadVisitor(e).then(function(e){t&&t.analytics&&e.setAnalytics(t.analytics),t&&t.errorLogger&&e.setErrorLogger(t.errorLogger),t&&"function"==typeof t.onVisitorLoaded&&t.onVisitorLoaded.call(null,e),e.notifyUnsyncedAssignments(),kt&&kt(e)}),this._setCookie(),this._visitorLoaded},t.prototype.vary=function(t,e){return this._visitorLoaded.then(function(r){r.vary(t,e)})},t.prototype.ab=function(t,e){return this._visitorLoaded.then(function(r){r.ab(t,e)})},t.prototype.logIn=function(t,e){var r=this;return this._visitorLoaded.then(function(n){return n.linkIdentifier(t,e).then(function(){r._setCookie(),n.analytics.identify(n.getId())})})},t.prototype.signUp=function(t,e){var r=this;return this._visitorLoaded.then(function(n){return n.linkIdentifier(t,e).then(function(){r._setCookie(),n.analytics.alias(n.getId())})})},t.prototype._setCookie=function(){return this._visitorLoaded.then(function(t){r.set(mt.getCookieName(),t.getId(),{expires:365,path:"/",domain:mt.getCookieDomain()})})},t.prototype.getPublicAPI=function(){var t=this;return{vary:this.vary.bind(this),ab:this.ab.bind(this),logIn:this.logIn.bind(this),signUp:this.signUp.bind(this),initialize:this.initialize.bind(this),_crx:{loadInfo:function(){return t._visitorLoaded.then(function(t){var e={};for(var r in t.getAssignmentRegistry())e[r]=t.getAssignmentRegistry()[r].getVariant();return{visitorId:t.getId(),splitRegistry:mt.getSplitRegistry().asV1Hash(),assignmentRegistry:e}})},persistAssignment:function(e,r,i,o){return t._visitorLoaded.then(function(t){return new vt({visitor:t,username:i,password:o,assignment:new n({splitName:e,variant:r,context:"chrome_extension",isUnsynced:!0})}).persistAssignment()})}}}},t}())).getPublicAPI(),Tt=function(){window.dispatchEvent(new CustomEvent("tt:lib:loaded",{detail:{TestTrack:Ut}}))},Dt=function(){document.body.classList.add("_tt");try{window.dispatchEvent(new CustomEvent("tt:class:added"))}catch(t){}};try{"loading"===document.readyState?document.addEventListener("DOMContentLoaded",Dt):Dt(),Tt(),window.addEventListener("tt:listener:ready",Tt)}catch(t){}return Ut});
@@ -12,9 +12,7 @@ module TestTrack::Controller
12
12
  class_methods do
13
13
  def require_feature_flag(feature_flag, *args)
14
14
  before_action(*args) do
15
- unless test_track_visitor.ab(feature_flag, context: self.class.name.underscore)
16
- raise ActionController::RoutingError, 'Not Found'
17
- end
15
+ raise ActionController::RoutingError, 'Not Found' unless test_track_visitor.ab(feature_flag, context: self.class.name.underscore)
18
16
  end
19
17
  end
20
18
  end
@@ -6,6 +6,7 @@ module TestTrack::RequiredOptions
6
6
  def require_option!(opts, opt_name, my_opts = {})
7
7
  opt_provided = my_opts[:allow_nil] ? opts.key?(opt_name) : opts[opt_name]
8
8
  raise(ArgumentError, "Must provide #{opt_name}") unless opt_provided
9
+
9
10
  opts.delete(opt_name)
10
11
  end
11
12
  end
@@ -19,10 +19,8 @@ class TestTrack::ABConfiguration
19
19
  private
20
20
 
21
21
  def build_variant_hash
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
25
- { true: true_variant, false: false_variant }
22
+ notify_because_ab("configures split with more than 2 variants") if split_variants && split_variants.size > 2
23
+ { true: true_variant, false: false_variant } # rubocop:disable Lint/BooleanSymbol
26
24
  end
27
25
 
28
26
  def true_variant
@@ -30,7 +28,7 @@ class TestTrack::ABConfiguration
30
28
  end
31
29
 
32
30
  def false_variant
33
- @false_variant ||= non_true_variants.present? ? non_true_variants.sort.first : false
31
+ @false_variant ||= non_true_variants.present? ? non_true_variants.min : false
34
32
  end
35
33
 
36
34
  attr_reader :split_name, :split_registry
@@ -8,6 +8,7 @@ module TestTrack::Analytics
8
8
 
9
9
  def mixpanel
10
10
  raise "ENV['MIXPANEL_TOKEN'] must be set" unless ENV['MIXPANEL_TOKEN']
11
+
11
12
  @mixpanel ||= Mixpanel::Tracker.new(ENV['MIXPANEL_TOKEN'])
12
13
  end
13
14
  end
@@ -9,6 +9,7 @@ module TestTrack::Analytics
9
9
  def error_handler=(handler)
10
10
  raise ArgumentError, "error_handler must be a lambda" unless handler.lambda?
11
11
  raise ArgumentError, "error_handler must accept 1 argument" unless handler.arity == 1
12
+
12
13
  @error_handler = handler
13
14
  end
14
15
 
@@ -7,6 +7,7 @@ class TestTrack::ApplicationIdentity
7
7
 
8
8
  def app_name
9
9
  raise 'must configure TestTrack.app_name on application initialization' if TestTrack.app_name.blank?
10
+
10
11
  TestTrack.app_name
11
12
  end
12
13
 
@@ -27,7 +27,8 @@ class TestTrack::Assignment
27
27
 
28
28
  def _variant
29
29
  return if visitor.offline?
30
+
30
31
  variant = TestTrack::VariantCalculator.new(visitor: visitor, split_name: split_name).variant
31
- variant && variant.to_s
32
+ variant&.to_s
32
33
  end
33
34
  end
@@ -1,5 +1,5 @@
1
1
  class TestTrack::ConfigUpdater
2
- def initialize(schema_file_path = Rails.root.join('db', 'test_track_schema.yml'))
2
+ def initialize(schema_file_path = Rails.root.join('db/test_track_schema.yml'))
3
3
  @schema_file_path = schema_file_path
4
4
  end
5
5
 
@@ -75,7 +75,7 @@ class TestTrack::ConfigUpdater
75
75
  end
76
76
 
77
77
  def unpersisted_split_names
78
- @unpersisted_splits ||= splits.keys - remote_splits.keys
78
+ @unpersisted_split_names ||= splits.keys - remote_splits.keys
79
79
  end
80
80
 
81
81
  def splits
@@ -90,7 +90,7 @@ class TestTrack::ConfigUpdater
90
90
  end
91
91
 
92
92
  def schema_file_hash
93
- @schema_hash ||= YAML.safe_load(schema_file_contents) || {}
93
+ @schema_file_hash ||= YAML.safe_load(schema_file_contents) || {}
94
94
  end
95
95
 
96
96
  def schema_file_contents
@@ -50,26 +50,24 @@ class TestTrack::Fake::SplitRegistry
50
50
  end
51
51
 
52
52
  def legacy_test_track_schema_yml
53
- unless instance_variable_defined?(:@legacy_test_track_schema_yml)
54
- @legacy_test_track_schema_yml = _legacy_test_track_schema_yml
55
- end
53
+ @legacy_test_track_schema_yml = _legacy_test_track_schema_yml unless instance_variable_defined?(:@legacy_test_track_schema_yml)
56
54
  @legacy_test_track_schema_yml
57
55
  end
58
56
 
59
57
  def _legacy_test_track_schema_yml
60
58
  File.exist?(legacy_test_track_schema_yml_path) &&
61
59
  YAML.load_file(legacy_test_track_schema_yml_path).with_indifferent_access
62
- rescue
60
+ rescue StandardError
63
61
  nil
64
62
  end
65
63
 
66
64
  def legacy_test_track_schema_yml_path
67
- ENV["TEST_TRACK_SCHEMA_FILE_PATH"] || Rails.root.join('db', 'test_track_schema.yml').to_s
65
+ ENV["TEST_TRACK_SCHEMA_FILE_PATH"] || Rails.root.join('db/test_track_schema.yml').to_s
68
66
  end
69
67
 
70
68
  def split_registry_with_deterministic_weights
71
69
  splits = split_hash.each_with_object({}) do |(split_name, weighting_registry), result|
72
- default_variant = weighting_registry.keys.sort.first
70
+ default_variant = weighting_registry.keys.min
73
71
 
74
72
  adjusted_weights = { default_variant => 100 }
75
73
  weighting_registry.except(default_variant).each_key do |variant|
@@ -24,9 +24,7 @@ class TestTrack::JobSession
24
24
 
25
25
  def notify_unsynced_assignments!
26
26
  identity_visitor_map.each_value do |visitor|
27
- if visitor.loaded? && visitor.unsynced_assignments.present?
28
- TestTrack::ThreadedVisitorNotifier.new(visitor).notify
29
- end
27
+ TestTrack::ThreadedVisitorNotifier.new(visitor).notify if visitor.loaded? && visitor.unsynced_assignments.present?
30
28
  end
31
29
  end
32
30
 
@@ -4,7 +4,7 @@ class TestTrack::LazyVisitorByIdentity
4
4
  end
5
5
 
6
6
  def loaded?
7
- @visitor.present?
7
+ @__visitor__.present?
8
8
  end
9
9
 
10
10
  def id_loaded?
@@ -13,8 +13,12 @@ class TestTrack::LazyVisitorByIdentity
13
13
 
14
14
  private
15
15
 
16
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
17
- __visitor__.send(method, *args, &block)
16
+ def method_missing(method, *args, &block)
17
+ if __visitor__.respond_to?(method)
18
+ __visitor__.send(method, *args, &block)
19
+ else
20
+ super
21
+ end
18
22
  end
19
23
 
20
24
  def respond_to_missing?(method, include_private = false)
@@ -22,7 +26,7 @@ class TestTrack::LazyVisitorByIdentity
22
26
  end
23
27
 
24
28
  def __visitor__
25
- @visitor ||= __load_visitor__
29
+ @__visitor__ ||= __load_visitor__
26
30
  end
27
31
 
28
32
  def __load_visitor__
@@ -22,6 +22,7 @@ module TestTrack::MisconfigurationNotifier
22
22
  class Airbrake
23
23
  def notify(msg)
24
24
  raise "Aibrake was configured but not found" unless defined?(::Airbrake)
25
+
25
26
  if ::Airbrake.respond_to?(:notify_or_ignore)
26
27
  ::Airbrake.notify_or_ignore(StandardError.new, error_message: msg)
27
28
  else
@@ -28,6 +28,7 @@ class TestTrack::NotifyAssignmentJob
28
28
  def maybe_track
29
29
  return "failure" unless TestTrack.enabled?
30
30
  return "success" if skip_analytics_event?
31
+
31
32
  result = TestTrack.analytics.track(TestTrack::AnalyticsEvent.new(visitor_id, assignment))
32
33
  result ? "success" : "failure"
33
34
  end
@@ -1,7 +1,7 @@
1
1
  class TestTrack::Remote::AssignmentEvent
2
2
  include TestTrack::RemoteModel
3
3
 
4
- collection_path '/api/v1/assignment_event'
4
+ collection_path 'api/v1/assignment_event'
5
5
 
6
6
  attributes :visitor_id, :split_name, :unsynced
7
7
 
@@ -3,6 +3,7 @@ class TestTrack::Remote::FakeServer
3
3
 
4
4
  def self.reset!(seed)
5
5
  raise('Cannot reset FakeServer if TestTrack is enabled.') if TestTrack.enabled?
6
+
6
7
  put('api/v1/reset', seed: seed)
7
8
  end
8
9
  end
@@ -1,7 +1,7 @@
1
1
  class TestTrack::Remote::Identifier
2
2
  include TestTrack::RemoteModel
3
3
 
4
- collection_path '/api/v1/identifier'
4
+ collection_path 'api/v1/identifier'
5
5
 
6
6
  has_one :remote_visitor, data_key: :visitor, class_name: "TestTrack::Remote::Visitor"
7
7
 
@@ -21,6 +21,7 @@ class TestTrack::Remote::Identifier
21
21
 
22
22
  def visitor_opts!
23
23
  raise("Visitor data unavailable until you save this identifier.") unless attributes[:remote_visitor]
24
+
24
25
  { id: remote_visitor.id, assignments: remote_visitor.assignments }
25
26
  end
26
27
  end
@@ -1,7 +1,7 @@
1
1
  class TestTrack::Remote::IdentifierType
2
2
  include TestTrack::RemoteModel
3
3
 
4
- collection_path '/api/v1/identifier_type'
4
+ collection_path 'api/v1/identifier_type'
5
5
 
6
6
  attributes :name
7
7
 
@@ -1,7 +1,7 @@
1
1
  class TestTrack::Remote::SplitConfig
2
2
  include TestTrack::RemoteModel
3
3
 
4
- collection_path '/api/v1/split_configs'
4
+ collection_path 'api/v1/split_configs'
5
5
 
6
6
  attributes :name, :weighting_registry
7
7
 
@@ -1,7 +1,7 @@
1
1
  class TestTrack::Remote::SplitDetail
2
2
  include TestTrack::RemoteModel
3
3
 
4
- collection_path '/api/v1/split_details'
4
+ collection_path 'api/v1/split_details'
5
5
 
6
6
  attributes :name, :hypothesis, :assignment_criteria, :description, :owner, :location, :platform, :variant_details
7
7
 
@@ -10,7 +10,7 @@ class TestTrack::Remote::SplitDetail
10
10
  if faked?
11
11
  new(fake_instance_attributes(name))
12
12
  else
13
- get("/api/v1/split_details/#{name}")
13
+ get("api/v1/split_details/#{name}")
14
14
  end
15
15
  end
16
16
 
@@ -3,7 +3,7 @@ class TestTrack::Remote::SplitRegistry
3
3
 
4
4
  CACHE_KEY = 'test_track_split_registry'.freeze
5
5
 
6
- collection_path '/api/v2/split_registry'
6
+ collection_path 'api/v3/builds/:build_timestamp/split_registry'
7
7
 
8
8
  class << self
9
9
  def fake_instance_attributes(_)
@@ -15,7 +15,7 @@ class TestTrack::Remote::SplitRegistry
15
15
  if faked?
16
16
  new(fake_instance_attributes(nil))
17
17
  else
18
- get('/api/v2/split_registry')
18
+ get("api/v3/builds/#{TestTrack.build_timestamp}/split_registry")
19
19
  end
20
20
  end
21
21
 
@@ -1,7 +1,7 @@
1
1
  class TestTrack::Remote::Visitor
2
2
  include TestTrack::RemoteModel
3
3
 
4
- collection_path '/api/v1/visitors'
4
+ collection_path 'api/v1/visitors'
5
5
 
6
6
  has_many :assignments
7
7
 
@@ -13,7 +13,7 @@ class TestTrack::Remote::Visitor
13
13
  if faked?
14
14
  new(fake_instance_attributes(nil))
15
15
  else
16
- get("/api/v1/identifier_types/#{identifier_type}/identifiers/#{identifier_value}/visitor")
16
+ get("api/v1/identifier_types/#{identifier_type}/identifiers/#{identifier_value}/visitor")
17
17
  end
18
18
  end
19
19
 
@@ -8,7 +8,7 @@ class TestTrack::Remote::VisitorDetail
8
8
  if faked?
9
9
  new(fake_instance_attributes(nil))
10
10
  else
11
- get("/api/v1/identifier_types/#{identifier_type}/identifiers/#{identifier_value}/visitor_detail")
11
+ get("api/v1/identifier_types/#{identifier_type}/identifiers/#{identifier_value}/visitor_detail")
12
12
  end
13
13
  end
14
14
 
@@ -27,9 +27,9 @@ class TestTrack::SplitRegistry
27
27
  registry_hash && registry_hash['splits'][split_name] && registry_hash['splits'][split_name]['weights'].freeze
28
28
  end
29
29
 
30
- def to_v1_hash
31
- registry_hash && registry_hash['splits'].each_with_object({}) do |(k, v), result|
32
- result[k] = v['weights']
30
+ def to_hash
31
+ registry_hash && registry_hash['splits'].transform_values do |v|
32
+ { weights: v['weights'], feature_gate: v['feature_gate'] }
33
33
  end
34
34
  end
35
35
 
@@ -21,14 +21,12 @@ class TestTrack::ThreadedVisitorNotifier
21
21
 
22
22
  def new_thread_with_request_store
23
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
24
+ RequestStore.begin!
25
+ RequestStore.store.merge!(original_store)
26
+ yield
27
+ ensure
28
+ RequestStore.end!
29
+ RequestStore.clear!
32
30
  end
33
31
  end
34
32
  end
@@ -14,12 +14,10 @@ class TestTrack::UnsyncedAssignmentsNotifier
14
14
  def notify
15
15
  assignments.each do |assignment|
16
16
  build_notify_assignment_job(assignment).tap do |job|
17
- begin
18
- job.perform
19
- rescue *TestTrack::SERVER_ERRORS => e
20
- Rails.logger.error "TestTrack failed to notify unsynced assignments, retrying. #{e}"
21
- Delayed::Job.enqueue(build_notify_assignment_job(assignment))
22
- end
17
+ job.perform
18
+ rescue *TestTrack::SERVER_ERRORS => e
19
+ Rails.logger.error "TestTrack failed to notify unsynced assignments, retrying. #{e}"
20
+ Delayed::Job.enqueue(build_notify_assignment_job(assignment))
23
21
  end
24
22
  end
25
23
  end
@@ -15,6 +15,7 @@ class TestTrack::VariantCalculator
15
15
 
16
16
  def variant
17
17
  return nil unless split_registry.loaded?
18
+
18
19
  @variant ||= _variant || raise("Assignment bucket out of range. #{assignment_bucket} unmatched in #{split_name}: #{weighting}")
19
20
  end
20
21
 
@@ -18,6 +18,7 @@ class TestTrack::VaryDSL
18
18
 
19
19
  def when(*variants, &block)
20
20
  raise ArgumentError, "must provide at least one variant" if variants.blank?
21
+
21
22
  variants.each do |variant|
22
23
  assign_behavior_to_variant(variant, block)
23
24
  end
@@ -25,6 +26,7 @@ class TestTrack::VaryDSL
25
26
 
26
27
  def default(variant, &block)
27
28
  raise ArgumentError, "cannot provide more than one `default`" unless default_variant.nil?
29
+
28
30
  @default_variant = assign_behavior_to_variant(variant, block)
29
31
  end
30
32
 
@@ -57,6 +59,7 @@ class TestTrack::VaryDSL
57
59
  variant = variant.to_s
58
60
 
59
61
  raise ArgumentError, "must provide block for #{variant}" unless behavior_proc
62
+
60
63
  notify_because_vary(<<-MESSAGE) if variant_behaviors.include?(variant)
61
64
  configures variant "#{variant}" more than once.
62
65
  This will raise an error in the next version of test_track_rails_client.
@@ -93,6 +96,7 @@ class TestTrack::VaryDSL
93
96
  raise ArgumentError, "must provide exactly one `default`" unless default_variant
94
97
  raise ArgumentError, "must provide at least one `when`" unless variant_behaviors.size >= 2
95
98
  return true unless split_variants
99
+
96
100
  missing_variants = split_variants - variant_behaviors.keys
97
101
  notify_because_vary("does not configure variants #{missing_variants.to_sentence}") && false unless missing_variants.empty?
98
102
  end
@@ -23,6 +23,7 @@ class TestTrack::Visitor
23
23
  raise "unknown opts: #{opts.keys.to_sentence}" if opts.present?
24
24
 
25
25
  raise ArgumentError, "must provide block to `vary` for #{split_name}" unless block_given?
26
+
26
27
  v = TestTrack::VaryDSL.new(assignment: assignment_for(split_name), context: context, split_registry: split_registry)
27
28
  yield v
28
29
  v.send :run
@@ -38,18 +39,18 @@ class TestTrack::Visitor
38
39
  ab_configuration = TestTrack::ABConfiguration.new split_name: split_name, true_variant: true_variant, split_registry: split_registry
39
40
 
40
41
  vary(split_name, context: context) do |v|
41
- v.when ab_configuration.variants[:true] do
42
+ v.when ab_configuration.variants[:true] do # rubocop:disable Lint/BooleanSymbol
42
43
  true
43
44
  end
44
- v.default ab_configuration.variants[:false] do
45
+ v.default ab_configuration.variants[:false] do # rubocop:disable Lint/BooleanSymbol
45
46
  false
46
47
  end
47
48
  end
48
49
  end
49
50
 
50
51
  def assignment_registry
51
- @assignment_registry ||= assignments.each_with_object({}) do |assignment, hsh|
52
- hsh[assignment.split_name] = assignment
52
+ @assignment_registry ||= assignments.index_by do |assignment|
53
+ assignment.split_name
53
54
  end
54
55
  end
55
56
 
@@ -28,8 +28,9 @@ class TestTrack::WebSession
28
28
  url: TestTrack.url,
29
29
  cookieDomain: cookie_domain,
30
30
  cookieName: visitor_cookie_name,
31
- registry: current_visitor.split_registry.to_v1_hash,
32
- assignments: current_visitor.assignment_json
31
+ splits: current_visitor.split_registry.to_hash,
32
+ assignments: current_visitor.assignment_json,
33
+ experienceSamplingWeight: current_visitor.split_registry.experience_sampling_weight
33
34
  }
34
35
  end
35
36
 
@@ -58,6 +59,7 @@ class TestTrack::WebSession
58
59
 
59
60
  If your app doesn't support authentication, set it to `:none`.
60
61
  ERROR
62
+
61
63
  identity = controller.class.test_track_identity
62
64
  controller.send(identity) unless identity == :none
63
65
  end
@@ -92,7 +94,7 @@ class TestTrack::WebSession
92
94
  end
93
95
 
94
96
  def _cookie_domain
95
- if bare_ip_address? || fully_qualified_cookie_domain_enabled?
97
+ if bare_ip_address? || request.local? || fully_qualified_cookie_domain_enabled?
96
98
  request.host
97
99
  else
98
100
  wildcard_domain
@@ -34,9 +34,7 @@ class TestTrack::WebSessionVisitorRepository
34
34
 
35
35
  def notify_unsynced_assignments!
36
36
  all.each do |visitor|
37
- if visitor.loaded? && visitor.unsynced_assignments.present?
38
- TestTrack::ThreadedVisitorNotifier.new(visitor).notify
39
- end
37
+ TestTrack::ThreadedVisitorNotifier.new(visitor).notify if visitor.loaded? && visitor.unsynced_assignments.present?
40
38
  end
41
39
  end
42
40
 
@@ -0,0 +1 @@
1
+ TestTrack.set_build_timestamp! unless ENV['SKIP_TESTTRACK_SET_BUILD_TIMESTAMP'] == '1'
@@ -10,6 +10,24 @@ namespace :test_track do
10
10
  end
11
11
  end
12
12
 
13
+ desc 'Generates build timestamp for fetching point-in-time split registries'
14
+ task generate_build_timestamp: :environment do
15
+ cli = TesttrackCli.instance
16
+
17
+ result = cli.call('generate_build_timestamp')
18
+ exit(result.exitstatus) unless result.success?
19
+ end
20
+
21
+ desc 'Sets an environment variable to block build timestamp generation on application initialization'
22
+ task :skip_set_build_timestamp do # rubocop:disable Rails/RakeEnvironment
23
+ ENV['SKIP_TESTTRACK_SET_BUILD_TIMESTAMP'] = '1'
24
+ end
25
+
26
+ desc 'Removes the testtrack/build_timestamp file'
27
+ task remove_build_timestamp: :environment do
28
+ File.delete('testtrack/build_timestamp') if File.exist?('testtrack/build_timestamp')
29
+ end
30
+
13
31
  namespace :schema do
14
32
  desc 'Load schema.yml state into TestTrack server'
15
33
  task load: :environment do
@@ -28,6 +46,9 @@ namespace :test_track do
28
46
  end
29
47
  end
30
48
 
49
+ task 'assets:clobber' => ['test_track:remove_build_timestamp']
50
+ task 'assets:environment' => ['test_track:skip_set_build_timestamp']
51
+ task 'assets:precompile' => ['test_track:generate_build_timestamp']
31
52
  task 'db:schema:load' => ['test_track:schema:load']
32
53
  task 'db:structure:load' => ['test_track:schema:load']
33
54
  task 'db:migrate' => ['test_track:migrate']
@@ -16,6 +16,8 @@ module TestTrack
16
16
  module_function
17
17
 
18
18
  SERVER_ERRORS = [Faraday::ConnectionFailed, Faraday::TimeoutError, Her::Errors::RemoteServerError].freeze
19
+ BUILD_TIMESTAMP_FILE_PATH = 'testtrack/build_timestamp'.freeze
20
+ BUILD_TIMESTAMP_REGEX = /\A\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(.\d+)?([+-][0-2]\d:[0-5]\d|Z)\z/.freeze
19
21
 
20
22
  mattr_accessor :enabled_override, :app_name
21
23
 
@@ -52,6 +54,26 @@ module TestTrack
52
54
  @misconfiguration_notifier_class_name = notifier_class_name
53
55
  end
54
56
 
57
+ def build_timestamp # rubocop:disable Metrics/MethodLength
58
+ @build_timestamp ||= begin
59
+ timestamp = _build_timestamp
60
+
61
+ if Rails.env.test? || Rails.env.development?
62
+ Time.zone.now.iso8601
63
+ elsif timestamp.present?
64
+ unless BUILD_TIMESTAMP_REGEX.match?(timestamp)
65
+ raise "./testtrack/build_timestamp is not a valid ISO 8601 timestamp, got '#{timestamp}'"
66
+ end
67
+
68
+ timestamp
69
+ else
70
+ raise 'TestTrack failed to load the required build timestamp. ' \
71
+ 'Ensure `test_track:generate_build_timestamp` task is run in `assets:precompile` and the build timestamp file is present.'
72
+ end
73
+ end
74
+ end
75
+ alias set_build_timestamp! build_timestamp
76
+
55
77
  private
56
78
 
57
79
  def analytics_instance
@@ -95,6 +117,7 @@ module TestTrack
95
117
 
96
118
  def url
97
119
  return nil unless private_url
120
+
98
121
  full_uri = URI.parse(private_url)
99
122
  full_uri.user = nil
100
123
  full_uri.password = nil
@@ -105,6 +128,10 @@ module TestTrack
105
128
  ENV['TEST_TRACK_API_URL']
106
129
  end
107
130
 
131
+ def _build_timestamp
132
+ File.read(BUILD_TIMESTAMP_FILE_PATH).chomp.presence if File.exist?(BUILD_TIMESTAMP_FILE_PATH)
133
+ end
134
+
108
135
  def enabled?
109
136
  enabled_override.nil? ? !Rails.env.test? : enabled_override
110
137
  end
@@ -2,13 +2,15 @@ require 'delayed_job'
2
2
 
3
3
  begin
4
4
  require 'airbrake'
5
- rescue LoadError # rubocop:disable Lint/HandleExceptions
5
+ rescue LoadError
6
+ # no-op
6
7
  end
7
8
 
8
9
  unless defined?(Delayed::Plugins::Airbrake) && Delayed::Worker.plugins.include?(Delayed::Plugins::Airbrake)
9
10
  begin
10
11
  require 'delayed-plugins-airbrake'
11
- rescue LoadError # rubocop:disable Lint/HandleExceptions
12
+ rescue LoadError
13
+ # no-op
12
14
  end
13
15
  end
14
16
 
@@ -1,3 +1,3 @@
1
1
  module TestTrackRailsClient
2
- VERSION = "4.0.0.alpha32" # rubocop:disable Style/MutableConstant
2
+ VERSION = "4.0.0.rc3" # 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.alpha32
4
+ version: 4.0.0.rc3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan O'Neill
@@ -10,10 +10,10 @@ authors:
10
10
  - John Mileham
11
11
  - Alan Norton
12
12
  - Sam Moore
13
- autorequire:
13
+ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2019-09-19 00:00:00.000000000 Z
16
+ date: 2020-12-04 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: delayed_job
@@ -134,7 +134,7 @@ dependencies:
134
134
  version: '4.1'
135
135
  - - "<"
136
136
  - !ruby/object:Gem::Version
137
- version: '6.0'
137
+ version: '7.0'
138
138
  type: :runtime
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
@@ -144,7 +144,7 @@ dependencies:
144
144
  version: '4.1'
145
145
  - - "<"
146
146
  - !ruby/object:Gem::Version
147
- version: '6.0'
147
+ version: '7.0'
148
148
  - !ruby/object:Gem::Dependency
149
149
  name: request_store
150
150
  requirement: !ruby/object:Gem::Requirement
@@ -161,20 +161,132 @@ dependencies:
161
161
  version: '1.3'
162
162
  - !ruby/object:Gem::Dependency
163
163
  name: appraisal
164
+ requirement: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ type: :development
170
+ prerelease: false
171
+ version_requirements: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ - !ruby/object:Gem::Dependency
177
+ name: pry-rails
178
+ requirement: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ type: :development
184
+ prerelease: false
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: rails-controller-testing
192
+ requirement: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ type: :development
198
+ prerelease: false
199
+ version_requirements: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: '0'
204
+ - !ruby/object:Gem::Dependency
205
+ name: rspec-rails
206
+ requirement: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ type: :development
212
+ prerelease: false
213
+ version_requirements: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ - !ruby/object:Gem::Dependency
219
+ name: rubocop
164
220
  requirement: !ruby/object:Gem::Requirement
165
221
  requirements:
166
222
  - - "~>"
167
223
  - !ruby/object:Gem::Version
168
- version: 2.2.0
224
+ version: 0.81.0
169
225
  type: :development
170
226
  prerelease: false
171
227
  version_requirements: !ruby/object:Gem::Requirement
172
228
  requirements:
173
229
  - - "~>"
174
230
  - !ruby/object:Gem::Version
175
- version: 2.2.0
231
+ version: 0.81.0
176
232
  - !ruby/object:Gem::Dependency
177
- name: pry-rails
233
+ name: rubocop-performance
234
+ requirement: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - ">="
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
239
+ type: :development
240
+ prerelease: false
241
+ version_requirements: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - ">="
244
+ - !ruby/object:Gem::Version
245
+ version: '0'
246
+ - !ruby/object:Gem::Dependency
247
+ name: rubocop-rails
248
+ requirement: !ruby/object:Gem::Requirement
249
+ requirements:
250
+ - - ">="
251
+ - !ruby/object:Gem::Version
252
+ version: '0'
253
+ type: :development
254
+ prerelease: false
255
+ version_requirements: !ruby/object:Gem::Requirement
256
+ requirements:
257
+ - - ">="
258
+ - !ruby/object:Gem::Version
259
+ version: '0'
260
+ - !ruby/object:Gem::Dependency
261
+ name: shoulda-matchers
262
+ requirement: !ruby/object:Gem::Requirement
263
+ requirements:
264
+ - - ">="
265
+ - !ruby/object:Gem::Version
266
+ version: '2.8'
267
+ type: :development
268
+ prerelease: false
269
+ version_requirements: !ruby/object:Gem::Requirement
270
+ requirements:
271
+ - - ">="
272
+ - !ruby/object:Gem::Version
273
+ version: '2.8'
274
+ - !ruby/object:Gem::Dependency
275
+ name: simplecov
276
+ requirement: !ruby/object:Gem::Requirement
277
+ requirements:
278
+ - - ">="
279
+ - !ruby/object:Gem::Version
280
+ version: '0'
281
+ type: :development
282
+ prerelease: false
283
+ version_requirements: !ruby/object:Gem::Requirement
284
+ requirements:
285
+ - - ">="
286
+ - !ruby/object:Gem::Version
287
+ version: '0'
288
+ - !ruby/object:Gem::Dependency
289
+ name: sqlite3
178
290
  requirement: !ruby/object:Gem::Requirement
179
291
  requirements:
180
292
  - - ">="
@@ -205,16 +317,16 @@ dependencies:
205
317
  name: webmock
206
318
  requirement: !ruby/object:Gem::Requirement
207
319
  requirements:
208
- - - "~>"
320
+ - - ">="
209
321
  - !ruby/object:Gem::Version
210
- version: 2.1.0
322
+ version: '0'
211
323
  type: :development
212
324
  prerelease: false
213
325
  version_requirements: !ruby/object:Gem::Requirement
214
326
  requirements:
215
- - - "~>"
327
+ - - ">="
216
328
  - !ruby/object:Gem::Version
217
- version: 2.1.0
329
+ version: '0'
218
330
  description: Easy split testing and feature flagging for Rails with TestTrack server
219
331
  email:
220
332
  - ryan.oneill@betterment.com
@@ -285,6 +397,7 @@ files:
285
397
  - app/views/tt/api/v1/split_details/show.json.jbuilder
286
398
  - app/views/tt/api/v1/visitors/_show.json.jbuilder
287
399
  - app/views/tt/api/v1/visitors/show.json.jbuilder
400
+ - config/initializers/set_build_timestamp.rb
288
401
  - config/initializers/test_track_api.rb
289
402
  - config/routes.rb
290
403
  - lib/generators/test_track/migration_generator.rb
@@ -341,7 +454,7 @@ homepage: https://github.com/Betterment
341
454
  licenses:
342
455
  - MIT
343
456
  metadata: {}
344
- post_install_message:
457
+ post_install_message:
345
458
  rdoc_options: []
346
459
  require_paths:
347
460
  - lib
@@ -349,16 +462,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
349
462
  requirements:
350
463
  - - ">="
351
464
  - !ruby/object:Gem::Version
352
- version: 2.1.0
465
+ version: 2.5.0
353
466
  required_rubygems_version: !ruby/object:Gem::Requirement
354
467
  requirements:
355
468
  - - ">"
356
469
  - !ruby/object:Gem::Version
357
470
  version: 1.3.1
358
471
  requirements: []
359
- rubyforge_project:
360
- rubygems_version: 2.5.1
361
- signing_key:
472
+ rubygems_version: 3.1.4
473
+ signing_key:
362
474
  specification_version: 4
363
475
  summary: Rails client for TestTrack
364
476
  test_files: []