使用Lodash从JSON对象获取特定于环境的configuration

鉴于我有以下的JSON对象,

dbConfig = { "db": "default", "default": { "defaultDB": "sqlite", "init": "init", "migrations": { "directory": "migrations", "tableName": "migrations" }, "pool": { "min": "2", "max": "10" }, "sqlite": { "client": "sqlite3", "connection": { "filename": "data/default/sqlitedb/test.db" } }, "oracle": { "client": "oracledb", "config": { "development": { "user": "test", "pass": "test", "db": "test" }, "production": { "user": "test", "pass": "test", "db": "test" }, "test": { "user": "test", "pass": "test", "db": "test" } } } } }; 

使用Node& Lodash ,有没有可能获得connectionconfig. 取决于dbConfig.default[dbConfig.default.defaultDB]设置为什么。

所以,例如,如果我设置dbConfig.default.defaultDB=oracledbprocess.env.NODE_ENV=development我想能够得到dbConfig.default[dbConfig.default.defaultDB].config.development

或者,如果我设置dbConfig.default.defaultDB=sqlite只是为了获取dbConfig.default[dbConfig.default.defaultDB].connection

换句话说,如果数据库具有环境特定的configuration,那么这将在"config": {} ,如果不是"connection": {}

它不一定是Lodash。 它也可以是纯javascript。

解决scheme没有lodash

 var defaultDbName = dbConfig.default[dbConfig.default.defaultDB]; var db; if (defaultDb === 'sqllite') { db = dbConfig.default[defaultDb].connection; } else { var env = process.env.NODE_ENV; db = dbConfig.default[defaultDb].config[env]; } 

lodash解决scheme

在这里,我使用lodash get函数获取对象字段值,如果不存在,则为null 。 另外,我正在使用模板string语法: ${val}来格式化字段path。

 var defaultDbName = dbConfig.default[dbConfig.default.defaultDB]; var defaultDbConf = dbConfig.default[defaultDb]; var env = process.env.NODE_ENV; var db = defaultDbConf.connection || _.get(defaultDbConf, `config.${env}`); 

顺便说一句,你的configurationjson太复杂了,每个环境configuration好得多。

解决scheme没有[依赖关系] (最初在这里回答,但不是AngularJS专用)

你的JSON是复杂的,但是,它也可以更小,更可读,没有所有的重复,每个环境具有相同的一组属性,可能会或可能不会有所不同,并会不必要地重复。

通过一个简单的algorithm (jsFiddle),您可以dynamic地parsing您的JSONconfiguration以获取特定的属性名称后缀( property @ suffix ),并且具有环境变化属性以及非变化属性的目录,而不会人为地构造您的configuration并且不会重复深度嵌套的configuration对象。

您还可以混合使用后缀,并结合任意数量的环境因素或其他任意因素来修改您的configuration对象。

例如,预处理的JSONconfiguration片段:

 var config = { 'help': { 'BLURB': 'This pre-production environment is not supported. Contact Development Team with questions.', 'PHONE': '808-867-5309', 'EMAIL': 'coder.jen@lostnumber.com' }, 'help@www.productionwebsite.com': { 'BLURB': 'Please contact Customer Service Center', 'BLURB@fr': 'S\'il vous plaît communiquer avec notre Centre de service à la clientèle', 'BLURB@de': 'Bitte kontaktieren Sie unseren Kundendienst!!1!', 'PHONE': '1-800-CUS-TOMR', 'EMAIL': 'customer.service@productionwebsite.com' }, } 

…和后处理(给定location.hostname =' http://www.productionwebsite.com '和' de '的navigator.language):

 prefer(config,['www.productionwebsite.com','de']); // prefer(obj,string|Array<string>) JSON.stringify(config); // { 'help': { 'BLURB': 'Bitte kontaktieren Sie unseren Kundendienst!!1!', 'PHONE': '1-800-CUS-TOMR', 'EMAIL': 'customer.service@productionwebsite.com' } } 

显然你可以在渲染时使用location.hostname和window.navigator.language来获取这些值。 处理JSON本身的algorithm不是非常复杂(但由于某种原因,您可能仍然对整个框架感到更舒适,而不是单个函数):

 function prefer(obj,suf) { function pr(o,s) { for (var p in o) { if (!o.hasOwnProperty(p) || !p.split('@')[1] || p.split('@@')[1] ) continue; // ignore: proto-prop OR not-suffixed OR temp prop score var b = p.split('@')[0]; // base prop name if(!!!o['@@'+b]) o['@@'+b] = 0; // +score placeholder var ps = p.split('@')[1].split('&'); // array of property suffixes var sc = 0; var v = 0; // reset (running)score and value while(ps.length) { // suffix value: index(of found suffix in prefs)^10 v = Math.floor(Math.pow(10,s.indexOf(ps.pop()))); if(!v) { sc = 0; break; } // found suf NOT in prefs, zero score (delete later) sc += v; } if(sc > o['@@'+b]) { o['@@'+b] = sc; o[b] = o[p]; } // hi-score! promote to base prop delete o[p]; } for (var p in o) if(p.split('@@')[1]) delete o[p]; // remove scores for (var p in o) if(typeof o[p] === 'object') pr(o[p],s); // recurse surviving objs } if( typeof obj !== 'object' ) return; // validate suf = ( (suf || suf === 0 ) && ( suf.length || suf === parseFloat(suf) ) ? suf.toString().split(',') : []); // array|string|number|comma-separated-string -> array-of-strings pr(obj,suf.reverse()); } 

属性名称后缀可以在'@'之后有任何数量的后缀,用'&'(&符号)分隔,如果有两个属性具有不同的但是优选的后缀,那么它们将会按照传递给function。 包含两个首选string的后缀将优先于其他所有string。 在JSON中find的未指定为优先的后缀将被丢弃。

优先/歧视将自上而下地应用于您的对象树上,如果高级对象存活下来,则会随后检查它们的首选后缀。

通过这种方法, 你的JSON (我正在做一些关于哪些属性在你的环境之间有所不同的假设),可以简化如下:

 dbConfig = { "pool": { "min": "2", "max": "10" }, "init": "init", "migrations": { "directory": "migrations", "tableName": "migrations" }, "db": "client": "sqlite", "filename": "data/default/sqlitedb/development.db" "filename@tst": "data/default/sqlitedb/test.db" "filename@prd": "data/default/sqlitedb/production.db" }, "db@oracle": { "client": "oracle", "user": "devuser", "user@tst": "testdbuser", "user@prd": "testdbuser", "pass": "devpass", "pass@tst": "testdbpass", "pass@prd": "testdbpass", "db": "devdb", "db@tst": "testdbschema", "db@prd": "testdbschema" } }; 

所以,你可以用这些args +结果把它提供给prefer()函数:

对于sqlite,testing环境:

 prefer(dbConfig,'tst'); JSON.stringify(dbConfig); // dbConfig: { "pool": { "min": "2", "max": "10" }, "init": "init", "migrations": { "directory": "migrations", "tableName": "migrations" }, "db": { "client": "sqlite", "filename": "data/default/sqlitedb/test.db" } }; 

对于oracle,默认/开发环境:

 prefer(dbConfig,'oracle'); // oracle, dev(default) env JSON.stringify(dbConfig); // dbConfig: { "pool": { "min": "2", "max": "10" }, "init": "init", "migrations": { "directory": "migrations", "tableName": "migrations" }, "db": { "client": "oracle", "user": "devdbuser", "pass": "devdbpass", "db": "devdbschema" } }; prefer(dbConfig,'oracle,prd'); // oracle, production env JSON.stringify(dbConfig); // dbConfig: { "pool": { "min": "2", "max": "10" }, "init": "init", "migrations": { "directory": "migrations", "tableName": "migrations" }, "db": { "client": "oracle", "user": "prddbuser", "pass": "prddbpass", "db": "prddbschema" } }; 

摘要用法和示例:

 var o = { 'a':'apple', 'a@dev':'apple-dev', 'a@fr':'pomme', 'b':'banana', 'b@fr':'banane', 'b@dev&fr':'banane-dev', 'c':{ 'o':'c-dot-oh', 'o@fr':'c-point-oh' }, 'c@dev': { 'o':'c-dot-oh-dev', 'o@fr':'c-point-oh-dev' } }; /*1*/ prefer(o,'dev'); // { a:'apple-dev', b:'banana', c:{o:'c-dot-oh-dev'} } /*2*/ prefer(o,'fr'); // { a:'pomme', b:'banane', c:{o:'c-point-oh'} } /*3*/ prefer(o,'dev,fr'); // { a:'apple-dev', b:'banane-dev', c:{o:'c-point-oh-dev'} } /*4*/ prefer(o,['fr','dev']); // { a:'pomme', b:'banane-dev', c:{o:'c-point-oh-dev'} } /*5*/ prefer(o); // { a:'apple', b:'banana', c:{o:'c-dot-oh'} } 

警告在属性名称中使用@不是标准的,并且在点符号中是无效的,但到目前为止还没有打破我们testing过的任何浏览器。这样做的好处是,它防止开发人员期望他们可以引用您的预处理后缀属性。 开发人员必须注意并且有点不合常规,并将你的属性引用为一个string(obj ['key @ suf'])来做到这一点,顺便说一下,这个function是可能的。

如果未来的JavaScript引擎拒绝它,替代任何其他可容忍的惯例,只是一致的。 该algorithm尚未针对性能进行性能分析,或严格testing其他潜在问题。 以目前的forms,一次性启动/加载,我们还没有遇到问题。 一如既往,YMMV。