Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Globbing includes #1

Merged
merged 1 commit into from

3 participants

@timoxley

This got a bit more tricky than anticipated, but it works ok.

3 systems:

// merge all items matching glob
config: "include config/*" 

// collect as map, keyed by filename (note use of '{}' )
config: "include {config/*}"
// e.g.
{
  database: { 
    // db settings from config/database.json 
  },
  app: { 
    // app settings from config/app.json 
  }
}

// collect as an array, (note use of '[]' )
users: "include [users/*]" 
// e.g.
{
  users: [
      {user: 'from users/dave.json'}
      {user: 'from users/barry.json'}
      {user: 'from users/joe.json'}
  ]
}
@timoxley

The readme could probably do with some work, let me know what you think.

@3rd-Eden
Collaborator

Do want

@timoxley

@visionmedia updated this to work with your latest master. Also refactored the code a bit as previously it looked like the ravings of a madman, high on mapreduce.

@timoxley

bump

@tj tj merged commit 0cc29c0 into tj:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 8, 2014
  1. @timoxley
This page is out of date. Refresh to see the latest.
View
84 Readme.md
@@ -105,6 +105,90 @@ yields:
{ prod: { whatever: 'is', within: 'config/production.json' }}
```
+ You can also include multiple files via a glob. This has a special syntax and works in one of three ways.
+
+Consider a config folder containing the following two files:
+
+*database.json:*
+```json
+{"db", "redis"}
+```
+*app.json:*
+```json
+{"listen", 3000}
+```
+
+##### Merging multiple files into one:
+
+```js
+
+eson()
+ .use(eson.include)
+ .parse('{ "prod": "include config/*" }');
+```
+yields:
+
+```js
+{
+ prod: {
+ db: "redis",
+ listen: 3000
+ }
+}
+```
+
+##### Collect files into a map, keyed by filename:
+
+```js
+
+// use curly brackets to collect as a map
+eson()
+ .use(eson.include)
+ .parse('{ "prod": "include { config/* }" }');
+
+```
+
+yields:
+
+
+```js
+{
+ prod: {
+ database: {
+ db: "redis"
+ },
+ app: {
+ listen: 3000
+ }
+ }
+}
+
+```
+
+##### Collect files as an array:
+
+
+```js
+
+// use square brackets to collect as an array
+eson()
+ .use(eson.include)
+ .parse('{ "prod": "include [ config/* ]" }');
+
+```
+
+yields:
+
+```js
+{
+ prod: [
+ {db: "redis"},
+ {listen: 3000}
+ ]
+}
+```
+
+
### eson.bools
Convert "yes", "no", "on", "off", "enabled", "disabled" into booleans.
View
62 lib/plugins/include.js
@@ -4,7 +4,8 @@
var path = require('path')
, dirname = path.dirname
- , join = path.join;
+ , join = path.join
+ , glob = require('glob')
/**
* Include another JSON file,
@@ -12,8 +13,61 @@ var path = require('path')
*/
module.exports = function(key, val, parser){
- var m = /^include +(.+)$/.exec(val);
+ // match globbing [] {} syntax or just single file
+ var single = /^include +([^{\[].*)$/.exec(val)
+ var globbing = /^include +([\{\[]?) *(.*?) *([\}\]]?)$/.exec(val);
+ // need to match against single first
+ var m = single ? single : globbing
if (!m) return;
- var path = join(dirname(parser.path), m[1] + '.json');
- return parser.clone().read(path);
+
+ var brace = "";
+ var relativePath = m[1];
+
+ if (!single) {
+ // try to match braces
+ if ((m[1] == "" && m[3] != "") ||
+ (m[1] != "" && m[1].charCodeAt(0) + 2 !== m[3].charCodeAt(0))) {
+ return; // braces don't match
+ }
+ brace = m[1];
+ relativePath = m[2];
+ }
+
+ relativePath += '.json';
+ var filePaths = glob.sync(join(dirname(parser.path), relativePath));
+ if (brace === "") return mergeFiles(parser, filePaths);
+ if (brace === "{") return filesAsMap(parser, filePaths);
+ if (brace === "[") return filesAsArray(parser, filePaths);
+ // shouldn't get here
+ throw new Error('Error: ' + brace);
+}
+
+// Break files into rows of key/value pairs where key is the filename
+// minus extension & value is file content
+function filesAsMap(parser, paths) {
+ var map = {};
+ paths.forEach(function(filePath) {
+ var key = path.basename(filePath, path.extname(filePath));
+ map[key] = parser.clone().read(filePath);
+ })
+ return map;
+}
+
+// Contents of each file become values in array
+function filesAsArray(parser, paths) {
+ return paths.map(function(filePath) {
+ return parser.clone().read(filePath);
+ })
+}
+
+// Merges each file's keys together as single result
+function mergeFiles(parser, paths) {
+ return paths.map(function(filePath) {
+ return parser.clone().read(filePath);
+ }).reduce(function(previous, data) {
+ for (var key in data) {
+ previous[key] = data[key];
+ }
+ return previous;
+ })
}
View
1  test/fixtures/config/database.json
@@ -0,0 +1 @@
+{"db": "redis"}
View
1  test/fixtures/config/http.json
@@ -0,0 +1 @@
+{"listen": 8000}
View
5 test/fixtures/include_glob.json
@@ -0,0 +1,5 @@
+{
+ "app-config": "include config/{database,http}",
+ "user-config": "include { config/{permissions,roles}}",
+ "users": "include [ users/* ]"
+}
View
1  test/fixtures/users/dave.json
@@ -0,0 +1 @@
+{"username": "Dave"}
View
1  test/fixtures/users/tobi.json
@@ -0,0 +1 @@
+{"username": "Tobi"}
View
12 test/include.js
@@ -16,4 +16,14 @@ describe('include', function(){
["admin", "guest"]
]);
})
-})
+ it('should parse files matching glob', function() {
+ Parser()
+ .use(include)
+ .read('test/fixtures/include_glob.json')
+ .should.eql({
+ "app-config": { "db": "redis", "listen": 8000 },
+ "user-config": { "permissions": { "view videos": "guest", "delete videos": "admin" }, "roles": ["admin", "guest"] },
+ "users": [ {"username": "Dave"}, {"username": "Tobi"} ]
+ })
+ })
+})
Something went wrong with that request. Please try again.