我正在为面向文档的系统添加安全层,并且我需要一种合理抽象的方法来定义哪些新文档和对现有文档的更新是特定用户合法的。
具体问题是: - 这些文档(至少在传输过程中)被定义为JSON对象,因此规则可能具有分层结构,因此规则引擎必须递归工作。例如,Employee对象可能有一个称为Compensation的子对象,该子对象具有称为PayPeriod的字段,该字段必须为“weekly”,“biweekly”或“monthly”之一。 - 它运行Node.js,并且某些规则需要从输入读取(例如,从数据库中读取更多用户数据),因此它必须以连续样式运行。
因此,我想出了这个解决方案:每个规则都是一个函数,它接受当前值、建议的新值和一个回调函数,该函数用要使用的值调用。该值可以是两个输入之一或由规则计算的第三个值。以下是一个规则的例子:
但我需要一种将规则串联起来的方法:
所以管理员更新员工记录的规则可能是:
具体问题是: - 这些文档(至少在传输过程中)被定义为JSON对象,因此规则可能具有分层结构,因此规则引擎必须递归工作。例如,Employee对象可能有一个称为Compensation的子对象,该子对象具有称为PayPeriod的字段,该字段必须为“weekly”,“biweekly”或“monthly”之一。 - 它运行Node.js,并且某些规则需要从输入读取(例如,从数据库中读取更多用户数据),因此它必须以连续样式运行。
因此,我想出了这个解决方案:每个规则都是一个函数,它接受当前值、建议的新值和一个回调函数,该函数用要使用的值调用。该值可以是两个输入之一或由规则计算的第三个值。以下是一个规则的例子:
var nonEmpty = function(proposedValue, existingValue, callback) {
callback( (proposedValue.length > 0) ? proposedValue : existingValue);
};
这个规则只允许您设置或替换此字段为非零长度的值。当然,这只适用于字符串值(暂时忽略列表),因此我们需要一条规则来强制执行字符串性质:
var isString = function(proposedValue, existingValue, callback) {
callback( ( typeof(proposedValue) === 'string') ? proposedValue : existingValue);
};
实际上,这似乎是一种常见的问题,因此我编写了一个规则生成器:
var ofType = function(typeName) {
return function(proposedValue, existingValue, callback) {
callback( ( typeof(proposedValue) === typeName) ? proposedValue : existingValue);
};
};
var isString = ofType('string')
但我需要一种将规则串联起来的方法:
var and = function(f1, f2) {
return function(proposedValue, existingValue, callback) {
f1(proposedValue, existingValue,
function(newProposedValue) {
f2(newProposedValue, existingValue, callback);
});
};
};
var nonEmptyString = and(isString, nonEmpty);
所以管理员更新员工记录的规则可能是:
limitedObject({
lastName : nonEmptyString,
firstName : nonEmptyString,
compensation : limitedObject({
payPeriod : oneOf('weekly', 'biweekly', 'monthly'),
pay : numeric
}
})
limitedObject
(就像ofType
一样)是一个规则生成函数,它只允许其参数中指定的字段,并将给定规则应用于这些字段的值。所以我编写了所有这些内容,并且它的运作非常好。所有我的错误都被证明是单元测试中的错误!好吧,几乎所有的错。无论如何,如果你已经读到了这里,这是我的问题:我一直在狂热地学习单子,我的阅读启发我用这种方式解决问题。但是,这是否真正是单子? (可能的答案:“是”,“不是,但这没关系,因为单子并不是解决这个问题的正确方法”,“不是,这里需要更改。”也欢迎第四种可能性。)