I want to get a variable's class type in freemarker, used var.class.simpleName;
but if var is a Map, freemarker will process class as a key to find value in var.
it throw exception. how can I do this ? thanks for any suggestion.
First I have to ask why do you need that, because FreeMarker templates aren't supposed to know even if var is Map at all. Maybe your data-model is not what the template needs.
Anyway, for now, I would write a custom TemplateMethodModelEx for this purpose, something that you can use like ${classOf(var)}. Inside the TemplateMethodModelEx implementation you will receive a TemplateModel as the argument value, and then you can check if it's an AdapterTemplateModel, and if so you can get back the original object and get its class. (If it's not a AdapterTemplateModel, then it perhaps isn't even a wrapped Java object, so it doesn't make sense to ask what the class of the original object is.) However, the DefaultObjectWrapper with incompatibleImprovements set to less than 2.3.22 doesn't give AdapterTemplateModel to wrapped Map-s... so in 2.3.21 you will still have to use BeansWrapper, but you can at least set simpleMapWrapper to true.
In 2.3.22 it will be actually possible to write ${var?api.class}... you might use the nightly build. Though it only supposed to solve the problem where you can't access business methods because the primary type of the business class is Map.
Related
Let's say in Java, I have class CryptoFormat, which has a static method named getLegacyFormat(). When I want to use the method, I just need to call CryptoFormat.getLegacyFormat(). This is clear because I know where the method comes from.
In Go, there is no static method. I don't really want to just make a file called crypto_format.go and define the method there. The reason is that whenever I need the method, I just call GetLegacyFormat(), which doesn't contain the context where the method comes from.
I could think of two ways to solve the problem:
Make a separate package named cryptoformat, and define the method as a global function in the package. This way, I need to make a new package for just few methods. Also, whenever I need static methods like this, I have to define new packages.
Define a struct named cryptoFormat containing method GetLegacyFormat(). Also, define a global public variable named CryptoFormat, which points to an instance of struct cryptoFormat. This way, I can call CryptoFormat.GetLegacyFormat() when I need the method.
I am not sure which one is better, or whether there is better way.
I would say option 1 you mention is the more idiomatic way to define such functions, if they don't need any state that would warrant to tie them to an underlying struct.
If there is some state you'd like to have as context for the function, then option 2 would be the way to go.
Note that in Go, functions are "first class citizens", so you don't have Java's constraints of needing to define a class for static methods.
And yes, if you want a separate namespace you'd need to define separate packages (just as in Java you'd need to define separate classes and/or packages).
If you want your implementation to be idiomatic, I'd suggest you take a look at Go's standard libraries (pick a few packages and explore how they implement their functions) to get a better feeling of the usual ways to structure this.
whenever I need the method, I just call GetLegacyFormat(), which doesn't contain the context where the method comes from.
So add context to the function name.
GetLegacyCryptoFormat()
Im using Magnolia RenderingModel in combination with Freemarker.
I have URLs like the following:
http://anyPath/context?productTypes=XXXXX&productTypes=YYYYY
my rendering model class looks like:
class MyModel extends RenderingModelImpl {
...
private String[] productTypes;
...
}
However the mentioned array contains only the first value, but not the second.
I checked the behaviour of template directives like ctx.getParameters(). This shows the same behaviour, I get only the first value returned. But if im using ctx.getParameterValues(paramName), it returns both values.
This leads me to following questions:
How would I go, if I want to lookup how the request parameters are mapped into the rendering model, or better:
How can i change the behaviour of that ?
Can anyone acknowledge, that this behaviour is wrong ?
It used to be mentioned in documentation and I believe it still is - if you use .getParameters() you get only first value for multivalue parameter. If you want to get all the values, you need to use .getParameterValues(String param).
From what I understand reasons for that were backward compatibility.
As for changing the behavior, you would need to write your own renderer (e.g. by extending default FreemarkerRenderer and override info.magnolia.rendering.renderer.AbstractRenderer.newModel(Class<T>, Node, RenderableDefinition, RenderingModel<?>) method which instantiates and populates the model class.
Alternatively you can provide fix for above set population method and submit it to Magnolia as a patch. While the .getParameters() behavior is iirc on purpose, the model param population might not be, so you have high chance of getting that changed.
I'm writing a function that iterates over the methods on a given struct and binds the methods to handlers. I would like to skip over internal methods if possible. I'm not sure if this is possible to do so explicitly - I reviewed the documentation for the reflect package and I didn't see a means to detect if a given Value is an internal method. I know I can get the method's name, and then check if it starts with a lowercase character but I'm not sure if there's a kosher way to accomplish this. It's also possible that the internal / public boundary really only exists at compile time anyways, so there really isn't even a way of knowing this beyond the method's name. In either case I'd like to know for sure. Thanks!
The reflect package will not give you unexported methods via Type.Method. Pretty much, if you can see it via reflect, it is already exported.
See https://play.golang.org/p/61qQYO38P0
when creating custom method, I implements TemplateMethodModelEx and returns SimpleSequence object.
according to the API, I should use this constructor:
SimpleSequence(ObjectWrapper wrapper)
since I am setting incompatibleImprovements as 2.3.24, the doc said I can simply use Configuration instance's getObjectWrapper(). My problem is when implementing TemplateMethodModelEx, I have no access to the current config unless I pass cfg to the method's constuctor. then the root.put would look like:
root.put("getMeList", new GetMeListMethod(cfg));
this looks odd to me, i wonder whats the right to construct this kind of SimpleSquence model and whats the right way to get the default object wrapper.
Thanks a lot
You should pass in the ObjectWrapper as the constructor parameter. (It's unrelated to incompatibleImprovements 2.3.24.) Any TemplateModel that creates other TemplateModel-s (like TemplteSequenceModel-s, TemplateHashModel-s, TemplateMethodModel-s) used to work like that. This is normally not apparent because they are created by an ObjectWrapper. If you do the TemplateModel-s manually however (which is fine), then you will face this fact.
I want to validate an entity using a values stored in an array which is in the params.
What I tried to do is injecting the array from params.yml (I'm using YAML) via service into a model.
In validation.yml, I tried to use the choice constraint with a callback. but I don't know how to call a method non-static from a different class.
To do this:
- Choice: { callback: [CountryHandler, getCountries] }
getCountries must be static.
Is it possible to do something like that with a method non static? Is it a better idea* to validate the entity with my own constraint as they explain here: http://symfony.com/doc/current/cookbook/validation/custom_constraint.html?
I only have to validate one param and at first sight it doesn't seems a good idea.
I think that is a better idea because you can re-use in other case and better readability. As show in the documentation you give, you must create 2 files, one for your validator and one for the constraint, if you have dependency, you can inject to Validator declaring a new service with dependancy declared as arguments. After this, you have only to call your constraint as another Constraint.