Skip to content

Commit fc29ecd

Browse files
committed
Improve event handling.
- Setup eventHandler defaults once at top level. - Use simple existence check to optimize if handlers need to be called. - Better naming for event capture handler.
1 parent ebbe311 commit fc29ecd

File tree

5 files changed

+135
-106
lines changed

5 files changed

+135
-106
lines changed

lib/context.js

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,20 @@ api.process = async ({
6767

6868
// event handler for capturing events to replay when using a cached context
6969
const events = [];
70-
const eventHandler = [
70+
const eventCaptureHandler = [
7171
({event, next}) => {
7272
events.push(event);
7373
next();
7474
}
7575
];
7676
// chain to original handler
7777
if(options.eventHandler) {
78-
eventHandler.push(options.eventHandler);
78+
eventCaptureHandler.push(options.eventHandler);
7979
}
8080
// store original options to use when replaying events
8181
const originalOptions = options;
82-
// shallow clone options with custom event handler
83-
options = {...options, eventHandler};
82+
// shallow clone options with event capture handler
83+
options = {...options, eventHandler: eventCaptureHandler};
8484

8585
// resolve contexts
8686
const resolved = await options.contextResolver.resolve({
@@ -133,9 +133,11 @@ api.process = async ({
133133
// get processed context from cache if available
134134
const processed = resolvedContext.getProcessed(activeCtx);
135135
if(processed) {
136-
// replay events with original non-capturing options
137-
for(const event of processed.events) {
138-
_handleEvent({event, options: originalOptions});
136+
if(originalOptions.eventHandler) {
137+
// replay events with original non-capturing options
138+
for(const event of processed.events) {
139+
_handleEvent({event, options: originalOptions});
140+
}
139141
}
140142

141143
rval = activeCtx = processed.context;
@@ -474,18 +476,20 @@ api.createTermDefinition = ({
474476
'jsonld.SyntaxError',
475477
{code: 'keyword redefinition', context: localCtx, term});
476478
} else if(term.match(KEYWORD_PATTERN)) {
477-
_handleEvent({
478-
event: {
479-
code: 'invalid reserved term',
480-
level: 'warning',
481-
message:
482-
'Terms beginning with "@" are reserved for future use and ignored.',
483-
details: {
484-
term
485-
}
486-
},
487-
options
488-
});
479+
if(options.eventHandler) {
480+
_handleEvent({
481+
event: {
482+
code: 'invalid reserved term',
483+
level: 'warning',
484+
message:
485+
'Terms beginning with "@" are reserved for future use and ignored.',
486+
details: {
487+
term
488+
}
489+
},
490+
options
491+
});
492+
}
489493
return;
490494
} else if(term === '') {
491495
throw new JsonLdError(
@@ -566,19 +570,21 @@ api.createTermDefinition = ({
566570
}
567571

568572
if(reverse.match(KEYWORD_PATTERN)) {
569-
_handleEvent({
570-
event: {
571-
code: 'invalid reserved value',
572-
level: 'warning',
573-
message:
574-
'Values beginning with "@" are reserved for future use and' +
575-
' ignored.',
576-
details: {
577-
reverse
578-
}
579-
},
580-
options
581-
});
573+
if(options.eventHandler) {
574+
_handleEvent({
575+
event: {
576+
code: 'invalid reserved value',
577+
level: 'warning',
578+
message:
579+
'Values beginning with "@" are reserved for future use and' +
580+
' ignored.',
581+
details: {
582+
reverse
583+
}
584+
},
585+
options
586+
});
587+
}
582588
if(previousMapping) {
583589
activeCtx.mappings.set(term, previousMapping);
584590
} else {
@@ -612,19 +618,21 @@ api.createTermDefinition = ({
612618
// reserve a null term, which may be protected
613619
mapping['@id'] = null;
614620
} else if(!api.isKeyword(id) && id.match(KEYWORD_PATTERN)) {
615-
_handleEvent({
616-
event: {
617-
code: 'invalid reserved value',
618-
level: 'warning',
619-
message:
620-
'Values beginning with "@" are reserved for future use and' +
621-
' ignored.',
622-
details: {
623-
id
624-
}
625-
},
626-
options
627-
});
621+
if(options.eventHandler) {
622+
_handleEvent({
623+
event: {
624+
code: 'invalid reserved value',
625+
level: 'warning',
626+
message:
627+
'Values beginning with "@" are reserved for future use and' +
628+
' ignored.',
629+
details: {
630+
id
631+
}
632+
},
633+
options
634+
});
635+
}
628636
if(previousMapping) {
629637
activeCtx.mappings.set(term, previousMapping);
630638
} else {
@@ -1144,17 +1152,19 @@ function _expandIri(activeCtx, value, relativeTo, localCtx, defined, options) {
11441152
if(expandedResult !== undefined) {
11451153
value = expandedResult;
11461154
} else {
1147-
_handleEvent({
1148-
event: {
1149-
code: 'relative IRI after expansion',
1150-
level: 'warning',
1151-
message: 'Expansion resulted in a relative IRI.',
1152-
details: {
1153-
value
1154-
}
1155-
},
1156-
options
1157-
});
1155+
if(options.eventHandler) {
1156+
_handleEvent({
1157+
event: {
1158+
code: 'relative IRI after expansion',
1159+
level: 'warning',
1160+
message: 'Expansion resulted in a relative IRI.',
1161+
details: {
1162+
value
1163+
}
1164+
},
1165+
options
1166+
});
1167+
}
11581168
}
11591169
}
11601170

lib/events.js

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,25 @@ module.exports = api;
2121
api.defaultEventHandler = null;
2222

2323
/**
24-
* Check if event handler is in use in options or by default.
24+
* Setup event handler.
2525
*
26-
* This call is used to avoid building event structures and calling the main
27-
* handleEvent() call. It checks if safe mode is on, an event handler is in the
28-
* processing options, or a default handler was set.
26+
* Return an array event handler constructed from an optional safe mode
27+
* handler, an optional options event handler, and an optional default handler.
2928
*
30-
* @param {object} options - processing options:
31-
* {boolean} [safe] - flag for Safe Mode.
29+
* @param {object} options - processing options
3230
* {function|object|array} [eventHandler] - an event handler.
31+
*
32+
* @return an array event handler.
3333
*/
34-
api.hasEventHandler = options => {
35-
return options.safe || options.eventHandler || api.defaultEventHandler;
34+
api.setupEventHandler = ({options = {}}) => {
35+
// build in priority order
36+
const eventHandler = [].concat(
37+
options.safe ? api.safeModeEventHandler : [],
38+
options.eventHandler ? _asArray(options.eventHandler) : [],
39+
api.defaultEventHandler ? api.defaultEventHandler : []
40+
);
41+
// null if no handlers
42+
return eventHandler.length === 0 ? null : eventHandler;
3643
};
3744

3845
/**
@@ -45,24 +52,23 @@ api.hasEventHandler = options => {
4552
* handlers should process the event as appropriate. The 'next()' function
4653
* should be called to let the next handler process the event.
4754
*
55+
* NOTE: Only call this function if options.eventHandler is set and is an
56+
* array of hanlers. This is an optimization. Callers are expected to check
57+
* for an event handler before constructing events and calling this function.
58+
*
4859
* @param {object} event - event structure:
4960
* {string} code - event code
5061
* {string} level - severity level, one of: ['warning']
5162
* {string} message - human readable message
5263
* {object} details - event specific details
5364
* @param {object} options - processing options
65+
* {array} eventHandler - an event handler array.
5466
*/
5567
api.handleEvent = ({
5668
event,
5769
options
5870
}) => {
59-
const handlers = [].concat(
60-
// priority is safe mode handler, options handler, then default handler
61-
options.safe ? api.safeModeEventHandler : [],
62-
options.eventHandler ? _asArray(options.eventHandler) : [],
63-
api.defaultEventHandler ? api.defaultEventHandler : []
64-
);
65-
_handle({event, handlers});
71+
_handle({event, handlers: options.eventHandler});
6672
};
6773

6874
function _handle({event, handlers}) {

lib/expand.js

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -465,18 +465,20 @@ async function _expandObject({
465465
expandedParent
466466
});
467467
if(expandedProperty === undefined) {
468-
_handleEvent({
469-
event: {
470-
code: 'invalid property expansion',
471-
level: 'warning',
472-
message: 'Invalid expansion for property.',
473-
details: {
474-
// FIXME: include expandedProperty before mapping
475-
property: key
476-
}
477-
},
478-
options
479-
});
468+
if(options.eventHandler) {
469+
_handleEvent({
470+
event: {
471+
code: 'invalid property expansion',
472+
level: 'warning',
473+
message: 'Invalid expansion for property.',
474+
details: {
475+
// FIXME: include expandedProperty before mapping
476+
property: key
477+
}
478+
},
479+
options
480+
});
481+
}
480482
continue;
481483
}
482484
}
@@ -628,17 +630,19 @@ async function _expandObject({
628630
// ensure language tag matches BCP47
629631
for(const language of value) {
630632
if(_isString(language) && !language.match(REGEX_BCP47)) {
631-
_handleEvent({
632-
event: {
633-
code: 'invalid @language value',
634-
level: 'warning',
635-
message: '@language value must be valid BCP47.',
636-
details: {
637-
language
638-
}
639-
},
640-
options
641-
});
633+
if(options.eventHandler) {
634+
_handleEvent({
635+
event: {
636+
code: 'invalid @language value',
637+
level: 'warning',
638+
message: '@language value must be valid BCP47.',
639+
details: {
640+
language
641+
}
642+
},
643+
options
644+
});
645+
}
642646
}
643647
}
644648

lib/fromRdf.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -341,17 +341,19 @@ function _RDFToObject(o, useNativeTypes, rdfDirection, options) {
341341
if(language.length > 0) {
342342
rval['@language'] = language;
343343
if(!language.match(REGEX_BCP47)) {
344-
_handleEvent({
345-
event: {
346-
code: 'invalid @language value',
347-
level: 'warning',
348-
message: '@language value must be valid BCP47.',
349-
details: {
350-
language
351-
}
352-
},
353-
options
354-
});
344+
if(options.eventHandler) {
345+
_handleEvent({
346+
event: {
347+
code: 'invalid @language value',
348+
level: 'warning',
349+
message: '@language value must be valid BCP47.',
350+
details: {
351+
language
352+
}
353+
},
354+
options
355+
});
356+
}
355357
}
356358
}
357359
rval['@direction'] = direction;

lib/jsonld.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const {
8484
logEventHandler: _logEventHandler,
8585
logWarningEventHandler: _logWarningEventHandler,
8686
setDefaultEventHandler: _setDefaultEventHandler,
87+
setupEventHandler: _setupEventHandler,
8788
strictModeEventHandler: _strictModeEventHandler,
8889
unhandledEventHandler: _unhandledEventHandler
8990
} = require('./events');
@@ -1042,7 +1043,13 @@ function _setDefaults(options, {
10421043
documentLoader = jsonld.documentLoader,
10431044
...defaults
10441045
}) {
1045-
return Object.assign({}, {documentLoader}, defaults, options);
1046+
return Object.assign(
1047+
{},
1048+
{documentLoader},
1049+
defaults,
1050+
options,
1051+
{eventHandler: _setupEventHandler({options})}
1052+
);
10461053
}
10471054

10481055
// end of jsonld API `wrapper` factory

0 commit comments

Comments
 (0)