Suppose I have a gsp snippet stored in my database. How do I programmatically merge it with a data model to produce a string.
The applicationContext of any Grails app contains a bean named
groovyPagesTemplateEngine
By default this is a instance of GroovyPagesTemplateEngine. So you might use code like this in your controller or service:
class MyService/MyController {
def groovyPagesTemplateEngine
String renderGSPToString(String uri, Map model) {
groovyPagesTemplateEngine.createTemplate(uri).make(model).toString()
}
}
NB: this snippet is not really taken from running code, it should just clarify the idea.
I found a DIRTY (but working) way of rendering complex gsps offline using groovyPageRenderer with substituted scriptsource. In that case you have access to all gsp syntax including g:if etc..
First define two dummy classes:
class StringPageLocator extends GrailsConventionGroovyPageLocator {
GroovyPageScriptSource findViewByPath(String content) {
return new StringScriptSource(content)
}
}
class StringScriptSource implements GroovyPageScriptSource{
String content
public StringScriptSource(String content) {
this.content=content
}
#Override String suggestedClassName() { "DummyName" }
#Override boolean isPublic() { true }
#Override String getScriptAsString() { return content }
#Override boolean isModified() { true }
#Override String getURI() { "DummyURI" }
}
And then you can use it as such:
def groovyPageLocator // Injected automaticaly to service/controller etc...
groovyPageRenderer.groovyPageLocator=new StringPageLocator()
String output=groovyPageRenderer.render(
view:'Hello2 ${user} <g:if test="${test}">TRUE!!!</g:if>',
model:[user:'test user2',test:true]
)
You can make a controller method that does what you want. Then you will have an HTTP api to accomplish what you want. The controller method's template will have a <g:render> tag, appropriately parameterized.
Related
Since Jackson's hibernate5-module not working for me. I'm trying to implement my own lazy property filter. I implemented custom annotation introspection successfully.
But when I apply my custom serializer, #JsonIgnoreProperties is ignored.
#Entity
class Call {
#OneToMany(mappedBy = "call")
#JsonIgnoreProperties("call")
List<CallEvent> events;
}
#Entity
class CallEvent {
#ManyToOne(fetch = FetchType.LAZY)
Call call;
}
public class LazyValueIntrospector extends JacksonAnnotationIntrospector {
#Override
public Object findSerializer(Annotated a) {
var yes = a.hasAnnotation(ManyToOne.class)
|| a.hasAnnotation(Basic.class)
|| a.hasAnnotation(OneToMany.class)
|| a.hasAnnotation(OneToOne.class);
if (yes) {
return LazyValueSerializer.class;
}
return super.findSerializer(a);
}
}
public class LazyValueSerializer extends JsonSerializer<Object> {
#Override
public boolean isEmpty(SerializerProvider provider, Object value) {
return value == null || !Hibernate.isInitialized(value);
}
#Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeObject(value);
}
}
Explanation:
LazyValueIntrospector.findSerializer detects possible lazy properties.
Hibernate.isInitialized tells me the value is initialized or not.
gen.writeObject(value); writes if property is initialized.
The problem is gen.writeObject(value); method call is ignoring #JsonIgnoreProperties("call") annotation.
The question is:
How to apply #JsonIgnoreProperties("call") annotation in my custom serializer?
Ps: spring.jackson.defaultPropertyInclusion=non_empty property applied globally. Which allows isEmpty checking.
As we are ignoring only one property, try giving #JsonIgnore over the call property inside the CallEvent entity above Call.
Example:
#Entity
class CallEvent {
#ManyToOne(fetch = FetchType.LAZY)
#JsonIgnore
Call call;
}
Without actual example of where #JsonIgnoreProperties is ignored it is hard to say. But I think that the custom serializer would need to delegate to the original serializer and not replace it; there's quite a bit more that is needed to support various other features.
To do that you cannot register serializer the way like shown here, but instead replace it using one of methods in BeanSerializerModifier (and need to register that modifier with ObjectMapper) -- that way you get the "real" serializer to delegate to.
Usually implementations also need to implement createContextual() (from ContextSerializer) which needs to be passed to the original ("delegate") serializer.
You may want to have a look at how serializers are implemented in Hibernate module itself.
So, Since the question is how to apply jackson's own behaviors in the custom bean serializer. I found the answer for myself.
extend from BeanSerializerBase, not BeanSerializer.
override with* methods. Such as withByNameInclusion and withProperties
This way, Jackson calls appropriate methods when it's needed.
#Override
public JsonSerializer<Object> unwrappingSerializer(NameTransformer unwrapper) {
return new LazyBeanUnwrappingSerializer(this, unwrapper);
}
#Override
protected BeanSerializerBase withProperties(BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) {
return new LazyBeanSerializer(this, properties, filteredProperties);
}
#Override
protected BeanSerializerBase withByNameInclusion(Set<String> toIgnore, Set<String> toInclude) {
return new LazyBeanSerializer(this, toIgnore, toInclude);
}
#Override
public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) {
return new LazyBeanSerializer(this, objectIdWriter, _propertyFilterId);
}
#Override
public BeanSerializerBase withFilterId(Object filterId) {
return new LazyBeanSerializer(this, _objectIdWriter, filterId);
}
#Override
protected BeanSerializerBase asArraySerializer() {
throw new RuntimeException("Array serializer no supported");
}
So i am trying to implement an improved version of the available form tags so I am extending FormTagLib. I am tryign to do a simple test with teh textField tag but I can't seem to even figure out which method is getting called on the tag. I have override every available textField method available but none of them are getting hit
class TestTagLib extends FormTagLib {
static namespace = "test"
#Override
Object textField(Map attrs) {
return super.textField(attrs)
}
#Override
Object textField() {
return super.textField()
}
#Override
Object textField(Map attrs, CharSequence body) {
return super.textField(attrs, body)
}
#Override
Object textField(Closure body) {
return super.textField(body)
}
#Override
Object textField(Map attrs, Closure body) {
return super.textField(attrs, body)
}
}
I have tried putting breakpoints, console outputs for each method but nothing happens. The input fields are being generated just fine, but it doesn't seem to be using my code to do it. Heck i have even tried completely removing the call the super class and everything still works.
<test:textField name="test"/>
<input type="text" name="test" value="" id="test" />
What am I missing here and how do I intercept the creation of the textfield so I can make my modifications?
Have you taken a look at how the FormTagLib is implemented?
I think most tags are defined as Closures, like textField = { … }. This causes the implementation of the textField method to be replaced with the code between the {}.
I believe your example is a demonstration of the risks of extension. I think delegation is usually a better solution. Not sure if the tagLibs are spring beans, but you could try something like this (not tested):
class TestTagLib {
def formTagLib
def textField(args) {
formTagLib.textField(args)
}
}
I am using Grails 2.5 and use Grails databinding in request methods.
For a basic example of the situation consider the following:
Domain class
class Product {
String field1
String privateField
}
Controller
class ProductController {
def update(Product productInstance) {
productInstance.save()
}
}
If I pass an existing Product to the controller like
{"id":3, "privateField":"newValue","field1":"whatever"}
the old value of privateField is overwritten. I want to enforce, that privateField is never bound from a request and avoid checking if the field is dirty.
Is there a mechanism in Grails to achieve this?
If I have to do the dirty check, how can I discard the new value and use the old one?
Pretty sure there's a "bindable" constraint.
http://grails.github.io/grails-doc/2.5.x/ref/Constraints/bindable.html
class Product {
String field1
String privateField
static constraints = {
privateField bindable: false
}
}
Should keep that field from binding automatically.
You can enforce which values are bound, but you'll need to change your method signature to get more control of the data binding process.
class ProductController {
def update() {
def productInstance = Product.get(params.id)
bindData(productInstance, params, [exclude: ['privateField']]
productInstance.save()
}
}
I found grails criteria and want to customize sorting options.
E.g. I have domain Book and I want to make some criteria:
Book.createCriteria().list {
//some code like ilike('name', 'book')
...
order(params.sort, params.order)
}
I want to make specific sorting rule, e.g. by name.trim().
How can I do this?
Based on a solution provided here, by extending the hirbernate Order class, you can customize it to accept functions and use it with createCriteria.
I wont be surprised, if there is a nicer and easier approach since this source is pretty old and also Grails is cooler than this :D
First you need a class extending Hibernate Order:
Originally by:spostelnicu
public class OrderBySqlFormula extends Order {
private String sqlFormula;
protected OrderBySqlFormula(String sqlFormula) {
super(sqlFormula, true);
this.sqlFormula = sqlFormula;
}
public String toString() {
return sqlFormula;
}
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
return sqlFormula;
}
public static Order sqlFormula(String sqlFormula) {
return new OrderBySqlFormula(sqlFormula);
}
}
Then you can pass instance of this class to your createCriteria:
def ls = Domain.createCriteria().list {
order OrderBySqlFormula.sqlFormula("TRIM(name)")
}
Note1: You can pass any formula to sqlFormula as long as the underlying database accepts it.
Note2: Using such approach might cause migration challenges.
Hope it helps
I have a structuremap configuration that has me scratching my head. I have a concrete class that requires a interfaced ui element which requires an interfaced validation class. I want the outer concrete class to get the default ui element, but get a concrete-class-specific validation object. Something like this:
class MyView
{
IPrompt prompt
}
class GenericPrompt : IPrompt
{
IValidator validator
}
class MyValidator : IValidator
{
bool Validate() {}
}
How can I configure structuremap with the Registry DSL to only use MyValidator when creating dependencies for MyView. (And assumedly using BobsValidator when creating dependencies for BobsView)
Are you getting MyView (and BobsView) from the container? Can we assume that they will all take an instance of IPrompt?
One approach would be to register all of your validators with a name that matches the names of your view. You could implement your own type scanner that just removes the Validator suffix:
public class ValidatorScanner : ITypeScanner
{
public void Process(Type type, PluginGraph graph)
{
if (!typeof (IValidator).IsAssignableFrom(type)) return;
var validatorName = type.Name.Replace("Validator", "");
graph.AddType(typeof(IValidator), type, validatorName);
}
}
Now, if you assume an IPrompt will always be requested by a View that follows that naming convention, your registry could look like:
public class ValidatorRegistry : Registry
{
public ValidatorRegistry()
{
Scan(scan =>
{
scan.TheCallingAssembly();
scan.With<ValidatorScanner>();
});
ForRequestedType<IPrompt>().TheDefault.Is.ConstructedBy(ctx =>
{
var viewName = ctx.Root.RequestedType.Name.Replace("View", "");
ctx.RegisterDefault(typeof(IValidator), ctx.GetInstance<IValidator>(viewName));
return ctx.GetInstance<GenericPrompt>();
});
}
}
To retrieve your view with the appropriate validator, you would have to request the concrete type:
var view = container.GetInstance<MyView>();
Note that this will only work if you are retrieving your view with a direct call to the container (service location), since it depends on the "Root.RequestedType". Depending on how you plan to get your views, you might be able to walk up the BuildStack looking for a View (instead of assuming it is always Root).