Pattern matching
matches tests a string against a portable regular expression.
matches searches a string for a pattern. The pattern is a small, portable subset of regular
expressions with one meaning in every language. It searches anywhere in the string unless you
anchor it with ^ and $.
Syntax
{ op: matches, args: [<string>, '<pattern>'] }| Operator | Arguments | Result |
|---|---|---|
matches | string, pattern | Boolean. Unanchored search; the pattern must be a string literal |
Write the pattern in single quotes in YAML, so that \d stays as written.
Example
The request has the reference ORD-12AB34. A reference is ORD- and six digits.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.pattern, version: 1.0.0, title: Pattern matching }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.reference.format
kind: validation
target: { entity: Order, field: /reference }
operations: [create]
assert: { op: matches, args: [{ var: data.reference }, '^ORD-\d{6}$'] }
severity: error
finding: { code: PAT-001, message: reference.format }
- id: order.email.shape
kind: validation
target: { entity: Order, field: /email }
operations: [create]
assert: { op: matches, args: [{ var: data.email }, '^[^@]+@[^@]+\.[a-z]+$'] }
severity: error
finding: { code: PAT-002, message: email.shape }
messages:
en:
reference.format: "A reference is ORD- followed by six digits."
email.shape: "Enter an e-mail address like name@example.com."
tests:
- name: a reference with letters is denied
entity: Order
operation: create
given:
data: { reference: "ORD-12AB34", email: "ana@example.com" }
expect:
decision: deny
findings:
- { rule: order.reference.format, fields: [/reference] }
- name: a well-formed reference and address
entity: Order
operation: create
given:
data: { reference: "ORD-123456", email: "ana@example.com" }
expect: { decision: allow, findings: [] }
- name: a pattern is a search unless anchored
entity: Order
operation: create
given:
data: { reference: "ORD-123456", email: "ana@example" }
expect:
decision: deny
findings:
- { rule: order.email.shape }{
"entity": "Order",
"operation": "create",
"data": {
"reference": "ORD-12AB34",
"email": "ana@example.com"
}
}Result, from the engine
Decisiondeny1 finding, server channel
PAT-001errorblockingA reference is ORD- followed by six digits./reference
Common mistakes
- Without
^and$the pattern matches anywhere:\d{6}accepts"abc123456xyz". \s,\b, look-ahead and flags are not portable; the ruleset fails to load. See Portable patterns.- Matching is case-sensitive. Write
[A-Za-z]to accept both cases.
Exercise
A Dutch postcode (country is "NL") is four digits, an optional space and two capital letters,
like 1012 AB. Write the rule on Customer. Other countries are not checked.
Hint
Four digits, an optional space, two capital letters: ^\d{4} ?[A-Z]{2}$. Add a when for the country.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.pattern, version: 1.0.0, title: Pattern matching }
scope:
- { level: organization, id: learn }
entities:
Customer:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Customer" }
rules:
- id: customer.postcode.dutch
kind: validation
target: { entity: Customer, field: /postcode }
operations: [create]
when: { op: eq, args: [{ var: data.country }, "NL"] }
assert: { op: matches, args: [{ var: data.postcode }, '^\d{4} ?[A-Z]{2}$'] }
severity: error
finding: { code: PAT-001, message: postcode.dutch }
messages:
en:
postcode.dutch: "A Dutch postcode is four digits and two capital letters, like 1012 AB."
tests:
- name: a lower-case Dutch postcode is denied
entity: Customer
operation: create
given:
data: { country: "NL", postcode: "1012 ab" }
expect:
decision: deny
findings:
- { rule: customer.postcode.dutch, fields: [/postcode] }
- name: both spellings of a Dutch postcode are allowed
entity: Customer
operation: create
given:
data: { country: "NL", postcode: "1012AB" }
expect: { decision: allow, findings: [] }
- name: other countries are not checked
entity: Customer
operation: create
given:
data: { country: "DE", postcode: "10115" }
expect: { decision: allow, findings: [] }{
"entity": "Customer",
"operation": "create",
"data": {
"country": "NL",
"postcode": "1012 ab"
}
}Result, from the engine
Decisiondeny1 finding, server channel
PAT-001errorblockingA Dutch postcode is four digits and two capital letters, like 1012 AB./postcode