-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from dollarshaveclub/refactor
Refactor
- Loading branch information
Showing
11 changed files
with
275 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"presets": ["es2015"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import babelify from 'babelify'; | ||
import browserify from 'browserify'; | ||
import buffer from 'vinyl-buffer'; | ||
import gulp from 'gulp'; | ||
import rename from 'gulp-rename'; | ||
import source from 'vinyl-source-stream'; | ||
import uglify from 'gulp-uglify'; | ||
|
||
gulp.task('default', () => { | ||
var bundler = browserify('./index.js', { | ||
standalone: 'Test' | ||
}); | ||
bundler.transform(babelify); | ||
bundler.bundle() | ||
.on('error', function (err) { console.error(err); }) | ||
.pipe(source('test.min.js')) | ||
.pipe(buffer()) | ||
.pipe(uglify()) | ||
.pipe(gulp.dest('build')); | ||
}); | ||
|
||
gulp.task('do-watch', () => { | ||
gulp.watch('lib/**/*.js', ['default']); | ||
}); | ||
|
||
|
||
gulp.task('watch', ['default', 'do-watch']); |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,132 +1 @@ | ||
(function() { | ||
|
||
var utils = { | ||
|
||
/** | ||
* Returns a random number between min and max | ||
* @param {Number} min The lower bound, inclusive | ||
* @param {Number} max The upper bound, exclusive | ||
* @return {Number} Random number | ||
*/ | ||
rand: function(min, max) { | ||
return Math.random() * (max - min) + min; | ||
}, | ||
|
||
/** | ||
* Fetches a random number and loops through the bounds of weights | ||
* @param {Array} names List of test names (Strings) | ||
* @param {Array} weights List of test weights (Numbers) | ||
* @return {String} Chosen test name | ||
* | ||
* .1 / 1 = 10% / 90% | ||
* .25 / .75 = 33% / 60% | ||
* .25 / 1 = 25% / 75% | ||
* | ||
* Weight sums represent the bounds. | ||
*/ | ||
chooseWeightedItem: function(names, weights) { | ||
|
||
// Total out the number of weights | ||
var total = 0, i; | ||
for(i = 0; i < weights.length; i++) { | ||
total += weights[i]; | ||
} | ||
|
||
var sum = 0; | ||
|
||
// Get a random number between 0 and the total number of weights | ||
var n = utils.rand(0, total); | ||
|
||
// Loop until we've encountered the first weight greater than our random number | ||
for (i = 0; i < names.length; i++) { | ||
sum += weights[i]; | ||
|
||
if (n <= sum) { | ||
return names[i]; | ||
} | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Test constructor | ||
* @param {String} name Name of the test | ||
* @param {Object} data Tests to execute | ||
* @param {Object} options Additional options | ||
* @return {Object} Selected test | ||
*/ | ||
var Test = function(name, data, options) { | ||
|
||
// Ensure options exists | ||
options = options || {}; | ||
options = { | ||
persist: typeof options.persist !== "undefined" ? options.persist : true, | ||
active: typeof options.active !== "undefined" ? options.active : true | ||
}; | ||
|
||
// Return if inactive | ||
if (!options.active) { | ||
return { | ||
active: false | ||
}; | ||
} | ||
|
||
// Set test name | ||
var storageKey = 'test-' + name; | ||
var bucket; | ||
|
||
// Retrieve Bucket from storage if possible | ||
if (options.persist && sessionStorage.getItem(storageKey)) { | ||
bucket = sessionStorage.getItem(storageKey); | ||
} | ||
|
||
// Determine bucket | ||
else { | ||
|
||
// Get a list of test names and test weights | ||
var names = Object.keys(data); | ||
var weights = []; | ||
for (var i = 0; i<names.length; i++) { | ||
if(typeof data[names[i]].weight == "undefined") data[names[i]].weight = 1; | ||
weights.push( data[names[i]].weight ); | ||
} | ||
|
||
// Select a random weighted bucket | ||
bucket = utils.chooseWeightedItem(names, weights); | ||
|
||
// Save | ||
sessionStorage.setItem(storageKey, bucket); | ||
} | ||
|
||
// We've now bucketed our user | ||
// Add classname | ||
document.body.classList.add(name); | ||
document.body.classList.add(bucket); | ||
|
||
// Call function if provided | ||
if (data[bucket]) { | ||
if (!data[bucket].chosen) data[bucket].chosen = Test.noop; | ||
data[bucket].chosen.call(this); | ||
} | ||
|
||
// Record test with GTM if possible | ||
if(typeof dataLayer !== "undefined") { | ||
var metrics = { | ||
abTests: {} | ||
}; | ||
metrics.abTests[name] = bucket; | ||
dataLayer.push(metrics); | ||
} | ||
|
||
// Return | ||
return { | ||
bucket: bucket, | ||
data: data[bucket], | ||
active: true | ||
}; | ||
}; | ||
|
||
Test.noop = function() {}; | ||
|
||
window.Test = Test; | ||
})(); | ||
module.exports = require('./lib').default; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import storage from '../vendor/storage'; | ||
|
||
const supportsClasslist = "classList" in document.createElement("_"); | ||
|
||
class Test { | ||
constructor(name, data = {}, options = {}) { | ||
|
||
options = { | ||
persist: typeof options.persist !== "undefined" ? options.persist : true, | ||
active: typeof options.active !== "undefined" ? options.active : true, | ||
chosen: typeof options.chosen !== "undefined" ? options.chosen : false | ||
}; | ||
|
||
if(!name) { throw new Error('Tests must have a name'); } | ||
|
||
// Return if inactive | ||
if (!options.active) { | ||
return { | ||
active: false | ||
}; | ||
} | ||
|
||
// Set test name | ||
let storageKey = 'test-' + name; | ||
let bucket; | ||
|
||
// Retrieve Bucket from storage if possible | ||
if (options.persist && storage.local.getItem(storageKey)) { | ||
bucket = storage.local.getItem(storageKey); | ||
} | ||
|
||
// Determine bucket | ||
else { | ||
|
||
// Get a list of test names and test weights | ||
let names = Object.keys(data); | ||
let weights = []; | ||
for (var i = 0; i < names.length; i++) { | ||
if (typeof data[names[i]].weight == "undefined") | ||
data[names[i]].weight = 1; | ||
weights.push( data[names[i]].weight ); | ||
} | ||
|
||
// Select a random weighted bucket | ||
bucket = Test.chooseWeightedItem(names, weights); | ||
|
||
// Save | ||
storage.local.setItem(storageKey, bucket); | ||
} | ||
|
||
// We've now bucketed our user | ||
// Add classname | ||
if (supportsClasslist) { | ||
document.body.classList.add(name, bucket); | ||
} else { | ||
document.body.className += ` ${name} ${bucket}`; | ||
} | ||
|
||
let info = { | ||
bucket: bucket, | ||
data: data[bucket], | ||
active: true | ||
}; | ||
|
||
// Call function if provided | ||
if (data[bucket]) { | ||
if (!data[bucket].chosen) data[bucket].chosen = Test.noop; | ||
data[bucket].chosen.call(this); | ||
} | ||
|
||
// Call chosen function | ||
if (options.chosen) { options.chosen.call(this, info); } | ||
|
||
// Record test with GTM if possible | ||
if(typeof dataLayer !== "undefined") { | ||
var metrics = { | ||
abTests: {} | ||
}; | ||
metrics.abTests[name] = bucket; | ||
dataLayer.push(metrics); | ||
} | ||
|
||
// Return | ||
return info; | ||
} | ||
|
||
static chooseWeightedItem (names, weights) { | ||
|
||
// Total out the number of weights | ||
var total = 0, i; | ||
for(i = 0; i < weights.length; i++) { | ||
total += weights[i]; | ||
} | ||
|
||
var sum = 0; | ||
|
||
// Get a random number between 0 and the total number of weights | ||
var n = Test.rand(0, total); | ||
|
||
// Loop until we've encountered the first weight greater than our random number | ||
for (i = 0; i < names.length; i++) { | ||
sum += weights[i]; | ||
|
||
if (n <= sum) { | ||
return names[i]; | ||
} | ||
} | ||
} | ||
|
||
static noop () {} | ||
|
||
static rand (min, max) { | ||
return Math.random() * (max - min) + min; | ||
} | ||
|
||
static storage () { return storage; } | ||
} | ||
|
||
export default Test; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.