@@ -70,8 +70,8 @@ class Rule extends EventEmitter {
7070 * @param {object } conditions - conditions, root element must be a boolean operator
7171 */
7272 setConditions ( conditions ) {
73- if ( ! Object . prototype . hasOwnProperty . call ( conditions , 'all' ) && ! Object . prototype . hasOwnProperty . call ( conditions , 'any' ) ) {
74- throw new Error ( '"conditions" root must contain a single instance of "all" or "any "' )
73+ if ( ! Object . prototype . hasOwnProperty . call ( conditions , 'all' ) && ! Object . prototype . hasOwnProperty . call ( conditions , 'any' ) && ! Object . prototype . hasOwnProperty . call ( conditions , 'not' ) ) {
74+ throw new Error ( '"conditions" root must contain a single instance of "all", "any", or "not "' )
7575 }
7676 this . conditions = new Condition ( conditions )
7777 return this
@@ -193,10 +193,12 @@ class Rule extends EventEmitter {
193193 let comparisonPromise
194194 if ( condition . operator === 'all' ) {
195195 comparisonPromise = all ( subConditions )
196- } else {
196+ } else if ( condition . operator === 'any' ) {
197197 comparisonPromise = any ( subConditions )
198+ } else {
199+ comparisonPromise = not ( subConditions )
198200 }
199- // for booleans, rule passing is determined by the all/any result
201+ // for booleans, rule passing is determined by the all/any/not result
200202 return comparisonPromise . then ( comparisonValue => {
201203 const passes = comparisonValue === true
202204 condition . result = passes
@@ -230,19 +232,25 @@ class Rule extends EventEmitter {
230232 }
231233
232234 /**
233- * Evaluates a set of conditions based on an 'all' or 'any ' operator.
235+ * Evaluates a set of conditions based on an 'all', 'any', or 'not ' operator.
234236 * First, orders the top level conditions based on priority
235237 * Iterates over each priority set, evaluating each condition
236238 * If any condition results in the rule to be guaranteed truthy or falsey,
237239 * it will short-circuit and not bother evaluating any additional rules
238240 * @param {Condition[] } conditions - conditions to be evaluated
239- * @param {string('all'|'any') } operator
241+ * @param {string('all'|'any'|'not' ) } operator
240242 * @return {Promise(boolean) } rule evaluation result
241243 */
242244 const prioritizeAndRun = ( conditions , operator ) => {
243245 if ( conditions . length === 0 ) {
244246 return Promise . resolve ( true )
245247 }
248+ if ( conditions . length === 1 ) {
249+ // no prioritizing is necessary, just evaluate the single condition
250+ // 'all' and 'any' will give the same results with a single condition so no method is necessary
251+ // this also covers the 'not' case which should only ever have a single condition
252+ return evaluateCondition ( conditions [ 0 ] )
253+ }
246254 let method = Array . prototype . some
247255 if ( operator === 'all' ) {
248256 method = Array . prototype . every
@@ -292,6 +300,15 @@ class Rule extends EventEmitter {
292300 return prioritizeAndRun ( conditions , 'all' )
293301 }
294302
303+ /**
304+ * Runs a 'not' boolean operator on a single condition
305+ * @param {Condition } condition to be evaluated
306+ * @return {Promise(boolean) } condition evaluation result
307+ */
308+ const not = ( condition ) => {
309+ return prioritizeAndRun ( [ condition ] , 'not' ) . then ( result => ! result )
310+ }
311+
295312 /**
296313 * Emits based on rule evaluation result, and decorates ruleResult with 'result' property
297314 * @param {RuleResult } ruleResult
@@ -305,9 +322,12 @@ class Rule extends EventEmitter {
305322 if ( ruleResult . conditions . any ) {
306323 return any ( ruleResult . conditions . any )
307324 . then ( result => processResult ( result ) )
308- } else {
325+ } else if ( ruleResult . conditions . all ) {
309326 return all ( ruleResult . conditions . all )
310327 . then ( result => processResult ( result ) )
328+ } else {
329+ return not ( ruleResult . conditions . not )
330+ . then ( result => processResult ( result ) )
311331 }
312332 }
313333}
0 commit comments