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

Reply via email to