CodeNarc - Creating a RuleSet

Contents

You can use the predefined RuleSets distributed with CodeNarc or you can create your own custom Groovy or XML RuleSet. See the site navigation menu for a list of the RuleSets and rules provided out of the box by CodeNarc.

Creating a Groovy RuleSet File

CodeNarc provides a Groovy DSL (domain-specific language) for defining RuleSets.

A Sample Groovy RuleSet

Here is an example of a Groovy RuleSet file:

import org.codenarc.rule.basic.ThrowExceptionFromFinallyBlockRule

ruleset {

    description 'A sample Groovy RuleSet'

    ruleset('rulesets/basic.xml') {
        'CatchThrowable' {
            priority = 1
            enabled = false
        }
        'EqualsAndHashCode' priority:3
        exclude 'Empty*'
    }

    rule(ThrowExceptionFromFinallyBlockRule) {
        priority = 3
    }

    rule("rules/MyCustomRuleScript.groovy")

    ruleset('MyGroovyRuleSet.groovy')
}

Things to note:

  • The Groovy RuleSet file itself must be accessible on the classpath.
  • The "outer" ruleset defines the contents of the RuleSet (within a closure). It can include an optional description and any combination of ruleset (other RuleSets) and rule statements (individual Rules).

About the "inner" ruleset statements:

  • Each ruleset statement loads a RuleSet file. The path specifies either a Groovy file or an XML file. By default, the paths specified are relative to the classpath. But these paths may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute path on the filesystem), or "http:".
  • The 'rulesets/basic.xml' RuleSet file is interpreted as an XML file based on the '.xml' extension. Likewise, the 'MyGroovyRuleSet.groovy' file is interpreted as a Groovy RuleSet file.
  • The RuleSet can be customized by following the RuleSet path with an (optional) closure, containing any combination of the following:
    1. Rule Configurations -- A ruleset statement can optionally provide configuration of the properties for individual rules within the RuleSet. Specify the name of the rule (as a String), followed by its configuration. (Remember to specify the rule name, not the class name. In most cases, the rule name is the class name without the "Rule" suffix). The name of the Rule can be followed by:
      1. A Map of property names and values. See 'EqualsAndHashCode' within the example.
      2. A closure containing property assignments statements. See 'CatchThrowable' within the example.

      Properties set this way can be of type String, int, long or boolean.

    2. Rule Filtering (include or exclude statements) -- A ruleset statement can optionally specify include and/or exclude pattern(s) of rule names to include or exclude from the RuleSet. See Filtering Rules Within a RuleSet.

About the rule statements:

  • Each rule statements loads a single Rule.
  • The rule statement must specify either:
    1. The class name for a Rule. See ThrowExceptionFromFinallyBlockRule within the example. The Rule class must be available on the classpath.
    2. The path to a Rule Script file. See "MyGroovyRuleSet.groovy" within the example. By default, the paths specified are relative to the classpath. But these paths may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute path on the filesystem) or "http:".
  • A rule can optionally provide configuration of the Rule properties by specifying a closure containing property assignment statements. See ThrowExceptionFromFinallyBlockRule within the example. As within ruleset statements, properties set this way can be of type String, int, long or boolean.

Creating an XML RuleSet File

The XML schema for a CodeNarc RuleSet file is embodied in the "ruleset-schema.xsd" file which is included within the CodeNarc jar. It contains three sections, all of which are optional, though the sections must be in the order listed:

XML Tag Purpose How many are allowed
<description> Describe the purpose of the RuleSet Zero or one
<ruleset-ref> Include a nested RuleSet, optionally configuring and/or
filtering the rules within it. The path to the RuleSet
can specify either an XML file or a Groovy RuleSet file.
Zero or more
<rule> Include a single rule; specify its fully-qualified classname Zero or more
<rule-script> Include a single rule implemented by a groovy script;
specify the path of the script. The path is relative to the
classpath by default, but can optionally specify a URL prefix.
Zero or more

A Sample XML RuleSet

Here is an example XML RuleSet file:

<ruleset xmlns="http://codenarc.org/ruleset/1.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://codenarc.org/ruleset/1.0 http://codenarc.org/ruleset-schema.xsd"
        xsi:noNamespaceSchemaLocation="http://codenarc.org/ruleset-schema.xsd">

    <description>Sample rule set</description>

    <ruleset-ref path='rulesets/imports.xml'>
        <rule-config name='DuplicateImport'>
            <property name='priority' value='1'/>
        </rule-config>
    </ruleset-ref>

    <ruleset-ref path='rulesets/basic.xml'>
        <exclude name='StringInstantiation'/>
    </ruleset-ref>

    <rule class='org.codenarc.rule.generic.IllegalRegexRule'>
        <property name="name" value="AuthorTagNotAllowed"/>
        <property name='regex' value='\@author'/>
    </rule>

    <rule-script path='rules/MyStaticFieldRule.groovy'/>

</ruleset>

Things to note:

  • The RuleSet file itself must be accessible on the classpath.
  • The top-level <ruleset> element must include the namespace declaration shown.

About the <ruleset-ref> elements:

  • By default, The path of the <ruleset-ref> is relative to the classpath. But these paths may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute path on the filesystem) or "http:".
  • You can optionally set arbitrary properties for the rules within the RuleSet by including one or more <property> elements within a <rule-config> element. In the example above, the DuplicateImportRule priority property is set to 1. Properties set this way can be of type String, int, long or boolean.
  • Remember that the name for a <rule-config> specifies the rule name, not the class name. (In most cases, the rule name is the class name without the "Rule" suffix.)

About the <rule> elements:

  • You must specify the fully-qualified class name for each Rule.
  • The Rule class must be available on the classpath.
  • You can optionally set arbitrary properties for individual rules by including a <property> element within a <rule>. For the IllegalRegexRule rule above, the name property is set to "AuthorTagNotAllowed" and the regex property is set to "\@author". Properties set this way can be of type String, int, long or boolean.
  • Because the name property is customized for the IllegalRegexRule, the localized rule description will be retrieved from the custom messages resource bundle file ("codenarc-messages.properties"), if that bundle file exists, otherwise a default generic message will be used. The resource bundle message key will be based on the customized name. In this case, the message key will be "AuthorTagNotAllowed.description".

About the <rule-script> elements:

  • You must specify the path for the rule script Groovy file. By default, this path is relative to the classpath. But these paths may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute path on the filesystem) or "http:".
  • The class defined within the rule script Groovy file must implement the org.codenarc.rule.Rule interface.
  • You can optionally set arbitrary properties for the rule by including a <property> element within a <rule-script> the same way you can for a <rule>. That is not shown here.

Filtering Rules Within a RuleSet

You can use the <include> and <exclude> elements within a <ruleset-ref> to filter a RuleSet and include and/or the exclude individual rules. In the following RuleSet excerpt, the entire "rulesets/basic.xml" RuleSet is included, except for the BooleanInstantiationRule and StringInstantiationRule.

    ruleset('rulesets/basic.xml') {
        exclude 'BooleanInstantiation'
        exclude 'StringInstantiation'
    }

And here is the same example in XML RuleSet format:

    <ruleset-ref path='rulesets/basic.xml'>
        <exclude name='BooleanInstantiation'/>
        <exclude name='StringInstantiation'/>
    </ruleset-ref>

Alternatively, you may wish to explicitly specify the rules that you want included from a RuleSet rather than those that are excluded. In the following RuleSet excerpt, ONLY the ReturnFromFinallyBlockRule and StringInstantiationRule rules are included from the "rulesets/basic.xml" RuleSet.

    ruleset('rulesets/basic.xml') {
        include 'ReturnFromFinallyBlockRule'
        include 'StringInstantiation'
    }

And here is the same example in XML RuleSet format:

    <ruleset-ref path='rulesets/basic.xml'>
        <include name='ReturnFromFinallyBlockRule'/>
        <include name='StringInstantiation'/>
    </ruleset-ref>

Note: In all cases, the rule name is specified, not the class name. (In most cases, the rule name is the class name without the "Rule" suffix.)

Note: If you specify at least one <include>, then ONLY rules matching an <include> will be included.

Note: If you specify an <include> and an <exclude> for a rule, then the <exclude> takes precedence.

Using Wildcards Within <include> and <exclude> Names

You may optionally include wildcard characters ('*' or '?') in the rule name for both <include> and an <exclude> elements. The wildcard character '*' within the name matches a sequence of zero or more characters in the rule name, while the wildcard character '?' within the name matches exactly one character in the rule name.

In the following RuleSet excerpt, all rules matching '*Instantiation' are included from the "rulesets/basic.xml" RuleSet. In this case, that will include the StringInstantiationRule and BooleanInstantiationRule rules.

    <ruleset-ref path='rulesets/basic.xml'>
        <include name='*Instantiation'/>
    </ruleset-ref>