diff --git a/README.md b/README.md index 46ee359..98c40e7 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,37 @@ m(12).inches; m(12, inch); ``` +##### Implicit conversion + +Measurements created with `m` can also be implicitly converted. + +###### `m(number, unit).as(unit)` + +```js +const foot = createUnit('foot', { + convert: { + from: [inch, conversion.divideBy(12)], + }, +}); +const fourFeet = m`48 inches`.as(foot); +fourFeet.value === 4; +fourFeet.unit === foot; +``` + +###### `m(number, unit).in.` + +```js +createUnit('foot', { + alias: 'feet', + convert: { + from: [inch, conversion.divideBy(12)], + }, +}); +const fourFeet = m`48 inches`.in.feet; +fourFeet.value === 4; +fourFeet.unit === foot; +``` + #### `convert(measurement, unit)` Given a `Measurement` and a `Unit`, `convert(measurement, unit)` will attempt to convert that measurement to the given unit. (see `UnitSystem#convert()` for details) diff --git a/src/UnitSystem/UnitSystem.js b/src/UnitSystem/UnitSystem.js index fdb0b02..0316b62 100644 --- a/src/UnitSystem/UnitSystem.js +++ b/src/UnitSystem/UnitSystem.js @@ -1,4 +1,5 @@ const { add, subtract, multiply, divide } = require('../math'); +const createAliasedMeasurementProxy = require('../createAliasedMeasurementProxy'); const Measurement = require('../Measurement'); const Unit = require('../Unit'); const Aliases = require('./Aliases'); @@ -10,6 +11,18 @@ class UnitSystem { this._aliases = new Aliases(); this._converters = new Converters(); + const system = this; + this.SystemMeasurement = class SystemMeasurement extends Measurement { + get in() { + return createAliasedMeasurementProxy(system, unit => + system.convert(this, unit) + ); + } + as(unit) { + return system.convert(this, unit); + } + }; + this.registerAll(units); } @@ -102,7 +115,8 @@ class UnitSystem { }` ); } - return new Measurement(convert(measurement.value), endUnit); + const { SystemMeasurement } = this; + return new SystemMeasurement(convert(measurement.value), endUnit); } _normalizeUnits(measurements) { diff --git a/src/createAliasedMeasurementProxy.js b/src/createAliasedMeasurementProxy.js new file mode 100644 index 0000000..3279b5b --- /dev/null +++ b/src/createAliasedMeasurementProxy.js @@ -0,0 +1,21 @@ +// Returns a proxy object. Any alias in the given unit system +// is a valid property, and getting one of those properties +// will return the result of calling `getValueForUnit` with +// the corresponding unit for that alias. +// +// For example: +// const obj = createAliasedMeasurementProxy( +// system, +// unit => `${unit.name.toUpperCase()}!!` +// ); +// obj.ft // -> "FOOT!!", if "ft" was registered as an alias +// for `new Unit("foot")` +function createAliasedMeasurementProxy(system, getValueForUnit) { + return new Proxy(Object.create(null), { + get(target, alias) { + const unit = system.getUnitForAlias(alias); + return getValueForUnit(unit); + }, + }); +} +module.exports = createAliasedMeasurementProxy; diff --git a/src/createMeasurement.js b/src/createMeasurement.js index 94205c2..b0b1053 100644 --- a/src/createMeasurement.js +++ b/src/createMeasurement.js @@ -1,15 +1,8 @@ +const createAliasedMeasurementProxy = require('./createAliasedMeasurementProxy'); const Unit = require('./Unit'); -const Measurement = require('./Measurement'); function createMeasurement(system) { - function createAliasLookupObject(value) { - return new Proxy(Object.create(null), { - get(target, alias) { - const unit = system.getUnitForAlias(alias); - return new Measurement(value, unit); - }, - }); - } + const { SystemMeasurement } = system; return function m(value, unit) { if (Array.isArray(value)) { @@ -20,9 +13,12 @@ function createMeasurement(system) { } if (!(unit instanceof Unit)) { - return createAliasLookupObject(value); + return createAliasedMeasurementProxy( + system, + unit => new SystemMeasurement(value, unit) + ); } - return new Measurement(value, unit); + return new SystemMeasurement(value, unit); }; } diff --git a/src/createMeasurement.test.js b/src/createMeasurement.test.js index 056fdc5..3f40c6f 100644 --- a/src/createMeasurement.test.js +++ b/src/createMeasurement.test.js @@ -1,6 +1,7 @@ const Unit = require('./Unit'); const Measurement = require('./Measurement'); const UnitSystem = require('./UnitSystem'); +const { divideBy } = require('./conversion'); const createMeasurement = require('./createMeasurement'); describe(createMeasurement, () => { @@ -35,4 +36,35 @@ describe(createMeasurement, () => { it('can be called as a tagged template with an inline unit', () => { expect(m`12 ${inch}`).toEqual(new Measurement(12, inch)); }); + + it('can convert a measurement to a different unit using an alias', () => { + system.register(inch, { alias: 'inches' }); + const foot = new Unit('foot'); + system.register(foot, { + alias: 'feet', + convert: { from: [inch, divideBy(12)] }, + }); + expect(m`48 inches`.in.feet).toEqual(new Measurement(4, foot)); + }); + + it('can convert a measurement by passing in the desired unit', () => { + system.register(inch, { alias: 'inches' }); + const foot = new Unit('foot'); + system.register(foot, { + convert: { from: [inch, divideBy(12)] }, + }); + expect(m`60 inches`.as(foot)).toEqual(new Measurement(5, foot)); + }); + + it('can repeatedly implicitly convert', () => { + system.register(inch, { alias: 'inches' }); + const foot = new Unit('foot'); + system.register(foot, { + alias: 'feet', + convert: { from: [inch, divideBy(12)] }, + }); + expect(m`60 inches`.as(foot).in.inches.in.feet).toEqual( + new Measurement(5, foot) + ); + }); });