一键导入
new-rule
Guide for creating a Rule in rulii — lambda-based and class-based styles, conditions, actions, otherwise, preCondition, and running rules manually
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Guide for creating a Rule in rulii — lambda-based and class-based styles, conditions, actions, otherwise, preCondition, and running rules manually
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Guide for creating a RuleFlow in rulii — fluent pipeline builder covering bind, run/apply/execute steps, step specs (as/with/onException), when/forEach/scope containers, exit, async (asyncRun/await), exception handling, and custom containers
Diagnostic guide for troubleshooting rulii rules that aren't firing, returning unexpected PASS/FAIL/SKIP, or throwing exceptions — covers bindings, types, scope, tracing, and validation violations
Guide for creating a RuleSet in rulii — covers builder API, lifecycle hooks, input params, stop conditions, validating mode, async execution, and error handling
Guide for creating a new custom ValidationRule in rulii — rule class, builder class, annotations, supported types, isValid logic, and customizeViolation
Guide for writing JUnit 5 tests in the rulii project — test structure, known JUnit 5.12.1 ambiguities, PASS/FAIL/SKIP patterns, ValidationRule test patterns, and missing-binding test patterns
| name | new-rule |
| description | Guide for creating a Rule in rulii — lambda-based and class-based styles, conditions, actions, otherwise, preCondition, and running rules manually |
| user-invocable | true |
Use this guide when the user asks to create, compose, or explain a Rule.
A Rule is an if / then / otherwise construct: a Condition that is tested, one or more Actions run when it passes, and an optional otherwise Action run when it fails.
Rule rule = Rule.builder()
.name("AgeCheck")
.given(condition((Integer age) -> age >= 18))
.build();
Rule.builder() returns the singleton RuleBuilder..name(String) is required for lambda-based rules..given(Condition) sets the main condition. A rule with no condition defaults to always-true (with a warning).Rule.builder()
.name("RuleName") // required for lambda rules; must match [A-Za-z][A-Za-z0-9_]*
.name("RuleName", "desc") // name + description shorthand
.description("...") // optional description
.preCondition(condition) // skip this rule entirely if false (no then/otherwise called)
.given(condition) // main condition — required
.then(action) // action(s) when condition is true (chainable)
.then(action2)
.otherwise(action) // action when condition is false
.build()
Import the static helpers for concise lambda syntax:
import static org.rulii.model.condition.Conditions.condition;
import static org.rulii.model.action.Actions.action;
Condition examples:
// Single binding resolved by parameter name
condition((Integer age) -> age >= 18)
// Multiple bindings
condition((String firstName, String lastName) -> firstName != null && lastName != null)
// Always true / false
Conditions.TRUE()
Conditions.FALSE()
// Combine with .and() / .or() / .negate()
condition((Integer age) -> age >= 18).and(condition((String email) -> email.contains("@")))
Action examples:
// Mutation via Binding<T>
action((Binding<Integer> count) -> count.setValue(count.getValue() + 1))
// Side effect with a plain value
action((String name) -> System.out.println("Hello " + name))
// Access the full Bindings
action((Bindings bindings) -> bindings.bind(result -> "done"))
// No-op
Actions.EMPTY_ACTION()
| PreCondition | Condition | |
|---|---|---|
| Fails → result | SKIP (no then/otherwise) | otherwise action runs (if set) |
| Typical use | Guard — wrong type, missing context | Business logic |
| Class-based | @PreCondition annotation | @Given annotation |
Rule rule = Rule.builder()
.name("NumericCheck")
.preCondition(condition((Object value) -> value instanceof String)) // SKIP if not a String
.given(condition((String value) -> value.matches("\\d+")))
.otherwise(action((RuleViolations rv) -> rv.add(...)))
.build();
Annotate a plain class — rulii discovers @Given, @Then, @Otherwise, @PreCondition by annotation:
@org.rulii.annotation.Rule
@Description("User must be of legal age")
public class AgeCheckRule {
@PreCondition
public boolean isApplicable(Object value) {
return value instanceof Integer;
}
@Given
public boolean isValid(Integer age) {
return age >= 18;
}
@Then
public void onPass(Bindings bindings) {
// runs when condition is true
}
@Otherwise
public void onFail(RuleViolations violations) {
// runs when condition is false
}
}
// Build from class
Rule rule = Rule.builder().build(AgeCheckRule.class);
// Build from instance (useful when constructor arguments are needed)
Rule rule = Rule.builder().build(new AgeCheckRule());
Via RuleContext (normal path — used in RuleSets):
Bindings bindings = Bindings.builder().standard();
bindings.bind(age -> 25);
RuleContext ctx = RuleContext.builder().build(bindings);
RuleResult result = rule.run(ctx);
// result.getStatus() → PASS, FAIL, or SKIP
Manually testing the condition (no actions fired):
// Pass BindingDeclaration lambdas — parameter name becomes binding name
boolean passes = rule.getCondition().isTrue(age -> 25);
boolean passes = rule.getCondition().isTrue(age -> 25, email -> "a@b.com");
// Or with a RuleContext
boolean passes = rule.isTrue(ctx);
Shorthand run with inline bindings:
rule.run(age -> 25, name -> "Alice");
| Status | Meaning |
|---|---|
PASS | preCondition passed, condition returned true, then-actions ran |
FAIL | preCondition passed, condition returned false, otherwise ran |
SKIP | preCondition returned false — rule was bypassed entirely |
RuleContext ctx = RuleContext.builder()
.tracer(Tracer.builder()
.listener(new RuliiListener() {
@Override
public void onRuleConditionCheck(Rule rule, Condition condition, boolean result) {
System.out.println(rule.getName() + " condition = " + result);
}
})
.build())
.build(bindings);
| Mistake | Fix |
|---|---|
No .name() on a lambda rule | Required — the framework throws IllegalArgumentException |
Calling .run(bindings) instead of .run(ctx) | Wrap bindings: RuleContext.builder().build(bindings), then rule.run(ctx) |
Expecting otherwise to fire on SKIP | It doesn't — only fires when condition is false, not when preCondition is false |
Passing a Bindings container as a single lambda to isTrue() | Pass individual BindingDeclaration lambdas: isTrue(age -> 25, name -> "x") |