Henning Snater has uploaded a new change for review.
https://gerrit.wikimedia.org/r/102089
Change subject: Implemented front-end ValueFormatterFactory
......................................................................
Implemented front-end ValueFormatterFactory
The ValueFormatterFactory manages ValueFormatters by DataType or DataValue type.
Change-Id: I49ead230e2d1cf64ffd4e7f347201325e38a3e07
---
M DataValuesCommon/DataValuesCommon.mw.php
A DataValuesCommon/js/ValueFormatters.resources.mw.php
M DataValuesCommon/js/ValueFormatters.resources.php
A DataValuesCommon/js/src/ValueFormatters/ValueFormatterFactory.js
A DataValuesCommon/js/src/ValueFormatters/mw.ext.valueFormatters.js
A DataValuesCommon/js/tests/ValueFormatters/ValueFormatterFactory.tests.js
6 files changed, 457 insertions(+), 1 deletion(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues
refs/changes/89/102089/2
diff --git a/DataValuesCommon/DataValuesCommon.mw.php
b/DataValuesCommon/DataValuesCommon.mw.php
index 85b7f78..afe448c 100644
--- a/DataValuesCommon/DataValuesCommon.mw.php
+++ b/DataValuesCommon/DataValuesCommon.mw.php
@@ -134,6 +134,17 @@
),
);
+ $testModules['qunit']['ext.valueFormatters.factory'] = $moduleTemplate
+ array(
+ 'scripts' => array(
+ 'ValueFormatterFactory.tests.js',
+ ),
+ 'dependencies' => array(
+ 'qunit.parameterize',
+ 'valueFormatters.factory',
+ 'valueFormatters.formatters',
+ ),
+ );
+
$testModules['qunit']['ext.valueFormatters.formatters'] =
$moduleTemplate + array(
'scripts' => array(
'formatters/NullFormatter.tests.js',
@@ -208,6 +219,6 @@
// Resource Loader module registration
$GLOBALS['wgResourceModules'] = array_merge(
$GLOBALS['wgResourceModules'],
- include( __DIR__ . '/js/ValueFormatters.resources.php' ),
+ include( __DIR__ . '/js/ValueFormatters.resources.mw.php' ),
include( __DIR__ . '/js/ValueParsers.resources.mw.php' )
);
diff --git a/DataValuesCommon/js/ValueFormatters.resources.mw.php
b/DataValuesCommon/js/ValueFormatters.resources.mw.php
new file mode 100644
index 0000000..815277d
--- /dev/null
+++ b/DataValuesCommon/js/ValueFormatters.resources.mw.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Definition of "ValueFormatters" ResourceLoader modules to be used when run
as MediaWiki
+ * extension.
+ * @since 0.1
+ *
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ *
+ * @codeCoverageIgnoreStart
+ */
+return call_user_func( function() {
+
+ $moduleTemplate = array(
+ 'localBasePath' => __DIR__ . '/src/ValueFormatters/',
+ 'remoteExtPath' =>
'DataValues/DataValuesCommon/js/src/ValueFormatters',
+ );
+
+ $mwVfResources = array(
+ 'mw.ext.valueFormatters' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'mw.ext.valueFormatters.js',
+ ),
+ 'dependencies' => array(
+ 'dataValues.values',
+ 'valueFormatters',
+ 'valueFormatters.formatters',
+ 'valueFormatters.factory',
+ 'mw.ext.valueView',
+ ),
+ ),
+ );
+
+ // Return ValueFormatter's native resources plus those required by the
MW extension:
+ return $mwVfResources + include( __DIR__ .
'/ValueFormatters.resources.php' );
+} );
diff --git a/DataValuesCommon/js/ValueFormatters.resources.php
b/DataValuesCommon/js/ValueFormatters.resources.php
index d26c392..73436ee 100644
--- a/DataValuesCommon/js/ValueFormatters.resources.php
+++ b/DataValuesCommon/js/ValueFormatters.resources.php
@@ -35,6 +35,16 @@
),
),
+ 'valueFormatters.factory' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'ValueFormatterFactory.js',
+ ),
+ 'dependencies' => array(
+ 'dataTypes',
+ 'valueFormatters',
+ ),
+ ),
+
'valueFormatters.formatters' => $moduleTemplate + array(
'scripts' => array(
'formatters/NullFormatter.js',
diff --git a/DataValuesCommon/js/src/ValueFormatters/ValueFormatterFactory.js
b/DataValuesCommon/js/src/ValueFormatters/ValueFormatterFactory.js
new file mode 100644
index 0000000..6e1103f
--- /dev/null
+++ b/DataValuesCommon/js/src/ValueFormatters/ValueFormatterFactory.js
@@ -0,0 +1,152 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, vf, dt ) {
+ 'use strict';
+
+ /**
+ * Factory managing ValueFormatter instances
+ * @constructor
+ * @since 0.1
+ *
+ * @param {Function} [DefaultFormatter] Constructor of a default
formatter that shall be
+ * returned when no formatter is registered for a specific
purpose.
+ */
+ var SELF = vf.ValueFormatterFactory = function VpValueFormatterFactory(
DefaultFormatter ) {
+ this._DefaultFormatter = DefaultFormatter || null;
+ this._formattersForDataTypes = {};
+ this._formattersForDataValueTypes = {};
+ };
+
+ $.extend( SELF.prototype, {
+ /**
+ * Default formatter constructor to be returned when no
formatter is registered for a
+ * specific purpose.
+ * @type {Function|null}
+ */
+ _DefaultFormatter: null,
+
+ /**
+ * @type {Object}
+ */
+ _formattersForDataTypes: null,
+
+ /**
+ * @type {Object}
+ */
+ _formattersForDataValueTypes: null,
+
+ /**
+ * Registers a formatter.
+ * @since 0.1
+ *
+ * @param {dataTypes.DataType|string} purpose DataType object
or DataValue type.
+ * @param {Function} Formatter ValueFormatter constructor.
+ *
+ * @throws {Error} if the formatter constructor is invalid.
+ * @throws {Error} no proper purpose is provided.
+ */
+ registerFormatter: function( purpose, Formatter ) {
+ if( !$.isFunction( Formatter ) ) {
+ throw new Error( 'Invalid ValueFormatter
constructor' );
+ }
+
+ if( purpose instanceof dt.DataType ) {
+ this._registerDataTypeFormatter( purpose,
Formatter );
+ } else if( typeof purpose === 'string' ) {
+ this._registerDataValueFormatter( purpose,
Formatter );
+ } else {
+ throw new Error( 'No sufficient purpose
provided what to register the formatter '
+ + 'for' );
+ }
+ },
+
+ /**
+ * Registers a formatter for a certain data type.
+ * @since 0.1
+ *
+ * @param {dataTypes.DataType} dataType
+ * @param {Function} Formatter
+ *
+ * @throws {Error} if a formatter for the specified dataType
object is registered already.
+ */
+ _registerDataTypeFormatter: function( dataType, Formatter ) {
+ assertIsValueFormatterConstructor( Formatter );
+
+ if( this._formattersForDataTypes[dataType.getId()] ) {
+ throw new Error( 'Formatter for DataType "' +
dataType.getId() + '" is registered '
+ + 'already' );
+ }
+
+ this._formattersForDataTypes[dataType.getId()] =
Formatter;
+ },
+
+ /**
+ * Registers a formatter for a certain data value type.
+ * @since 0.1
+ *
+ * @param {string} dataValueType
+ * @param {Function} Formatter
+ *
+ * @throws {Error} if a formatter for the specified DataValue
type is registered already.
+ */
+ _registerDataValueFormatter: function( dataValueType, Formatter
) {
+ assertIsValueFormatterConstructor( Formatter );
+
+ if( this._formattersForDataValueTypes[dataValueType] ) {
+ throw new Error( 'Formatter for DataValue type
"' + dataValueType + '" is '
+ + 'registered already' );
+ }
+
+ this._formattersForDataValueTypes[dataValueType] =
Formatter;
+ },
+
+ /**
+ * Returns the ValueFormatter constructor registered for the
specified purpose or the
+ * default formatter if no ValueFormatter is registered for
that purpose.
+ * @since 0.1
+ *
+ * @param {dataTypes.DataType|string} purpose DataType object
or DataValue type.
+ * @return {Function|null}
+ *
+ * @throws {Error} if no proper purpose is provided to retrieve
a formatter.
+ */
+ getFormatter: function( purpose ) {
+ var dataValueType,
+ dataTypeId,
+ formatter;
+
+ if( purpose instanceof dataTypes.DataType ) {
+ dataValueType = purpose.getDataValueType();
+ dataTypeId = purpose.getId();
+ } else if( typeof purpose === 'string' ) {
+ dataValueType = purpose;
+ } else {
+ throw new Error( 'No sufficient purpose
provided for choosing a formatter' );
+ }
+
+ if( dataTypeId ) {
+ formatter =
this._formattersForDataTypes[dataTypeId];
+ }
+
+ if( !formatter ) {
+ // No formatter for specified data type or only
DataValue provided.
+ formatter =
this._formattersForDataValueTypes[dataValueType];
+ }
+
+ return formatter || this._DefaultFormatter;
+ }
+ } );
+
+ /**
+ * @param {Function} Formatter
+ * @throws {Error} if the provided argument is not a
valueFormatters.ValueFormatter constructor.
+ */
+ function assertIsValueFormatterConstructor( Formatter ) {
+ if( !$.isFunction( Formatter ) && Formatter.prototype
instanceof vf.ValueFormatter ) {
+ throw new Error( 'Invalid ValueFormatter constructor' );
+ }
+ }
+
+}( jQuery, valueFormatters, dataTypes ) );
diff --git a/DataValuesCommon/js/src/ValueFormatters/mw.ext.valueFormatters.js
b/DataValuesCommon/js/src/ValueFormatters/mw.ext.valueFormatters.js
new file mode 100644
index 0000000..56851cb
--- /dev/null
+++ b/DataValuesCommon/js/src/ValueFormatters/mw.ext.valueFormatters.js
@@ -0,0 +1,40 @@
+/**
+ * Entrypoint for MediaWiki "ValueFormatters" extension JavaScript code.
+ *
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( mw, dv, vf, vv ) {
+ 'use strict';
+
+ mw.ext = mw.ext || {};
+
+ var valueFormatterProvider = new vf.ValueFormatterFactory(
vf.NullFormatter );
+
+ valueFormatterProvider.registerFormatter(
+ dv.StringValue.TYPE,
+ vf.StringFormatter
+ );
+
+ /**
+ * Object representing the MediaWiki "ValueFormatters" extension.
+ * @since 0.1
+ */
+ mw.ext.valueFormatters = new ( function MwExtValueFormatters() {
+ /**
+ * Value formatter provider containing all value formatters
available in global MediaWiki
+ * context.
+ * @since 0.1
+ *
+ * @type {valueFormatters.ValueFormatterFactory}
+ */
+ this.valueFormatterProvider = valueFormatterProvider;
+ } )();
+
+ // 'valueFormatterProvider' is a required option in the original
jQuery.valueview widget
+ // implementation. However, if valueFormatters is used in MediaWiki
context, then the option
+ // should not be required anymore and default to the
ValueFormatterFactory object set in
+ // mw.ext.valueFormatters.
+ vv.prototype.options.valueFormatterProvider = valueFormatterProvider;
+
+}( mediaWiki, dataValues, valueFormatters, jQuery.valueview ) );
diff --git
a/DataValuesCommon/js/tests/ValueFormatters/ValueFormatterFactory.tests.js
b/DataValuesCommon/js/tests/ValueFormatters/ValueFormatterFactory.tests.js
new file mode 100644
index 0000000..366c0c5
--- /dev/null
+++ b/DataValuesCommon/js/tests/ValueFormatters/ValueFormatterFactory.tests.js
@@ -0,0 +1,207 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, QUnit, dt, dv, vf ) {
+ 'use strict';
+
+ /**
+ * Returns a descriptive string to be used as id when registering a
ValueFormatter in a
+ * ValueFormatterFactory.
+ *
+ * @param {dataTypes.DataType|string} purpose
+ * @return {string}
+ */
+ function getTypeInfo( purpose ) {
+ if( purpose instanceof dt.DataType ) {
+ return 'DataType with data value type "' +
purpose.getDataValueType() + '"';
+ }
+ return 'constructor for DataValue of type "' + purpose + '"';
+ }
+
+ var StringValue = dv.StringValue,
+ UnknownValue = dv.UnknownValue,
+ stringType = new dt.DataType( 'somestringtype', StringValue ),
+ numberType = new dt.DataType( 'somenumbertype', dv.NumberValue
),
+ StringFormatter = vf.StringFormatter,
+ NullFormatter = vf.NullFormatter;
+
+ QUnit.module( 'valueFormatters.ValueFormatterFactory' );
+
+ QUnit.test( 'Constructor', function( assert ) {
+ var formatterFactory = new vf.ValueFormatterFactory();
+
+ assert.ok(
+ formatterFactory instanceof vf.ValueFormatterFactory,
+ 'Instantiated ValueFormatterFactory.'
+ );
+ } );
+
+ QUnit.test( 'Error handling', function( assert ) {
+ var formatterFactory = new vf.ValueFormatterFactory();
+
+ assert.throws(
+ function() {
+ formatterFactory.registerFormatter(
StringValue.TYPE, 'invalid' );
+ },
+ 'Failed trying to register an invalid formatter
constructor.'
+ );
+
+ assert.throws(
+ function() {
+ formatterFactory.register( 'invalid',
StringFormatter );
+ },
+ 'Failed trying to register a formatter with an invalid
purpose.'
+ );
+
+ formatterFactory.registerFormatter( StringValue.TYPE,
StringFormatter );
+
+ assert.throws(
+ function() {
+ formatterFactory.getFormatter( StringValue );
+ },
+ 'Failed trying to get a formatter with an invalid
purpose.'
+ );
+ } );
+
+ QUnit.test( 'Return default formatter on getFormatter()', function(
assert ) {
+ var formatterFactory = new vf.ValueFormatterFactory(
NullFormatter );
+
+ assert.equal(
+ formatterFactory.getFormatter( StringValue.TYPE ),
+ NullFormatter,
+ 'Returning default formatter if no formatter is
registered for a specific data value.'
+ );
+
+ formatterFactory.registerFormatter( StringValue.TYPE,
StringFormatter );
+
+ assert.equal(
+ formatterFactory.getFormatter( StringValue.TYPE ),
+ StringFormatter,
+ 'Returning specific formatter if a formatter is
registered for a specific data value.'
+ );
+
+ assert.equal(
+ formatterFactory.getFormatter( UnknownValue.TYPE ),
+ NullFormatter,
+ 'Still returning default formatter if no formatter is
registered for a specific data '
+ + 'value.'
+ );
+ } );
+
+ // Tests regarding registration of formatters:
+
+ /**
+ * Array of test definitions used as provider for
"valueFormatterFactoryRegistrationTest".
+ * @type {Object[]}
+ */
+ var valueFormatterFactoryRegistrationTestCases = [
+ {
+ title: 'Empty ValueFormatterFactory',
+ register: [],
+ expect: [
+ [ StringValue.TYPE, null ],
+ [ stringType, null ]
+ ]
+ },
+ {
+ title: 'Factory with formatter for string DataValue
which is also suitable for string '
+ + 'DataType',
+ register: [
+ [ StringValue.TYPE, StringFormatter ]
+ ],
+ expect: [
+ [ StringValue.TYPE, StringFormatter ],
+ [ stringType, StringFormatter ], // data type
uses value type
+ [ UnknownValue.TYPE, null ],
+ [ numberType, null ]
+ ]
+ },
+ {
+ title: 'Factory for string DataType. String DataValue
can\'t use this potentially more '
+ + 'specialized formatter',
+ register: [
+ [ stringType, StringFormatter ]
+ ],
+ expect: [
+ [ StringValue.TYPE, null ],
+ [ stringType, StringFormatter ]
+ ]
+ },
+ {
+ title: 'Factory with two formatters: For DataValue and
for DataType using that '
+ + 'DataValue type',
+ register: [
+ [ StringValue.TYPE, StringFormatter ],
+ [ stringType, StringFormatter ]
+ ],
+ expect: [
+ [ StringValue.TYPE, StringFormatter ],
+ [ stringType, StringFormatter ],
+ [ UnknownValue.TYPE, null ]
+ ]
+ },
+ {
+ title: 'Factory with two formatters for two different
DataValue types',
+ register: [
+ [ StringValue.TYPE, StringFormatter ],
+ [ UnknownValue.TYPE, NullFormatter ]
+ ],
+ expect: [
+ [ StringValue.TYPE, StringFormatter ],
+ [ UnknownValue.TYPE, NullFormatter ],
+ [ numberType, null ]
+ ]
+ }
+ ];
+
+ /**
+ * Test for registration of ValueFormatters to ValueFormatterFactory
and expected conditions
+ * afterwards.
+ *
+ * @param {QUnit.assert} assert
+ * @param {Array[]} toRegister Array containing arrays where each one
tells a
+ * ValueFormatterFactory what formatters to register. The inner
array has to consist out
+ * of two objects as ValueFormatterFactory.registerFormatter
would take them.
+ * @param {Array[]} toExpect Array containing arrays where each one
states one expected
+ * condition of the ValueFormatterFactory after registration of
what is given in the
+ * first parameter. Each inner array should contain a data type,
data value or data value
+ * constructor and a ValueFormatter which is expected to be
registered for it.
+ */
+ function valueFormatterFactoryRegistrationTest( assert, toRegister,
toExpect ) {
+ var formatterFactory = new vf.ValueFormatterFactory();
+
+ // Register ValueFormatters as per definition:
+ $.each( toRegister, function( i, registerPair ) {
+ var purpose = registerPair[0],
+ formatter = registerPair[1];
+
+ formatterFactory.registerFormatter( purpose, formatter
);
+
+ assert.ok(
+ true,
+ 'Registered formatter for ' + getTypeInfo(
purpose )
+ );
+ } );
+
+ // Check for expected conditions:
+ $.each( toExpect, function( i, expectPair ) {
+ var purpose = expectPair[0],
+ formatter = expectPair[1];
+
+ assert.strictEqual(
+ formatterFactory.getFormatter( purpose ),
+ formatter,
+ 'Requesting formatter for ' + getTypeInfo(
purpose ) +
+ ( formatter !== null ? ' returns
expected formatter' : ' returns null' )
+ );
+ } );
+ }
+
+ QUnit
+ .cases( valueFormatterFactoryRegistrationTestCases )
+ .test( 'registerFormatter() & getFormatter() ', function(
params, assert ) {
+ valueFormatterFactoryRegistrationTest( assert,
params.register, params.expect );
+ } );
+
+}( jQuery, QUnit, dataTypes, dataValues, valueFormatters ) );
--
To view, visit https://gerrit.wikimedia.org/r/102089
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I49ead230e2d1cf64ffd4e7f347201325e38a3e07
Gerrit-PatchSet: 2
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Daniel Werner <[email protected]>
Gerrit-Reviewer: Jeroen De Dauw <[email protected]>
Gerrit-Reviewer: Tobias Gritschacher <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits