Clause
(0.1.7)Introduction
Concepts
API
- clause.compose.regex
- clause.compose.string
- clause.compose
- clause.utils
- clause.preds
- clause.namespace
- clause.types
Clause (js)
(
API docs and the index table are auto-generated with clausejs-docgen.
To contribute to API notes & examples, make a pull request to annotation source files here.)
Introduction
Refer to README file for basic intro.What is Conformation?
To put it plainly, as a developer you expect your data to be of certain "shape".
You define the shape of the data with clauses.
To "conform" your data with your clauses is to shape it in a way that is better organized, making it easier for your code to consume.
In order for conformation to work, one must specify a label for each part of your clause definition (example to follow below).
There are 3 ways that data can be conformed with Clause:
cat
Conformation (on arrays and strings)
var MyCatClause = C.cat(
'first', C.isStr,
'second', C.isNum,
'third', C.isBool
);
C.conform( MyCatClause, ["a", 2, true] )
In addition to arrays, strings can also be conformed with scat
.
var StrClause = C.cat('greetings', C.zeroOrMore(C.scat('hello')),
'substence', C.zeroOrMore(C.scat('i am optional')),
'farewell', C.zeroOrMore(C.scat('bye')));
C.conform(StrClause, 'hellohellobyebyebyebye');
or
Conformation
var MyOrClause = C.or('option1', C.isStr, 'option2', C.isNum );
C.conform( MyOrClause, 2 )
shape
Conformation
var ShapeClause3 = C.shape( {
required: {
abilityGroup: [
// key expression
(key) => key.indexOf('can') === 0,
// value expression
C.isBool
],
someKey: C.isNatInt,
},
optional: ["somekey"],
} );
C.conform( ShapeClause3, {
canDance: true,
canFly: true,
canSwim: false,
someKey: 999
} )
Notice that for all the examples given above, a label is supplied for each conformed parts.
Regex-like Operations on Iterables
Both array and string are treated as iterables in Clause, which means both types can be the subject of regex-like operations.
For example, both of the following clause definitions are valid:
Array
var ArrayClause = C.cat('foos', C.oneOrMore(C.equal('foo')),
'bars', C.oneOrMore(C.equal('bar')));
C.conform(ArrayClause, ['foo', 'foo', 'foo', 'bar', 'bar']);
String
var StrClause = C.cat('foos', C.oneOrMore(C.scat('foo')),
'bars', C.oneOrMore(C.scat('bar')));
C.conform(StrClause, 'foofoofoobarbar');
Notice that in the first clause, we use C.equals()
to treat
the string as a single entity, whereas in the second, we use
C.scat()
to indicate that each character in the string is
part of the collection that we run regex ops on.
Delayed & Namespaced Clauses
Delayed & namespaced clauses are useful when composing recursively defined clauses.
For example, suppose we want to write clause for binary trees. A binary tree node contains a left node and a right node, which in turn is also a binary tree node.
There are two ways to represent such structure with Clause.
Delayed Clause
var BinaryTreeClause = C.maybe( C.wall(
C.cat(
'nodeName', C.isStr,
'leftNode', C.delayed( () => BinaryTreeClause ),
'rightNode', C.delayed( () => BinaryTreeClause )
)
) );
C.isValid(
BinaryTreeClause,
['john', ['bob', ['alice', null, null], null], ['sam', null, ['jane', null, null]]]
);
Namespaced Clause
Alternatively, you may also use a namespaced clause to achieve the same effect by giving the tree node a global name in the registry:
C('myapp/BinaryTreeNode', C.maybe( C.wall(
C.cat(
'nodeName', C.isStr,
'leftNode', C( 'myapp/BinaryTreeNode' ),
'rightNode', C( 'myapp/BinaryTreeNode' )
) ) ) );
C.isValid(
C( 'myapp/BinaryTreeNode' ),
['john', ['bob', ['alice', null, null], null], ['sam', null, ['jane', null, null]]]
);
Another added benefit for the second approach is that C( 'myapp/BinaryTreeNode' )
will be exposed as a global reference which you can use anywhere in your app.
API
Work in progress.
Below is a set of auto-generated API reference by
clausejs-docgen based on clause declarations and their corrosponding annotations.
Should be one of the following:
Should satisfy all of the following expression:
Should be an ordered list of the following:
Should be one of the following:
Should be one of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should satisfy all of the following expression:
Should be an ordered list of the following:
Should be one of the following:
Should be one of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be one of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be one of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be one of the following:
Should be an ordered list of the following:
Should be one of the following:
Should be one of the following:
Should be one of the following:
Should be an ordered list of the following:
Should be an ordered list of the following:
Should be one of the following:
Should be an ordered list of the following:
Should be one of the following:
Should be one of the following:
Should satisfy all of the following expression:
Should satisfy all of the following expression:
Should be an ordered list of the following:
Should be one of the following:
Should satisfy all of the following expression:
Should satisfy all of the following expression:
Should be one of the following:
C
function
C("awesomeapp/TodoItem", TodoItemClause)
var ref = C("awesomeapp/TodoItem");
ref.get()
fclause( {
<args>: or(
"register", cat(
"nsPath", NamespacePath,
"expression", Expression
),
"retrieve", cat( "nsPath", NamespacePath )
),
<ret>: or(
Undefined,
ClauseReference
)
} )
clause.compose/
clause.compose.regex/
cat()
function
var CatClause = C.cat(C.isStr, C.isNum, C.isBool);
C.isValid( CatClause, ["a", 2, true] )
C.isValid( CatClause, ["a", false, "b"] )
var LabelledCatClause = C.cat(
'first', C.isStr,
'second', C.isNum,
'third', C.isBool
);
C.conform( LabelledCatClause, ["a", 2, true] )
// equivalent to the above
var LabelledCatClause = C.cat([
[ 'first', C.isStr ],
[ 'second', 'this is an optional comment on second', C.isNum ],
[ 'third', C.isBool ]
]);
C.conform( LabelledCatClause, ["a", 2, true] )
fclause( {
<args>: and(
cat(
"expressions", or(
"withLabels", or(
zeroOrMore( cat(
"name", ClauseLabel,
"comment", zeroOrOne( String ),
"expr", Expression
) ),
collOf( cat(
"name", ClauseLabel,
"comment", zeroOrOne( String ),
"expr", Expression
) )
),
"withoutLabels", zeroOrMore( Expression )
),
"options", zeroOrOne( PlainObject )
),
noDupelicateLabels
),
<ret>: Clause
} )
or()
function
var OrClause = C.or(C.isStr, C.isNum);
C.conform( OrClause, "a" )
var LabelledOrClause = C.or('option1', C.isStr, 'option2', C.isNum );
C.conform( LabelledOrClause, 2 )
// equivalent to the above
var LabelledOrClause = C.or([['option1', C.isStr], ['option2', C.isNum]] );
C.conform( LabelledOrClause, 2 )
fclause( {
<args>: and(
cat(
"expressions", or(
"withLabels", or(
zeroOrMore( cat(
"name", ClauseLabel,
"comment", zeroOrOne( String ),
"expr", Expression
) ),
collOf( cat(
"name", ClauseLabel,
"comment", zeroOrOne( String ),
"expr", Expression
) )
),
"withoutLabels", zeroOrMore( Expression )
),
"options", zeroOrOne( PlainObject )
),
noDupelicateLabels
),
<ret>: Clause
} )
zeroOrMore()
function
var ZOMClause = C.zeroOrMore(C.isNum);
C.isValid( ZOMClause, [1, 2, 3] )
C.isValid( ZOMClause, [1, 2, "3"] )
C.isValid( ZOMClause, [] )
var CombinedClause = C.cat(
"babbles", C.zeroOrMore(C.oneOf("foo", "bar", "baz")),
"truths", C.zeroOrMore(C.isBool),
"counts", C.zeroOrMore(C.isNatInt)
);
C.conform( CombinedClause, ["foo", "foo", "bar", true, 2, 3 , 4] )
C.isValid( CombinedClause, ["foo", "foo", "bar", 2, 3 , 4] )
C.isValid( CombinedClause, [ 2, 3 , 4 ] )
C.isValid( CombinedClause, [ ] )
fclause( {
<args>: cat(
"expr", Expression,
"opts", zeroOrOne( Object )
),
<ret>: Clause
} )
oneOrMore()
function
var OOMClause = C.oneOrMore(C.equals("hello"));
C.isValid( OOMClause, [ "hello", "hello", "hello" ] )
C.isValid( OOMClause, [ "hello", "hello", "hello" ] )
var CombinedClause = C.cat(
"babbles", C.oneOrMore(C.oneOf("foo", "bar", "baz")),
"truths", C.oneOrMore(C.isBool),
"counts", C.oneOrMore(C.isNatInt)
);
C.conform( CombinedClause, ["foo", "foo", "baz", false, true, 2, 3 ] )
C.isValid( CombinedClause, [false, true, 2, 3 ] )
fclause( {
<args>: cat(
"expr", Expression,
"opts", zeroOrOne( Object )
),
<ret>: Clause
} )
zeroOrOne()
function
var ZOOClause = C.zeroOrOne( C.equals( "hello" ) );
C.isValid( ZOOClause, [ "hello" ] )
C.isValid( ZOOClause, [] )
var CombinedClause = C.cat(
"the babble", C.zeroOrOne( C.oneOf("foo", "bar", "baz") ),
"the truth", C.zeroOrOne( C.isBool ),
"the count", C.zeroOrOne( C.isNatInt )
);
C.conform( CombinedClause, ["foo", 4] )
fclause( {
<args>: cat(
"expr", Expression,
"opts", zeroOrOne( Object )
),
<ret>: Clause
} )
clause.compose.string/
scat()
function
var StrClause = C.cat('greetings', C.zeroOrMore(C.scat('hello')),
'substence', C.zeroOrMore(C.scat('i am optional')),
'farewell', C.zeroOrMore(C.scat('bye')));
C.conform(StrClause, 'hellohellobyebyebyebye');
fclause( {
<args>: cat( "string", String ),
<ret>: Clause
} )
and()
function
var AndClause = C.and( C.isStr, (s) => s.length > 5 );
C.isValid( AndClause, "abcdefgh" )
C.isValid( AndClause, "abc" )
var PropagatingAndClause = C.and(
C.cat(
"first", C.isStr,
"second", C.zeroOrMore( C.isNum )
),
// "second" propagated from conformed results of the previous clause
({ second }) => second.length > 4 )
C.isValid( PropagatingAndClause, [ "foo", 2, 3, 4, 5, 6 ] )
C.conform( PropagatingAndClause, [ "foo", 2, 3, 4, 5, 6 ] )
C.isValid( PropagatingAndClause, [ "foo", 2, 3 ] )
fclause( {
<args>: oneOrMore( Expression ),
<ret>: Clause
} )
collOf()
function
var CollOfStrClause = C.collOf(C.isStr);
C.conform( CollOfStrClause, ["a", "b", "c"] )
C.conform( CollOfStrClause, [] )
// notice the difference in behavior from C.zeroOrOne
var CombinedCollOfClause = C.cat(
"babbles", C.collOf(C.oneOf("foo", "bar", "baz")),
"truths", C.collOf(C.isBool),
"counts", C.collOf(C.isNatInt)
);
C.isValid( CombinedCollOfClause, [["foo", "foo", "bar"], [], [2, 3 , 4]] )
// invalid because collOf does not mix with regex ops like zeroOrMore
// collOf(...) here is equivalent to wall(zeroOrMore(...))
C.isValid( CombinedCollOfClause, ["foo", "foo", "bar", 2, 3 , 4] )
fclause( {
<args>: cat(
"expr", Expression,
"opts", zeroOrOne( Object )
),
<ret>: Clause
} )
mapOf()
function
var AbilityMapClause = C.mapOf(
(key) => key.indexOf("can") === 0,
C.isBool );
C.isValid( AbilityMapClause, { canFly: true, canSwim: true, canDance: false } )
C.isValid( AbilityMapClause, { canFly: true, canSwim: true, canDance: "huh?" } )
C.isValid( AbilityMapClause, { meNotValid: true, canSwim: true } )
fclause( {
<args>: cat(
"keyExpression", Expression,
"valExpression", Expression
),
<ret>: Clause
} )
shape()
function
var ShapeClause = C.shape( {
required: ["key1", "key2", "key3"],
optional: ["key4"],
} );
C.isValid( ShapeClause, { key1: true, key2: 2, key3: "ss", key4: false } )
C.isValid( ShapeClause, { key1: true, key2: 2, key3: "ss" } )
C.isValid( ShapeClause, { key1: true, key2: 2 } )
var ShapeClause2 = C.shape( {
required: {
key1: C.isBool,
key2: C.isNatInt,
},
optional: ["key5"],
} );
C.isValid( ShapeClause2, { key1: true, key2: 2, key5: false } )
C.isValid( ShapeClause2, { key1: "not bool", key2: 2, key5: false } )
// key group conformation
var ShapeClause3 = C.shape( {
required: {
abilityGroup: [
// key expression
(key) => key.indexOf('can') === 0,
// value expression
C.isBool
],
someKey: C.isNatInt,
},
optional: ["key5"],
} );
C.conform( ShapeClause3, {
canDance: true,
canFly: true,
canSwim: false,
someKey: 999
} )
fclause( {
<args>: cat( "shapeArgs", shape( {
<optional>: {
"requiredFields": {
<key>: o,n,e,O,f,(,"req", "required",),
<val>: or(
"keyList", zeroOrMore( isPropName ),
"fieldDefs", mapOf( {
<key>: String,
<val>: or(
"valExpressionOnly", Expression,
"keyValExprPair", cat(
"keyExpression", Expression,
"valExpression", Expression
)
)
} )
)
},
"optionalFields": {
<key>: o,n,e,O,f,(,"opt", "optional",),
<val>: or(
"keyList", zeroOrMore( isPropName ),
"fieldDefs", mapOf( {
<key>: String,
<val>: or(
"valExpressionOnly", Expression,
"keyValExprPair", cat(
"keyExpression", Expression,
"valExpression", Expression
)
)
} )
)
}
}
} ) ),
<ret>: Clause
} )
any()
function
C.isValid(C.any(), 123)
C.isValid(C.any(), true)
C.isValid(C.any(), ["sass"])
C.isValid(C.any(), null)
C.isValid(C.any(), undefined)
fclause( {
<ret>: Clause
} )
wall()
function
var FreeMixClause = C.cat( C.oneOrMore( C.isNum ) );
C.isValid( FreeMixClause, [2, 3, 4] )
C.isValid( FreeMixClause, [ [2, 3, 4] ] )
var WalledClause = C.cat( C.wall( C.oneOrMore( C.isNum ) ) );;
C.isValid( WalledClause, [2, 3, 4] )
C.isValid( WalledClause, [ [2, 3, 4] ] )
fclause( {
<args>: cat( Expression ),
<ret>: Expression
} )
fclause()
function
// first, we define a cat clause
var FnArgsClause = C.zeroOrMore(
C.cat(
'name', C.isStr,
'age', C.isNatInt,
// optional field
'isMember', C.zeroOrOne( C.isBool )
)
);
C.conform(FnArgsClause, ["john", 23, true, "mary", 25, "bob", 34])
// next, we define a fclause with args as the cat clause defined above
var MyFnSpec = C.fclause( {
args: FnArgsClause,
ret: C.isNatInt
} );
var nameCount = MyFnSpec.instrument(
function _nameCount() { return Array.from(arguments).filter((s) => C.isStr(s)).length; }
);
nameCount("john", 23, true, "mary", 25, "bob", 34);
// invalid: missing name
nameCount("john", 23, true, 24, false);
var averageAge = MyFnSpec.instrumentConformed(
function (cprofiles) { // "c" stands for "conformed"
// cprofiles example:
// [ { name: "john", age: 4, isMember: true }, { name: "mary", age: 23 }, { name: "bob", age: 102 } ]
var sumOfAges = cprofiles
.map( ( { age } ) => age )
.reduce( (acc, c) => c + acc, 0 );
return sumOfAges / cprofiles.length;
}
);
// cprofile below conforms into
averageAge("john", 4, true, "mary", 23, "bob", 102);
fclause( {
<args>: cat( shape( {
<optional>: {
"args": Expression,
"ret": Expression,
"fn": fclause( {
<args>: cat(
"conformedArguments", any,
"conformedReturnValue", any
),
<ret>: any
} )
}
} ) )
} )
delayed()
function
var BinaryTreeClause = C.maybe( C.wall(
C.cat(
'nodeName', C.isStr,
'leftNode', C.delayed( () => BinaryTreeClause ),
'rightNode', C.delayed( () => BinaryTreeClause )
)
) );
C.isValid(
BinaryTreeClause,
['john', ['bob', ['alice', null, null], null], ['sam', null, ['jane', null, null]]]
);
fclause( {
<args>: cat( "getFn", fclause( {
<args>: cat(),
<ret>: Expression
} ) ),
<ret>: DelayedClause
} )
nullable()
function
C.isValid(C.isStr, null);
C.isValid(C.nullable(C.isStr), null);
fclause( {
<args>: cat( Expression ),
<ret>: Clause
} )
undefinable()
function
C.isValid(C.isStr, undefined);
C.isValid(C.undefinable(C.isStr), undefined);
fclause( {
<args>: cat( Expression ),
<ret>: Clause
} )
maybe()
function
C.isValid(C.isObj, undefined);
C.isValid(C.maybe(C.isObj), null);
C.isValid(C.maybe(C.isObj), undefined);
fclause( {
<args>: cat( Expression ),
<ret>: Clause
} )
clause.utils/
enforce()
function
fclause( {
<args>: cat(
"expression", Expression,
"valueToCheck", any()
),
<ret>: Undefined
} )
conform()
function
fclause( {
<args>: cat(
"expression", Expression,
"valueToConform", any()
),
<ret>: or(
"conformedValue", any(),
"problem", Problem
)
} )
describe()
function
fclause( {
<args>: cat(
"expression", Expression,
"replacer", zeroOrOne( fclause( {
<args>: cat( Expression ),
<ret>: or(
Undefined,
Null,
collOf( String )
)
} ) ),
"space", zeroOrOne( isNatInt )
),
<ret>: String
} )
sExpression()
function
fclause( {
<args>: cat( "expression", Expression ),
<ret>: SExpression
} )
match()
function
fclause( {
<args>: cat(
"keyValMap", Object,
"handlers", mapOf( {
<key>: String,
<val>: fclause( {
<args>: cat( "val", any ),
<ret>: any
} )
} )
)
} )
isProblem()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
clause.preds/
not()
function
fclause( {
<args>: cat( "predicateToNegate", Predicate ),
<ret>: fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
} )
isObj()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isPlainObj()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isStr()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isArray()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isDate()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isNull()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isUndefined()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
notEmpty()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isBool()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isFn()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isNum()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isInt()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isNatInt()
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
isUuid()
function
fclause( {
<args>: cat( "x", String ),
<ret>: Bool
} )
oneOf()
function
fclause( {
<args>: cat( "valueOptions", collOf( Primitive ) ),
<ret>: fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
} )
equals()
function
fclause( {
<args>: cat( "valueToCompareWith", any() ),
<ret>: fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
} )
instanceOf()
function
fclause( {
<args>: cat( "instanceType", isFunction ),
<ret>: fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
} )
clause.namespace/
set()
function
C("awesomeapp/TodoItem", TodoItemClause)
fclause( {
<args>: cat(
"nsPath", NamespacePath,
"expression", Expression
),
<ret>: Bool
} )
get()
function
C("awesomeapp/TodoItem")
C("awesomeapp/TodoItem").get()
fclause( {
<args>: cat( "nsPath", NamespacePath ),
<ret>: ClauseReference
} )
resolve()
function
fclause( {
<args>: cat(
"expression", Expression,
"registry", zeroOrOne( Object )
),
<ret>: NamespacePath
} )
setMeta()
function
fclause( {
<args>: cat(
"source", or(
"namespacePath", NamespacePath,
"expression", Expression
),
"metaObj", Object,
"registry", zeroOrOne( Object )
),
<ret>: Undefined
} )
getMeta()
function
fclause( {
<args>: cat(
"source", or(
"namespacePath", NamespacePath,
"expression", Expression
),
"registry", zeroOrOne( Object )
),
<ret>: or(
Null,
Undefined,
Object
)
} )
clause.types/
Expression
or
Clause
predicate
Problem
predicate
FClause
predicate
Predicate
function
fclause( {
<args>: cat( "x", any() ),
<ret>: Bool
} )
DelayedClause
predicate
ClauseReference
predicate
NamespaceObj
shape
NamespacePath
predicate
ClauseLabel
predicate
SExpression
wall
Primitive
or
String
predicate
Bool
predicate
Number
predicate
Object
predicate
PlainObject
predicate
Undefined
predicate
Null
predicate