Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

v1.2.0

  • Loading branch information...
commit 5639dcaf2dc59a3b2f35de2c14c96183fc7dcc80 1 parent 6f03803
Gabriel Llamas authored
170 CHANGES
View
@@ -1,137 +1,143 @@
+v1.2.0 (07 Feb 2014)
+ The sections now can contain a namespace chain, that is, dot-separated
+ sections can be split up like dot-separated properties.
+ Fixed variable expansion when the "namespace" option is enabled.
+ Minor improvements.
+
v1.1.3 (07 Dec 2013)
- When a namespace chain is invalid it now returns an error.
+ When a namespace chain is invalid it now returns an error.
v1.1.2 (22 Oct 2013)
- Bugfix stringifying characters when "unicode" was true (improved ISO 8859-1
- compatibility).
+ Bugfix stringifying characters when "unicode" was true (improved ISO 8859-1
+ compatibility).
v1.1.1 (14 Oct 2013)
- Bugfix when "parse()" was called with only 1 parameter.
+ Bugfix when "parse()" was called with only 1 parameter.
v1.1.0 (12 Oct 2013)
- Removed "json" parsing option (improved parsing speed).
- Removed "Stringifier.from()".
- Removed optional parameter from "stringifier()".
- Renamed "stringifier()" to "createStringifier()".
+ Removed "json" parsing option (improved parsing speed).
+ Removed "Stringifier.from()".
+ Removed optional parameter from "stringifier()".
+ Renamed "stringifier()" to "createStringifier()".
v1.0.4 (07 Sep 2013)
- When "namespaces" are in use the objects are merged correctly.
- When "path" is used the callback is called correctly.
+ When "namespaces" are in use the objects are merged correctly.
+ When "path" is used the callback is called correctly.
v1.0.3 (16 Aug 2013)
- The paths from the imported files are relative to the current file.
+ The paths from the imported files are relative to the current file.
v1.0.2 (16 Aug 2013)
- Minor bugfixes with sections that don't end with "]".
+ Minor bugfixes with sections that don't end with "]".
v1.0.1 (11 Aug 2013)
- Some bugfixes including files.
+ Some bugfixes including files.
v1.0.0 (08 Aug 2013)
- Complete refactor from scratch.
- Improved reading speed by ~45%. This new version iterates the data only once
- while the previous version was a direct port from the Java source code and
- was iterating four times over the same data.
- Improved writing speed by ~77%.
- All the dependencies have been removed.
- If the sections are enabled the properties should be stringified using a
- Stringifier instead of giving a raw object.
- "load()" and "store" have been removed and "parse()" and "stringify()" can
- read and write from/to files with the "path" option.
- Added "json" option to "parse()". When it is enabled stringified json arrays
- and objects are parsed into javascript arrays and objects.
- Added "namespaces" option to "parse()". When it is enabled the dots in the
- keys will be interpreted as namespaces.
- Added "vars" option to "parse()". When the variables are enabled and a
- variable does not exist it first looks into the "vars" object before
- throwing an error. It can be used to pass environment variables.
- Added "strict" option to "parse()". When it is enabled the parser will only
- consider as coments and separators the tokens included in the "comments"
- and "separators" options.
- Added "include" option to "parse()". The "include" key can be used to import
- other files.
- Removed "bufferSize" and "encoding" options. Files are read using a 16KB
- buffer size and utf8 encoding.
- Removed the "pretty" option from "stringify()". The required code to
- accomplish the column limit is too complex and the feature is rarely used.
- This option contains bugs in the previous versions. The overall stringify
- speed is faster.
- The stringify replacer must be a function. Cannot be an array like in previous
- versions.
- Some bugfixes with escaped backslashes.
- Some bugfixes with whitespace values.
- Some bugfixes with sections.
- Some bugfixes with "stringify()".
+ Complete refactor from scratch.
+ Improved reading speed by ~45%. This new version iterates the data only once
+ while the previous version was a direct port from the Java source code and
+ was iterating four times over the same data.
+ Improved writing speed by ~77%.
+ All the dependencies have been removed.
+ If the sections are enabled the properties should be stringified using a
+ Stringifier instead of giving a raw object.
+ "load()" and "store" have been removed and "parse()" and "stringify()" can
+ read and write from/to files with the "path" option.
+ Added "json" option to "parse()". When it is enabled stringified json arrays
+ and objects are parsed into javascript arrays and objects.
+ Added "namespaces" option to "parse()". When it is enabled the dots in the
+ keys will be interpreted as namespaces.
+ Added "vars" option to "parse()". When the variables are enabled and a
+ variable does not exist it first looks into the "vars" object before
+ throwing an error. It can be used to pass environment variables.
+ Added "strict" option to "parse()". When it is enabled the parser will only
+ consider as coments and separators the tokens included in the "comments"
+ and "separators" options.
+ Added "include" option to "parse()". The "include" key can be used to import
+ other files.
+ Removed "bufferSize" and "encoding" options. Files are read using a 16KB
+ buffer size and utf8 encoding.
+ Removed the "pretty" option from "stringify()". The required code to
+ accomplish the column limit is too complex and the feature is rarely used.
+ This option contains bugs in the previous versions. The overall stringify
+ speed is faster.
+ The stringify replacer must be a function. Cannot be an array like in previous
+ versions.
+ Some bugfixes with escaped backslashes.
+ Some bugfixes with whitespace values.
+ Some bugfixes with sections.
+ Some bugfixes with "stringify()".
v0.3.3 (12 Jan 2013)
- Fixed column width in pretty feature.
+ Fixed column width in pretty feature.
v0.3.2 (04 Jan 2013)
- Removed "config()".
+ Removed "config()".
v0.3.1 (01 Jan 2013)
- Fixed pretty print with sections and comments.
+ Fixed pretty print with sections and comments.
v0.3.0 (30 Dec 2012)
- Added "reviver" callback setting to "load()".
- Added "replacer" callback setting to "store()".
- Added "pretty" setting to "store()".
- Added "stringify()".
- Added "parse()".
- Merged enhanced-properties module with this module.
- Added sections feature.
- Added custom characters feature.
- Added variable substitution feature.
+ Added "reviver" callback setting to "load()".
+ Added "replacer" callback setting to "store()".
+ Added "pretty" setting to "store()".
+ Added "stringify()".
+ Added "parse()".
+ Merged enhanced-properties module with this module.
+ Added sections feature.
+ Added custom characters feature.
+ Added variable substitution feature.
v0.2.0 (14 Dec 2012)
- Complete code revision and refactor.
- Non printable characters (C0 and C1 control codes) are converted to unicode
- string representations regardless the encoding.
- Added "config()".
+ Complete code revision and refactor.
+ Non printable characters (C0 and C1 control codes) are converted to unicode
+ string representations regardless the encoding.
+ Added "config()".
v0.1.13 (30 Jul 2012)
- Added the option to pass the file encoding, by default is utf8.
+ Added the option to pass the file encoding, by default is utf8.
v0.1.12 (29 Jul 2012)
- Fixed error when "get()" was called with not existent key.
+ Fixed error when "get()" was called with not existent key.
v0.1.11 (23 Jul 2012)
- Fixed case sensitivity.
+ Fixed case sensitivity.
v0.1.10 (22 Jul 2012)
- The empty keys now have a "null" value instead of an empty string.
+ The empty keys now have a "null" value instead of an empty string.
v0.1.9 (22 Jul 2012)
- Added "remove()" method.
+ Added "remove()" method.
v0.1.8 (21 Jul 2012)
- Added the option to configure the case sensitivity.
+ Added the option to configure the case sensitivity.
v0.1.7 (02 May 2012)
- Changed the way to require the class.
- Now: "var Properties = require ("properties");".
- Inside the "load()" and "store()" callbacks, "this" points to the object
- itself.
- Some maintenance.
+ Changed the way to require the class.
+ Now: "var Properties = require ("properties");".
+ Inside the "load()" and "store()" callbacks, "this" points to the object
+ itself.
+ Some maintenance.
v0.1.6 (02 May 2012)
- Fixed source code to support the new `BufferedReader` settings.
+ Fixed source code to support the new `BufferedReader` settings.
v0.1.5 (28 Apr 2012)
- Added "buffered-writer" dependency.
+ Added "buffered-writer" dependency.
v0.1.4 (21 Apr 2012)
- Added "buffered-writer" dependency.
+ Added "buffered-writer" dependency.
v0.1.3 (16 Apr 2012)
- Removed "getFileName()", internal function.
+ Removed "getFileName()", internal function.
v0.1.2 (15 Apr 2012)
- The property value is now converted to a string before persisting.
+ The property value is now converted to a string before persisting.
v0.1.1 (15 Apr 2012)
- Now it's possible to add comments for each key-value pair. A header comment
- can also be added.
+ Now it's possible to add comments for each key-value pair. A header comment
+ can also be added.
v0.1.0 (15 Apr 2012)
- First release.
+ First release.
60 README.md
View
@@ -23,7 +23,7 @@ b: 2
```javascript
var properties = require ("properties");
-properties.load ("file", { path: true }, function (error, obj){
+properties.parse ("file", { path: true }, function (error, obj){
if (error) return console.error (error);
console.log (obj);
@@ -133,7 +133,7 @@ You can also pass external variables with the `vars` option and use their value
<a name="namespaces"></a>
__Namespaces__
-When the `namespaces` option is enabled dot separated keys are parsed as namespaces, that is, they are interpreted as JavaScript objects.
+When the `namespaces` option is enabled dot separated keys and sections are parsed as namespaces, that is, they are interpreted as JavaScript objects.
```
a.b = 1
@@ -157,27 +157,29 @@ These properties create the following object:
You can also use sections and variables:
```
-[s1]
+[s1.x]
a.b = 1
# a.c.d = 1
-a.c.d = ${s1|a.b}
+a.c.d = ${s1.x|a.b}
```
```javascript
{
s1: {
- a: {
- b: 1,
- c: {
- d: 1
+ x: {
+ a: {
+ b: 1,
+ c: {
+ d: 1
+ }
}
}
}
}
```
-The external variables can also be read using namespaces:
+The external variables can be also read using namespaces:
```javascript
var options = {
@@ -223,7 +225,7 @@ Note: The whitespace (`<space>`, `\t`, `\f`) is still considered a separator eve
<a name="include"></a>
__Importing files__
-When the `include` option is enabled, the `include` key allows you import files. If the path is a directory it tries to load the file `index.properties`. The paths are relative from the __current__ .properties file.
+When the `include` option is enabled, the `include` key allow you import files. If the path is a directory it tries to load the file `index.properties`. The paths are relative to the __current__ .properties file.
The imported files are merged with the current file, they can replace old data.
@@ -266,17 +268,23 @@ There are too many options that you can enable but, which of them should you use
db_pool_min 5
db_pool_max 10
```
-- __sections__: More organization. You don't need to write the first namespace level. For example:
+- __sections__: More organization. You don't need to write all the namespace chain. For example:
```
[db]
- pool.min 5
- pool.max 10
+ host 1.2.3.4
+ port 1234
+
+ [db.pool]
+ min 5
+ max 10
```
Instead of:
```
+ db.host 1.2.3.4
+ db.port 1234
db.pool.min 5
db.pool.max 10
```
@@ -329,6 +337,8 @@ config.load (function (error, obj){
});
```
+Note: You can also use a configuration loader like [seraphim](https://github.com/gagle/node-seraphim).
+
---
<a name="parse"></a>
@@ -339,10 +349,16 @@ Parses a .properties string.
If a callback is given, the result is returned as the second parameter.
```javascript
-obj = properties.parse ({ ... });
+try{
+ //Certain options can throw errors, so if the callback is not used, try-catch
+ //the function
+ obj = properties.parse ({ ... });
+}catch (error){
+ ...
+}
properties.parse ({ ... }, function (error, obj){
- //The "error" can be ignored, it is always null if the "path" option is not used
+ //The callback must be used if the "path" option is used
});
```
@@ -367,15 +383,15 @@ Options:
- __strict__ - _Boolean_
This option can be used with the `comments` and `separators` options. If true, __only__ the tokens specified in these options are used to parse comments and separators.
- __sections__ - _Boolean_
- Parses INI sections. See the [ini](#ini) section for further details.
+ Parses INI sections. Read the [INI](#ini) section for further details.
- __namespaces__ - _Boolean_
- Parses dot separated keys as JavaScript objects. See the [namespaces](#namespaces) section for further details.
+ Parses dot separated keys as JavaScript objects. Look at the [namespaces](#namespaces) section for further details.
- __variables__ - _Boolean_
- Allows you to read the value of a key while the file is being parsed. See the [variables](#variables) section for further details.
+ Allows you to read the value of a key while the file is being parsed. Look at the [variables](#variables) section for further details.
- __vars__ - _Boolean_
- External variables can be passed to the file if the variables option is enabled. See the [variables](#variables) section for further details.
+ External variables can be passed to the file if the variables option is enabled. Look at the [variables](#variables) section for further details.
- __include__ - _Boolean_
- Files can be linked and imported with the `include` key. If this option is used the callback is mandatory. See the [include](#include) section for further details.
+ Files can be linked and imported with the `include` key. If this option is used the callback is mandatory. Look at the [include](#include) section for further details.
- __reviver__ - _Function_
Each property or section can be removed or modified from the final object. It's similar to the reviver of the `JSON.parse()` function.
@@ -470,9 +486,9 @@ ___module_.stringify(obj[, options][, callback]) : undefined | String__
Stringifies an object or a [Stringifier](#Stringifier).
-If you don't need to add sections nor comments simply pass an object, otherwise use a [Stringifier](#Stringifier).
+If you don't need to add sections or comments simply pass an object, otherwise use a [Stringifier](#Stringifier).
-The callback is only needed when the `path` option is used.
+The callback is only necessary when the `path` option is used.
Nested objects and arrays cannot be stringified like in JSON.stringify:
13 examples/namespaces/namespaces
View
@@ -4,10 +4,9 @@ app.title ${app.name} v${app.version}
path.home ./app
path.test ${path.home}/test
-path.web ${path.home}/web
-path.web.logs ${path.web}/logs
-path.web.controllers ${path.web}/controllers
-path.web.views ${path.web}/views
+path.web.logs ${path.home}/web/logs
+path.web.controllers ${path.home}/web/controllers
+path.web.views ${path.home}/web/views
log.basename app
log.max_size 1kb
@@ -18,5 +17,7 @@ log.backups 1
[db]
host 10.10.10.10
port 1234
-pool.min 5
-pool.max 10
+
+[db.pool]
+min 5
+max 10
6 examples/namespaces/namespaces.js
View
@@ -25,7 +25,11 @@ properties.parse ("namespaces", options, function (error, p){
path: {
home: "./app",
test: "./app/test",
- web: "./app/web"
+ web: {
+ logs: "./app/web/logs",
+ controllers: "./app/web/controllers",
+ views: "./app/web/views"
+ }
},
log: {
basename: "app",
3  lib/parse.js
View
@@ -1,8 +1,5 @@
"use strict";
-//The data doesn't need to be buffered because .properties files typically
-//have a size less than a block (default is 16KB)
-
var hex = function (c){
switch (c){
case "0": return 0;
149 lib/read.js
View
@@ -47,20 +47,18 @@ var expand = function (o, str, options, cb){
key = "";
continue;
}else if (c === "}"){
- holder = section !== null ? o[section] : o;
+ holder = section !== null ? searchValue (o, section, true) : o;
if (!holder){
return cb (new Error ("The section \"" + section + "\" does not " +
"exist"));
}
- v = holder[key];
+ v = options.namespaces ? searchValue (holder, key) : holder[key];
if (v === undefined){
//Read the external vars
- if (options.namespaces){
- v = namespaceValue (options._vars, key);
- }else{
- v = options._vars[key];
- }
+ v = options.namespaces
+ ? searchValue (options._vars, key)
+ : options._vars[key]
if (v === undefined){
return cb (new Error ("The property \"" + key + "\" does not " +
@@ -86,35 +84,60 @@ var expand = function (o, str, options, cb){
cb (null, key);
};
-var namespaceValue = function (p, key){
- var n = key.split (".");
+var searchValue = function (o, chain, section){
+ var n = chain.split (".");
var str;
for (var i=0; i<n.length-1; i++){
str = n[i];
- if (p[str] === undefined) return;
- p = p[str];
+ if (o[str] === undefined) return;
+ o = o[str];
}
- return p[n[n.length - 1]];
+ var v = o[n[n.length - 1]];
+ if (section){
+ if (typeof v !== "object") return;
+ return v;
+ }else{
+ if (typeof v === "object") return;
+ return v;
+ }
};
-var namespace = function (p, key, value){
+var namespaceKey = function (o, key, value){
var n = key.split (".");
var str;
for (var i=0; i<n.length-1; i++){
str = n[i];
- if (p[str] === undefined){
- p[str] = {};
- }else if (typeof p[str] !== "object"){
- return new Error ("Invalid namespace chain, the key must contain an " +
- "object: " + key);
+ if (o[str] === undefined){
+ o[str] = {};
+ }else if (typeof o[str] !== "object"){
+ throw new Error ("Invalid namespace chain in the property name '" +
+ key + "' ('" + str + "' has already a value)");
}
- p = p[str];
+ o = o[str];
}
- p[n[n.length - 1]] = value;
+ o[n[n.length - 1]] = value;
+};
+
+var namespaceSection = function (o, section){
+ var n = section.split (".");
+ var str;
+
+ for (var i=0; i<n.length; i++){
+ str = n[i];
+ if (o[str] === undefined){
+ o[str] = {};
+ }else if (typeof o[str] !== "object"){
+ throw new Error ("Invalid namespace chain in the section name '" +
+ section + "' ('" + str + "' has already a value)");
+ }
+ o = o[str];
+ }
+
+ return o;
};
var merge = function (o1, o2){
@@ -183,6 +206,7 @@ var build = function (data, options, dirname, cb){
}
var currentSection = null;
+ var currentSectionStr = null;
var abort = function (error){
control.abort = true;
@@ -219,15 +243,18 @@ var build = function (data, options, dirname, cb){
reviver.isProperty = true;
reviver.isSection = false;
- value = options.reviver.call (reviver, key, value, currentSection);
+ value = options.reviver.call (reviver, key, value, currentSectionStr);
if (value !== undefined){
- if (currentSection === null) o[key] = value;
- else o[currentSection][key] = value;
-
if (options.namespaces){
- error = namespace (currentSection === null ? n : n[currentSection],
- key, value);
- if (error) abort (error);
+ try{
+ namespaceKey (currentSection === null ? n : currentSection,
+ key, value);
+ }catch (error){
+ abort (error);
+ }
+ }else{
+ if (currentSection === null) o[key] = value;
+ else currentSection[key] = value;
}
}
};
@@ -241,11 +268,14 @@ var build = function (data, options, dirname, cb){
value = options.reviver.call (reviver, key, value);
if (value !== undefined){
- o[key] = value;
-
if (options.namespaces){
- error = namespace (n, key, value);
- if (error) abort (error);
+ try{
+ namespaceKey (n, key, value);
+ }catch (error){
+ abort (error);
+ }
+ }else{
+ o[key] = value;
}
}
};
@@ -255,24 +285,30 @@ var build = function (data, options, dirname, cb){
line = function (key, value){
if (options.include && key === INCLUDE_KEY) return include (value);
- if (currentSection === null) o[key] = value;
- else o[currentSection][key] = value;
-
if (options.namespaces){
- error = namespace (currentSection === null ? n : n[currentSection],
- key, value);
- if (error) abort (error);
+ try{
+ namespaceKey (currentSection === null ? n : currentSection, key,
+ value);
+ }catch (error){
+ abort (error);
+ }
+ }else{
+ if (currentSection === null) o[key] = value;
+ else currentSection[key] = value;
}
};
}else{
line = function (key, value){
if (options.include && key === INCLUDE_KEY) return include (value);
- o[key] = value;
-
if (options.namespaces){
- error = namespace (n, key, value);
- if (error) abort (error);
+ try{
+ namespaceKey (n, key, value);
+ }catch (error){
+ abort (error);
+ }
+ }else{
+ o[key] = value;
}
};
}
@@ -283,17 +319,21 @@ var build = function (data, options, dirname, cb){
if (options.sections){
if (options.reviver){
section = function (section){
+ currentSectionStr = section;
reviverLine.section = section;
reviver.isProperty = false;
reviver.isSection = true;
var add = options.reviver.call (reviver, null, null, section);
if (add){
- currentSection = section;
- o[currentSection] = {};
-
if (options.namespaces){
- n[currentSection] = {};
+ try{
+ currentSection = namespaceSection (n, section);
+ }catch (error){
+ abort (error);
+ }
+ }else{
+ currentSection = o[section] = {};
}
}else{
control.skipSection = true;
@@ -301,11 +341,15 @@ var build = function (data, options, dirname, cb){
};
}else{
section = function (section){
- currentSection = section;
- o[currentSection] = {};
-
+ currentSectionStr = section;
if (options.namespaces){
- n[currentSection] = {};
+ try{
+ currentSection = namespaceSection (n, section);
+ }catch (error){
+ abort (error);
+ }
+ }else{
+ currentSection = o[section] = {};
}
};
}
@@ -314,10 +358,11 @@ var build = function (data, options, dirname, cb){
//Variables
if (options.variables){
handlers.line = function (key, value){
- expand (o, key, options, function (error, key){
+ expand (options.namespaces ? n : o, key, options, function (error, key){
if (error) return abort (error);
- expand (o, value, options, function (error, value){
+ expand (options.namespaces ? n : o, value, options,
+ function (error, value){
if (error) return abort (error);
line (key, cast (value || null));
@@ -327,7 +372,7 @@ var build = function (data, options, dirname, cb){
if (options.sections){
handlers.section = function (s){
- expand (o, s, options, function (error, s){
+ expand (options.namespaces ? n : o, s, options, function (error, s){
if (error) return abort (error);
section (s);
2  package.json
View
@@ -1,6 +1,6 @@
{
"name": "properties",
- "version": "1.1.3",
+ "version": "1.2.0",
"description": ".properties parser/stringifier",
"keywords": ["properties", "ini", "parser", "stringifier", "config"],
"author": "Gabriel Llamas <gagle@outlook.com>",
4 test/namespaces-sections
View
@@ -6,5 +6,5 @@ a.c ${a.a}${a.b}
b.a a
b.b ${s1|b.a}a
-[s2]
-c true
+[s2.b.c]
+d true
37 test/parse.js
View
@@ -321,6 +321,35 @@ var tests = {
done ();
});
},
+ "variables with sections and namespaces": function (done){
+ var options = {
+ variables: true,
+ sections: true,
+ namespaces: true,
+ path: true
+ };
+
+ properties.parse ("variables-sections-namespaces", options,
+ function (error, p){
+ assert.ifError (error);
+
+ assert.deepEqual (p, {
+ a: {
+ b: "c"
+ },
+ c: {
+ a: {
+ b: {
+ x: 1
+ },
+ e: 1
+ }
+ }
+ });
+
+ done ();
+ });
+ },
"namespaces": function (done){
var options = { path: true, variables: true, namespaces: true };
@@ -364,7 +393,11 @@ var tests = {
}
},
s2: {
- c: true
+ b: {
+ c: {
+ d: true
+ }
+ }
}
});
@@ -407,7 +440,7 @@ var tests = {
namespaces: true,
sections: true,
reviver: function (key, value, section){
- if (this.isSection && section === "s2") return;
+ if (this.isSection && section === "s2.b.c") return;
if (this.isProperty && key === "b.a") return "b";
return this.assert ();
}
6 test/variables-sections-namespaces
View
@@ -0,0 +1,6 @@
+a.b c
+
+[${a.b}.a]
+b.x 1
+
+e ${c.a|b.x}
Please sign in to comment.
Something went wrong with that request. Please try again.