Skip to content

Commit 3a66b0c

Browse files
committed
add support for ANY/ALL rule matching
1 parent 4d48f99 commit 3a66b0c

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# Semaphore
2+
23
A minimalist Feature Flag engine for CFML apps
34

45
[![Tests](https://github.com/atuttle/semaphore/actions/workflows/main_tests.yml/badge.svg)](https://github.com/atuttle/semaphore/actions/workflows/main_tests.yml)
6+
57
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
68
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)
79
<!-- ALL-CONTRIBUTORS-BADGE:END -->
10+
811
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](CODE_OF_CONDUCT.md)
912

1013
> #### ⚠️ UNPROVEN! DANGER! ⚠️
@@ -102,6 +105,27 @@ This is likely to change, but for now here's what they look like:
102105
}
103106
]
104107
}
108+
,'example_AND_flag': {
109+
name: 'Example AND Flag',
110+
description: 'This flag requires both rules to evaluate to TRUE',
111+
active: true,
112+
baseState: false,
113+
matchRules: 'all',
114+
rules: [
115+
{
116+
type: 'filter',
117+
attribute: 'role',
118+
operator: 'has',
119+
comparator: ['writer']
120+
},
121+
{
122+
type: 'filter',
123+
attribute: 'betaOptIn',
124+
operator: '==',
125+
comparator: true
126+
}
127+
]
128+
}
105129
,'example_inactive_flag': {
106130
name: 'Example Inactive Flag',
107131
description: 'This flag is false for everyone because it is inactive',
@@ -120,6 +144,12 @@ This is likely to change, but for now here's what they look like:
120144
- `everybody`: Flag is ON for all users
121145
- More TBD? If you have ideas, [hit me up!](/atuttle/semaphore/issues)
122146

147+
#### AND vs. OR
148+
149+
Flags can have multiple rules. At present, Semaphore only supports flag-wide AND/OR: You can require that `ALL` of the rules evaluate to TRUE, or that `ANY` of the rules evaluate to TRUE.
150+
151+
The default is `ANY`. If you want to require all rules match, set `matchRules: 'ALL'` on your flag.
152+
123153
# Why not just use config settings?
124154

125155
You could do that, sure. But the value proposition of feature flags is that they can be toggled independendtly of deploying code changes to your application, and often much more rapidly. They can take effect as quickly as you can update the flag state on your application.
@@ -129,6 +159,7 @@ You could do that, sure. But the value proposition of feature flags is that they
129159
ALSO, feature flags allow you to dynamically segment the user population. As we'll see below, I've already got support for %-based rollouts, as well as specific user-attribute and environment-attribute filtering.
130160

131161
# Why roll your own?
162+
132163
I created this because I got fed up trying to implement [FlagSmith](https://flagsmith.com) and [Split.io](https://www.split.io) in my app. They both assume that if you're using Java then you're willing/comfortable using Maven (strike 1), both of their docs barely cover SDK instantiation and I couldn't get either of them even simply on its feet let alone doing something useful (strike 2), and it's (mostly) just "if-statements", right? Why can't we host that ourselves? (strike 3)
133164

134165
# Contributing

semaphore.cfc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ component {
4848
if ( arguments.flag.active == false ){
4949
return false;
5050
}
51+
52+
if ( structKeyExists(arguments.flag, 'matchRules') && lcase(arguments.flag.matchRules) == 'all' ){
53+
// this is how we differentiate between AND and OR.
54+
for ( var rule in arguments.flag.rules ){
55+
if ( !evaluateRule( rule, arguments.userAttributes ) ){
56+
return false;
57+
}
58+
}
59+
return true;
60+
}//else the default behavior; match "any":
61+
5162
//return true at first matching rule, no need to check them all
5263
for ( var rule in arguments.flag.rules ){
5364
if ( evaluateRule( rule, arguments.userAttributes ) ){

0 commit comments

Comments
 (0)