Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fflip

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fflip - npm Package Compare versions

Comparing version 0.6.2 to 0.8.1

73

lib/fflip.js

@@ -0,4 +1,7 @@

'use strict';
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
var getFeatures,

@@ -9,11 +12,3 @@ getCriteria,

function setCriteria(configVal) {
if(typeof configVal == 'function') {
getCriteria = configVal;
updateCriteria();
} else {
getCriteria = undefined;
}
if(typeof configVal == 'object') {
self._criteria = configVal;
}
self._criteria = configVal;
}

@@ -33,16 +28,2 @@

function updateCriteria() {
if(!getCriteria) {
return;
} else if(getCriteria.length === 0) {
self._criteria = getCriteria() || self._criteria;
return;
} else if(getCriteria.length === 1) {
getCriteria(getCriteriaCallback);
return;
} else if(getCriteria.length > 1) {
throw new Error('Too Many Arguments!');
}
}
function updateFeatures() {

@@ -93,21 +74,21 @@ if(!getFeatures) {

reload: function() {
updateCriteria();
updateFeatures();
},
userHasFeature: function(user, f_name) {
var featureCriteria = self._features[f_name];
if(typeof featureCriteria == 'undefined') {
userHasFeature: function(user, featureName) {
var feature = self._features[featureName];
if(typeof feature != 'object') {
return null;
}
if(Object.keys(featureCriteria).length === 0) {
var featureCriteria = feature.criteria || {};
var criteriaArray = Object.keys(featureCriteria);
var isEnabled = true;
if(criteriaArray.length == 0) {
return false;
}
var isEnabled = true;
Object.keys(featureCriteria).forEach(function(ckey) {
criteriaArray.forEach(function(cKey) {
if(isEnabled) {
var c_name = ckey;
var c_data = featureCriteria[ckey];
var c_func = self._criteria[c_name];
isEnabled = c_func(user, c_data);
var c_data = featureCriteria[cKey];
var c_func = self._criteria[cKey];
isEnabled = c_func(user, c_data);
}

@@ -121,7 +102,7 @@ });

var user_features = {};
Object.keys(self._features).forEach(function(f_name) {
if(flags[f_name] !== undefined) {
user_features[f_name] = flags[f_name];
Object.keys(self._features).forEach(function(featureName) {
if(flags[featureName] !== undefined) {
user_features[featureName] = flags[featureName];
} else {
user_features[f_name] = self.userHasFeature(user, f_name);
user_features[featureName] = self.userHasFeature(user, featureName);
}

@@ -137,15 +118,19 @@ });

_isSet: false,
features: {},
flags: req.cookies.fflip
features: {}
};
req.fflip.setFeatures = function(user) {
if(req.cookies) {
req.fflip.flags = req.cookies.fflip;
} else {
req.fflip.flags = {};
}
req.fflip.setForUser = function(user) {
req.fflip.features = self.featuresForUser(user, req.fflip.flags);
req.fflip._isSet = true;
};
req.fflip.hasFeature = function(fName) {
req.fflip.has = function(featureName) {
if(!req.fflip._isSet) {
console.error('FFlip: features not set - call setFeatures() before checking for features (and consider adding middleware to always set features)');
console.error('FFlip: features not set - call setForUser() before checking for features (and consider adding middleware to always set features)');
return null;
}
return req.fflip.features[fName];
return req.fflip.features[featureName];
};

@@ -152,0 +137,0 @@

{
"name": "fflip",
"description": "Highly Customizable & Über Powerful Feature Flipping",
"description": "Feature Flipping Across the Server and Client",
"author": "Fred Schott <fredkschott@gmail.com>",
"licence": "MIT",
"version": "0.6.2",
"version": "0.8.1",
"main": "lib/fflip",
"repository": {
"type": "git",
"url": "http://github.com/FredKSchott/fflip"
},
"repository": "FredKSchott/fflip",
"bugs": "https://github.com/FredKSchott/fflip/issues",

@@ -23,10 +20,9 @@ "scripts": {

"keywords": [
"feature flipping",
"feature toggling",
"feature",
"flip",
"toggle",
"continuous integration",
"ci"
"feature flipping",
"feature toggling",
"continuous integration"
]
}

@@ -7,3 +7,3 @@ [![Build Status](https://travis-ci.org/FredKSchott/fflip.png)](https://travis-ci.org/FredKSchott/fflip)

Working on an experimental new design? Starting a closed beta? Rolling out a new feature over the next few weeks? Fa-fa-fa-flip it! __fflip__ gives you complete control over releasing new functionality to your users based on thier user id, join date, membership status, and whatever else you can think of. __fflip's__ goal is to be the most extendable and customizable feature flipping/toggling module out there.
Working on an experimental new design? Starting a closed beta? Rolling out a new feature over the next few weeks? Fa-fa-fa-flip it! __fflip__ gives you complete control over releasing new functionality to your users based on thier user id, join date, membership status, and whatever else you can think of. __fflip's__ goal is to be the most powerful and extendable feature flipping/toggling module out there.

@@ -49,3 +49,4 @@ - Describes __custom criteria and features__ using easy-to-read JSON

for(var id in idArr) {
if(user.id == idArr[id]) return true;
if(user.id == idArr[id])
return true;
}

@@ -58,14 +59,24 @@ return false;

###Features
Features are sets of criteria to test users against. The value associated with the criteria is passed in as the data argument of the criteria function. A user will have a featured enabled if they match all listed criteria, otherwise the feature is disabled. Features are described as follows:
Features contain sets of criteria to test users against. The value associated with the criteria is passed in as the data argument of the criteria function. A user will have a featured enabled if they match all listed criteria, otherwise the feature is disabled. Features can include other optional properties for context. Features are described as follows:
```javascript
var ExampleFeaturesObject = {
paidFeature: {
isPaidUser: true
criteria: {
isPaidUser: true
}
},
closedBeta: {
allowUserIDs: [20,30,80,181],
name: "A Closed Beta",
criteria: {
allowUserIDs: [20,30,80,181]
}
},
newFeatureRollout: {
isPaidUser: false,
percentageOfUsers: 0.50,
name: "A New Feature Rollout",
description: "Rollout of that new feature over the next month",
owner: "FredKSchott", // Remember: These are all optional, only criteria is required
criteria: {
isPaidUser: false,
percentageOfUsers: 0.50
}
}

@@ -80,3 +91,3 @@ }

Bool userHasFeature(user, featureName) // Return true/false if featureName is enabled for user
void reload() // Force a reload of criteria/features
void reload() // Force a reload (if loading features dynamically)
void __express(app) // Connect with an Express app (see below)

@@ -88,17 +99,17 @@ ```

fflip.config({
criteria: {}, // Object (see above) or Function (see below)
features: {}, // Object or Function
reload: 30, // Time between refreshing features/criteria, in seconds
criteria: {}, // Criteria Object
features: {}, // Features Object | Function (see below)
reload: 30, // Interval for refreshing features, in seconds
});
```
###Loading Features & Criteria Dynamically
__fflip__ also accepts functions for loading criteria and features. If __fflip__ is passed a funciton with no arguments it will call the function and accept the return value. To load asyncronously, pass a function that sends a features/criteria data object to a callback. __fflip__ will recieve the callback and set the data accordingly. In both cases, __fflip__ will save these functions and call them again every X seconds, as set by the reload parameter.
###Loading Features Dynamically
__fflip__ also accepts functions for loading features. If __fflip__ is passed a funciton with no arguments it will call the function and accept the return value. To load asyncronously, pass a function that sends a features object to a callback. __fflip__ will recieve the callback and set the data accordingly. In both cases, __fflip__ will save the function and call it again every X seconds, as set by the reload parameter.
```javascript
// Load Criteria Syncronously
var getCriteriaSync = function() {
var collection = db.collection('criteria');
var criteriaArr = collection.find().toArray();
/* Proccess criteriaArr -> criteriaObj (format described above) */
return criteriaObj;
var getFeaturesSync = function() {
var collection = db.collection('features');
var featuresArr = collection.find().toArray();
/* Proccess featuresArr -> featuresObj (format described above) */
return featuresObj;
}

@@ -117,5 +128,4 @@

fflip.config({
criteria: getCriteriaSync,
features: getFeaturesAsync,
reload: 60 /* Call each function again and update features every 60 secondss */
features: getFeaturesAsync, // or: getFeaturesSync
reload: 60 /* Call the function again and update every 60 secondss */
});

@@ -126,3 +136,4 @@ ```

##Express Integration
__fflip__ provides easy integration with the popular web framework [Express](https://github.com/visionmedia/express). Just call ``fflip.__express(app)`` wherever you set up your server to enable the following:
__fflip__ provides easy integration with the popular web framework [Express](https://github.com/visionmedia/express).
Just call ``fflip.__express(app)`` wherever you set up your express application to enable the following:

@@ -137,8 +148,7 @@ ####__A route for manually flipping on/off features__

flags: Any override flags set by the fflip cookie
features: A user's fflip features object. Undefined until setFeatures() is called.
setFeatures(user): Given a user, attaches the features object to the request (at req.fflip.features)
hasFeature(featureName): Given a feature name, returns the feature boolean, or null if setFeatures() has't been called
features: A user's fflip features object. Empty until setForUser() is called.
setForUser(user): Given a user, attaches the features object to the request (at req.fflip.features)
has(featureName): Given a feature name, returns the feature boolean, undefined if feature doesn't exist, or null if setForUser() has't been called
}
```
To avoid polluting the request object, All fflip functionality is contained within req.fflip. But (If your implementation allows it) you can add aliases directly onto the request object.

@@ -145,0 +155,0 @@ ####Automatically deliver Features to the client

@@ -19,9 +19,17 @@ var assert = require('assert'),

fOpen: {
c1: true
name: 'fOpen',
description: 'true for all users',
criteria: {
c1: true
}
},
fClosed: {
c1: false
fClosed: {
criteria: {
c1: false
}
},
fEval: {
c2: 'abc'
criteria: {
c2: 'abc'
}
}

@@ -87,19 +95,2 @@ },

test('should set criteria if given a syncronous loading function', function(){
var loadSyncronously = function() {
return configData.criteria;
};
fflip.config({criteria: loadSyncronously});
assert.equal(configData.criteria, fflip._criteria);
});
test('should set criteria if given an asyncronous loading function', function(done){
var loadAsyncronously = function(callback) {
callback(configData.criteria);
assert.equal(configData.criteria, fflip._criteria);
done();
};
fflip.config({criteria: loadAsyncronously});
});
test('should set reloadRate if given reload', function(){

@@ -120,27 +111,21 @@ fflip._reloadRate = 0;

this.timeout(205);
var count = -99;
var loadAsyncronously = function(callback) {
count++;
if(count >= 2) {
done();
}
callback({});
done();
};
fflip.config({features: loadAsyncronously, criteria: loadAsyncronously, reload: 0.2});
fflip.config({features: loadAsyncronously, reload: 0.2});
count = 0;
});
test('should update criteria and features', function(done){
var count;
test('should update features', function(done){
this.timeout(100);
var testReady = false;
var loadAsyncronously = function(callback) {
count++;
callback({});
if(testReady)
done();
};
fflip.config({features: loadAsyncronously, criteria: loadAsyncronously});
count = 0;
fflip.config({features: loadAsyncronously});
testReady = true;
fflip.reload();
setTimeout(function() {
assert.equal(count, 2);
done();
}, 100);
});

@@ -160,3 +145,3 @@

test('should return false if features has no criteria', function(){
test('should return false if no criteria set', function(){
assert.equal(false, fflip.userHasFeature(userABC, 'fEmpty'));

@@ -241,3 +226,3 @@ });

fflip._express_middleware(this.reqMock, this.resMock, function() {
me.reqMock.fflip.setFeatures(userXYZ);
me.reqMock.fflip.setForUser(userXYZ);
assert(fflip.featuresForUser.calledOnce);

@@ -250,9 +235,9 @@ assert(fflip.featuresForUser.calledWith(userXYZ, {fClosed: false}));

test('req.fflip.hasFeatures() should get the correct features', function(done) {
test('req.fflip.has() should get the correct features', function(done) {
var me = this;
fflip._express_middleware(this.reqMock, this.resMock, function() {
me.reqMock.fflip.setFeatures(userXYZ);
assert.strictEqual(me.reqMock.fflip.hasFeature('fOpen'), true);
assert.strictEqual(me.reqMock.fflip.hasFeature('fClosed'), false);
assert.strictEqual(me.reqMock.fflip.hasFeature('notafeature'), undefined);
me.reqMock.fflip.setForUser(userXYZ);
assert.strictEqual(me.reqMock.fflip.has('fOpen'), true);
assert.strictEqual(me.reqMock.fflip.has('fClosed'), false);
assert.strictEqual(me.reqMock.fflip.has('notafeature'), undefined);
done();

@@ -262,9 +247,9 @@ });

test('req.fflip.hasFeatures() should return null if features have not been set', function(done) {
test('req.fflip.has() should return null if features have not been set', function(done) {
var me = this;
var consoleErrorStub = sinon.stub(console, 'error'); // Supress Error Output
fflip._express_middleware(this.reqMock, this.resMock, function() {
assert.strictEqual(me.reqMock.fflip.hasFeature('fOpen'), null);
assert.strictEqual(me.reqMock.fflip.hasFeature('fClosed'), null);
assert.strictEqual(me.reqMock.fflip.hasFeature('notafeature'), null);
assert.strictEqual(me.reqMock.fflip.has('fOpen'), null);
assert.strictEqual(me.reqMock.fflip.has('fClosed'), null);
assert.strictEqual(me.reqMock.fflip.has('notafeature'), null);
done();

@@ -271,0 +256,0 @@ consoleErrorStub.restore();

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc