Rule Cascade
LearnOperators

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

matches
{ op: matches, args: [<string>, '<pattern>'] }
OperatorArgumentsResult
matchesstring, patternBoolean. 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.

pattern.ruleset.yaml
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 }
request.json
{
  "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
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.

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
pattern.ruleset.yaml
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: [] }
request.json
{
  "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
Course overview

On this page