1
0
Fork 0

Replace Currency formatter with StringFormat

This is a much more general solution.

* src/validate/formatter/Currency.js: Remove trait.
* test/validate/formatter/CurrencyTest.js: Remove test case.

* src/validate/formatter/StringFormat.js: Add trait.
* test/validate/formatter/StringFormatTest.js: Add test case.
master
Mike Gerwitz 2016-12-01 08:44:20 -05:00
parent ab3f5f4cb6
commit 5607bf1927
4 changed files with 293 additions and 135 deletions

View File

@ -1,75 +0,0 @@
/**
* Currency formatter
*
* Copyright (C) 2016 LoVullo Associates, Inc.
*
* This file is part of liza.
*
* liza is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var Trait = require( 'easejs' ).Trait,
ValidatorFormatter = require( '../ValidatorFormatter' );
/**
* Formats amount as currency
*
* This does not guarantee that the value is a number; this should be
* mixed in atop of a formatter that does guarantee such.
*/
module.exports = Trait( 'Currency' )
.implement( ValidatorFormatter )
.extend(
{
/**
* Parse item as currency
*
* @param {string} data data to parse
*
* @return {string} data formatted for storage
*/
'virtual abstract override public parse': function( data )
{
return this.__super( data ).replace( /^\$*/g, '' );
},
/**
* Format amount as a currency
*
* @param {string} data data to format for display
*
* @return {string} data formatted for display
*/
'virtual abstract override public retrieve': function( data )
{
return this.styleCurrency( this.__super( data ) );
},
/**
* Style amount with currency symbol
*
* @param {string} amount amount to style
*
* @return {string} formatted number
*/
'virtual protected styleCurrency': function( amount )
{
return ( ''+amount === '' )
? amount
: '$' + amount;
},
} );

View File

@ -0,0 +1,178 @@
/**
* @license
* StringFormat formatter
*
* Copyright (C) 2016 LoVullo Associates, Inc.
*
* This file is part of liza.
*
* liza is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @module validate/formatter
*/
var Trait = require( 'easejs' ).Trait,
ValidatorFormatter = require( '../ValidatorFormatter' );
/**
* Basic data formatting
*
* `StringFormat` effectively allows for the definition of prefixes
* and suffixes by using a format string of the form `'pre %s post'`.
*
* For example, the format string `'$%sUSD'` applied to the
* input `'$25.00USD'` would yield `'25.00'` after parsing and
* re-yield the original string when retrieved.
*
* If `StringFormat` is mixed in with other formatters that
* modify the input, then the former will be applied after earlier
* formatters process the data. For example:
*
* @example
* let fmt = EchoFormatter
* .use( Number( 2 ) )
* .use( StringFormatter( '$%sUSD' ) )();
*
* fmt.parse( '$25USD' ); // => 25.00
* fmt.retrieve( '25' ); // => $25.00USD
*
* The simple, restricted format string in place of a regular expression
* allows for a declarative stacking of formatters without concern as
* to whether the format was written correctly. Regular expressions,
* however, might well need associated tests for confidence in the
* implementation, which complicates the design. Simply: it's easy to
* reason about.
*/
module.exports = Trait( 'StringFormat' )
.implement( ValidatorFormatter )
.extend(
{
/**
* Prefix string
*
* @type {string}
*/
'private _pre': '',
/**
* Postfix string
*
* @type {string}
*/
'private _post': '',
/**
* Define format string
*
* The format string must have a single `'%s'` denoting the
* placement of the data.
*
* @param {string} format format string with single `'%s'`
*/
__mixin: function( format )
{
var parts = this.parseFormat( ''+format );
this._pre = parts.pre;
this._post = parts.post;
},
/**
* Extract prefix and suffix from format string FORMAT
*
* Everything before the `'%s'` is the prefix, and everything
* after is the suffix.
*
* @param {string} format format string with single `'%s'`
*
* @return {Object.<pre,post>} prefix and suffix of FORMAT
*
* @throws {Error} if FORMAT does not have exactly one `'%s'`
*/
'virtual protected parseFormat': function( format )
{
var parts = format.split( '%s' );
if ( parts.length !== 2 )
{
throw Error(
"Format string must have a single '%s': " + format
);
}
return {
pre: parts[ 0 ],
post: parts[ 1 ]
};
},
/**
* Remove prefix and suffix from data
*
* @param {string} data data to parse
*
* @return {string} data formatted for storage
*/
'virtual abstract override public parse': function( data )
{
return this.__super( this._stripPrePost( data ) );
},
/**
* Recursively strip prefixes and suffixes from STR
*
* This simply allows us to avoid having to use regexes and,
* consequently, worry about escaping format strings.
*
* @param {string} str string to strip
*
* @return {string} stripped string
*/
'private _stripPrePost': function( str )
{
if ( this._pre && ( str.substr( 0, this._pre.length ) === this._pre ) )
{
return this._stripPrePost(
str.substr( this._pre.length )
);
}
if ( this._post && ( str.substr( -this._post.length ) === this._post ) )
{
return this._stripPrePost(
str.substr( 0, str.length - this._post.length )
);
}
return str;
},
/**
* Format data by adding prefix and suffix
*
* @param {string} data data to format for display
*
* @return {string} data formatted for display
*/
'virtual abstract override public retrieve': function( data )
{
return this._pre + this.__super( data ) + this._post;
},
} );

View File

@ -1,60 +0,0 @@
/**
* Currency formatter test
*
* Copyright (C) 2016 LoVullo Associates, Inc.
*
* This file is part of liza.
*
* liza is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var liza = require( '../../../' ),
Sut = liza.validate.formatter.Currency,
EchoFormatter = liza.validate.formatter.EchoFormatter,
common = require( './common' );
describe( 'validate.formatter.Currency', function()
{
common.testValidate( EchoFormatter.use( Sut )(), {
// should format anything given to it, with or without prefix
"1": [ "1", "$1" ],
"foo": [ "foo", "$foo" ],
"+": [ "+", "$+" ],
"$foo": [ "foo", "$foo" ],
// empty shouldn't format as anything
"": [ "", "" ],
"$": [ "", "" ],
"$$": [ "", "" ],
// make sure these aren't considered to be empty
"0": [ "0", "$0" ],
"$0": [ "0", "$0" ],
// be lax on input
"$$foo": [ "foo", "$foo" ],
"$$$$$$$12.34": [ "12.34", "$12.34" ],
} );
common.testMixin(
EchoFormatter,
Sut,
'foo',
'123',
'foo123',
'$foo123'
);
} );

View File

@ -0,0 +1,115 @@
/**
* @license
* StringFormat formatter test
*
* Copyright (C) 2016 LoVullo Associates, Inc.
*
* This file is part of liza.
*
* liza is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var liza = require( '../../../' ),
expect = require( 'chai' ).expect,
Sut = liza.validate.formatter.StringFormat,
EchoFormatter = liza.validate.formatter.EchoFormatter,
common = require( './common' );
describe( 'validate.formatter.StringFormat', function()
{
common.testValidate( EchoFormatter.use( Sut( 'PRE%sPOST' ) )(), {
// basic prefix/suffix
"": [ "", "PREPOST" ],
"foo": [ "foo", "PREfooPOST" ],
"PREfoo": [ "foo", "PREfooPOST" ],
"barPOST": [ "bar", "PREbarPOST" ],
"PREbazPOST": [ "baz", "PREbazPOST" ],
// only prefix/suffix
"PRE": [ "", "PREPOST" ],
"POST": [ "", "PREPOST" ],
"PREPOST": [ "", "PREPOST" ],
// repeated prefix/suffix normalization
"PREPREfoo": [ "foo", "PREfooPOST" ],
"barPOSTPOST": [ "bar", "PREbarPOST" ],
"PREPREbazPOSTPOSTPOST": [ "baz", "PREbazPOST" ],
"PREPREPOSTPOST": [ "", "PREPOST" ],
// convoluted interpretations
"PREfooPOSTPRE": [ "fooPOSTPRE", "PREfooPOSTPREPOST" ],
"PREmooPREfooPOST": [ "mooPREfoo", "PREmooPREfooPOST" ],
"mooPREfoo": [ "mooPREfoo", "PREmooPREfooPOST" ],
} );
// only prefix format
common.testValidate( EchoFormatter.use( Sut( 'BEG%s' ) )(), {
"foo": [ "foo", "BEGfoo" ],
"BEGfoo": [ "foo", "BEGfoo" ],
} );
// only suffix format
common.testValidate( EchoFormatter.use( Sut( '%sEND' ) )(), {
"fooEND": [ "foo", "fooEND" ],
"fooEND": [ "foo", "fooEND" ],
} );
// no prefix or suffix
common.testValidate( EchoFormatter.use( Sut( '%s' ) )(), {
"foo": [ "foo", "foo" ],
} );
describe( 'given multiple %s', function()
{
it( 'throws an error', function()
{
expect( function()
{
EchoFormatter.use( Sut( 'foo%sbar%sbaz' ) )();
} ).to.throw( Error );
} );
} );
describe( 'given no %s', function()
{
it( 'throws an error', function()
{
expect( function()
{
EchoFormatter.use( Sut( '' ) )();
} ).to.throw( Error );
expect( function()
{
EchoFormatter.use( Sut( 'Foo' ) )();
} ).to.throw( Error );
} );
} );
common.testMixin(
EchoFormatter,
Sut( 'PRE%sPOST' ),
'base',
'PREfooPOST',
'basefoo',
'PREbasePREfooPOSTPOST'
);
} );