ruthst00 commented on PR #16453:
URL: https://github.com/apache/grails-core/pull/16453#issuecomment-5939043687
@jdaugherty, @matrei, as always, thanks very much for your feedback. Here's
a summary of changes:
#### Four files were changed in
`grails-test-examples/views-functional-tests`:
1. **`RenderTemplateController.groovy`** (new) — a minimal controller whose
`index()` action calls `render(template: 'snippet', model: [message: 'hello
from template'])`. This is the exact public flow that stores the model in
`GrailsApplicationAttributes.TEMPLATE_MODEL` rather than in a `ModelAndView`,
exercising the fallback branch of `Interceptor#getModel()` that the existing
unit tests only covered by directly setting the request attribute.
2. **`grails-app/views/renderTemplate/_snippet.gsp`** (new) — the GSP
partial template rendered by the controller, containing `${message}` so the
render call succeeds end-to-end.
3. **`ModelInterceptor.groovy`** — extended the controller regex from
`'modelAndView|respond|return'` to
`'modelAndView|respond|return|renderTemplate'` so the interceptor fires for the
new controller.
4. **`ModelInterceptorIntSpec.groovy`** — added a new feature method
`"interceptor after() can read model set by render(template:..., model:...) via
TEMPLATE_MODEL fallback"` that hits `/renderTemplate`, asserts HTTP 200, and
then asserts that `modelInterceptor.latestModel` is non-null, is a `Map`, and
contains `message == 'hello from template'` — verifying the actual model
contents rather than just presence.
#### Added a `NOTE` block and annotated code example to
`grails-doc/src/en/guide/theWebLayer/interceptors/definingInterceptors.adoc`
(after the existing `after()` example, lines 48–59) that explains the timing
distinction between view rendering and template rendering:
- **View rendering** (`render(view: ..., model: [...])`) — Spring MVC defers
the actual render until after `after()` returns, so modifications to `model`
and `view` in `after()` are reflected in the final response. This is the
existing documented behavior.
- **Template rendering** (`render(template: ..., model: [...])`) —
`ResponseRenderer` processes the template and writes the response immediately
inside the controller's `render()` call, before `after()` is invoked. The model
is stored on the request as `GrailsApplicationAttributes.TEMPLATE_MODEL` (the
fallback branch of `Interceptor#getModel()`), so `after()` can *read* it, but
any modifications will not affect the already-written response.
The explanation is backed by the source code (`ResponseRenderer.groovy` sets
`TEMPLATE_MODEL` synchronously, `Interceptor#getModel()` reads it as a
fallback) and by the passing integration test `ModelInterceptorIntSpec` that
was already in the repository confirming this exact behavior.
#### Updated documentation with the follow notes and caveats:
- Bean and collection renders behave the same after this change and return
null or part of the model.
- For render template:, model is read-only information about what was
rendered, and changing model or view in after() has no effect.
#### The second test in `ModelInterceptorIntSpec` has been strengthened with
a direct map equality assertion. Three files were updated:
1. RenderTemplateController.groovy — changed the model from `[message:
'hello from template']` to `[title: 'x']`
2. _snippet.gsp — updated the template variable from `${message}` to
`${title}`
3. ModelInterceptorIntSpec.groovy — replaced the piecemeal `!= null` /
`instanceof Map` / `.message == ...` assertions with a single direct equality
check: `modelInterceptor.latestModel == [title: 'x']`
#### Updated getModel() javadoc
Mentioned the render template: fallback and that MODEL_AND_VIEW takes
precedence.
--
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]