You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by da...@apache.org on 2026/02/26 13:01:13 UTC
(grails-core) 04/08: Bind Map tag method args by name except reserved attrs map
This is an automated email from the ASF dual-hosted git repository.
davydotcom pushed a commit to branch feature/taglib-method-actions
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit f42e058c6edd0fe67119e5b45b059beaeefd7b8e
Author: David Estes <da...@gmail.com>
AuthorDate: Wed Feb 25 21:59:21 2026 -0500
Bind Map tag method args by name except reserved attrs map
- 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...@warp.dev>
---
.../main/groovy/org/grails/taglib/TagMethodInvoker.java | 4 ++--
.../grails/web/taglib/MethodDefinedTagLibSpec.groovy | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/TagMethodInvoker.java b/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/TagMethodInvoker.java
index 5669a77512..cf19e2fb4b 100644
--- a/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/TagMethodInvoker.java
+++ b/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/TagMethodInvoker.java
@@ -126,8 +126,9 @@ public final class TagMethodInvoker {
Parameter[] parameters = method.getParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
+ String parameterName = parameters[i].getName();
Class<?> parameterType = parameters[i].getType();
- if (Map.class.isAssignableFrom(parameterType)) {
+ if (Map.class.isAssignableFrom(parameterType) && "attrs".equals(parameterName)) {
args[i] = attrs;
continue;
}
@@ -135,7 +136,6 @@ public final class TagMethodInvoker {
args[i] = body != null ? body : TagOutput.EMPTY_BODY_CLOSURE;
continue;
}
- String parameterName = parameters[i].getName();
Object value = attrs != null ? attrs.get(parameterName) : null;
if (value == null && parameters.length == 1 && attrs != null && attrs.size() == 1) {
value = attrs.values().iterator().next();
diff --git a/grails-gsp/plugin/src/test/groovy/org/grails/web/taglib/MethodDefinedTagLibSpec.groovy b/grails-gsp/plugin/src/test/groovy/org/grails/web/taglib/MethodDefinedTagLibSpec.groovy
index ae636b4996..3deeb6171c 100644
--- a/grails-gsp/plugin/src/test/groovy/org/grails/web/taglib/MethodDefinedTagLibSpec.groovy
+++ b/grails-gsp/plugin/src/test/groovy/org/grails/web/taglib/MethodDefinedTagLibSpec.groovy
@@ -43,6 +43,15 @@ class MethodDefinedTagLibSpec extends Specification implements TagLibUnitTest<Me
applyTemplate('<g:multiTypedTag first="hello" second="world" />') == 'hello-world'
}
+ void "method tag can bind map-valued attribute to map-typed argument by parameter name"() {
+ expect:
+ applyTemplate('<g:mapValueTag config="${[k:\'v\']}" />') == 'v'
+ }
+ void "method tag still supports reserved attrs map parameter"() {
+ expect:
+ applyTemplate('<g:attrsMapTag blah="duh" />') == 'duh'
+ }
+
void "method tag can use implicit body closure"() {
expect:
applyTemplate('<g:bodyTag>abc</g:bodyTag>') == 'before-abc-after'
@@ -100,6 +109,14 @@ class MethodTagLib {
out << "${first}-${second}"
}
+ def mapValueTag(Map config) {
+ out << "${config.k}"
+ }
+
+ def attrsMapTag(Map attrs) {
+ out << "${attrs.blah}"
+ }
+
def bodyTag() {
out << "before-${body()}-after"
}