Skip to content

Commit

Permalink
Throw fewer runtime errors for #, add Runtime#setStrictNumber
Browse files Browse the repository at this point in the history
  • Loading branch information
eemeli committed Aug 1, 2016
1 parent 31d4164 commit 1fa9b93
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 5 deletions.
4 changes: 3 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,8 @@ MessageFormat.prototype.setIntlSupport = function(enable) {
* replace `#` signs with the value of the nearest surrounding `plural` or
* `selectordinal` statement.
*
* Set this to true to follow the stricter ICU MessageFormat spec.
* Set this to true to follow the stricter ICU MessageFormat spec, and to
* throw a runtime error if `#` is used with non-numeric input.
*
* @memberof MessageFormat
* @param {boolean} [enable=true]
Expand All @@ -350,6 +351,7 @@ MessageFormat.prototype.setIntlSupport = function(enable) {
*/
MessageFormat.prototype.setStrictNumberSign = function(enable) {
this.strictNumberSign = !!enable || (typeof enable == 'undefined');
this.runtime.setStrictNumber(this.strictNumberSign);
return this;
};

Expand Down
30 changes: 29 additions & 1 deletion lib/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var Compiler = require('./compiler');
*/
function Runtime(mf) {
this.mf = mf;
this.setStrictNumber(mf.strictNumberSign);
}

module.exports = Runtime;
Expand All @@ -20,17 +21,44 @@ module.exports = Runtime;
* Will throw an Error if `value` has a non-numeric value and `offset` is
* non-zero or {@link MessageFormat#setStrictNumberSign} is set.
*
* @function Runtime#number
* @param {number} value - The value to operate on
* @param {string} name - The name of the argument, used for error reporting
* @param {number} [offset=0] - An optional offset, set by the surrounding context
* @returns {number|string} The result of applying the offset to the input value
*/
Runtime.prototype.number = function(value, name, offset) {
function defaultNumber(value, name, offset) {
if (!offset) return value;
if (isNaN(value)) throw new Error('Can\'t apply offset:' + offset + ' to argument `' + name +
'` with non-numerical value ' + JSON.stringify(value) + '.');
return value - offset;
}


/** @private */
function strictNumber(value, name, offset) {
if (isNaN(value)) throw new Error('Argument `' + name + '` has non-numerical value ' + JSON.stringify(value) + '.');
return value - (offset || 0);
}


/** Set how strictly the {@link number} method parses its input.
*
* According to the ICU MessageFormat spec, `#` can only be used to replace a
* number input of a `plural` statement. By default, messageformat.js does not
* throw a runtime error if you use non-numeric argument with a `plural` rule,
* unless rule also includes a non-zero `offset`.
*
* This is called by {@link MessageFormat#setStrictNumberSign} to follow the
* stricter ICU MessageFormat spec.
*
* @param {boolean} [enable=false]
*/
Runtime.prototype.setStrictNumber = function(enable) {
this.number = enable ? strictNumber : defaultNumber;
}


/** Utility function for `{N, plural|selectordinal, ...}`
*
* @param {number} value - The key to use to find a pluralization rule
Expand Down
8 changes: 5 additions & 3 deletions test/messageformat.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,12 @@ describe("Basic Message Formatting", function() {

it("should have configurable # parsing support", function() {
var mf = new MessageFormat('en');
var msg = '{X, plural, other{{Y, select, other{#}}}}';
var msg = '{X, plural, one{#} other{{Y, select, other{#}}}}';
expect(mf.compile(msg)({ X: 3, Y: 5 })).to.eql('3');
expect(mf.compile(msg)({ X: 'x' })).to.eql('x');
mf.setStrictNumberSign(true);
expect(mf.compile(msg)({ X: 3, Y: 5 })).to.eql('#');
expect(function() { mf.compile(msg)({ X: 'x' }); }).to.throwError(/\bX\b.*non-numerical value/);
});

it("obeys plural functions", function() {
Expand Down Expand Up @@ -290,9 +292,9 @@ describe("Basic Message Formatting", function() {
"=0{no enemies} =1{one nemesis} one{two enemies} other{one nemesis and # enemies}}}}."
);
expect(mfunc({FRIENDS:0, ENEMIES: 0})).to.eql("I have 0 friends but no enemies.");
expect(function(){ var x = mfunc({}); }).to.throwError(/\bFRIENDS\b.*non-numerical value/);
expect(function(){ var x = mfunc({}); }).to.throwError(/\bENEMIES\b.*non-numerical value/);
expect(function(){ var x = mfunc({FRIENDS:0}); }).to.throwError(/\bENEMIES\b.*non-numerical value/);
expect(function(){ var x = mfunc({ENEMIES:0}); }).to.throwError(/\bFRIENDS\b.*non-numerical value/);
expect(mfunc({ENEMIES:1})).to.eql('I have undefined friends but one nemesis.');
});

it("should not expose prototype members - selects", function() {
Expand Down

0 comments on commit 1fa9b93

Please sign in to comment.