Phuedx has uploaded a new change for review.
https://gerrit.wikimedia.org/r/193083
Change subject: Four minute abs
......................................................................
Four minute abs
* Allow experiments to be configured server-side and run client-side
** Experiments are all contained in the wgMFExperiments configuration
variable
** You enter a user into an experiment and get the bucket they've been
assigned to by calling
M.require( 'experiments' ).getBucket( experiment );
Here an experiment is a set of buckets and associated probabilities,
e.g. the user has a 50% likelihood of being assigned to the control
bucket, etc.
DISCUSSION
The `experiments` module simply wraps the `mw.user.bucket` method, which
has been deprecated since 1.23. We should either revert the deprecation
or add a similar method to the `user` module.
Change-Id: I309e52d9d85735b904830b5b15cada63a5947bb7
---
M includes/Config.php
M includes/MobileFrontend.hooks.php
M includes/Resources.php
A includes/config/Experiments.php
A javascripts/experiments.js
A tests/qunit/test_experiments.js
6 files changed, 141 insertions(+), 1 deletion(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend
refs/changes/83/193083/1
diff --git a/includes/Config.php b/includes/Config.php
index 64442c9..6c13fc9 100644
--- a/includes/Config.php
+++ b/includes/Config.php
@@ -3,6 +3,7 @@
require_once __DIR__ . "/config/Analytics.php";
require_once __DIR__ . "/config/Editing.php";
require_once __DIR__ . "/config/Experimental.php";
+ require_once __DIR__ . "/config/Experiments.php";
require_once __DIR__ . "/config/Legacy.php";
require_once __DIR__ . "/config/Nearby.php";
require_once __DIR__ . "/config/Site.php";
diff --git a/includes/MobileFrontend.hooks.php
b/includes/MobileFrontend.hooks.php
index baaee6c..d963d6c 100644
--- a/includes/MobileFrontend.hooks.php
+++ b/includes/MobileFrontend.hooks.php
@@ -354,7 +354,7 @@
* @return boolean
*/
public static function onResourceLoaderGetConfigVars( &$vars ) {
- global $wgMFNearbyEndpoint, $wgMFContentNamespace;
+ global $wgMFNearbyEndpoint, $wgMFContentNamespace,
$wgMFExperiments;
$vars['wgMFNearbyEndpoint'] = $wgMFNearbyEndpoint;
$vars['wgMFThumbnailSizes'] = array(
'tiny' => MobilePage::TINY_IMAGE_WIDTH,
@@ -369,6 +369,7 @@
// Set the licensing agreement that is displayed in the
uploading interface.
$wgMFUploadLicenseLink = SkinMinerva::getLicenseLink( 'upload'
);
$vars['wgMFUploadLicenseLink'] = $wgMFUploadLicenseLink;
+ $vars['wgMFExperiments'] = $wgMFExperiments;
return true;
}
diff --git a/includes/Resources.php b/includes/Resources.php
index 77ce766..6941323 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -306,6 +306,7 @@
'mobile.user',
'mediawiki.api',
'mobile.redlinks',
+ 'mobile.experiments',
),
'templates' => array(
'icon.hogan' => 'templates/icon.hogan',
@@ -1599,6 +1600,11 @@
'mobile.toc',
),
),
+ 'mobile.experiments' => $wgMFResourceFileModuleBoilerplate + array(
+ 'scripts' => array(
+ 'javascripts/experiments.js',
+ ),
+ ),
);
$wgResourceModules = array_merge( $wgResourceModules,
diff --git a/includes/config/Experiments.php b/includes/config/Experiments.php
new file mode 100644
index 0000000..c62a56d
--- /dev/null
+++ b/includes/config/Experiments.php
@@ -0,0 +1,36 @@
+<?php
+// Needs to be called within MediaWiki; not standalone
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'Not an entry point.' );
+}
+
+/**
+ * @var array A set of experiments.
+ *
+ * Consider the following example:
+ *
+ * <code>
+ * $wgMFExperiments = array(
+ * 'wikigrok' => array(
+ * 'enabled' => true,
+ * 'buckets' => array(
+ * 'control' => 0.33,
+ * 'A' => 0.33,
+ * 'B' => 0.33,
+ * ),
+ * 'version' => 1,
+ * 'expiry' => 14, // 14 days
+ * ),
+ * );
+ * </code>
+ *
+ * The wikigrok experiment has three buckets: control, A, and B. The user has
a 33% chance of being
+ * being assigned to each bucket. Note well that if the experiment were
disabled, then the user is
+ * always assigned to the control bucket.
+ *
+ * This is the first version of the experiment.
+ *
+ * The user will be rebucketed in 14 days unless the experiment is updated,
which would require
+ * changing the version, or is disabled.
+ */
+$wgMFExperiments = array();
diff --git a/javascripts/experiments.js b/javascripts/experiments.js
new file mode 100644
index 0000000..f840756
--- /dev/null
+++ b/javascripts/experiments.js
@@ -0,0 +1,37 @@
+( function ( mw, M ) {
+
+ var CONTROL_BUCKET = 'control';
+
+ M.define( 'experiments', {
+
+ /**
+ * Gets the bucket for the experiment.
+ *
+ * Defers to `mw.user.bucket` to do the bucketing.
+ *
+ * @param {String} experiment
+ * @throws Error If the experiment hasn't been defined in the
`$wgMFExperiments`
+ * configuration variable
+ * @returns {String}
+ */
+ getBucket: function ( experiment ) {
+ var experiments = mw.config.get( 'wgMFExperiments' ) ||
{},
+ options;
+
+ if ( !experiments.hasOwnProperty( experiment ) ) {
+
+ // TODO: Should errors like this be translated?
+ throw new Error( 'The experiment "' +
experiment + '" hasn\'t been defined.' );
+ }
+
+ options = experiments[experiment];
+
+ if ( !options.enabled ) {
+ return CONTROL_BUCKET;
+ }
+
+ return mw.user.bucket( experiment, options );
+ }
+ } );
+
+} ( mw, mw.mobileFrontend ) );
diff --git a/tests/qunit/test_experiments.js b/tests/qunit/test_experiments.js
new file mode 100644
index 0000000..c94ef4f
--- /dev/null
+++ b/tests/qunit/test_experiments.js
@@ -0,0 +1,59 @@
+( function ( mw, M ) {
+
+ QUnit.module( 'MobileFrontend Experiments', {
+ setup: function () {
+ this.wgMFExperiments = {
+ foo: {
+ enabled: true,
+ buckets: {
+ control: 50,
+ 'wikigrok-version-a': 25,
+ 'wikigrok-version-b': 25
+ },
+ version: 1,
+ expires: 14 // 14 days
+ },
+ bar: {
+ enabled: false,
+ buckets: {
+ control: 84,
+ 'should-see-wikigrok-roulette':
16
+ }
+ }
+ };
+ mw.config.set( 'wgMFExperiments', this.wgMFExperiments
);
+
+ this.bucketStub = this.stub( mw.user, 'bucket' );
+
+ this.experiments = M.require( 'experiments' );
+ }
+ } );
+
+ QUnit.test( 'it should throw when the experiment hasn\'t been defined',
1, function ( assert ) {
+ assert.throws( function () {
+ this.experiments.getBucket( 'baz' );
+ } );
+ } );
+
+ QUnit.test( 'it should defer to mw.user.bucket to perform the
bucketing', 1, function ( assert ) {
+ this.experiments.getBucket( 'foo' );
+
+ assert.strictEqual( true, this.bucketStub.calledWith( 'foo',
this.wgMFExperiments.foo ) );
+ } );
+
+ QUnit.test( 'it should get the bucket if the experiment has been
defined', 1, function ( assert ) {
+ var bucket = 'wikigrok-version-a';
+
+ this.bucketStub.returns( bucket );
+
+ assert.strictEqual( bucket, this.experiments.getBucket( 'foo' )
);
+ } );
+
+ QUnit.test( 'it should always return "control" if the experiment has
been defined as disabled', 2, function ( assert ) {
+ var bucket = this.experiments.getBucket( 'bar' );
+
+ assert.strictEqual( 'control', bucket );
+ assert.strictEqual( false, this.bucketStub.called );
+ } );
+
+} ( mw, mw.mobileFrontend ) );
--
To view, visit https://gerrit.wikimedia.org/r/193083
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I309e52d9d85735b904830b5b15cada63a5947bb7
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Phuedx <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits