Comparing version 0.3.5 to 0.4.0
# Changes | ||
## 0.4.0 | ||
- Add support for capturing an entire nodeset | ||
- Produce sensible RelaxNG grammars for captures | ||
## 0.3.5 | ||
@@ -4,0 +9,0 @@ |
@@ -8,3 +8,10 @@ // Generated by CoffeeScript 2.5.1 | ||
module.exports = function(opts = {}) { | ||
var attr, comment, element, parse, relaxng, text; | ||
/* | ||
Produces a RelaxNG schema for the given template. Refs will be resolved using | ||
the object passed in. If there is a reference to something not defined in the | ||
schema, then we will attempt to resolve it using the refs object. If it | ||
exist, we will add a definition of that name and inline the corresponding | ||
snippet. | ||
*/ | ||
var attr, capture, comment, element, parse, relaxng, relaxngAny, text; | ||
opts = _.cloneDeep(opts); | ||
@@ -15,10 +22,38 @@ opts.types = _.merge({}, require('./model/types'), opts.types || {}); | ||
text = require('./model/text')(opts); | ||
capture = require('./model/capture')(opts); | ||
comment = require('./model/comment')(opts); | ||
parse = require('./model/builder')({element, attr, text, comment}); | ||
relaxng = function(template) { | ||
return element('grammar').ns('http://relaxng.org/ns/structure/1.0').attrs(attr('datatypeLibrary').value('http://www.w3.org/2001/XMLSchema-datatypes')).content(element('start').content(template.relaxng({element, attr, text}))).toXML(); | ||
parse = require('./model/builder')({element, attr, text, comment, capture}); | ||
relaxngAny = element('zeroOrMore').ns('http://relaxng.org/ns/structure/1.0').content(element('choice').content(element('element').content(element('anyName'), element('ref').attrs(attr('name').value('any'))), element('attribute').content(element('anyName')), element('text'))); | ||
relaxng = function(template, refs = {}) { | ||
var definitions, refElementsFound, refsFound, schema, trackingElement; | ||
refElementsFound = []; | ||
trackingElement = function(name) { | ||
var created; | ||
created = element(name); | ||
if (name === 'ref') { | ||
refElementsFound.push(created); | ||
} | ||
return created; | ||
}; | ||
schema = template.relaxng({ | ||
element: trackingElement, | ||
attr, | ||
text | ||
}); | ||
refsFound = refElementsFound.map(function(ref) { | ||
var ref1; | ||
return (ref1 = ref.getAttribute('name')) != null ? ref1.getValue() : void 0; | ||
}); | ||
definitions = _.uniq(refsFound).map(function(name) { | ||
if (refs[name] != null) { | ||
return element('define').attrs(attr('name').value(name)).content(refs[name]); | ||
} else { | ||
return element('define').attrs(attr('name').value(name)).content(relaxngAny); | ||
} | ||
}); | ||
return element('grammar').ns('http://relaxng.org/ns/structure/1.0').attrs(attr('datatypeLibrary').value('http://www.w3.org/2001/XMLSchema-datatypes')).content(element('start').content(schema), ...definitions).toXML(); | ||
}; | ||
return {element, attr, text, parse, relaxng}; | ||
return {element, attr, text, parse, relaxng, capture}; | ||
}; | ||
}).call(this); |
@@ -41,2 +41,8 @@ // Generated by CoffeeScript 2.5.1 | ||
}, | ||
getName: function() { | ||
return meta.name; | ||
}, | ||
getValue: function() { | ||
return meta.value; | ||
}, | ||
generate: function(obj, elem) { | ||
@@ -43,0 +49,0 @@ var value; |
@@ -26,6 +26,6 @@ // Generated by CoffeeScript 2.5.1 | ||
module.exports = function(opts) { | ||
var attr, comment, element, parse, text; | ||
({element, attr, text, comment} = opts); | ||
var attr, capture, comment, element, parse, text; | ||
({element, attr, text, comment, capture} = opts); | ||
parse = function(node) { | ||
var attrs, childNodes, content, el, empty, piNode; | ||
var attrs, captured, childNodes, content, el, empty, piNode; | ||
switch (node.nodeType) { | ||
@@ -44,5 +44,7 @@ case 1: | ||
} | ||
// commentNode = childNodes.find (node) -> node.nodeType is 8 | ||
// if commentNode? | ||
// binding.raw(commentNode.textContent).apply(el) | ||
if ((piNode != null) && piNode.target === 'capture') { | ||
captured = capture(); | ||
binding.raw(piNode.data).apply(captured); | ||
el.content(captured); | ||
} | ||
content = childNodes.map(parse).filter(_.negate(_.isUndefined)); | ||
@@ -49,0 +51,0 @@ el.content(...content); |
@@ -217,2 +217,7 @@ // Generated by CoffeeScript 2.5.1 | ||
}, | ||
getAttribute: function(name) { | ||
return meta.attrs.find(function(attr) { | ||
return attr.getName() === name; | ||
}); | ||
}, | ||
descriptor: function() { | ||
@@ -219,0 +224,0 @@ return meta.descriptor(); |
{ | ||
"name": "cruftless", | ||
"version": "0.3.5", | ||
"version": "0.4.0", | ||
"description": "Yet another simple way to parse and generate XML", | ||
@@ -5,0 +5,0 @@ "main": "lib/cruftless.js", |
135
README.js.md
```javascript --hide | ||
require('coffeescript/register'); | ||
const format = require('xml-formatter'); | ||
runmd.onRequire = function(path) { | ||
if (path === 'cruftless') { | ||
return './readme.cruftless.coffee'; | ||
require("coffeescript/register"); | ||
const format = require("xml-formatter"); | ||
runmd.onRequire = function (path) { | ||
if (path === "cruftless") { | ||
return "./readme.cruftless.coffee"; | ||
} | ||
} | ||
}; | ||
``` | ||
@@ -19,6 +19,5 @@ | ||
## Yet another XML binding framework? | ||
I hate to say this, but: 'yes'. Or, perhaps: 'no'. Because Cruftless is not really an XML binding framework as you know it. It's almost more like Handlebars. But where Handlebars allows you to only *generate* documents, Cruftless also allows you to *extract* data from documents. | ||
I hate to say this, but: 'yes'. Or, perhaps: 'no'. Because Cruftless is not really an XML binding framework as you know it. It's almost more like Handlebars. But where Handlebars allows you to only _generate_ documents, Cruftless also allows you to _extract_ data from documents. | ||
@@ -36,14 +35,10 @@ ## Building XML documents | ||
Then, using the builder API, Cruftless allows you to *build* a model of your document like this: | ||
Then, using the builder API, Cruftless allows you to _build_ a model of your document like this: | ||
```javascript --run simple | ||
const { element, attr, text } = require('cruftless')(); | ||
const { element, attr, text } = require("cruftless")(); | ||
let el = element('person').content( | ||
element('name').content( | ||
text().value('John Doe') | ||
), | ||
element('age').content( | ||
text().value(16) | ||
) | ||
let el = element("person").content( | ||
element("name").content(text().value("John Doe")), | ||
element("age").content(text().value(16)) | ||
); | ||
@@ -69,9 +64,5 @@ ``` | ||
```javascript --run simple | ||
el = element('person').content( | ||
element('name').content( | ||
text().bind('name') | ||
), | ||
element('age').content( | ||
text().bind('age') | ||
) | ||
el = element("person").content( | ||
element("name").content(text().bind("name")), | ||
element("age").content(text().bind("age")) | ||
); | ||
@@ -83,6 +74,6 @@ ``` | ||
```javascript --run simple | ||
let xml = el.toXML({ name: 'John Doe', age: '16'}); // RESULT | ||
let xml = el.toXML({ name: "John Doe", age: "16" }); // RESULT | ||
``` | ||
But the beauty is, it also works the other way around. If you have your model with binding expressions, then you're able to *extract* data from XML like this: | ||
But the beauty is, it also works the other way around. If you have your model with binding expressions, then you're able to _extract_ data from XML like this: | ||
@@ -101,8 +92,8 @@ ```javascript --run simple | ||
<age>{{age}}</age> | ||
</person>` | ||
</person>`; | ||
let { parse } = require('cruftless')(); | ||
let { parse } = require("cruftless")(); | ||
el = parse(template) | ||
console.log(el.toXML({ name: 'Jane Doe', age: '18' })); | ||
el = parse(template); | ||
console.log(el.toXML({ name: "Jane Doe", age: "18" })); | ||
``` | ||
@@ -132,6 +123,10 @@ | ||
console.log(template.toXML({ persons: [ | ||
{ name: 'John Doe', age: 16 }, | ||
{ name: 'Jane Doe', age: 18 } | ||
]})); | ||
console.log( | ||
template.toXML({ | ||
persons: [ | ||
{ name: "John Doe", age: 16 }, | ||
{ name: "Jane Doe", age: 18 }, | ||
], | ||
}) | ||
); | ||
``` | ||
@@ -143,10 +138,10 @@ | ||
```javascript --run simple-2 | ||
const { element, attr, text, parse } = require('cruftless')({ | ||
const { element, attr, text, parse } = require("cruftless")({ | ||
types: { | ||
zeroOrOne: { | ||
type: 'boolean', | ||
from: str => str == '1', | ||
to: value => value ? '1' : '0' | ||
} | ||
} | ||
type: "boolean", | ||
from: (str) => str == "1", | ||
to: (value) => (value ? "1" : "0"), | ||
}, | ||
}, | ||
}); | ||
@@ -166,3 +161,3 @@ | ||
// The second argument defaults to false, so might as well leave it out | ||
console.log(template.fromXML('<foo>1</foo>', false)); | ||
console.log(template.fromXML("<foo>1</foo>", false)); | ||
``` | ||
@@ -173,6 +168,5 @@ | ||
```javascript --run simple-2 | ||
console.log(template.fromXML('<foo>1</foo>', true)); | ||
console.log(template.fromXML("<foo>1</foo>", true)); | ||
``` | ||
## Alternative notation | ||
@@ -190,6 +184,10 @@ | ||
console.log(template.toXML({ persons: [ | ||
{ name: 'John Doe', age: 16 }, | ||
{ name: 'Jane Doe', age: 18 } | ||
]})); | ||
console.log( | ||
template.toXML({ | ||
persons: [ | ||
{ name: "John Doe", age: 16 }, | ||
{ name: "Jane Doe", age: 18 }, | ||
], | ||
}) | ||
); | ||
``` | ||
@@ -208,6 +206,10 @@ | ||
console.log(template.toXML({ persons: [ | ||
{ name: 'John Doe', age: 16 }, | ||
{ name: 'Jane Doe', age: 18 } | ||
]})); | ||
console.log( | ||
template.toXML({ | ||
persons: [ | ||
{ name: "John Doe", age: 16 }, | ||
{ name: "Jane Doe", age: 18 }, | ||
], | ||
}) | ||
); | ||
``` | ||
@@ -236,3 +238,3 @@ | ||
nested element contains references to variable, and that variable is not | ||
defined, then it will not only drop *that* element, but all elements that | ||
defined, then it will not only drop _that_ element, but all elements that | ||
included that element referring to a non-existing variable. | ||
@@ -274,7 +276,7 @@ | ||
However, if you would *produce* XML, then — by default — it will always produce | ||
However, if you would _produce_ XML, then — by default — it will always produce | ||
a text node: | ||
```javascript --run simple-2 | ||
console.log(template.toXML({ name: 'Alice' })); | ||
console.log(template.toXML({ name: "Alice" })); | ||
``` | ||
@@ -286,6 +288,5 @@ | ||
template = parse(`<person>{{name|cdata}}</person>`); | ||
console.log(template.toXML({ name: 'Alice' })); | ||
console.log(template.toXML({ name: "Alice" })); | ||
``` | ||
## JSON-ish Schema (incomplete, subject to change) | ||
@@ -325,3 +326,3 @@ | ||
```javascript --run simple-2 | ||
const { relaxng } = require('cruftless')(); | ||
const { relaxng } = require("cruftless")(); | ||
@@ -331,1 +332,21 @@ console.log(relaxng(template)); | ||
## Nodeset Capture | ||
There are situations where it makes very little sense to have one template | ||
dictating the structure of the entire document. Typically, in those cases, you | ||
want to slowly peel the entire structure about, starting with the outer envelope | ||
/ container, and then slowly work your way in. | ||
In order to support that, Cruftless offers a solution to capture parts of the | ||
DOM tree as is and store it in a variable to be processed further downstream. | ||
The syntax is not all that different than the bind syntax and might be | ||
harmonized at some point. | ||
This is how you use it: | ||
```javascript --run simple-2 | ||
template = parse(`<foo><?capture nodes?></foo>`); | ||
const { nodes } = template.fromXML(`<foo><bar/><bar/></foo>`); | ||
console.log(nodes.length); | ||
console.log(nodes[0].tagName); | ||
``` |
125
README.md
@@ -13,6 +13,5 @@ <!-- | ||
## Yet another XML binding framework? | ||
I hate to say this, but: 'yes'. Or, perhaps: 'no'. Because Cruftless is not really an XML binding framework as you know it. It's almost more like Handlebars. But where Handlebars allows you to only *generate* documents, Cruftless also allows you to *extract* data from documents. | ||
I hate to say this, but: 'yes'. Or, perhaps: 'no'. Because Cruftless is not really an XML binding framework as you know it. It's almost more like Handlebars. But where Handlebars allows you to only _generate_ documents, Cruftless also allows you to _extract_ data from documents. | ||
@@ -30,14 +29,10 @@ ## Building XML documents | ||
Then, using the builder API, Cruftless allows you to *build* a model of your document like this: | ||
Then, using the builder API, Cruftless allows you to _build_ a model of your document like this: | ||
```javascript | ||
const { element, attr, text } = require('cruftless')(); | ||
const { element, attr, text } = require("cruftless")(); | ||
let el = element('person').content( | ||
element('name').content( | ||
text().value('John Doe') | ||
), | ||
element('age').content( | ||
text().value(16) | ||
) | ||
let el = element("person").content( | ||
element("name").content(text().value("John Doe")), | ||
element("age").content(text().value(16)) | ||
); | ||
@@ -63,9 +58,5 @@ ``` | ||
```javascript | ||
el = element('person').content( | ||
element('name').content( | ||
text().bind('name') | ||
), | ||
element('age').content( | ||
text().bind('age') | ||
) | ||
el = element("person").content( | ||
element("name").content(text().bind("name")), | ||
element("age").content(text().bind("age")) | ||
); | ||
@@ -77,6 +68,6 @@ ``` | ||
```javascript | ||
let xml = el.toXML({ name: 'John Doe', age: '16'}); // ⇨ '<person>\r\n <name>John Doe</name>\r\n <age>16</age>\r\n</person>' | ||
let xml = el.toXML({ name: "John Doe", age: "16" }); // ⇨ '<person>\r\n <name>John Doe</name>\r\n <age>16</age>\r\n</person>' | ||
``` | ||
But the beauty is, it also works the other way around. If you have your model with binding expressions, then you're able to *extract* data from XML like this: | ||
But the beauty is, it also works the other way around. If you have your model with binding expressions, then you're able to _extract_ data from XML like this: | ||
@@ -95,8 +86,8 @@ ```javascript | ||
<age>{{age}}</age> | ||
</person>` | ||
</person>`; | ||
let { parse } = require('cruftless')(); | ||
let { parse } = require("cruftless")(); | ||
el = parse(template) | ||
console.log(el.toXML({ name: 'Jane Doe', age: '18' })); | ||
el = parse(template); | ||
console.log(el.toXML({ name: "Jane Doe", age: "18" })); | ||
⇒ <person> | ||
@@ -130,6 +121,10 @@ ⇒ <name>Jane Doe</name> | ||
console.log(template.toXML({ persons: [ | ||
{ name: 'John Doe', age: 16 }, | ||
{ name: 'Jane Doe', age: 18 } | ||
]})); | ||
console.log( | ||
template.toXML({ | ||
persons: [ | ||
{ name: "John Doe", age: 16 }, | ||
{ name: "Jane Doe", age: 18 }, | ||
], | ||
}) | ||
); | ||
⇒ <persons> | ||
@@ -151,10 +146,10 @@ ⇒ <person> | ||
```javascript | ||
const { element, attr, text, parse } = require('cruftless')({ | ||
const { element, attr, text, parse } = require("cruftless")({ | ||
types: { | ||
zeroOrOne: { | ||
type: 'boolean', | ||
from: str => str == '1', | ||
to: value => value ? '1' : '0' | ||
} | ||
} | ||
type: "boolean", | ||
from: (str) => str == "1", | ||
to: (value) => (value ? "1" : "0"), | ||
}, | ||
}, | ||
}); | ||
@@ -176,3 +171,3 @@ | ||
// The second argument defaults to false, so might as well leave it out | ||
console.log(template.fromXML('<foo>1</foo>', false)); | ||
console.log(template.fromXML("<foo>1</foo>", false)); | ||
⇒ { value: true } | ||
@@ -184,7 +179,6 @@ ``` | ||
```javascript | ||
console.log(template.fromXML('<foo>1</foo>', true)); | ||
console.log(template.fromXML("<foo>1</foo>", true)); | ||
⇒ { value: '1' } | ||
``` | ||
## Alternative notation | ||
@@ -202,6 +196,10 @@ | ||
console.log(template.toXML({ persons: [ | ||
{ name: 'John Doe', age: 16 }, | ||
{ name: 'Jane Doe', age: 18 } | ||
]})); | ||
console.log( | ||
template.toXML({ | ||
persons: [ | ||
{ name: "John Doe", age: 16 }, | ||
{ name: "Jane Doe", age: 18 }, | ||
], | ||
}) | ||
); | ||
⇒ <persons> | ||
@@ -230,6 +228,10 @@ ⇒ <person> | ||
console.log(template.toXML({ persons: [ | ||
{ name: 'John Doe', age: 16 }, | ||
{ name: 'Jane Doe', age: 18 } | ||
]})); | ||
console.log( | ||
template.toXML({ | ||
persons: [ | ||
{ name: "John Doe", age: 16 }, | ||
{ name: "Jane Doe", age: 18 }, | ||
], | ||
}) | ||
); | ||
⇒ <persons> | ||
@@ -268,3 +270,3 @@ ⇒ <person> | ||
nested element contains references to variable, and that variable is not | ||
defined, then it will not only drop *that* element, but all elements that | ||
defined, then it will not only drop _that_ element, but all elements that | ||
included that element referring to a non-existing variable. | ||
@@ -321,7 +323,7 @@ | ||
However, if you would *produce* XML, then — by default — it will always produce | ||
However, if you would _produce_ XML, then — by default — it will always produce | ||
a text node: | ||
```javascript | ||
console.log(template.toXML({ name: 'Alice' })); | ||
console.log(template.toXML({ name: "Alice" })); | ||
⇒ <person>Alice</person> | ||
@@ -334,3 +336,3 @@ ``` | ||
template = parse(`<person>{{name|cdata}}</person>`); | ||
console.log(template.toXML({ name: 'Alice' })); | ||
console.log(template.toXML({ name: "Alice" })); | ||
⇒ <person> | ||
@@ -341,3 +343,2 @@ ⇒ <![CDATA[Alice]]> | ||
## JSON-ish Schema (incomplete, subject to change) | ||
@@ -398,3 +399,3 @@ | ||
```javascript | ||
const { relaxng } = require('cruftless')(); | ||
const { relaxng } = require("cruftless")(); | ||
@@ -420,4 +421,26 @@ console.log(relaxng(template)); | ||
## Nodeset Capture | ||
There are situations where it makes very little sense to have one template | ||
dictating the structure of the entire document. Typically, in those cases, you | ||
want to slowly peel the entire structure about, starting with the outer envelope | ||
/ container, and then slowly work your way in. | ||
In order to support that, Cruftless offers a solution to capture parts of the | ||
DOM tree as is and store it in a variable to be processed further downstream. | ||
The syntax is not all that different than the bind syntax and might be | ||
harmonized at some point. | ||
This is how you use it: | ||
```javascript | ||
template = parse(`<foo><?capture nodes?></foo>`); | ||
const { nodes } = template.fromXML(`<foo><bar/><bar/></foo>`); | ||
console.log(nodes.length); | ||
console.log(nodes[0].tagName); | ||
⇒ 2 | ||
⇒ bar | ||
``` | ||
---- | ||
Markdown generated from [./README.js.md](./README.js.md) by [![RunMD Logo](http://i.imgur.com/h0FVyzU.png)](https://github.com/broofa/runmd) |
59656
18
1055
429