Skip to content

Commit 2527405

Browse files
committed
feat: 🎸 promote m from createUnitSystem to UnitSystem.measure
1 parent ab79c2f commit 2527405

File tree

6 files changed

+172
-172
lines changed

6 files changed

+172
-172
lines changed

README.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,10 @@ const foot = createUnit('foot', {
5959

6060
#### `m(number, unit)`
6161

62-
Shorthand function for creating new `Measurement` instances. If a unit is defined with an alias, that alias can be used instead of passing in the unit.
62+
Shorthand function for creating new `Measurement` instances. If a unit is defined with an alias, that alias can be used instead of passing in the unit. (see `UnitSystem#measure()` for details)
6363

6464
```js
65-
// All of are equivalent to `new Measurement(12, inch)`
6665
m`12 inches`;
67-
m`12inches`;
68-
m`12 ${inch}`;
69-
m`12`.inches;
70-
m(12).inches;
71-
m(12, inch);
7266
```
7367

7468
#### `convert(measurement, unit)`
@@ -416,3 +410,24 @@ system.equal(m`12 inches`, m`1 foot`) === true;
416410
#### `.greaterThanOrEqual(measurement, measurement)`
417411

418412
`greaterThanOrEqual` will return `true` if the first measurement is greater than or equal to the second (after conversion). Otherwise it returns `false`.
413+
414+
#### `.measure(value, unit)`
415+
416+
Shorthand function for creating new `Measurement` instances. If a unit is defined with an alias, that alias can be used instead of passing in the unit.
417+
418+
```js
419+
// All of these are equivalent to `new Measurement(12, inch)`
420+
system.measure`12 inches`;
421+
system.measure`12inches`;
422+
system.measure`12 ${inch}`;
423+
system.measure`12`.inches;
424+
system.measure(12).inches;
425+
system.measure(12, inch);
426+
```
427+
428+
`measure` is bound to `system` (its `this` cannot be changed), so it can be "detached" and used as a bare function (but still retain access to your unit system):
429+
430+
```js
431+
const m = system.measure;
432+
m`12 inches`;
433+
```

src/UnitSystem/UnitSystem.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class UnitSystem {
1818
this._converters = new Converters();
1919

2020
this.registerAll(units);
21+
22+
this.measure = this.measure.bind(this);
2123
}
2224

2325
merge(...otherSystems) {
@@ -153,6 +155,47 @@ class UnitSystem {
153155
greaterThanOrEqual(...values) {
154156
return greaterThanOrEqual(...this._normalizeUnits(values));
155157
}
158+
159+
_parse(str, unit = undefined) {
160+
const LOOSE_VALUE_AND_OPTIONAL_ALIAS_RE = /^(-?[\d\.\,]+)\s*(.+)?$/;
161+
const STRICT_VALUE_RE = /^-?((\d{1,3}(,\d{3})+)|(\d+))?(\.\d+)?$/;
162+
163+
const result = str.match(LOOSE_VALUE_AND_OPTIONAL_ALIAS_RE);
164+
165+
let value, alias;
166+
[, value, alias] = result || [];
167+
168+
if (result === null || !STRICT_VALUE_RE.test(value)) {
169+
throw Error(
170+
`Tried to parse "${str}" but it doesn't appear to be a valid number`
171+
);
172+
}
173+
174+
value = value.replace(/,/g, '');
175+
176+
return [Number(value), unit || this.getUnitForAlias(alias)];
177+
}
178+
179+
measure(value, unit) {
180+
if (Array.isArray(value)) {
181+
return this.measure(...this._parse(value[0], unit));
182+
}
183+
184+
if (unit instanceof Unit) {
185+
return new Measurement(value, unit);
186+
}
187+
188+
const system = this;
189+
return new Proxy(Object.create(null), {
190+
get(target, property) {
191+
const unit = system.getUnitForAlias(property);
192+
if (!unit) {
193+
throw new Error(`Could not find a unit for the alias "${property}"`);
194+
}
195+
return system.measure(value, unit);
196+
},
197+
});
198+
}
156199
}
157200

158201
module.exports = UnitSystem;

src/UnitSystem/UnitSystem.test.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,4 +529,110 @@ describe(UnitSystem, () => {
529529
}
530530
);
531531
});
532+
533+
describe('#measure', () => {
534+
let system;
535+
const inch = new Unit('inch');
536+
537+
beforeEach(() => {
538+
system = new UnitSystem();
539+
});
540+
541+
it('creates a Measurement', () => {
542+
expect(system.measure(12, inch)).toEqual(new Measurement(12, inch));
543+
});
544+
545+
it('can look up a unit by its alias', () => {
546+
system.register(inch, { alias: 'inches' });
547+
expect(system.measure(12).inches).toEqual(new Measurement(12, inch));
548+
});
549+
550+
it('can be called as a tagged template', () => {
551+
system.register(inch, { alias: 'inches' });
552+
expect(system.measure`12 inches`).toEqual(new Measurement(12, inch));
553+
});
554+
555+
it('can be called as a tagged template (negative value)', () => {
556+
system.register(inch, { alias: 'inches' });
557+
expect(system.measure`-12 inches`).toEqual(new Measurement(-12, inch));
558+
});
559+
560+
it('can be called as a tagged template (no space)', () => {
561+
system.register(inch, { alias: 'in' });
562+
expect(system.measure`12in`).toEqual(new Measurement(12, inch));
563+
});
564+
565+
it('can be called as a tagged template (commas and decimal)', () => {
566+
system.register(inch, { alias: 'inches' });
567+
expect(system.measure`1,234.567 inches`).toEqual(
568+
new Measurement(1234.567, inch)
569+
);
570+
});
571+
572+
it('can be called as a tagged template (everything)', () => {
573+
system.register(inch, { alias: 'in' });
574+
expect(system.measure`-1,234,567.890in`).toEqual(
575+
new Measurement(-1234567.89, inch)
576+
);
577+
});
578+
579+
it.each([
580+
'1',
581+
'12',
582+
'123',
583+
'-123.4',
584+
'123.45',
585+
'1,234',
586+
'12,345',
587+
'123,456',
588+
'1,234,567',
589+
'1,234.567',
590+
'12,345.67',
591+
'1,234,567,890',
592+
'.1',
593+
])('can parse "%s', str => {
594+
system.register(inch, { alias: 'in' });
595+
expect(system.measure([`${str}in`])).toEqual(
596+
new Measurement(Number(str.replace(/,/g, '')), inch)
597+
);
598+
});
599+
600+
it.each([
601+
'1,2',
602+
',123',
603+
'123.',
604+
'12,34',
605+
'1234,567',
606+
'123,456,7',
607+
'1234.567,890',
608+
'1,234,567890',
609+
undefined,
610+
null,
611+
{},
612+
'',
613+
])('throws an error when attempting to parse "%s"', str => {
614+
system.register(inch, { alias: 'in' });
615+
expect(() => system.measure([`${str}in`])).toThrowError(
616+
new Error(
617+
`Tried to parse "${str}in" but it doesn't appear to be a valid number`
618+
)
619+
);
620+
});
621+
622+
it('can be called as a tagged template with an alias property', () => {
623+
system.register(inch, { alias: 'inches' });
624+
expect(system.measure`12`.inches).toEqual(new Measurement(12, inch));
625+
});
626+
627+
it('throws an error if called as a tagged template with an alias property but alias does not exist', () => {
628+
system.register(inch, { alias: 'inches' });
629+
expect(() => system.measure`12`.anchovies).toThrowError(
630+
new Error('Could not find a unit for the alias "anchovies"')
631+
);
632+
});
633+
634+
it('can be called as a tagged template with an inline unit', () => {
635+
expect(system.measure`12 ${inch}`).toEqual(new Measurement(12, inch));
636+
});
637+
});
532638
});

src/createMeasurement.js

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/createMeasurement.test.js

Lines changed: 0 additions & 108 deletions
This file was deleted.

src/createUnitSystem.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const Unit = require('./Unit');
22
const UnitSystem = require('./UnitSystem');
3-
const createMeasurement = require('./createMeasurement');
43

54
function bound(obj, method) {
65
return obj[method].bind(obj);
@@ -28,7 +27,7 @@ function createUnitSystem(units) {
2827
const greaterThan = bound(system, 'greaterThan');
2928
const greaterThanOrEqual = bound(system, 'greaterThanOrEqual');
3029

31-
const m = createMeasurement(system);
30+
const m = system.measure;
3231

3332
return {
3433
m,

0 commit comments

Comments
 (0)