Comparing version 0.0.3 to 0.1.0
@@ -8,12 +8,48 @@ "use strict"; | ||
ep.create (ep.next (), "NOT_A_RESOURCE", "The resource is not a file or " + | ||
"directory", { path: "{path}" }); | ||
ep.create (ep.next (), "RESOURCE_NOT_FOUND", "The resource does not exist", | ||
{ path: "{path}" }); | ||
ep.create (ep.next (), "INVALID_JSON", "{error}", { path: "{path}" }); | ||
if (!path.sep) path.sep = process.platform === "win32" ? "\\" : "/"; | ||
var gb = module.exports = {}; | ||
var resources = {}; | ||
var types = {}; | ||
var custom = 0; | ||
gb.create = function (name){ | ||
return new GrabBag (name); | ||
}; | ||
gb.define = function (extensions, io){ | ||
var type = { | ||
reader: io.reader, | ||
writer: io.writer | ||
}; | ||
gb.types["CUSTOM" + (custom++)] = type; | ||
extensions.forEach (function (ext){ | ||
supportedExtensions[ext] = type; | ||
}); | ||
}; | ||
var removeType = function (type){ | ||
for (var ext in supportedExtensions){ | ||
if (supportedExtensions[ext] === type) return; | ||
} | ||
for (var t in gb.types){ | ||
if (gb.types[t] === type){ | ||
delete gb.types[t]; | ||
return; | ||
} | ||
} | ||
}; | ||
gb.remove = function (extensions){ | ||
extensions.forEach (function (ext){ | ||
var type = supportedExtensions[ext]; | ||
if (!type) return; | ||
delete supportedExtensions[ext]; | ||
removeType (type); | ||
}); | ||
}; | ||
gb.types = { | ||
@@ -81,3 +117,3 @@ PROPERTIES: { | ||
gb.extensions = { | ||
var supportedExtensions = { | ||
"properties": gb.types.PROPERTIES, | ||
@@ -89,39 +125,12 @@ "ini": gb.types.INI, | ||
var removeType = function (type){ | ||
if (type === gb.types.PROPERTIES || type === gb.types.JSON || | ||
type === gb.types.JS) return; | ||
for (var ext in gb.extensions){ | ||
if (gb.extensions[ext] === type) return; | ||
} | ||
for (var t in gb.types){ | ||
if (gb.types[t] === type){ | ||
delete gb.types[t]; | ||
return; | ||
} | ||
} | ||
var GrabBag = function (name){ | ||
this._name = name; | ||
this._resources = {}; | ||
this._types = {}; | ||
this._ignored = {}; | ||
}; | ||
gb.define = function (extensions, reader, writer){ | ||
if (arguments.length === 1){ | ||
extensions.forEach (function (ext){ | ||
var type = gb.extensions[ext]; | ||
delete gb.extensions[ext]; | ||
removeType (type); | ||
}); | ||
return; | ||
} | ||
var type = { | ||
reader: reader, | ||
writer: writer | ||
}; | ||
gb.types["CUSTOM" + (custom++)] = type; | ||
extensions.forEach (function (ext){ | ||
gb.extensions[ext] = type; | ||
}); | ||
}; | ||
gb.get = function (p){ | ||
if (!p) return resources; | ||
var holder = resources; | ||
GrabBag.prototype.get = function (p){ | ||
if (!p) return this._resources; | ||
var holder = this._resources; | ||
var paths = p.split (/\\|\//); | ||
@@ -137,17 +146,39 @@ for (var i=0, len=paths.length; i<len; i++){ | ||
var loadFile = function (file, cb){ | ||
GrabBag.prototype.ignore = function (p){ | ||
if (!Array.isArray (p)){ | ||
p = [p]; | ||
} | ||
var me = this; | ||
p.forEach (function (p){ | ||
me._ignored[p.replace (/\/|\\/g, path.sep)] = null; | ||
}); | ||
}; | ||
var loadFile = function (file, type, cb){ | ||
var extname = path.extname (file); | ||
if (extname[0] === ".") extname = extname.substring (1); | ||
if (extname in gb.extensions){ | ||
var type = gb.extensions[extname]; | ||
type.reader (file, extname, function (error, data){ | ||
if (error) return cb (error, null, null); | ||
cb (null, data, type); | ||
}); | ||
}else{ | ||
cb (null, null, null); | ||
if (!type){ | ||
if (extname in supportedExtensions){ | ||
type = supportedExtensions[extname]; | ||
}else{ | ||
return cb (null, null, null); | ||
} | ||
} | ||
if (!type.reader) return cb (null, null, null); | ||
type.reader (file, extname, function (error, data){ | ||
if (error) return cb (error, null, null); | ||
cb (null, data, type); | ||
}); | ||
}; | ||
gb.load = function (p, cb){ | ||
GrabBag.prototype._isIgnored = function (p){ | ||
return p.replace (/\/|\\/g, path.sep) in this._ignored; | ||
} | ||
GrabBag.prototype.load = function (p, type, cb){ | ||
if (arguments.length === 2){ | ||
cb = type; | ||
type = null; | ||
} | ||
var load = function (p, resHolder, typeHolder, cb){ | ||
@@ -161,2 +192,4 @@ var exit = function (error){ | ||
if (me._isIgnored (p)) return exit (); | ||
fs.stat (p, function (error, stats){ | ||
@@ -166,3 +199,3 @@ if (error) return exit (error); | ||
if (stats.isFile ()){ | ||
loadFile (p, function (error, data, type){ | ||
loadFile (p, type, function (error, data, type){ | ||
if (error) return exit (error); | ||
@@ -175,3 +208,4 @@ if (type === null) return exit (); | ||
resHolder[basename] = data; | ||
typeHolder[basename] = { type: type, path: p, extension: extname }; | ||
typeHolder[basename] = { type: type, path: p, extension: extname, | ||
set: false }; | ||
@@ -202,3 +236,3 @@ exit (); | ||
}else{ | ||
exit (ep.get ("NOT_A_RESOURCE", { path: p })); | ||
exit (); | ||
} | ||
@@ -221,8 +255,96 @@ }); | ||
p.forEach (function (res){ | ||
load (res, resources, types, finish); | ||
var me = this; | ||
p.forEach (function (p){ | ||
load (p, me._resources, me._types, finish); | ||
}); | ||
}; | ||
gb.store = function (p, cb){ | ||
GrabBag.prototype.name = function (){ | ||
return this._name; | ||
}; | ||
GrabBag.prototype.remove = function (p){ | ||
if (!p){ | ||
this._resources = {}; | ||
this._types = {}; | ||
return; | ||
} | ||
if (!Array.isArray (p)){ | ||
p = [p]; | ||
} | ||
var me = this; | ||
p.forEach (function (p){ | ||
var resHolder = me._resources; | ||
var resType = me._types; | ||
var paths = p.split (/\\|\//); | ||
for (var i=0, len=paths.length; i<len-1; i++){ | ||
resHolder = resHolder[paths[i]]; | ||
resType = resType[paths[i]]; | ||
if (resHolder === undefined){ | ||
return null; | ||
} | ||
} | ||
var name = paths[paths.length - 1]; | ||
delete resHolder[name]; | ||
delete resType[name]; | ||
}); | ||
}; | ||
GrabBag.prototype._createHolders = function (p){ | ||
var parent = function (p, res, typ){ | ||
if (p === ".") return { res: res, typ: typ }; | ||
var h = parent (path.dirname (p), res, typ); | ||
var name = path.basename (p); | ||
var hres = h.res[name]; | ||
var htyp = h.typ[name]; | ||
if (hres === undefined){ | ||
hres = {}; | ||
h.res[name] = hres; | ||
htyp = {}; | ||
h.typ[name] = htyp; | ||
} | ||
return { res: hres, typ: htyp }; | ||
}; | ||
return parent (path.dirname (p), this._resources, this._types); | ||
}; | ||
GrabBag.prototype.set = function (p, obj, type){ | ||
var extname = path.extname (p); | ||
if (extname[0] === ".") extname = extname.substring (1); | ||
if (!type){ | ||
if (extname in supportedExtensions){ | ||
type = supportedExtensions[extname]; | ||
}else{ | ||
return; | ||
} | ||
} | ||
var h = this._createHolders (p); | ||
var name = path.basename (p); | ||
h.res[name] = obj; | ||
h.typ[name] = { type: type, path: p, extension: extname, set: true }; | ||
}; | ||
var mkdirs = function (p, set, cb){ | ||
if (p === "." || !set) return cb (null); | ||
fs.mkdir (p, function (error){ | ||
if (error && error.code !== "ENOENT" && error.code !== "EEXIST"){ | ||
return cb (error); | ||
}else if (error && error.code === "EEXIST"){ | ||
return cb (null); | ||
}else if (!error){ | ||
return cb (null); | ||
} | ||
//ENOENT | ||
mkdirs (path.dirname (p), set, function (error){ | ||
if (error) return cb (error); | ||
fs.mkdir (p, cb); | ||
}); | ||
}); | ||
}; | ||
GrabBag.prototype.store = function (p, cb){ | ||
if (arguments.length === 1){ | ||
@@ -233,10 +355,19 @@ cb = p; | ||
var store = function (resHolder, typeHolder, cb){ | ||
var store = function (p, resHolder, typeHolder, cb){ | ||
if (me._isIgnored (p)) return cb (); | ||
if (typeHolder.type){ | ||
if (typeHolder.type.writer){ | ||
typeHolder.type.writer (typeHolder.path, typeHolder.extension, | ||
resHolder, function (error){ | ||
if (error) errors.push (error); | ||
cb (); | ||
}); | ||
mkdirs (path.dirname (typeHolder.path), typeHolder.set, | ||
function (error){ | ||
if (error){ | ||
errors.push (error); | ||
return cb (); | ||
} | ||
typeHolder.type.writer (typeHolder.path, typeHolder.extension, | ||
resHolder, function (error){ | ||
if (error) errors.push (error); | ||
cb (); | ||
}); | ||
}); | ||
} | ||
@@ -252,12 +383,13 @@ }else{ | ||
for (var r in resHolder){ | ||
store (resHolder[r], typeHolder[r], finish); | ||
store (p === "." ? r : p + path.sep + r, resHolder[r], typeHolder[r], | ||
finish); | ||
} | ||
} | ||
cb (); | ||
}; | ||
var errors = []; | ||
var me = this; | ||
if (!p){ | ||
return store (resources, types, function (){ | ||
return store (".", this._resources, this._types, function (){ | ||
cb (errors.length === 0 ? null : errors); | ||
@@ -278,7 +410,7 @@ }); | ||
p.forEach (function (res){ | ||
var resHolder = resources; | ||
var typeHolder = types; | ||
p.forEach (function (p){ | ||
var resHolder = me._resources; | ||
var typeHolder = me._types; | ||
var paths = res.split (/\\|\//); | ||
var paths = p.split (/\\|\//); | ||
for (var i=0, len=paths.length; i<len; i++){ | ||
@@ -288,8 +420,8 @@ resHolder = resHolder[paths[i]]; | ||
if (resHolder === undefined){ | ||
return cb ([ep.get ("NOT_A_RESOURCE", { path: res })]); | ||
return cb ([ep.get ("RESOURCE_NOT_FOUND", { path: p })]); | ||
} | ||
} | ||
store (resHolder, typeHolder, finish); | ||
store (p, resHolder, typeHolder, finish); | ||
}); | ||
}; |
{ | ||
"name": "grab-bag", | ||
"version": "0.0.3", | ||
"version": "0.1.0", | ||
"description": "Easily loads and stores system resources", | ||
"keywords": ["properties", "json", "configuration", "settings", "resources"], | ||
"keywords": ["properties", "json", "configuration", "settings", "resources", | ||
"namespace", "file"], | ||
"author": { | ||
@@ -7,0 +8,0 @@ "name": "Gabriel Llamas", |
368
README.md
@@ -8,6 +8,13 @@ grab-bag | ||
Version: 0.0.3 | ||
Version: 0.1.0 | ||
This module can be used to ease the loading and storing process for system resources without the need to worry about how they are loaded and stored and how you save them in namespaces. A resource is anything you save in configuration files. | ||
The main goal of this module is to ease the loading and storing process of system resources without the need to worry about how they are loaded and stored and where they are saved into memory, configure the I/O calls once and just load and store. A resource is anything you save in files, typically configuration data. A grab bag, or simply a box, provides a centralized and well organized place that grants to you a better control over your files. | ||
Because encapsulation and abstraction is an art this module is the glue between your application and your configuration files. Useful when you have to load, update and store a lot of files with the minimum dependencies (loosely coupled). | ||
Put it simply, you need to work with localized strings and you need to load, update and store some configuration files. You need to save them somewhere for a later use. You could create a module called "i18n" holding and managing all your language files. That's fine. Furthermore, your application needs to externalize some configuration properties so you could create another module called "conf" trying to encapsulate the way you load and store your files, or simply you could just load and store the configuration properties when you need to do so if encapsulation is not one of your strengths. | ||
Have you thought the format of the properties? You have to decide a format because you need to load and store them to files. Typically you'll use a json, ini or yaml file. Perhaps you don't need a complex format and you simply store the information in different lines. These methods are highly coupled with a lot of dependencies. If you need to change how you load and store the properties there's a big risk to break your code accidentally. With a grab bag you must define once how the files are loaded and stored and then you can abstract from this and just call to [load()](#load) or [store()](#store). | ||
#### Installation #### | ||
@@ -26,7 +33,9 @@ | ||
var box = gb.create ("system"); | ||
//Loads recursively all the files inside conf | ||
gb.load ("conf", function (error){ | ||
if (error) return handleError (error); | ||
box.load ("conf", function (error){ | ||
if (error) return console.log (error); | ||
var conf = gb.get ("conf"); | ||
var conf = box.get ("conf"); | ||
//a.json file | ||
@@ -43,8 +52,8 @@ var a = conf["a.json"]; | ||
//Modifies in-memory a.json and b.properties | ||
//Modifies a.json and b.properties | ||
doSomething (conf); | ||
//Stores all the loaded resources to their files | ||
gb.store (function (error){ | ||
if (error) return handleError (error); | ||
box.store (function (error){ | ||
if (error) return console.log (error); | ||
}); | ||
@@ -56,11 +65,20 @@ }); | ||
- [gb.define(extensions[, reader][, writer])](#define) | ||
- [gb.extensions](#ext) | ||
- [gb.get([resource])](#get) | ||
- [gb.load(resource, callback)](#load) | ||
- [gb.store([resource], callback)](#store) | ||
- [gb.create(name)](#create) | ||
- [gb.define(extensions, io)](#define) | ||
- [gb.remove(extensions)](#remove-gb) | ||
- [gb.types](#types) | ||
- [GrabBag#get([path])](#get) | ||
- [GrabBag#ignore(paths)](#ignore) | ||
- [GrabBag#load(path[, type], callback)](#load) | ||
- [GrabBag#name()](#name) | ||
- [GrabBag#remove([paths])](#remove) | ||
- [GrabBag#set(path, obj[, type])](#set) | ||
- [GrabBag#store([paths], callback)](#store) | ||
<a name="create"></a> | ||
__gb.create([name])__ | ||
Creates a new GrabBag with an optional name. | ||
<a name="define"></a> | ||
__gb.define(extensions[, reader][, writer])__ | ||
__gb.define(extensions, io)__ | ||
Defines a new parser/stringifier for every extension. | ||
@@ -76,7 +94,14 @@ | ||
For example, we need to add support for YAML files. We're going to use the [yaml.js](#https://github.com/jeremyfa/yaml.js) module to parse and stringify properties. Also, we want to parse/stringify files with no extension as INI files. | ||
Default extensions and their associated parser/stringifier are: | ||
- "properties": gb.types.PROPERTIES | ||
- "ini": gb.types.INI | ||
- "json": gb.types.JSON | ||
- "js": gb.types.JS | ||
For example, we need to add support for YAML files. We're going to use the [yaml.js](#https://github.com/jeremyfa/yaml.js) module to parse and stringify properties. Furthermore, we want to parse/stringify files with no extension as INI files. | ||
```javascript | ||
var yaml = require ("yamljs"); | ||
var gb = requir ("grab-bag"); | ||
var gb = require ("grab-bag"); | ||
var fs = require ("fs"); | ||
@@ -100,6 +125,9 @@ | ||
//Defines a new parser/stringifier | ||
gb.define (["yml", "yaml"], reader, writer); | ||
gb.define (["yml", "yaml"], { | ||
reader: reader, | ||
writer: writer | ||
}); | ||
//Uses the buil-in .ini parser/stringifier to read/write files with no extension | ||
gb.define ([""], gb.types.INI.reader, gb.types.INI.writer); | ||
//Uses the buil-in ini parser/stringifier to read/write files with no extension | ||
gb.define ([""], gb.types.INI); | ||
``` | ||
@@ -110,3 +138,5 @@ | ||
```javascript | ||
gb.define (["yml", "yaml"], reader); | ||
gb.define (["yml", "yaml"], { | ||
reader: reader | ||
}); | ||
``` | ||
@@ -132,29 +162,44 @@ | ||
gb.define (["ini"], reader, writer); | ||
gb.define (["ini"], { | ||
reader: reader, | ||
writer: writer | ||
}); | ||
``` | ||
Additionally, you can remove extensions from the set of extensions bound to a parser/stringifier. For example, we don't want to parse/stringify files with extension `js`. Both reader and writer functions must be ignored to remove the extension. | ||
<a name="remove-gb"></a> | ||
__gb.remove(extensions)__ | ||
Removes extensions from the set of extensions bound to a parser/stringifier. For example, we don't want to parse/stringify files with extension `js`. | ||
```javascript | ||
gb.define (["conf"]); | ||
gb.remove (["js"]); | ||
``` | ||
Now, if a file with `conf` extension is found it won't be parsed. | ||
Now, if a file with `js` extension is found it will be ignored. | ||
The reader must be given if a writer is passed, that is, before writing to a file, the data has to be loaded with the reader function. | ||
<a name="types"></a> | ||
__gb.types__ | ||
Contains the default parsers/stringifiers. Every parser/stringifier has a "reader" and "writer" function that are used to load and store properties from disk. | ||
To know what extensions are bound to default parsers/stringifiers, see [gb.ext](#ext) and [gb.types](#types). | ||
- gb.types.PROPERTIES.reader, gb.types.PROPERTIES.writer | ||
- gb.types.INI.reader, gb.types.INI.writer | ||
- gb.types.JSON.reader, gb.types.JSON.writer | ||
- gb.types.JS.reader, gb.types.JS.writer | ||
<a name="ext"></a> | ||
__gb.extensions__ | ||
Contains all the supported extensions and their associated parser/stringifier. By default the .properties parser/stringifier accepts "properties", "ini" and "conf" extensions, the json parser/stringifier, "json", and the JavaScript modules, "js". | ||
The PROPERTIES type uses the [properties](#https://github.com/Gagle/Node-Properties) module with the variables feature enabled. | ||
The INI type uses the [properties](#https://github.com/Gagle/Node-Properties) module with the variables and sections features enabled. | ||
The JSON type uses the built-in json parser/stringifier. | ||
The JS type uses the `require` function to load the file, the script needs to export an object. Take into account that `require` is synchronous and therefore it will block the entire event loop. | ||
- gb.extensions.properties === gb.types.PROPERTIES; | ||
- gb.extensions.ini === gb.types.INI; | ||
- gb.extensions.json === gb.types.JSON; | ||
- gb.extensions.js === gb.types.JS; | ||
The custom parser/stringifier defined with [gb.define()](#define) will be stored here with the name `CUSTOMX`, where `X` is an incremental number that starts at 0. | ||
Default extensions and their associated parser/stringifier are: | ||
- "properties": gb.types.PROPERTIES | ||
- "ini": gb.types.INI | ||
- "json": gb.types.JSON | ||
- "js": gb.types.JS | ||
<a name="get"></a> | ||
__gb.get([resource])__ | ||
Returns the given resource. The "resource" parameter is a path. If no path is passed the function returns all the loaded data: | ||
__GrabBag#get([path])__ | ||
Returns a resource given a path. If no path is given the function returns all the resources: | ||
@@ -202,9 +247,11 @@ ``` | ||
```javascript | ||
gb.load (["a", "e.json"], function (error){ | ||
if (error) return handleError (error); | ||
var box = gb.create (); | ||
box.load (["a", "e.json"], function (error){ | ||
if (error) return console.log (error); | ||
console.log (gb.get ("a/b/b.json").a); //Prints: 2 | ||
console.log (gb.get ().a.b["b.json"].a); //Prints: 2 | ||
console.log (gb.get ("e.json").d); //Prints: 4 | ||
console.log (require ("util").inspect (gb.get (), true, null)); | ||
console.log (box.get ("a/b/b.json").a); //Prints: 2 | ||
console.log (box.get ().a.b["b.json"].a); //Prints: 2 | ||
console.log (box.get ("e.json").d); //Prints: 4 | ||
console.log (require ("util").inspect (box.get (), true, null)); | ||
@@ -239,26 +286,229 @@ /* | ||
<a name="ignore"></a> | ||
__GrabBag#ignore(paths)__ | ||
Ignores the given resources when loading or storing. The "paths" parameter is an array of paths. The paths are relative to the current working directory but they must not begin with `.` or `..`. | ||
```javascript | ||
//This is valid | ||
gb.create ().ignore (["a", "b/c"]); | ||
//This is not valid | ||
gb.create ().ignore (["./a", "../b"]); | ||
``` | ||
For example, we have the following directory and we want to load `f1.json` and `f2.ini`. | ||
``` | ||
./ | ||
a/ | ||
f1.json | ||
f2.ini | ||
f3.properties | ||
``` | ||
We can load indivual files: | ||
```javascript | ||
gb.create ().load (["a/f1.json", "a/f2.ini"], function (error){ | ||
if (error) return console.log (error); | ||
}); | ||
``` | ||
Or we can ignore `f3.properties` and load the entire directory: | ||
```javascript | ||
var box = gb.create (); | ||
box.ignore ("a/f3.properties"); | ||
box.load ("a", function (error){ | ||
if (error) return console.log (error); | ||
}); | ||
``` | ||
<a name="load"></a> | ||
__gb.load(resource, callback)__ | ||
Loads resources into memory. The "resource" parameters can be a string with the path to a file or directory or an array of strings. If an array is passed all the resources are loaded in parallel. If the path points to a directory, the directory is read recursively and all the files found are loaded. The callback with an error parameter is executed on completion. | ||
__GrabBag#load(path[, type], callback)__ | ||
Loads resources into memory. The "path" parameter can be a string with the path to a file or directory or an array of paths. If a path points to a directory, the directory is read recursively and all the sub-directories and supported files are loaded. The callback with an error parameter is executed on completion. See [get()](#get) example. | ||
<a name="store"></a> | ||
__gb.store([resource], callback)__ | ||
Stores resources into their files. The "resource" parameters can be a string with the path to a file or directory or an array of strings. If an array is passed all the resources are stored in parallel. If the path points to a directory, all the resources that has been loaded into memory previously that belongs to this path will be stored recursively, that is, if an in-memory directory is found, all the properties are stored to their files. The callback with an error parameter is executed on completion, if any. If "resource" is not passed, stores all the loaded resources. | ||
How can we load files with no extension without loading other specific files, for example readme files? | ||
<a name="types"></a> | ||
__gb.types__ | ||
Contains the default parsers/stringifiers. Every parser/stringifier has a "reader" and "writer" functions used to parse and store properties. | ||
``` | ||
./ | ||
conf/ | ||
file1 | ||
file2 | ||
README1 | ||
system/ | ||
boot.properties | ||
README2 | ||
README3 | ||
``` | ||
- gb.types.PROPERTIES.reader, gb.types.PROPERTIES.writer | ||
- gb.types.INI.reader, gb.types.INI.writer | ||
- gb.types.JSON.reader, gb.types.JSON.writer | ||
- gb.types.JS.reader, gb.types.JS.writer | ||
Put it simply, define a new type and load both directories: | ||
The PROPERTIES type uses the [properties](#https://github.com/Gagle/Node-Properties) module with the variables feature enabled. | ||
The INI type uses the [properties](#https://github.com/Gagle/Node-Properties) module with the variables and sections features enabled. | ||
The JSON type uses the built-in json parser/stringifier. | ||
The JS type uses the `require` function to load the file, the script file need to export an object. Take into account that `require` is synchronous and therefore it will block the entire event loop. | ||
```javascript | ||
gb.define ([""], gb.types.PROPERTIES); | ||
The custom parser/stringifier defined with [gb.define()](#define) will be stored here with the name `CUSTOMX`, where `X` is an incremental number that starts at 0. | ||
var box = gb.create (); | ||
box.load (["conf", "system"], function (error){ | ||
if (error) return console.log (error); | ||
}); | ||
``` | ||
The extensions that are associated with each parser/stringifier can be found at [gb.ext](#ext). | ||
Here we have a problem because the three README files should not be parsed but because we have included the empty extension as a valid extension, they are parsed. | ||
A good solution is to define the empty extension and load files individually. | ||
```javascript | ||
gb.define ([""], gb.types.PROPERTIES); | ||
var box = gb.create (); | ||
box.load (["conf/file1", "conf/file2", "system/boot.properties"], function (error){ | ||
if (error) return console.log (error); | ||
}); | ||
``` | ||
But this has a problem because if you need to load a lot of files you have to include them in the array. | ||
A better solution consists of using the [ignore()](#ignore) function. Just ignore the files that you don't want to load or store: | ||
```javascript | ||
gb.define ([""], gb.types.PROPERTIES); | ||
var box = gb.create (); | ||
box.ignore (["conf/README1", "system/README2", "system/README3"]); | ||
box.load (["conf", "system"], function (error){ | ||
if (error) return console.log (error); | ||
}); | ||
``` | ||
The optional "type" parameter is the type of the content of the file or files if the path is a directory. This parameter is typically used when you want to load a file that has an extension that is not found in the set of default extensions but you don't want to define a new type because you have multiple files with the same extension but with different format, like the previous scenario. | ||
For example, we want to load a file with a txt extension that has a custom format (line separated values). | ||
``` | ||
//users.txt | ||
Broderick Distilo | ||
Ellsworth Deperte | ||
Willian Garzone | ||
Marcellus Hoysock | ||
Iesha Calvelo | ||
``` | ||
```javascript | ||
var type = { | ||
reader: function (file, extension, cb){ | ||
fs.readFile (file, "utf8", function (error, data){ | ||
if (error) return cb (error, null); | ||
cb (null, data.split (/\r\n|\n/)); | ||
}); | ||
} | ||
}; | ||
var box = gb.create (); | ||
box.load ("users.txt", type, function (error){ | ||
if (error) return console.log (error); | ||
console.log (box.get ()); | ||
/* | ||
Prints: | ||
{ | ||
"users.txt": ["Broderick Distilo", "Ellsworth Deperte", "Willian Garzone", "Marcellus Hoysock", "Iesha Calvelo"] | ||
} | ||
*/ | ||
}); | ||
``` | ||
You can also use a predefined type: | ||
```javascript | ||
//file will be parsed as a .properties file | ||
var box = gb.create (); | ||
box.load ("file", gb.types.PROPERTIES, function (error){ | ||
if (error) return console.log (error); | ||
}); | ||
``` | ||
<a name="name"></a> | ||
__GrabBag#name()__ | ||
Returns the name of the grab bag. | ||
<a name="remove"></a> | ||
__GrabBag#remove([paths])__ | ||
Removes a resource or resources if the path points to a directory. The "paths" parameter can be a string or an array of paths. Take into account that the resource (JavaScript object) won't be freed if you have a reference pointing to it. In fact, this function calls to `delete` to remove the resource. Be aware of this if you don't want memory leaks. | ||
``` | ||
./ | ||
a/ | ||
a.json | ||
b/ | ||
b.json | ||
c/ | ||
c.properties | ||
d/ | ||
e.json | ||
``` | ||
``` | ||
//e.json | ||
{ | ||
"d": 4 | ||
} | ||
``` | ||
```javascript | ||
var box = gb.create (); | ||
box.load (["a", "e.json"], function (error){ | ||
if (error) return console.log (error); | ||
box.remove ("a"); | ||
console.log (require ("util").inspect (box.get (), true, null)); | ||
/* | ||
Prints: | ||
{ | ||
"e.json": { | ||
d: 4 | ||
} | ||
} | ||
*/ | ||
}); | ||
``` | ||
The paths are relative to the current working directory but they must not begin with `.` or `..`. | ||
```javascript | ||
//This is valid | ||
gb.create ().remove (["a", "b/c"]); | ||
//This is not valid | ||
gb.create ().remove (["./a", "../b"]); | ||
``` | ||
<a name="set"></a> | ||
__GrabBag#set(path, obj[, type])__ | ||
Saves an object into the set of resources. Instead of loading a file to populate the set of resources you can populate it with in-memory objects. Make sure to not to save a reference to the object in you application because if you want to free the object you'll produce a memory leak. | ||
The path is relative to the current working directory but it must not begin with `.` or `..`. | ||
```javascript | ||
//This is valid | ||
gb.create ().set ("a.ini", { p: 1 }); | ||
//This is not valid | ||
gb.create ().set ("./a.ini", { p: 1 }); | ||
gb.create ().set ("../a.ini", { p: 1 }); | ||
``` | ||
<a name="store"></a> | ||
__GrabBag#store([paths], callback)__ | ||
Stores resources into their files. The "paths" parameters can be a string with the path to a file or directory or an array of paths. If an array is passed all the resources are stored in parallel. If the path points to a directory, all the resources that has been loaded into memory previously that belongs to this path will be stored recursively, that is, if an in-memory directory is found, all the properties are stored to their files. The callback with an error parameter is executed on completion, if any. If "paths" is not passed, stores all the loaded resources. | ||
The paths are relative to the current working directory but they must not begin with `.` or `..`. | ||
```javascript | ||
//This is valid | ||
gb.create ().store ("a.ini", function (error){}); | ||
//This is not valid | ||
gb.create ().set ("./a.ini", function (error){}); | ||
gb.create ().set ("../a.ini", function (error){}); |
25525
359
505