Comparing version 0.0.1 to 0.0.2
@@ -10,7 +10,15 @@ var fs = require('fs'), | ||
get: function() { return isEnabled(featureName); }, | ||
set: function(value) { value ? enable(featureName) : disable(featureName); }, | ||
set: function(value) { | ||
if (value) { | ||
enable(featureName); | ||
} else { | ||
disable(featureName); | ||
} | ||
}, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
storage && storage.save(featureName); | ||
if (storage) { | ||
storage.save(featureName); | ||
} | ||
} | ||
@@ -25,3 +33,5 @@ } | ||
features[featureName] = true; | ||
storage && storage.save(featureName); | ||
if (storage) { | ||
storage.save(featureName); | ||
} | ||
} | ||
@@ -31,3 +41,5 @@ | ||
features[featureName] = false; | ||
storage && storage.save(featureName); | ||
if (storage) { | ||
storage.save(featureName); | ||
} | ||
} | ||
@@ -34,0 +46,0 @@ |
140
lib/http.js
@@ -1,64 +0,96 @@ | ||
function setup(flipper) { | ||
function validStatus(data) { | ||
return (data === 'true' || data === 'false'); | ||
} | ||
var flipper, | ||
allFeatures = { | ||
test: function(method, feature) { return method === 'GET' && feature === ''; }, | ||
action: function(feature, req, res) { | ||
res.statusCode = 200; | ||
res.end(JSON.stringify(flipper.allFeatures())); | ||
} | ||
}, | ||
singleFeature = { | ||
test: function(method, feature) { return method === 'GET' && flipper.exists(feature); }, | ||
action: function(feature, req, res) { | ||
res.statusCode = 200; | ||
res.end(JSON.stringify(flipper[feature])); | ||
} | ||
}, | ||
createOrUpdateFeature = { | ||
test: function(method, feature) { return method === 'PUT' && feature !== ''; }, | ||
action: function(feature, req, res) { | ||
var data = ''; | ||
if (req.body) { | ||
data = req.body; | ||
parseRequestBody(feature, data, res); | ||
} else { | ||
req.setEncoding('utf8'); | ||
req.on('data', function(chunk){ data += chunk; }); | ||
req.on('end', function() { parseRequestBody(feature, data, res); }); | ||
} | ||
} | ||
}, | ||
notFound = { | ||
test: function(method, feature) { return true; }, | ||
action: function(feature, req, res) { | ||
res.statusCode = 404; | ||
res.end(); | ||
} | ||
}, | ||
handlers = [ | ||
allFeatures, | ||
singleFeature, | ||
createOrUpdateFeature, | ||
notFound | ||
]; | ||
/* this seems silly, perhaps being overly cautious here */ | ||
function convert(data) { | ||
if (data === 'true') { | ||
return true; | ||
} else if (data === 'false') { | ||
return false; | ||
} else { | ||
return undefined; | ||
} | ||
function setup(lib) { | ||
flipper = lib; | ||
return http; | ||
} | ||
/* this seems silly, perhaps being overly cautious here */ | ||
function convert(data) { | ||
if (data === 'true') { | ||
return true; | ||
} else if (data === 'false') { | ||
return false; | ||
} else { | ||
return undefined; | ||
} | ||
} | ||
return function http(baseurl) { | ||
function validStatus(data) { | ||
return (data === 'true' || data === 'false'); | ||
} | ||
if (!baseurl) throw new Error("Flipper HTTP interface needs a base URL"); | ||
function parseRequestBody(feature, data, res) { | ||
if (validStatus(data)) { | ||
res.statusCode = flipper.exists(feature) ? 200 : 201; | ||
//it's safe to always add a feature | ||
flipper.add(feature); | ||
flipper[feature] = convert(data); | ||
res.end(JSON.stringify(flipper[feature])); | ||
} else { | ||
res.statusCode = 400; | ||
res.end(); | ||
} | ||
} | ||
return function (req, res, next) { | ||
if (req.url.indexOf(baseurl) !== 0) { | ||
next(); | ||
return; | ||
} | ||
res.setHeader('Content-Type', 'application/json'); | ||
function http(baseurl) { | ||
if (!baseurl) throw new Error("Flipper HTTP interface needs a base URL"); | ||
if (req.url === baseurl && req.method === 'GET') { | ||
res.statusCode = 200; | ||
res.end(JSON.stringify(flipper.allFeatures())); | ||
return; | ||
} | ||
return function (req, res, next) { | ||
var feature, notHandled = true; | ||
if (req.url.indexOf(baseurl) !== 0) { | ||
next(); | ||
return; | ||
} | ||
var feature = req.url.substring(baseurl.length + 1); | ||
if (req.method === 'GET') { | ||
if (flipper.exists(feature)) { | ||
res.statusCode = 200; | ||
res.end(JSON.stringify(flipper[feature])); | ||
} else { | ||
res.statusCode = 404; | ||
res.end(); | ||
} | ||
res.setHeader('Content-Type', 'application/json'); | ||
feature = req.url.substring(baseurl.length + 1); | ||
handlers.forEach(function (handler) { | ||
if (notHandled && handler.test(req.method, feature)) { | ||
handler.action(feature, req, res); | ||
notHandled = false; | ||
} | ||
if (req.method === 'PUT') { | ||
var data = ''; | ||
req.setEncoding('utf8'); | ||
req.on('data', function(chunk){ data += chunk }); | ||
req.on('end', function() { | ||
if (validStatus(data)) { | ||
res.statusCode = flipper.exists(feature) ? 200 : 201; | ||
//it's safe to always add a feature | ||
flipper.add(feature); | ||
flipper[feature] = convert(data); | ||
res.end(JSON.stringify(flipper[feature])); | ||
} else { | ||
res.statusCode = 400; | ||
res.end(); | ||
} | ||
}); | ||
} | ||
}; | ||
}); | ||
}; | ||
@@ -65,0 +97,0 @@ } |
@@ -5,3 +5,3 @@ { | ||
"description": "Feature Flipping for node.js", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"repository": { | ||
@@ -8,0 +8,0 @@ "type": "git", |
# Flipper - feature flipping for node.js # | ||
[![Build Status](https://secure.travis-ci.org/nomiddlename/flipper.png?branch=master)](http://travis-ci.org/nomiddlename/flipper) | ||
@@ -7,2 +8,6 @@ [Feature flipping](http://code.flickr.com/blog/2009/12/02/flipping-out/) (also called [feature toggling](http://martinfowler.com/bliki/FeatureToggle.html)) is a technique used in Continuous Deployment to allow developers to work on new features without blocking releases to production. [99 Designs explain it well](http://99designs.com/tech-blog/blog/2012/03/01/feature-flipping/). | ||
## Installation ## | ||
npm install flipper | ||
## API ## | ||
@@ -9,0 +14,0 @@ ### Defining a feature ### |
@@ -40,3 +40,3 @@ var vows = require('vows'), | ||
assert.isTrue(lib.isEnabled('featureToEnable')); | ||
assert.isTrue(lib.allFeatures()['featureToEnable']); | ||
assert.isTrue(lib.allFeatures().featureToEnable); | ||
}, | ||
@@ -52,3 +52,3 @@ 'then disabling the feature': { | ||
assert.isFalse(lib.isEnabled('featureToEnable')); | ||
assert.isFalse(lib.allFeatures()['featureToEnable']); | ||
assert.isFalse(lib.allFeatures().featureToEnable); | ||
} | ||
@@ -61,3 +61,3 @@ }, | ||
assert.isTrue(lib.featureToDisable); | ||
lib.disable("featureToDisable") | ||
lib.disable("featureToDisable"); | ||
return lib; | ||
@@ -68,3 +68,3 @@ }, | ||
assert.isFalse(lib.isEnabled('featureToDisable')); | ||
assert.isFalse(lib.allFeatures()['featureToDisable']); | ||
assert.isFalse(lib.allFeatures().featureToDisable); | ||
} | ||
@@ -71,0 +71,0 @@ } |
@@ -5,6 +5,7 @@ var vows = require('vows'), | ||
function request(method, url, body) { | ||
function request(method, url, body, bodyParserBody) { | ||
return { | ||
method: method, | ||
url: url, | ||
body: bodyParserBody, | ||
setEncoding: function(encoding) { | ||
@@ -35,2 +36,8 @@ assert.equal(encoding, 'utf8'); | ||
function mockNext() { | ||
return function() { | ||
this.called = true; | ||
}; | ||
} | ||
function responseOf(contentType, status, body) { | ||
@@ -85,4 +92,4 @@ return function(response) { | ||
'response should include status of the features': function(response) { | ||
assert.equal(JSON.parse(response.data)['testFeature'], flipper.testFeature); | ||
assert.equal(JSON.parse(response.data)['mutatedDolphins'], flipper.mutatedDolphins); | ||
assert.equal(JSON.parse(response.data).testFeature, flipper.testFeature); | ||
assert.equal(JSON.parse(response.data).mutatedDolphins, flipper.mutatedDolphins); | ||
} | ||
@@ -105,5 +112,3 @@ }, | ||
}, | ||
'response should have a status code of 404 Not Found': function(response) { | ||
assert.equal(response.statusCode, 404); | ||
} | ||
'response should be': responseOf('application/json', 404) | ||
}, | ||
@@ -115,3 +120,3 @@ 'when a PUT request for a feature is made': { | ||
flipper.disable('disturbedChickens'); | ||
http(request('PUT', '/baseurl/disturbedChickens', 'true'), response); | ||
http(request('PUT', '/baseurl/disturbedChickens', 'true'), response, mockNext()); | ||
return response; | ||
@@ -160,2 +165,27 @@ }, | ||
} | ||
}, | ||
'when a PUT request to the baseurl is made': { | ||
topic: function(http) { | ||
var response = mockResponse(); | ||
http(request('PUT', '/baseurl', 'true'), response); | ||
return response; | ||
}, | ||
'response should be': responseOf('application/json', 404), | ||
'feature should not have been added': function() { | ||
assert.isFalse(flipper.exists('')); | ||
} | ||
}, | ||
'with connect.bodyParser in the stack': { | ||
'when a PUT request is made': { | ||
topic: function(http) { | ||
var response = mockResponse(); | ||
http(request('PUT', '/baseurl/elbows', undefined, 'true'), response); | ||
return response; | ||
}, | ||
'response should be': responseOf('application/json', 201), | ||
'feature should have been added': function() { | ||
assert.isTrue(flipper.exists('elbows')); | ||
assert.isTrue(flipper.elbows); | ||
} | ||
} | ||
} | ||
@@ -162,0 +192,0 @@ } |
Sorry, the diff of this file is not supported yet
25307
578
55