一键导入
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 页面并帮你完成安装。
基于 SOC 职业分类
Guide for declaring rulii RuleFlows in Spring XML using the rulii namespace — covers the command grammar (run/bind/when/for-each/scope), async execution, exception handling, custom commands, terse attribute forms, and running flows
Guide for declaring rulii Rules, RuleSets, and predefined validators in Spring XML using the rulii namespace — covers namespace setup, SpEL expressions, lifecycle hooks, validation rules, and loading via @RuleScan
Diagnostic guide for troubleshooting rulii rules that aren't firing, returning unexpected PASS/FAIL/SKIP, or throwing exceptions — covers bindings, types, scope, tracing, and Spring context issues
Guide for creating a RuleSet in rulii — covers builder API, lifecycle hooks, input params, stop conditions, validating mode, async execution, and Spring @Bean wiring
Guide for writing JUnit 5 tests in the rulii-spring project — Spring Boot test setup, @RuleScan wiring, PASS/FAIL/SKIP patterns, ValidationRule patterns, and missing-binding 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());
In a Spring context, rules discovered via @RuleScan are Spring-managed. Use @Value, @Autowired, or constructor injection:
@Rule
@Description("Validates that fromDate is before toDate.")
public class ConsistentDateRule {
@Value("${errorCode.100}")
private String message;
@PreCondition
public boolean check(LocalDate fromDate, LocalDate toDate) {
return fromDate != null && toDate != null;
}
@Given
public boolean isValid(LocalDate fromDate, LocalDate toDate) {
return fromDate.isBefore(toDate);
}
@Otherwise
public void otherwise(LocalDate fromDate, LocalDate toDate, RuleViolations violations) {
violations.add(RuleViolation.builder().build("consistentDateRule", "errorCode.100", message));
}
}
For rules built manually outside the scan, pass ObjectFactory to retain Spring support:
@Bean
public Rule myRule(ObjectFactory objectFactory) {
return Rule.builder()
.with(MyRule.class, objectFactory)
.build();
}
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 |
| 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") |
Spring @Value not injected in manually-built rule | Use Rule.builder().with(MyRule.class, objectFactory).build() instead of Rule.builder().build(MyRule.class) |