8.x Add statically-resolvable message(Map) for controllers#15733
8.x Add statically-resolvable message(Map) for controllers#15733codeconsole wants to merge 3 commits into
Conversation
…TagLibraryInvoker
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #15733 +/- ##
==================================================
+ Coverage 48.3255% 48.4136% +0.0880%
- Complexity 15216 15257 +41
==================================================
Files 1875 1875
Lines 85818 85852 +34
Branches 14969 14972 +3
==================================================
+ Hits 41472 41564 +92
+ Misses 37975 37883 -92
- Partials 6371 6405 +34
🚀 New features to boost your workflow:
|
fd477a0 to
0f802b2
Compare
✅ All tests passed ✅🏷️ Commit: 0f802b2 Learn more about TestLens at testlens.app. |
| * {@code message}, {@code locale}, {@code encodeAs}) | ||
| * @return the resolved message | ||
| */ | ||
| Object message(Map attrs) { |
There was a problem hiding this comment.
This is the wrong solution: we basically are copying/moving the functionality for a specific tag. You can inject the taglib directly to get static support. We should consider the registry solution I proposed last week or the static solution that defers to dynamic dispatch.
|
The current implementation in Regarding the suggestion to use a registry or a static solution that defers to dynamic dispatch:
Since the current implementation is already a static method that defers to the dynamic grails-gsp/grails-web-taglib/src/main/groovy/grails/artefact/gsp/ControllerTagLibraryInvoker.groovy |
|
in favor of #15669 |
Summary
message(code: ..., args: [...])is by far the most common tag invoked as a method from controllers — it is what scaffolding generates for every flash message:Today that call only resolves via
TagLibraryInvoker.methodMissing, so under@CompileStatic/@GrailsCompileStaticit fails to compile and forces@CompileDynamiconto exactly the controller actions that are otherwise fully statically compilable (the same pain #15715 addressed for attribute access). This PR makes the canonical idiom compile statically, verbatim.Design
A new trait
grails.artefact.gsp.ControllerTagLibraryInvoker extends TagLibraryInvokerdeclares a realObject message(Map attrs), andControllerTagLibraryTraitInjectornow injects it (controllers keep the full inheritedTagLibraryInvokerAPI).methodMissing(declaredtaglibNamespacefirst, then the default namespace) and invokes it through the sameTagOutput.captureTagOutput(..., OutputContextLookupHelper.lookupOutputContext())machinery that GSP-compiled code and theTagLibraryMetaUtils-registered meta-methods use — so attribute handling (code,args,default,error,message,locale,encodeAs), encoding, and return semantics are identical to the dynamic path. When no tag library provides the tag, it throwsMissingMethodException, matchingmethodMissing's failure mode.TagLibraryInvokeritself: tag libraries also implementTagLibraryInvoker(viagrails.artefact.TagLibrary), and a realmessagemethod inherited by every tag library is picked up byTagMethodInvokeras if the library declared amessagetag — recursing infinitely for libraries that don't (caught bygrails-test-suite-uberduring development). Scoping the method to the controller-injected trait avoids leaking it into the tag-dispatch path. The javadoc documents this constraint.Tests
New
ControllerTagLibraryInvokerMessageSpec:messagetag of the default namespace with attrs passed throughtaglibNamespace, falling back to the default — mirroringmethodMissingorderMissingMethodExceptionwhen no tag library provides the tag@CompileStaticcontroller-style class invoking the canonical idiom compiles and resolves (this guard fails test compilation without the trait method):grails-web-taglib:test,:grails-gsp:test,:grails-test-suite-web:test, and:grails-test-suite-uber:testall pass.