Add method-based TagLib syntax with legacy compatibility, benchmarks, and docs#15465
Add method-based TagLib syntax with legacy compatibility, benchmarks, and docs#15465davydotcom wants to merge 11 commits into8.0.xfrom
Conversation
…pdate - implement method-defined tag handler support and invocation context - preserve closure-style behavior across property/direct and namespaced paths - convert built-in web/GSP taglibs to method syntax - add compile-time warning for closure-defined tag fields - add coverage and benchmark for method vs closure invocation - update guides and demo taglib samples to method syntax Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
- treat only Map parameter named attrs as full tag attributes map - allow other Map-typed parameters to bind from attribute key by parameter name - add regression tests for map-valued attribute binding and reserved attrs behavior Co-Authored-By: Oz <oz-agent@warp.dev>
- use private implementation helpers to avoid recursive dispatch in typed overloads - keep Map-based handlers for validation-safe fallback behavior - add regression test ensuring private/protected methods are not exposed as tag methods - document overload pattern for typed signatures with existing validation paths Co-Authored-By: Oz <oz-agent@warp.dev>
…gnment - keep typed overloads delegating to private implementation helpers - remove unnecessary attrs.name writes since typed args are sourced from attrs - preserve behavior validated by focused FormTagLib and method-tag test suites Co-Authored-By: Oz <oz-agent@warp.dev>
- add thread-safe ClassValue cache for invokable public tag methods by name - remove per-invocation getMethods scans in hasInvokableTagMethod/invokeTagMethod - optimize TagLibrary.propertyMissing by caching method fallback closures in non-dev mode - use resolved namespace for default-namespace fallback closures Co-Authored-By: Oz <oz-agent@warp.dev>
- restore attrs-reserved binding for paginate - route namespaced method tag calls via tag output capture - add fieldValue(Map) compatibility overload - harden form fields rendering/raw handling with method dispatch Co-Authored-By: Oz <oz-agent@warp.dev>
Doc ExamplesFiles: class SimpleTagLib {
static namespace = "my"
def example() { // ← new method added
def example = { attrs -> // ← old closure NOT removed
//...
}Same issue in
|
…egistered as tag methods Make helper methods private across all affected TagLib files to prevent TagMethodInvoker.isTagMethodCandidate() from matching them as tag methods. Remove convenience overloads (e.g. textField(String,Map)) entirely where Groovy 4's multimethod restriction forbids mixing private/public methods of the same name. Changes: - ApplicationTagLib: make renderResourceLink, doCreateLink private - FormatTagLib: make messageHelper private - UrlMappingTagLib: make appendClass private - ValidationTagLib: remove fieldValue(Map) overload, make formatValue private, remove formatValue from returnObjectForTags - FormTagLib: remove 5 typed convenience overloads, make renderNoSelectionOption private - FormFieldsTagLib: make 9 protected helper methods private - TagMethodInvoker: sort methods by descending param count to prefer (Map,Closure) over (Map) signatures - Checkstyle/CodeNarc fixes: alphabetical imports, blank lines before constructors, single-quoted strings
Summary
This PR introduces method-based TagLib handlers as the recommended syntax, while preserving full backward compatibility with closure-based handlers and legacy invocation paths.
Rebased onto
8.0.xfrom the original PR #15459.What's included
attrsandbody) for method handlersdef greeting(String name)binds fromname="...")Performance
The method-vs-closure benchmark added in this change set shows an approximately 7–10% improvement for method-based invocation in the covered scenarios.
TagLib syntax examples
Recommended (method-based)
Usage:
Legacy-compatible (closure field)
Validation performed
FormTagLib2TestsFormTagLib3TestsSelectTagTestsNamespacedTagLibMethodTestsTagLibMethodMissingSpecMethodDefinedTagLibSpec:grails-gsp:test:grails-test-examples-app1:integrationTest --tests functionaltests.MiscFunctionalSpecCo-Authored-By: Oz oz-agent@warp.dev