Rascal MPL dynamically get properties / fields of variable - reflection

I often find myself testing using typeOf whenever I'm lost. Is there a similar way to print all properties or fields of a variable, similar to Object.keys (JavaScript) or vars (Python)?

To print all the current keyword fields of a constructor:
import Node;
iprintln(getKeywordParameters(myInput));
I use that a lot to print the fields of an M3 model, for example. However, that does not print the fields that have not been set. Rascal is a statically typed language, and there is information which can be retrieved which is not present in the value (default fields for example).
If I want to know the general signature of a function or constructor, I type the name in the console, then click on its source location:
rascal>println
value: choice(
[
function(|file:///Users/jurgenv/git/rascal/src/org/rascalmpl/library/IO.rsc|(11118,784,<418,0>,<455,36>)),
function(|file:///Users/jurgenv/git/rascal/src/org/rascalmpl/library/IO.rsc|(11904,69,<457,0>,<458,27>))
],
[])
That works for constructors are well, so you can see which positional fields they have or keyword fields.
To find out the name of the current constructor:
import Node;
getName(myValue);

Related

Graphql Schema doku displays Input type automatically with Input

I have added leangen/graphql-spqr as described in the readme.
Before we had a custom implementation of graphql types like in customtype.types.gql.
After implementation, everything works fine, except the type which are called e.g. OperatorInput, are named in the autogenerated graphql doc like "OperatorInputInput".
I tried to Change it like this in the declaration:
#GraphQLArgument(name = "OperatorInput", description = "Required fields for Operator") OperatorInput operator
But it wasn't applied.
Do you know any workaround?
This is intended. Keep in mind that in GraphQL's type system, input and output types are strictly different. Let's say you have this in Java:
public Book saveBook(Book in) {...}
The Book that is the return type and the Book that is the argument type are 2 different types in GraphQL, and must have unique names. So Input is added automatically to make the names unique.
If you're 100% sure you want to override this, register a custom TypeInfoGenerator via GraphQLSchemaGenerator#withTypeInfoGenerator. You can inherit from DefaultTypeInfoGenerator and override generateInputTypeName. BUT! Do pay attention that if you end up producing the same name for 2 different types, all hell breaks loose. So maybe only drop the suffix Input if the classname already ends with Input and never ever use such classes for output.
Btw #GraphQLArgument sets the name of the argument, not the name of the type of the argument.

Why does FreeMarkers built-in "?is_string" return true for an Object?

If I pass an Object into the model and test it with the "?is_string" built-in, it will falsely return a true value.
Is it possible (without checking the class name) to have a proper type checks on Objects?
FreeMarker: 2.3.28
Code to reproduce:
public class Test {}
// In Test Controller
ModelAndView mv = new ModelAndView("test");
mv.addObject("test", new Test());
// In test.ftl
<#if test?is_string>
${test} - is a string!
</#if>
// Result
Test#455b31c - is a string
The problem is that that approach isn't really supported by FreeMarker. The Java objects are mapped to some template language values via Configuration.objectWrapper, and the template only sees the result of that mapping. Furthermore the template language has a different type system than Java, a simplistic one, without classes. (It was a design goal back then that the data-model is just some simple tree, and the templates will work no mater what objects are behind, as far as it still gives the same tree.) ?is_... doesn't check the Java type, but the type according the template language. As with the usual ObjectWrapper-s a "generic" object (means, nothing recognized like List, Map, Date, etc.) can be used as a strings whose value is whatever toString() returns, it's a string as far as the template language is concerned. It's kind of duck typed...
A workaround I can think of is that first check the value with ?is_hash, as that will catch the said generic objects (as they support ., they are hashes as well, not just strings). Or instead just check the property you expect to be present in a Test. Then on the "else" branch you can continue with ?is_string.

How to use annotations with z3c.form's DictionaryField

There is documentation on using Python dict with z3c.form (loading & storing form data).
However, the z3c.form datamanager used for dicts is not registered for other types or interfaces (see reference), whereas annotations typically use something like PersistentDict.
How can I use the DictionaryField datamanager in this scenario? Ie. so that in my form's getContent method I merely return the PersistentDictannotation.
Well, unfortunately there seems no simple solution for this requirement.
I once faced the same problem using the datagrid field in a z3c form.
The following instruction solves the problem for the datagrid field, which is a list (PersistentList of dicts (PersistentMappings).
I guess you may adapt this solution for your case.
First you need to add the following code to the getContent method:
from plone.directives import form
class MyForm(form.SchemaEditForm):
schema = IMyFormSchema
ignoreContext = False
def getContent(self):
annotations = IAnnotations(self.context)
if ANNOTATION_KEY not in annotations:
annotations[ANNOTATION_KEY] = PersistentMapping()
return YourStorageConfig(annotations[ANNOTATION_KEY])
Important note: I wrap the annotation storage to satisfy the get/set behavior of the z3c form. Check the following YourStorageConfig implementation and you will see why :-).
class YourStorageConfig(object):
implements(IMyFormSchema)
def __init__(self, storage):
self.storage = storage
def __getattr__(self, name):
if name == 'storage':
return object.__getattr__(self, name)
value = self.storage.get(name)
return value
def __setattr__(self, name, value):
if name == 'storage':
return object.__setattr__(self, name, value)
if name == 'yourfieldname':
self.storage[name] = PersistentList(map(PersistentMapping, value))
return
raise AttributeError(name)
yourfieldname should be the field name you are using in the form schema.
To implement the a datagrid field, there is some more work to do, but this may be enough for your case.
Please post comments, or tracebacks, so I can provide further help. I'll gonna add more details/explanation if necessary ;-)
It turns out the answer is as easy as the following ZCML adapter registration:
<adapter
for="persistent.dict.PersistentDict zope.schema.interfaces.IField"
provides="z3c.form.interfaces.IDataManager"
factory="z3c.form.datamanager.DictionaryField"
/>
With that, the following customization of a form is sufficient to use (PersistentDict) annotations for loading & storing form data:
def getContent(self):
"return the object the form will manipulate (load from & store to)"
annotations = IAnnotations(self.context)
return annotations[SOME_ANNOTATIONS_KEY_HERE]
This is assuming that a PersistentDict has been previously stored at annotations[SOME_ANNOTATIONS_KEY_HERE] - otherwise the above code will result in KeyError. It would probably be a good idea to change above getContent so that if the annotation does not yet exist, it is created and initialized with some default values.
Finally, note that for some reason, z3c.form warns against enabling DictionaryField for every mapping type, so it may be prudent to for example subclass PersistentDict for form storage, rather than use it directly. I submitted an issue to z3c.form asking for clarification of that warning.

How to set values of a Plone autoform in update methode with ignoreContext = True

I'm trying to make a form where I'm storing values outside of the context. Storing is done and working well but now I would like the update method to fill the form on rendering process. So I'm overriding update method of the Form class that way:
def update(self):
super(ConfigurationForm,self).update()
form = self.request.form
if not form:
#We are on a rendering process
provider = self.getProvider()
settings = provider.get()
#TODO: update widget values !?
settings is a dict where keys are equals to Interface fields's names.
So I have tried many ways to update widgets values:
Using dataconverter (too much complex and don't know if this is the only way
Updating the self.request.form dict and call again the update method
playing with field objects
What is the good way to achieve this ? (supporting all kind of field ?)
Don't do ignoreContext. Override getContent() to return a dict instead. The dict will be used as a pseudo context.

Link between CCK field and view

I want to use a view to select nodes in a content type field. This view must receive an argument that is another field of the content type.
Can someone explain me how to pass the argument from the field to the view?
Excuse my poor english
You might be able to use the Views Arguments Extras module. It will allow the argument of the view to come from a cck field. Some more details about this module (from its project page):
This module contains a group of view handlers and plugins that add the following options:
Argument Default Current Node CCK
allows for cck field values of the current node to be loaded as default arguments
Argument Default Request Params
allows for get and post params as default values
Argument Order Sort
a sort handler, that allows for the order of items to be based on their order in a multi-value argument
I believe you can use the argument validation to validate the argument, and at that point you are free to change the $handler->argument value before it is passed in to Views.
If you just want to change what the view displays based on the value of a CCK field, the easiest way I have found is to embed a view into the template using views_embed_view(). Something like this in your template file would work I think:
//Use the dsm function to print out your $node object
//to get the name of the field you want to pass as an arg
//like this: dsm($node);
//Assuming that the value of that field is in $node->cck_field['0']:
print views_embed_view('name_of_view', 'name_of_display', $node->cck_field['0'];
views_embed_view() only needs the first argument, the name of the view, to work. It will return the HTML for the default display of the named view. We pass it a specific display as a second argument. Anything after the second argument gets passed into the view as an argument, so we pass in the value of the field as an argument to the view. See this link for some documentation on how the function works.

Resources