diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md index 4505ff2..7eb1e7f 100644 --- a/docs/USERGUIDE.md +++ b/docs/USERGUIDE.md @@ -19,38 +19,40 @@ Architecture rules are defined using Taikai's fluent API, allowing developers to ## 3. Usage -| Category | Subcategory | Method Name | Rule Description | Import Options | -|-------------|----------------|-------------|------------------|-------------------------| -| **Java** | General | `classesShouldImplementHashCodeAndEquals` | Classes should implement `hashCode` and `equals` | Default (WITHOUT_TESTS) | -| | General | `fieldsShouldNotBePublic` | Fields should not be `public` (except constants) | Default (WITHOUT_TESTS) | -| | General | `methodsShouldNotThrowGenericException` | Methods should not throw generic exceptions (`Exception`, `RuntimeException`) | Default (WITHOUT_TESTS) | -| | General | `noUsageOf` | Disallow usage of specific classes | Default (WITHOUT_TESTS) | -| | General | `noUsageOf` | Disallow usage of specific classes by class reference | Default (WITHOUT_TESTS) | -| | General | `noUsageOfDeprecatedAPIs` | No usage of deprecated APIs annotated with `Deprecated` | Default (WITHOUT_TESTS) | -| | General | `noUsageOfSystemOutOrErr` | Disallow usage of `System.out` or `System.err` | Default (WITHOUT_TESTS) | -| | General | `utilityClassesShouldBeFinalAndHavePrivateConstructor` | Utility classes should be `final` and have a private constructor | Default (WITHOUT_TESTS) | -| | Imports | `shouldHaveNoCycles` | No cyclic dependencies in imports | Default (WITHOUT_TESTS) | -| | Imports | `shouldNotImport` | Disallow specific imports (e.g., `..shaded..`) | Default (WITHOUT_TESTS) | -| | Naming | `classesShouldNotMatch` | Classes should not match specific naming patterns (e.g., `.*Impl`) | Default (WITHOUT_TESTS) | -| | Naming | `constantsShouldFollowConvention` | Constants should follow naming conventions | Default (WITHOUT_TESTS) | -| | Naming | `interfacesShouldNotHavePrefixI` | Interfaces should not have the prefix `I` | Default (WITHOUT_TESTS) | -| **Test** | JUnit 5 | `jclassesShouldNotBeAnnotatedWithDisabled` | Ensure JUnit 5 classes are not annotated with `@Disabled` | Default (WITH_TESTS) | -| | JUnit 5 | `jmethodsShouldNotBeAnnotatedWithDisabled` | Ensure JUnit 5 methods are not annotated with `@Disabled` | Default (WITH_TESTS) | -| **Spring** | Boot | `springBootApplicationShouldBeIn` | Ensure `@SpringBootApplication` is in the default package | Default (WITH_TESTS) | -| | Configurations | `namesShouldEndWithConfiguration` | Configuration classes should end with "Configuration" | Default (WITH_TESTS) | -| | Configurations | `namesShouldMatch` | Configuration classes should match a regex pattern | Default (WITH_TESTS) | -| | Controllers | `namesShouldEndWithController` | Controllers should end with "Controller" | Default (WITH_TESTS) | -| | Controllers | `namesShouldMatch` | Controllers should match a regex pattern | Default (WITH_TESTS) | -| | Controllers | `shouldBeAnnotatedWithRestController` | Controllers should be annotated with `@RestController` | Default (WITH_TESTS) | -| | Controllers | `shouldBePackagePrivate` | Controllers should be package-private | Default (WITH_TESTS) | -| | Controllers | `shouldNotDependOnOtherControllers` | Controllers should not depend on other controllers | Default (WITH_TESTS) | -| | General | `noAutowiredFields` | Fields should not be annotated with `@Autowired` (prefer constructor injection) | Default (WITH_TESTS) | -| | Repositories | `namesShouldEndWithRepository` | Repositories should end with "Repository" | Default (WITH_TESTS) | -| | Repositories | `namesShouldMatch` | Repositories should match a regex pattern | Default (WITH_TESTS) | -| | Repositories | `shouldBeAnnotatedWithRepository` | Repositories should be annotated with `@Repository` | Default (WITH_TESTS) | -| | Services | `namesShouldEndWithService` | Services should end with "Service" | Default (WITH_TESTS) | -| | Services | `namesShouldMatch` | Services should match a regex pattern | Default (WITH_TESTS) | -| | Services | `shouldBeAnnotatedWithService` | Services should be annotated with `@Service` | Default (WITH_TESTS) | +| Category | Subcategory | Method Name | Rule Description | Import Options | +|-------------|----------------|--------------------------------------------------------|---------------------------------------------------------------------------------|-------------------------| +| **Java** | General | `classesShouldImplementHashCodeAndEquals` | Classes should implement `hashCode` and `equals` | Default (WITHOUT_TESTS) | +| | General | `fieldsShouldNotBePublic` | Fields should not be `public` (except constants) | Default (WITHOUT_TESTS) | +| | General | `methodsShouldNotThrowGenericException` | Methods should not throw generic exceptions (`Exception`, `RuntimeException`) | Default (WITHOUT_TESTS) | +| | General | `noUsageOf` | Disallow usage of specific classes | Default (WITHOUT_TESTS) | +| | General | `noUsageOf` | Disallow usage of specific classes by class reference | Default (WITHOUT_TESTS) | +| | General | `noUsageOfDeprecatedAPIs` | No usage of deprecated APIs annotated with `Deprecated` | Default (WITHOUT_TESTS) | +| | General | `noUsageOfSystemOutOrErr` | Disallow usage of `System.out` or `System.err` | Default (WITHOUT_TESTS) | +| | General | `utilityClassesShouldBeFinalAndHavePrivateConstructor` | Utility classes should be `final` and have a private constructor | Default (WITHOUT_TESTS) | +| | Imports | `shouldHaveNoCycles` | No cyclic dependencies in imports | Default (WITHOUT_TESTS) | +| | Imports | `shouldNotImport` | Disallow specific imports (e.g., `..shaded..`) | Default (WITHOUT_TESTS) | +| | Naming | `classesShouldNotMatch` | Classes should not match specific naming patterns (e.g., `.*Impl`) | Default (WITHOUT_TESTS) | +| | Naming | `methodsShouldNotMatch` | Methods should not match specific naming patterns | Default (WITHOUT_TESTS) | +| | Naming | `fieldsShouldNotMatch` | Fields should not match specific naming patterns | Default (WITHOUT_TESTS) | +| | Naming | `constantsShouldFollowConvention` | Constants should follow naming conventions | Default (WITHOUT_TESTS) | +| | Naming | `interfacesShouldNotHavePrefixI` | Interfaces should not have the prefix `I` | Default (WITHOUT_TESTS) | +| **Test** | JUnit 5 | `jclassesShouldNotBeAnnotatedWithDisabled` | Ensure JUnit 5 classes are not annotated with `@Disabled` | Default (WITH_TESTS) | +| | JUnit 5 | `jmethodsShouldNotBeAnnotatedWithDisabled` | Ensure JUnit 5 methods are not annotated with `@Disabled` | Default (WITH_TESTS) | +| **Spring** | Boot | `springBootApplicationShouldBeIn` | Ensure `@SpringBootApplication` is in the default package | Default (WITH_TESTS) | +| | Configurations | `namesShouldEndWithConfiguration` | Configuration classes should end with "Configuration" | Default (WITH_TESTS) | +| | Configurations | `namesShouldMatch` | Configuration classes should match a regex pattern | Default (WITH_TESTS) | +| | Controllers | `namesShouldEndWithController` | Controllers should end with "Controller" | Default (WITH_TESTS) | +| | Controllers | `namesShouldMatch` | Controllers should match a regex pattern | Default (WITH_TESTS) | +| | Controllers | `shouldBeAnnotatedWithRestController` | Controllers should be annotated with `@RestController` | Default (WITH_TESTS) | +| | Controllers | `shouldBePackagePrivate` | Controllers should be package-private | Default (WITH_TESTS) | +| | Controllers | `shouldNotDependOnOtherControllers` | Controllers should not depend on other controllers | Default (WITH_TESTS) | +| | General | `noAutowiredFields` | Fields should not be annotated with `@Autowired` (prefer constructor injection) | Default (WITH_TESTS) | +| | Repositories | `namesShouldEndWithRepository` | Repositories should end with "Repository" | Default (WITH_TESTS) | +| | Repositories | `namesShouldMatch` | Repositories should match a regex pattern | Default (WITH_TESTS) | +| | Repositories | `shouldBeAnnotatedWithRepository` | Repositories should be annotated with `@Repository` | Default (WITH_TESTS) | +| | Services | `namesShouldEndWithService` | Services should end with "Service" | Default (WITH_TESTS) | +| | Services | `namesShouldMatch` | Services should match a regex pattern | Default (WITH_TESTS) | +| | Services | `shouldBeAnnotatedWithService` | Services should be annotated with `@Service` | Default (WITH_TESTS) | ### Java Configuration @@ -125,7 +127,7 @@ Taikai.builder() .check(); ``` -- **Naming Configuration**: Define naming conventions for classes, constants, and interfaces. +- **Naming Configuration**: Define naming conventions for classes, methods, fields, constants, and interfaces. ```java Taikai.builder() @@ -133,6 +135,8 @@ Taikai.builder() .java(java -> java .naming(naming -> naming .classesShouldNotMatch(".*Impl") + .methodsShouldNotMatch("blameThrower") + .fieldsShouldNotMatch("notGonnaGiveYouUp") .constantsShouldFollowConvention() .interfacesShouldNotHavePrefixI()))) .build() diff --git a/src/main/java/com/enofex/taikai/java/NamingConfigurer.java b/src/main/java/com/enofex/taikai/java/NamingConfigurer.java index 4db5b08..66a1830 100644 --- a/src/main/java/com/enofex/taikai/java/NamingConfigurer.java +++ b/src/main/java/com/enofex/taikai/java/NamingConfigurer.java @@ -3,6 +3,8 @@ import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noFields; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noMethods; import com.enofex.taikai.TaikaiRule; import com.enofex.taikai.TaikaiRule.Configuration; @@ -31,6 +33,26 @@ public NamingConfigurer classesShouldNotMatch(String regex, Configuration config .as("Classes should not have names matching %s".formatted(regex)), configuration)); } + public NamingConfigurer methodsShouldNotMatch(String regex) { + return methodsShouldNotMatch(regex, null); + } + + public NamingConfigurer methodsShouldNotMatch(String regex, Configuration configuration) { + return addRule(TaikaiRule.of(noMethods() + .should().haveNameMatching(regex) + .as("Methods should not have names matching %s".formatted(regex)), configuration)); + } + + public NamingConfigurer fieldsShouldNotMatch(String regex) { + return fieldsShouldNotMatch(regex, null); + } + + public NamingConfigurer fieldsShouldNotMatch(String regex, Configuration configuration) { + return addRule(TaikaiRule.of(noFields() + .should().haveNameMatching(regex) + .as("Fields should not have names matching %s".formatted(regex)), configuration)); + } + public NamingConfigurer interfacesShouldNotHavePrefixI() { return interfacesShouldNotHavePrefixI(null); } diff --git a/src/test/java/com/enofex/taikai/Usage.java b/src/test/java/com/enofex/taikai/Usage.java index bc14985..761c248 100644 --- a/src/test/java/com/enofex/taikai/Usage.java +++ b/src/test/java/com/enofex/taikai/Usage.java @@ -17,10 +17,10 @@ public static void main(String[] args) { .namesShouldMatch("regex")) .controllers(controllers -> controllers .shouldBeAnnotatedWithRestController() - .namesShouldEndWithController() - .namesShouldMatch("regex") .shouldNotDependOnOtherControllers() - .shouldBePackagePrivate()) + .shouldBePackagePrivate() + .namesShouldEndWithController() + .namesShouldMatch("regex")) .services(services -> services .shouldBeAnnotatedWithService() .namesShouldMatch("regex") @@ -34,14 +34,14 @@ public static void main(String[] args) { .classesShouldNotBeAnnotatedWithDisabled() .methodsShouldNotBeAnnotatedWithDisabled())) .java(java -> java + .noUsageOf(Date.class) + .noUsageOf(Calendar.class) + .noUsageOf("java.text.SimpleDateFormat") .noUsageOfSystemOutOrErr() .noUsageOfDeprecatedAPIs() .classesShouldImplementHashCodeAndEquals() .methodsShouldNotThrowGenericException() .utilityClassesShouldBeFinalAndHavePrivateConstructor() - .noUsageOf(Date.class) - .noUsageOf(Calendar.class) - .noUsageOf("java.text.SimpleDateFormat") .imports(imports -> imports .shouldHaveNoCycles() .shouldNotImport("..shaded..") @@ -49,11 +49,12 @@ public static void main(String[] args) { .shouldNotImport("org.junit..")) .naming(naming -> naming .classesShouldNotMatch(".*Impl") + .methodsShouldNotMatch("foo") + .fieldsShouldNotMatch("bar") .constantsShouldFollowConvention() .interfacesShouldNotHavePrefixI())) .build(); taikai.check(); } - }