Skip to content

Conversation

@codeconsole
Copy link
Contributor

@codeconsole codeconsole commented Dec 14, 2025

add-property CLI Command

Overview

The add-property command adds a new property to an existing Grails domain class, optionally with validation
constraints using either Grails constraints or Jakarta Validation annotations.

Usage

grails add-property FIELD:TYPE [OPTIONS]

Arguments

Argument Description Example
DOMAIN-CLASS The name of the domain class Book, com.example.Author
FIELD:TYPE Field name and type separated by colon title:String, pages:Integer

Supported Field Types

  • String
  • Integer
  • Long
  • Boolean
  • Date
  • BigDecimal
  • Double
  • Float
  • Short
  • Byte
  • Character

Options

Option Description
--nullable Mark the field as nullable
--not-nullable Mark the field as NOT nullable (generates @NotNull or nullable: false)
--blank Allow blank values (String fields only)
--not-blank Disallow blank values (generates @NotBlank or blank: false)
--max-size Maximum size constraint (String fields only)
--min-size Minimum size constraint (String fields only)
--constraint-style <STYLE> Constraint style: grails (default), jakarta, or both

Constraint Styles

Style Description
grails Uses Grails static constraints block (default)
jakarta Uses Jakarta Validation annotations (@NotNull, @NotBlank, @Size)
both Uses both Grails constraints AND Jakarta annotations

Examples

Basic field (no constraints)

add-field Book title:String
Result:

  class Book {
      String title
  }

With Grails constraints (default)

add-field Book title:String --not-nullable --max-size 255
Result:

  class Book {
      String title

      static constraints = {
          title nullable: false, maxSize: 255
      }
  }

With Jakarta Validation annotations

add-field Book title:String --not-nullable --not-blank --max-size 255 --constraint-style jakarta
Result:

  import jakarta.validation.constraints.NotNull
  import jakarta.validation.constraints.NotBlank
  import jakarta.validation.constraints.Size

  class Book {
      @NotNull
      @NotBlank
      @Size(max = 255)
      String title
  }

With both constraint styles

add-field Book title:String --not-nullable --max-size 255 --constraint-style both
Result:

  import jakarta.validation.constraints.NotNull
  import jakarta.validation.constraints.Size

  class Book {
      @NotNull
      @Size(max = 255)
      String title

      static constraints = {
          title nullable: false, maxSize: 255
      }
  }

Constraint Mapping

Option Grails Constraint Jakarta Annotation
--not-nullable nullable: false @NotNull
--nullable nullable: true (none)
--not-blank blank: false @notblank
--blank blank: true (none)
--max-size N maxSize: N @SiZe(max = N)
--min-size N minSize: N @SiZe(min = N)

Error Handling

  • Domain class not found: Displays error message suggesting to run create-domain-class first
  • Field already exists: Displays error message indicating the field already exists
  • Invalid field type: Displays error with list of supported types
  • Invalid constraint options: Displays specific validation error (e.g., --blank only valid for String)


implementation project(':grails-forge-core')
implementation "org.apache.grails.bootstrap:grails-bootstrap:$projectVersion"
implementation "org.codehaus.groovy:groovy:$groovyVersion"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Groovy 4.x is org.apache.groovy! This is polluting the classpath.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is in the forge-cli, which is still a micronaut app and on groovy 3.

Copy link
Contributor

@jdaugherty jdaugherty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only concern I have with this PR is the decision to put it in grails-bootstrap. I am assuming end user apps won't be generating fields at runtime of a Grails application so this is the wrong spot to place it.

} else {
int constraintBlockIndex = fieldInsertIndex + 1
lines.add(constraintBlockIndex, '')
lines.add(constraintBlockIndex + 1, ' static constraints = {')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't formatting be handled independently of line addition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you suggesting to reformat the entire file after making changes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to suggest extracting the spacing instead of hard coding the spaces into the argument.

i.e.

    int indent = 5
    "${indent*1}static constraints = {" 

This way you're not hard coding various length spaces and later if we have to change the indent / wrap the code, we don't have to manually modify every line.


implementation project(':grails-forge-core')
implementation "org.apache.grails.bootstrap:grails-bootstrap:$projectVersion"
implementation "org.codehaus.groovy:groovy:$groovyVersion"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is in the forge-cli, which is still a micronaut app and on groovy 3.

@matrei
Copy link
Contributor

matrei commented Jan 21, 2026

This looks OK to me.
I would probably prefer add-property or add-domain-property instead of add-field.

(We could move code generation classes to it's own module in Grails 8 if we want to get them out of grails-bootstrap.)

@matrei matrei added the feature label Jan 21, 2026
@matrei matrei added this to the grails:7.1.0 milestone Jan 21, 2026
@matrei matrei moved this to In Progress in Apache Grails Jan 21, 2026
@codeconsole
Copy link
Contributor Author

This looks OK to me. I would probably prefer add-property or add-domain-property instead of add-field.

(We could move code generation classes to it's own module in Grails 8 if we want to get them out of grails-bootstrap.)

@matrei I modeled it after Spring Roo
In Java, it is obviously adding a field, but in Groovy there seems to be confusion.

Is the Groovy consensus still that it is a property because it has no access modifier? This is from 2014:
[groovy-user] Property vs Field

@matrei
Copy link
Contributor

matrei commented Jan 23, 2026

Is the Groovy consensus still that it is a property because it has no access modifier?

@codeconsole
https://groovy-lang.org/objectorientation.html#_fields_and_properties

@codeconsole
Copy link
Contributor Author

codeconsole commented Jan 24, 2026

Is the Groovy consensus still that it is a property because it has no access modifier?

https://groovy-lang.org/objectorientation.html#_fields_and_properties

@matrei

ok, that is pretty clear. Thank you. Since there is no access modifier, I agree that we should use add-property for property additions.

Since add-field could also be useful, I think we should have add-field for fields and add-property for properties.

add-field now requires passing an access modifier.

@codeconsole codeconsole changed the title 7.1.x cli add-field for domain field additions 7.1.x cli add-field and add-property for domain field and property additions Jan 24, 2026
@matrei
Copy link
Contributor

matrei commented Jan 25, 2026

Since add-field could also be useful, I think we should have add-field for fields and add-property for properties.

@codeconsole What is the use case for adding a field to a domain class, and does that use case warrant a shell command?

@codeconsole
Copy link
Contributor Author

codeconsole commented Jan 25, 2026

Since add-field could also be useful, I think we should have add-field for fields and add-property for properties.

@codeconsole What is the use case for adding a field to a domain class, and does that use case warrant a shell command?

@matrei well, it's kind of a freebie due to inheritance. The original premise behind this PR was to slowly introduce codegen capabilities to where you could build a workable demo with cli commands. I've had use cases for static fields in my domain objects.

I suppose I am indifferent. Adding properties is what is most important. Should we get rid of add-field altogether?

@matrei
Copy link
Contributor

matrei commented Jan 26, 2026

Since add-field could also be useful, I think we should have add-field for fields and add-property for properties.

@codeconsole What is the use case for adding a field to a domain class, and does that use case warrant a shell command?

@matrei well, it's kind of a freebie due to inheritance. The original premise behind this PR was to slowly introduce codegen capabilities to where you could build a workable demo with cli commands. I've had use cases for static fields in my domain objects.

I suppose I am indifferent. Adding properties is what is most important. Should we get rid of add-field altogether?

@codeconsole My preference would be to remove add-field as it might make things more complicated for a user who has to think/remember which one to pick.

@codeconsole
Copy link
Contributor Author

@matrei ok, I will get rid of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants