JSFiddle - React, Tailwind, and code Playground
by Paul
JavaScript
// Core boolean rules that need to be implemented in all clients, can't be overriden
const coreRules = {
true: () => true,
false: () => false,
not: (check, context, rules) => !check(context, rules),
all: (check, context, rules) => rules.every(r => check(context, r)),
any: (check, context, rules) => rules.some(r => check(context, r)),
};
// Context-aware rules where the client can apply business logic
const clientRules = {
user_is_native: (check, context) => context.user.type === 'native',
user_is_email: (check, context) => context.user.type === 'email',
session_has_email: (check, context) => context.session.has_email,
conversation_has_email: (check, context) => context.conversation.has_email,
// By default the client allows to send messages
can_send_message: (check, context) => true,
};
// Server rules need to be serialisable as JSON data
const serverRules = {
// Server can create custom rules by composing existing ones
conversation_hasnt_email: ['not', 'conversation_has_email'],
// The server overrides the client rule to send message based on other rules
can_send_message: [
'any',
'conversation_hasnt_email',
['all', 'conversation_has_email', 'session_has_email'],
],
};
// Transform rules object into functions
const processRules = rules => Object.keys(rules).reduce((acc, key) => {
acc[key] = (check, context) => check(context, rules[key]);
return acc;
}, {});
// Merge all rules
const allRules = Object.assign({}, clientRules, processRules(serverRules), coreRules);
// Main checking logic
const checker = (context, rules) => {
if (!Array.isArray(rules)) {
rules = [rules];
}
if (!rules.length) {
return false;
}
const callRule = allRules[rules.shift()];
return callRule ? callRule(checker, context, rules) : false;
};
// Example data for demo
const mock = {
user: {
type: 'native',
},
session1: {
has_email: true,
},
session2: {
has_email: false,
},
conversation1: {
...