type StudentGrade int
>= 0,
<= 100
end
This read “StudentGrade is an int whose values are >= 0 AND ⇐ 100”
It is recommended to put each rule on a separate line.
7.1. Built-in Rules
The built-in rules are
- > greater than
- < less than
- >= greater than or equals
- ⇐ less than or equals
- == equals
- ieq is case-insensitive version of ==
- ilt,igt is case-insensitive version of < and >
- ile,ige is case-insensitive version of <= and >=
- in the value is one of the values specified in the in expression
- range the value is within the range of values specified. An exclusive range.
- irange case-insensitive version of range, for string values. An exclusive range.
- contains the value contains within itself the value in the expression
- empty the value is empty, such as “”.
- hasText the value is not empty and contains at least one non-whitespace character
- or
- and
- ! not
- regex
- startsWith and endsWith
- isa
Rules are shape-specific. The following table describes the shapes that can be used with each rule.
|
Rule |
int |
long |
number |
boolean |
string |
date |
list |
struct |
enum |
|
>,<,>=,⇐ |
yes |
yes |
yes |
no |
yes |
yes |
no |
no |
no |
|
!=,== |
yes |
yes |
yes |
no |
yes |
yes |
no |
no |
yes |
|
ieq,ilt,ile,igt,ige |
no |
no |
no |
no |
yes |
no |
no |
no |
no |
|
in |
yes |
yes |
yes |
no |
yes |
no |
no |
no |
yes |
|
range |
yes |
yes |
yes |
no |
yes |
yes |
no |
no |
yes |
|
contains |
no |
no |
no |
no |
yes |
no |
yes |
no |
yes |
|
empty |
no |
no |
no |
no |
yes |
no |
yes |
no |
no |
|
regex |
no |
no |
no |
no |
yes |
no |
no |
no |
no |
|
isa |
yes |
yes |
no |
no |
yes |
no |
no |
no |
no |
|
startsWith,endsWith |
no |
no |
no |
no |
yes |
no |
no |
no |
no |
|
hasText |
no |
no |
no |
no |
yes |
no |
no |
no |
no |
7.1.1. Comparison Operators
The six comparison operators can be used to specify a condition about the type’s value. The type’s value is implied but not stated
type PositiveInt int
>= 0
end
This can be read as “PositiveInt is an int whose value is >= 0”
Rules for struct members specify the member by name. Here we validate that price is greater than or equals zero:
type Product struct {
name string,
price number
}
price >= 0.0
end
The len pseudo-value
For string and list values, a special pseudo-value len represents the length of the value.
type NonEmptyList list
len > 0
end
Note. A struct using the len pseudo-value must not have a member named “len”.
type Student struct {
firstName string,
lastName string
}
len(firstName) > 0
end
7.1.1.1. ieq, ilt, igt, ile, ige
For string values, there are case-insensitive versions
ieq('tcp') //equals
ilt('z') //less-than
ile('z') //less-than-or-equals
igt('a') //greater-than
ige('a') //greater-than-or-equals
For struct members, use the fieldName + “.” and the rule.
type StudentsNamedBob struct {
firstName string,
lastName string
}
firstName.iseq('Bob')
end
7.1.2. in
The in rule contains a list of allowed values. It can be specified as a list or range
in(3,5,7)
in(0..100)
The first rule requires that the type’s value is 3, 5, or 7.
The second rule requires that the type’s value is between 0 and 100 (inclusive).
|
Note
|
the range of values syntax “..” is not yet supported. |
For struct members, use the fieldName + “.” and the in rule.
someField.in(3,5,7)
7.1.3. range
The range rule contains a range of allowed values. It is an exclusive range.
range(0,100)
range(0..100) //same as above
This rule says that values 0,1,2,..99 are allowed.
Range of dates are supported, but only the comma form.
range(1481482266000, 1481482266089) //using longs
range('2016-12-11T13:51:06.079-0500','2016-12-11T13:51:06.099-0500')
range('2016','2017')
Range of strings are supported, but only the comma form.
range('a', 'z')
range('aardvark', 'zither')
For struct members, use the fieldName + “.” and the range rule.
someField.range(0..100)
7.1.3.2 irange
The irange rule contains a range of allowed string values. It is an exclusive range, and a case-insensitive compare is used.
irange('aa','zz')
For struct members, use the fieldName + “.” and the irange rule.
someField.irange('aa','zz')
7.1.4. contains
Used with strings or lists. It means the the string or list contains, as one of its elements, the given value
contains("able")
It can be used to ensure that a value does not contain certain characters
!contains(" ", "\t", "\r", "\n")
For struct members, use the fieldName + “.” and the contains rule.
someField.contains("able")
7.1.5. empty
The empty rule can be used with strings and lists. It is equivalent to len == 0.
7.1.6. regex
The regex rule applies a java regex
7.1.7. startsWith
This rule can be used with string values. It is equivalent to the Java String method startsWith.
7.1.8. endsWith
This rule can be used with string values. It is equivalent to the Java String method endsWith.
7.1.9. !
This reverses the logic and can be used with these rules
- !in
- !range
- !contains
- !empty
- !regex, !startsWith, or !endsWith
7.1.10. isa
The isa rules defines a value as being a foreign key to another type through the specified field. The specified field is usually a natural key for that type (and it ususally unique).
type Address struct {
customerId string
}
customerId isa Customer.email
street string
end
let someAddr Address = { 'rudy@home.com', '150 Main St.' }
The rule “customerId isa Customer.email” says that customerId will always hold an email value from Customer. When this rule is executed, DNAL searches
all Customer values
(in the current DataSet) until one is found with the value rudy@home.com. The rule fails if no Customer value has that email. An isa statement is equivalent to a foreign key statement in SQL.
The isa rule is used with the Via statement (see below)
7.1.11. or and and
Rules can be combined using or and and. Expressions can be grouped using parenthesis
(len == 4 and contains("A")) or (len == 5 and contains("B"))
7.1.12. hasText
Used with strings. This rule validates that the string is not-empty and contains at least one non-whitespace character.
For struct members, use the fieldName + “.” and the contains rule.
someField.hasText()
7.2. Custom Rules
The DNAL rule system is extensible. You can define your own rules. There are two steps required. First, declare a custom rule using the rule statement.
This declares the name of the rule and which base type (eg. shape) it can be used on. Declare the rule once for each shape
rule myspecialrule int
rule myspecialrule long
Rules are type-specific. The above rule could be used on int or long values or any types derived from int or long.
Custom rules have the syntax
rulename()
rulename(arg0,arg1,...)
A custom rule can have zero or more arguments. They can be a mix of explicit values, or a reference to a value.
let maxScore = 50
type Score int
myspecialrule(0,maxScore)
end
The second step is to provide DNAL with the Java class that implements your custom rule. dnalc has command-line arguments for loading JAR files
containing custom rules. DNALCompiler has the method registryRuleFactory for registering custom rule classes.
For struct members, use the fieldName + “.” and the custom rule.
someField.myspecialrule(0, maxScore)