codeconsole opened a new pull request, #16322:
URL: https://github.com/apache/grails-core/pull/16322

   ## Description
   
   Two defects that stop a freshly generated app from working. Both are in the 
generated GSP views, and both surface the moment you use the app the generator 
produces.
   
   ### 1. Scaffolded `index.gsp` declares the count model field as `Integer`
   
   `grails generate-views` produces an `index.gsp` whose typed model directive 
declares:
   
   ```
   @{ model="List<website.Sample> sampleList; Integer sampleCount" }
   ```
   
   but the `Service.groovy` template declares `Long count()`, and 
`Controller.groovy` passes that value straight through as the 
`${propertyName}Count` model entry. Model fields are populated by reflective 
`Field.set`, which does no numeric conversion, so **every scaffolded index view 
throws on first render**:
   
   ```
   java.lang.IllegalArgumentException: Can not set java.lang.Integer field
     ..._views_sample_index_gsp.sampleCount to java.lang.Long
       at 
org.grails.gsp.GroovyPage.applyModelFieldsFromBinding(GroovyPage.java:185)
   ```
   
   Declaring the field as `Long` matches what the service returns.
   
   While tracking this down, the error itself proved harder to read than it 
needed to be. `GroovyPage.applyModelFieldsFromBinding` caught only 
`IllegalAccessException`, which cannot occur — 
`GroovyPageMetaInfo.initializeModelFields` already calls 
`ReflectionUtils.makeAccessible` on every model field. The one failure that 
*can* occur, a type mismatch, escaped uncaught and surfaced as a bare JDK 
reflection message naming the mangled generated page class. It now reports 
something actionable:
   
   ```
   GroovyPagesException: Model field 'sampleCount' is declared as 
java.lang.Integer
     but the model supplied an instance of java.lang.Long. Declare the field 
with a
     type the model value is assignable to; model values are not coerced.
   ```
   
   Strict assignment is kept deliberately — coercing would silently narrow 
`Long` to `Integer`, and the typed model directive exists to give these pages 
real types.
   
   ### 2. Welcome page does not compile under GSP static compilation
   
   Adding the following to a generated app's `build.gradle` fails 
`compileGroovyPages`:
   
   ```groovy
   grails {
       compileStatic {
           all = true
           gsp = true
       }
   }
   ```
   
   ```
   [Static type checking] - Cannot find matching method 
java.lang.Object#toLowerCase()
   [Static type checking] - No such property: name for class: java.lang.Object
   ```
   
   Four `sort` closures in the welcome page take untyped parameters, so static 
type checking infers `Object` for the element and rejects the calls on it. Each 
loses the element type for a different reason:
   
   | Site | Why the element is `Object` |
   | --- | --- |
   | plugin list | `.collect { p, i -> [plugin: p, order: ...] }` — map literal 
values are `Object` |
   | `domainsByPlugin` | the `groupBy` closure returns a `def` local, so the 
key is `Object` |
   | `appListeners` | same map-literal inference |
   | `mimeTypes` | `applicationContext.getBean('mimeTypes')` returns `Object` |
   
   Fixed by typing the closure parameters and converting the values whose 
static type is `Object`. The plugin and mime type comparisons need the concrete 
element type, so those are cast.
   
   One wrinkle worth recording: `grails.plugins.GrailsPlugin` cannot be 
imported into a GSP, because the compiler already auto-imports the unrelated 
`grails.plugins.metadata.GrailsPlugin` annotation and the collision fails the 
build with `The name GrailsPlugin is already declared`. The cast uses the 
qualified name instead.
   
   `grails-forge-core`'s resource copy and the web profile skeleton copy of 
this page were byte-identical, so both are updated and remain identical.
   
   ## Verification
   
   Beyond the added unit tests, this was verified end to end against a 
generated app:
   
   - Built the CLI from this branch and published these artifacts to 
`mavenLocal`, then generated an app with them (`create-app`, 
`create-domain-class`, `generate-controller`, `generate-views`, 
`install-templates`).
   - The generated `index.gsp` now declares `Long sampleCount`; `/sample/index` 
returns 200 where it previously threw.
   - Temporarily forcing the declaration back to `Integer` produces the new 
diagnostic rather than the raw reflection error.
   - With `compileStatic { all = true; gsp = true }` enabled, `./gradlew 
bootJar` succeeds, and the resulting jar serves both `/` and `/sample/index` 
with a clean log.
   
   ## Note on scope
   
   These are two separate root causes. They are submitted together because they 
are the two things that stop the same freshly generated app from working, and 
the first is what led to the second being found. Happy to split them into 
separate PRs if reviewers prefer.
   
   ## Contributor Checklist
   
   Please review the following checklist before submitting your pull request. 
Pull requests that do not meet these requirements may be closed without review.
   
   ### Issue and Scope
   
   - [ ] This PR is linked to an existing issue that has been **acknowledged or 
approved** by the project team. If no approved issue exists, please give 
background on why this change is necessary.  Tickets are preferred for release 
change log history.
   - [ ] This PR addresses the **complete scope** of the linked issue. Partial 
implementations or unfinished work should not be submitted for review.
   - [ ] This PR contains a **single, focused change**. Unrelated changes 
should be submitted as separate pull requests.
   - [ ] This PR targets the **correct branch** for the type of change:
       - **Patch release branches** (e.g., `7.0.x`): Bug fixes only. No new 
features or API changes.
       - **Minor release branches** (e.g., `7.1.x`): New features are welcome, 
but breaking existing APIs must be avoided.
       - **Major release branches** (e.g., `8.0.x`): Reserved for major 
changes. Breaking API changes are permitted.
   
   ### Code Quality
   
   - [ ] I have **added or updated tests** that cover the changes introduced in 
this PR. All code contributions are expected to include appropriate test 
coverage.
   - [ ] I have verified that all existing tests pass by running `./gradlew 
build --rerun-tasks`.
   - [ ] My code follows the project's **code style** guidelines. I have run 
`./gradlew codeStyle` and resolved any violations. See [Code 
Style](../CONTRIBUTING.md#code-style) for details.
   - [ ] This PR does **not** include mass reformatting, style-only changes, or 
large-scale refactoring unless it was **explicitly approved** in the linked 
issue. Unsolicited reformatting will not be accepted.
   - [ ] If generative AI tooling was used in preparing this contribution, a 
quality model was used to ensure contributions are **consistent with the 
project's quality standards**.
   
   ### Licensing and Attribution
   
   - [ ] All contributed code is provided under the [Apache License 
2.0](https://www.apache.org/licenses/LICENSE-2.0), and new source files include 
the appropriate **Apache license header**.
   - [ ] I have the necessary rights to submit this contribution and confirm it 
is my own original work (see [Legal 
Notice](../CONTRIBUTING.md#i-want-to-contribute)).
   - [ ] If generative AI tooling was used in preparing this contribution, I 
have followed the [Apache Software Foundation's policy on generative 
tooling](https://www.apache.org/legal/generative-tooling.html) and have 
properly attributed its use.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to