art-object-tree-factory
v2.2.11
Published
Create Declarative frameworks that output tree structures with properties (e.g. ArtReact)
Downloads
629
Readme
Art.ObjectTreeFactory
simple, elegant and fast declarative tree generation
Fast, easy way to create declarative APIs for data-structures which consist of per-node-properties and per-node-ordered-children.
The Object Tree Factory
An object-tree-factory (OTF) is powerful tool for creating declarative programmatic structures or DSLs. OTFs are useful because they accept simplify a complex set of descriptive inputs into a simple set of outputs:
an arbitrary list of plain-objects, a function which accepts an arbitrary
<function>: objectTreeFactory = ->
IN:
Arguments are compacted and flattened
The resulting list of arguments can be any combination of:
plainObjects for props (merged in the order they appear)
other objects which become the 'children'
OUT:
object-tree-node generated by the nodeFactory
Example ArtHtmlFactory
Object-tree-factories excel when used in a reduced-syntax language such as CaffeineScript (or CoffeeScript). For example, you can express HTML much more compactly:
# CaffeineScript
import &ArtHtmlFactory
console.log
Html
Head
Meta name: "viewport" content: "user-scalable=no, width=device-width, initial-scale=1.0, viewport-fit=cover"
Meta name: "apple-mobile-web-app-capable" content: "yes"
Meta name: "apple-mobile-web-app-status-bar-style" content: "black"
Meta name: "format-detection" content: "telephone=no"
Body
H1 "Art.ObjectTreeFactory"
P "simple, elegant, fast declarative tree generation library"
Outputs:
<html>
<head>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="format-detection" content="telephone=no">
</head>
<body>
<h1>Art.ObjectTreeFactory</h1>
<p>simple, elegant, fast declarative tree generation library</p>
</body>
</html>
Even in JavaScript, object-tree-factories are an effective way to express HTML programmatically:
import {Html, Head, Meta, Body, H1, P} from "art-html-factory"
console.log(
Html(
Head(
Meta({
name: "viewport",
content: "user-scalable=no, width=device-width, initial-scale=1.0, viewport-fit=cover"
}),
Meta({ name: "apple-mobile-web-app-capable", content: "yes" }),
Meta({ name: "apple-mobile-web-app-status-bar-style", content: "black" }),
Meta({ name: "format-detection", content: "telephone=no" })
),
Body(
H1("Art.ObjectTreeFactory"),
P("simple, elegant, fast declarative tree generation library")
)
)
);
Example: React
You can easily create all your React factories:
# CaffeineScript
import &ArtStandardLib, &ArtObjectTreeFactory, &React
createObjectTreeFactories
:Div :Link
(nodeName, props, children) -> createElement nodeName, props, children...
Use:
# CaffeineScript
# React Function Component in
(props) ->
Div
"Everything you need "
Link "is here" src: "http://wikipedia.org"
API
createObjectTreeFactory
Create one object-tree-factory.
createObjectTreeFactory = (...inputs) => objectTreeFactory
Note: createObjectTreeFactory's
inputs
can appear in any order:
nodeFactory:
(props <Object>, children <Array>) => anything
nodeClass:
class Foo { constructor(props <Object>, children <Array>) {} }
options:
<Object>
inspectedName: string for introspection: Factory.getName() == inspectedName
class: alternative way to pass the nodeClass
bind:
<String or Array<String>>
list of method-names to bind from nodeClass onto the factory (note: nodeClass must be set)mergePropsInto:
(intoProps <Object>, fromProps <Object>) ->
custom function to mergefromProps
intointoProps
. This function is only called if there are two or more props-objects. If so, it will be called for every props object passed into the factory. However, since it won't be called if there is exactly one props object, it should not be relied upon to preprocess props. Note: it's safe to mutate intoProps, but NOT safe to mutate fromProps.preprocessElement:
(inputElement, Factory) -> inputElement
called on every input element, whether props, null, undefined or any other value. Note: you can return any value you wish, but don't mutate inputElement.
Example:
class TreeNode
constructor: (@props, @children) ->
toObjects: ->
TreeNode: {}
@props
children: @children && array child in @children
child.toObjects?() || child
Node = createObjectTreeFactory TreeNode
commonProps = color: "black"
a = Node
commonProps
height: "100"
width: "200"
"Does this work for you?"
Node commonProps, source: "images/piglet.png"
"This works for me!"
Node "Ka-blam!"
b = TreeNode:
props:
color: :black
height: :100
width: :200
children: []
"Does this work for you?"
TreeNode:
props:
color: :black
source: :images/piglet.png
children: undefined
"This works for me!"
TreeNode: props: undefined, children: [] "Ka-blam!"
JSON.stringify(a.toObjects()) === JSON.stringify(b)
createObjectTreeFactories
Create many object-tree-factories:
createObjectTreeFactories = (...inputs) => factoryMap
note: createObjectTreeFactories is itself an object-tree-factory! That means you can pass in arguments in any order. Props (options) are merged down. Strings are parsed into words and concatenated.
IN:
options <Object>
: passed directly tocreateObjectTreeFactory
as optionsnodeTypeNames <Strings>
strings are parsed into words and concatenatednodeFactory <(nodeTypeName, props, children) -> node>
called each time a factory is used to construct the nodenodeFactoryFactory <(nodeTypeName) -> (props, children) -> node>
called once for each nodeTypeName to generate a factory for that node-type
Required: You must have exactly one nodeFactory
or one nodeFactoryFactory
.
OUT:
factories <Object>
is a map from nodeTypeNames (upperCamelCased) to factories returned from createObjectTreeFactory
Example:
# An example class
class TagNode
constructor: (@tag, @props, @children) ->
# Output an HTML string
toString: (indent = '')->
"<#{@tag}"
+ if @props
' ' +
array v, k in @props
"#{k}='#{v}'"
.join ' '
else ''
+ ">"
+ if @children
indent2 = indent + ' '
"\n" + indent2 +
array child in @children
child.toString indent2
.join "\n#{indent2}"
+ "\n"
else ''
+ "#{indent}</#{@tag}>"
##############################
Create Object Tree Factories
##############################
{Html, Head, Body, Div, P, B} = createObjectTreeFactories
:html :head :body :div :p :b
TagNode
##########################
Create and output a Tree
##########################
console.log Html Head Body
Div
class: "row"
Div
class: "col"
P
"This is truly "
B "fantastic"
"!"
Div
class: "col"
P "What do you think?"
The output:
<html>
<head>
<body>
<div class='row'>
<div class='col'>
<p>
This is truly
<b>
fantastic
</b>
!
</p>
</div>
<div class='col'>
<p>
What do you think?
</p>
</div>
</div>
</body>
</head>
</html>