text: // settings.js
  //
  // A cache for settings, more or less.
  //
  var errors = require('@holmwell/errors');
  var guard  = errors.guard;
  var async  = require('async');
  
  var listeners = [];
  
  module.exports = function (db) {
  
  	var cache = null;
  	var isCacheValid = false;
  	
  	// Update one setting
  	var update = function (setting, callback) {
  		db.settings.update(setting, function (err, results) {
  			invalidateCache();
  			if (!err) {
  				handleUpdate(setting);
  			}
  
  			if (callback) {
  				callback(err, results);
  			}
  		});
  	}; 
  
  	// Update an array of settings
  	var set = function (settings, fnCallback) {
  		var tasks = [];
  
  		settings.forEach(function (setting) {
  			tasks.push(function (callback) {
  				update(setting, callback);				
  			});
  		});
  
  		async.parallel(tasks, function (err, results) {
  			invalidateCache();
  			fnCallback(err, results)
  		});
  	};
  
  	// Ensure an array of settings are in the database.
  	var init = function (settings, callback) {
  		var settingsTable = {};
  		var initialized = {};
  		settings.forEach(function (setting) {
  			settingsTable[setting.name] = setting;
  			initialized[setting.name] = undefined;
  		});
  
  		var callbackIfAllSettingsReady = function () {
  			var isReady = true;
  			for (var key in initialized) {
  				if (initialized[key] === undefined) {
  					isReady = false;
  					break;
  				}
  			}
  
  			if (isReady) {
  				get(callback);
  			}
  		};
  
  		db.settings.getAll(guard(callback, function (savedSettings) {
  			// var savedSetting;
  			// var defaultSetting;
  			var settingFound;
  			var settingReady = function (setting) {
  				initialized[setting.name] = setting;
  				callbackIfAllSettingsReady();
  			};
  
  			for (var defaultName in settingsTable) {
  				
  				settingFound = false;
  				for (var savedName in savedSettings) {
  					if (savedName === defaultName) {
  						settingFound = true;
  						break;
  					}
  				}
  
  				if (!settingFound) {
  					var addSettingToDatabase = function (settingToAdd) {
  						db.settings.add(settingToAdd, function (err, body) {
  							if (err) {
  								return callback({
  									message: "Could not set setting: " + settingToAdd.name
  								});
  							}
  							settingReady(settingToAdd);
  						});
  					}(settingsTable[defaultName]);				
  				}
  				else {
  					settingReady(savedSettings[savedName]);
  				}
  			}
  		}));
  	};
  
  
  	// Wait for the database to be ready before
  	// calling init. 
  	var initWhenReady = function (settings, callback) {
  		// TODO: Put this timeout in a configuration.
  		var tenSeconds = 10000;
  		db.whenReady(function () {
  			init(settings, callback);
  		}, tenSeconds);
  	};
  
  
  	// Get all settings, from the cache if there is one, or
  	// from the database if there isn't.
  	function get (callback) {
  		if (!isCacheValid) {
  			db.settings.getAll(guard(callback, function (settings) {
  				cache = settings;
  				callback(null, settings);
  			}));	
  		}
  		else {
  			return callback(null, cache);
  		}
  	}
  
  	// convenience function to get a value
  	var value = function (settingName) {
  		if (!cache || !cache[settingName]) {
  			return undefined;
  		}
  
  		return cache[settingName].value;
  	};
  
  
  	// when we want to get a setting just in time
  	var lazy = function (settingName) {
  		var fn = function () {
  			return value(settingName);
  		};
  
  		return fn;
  	};
  
  
  	// Adds a settings object to req.app, for
  	// use in Express routes. 
  	var middleware = function (req, res, next) {
  		get(function (err, settings) {
  			req.app.set('settings', settings);
  			next(err);
  		});
  	};
  
  
  	// Use this function if you want to know when 
  	// settings are updated.
  	var addListener = function (listener) {
  		listeners.push(listener);
  	};
  
  	// When a setting is updated, call all of the listeners
  	// to let them know.
  	function handleUpdate (setting) {
  		listeners.forEach(function (listener) {
  			listener(setting);
  		});
  	};
  
  	function invalidateCache () {
  		isCacheValid = false;
  	};
  
  
  	return {
  		init: initWhenReady,
  		update: update,
  		set: set,
  		get: get,
  		value: value,
  		lazy: lazy,
  		middleware: middleware,
  		addListener: addListener,
  	};
  };