grails reflection - Calling a dynamic tag - grails

I know this is odd, but I'm trying to provide a pass through interface in a taglib that allows the caller to pass in any other tag to be displayed in a container with additional processing.
To this effect, I'm trying to dynamically call an arbitrary tag in an arbitrary namespace. This may be clearer by example.
GSP:
<myLib:myTag someProp="blah" anotherProp="blah2" size="80" namespace="g" tag="textField">
In my taglib, I'm trying to display the tag they pass.
Taglib:
def myTag = {
String id = //some processing, not specified by caller
attrs.put("id", id)
def namespace = attrs.remove("namespace")
def tag = attrs.remove("tag")
out << ?????
}
The problem comes after the out... I'm having trouble calling the tag. I've tried the following with the following errors
namespace.tag(attrs) //No signature of method: java.lang.String.tag()
namespace."${tag}"(attrs) //No signature of method: java.lang.String.textField()
"${namespace}"."${tag}"(attrs) //No signature of method: java.lang.String.textField()
This seems to work, but the method needs to support tags in other namespaces
g."${tag}"(attrs)
So the question is How can I use reflection to use a dynamically defined taglib?
I don't have them pass the fully formed tag in the body() because I need to interact with it in the taglib.

I believe the following works, I'm not completely certain this is the best answer, so will leave the question open for suggestions from others.
String namespace = attrs.remove("namespace")
String tag = attrs.remove("tag")
GrailsTagLibClass tagLibClass = grailsApplication.getTagLibClasses().find { it.namespace == namespace && it.hasTag(tag)}
def tagLibBean = grailsApplication.mainContext.getBean(tagLibClass.getFullName())
out << tagLibBean."${tag}"(attrs)

Related

Combina pyramid with jinja2 install_gettext_translations function?

I have a pyramid application that uses a translation factory defined in this way:
from pyramid.i18n import get_localizer, TranslationStringFactory
from pyramid.threadlocal import get_current_request
def add_renderer_globals(event):
request = event.get('request')
if request is None:
request = get_current_request()
event['_'] = request.translate
event['localizer'] = request.localizer
tsf = TranslationStringFactory('climmob3')
def add_localizer(event):
request = event.request
localizer = get_localizer(request)
def auto_translate(string):
return localizer.translate(tsf(string))
request.localizer = localizer
request.translate = auto_translate
It works fine, however somewhere else I use jinja2 render() function to render small pieces of reusable code (snippets) as a jinja2 extension:
from jinja2 import Environment
jinjaEnv = Environment(extensions=['jinja2.ext.i18n'])
output = template.render(snippetVars=kw,renderer='snippet')
The problem here is that when I use the '_' translation function in the template code I get:
UndefinedError: 'gettext' is undefined
I saw some posts that maybe I need to use jinjaEnv.install_gettext_translations() but I cannot make it work. I tried:
jinjaEnv.install_gettext_translations(pyramid.il8n)
jinjaEnv.install_gettext_translations(tsf)
How can I integrate jinjaEnv.install_gettext_translations() with my pyramid translation factory?
Depending on your exact case, you could use pyramid_jinja2 or get inspiration from it. It creates a GetTextWrapper https://github.com/Pylons/pyramid_jinja2/blob/28944ce627745691ccd1603c56251e038aadd892/pyramid_jinja2/i18n.py that makes its way in the options passed when creating the Environment https://github.com/Pylons/pyramid_jinja2/blob/28944ce627745691ccd1603c56251e038aadd892/pyramid_jinja2/settings.py#L133
https://github.com/Pylons/pyramid_jinja2/blob/28944ce627745691ccd1603c56251e038aadd892/pyramid_jinja2/__init__.py#L394
https://github.com/Pylons/pyramid_jinja2/blob/28944ce627745691ccd1603c56251e038aadd892/pyramid_jinja2/__init__.py#L404-L405
The wrapper is needed because the localizer will change every request, depending on the user locale.
Or you can pass the gettext and ngettext arguments directly when you render. In you case, it would look something like:
localizer = request.localizer
def gt(message):
return localizer.translate(message, domain='your-domain')
def ngt(singular, plural, n):
return localizer.pluralize(singular, plural, n, domain='your-domain')
output = template.render(
snippetVars=kw,
renderer='snippet',
gettext=gt,
ngettext=ngt,
)

Create custom Grails Tag Lib the generates code

Is there any documentation regarding how to create a custom grails tag that generated groovy code similar to "<g:if>" and "<g:else>"?
Using the normal grails taglib, I'm able to generate html or raw output, but I'm looking to output code that can be interpreted similar to how the if and else tags work.
Here's a sample of the Grails source code for the "if" tag:
#Override
protected void outputStartTag(String envExpression, String testExpression) {
out.print("if(");
out.print(envExpression);
out.print(" && ");
out.print(testExpression);
out.println(") {");
}
How can I do something similar in my own taglib prefix.
Example:
<mine:doSomethingCool key="foo">This text is conditionally shown</mine:doSomethingCool>
In addition to hiding complex logic, I would like this ability so I can use the <g:else> tag after my custom tag.
In general, I would like another tool in my toolbox in addition to the current taglib format.
If for example you only want to render the conditional text when key == 'foo', you could do that like this:
class SomethingTagLib {
static namespace = "ns"
def doSomethingCool = { attrs, body ->
if (attrs.key == 'foo') {
out << body()
}
}
}
You could then use this tag like so:
<ns:doSomethingCool key="foo">This text is conditionally shown<ns:doSomethingCool>

Grails 3: Custom taglib not recognized

Using Grails 3.0.9
I tried setting up a custom tag, but I can't get it to work. I created the following groovy file in grails-app/taglib: BeanFormGenerator.groovy
class BeanFormGenerator {
def renderList = { attrs, body ->
// reads the 'values' attribute from the attributes list
def list = attrs.values
// iterates and renders list values
list.each {
// uses the implicit 'out' variable to append content to the response
out << "<span class=\"element\"> ${it} </span>"
}
}
}
And I have this call in a gsp file:
<g:renderList values="[1, 2, 3]">check check</g:renderList>
I get the error:
Tag [renderList] does not exist. No tag library found for namespace: g
I tried creating a custom namespace inside of BeanFormGenerator:
static namespace = "beanform"
But this just caused to be treated as markup. What am I doing wrong? The documentation makes it seem like this should be all there is to it. I'm running this inside of IntelliJ community edition if that makes a difference.
Quite simply, to create a tag library create a Groovy class that ends
with the convention TagLib and place it within the grails-app/taglib
directory
grails.github.io/grails-doc/3.0.9/guide/single.html#taglibs

How to selectivelly pretty print Grails responses?

Grails converters can be configured to pretty print (or not) by default.
I like to use the respond method instead any MarkupBuilder, JsonBuilder or whatever other library that I should handle response format.
respond myObject
The user can query xml, json or hal.
How can I allow the user to query for a pretty print (assuming default is not)
If the user pass a parameter in the url like this:
/foos?pretty=true
How can I force the respond to pretty print?
Obs. I saw this question but they all solve the problem statically. I want to change it dinamically on request scope.
Since you want to be able to select the format based on a controller parameter, I think a Grails converter long with render() will be better than using respond(). Here's an example:
import grails.converters.*
class SomeController {
def foos() {
def pretty = params.pretty
def prettyType
def myObject = /* whatever */
if(pretty == 'xml') prettyType = XML
if(pretty == 'json') prettyType = JSON
if(prettyType) render myObject.asType(prettyType)
else render myObject
}
}
The URL would be something like: /foos?pretty=xml or /foos?pretty=json.

Change Grails REST format /controller/<id>/<action>

I messed around with this a bit yesterday and failed miserably. I want to convert:
"/$controller/$action?/$id?"
To
#in psudo
"/$controller/$id?/$action?"
#ideal regex
"\/(\w+)(\/\d+)?(\/\w+)?"
The most obvious way failed "/$controller/$action?/$id?"
I can write the regex's to do it, but I am having trouble finding a way to using true regexs (I found RegexUrlMapping but could not find out how to use it), and also can't find documentation on how to assign a group to a variable.
My question is 2 parts:
How to I define a URL Resource with a true regex.
How to I bind a "group" to a variable. In other words if I define a regex, how do I bind it to a variable like $controller, $id, $action
I would also like to be able to support the .json notation /user/id.json
Other things I have tried, which I thought would work:
"/$controller$id?$action?"{
constraints {
controller(matches:/\w+/)
id(matches:/\/\d+/)
action(matches:/\/\w+/)
}
}
also tried:
"/$controller/$id?/$action?"{
constraints {
controller(matches:/\w+/)
id(matches:/\d+/)
action(matches:/\w+/)
}
}
The grails way to deal with this is to set
grails.mime.file.extensions = true
in Config.groovy. This will cause Grails to strip off the file extension before applying the URL mappings, but make it available for use by withFormat
def someAction() {
withFormat {
json {
render ([message:"hello"] as JSON)
}
xml {
render(contentType:'text/xml') {
//...
}
}
}
For this you'd just need a URL mapping of "$controller/$id?/$action?"
I'm not aware of any way to use regular expressions in the way you want in the URL mappings, but you could get a forward mapping working using the fact that you can specify closures for parameter values that get evaluated at runtime with access to the other params:
"$controller/$a?/$b?" {
action = { params.b ?: params.a }
id = { params.b ? params.a : null }
}
which says "if b is set then use that as the action and a as the id, otherwise use a as the action and set id to null". But this wouldn't give you a nice reverse mapping, i.e. createLink(controller:'foo', action:'bar', id:1) wouldn't generate anything sensible, you'd have to use createLink(controller:'foo', params:[a:1, b:'bar'])
Edit
A third possibility you could try is to combine the
"/$controller/$id/$action"{
constraints {
controller(matches:/\w+/)
id(matches:/\d+/)
action(matches:/\w+/)
}
}
mapping with a complementary
"/$controller/$action?"{
constraints {
controller(matches:/\w+/)
action(matches:/(?!\d+$)\w+/)
}
}
using negative lookahead to ensure the two mappings are disjoint.

Resources