├── .gitignore ├── Bakefile ├── README.md ├── index.js ├── lib └── settings.js ├── package.json ├── src ├── index.coffee ├── lib │ └── settings.coffee └── test │ ├── config │ ├── environment.coffee │ ├── override.coffee │ ├── override2.coffee │ ├── overrideRoot.coffee │ └── overrideRoot2.coffee │ └── settingsTest.coffee ├── support └── extend.js └── test ├── config ├── environment.js ├── override.js ├── override2.js ├── overrideRoot.js └── overrideRoot2.js └── settingsTest.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.bak 3 | .DS_Store 4 | *.swo 5 | *.swp 6 | .*.swp 7 | .*.swo 8 | .*.bak 9 | lib-cov 10 | *.seed 11 | *.log 12 | *.csv 13 | *.dat 14 | *.out 15 | *.pid 16 | *.gz 17 | pids 18 | logs 19 | result 20 | !.gitignore 21 | node_modules 22 | -------------------------------------------------------------------------------- /Bakefile: -------------------------------------------------------------------------------- 1 | PATH=node_modules/.bin:$PATH 2 | 3 | #. Run tests 4 | function test { 5 | compile 6 | mocha test/*Test.js 7 | } 8 | 9 | 10 | #. Compiles sources 11 | function compile { 12 | invoke clean 13 | coffee -cb -o . src 14 | } 15 | 16 | 17 | #. Cleans output files 18 | function clean { 19 | rm -rf test 20 | rm -rf lib 21 | rm index.js 22 | } 23 | 24 | # vim: set ft=SH : 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-settings 2 | 3 | Simple, hierarchical environment-based app settings. 4 | 5 | ## Installation 6 | 7 | npm install settings 8 | 9 | ## Usage 10 | 11 | Configuration file `config.js` 12 | 13 | module.exports = { 14 | common: { 15 | storage: { 16 | host: 'localhost', 17 | database: 'server_dev', 18 | user: 'qirogami_user', 19 | password: 'password' 20 | } 21 | }, 22 | 23 | // Rest of environments are deep merged over `common`. 24 | 25 | development: {}, 26 | test: { 27 | storage: { 28 | database: 'server_test', 29 | password: 'foo' 30 | } 31 | }, 32 | production: { 33 | storage: { 34 | password: 'secret' 35 | } 36 | } 37 | }; 38 | 39 | Application file `app.js` 40 | 41 | var Settings = require('settings'); 42 | var config = new Settings(require('./config')); 43 | // inherited from common 44 | assert.equal(config.storage.host, 'localhost'); 45 | // specific to test 46 | assert.equal(config.storage.password, 'foo'); 47 | 48 | 49 | ### Environments 50 | 51 | The environment to use is based on (highest precedence first): 52 | 53 | 1. `forceEnv` property in config file 54 | 55 | // config/environment.js 56 | exports.forceEnv = 'production'; 57 | 58 | 2. `$NODE_ENV` environment variable 59 | 60 | NODE_ENV=production node app.js 61 | 62 | 3. `env` option passed to constructor. 63 | 64 | new Settings(file, {env: 'test'}); 65 | 66 | 67 | ### Application Defaults 68 | 69 | Property defaults may be preset in code. 70 | 71 | var settings = new Settings(file, { 72 | defaults: { 73 | framework: { 74 | views: 'app/views' 75 | } 76 | } 77 | }); 78 | assert.equal(settings.framework.views, 'app/views'); 79 | 80 | 81 | ## Hacking on the source 82 | 83 | To compile and test 84 | 85 | npm install bake-bash -g 86 | npm install -d 87 | bake test 88 | 89 | ## Notes 90 | 91 | `globalKey` option has been removed. Do this instead 92 | 93 | global.APP = new Settings(file); 94 | 95 | 96 | ## Credits 97 | 98 | jQuery library for `support/extend.js` from @FGRibreau 99 | 100 | 101 | ## License 102 | 103 | Copyright (C) 2010 by Mario L. Gutierrez 104 | 105 | Permission is hereby granted, free of charge, to any person obtaining a copy 106 | of this software and associated documentation files (the "Software"), to deal 107 | in the Software without restriction, including without limitation the rights 108 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 109 | copies of the Software, and to permit persons to whom the Software is 110 | furnished to do so, subject to the following conditions: 111 | 112 | The above copyright notice and this permission notice shall be included in 113 | all copies or substantial portions of the Software. 114 | 115 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 116 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 117 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 118 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 119 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 120 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 121 | THE SOFTWARE. 122 | 123 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | module.exports = require('./lib/settings'); 3 | -------------------------------------------------------------------------------- /lib/settings.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | var Extend, Path, Settings, assert, loadModule; 3 | 4 | assert = require('assert'); 5 | 6 | Extend = require('../support/extend'); 7 | 8 | Path = require('path'); 9 | 10 | loadModule = function(pathOrModule) { 11 | var mod; 12 | 13 | if (typeof pathOrModule === 'string') { 14 | if (Path.extname(pathOrModule) === '.coffee') { 15 | require('coffee-script'); 16 | } 17 | mod = require(pathOrModule); 18 | } else { 19 | mod = pathOrModule; 20 | } 21 | return mod; 22 | }; 23 | 24 | Settings = function(pathOrModule, options) { 25 | this.options = options != null ? options : {}; 26 | if (typeof pathOrModule === 'string') { 27 | this.path = pathOrModule; 28 | } 29 | this.environments = loadModule(pathOrModule); 30 | this._useEnvironment(this.options.env, this.options.root); 31 | return this._settings; 32 | }; 33 | 34 | Settings.prototype._useEnvironment = function(environ, root) { 35 | var common, result; 36 | 37 | if (environ != null) { 38 | if (!this.environments[environ]) { 39 | environ = void 0; 40 | } 41 | } 42 | this.env = this.environments.forceEnv || environ || process.env.NODE_ENV || 'common'; 43 | assert.ok(this.environments.common, 'Environment common not found in: ' + this.path); 44 | assert.ok(this.environments[this.env], 'Environment `' + this.env + '` not found in: ' + this.path); 45 | if (this.options.defaults != null) { 46 | common = Extend.cloneExtend(this.options.defaults, this.environments.common); 47 | } else { 48 | common = Extend.clone(this.environments.common); 49 | } 50 | if (this.env === 'common') { 51 | result = common; 52 | } else { 53 | result = Extend.extend(common, this.environments[this.env]); 54 | } 55 | return this._setSettingsScope(result, root); 56 | }; 57 | 58 | Settings.prototype.override = function(pathOrModule, root) { 59 | var mod; 60 | 61 | mod = loadModule(pathOrModule); 62 | if (mod.common != null) { 63 | mod = new Settings(mod, { 64 | env: this.env 65 | }); 66 | mod._obj = null; 67 | } 68 | if (root != null) { 69 | console.assert(mod[root] != null, "configuration root property not found: " + root); 70 | mod = mod[root]; 71 | } 72 | Extend.extend(this._settings, mod); 73 | return this._settings._obj = this; 74 | }; 75 | 76 | Settings.prototype._setSettingsScope = function(settings, root) { 77 | if (root != null) { 78 | this.root = root; 79 | } 80 | this._settings = root != null ? settings[root] : settings; 81 | this._settings._obj = this; 82 | this._settings.ENV = this.env; 83 | return this._settings; 84 | }; 85 | 86 | module.exports = Settings; 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name": "settings", 2 | "version": "0.1.1", 3 | "description": "Simple environment-based application settings", 4 | "author": "Mario Gutierrez ", 5 | "repository" : "git://github.com/mgutz/node-settings", 6 | "devDependencies": { 7 | "chai": "~1.5.0", 8 | "coffee-script": "~1.6.2", 9 | "mocha": "~1.9.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/index.coffee: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/settings') 2 | -------------------------------------------------------------------------------- /src/lib/settings.coffee: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright(c) 2010-2013 Mario L Gutierrez 3 | # MIT Licensed 4 | # 5 | 6 | assert = require('assert') 7 | Extend = require('../support/extend') 8 | Path = require('path') 9 | 10 | loadModule = (pathOrModule) -> 11 | if typeof pathOrModule == 'string' 12 | require('coffee-script') if Path.extname(pathOrModule) == '.coffee' 13 | mod = require(pathOrModule) 14 | else 15 | mod = pathOrModule 16 | mod 17 | 18 | 19 | # TODO - add watcher on settings file 20 | 21 | # Provides settings from an environment file or an object. 22 | # 23 | # The settings module must export, at a minimum, a `common` property. 24 | # 25 | # Other environments are deep merged into `common`. 26 | # 27 | # @param {String | Object} pathOrModule The file to load or an object. 28 | # 29 | # @example 30 | # 31 | # exports.common = {connectionString: 'mysql_dev'}; 32 | # 33 | # exports.development = {}; 34 | # exports.test = {connectionString: 'mysql_test'}; 35 | # 36 | # development.connectionString === 'mysql_dev'; 37 | # test.connectionString === 'mysql_test'; 38 | # 39 | Settings = (pathOrModule, @options = {}) -> 40 | if typeof pathOrModule == 'string' 41 | @path = pathOrModule 42 | 43 | @environments = loadModule(pathOrModule) 44 | @_useEnvironment @options.env, @options.root 45 | @_settings 46 | 47 | 48 | # Get settings for a specific environment. 49 | # 50 | # @param {String} environ [optional] The environment to retrieve. 51 | # 52 | # If `environ` is not passed, an environment is selected in this order 53 | # 54 | # 1. Module's `forceEnv` property 55 | # 2. $NODE_ENV environment variable 56 | # 3. `common` environment 57 | # 58 | Settings.prototype._useEnvironment = (environ, root) -> 59 | if environ? 60 | environ = undefined if !@environments[environ] 61 | @env = @environments.forceEnv || environ || process.env.NODE_ENV || 'common' 62 | 63 | assert.ok @environments.common, 'Environment common not found in: ' + @path 64 | assert.ok @environments[@env], 'Environment `' + @env + '` not found in: ' + @path 65 | 66 | if @options.defaults? 67 | common = Extend.cloneExtend(@options.defaults, @environments.common) 68 | else 69 | common = Extend.clone(@environments.common) 70 | 71 | if @env == 'common' 72 | result = common 73 | else 74 | result = Extend.extend(common, @environments[@env]) 75 | 76 | @_setSettingsScope result, root 77 | 78 | 79 | # Override settings from path or module with an option to specify 80 | # a property as `root`. 81 | # 82 | # @param pathOrModule {String | Object} The path or module. 83 | # @param root {String} [optional] The property to use as root. 84 | # @returns A settings object. 85 | Settings.prototype.override = (pathOrModule, root) -> 86 | mod = loadModule(pathOrModule) 87 | if mod.common? 88 | mod = new Settings(mod, env: @env) 89 | mod._obj = null 90 | 91 | if root? 92 | console.assert mod[root]?, "configuration root property not found: "+root 93 | mod = mod[root] 94 | 95 | Extend.extend @_settings, mod 96 | @_settings._obj = this 97 | 98 | 99 | # Sets internal settings to `settings` with the option to 100 | # use a property as `root`. 101 | # 102 | # @param settings {Object} The object whose properties become the settings. 103 | # If this is null, then existing settings are used. 104 | # @param root {String} The root name. 105 | # @returns A settings object. 106 | Settings.prototype._setSettingsScope = (settings, root) -> 107 | if root? 108 | @root = root 109 | @_settings = if root? then settings[root] else settings 110 | @_settings._obj = this 111 | @_settings.ENV = @env 112 | @_settings 113 | 114 | 115 | module.exports = Settings 116 | 117 | -------------------------------------------------------------------------------- /src/test/config/environment.coffee: -------------------------------------------------------------------------------- 1 | # define all common settings here 2 | exports.common = 3 | storage: 4 | host: 'localhost', 5 | database: 'server_dev', 6 | user: 'qirogami_user', 7 | password: 'password' 8 | re: /^foo$/ 9 | 10 | 11 | # deep merges over common 12 | exports.development = {} 13 | 14 | exports.test = 15 | storage: 16 | database: 'server_test' 17 | 18 | exports.production = 19 | storage: 20 | database: 'server_production' 21 | -------------------------------------------------------------------------------- /src/test/config/override.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | storage: 3 | host: 'override' 4 | -------------------------------------------------------------------------------- /src/test/config/override2.coffee: -------------------------------------------------------------------------------- 1 | exports.common = 2 | storage: 3 | host: 'override2' 4 | -------------------------------------------------------------------------------- /src/test/config/overrideRoot.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | fubar: 3 | storage: 4 | host: 'override' 5 | -------------------------------------------------------------------------------- /src/test/config/overrideRoot2.coffee: -------------------------------------------------------------------------------- 1 | exports.common = 2 | bar: 3 | storage: 4 | host: 'override2' 5 | -------------------------------------------------------------------------------- /src/test/settingsTest.coffee: -------------------------------------------------------------------------------- 1 | {assert} = require("chai") 2 | Settings = require("../lib/settings") 3 | 4 | instance = -> 5 | new Settings(__dirname + "/config/environment") 6 | 7 | 8 | describe "Settings", -> 9 | 10 | it "should get specific environment", -> 11 | settings = instance()._obj._useEnvironment('development') 12 | assert.equal 'server_dev', settings.storage.database 13 | 14 | # default should be 'development' 15 | settings = instance()._obj._useEnvironment() 16 | assert.equal 'server_dev', settings.storage.database 17 | 18 | settings = instance()._obj._useEnvironment('test') 19 | assert.equal 'server_test', settings.storage.database 20 | 21 | settings = instance()._obj._useEnvironment('production') 22 | assert.equal 'server_production', settings.storage.database 23 | 24 | 25 | it "should get value from ancestor if key is not found", -> 26 | settings = instance()._obj._useEnvironment('test') 27 | assert.equal 'password', settings.storage.password 28 | 29 | 30 | it "should have a forceEnv property to force all settings through an environment", -> 31 | environments = 32 | 'common': 33 | foo: 'boo' 34 | 'development': 35 | foo: 'bar' 36 | 'test': 37 | foo: 'bah' 38 | 'prod': 39 | foo: 'baz' 40 | forceEnv: 'development' 41 | 42 | settings = new Settings(environments) 43 | set = settings._obj._useEnvironment('development') 44 | assert.equal 'bar', set.foo 45 | 46 | set = settings._obj._useEnvironment('test') 47 | assert.equal 'bar', set.foo 48 | 49 | set = settings._obj._useEnvironment('prod') 50 | assert.equal 'bar', set.foo 51 | 52 | 53 | it "should replace array values, not merge them", -> 54 | environments = 55 | 'common': 56 | arr: [1, 2, 3] 57 | 'development': 58 | arr: [4, 5, 6] 59 | 60 | settings = new Settings(environments)._obj._useEnvironment('development') 61 | assert.deepEqual [4, 5, 6], settings.arr 62 | 63 | 64 | it "should do a deep merge", -> 65 | environments = 66 | 'common': { a: { b: { c: { arr: [1, 2, 3], bah: 'baz' }, bar: 'bar' } } } 67 | 'development': { a: { b: { c: { arr: [4, 5, 6], fu: 'bot' } } } } 68 | 69 | settings = new Settings(environments)._obj._useEnvironment('development') 70 | assert.deepEqual [4, 5, 6], settings.a.b.c.arr 71 | assert.deepEqual 'baz', settings.a.b.c.bah 72 | assert.deepEqual 'bar', settings.a.b.bar 73 | assert.deepEqual 'bot', settings.a.b.c.fu 74 | 75 | 76 | it "should say which environment is current", -> 77 | settings = instance() 78 | assert.equal 'common', settings.ENV 79 | 80 | settings = new Settings(__dirname + '/config/environment', env: 'production') 81 | assert.equal 'production', settings.ENV 82 | 83 | 84 | settings = instance()._obj._useEnvironment('test') 85 | assert.equal 'test', settings.ENV 86 | 87 | 88 | it "should accept defaults", -> 89 | environments = 90 | 'common': 91 | foo: 'boo' 92 | 'development': 93 | foo: 'bar' 94 | 95 | options = 96 | globalKey: '$settings' 97 | 98 | defaults: 99 | framework: 100 | views: 'foo/views' 101 | models: 'foo/models' 102 | 103 | settings = new Settings(environments, options)._obj._useEnvironment('development') 104 | assert.equal 'foo/views', settings.framework.views 105 | 106 | 107 | it "should be overriden from file", -> 108 | settings = instance()._obj._useEnvironment('test') 109 | assert.equal 'server_test', settings.storage.database 110 | assert.equal 'localhost', settings.storage.host 111 | settings._obj.override __dirname + "/config/override" 112 | assert.equal 'server_test', settings.storage.database 113 | assert.equal 'override', settings.storage.host 114 | settings._obj.override __dirname + "/config/override2" 115 | assert.equal 'server_test', settings.storage.database 116 | assert.equal 'override2', settings.storage.host 117 | 118 | 119 | it "should override with root", -> 120 | settings = new Settings(__dirname + '/config/environment', env: 'test') 121 | assert.equal 'server_test', settings.storage.database 122 | assert.equal 'localhost', settings.storage.host 123 | settings._obj.override __dirname + "/config/overrideRoot", 'fubar' 124 | assert.equal 'server_test', settings.storage.database 125 | assert.equal 'override', settings.storage.host 126 | settings._obj.override __dirname + "/config/overrideRoot2", 'bar' 127 | assert.equal 'server_test', settings.storage.database 128 | assert.equal 'override2', settings.storage.host 129 | 130 | it "should handle regex literals", -> 131 | settings = new Settings(__dirname + '/config/environment') 132 | assert.ok /asdf/ instanceof RegExp 133 | assert.ok settings.re instanceof RegExp 134 | 135 | -------------------------------------------------------------------------------- /support/extend.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery.extend extracted from the jQuery source & optimised for NodeJS 3 | Twitter: @FGRibreau / fgribreau.com 4 | 5 | Usage: 6 | var Extend = require('./Extend'); 7 | 8 | 9 | // Extend 10 | var obj = Extend({opt1:true, opt2:true}, {opt1:false}); 11 | 12 | // Deep Copy 13 | var clonedObject = Extend(true, {}, myObject); 14 | var clonedArray = Extend(true, [], ['a',['b','c',['d']]]); 15 | */ 16 | var toString = Object.prototype.toString, 17 | hasOwn = Object.prototype.hasOwnProperty, 18 | push = Array.prototype.push, 19 | slice = Array.prototype.slice, 20 | trim = String.prototype.trim, 21 | indexOf = Array.prototype.indexOf, 22 | 23 | // [[Class]] -> type pairs 24 | class2type = {}; 25 | 26 | // Populate the class2type map 27 | "Boolean Number String Function Array Date RegExp Object".split(" ").forEach(function(name) { 28 | class2type[ "[object " + name + "]" ] = name.toLowerCase(); 29 | }); 30 | 31 | function type(obj){ 32 | return obj == null ? 33 | String( obj ) : 34 | class2type[ toString.call(obj) ] || "object"; 35 | } 36 | 37 | function isPlainObject( obj ) { 38 | if ( !obj || type(obj) !== "object") { 39 | return false; 40 | } 41 | 42 | // Not own constructor property must be Object 43 | if ( obj.constructor && 44 | !hasOwn.call(obj, "constructor") && 45 | !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { 46 | return false; 47 | } 48 | 49 | // Own properties are enumerated firstly, so to speed up, 50 | // if last one is own, then all properties are own. 51 | 52 | var key; 53 | for ( key in obj ) {} 54 | 55 | return key === undefined || hasOwn.call( obj, key ); 56 | } 57 | 58 | function extend(){ 59 | var options, name, src, copy, copyIsArray, clone, 60 | target = arguments[0] || {}, 61 | i = 1, 62 | length = arguments.length, 63 | deep = false; 64 | 65 | // Handle a deep copy situation 66 | if ( typeof target === "boolean" ) { 67 | deep = target; 68 | target = arguments[1] || {}; 69 | // skip the boolean and the target 70 | i = 2; 71 | } 72 | 73 | // Handle case when target is a string or something (possible in deep copy) 74 | if ( typeof target !== "object" && type(target) !== "function") { 75 | target = {}; 76 | } 77 | 78 | // extend jQuery itself if only one argument is passed 79 | if ( length === i ) { 80 | target = this; 81 | --i; 82 | } 83 | 84 | for ( ; i < length; i++ ) { 85 | // Only deal with non-null/undefined values 86 | if ( (options = arguments[ i ]) != null ) { 87 | // Extend the base object 88 | for ( name in options ) { 89 | src = target[ name ]; 90 | copy = options[ name ]; 91 | 92 | // Prevent never-ending loop 93 | if ( target === copy ) { 94 | continue; 95 | } 96 | 97 | // Recurse if we're merging plain objects or arrays 98 | if ( deep && copy && ( isPlainObject(copy) || (copyIsArray = type(copy) === "array") ) ) { 99 | if ( copyIsArray ) { 100 | copyIsArray = false; 101 | clone = src && type(src) === "array" ? src : []; 102 | 103 | } else { 104 | clone = src && isPlainObject(src) ? src : {}; 105 | } 106 | 107 | // Never move original objects, clone them 108 | target[ name ] = extend( deep, clone, copy ); 109 | 110 | // Don't bring in undefined values 111 | } else if ( copy !== undefined ) { 112 | target[ name ] = copy; 113 | } 114 | } 115 | } 116 | } 117 | 118 | // Return the modified object 119 | return target; 120 | }; 121 | 122 | 123 | 124 | /** 125 | * Extends `target` with properties from `source`. 126 | */ 127 | exports.extend = function deepExtend(target, source){ 128 | return extend(true, target, source); 129 | }; 130 | 131 | 132 | exports.shallowExtend = function(target, source) { 133 | return extend(target, source); 134 | }; 135 | 136 | 137 | exports.clone = function(o){ 138 | var result; 139 | 140 | if (isPlainObject(o)) { 141 | result = extend(true, {}, o); 142 | } 143 | if (type(o) == "array") { 144 | result = extend(true, [], o); 145 | } 146 | return result; 147 | }; 148 | 149 | 150 | /** 151 | * Deep clones `target`, then deep extends the clone with `source`. 152 | */ 153 | exports.cloneExtend = function(target, source) { 154 | var result = exports.clone(target); 155 | return exports.extend(result, source); 156 | } 157 | 158 | 159 | exports.shallowClone = function(o) { 160 | if (isPlainObject(o)) { 161 | o = extend(false, {}, o); 162 | } 163 | if (type(o) == "array") { 164 | o = extend(false, [], o); 165 | } 166 | return o; 167 | } 168 | -------------------------------------------------------------------------------- /test/config/environment.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | exports.common = { 3 | storage: { 4 | host: 'localhost', 5 | database: 'server_dev', 6 | user: 'qirogami_user', 7 | password: 'password' 8 | }, 9 | re: /^foo$/ 10 | }; 11 | 12 | exports.development = {}; 13 | 14 | exports.test = { 15 | storage: { 16 | database: 'server_test' 17 | } 18 | }; 19 | 20 | exports.production = { 21 | storage: { 22 | database: 'server_production' 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /test/config/override.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | module.exports = { 3 | storage: { 4 | host: 'override' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /test/config/override2.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | exports.common = { 3 | storage: { 4 | host: 'override2' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /test/config/overrideRoot.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | module.exports = { 3 | fubar: { 4 | storage: { 5 | host: 'override' 6 | } 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /test/config/overrideRoot2.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | exports.common = { 3 | bar: { 4 | storage: { 5 | host: 'override2' 6 | } 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /test/settingsTest.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | var Settings, assert, instance; 3 | 4 | assert = require("chai").assert; 5 | 6 | Settings = require("../lib/settings"); 7 | 8 | instance = function() { 9 | return new Settings(__dirname + "/config/environment"); 10 | }; 11 | 12 | describe("Settings", function() { 13 | it("should get specific environment", function() { 14 | var settings; 15 | 16 | settings = instance()._obj._useEnvironment('development'); 17 | assert.equal('server_dev', settings.storage.database); 18 | settings = instance()._obj._useEnvironment(); 19 | assert.equal('server_dev', settings.storage.database); 20 | settings = instance()._obj._useEnvironment('test'); 21 | assert.equal('server_test', settings.storage.database); 22 | settings = instance()._obj._useEnvironment('production'); 23 | return assert.equal('server_production', settings.storage.database); 24 | }); 25 | it("should get value from ancestor if key is not found", function() { 26 | var settings; 27 | 28 | settings = instance()._obj._useEnvironment('test'); 29 | return assert.equal('password', settings.storage.password); 30 | }); 31 | it("should have a forceEnv property to force all settings through an environment", function() { 32 | var environments, set, settings; 33 | 34 | environments = { 35 | 'common': { 36 | foo: 'boo' 37 | }, 38 | 'development': { 39 | foo: 'bar' 40 | }, 41 | 'test': { 42 | foo: 'bah' 43 | }, 44 | 'prod': { 45 | foo: 'baz' 46 | }, 47 | forceEnv: 'development' 48 | }; 49 | settings = new Settings(environments); 50 | set = settings._obj._useEnvironment('development'); 51 | assert.equal('bar', set.foo); 52 | set = settings._obj._useEnvironment('test'); 53 | assert.equal('bar', set.foo); 54 | set = settings._obj._useEnvironment('prod'); 55 | return assert.equal('bar', set.foo); 56 | }); 57 | it("should replace array values, not merge them", function() { 58 | var environments, settings; 59 | 60 | environments = { 61 | 'common': { 62 | arr: [1, 2, 3] 63 | }, 64 | 'development': { 65 | arr: [4, 5, 6] 66 | } 67 | }; 68 | settings = new Settings(environments)._obj._useEnvironment('development'); 69 | return assert.deepEqual([4, 5, 6], settings.arr); 70 | }); 71 | it("should do a deep merge", function() { 72 | var environments, settings; 73 | 74 | environments = { 75 | 'common': { 76 | a: { 77 | b: { 78 | c: { 79 | arr: [1, 2, 3], 80 | bah: 'baz' 81 | }, 82 | bar: 'bar' 83 | } 84 | } 85 | }, 86 | 'development': { 87 | a: { 88 | b: { 89 | c: { 90 | arr: [4, 5, 6], 91 | fu: 'bot' 92 | } 93 | } 94 | } 95 | } 96 | }; 97 | settings = new Settings(environments)._obj._useEnvironment('development'); 98 | assert.deepEqual([4, 5, 6], settings.a.b.c.arr); 99 | assert.deepEqual('baz', settings.a.b.c.bah); 100 | assert.deepEqual('bar', settings.a.b.bar); 101 | return assert.deepEqual('bot', settings.a.b.c.fu); 102 | }); 103 | it("should say which environment is current", function() { 104 | var settings; 105 | 106 | settings = instance(); 107 | assert.equal('common', settings.ENV); 108 | settings = new Settings(__dirname + '/config/environment', { 109 | env: 'production' 110 | }); 111 | assert.equal('production', settings.ENV); 112 | settings = instance()._obj._useEnvironment('test'); 113 | return assert.equal('test', settings.ENV); 114 | }); 115 | it("should accept defaults", function() { 116 | var environments, options, settings; 117 | 118 | environments = { 119 | 'common': { 120 | foo: 'boo' 121 | }, 122 | 'development': { 123 | foo: 'bar' 124 | } 125 | }; 126 | options = { 127 | globalKey: '$settings', 128 | defaults: { 129 | framework: { 130 | views: 'foo/views', 131 | models: 'foo/models' 132 | } 133 | } 134 | }; 135 | settings = new Settings(environments, options)._obj._useEnvironment('development'); 136 | return assert.equal('foo/views', settings.framework.views); 137 | }); 138 | it("should be overriden from file", function() { 139 | var settings; 140 | 141 | settings = instance()._obj._useEnvironment('test'); 142 | assert.equal('server_test', settings.storage.database); 143 | assert.equal('localhost', settings.storage.host); 144 | settings._obj.override(__dirname + "/config/override"); 145 | assert.equal('server_test', settings.storage.database); 146 | assert.equal('override', settings.storage.host); 147 | settings._obj.override(__dirname + "/config/override2"); 148 | assert.equal('server_test', settings.storage.database); 149 | return assert.equal('override2', settings.storage.host); 150 | }); 151 | it("should override with root", function() { 152 | var settings; 153 | 154 | settings = new Settings(__dirname + '/config/environment', { 155 | env: 'test' 156 | }); 157 | assert.equal('server_test', settings.storage.database); 158 | assert.equal('localhost', settings.storage.host); 159 | settings._obj.override(__dirname + "/config/overrideRoot", 'fubar'); 160 | assert.equal('server_test', settings.storage.database); 161 | assert.equal('override', settings.storage.host); 162 | settings._obj.override(__dirname + "/config/overrideRoot2", 'bar'); 163 | assert.equal('server_test', settings.storage.database); 164 | return assert.equal('override2', settings.storage.host); 165 | }); 166 | return it("should handle regex literals", function() { 167 | var settings; 168 | 169 | settings = new Settings(__dirname + '/config/environment'); 170 | assert.ok(/asdf/ instanceof RegExp); 171 | return assert.ok(settings.re instanceof RegExp); 172 | }); 173 | }); 174 | --------------------------------------------------------------------------------