Groovy DSL given syntax validation - groovy

Actually I'm experimenting writing a DSL with groovy. So far ...
There are some things unclear to be regarding delegation and intercepting unwanted (Closure) structures:
first of all: How can I throw a (type of?) Exception to point to the correct line of code in the DSL that fails?
assuming
abstract MyScript extends Script {
def type(#DelegateTo(MyType) Closure cl) {
cl.delegate = new MyType()
cl()
this
}
}
under
new GroovyShell(this.class.classLoader, new CompilerConfiguration(scriptBaseClass: MyScript.name)).evaluate(…)
the passed DSL / closure
type {
foo: "bar"
}
passes silently.
I'm aware of, that foo: is just a POJ label but I'm not that sure what that defined Closure is interpreted as?
Neither did I found anything regarding the AST metaprogramming to get in touch of any defined labels to use them?
giving in
type {
foo = "bar"
}
it's clear that he will try to set the property foo, but do I really have to intercept unwanted fields/props by
class MyType {
def propertyMissing(String name) {
… // where I'm unable to println name since this leads to field access 'name' ...
}
}
while user is still allowed to pass
type {
foo "bar"
}
which leads to method not defined .. so I have to write additionally some metaClass.methodMissing or metaClass.invokeMethod stuff ..
meanwhile I tend to dismiss any closures in my dsl only working with simple
def type(Map vars) {
store << new MyType(vars)
// where in the constructor I was forced to write metaClass stuff to validate that only fields are given in the map that are defined in the class
}
that works, but both drafts are not what I expected to do when reading "groovy is so great for making DSLs" ...

I would experiment with the different options and then settle for one.
To guide your users you should give feedback similar to that of the regular compiler (i.e. line-number and column, maybe the expression).
Enforcing the correctness of the input can be non-trivial -- depending on your DSL.
For example:
type {
foo: "bar"
}
Is just a closure that returns the String bar. Is that something your user is supposed to do? The label will be part of the AST, AFAIK in org.codehaus.groovy.ast.stmt.Statement.statementLabels. If you want this syntax to assign something to foo then you'll need to rewrite the AST. The Expression could become a Declaration for the local Variable foo or could become an assignment for the Field foo. That's really up to you, however, Groovy gives you some capabilities that make creating a DSL easier:
You already used #DelegateTo(MyType) so you could just add a Field foo to MyType:
class MyType {
String foo
}
And then either use #CompileStatic or #TypeChecked to verify your script. Note that #CompileStatic will deactivate Run-time Metaprogramming (i.e. propertyMissing etc. won't be called anymore.) while #TypeChecked does not. This, however, will only verify Type-Correctness. That is: assigning to anything but a declared Field will fail and assigning an incompatible Type will fail. It does not verify that something has been assigned to foo at all. If this is required you can verify the contents of the delegate after calling the Closure.

Related

TS: Cannot invoke an expression whose type lacks a call signature when defined dynamically, but it works

I'm still quite new to typescript, so please be gentle with me if I'm doing something with no sense for this technology!
The problem that I'm trying to solve is having a dynamic way to define how my application errors should be structured, but leaving to the users the faculty to enrich the messages.
So I tried to create this logic in a module that could be extended easily from the application, but I'm currently facing the problem:
Error:(35, 18) TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'ErrorMessage' has no compatible call signatures.
What I thought it was a good idea (but please tell me if I'm wrong), was to use a register and a map to have the possibility to extend this mapping every time I want. So I created my ErrorMessage interface to be like the following:
export interface ErrorMessage {
actionMessage: string;
actionSubject: string;
originalErrorMessage?: string;
toString: () => string;
}
and a register for these, called ErrorResponseRegister, as it follows:
export enum defaultErrors {
ExceptionA = 'ExceptionA',
ExceptionB = 'ExceptionB',
}
export class ErrorResponseRegister {
private mapping: Map<string, ErrorMessage>;
constructor() {
this.mapping = new Map()
.set(defaultErrors.ExceptionA, exceptionAErrorMessage)
.set(defaultErrors.ExceptionB, exceptionBErrorMessage);
}
}
So at the end, every ErrorMessage function should look like:
export function exceptionAErrorMessage(originalErrorMessage?: string): ErrorMessage {
return {
enrichment1: "Something happened",
enrichment2: "in the application core",
originalErrorMessage: originalErrorMessage,
toString(): string {
return `${this.enrichment1} ${this.enrichment2}. Original error message: ${originalErrorMessage}`;
},
};
}
Please note I haven't used classes for this ones, as it doesn't really need to be instantiated
and I can have a bunch of them where the toString() method can vary. I just want to enforce the errors should have an enrichment1 and enrichment2 that highlight the problem in a better way for not-technical people.
So, now, back to code. When I'm trying to use the exceptionAErrorMessage statically, I can't see any problem:
console.log(exceptionAErrorMessage(originalErrorMessage).toString())
But when I try dynamically, using the map defined in the ErrorResponseRegister, something weird happens:
// In ErrorResponseRegister
public buildFor(errorType: string, originalErrorMessage?: string): Error {
const errorMessageBuilder = this.mapping.get(errorType);
if (errorMessageBuilder) {
return errorMessageBuilder(originalErrorMessage).toString();
}
return "undefined - do something else";
}
The code works as expected, the error returned is in the right format, so the toString function is executed correctly.
BUT, the following error appears in the IDE:
Error:(32, 18) TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'ErrorMessage' has no compatible call signatures.
The line that causes the problem is
errorMessageBuilder(originalPosErrorMessage).toString()
Can someone help me to understand what I'm doing wrong?
It looks like your problem is you've mistyped mapping... it doesn't hold ErrorMessage values; it holds (x?: string)=>ErrorMessage values:
private mapping: Map<string, (x?: string) => ErrorMessage>;
What's unfortunate is that you initialize this variable via new Map().set(...) instead of the using an iterable constructor argument.
The former returns a Map<any, any> which is trivially assignable to mapping despite the mistyping. That is, you ran smack into this known issue where the standard library's typings for the no-argument Map constructor signature produces Map<any, any> which suppresses all kinds of otherwise useful error messages. Perhaps that will be fixed one day, but for now I'd suggest instead that you use the iterable constructor argument, whose type signature declaration will infer reasonable types for the keys/values:
constructor() {
this.mapping = new Map([
[defaultErrors.ExceptionA, exceptionAErrorMessage],
[defaultErrors.ExceptionB, exceptionBErrorMessage]
]); // inferred as Map<defaultErrors, (orig?: string)=>ErrorMessage>
}
If you had done so, it would have flagged the assignment as an error with your original typing for mapping (e.g., Type 'Map<defaultErrors, (originalErrorMessage?: string | undefined) => ErrorMessage>' is not assignable to type 'Map<string, ErrorMessage>'.) Oh well!
Once you make those changes, things should behave more reasonably for you. Hope that helps; good luck!
Link to code

How do groovy closure parameter mapping works?

I am curious (quite new to closure). How do groovy closure knows that it should map parameter animal to the testMap's key and parameter animalSound to testMap's value?
def testMap = ['cat':'Meow', 'dog':'Woof']
testMap.each { animal, animalSound ->
println "${animal} has the sound ${animalSound}"
};
It's not the closure that knows, it's the implementation of the each method that is defined in java.util.Map in the Groovy JDK.
As per the documentation, the method requires that the passed in closure accept 1 or 2 arguments. Otherwise, it will throw an exception
[foo:'bar'].each { -> } // throws MissingMethodException
The Groovy JDK is the subset of JavaSE classes (e.g. Collections API, I/O, java.lang, etc.) that have been enhanced by the Groovy language to make them more useful.

In which sequence does method call work in groovy?

I am using groovy 2.3.8
I am trying to figure out how method calls work in groovy. Specifically if we have a Java class hierarchy each having a metaClass like below
class A {
}
A.metaClass.hello = {
"hello superclass"
}
class B extends A {
}
B.metaClass.hello = {
"hello subclass"
}
If I use new B().hello() I get hello subclass. If I remove meta class of B then I get hello superclass.
Based on changing the above example I think groovy goes in the below sequence to find which method to call
method-in-subclass's-metaclass ?: subclass-metho ?: method-in-superclass's metaclass ?: method-in-superclass
So how does groovy lookup which method to call?
Well, the hierarchy is the expected object oriented programming method overloading, which is what you witnessed. What differs is the dispatching. Instead of starting with a method lookup in instance's class, it begins with the MOP (meta object protocol).
In layman's terms, because the MOP is programmable, so is the way methods are invoked :)
How it works
The following diagram from Groovy's documentation shows how methods are looked up.
What's not clear in the diagram is that there's an instance metaclass as well, and it comes before the class's metaclass.
Something that may help is looking at an object's or class's .metaClass.methods Methods added through inheritance, traits, metaclass, etc are listed in a flat list. The inheritance hierarchy is flattened. .metaClass.metaMethods on the other hand seems to contain methods added via the GDK. From the list I could not tell method precedence :(
Based on observation, the rule seems to be this: the last MetaClass standing wins.
class A { }
class B extends A { }
A.metaClass.hello = {
"hello superclass"
}
B.metaClass.hello = {
"hello subclass"
}
def b = new B()
assert b.hello() == "hello subclass"
b.metaClass = A.metaClass
assert b.hello() == "hello superclass"

When I use definition instead of class in puppet, what's the best practice for parameters?

I realise that it's generally a good idea to create params.pp in the module with modulename::params class and inherit that in modulename class to handle parameters in a separate file. How do I do that if instead of class, I am creating a definition?
Just to clarify, I'm using a definition to be able to install multiple versions of the same application on the server.
Good question. Since there is no inheritance available for defined types in Puppet the params.pp patterns can not be reproduced in the exact same way for defined types as for classes. There is another way though.
The following code outputs 'hello world' via the Foo['bar'] defined type:
class params {
$msg = 'hello world'
}
define foo($msg = $params::msg ) {
notify{ $msg: }
}
foo { 'bar': }
include params
Now, for the above to function it is necessary for params to be included. Otherwise the Puppet parser will complain that the class params has not been evaluated and therefore the $params::msg variable can not be resolved.
It is not necessary to provide ordering between the inclusion of params and the definition of bar, since in Puppet classes are always evaluated before defined types. If this would not be so the above would likely cause the same evaluation problem and you would have to write:
foo { 'bar':
require => Class['params'] # <- not necessary
}
include params
So for this to work in a module foo you can simply add a params class as you are used to and start your init.pp with:
include foo::params
define foo($x = $foo::params::x, $y = $foo::params::y, ...)
One important note
Before you happily proceed with the params.pp pattern I advise you to read this blog post: the problem with params.pp

Why missingMethod is not working for Closure?

UPDATE
I have to apologize for confusing the readers. After I got totally lost in the code, I reverted all my changes from Mercurial repo, carefully applied the same logic as before -- and it worked. The answers below helped me understand the (new to me) concept better, and for that I gave them upvotes.
Bottom line: if a call to a missing method happens within a closure, and resolution set to DELEGATE_FIRST, methodMissing() will be called on the delegate. If it doesn't -- check you own code, there is a typo somewhere.
Thanks a lot!
Edit:
OK, now that you've clarified what your are doing (somewhat ;--))
Another approach (one that I use for DSLs) is to parse your closure group to map via a ClosureToMap utility like this:
// converts given closure to map method => value pairs (1-d, if you need nested, ask)
class ClosureToMap {
Map map = [:]
ClosureToMap(Closure c) {
c.delegate = this
c.resolveStrategy = Closure.DELEGATE_FIRST
c.each{"$it"()}
}
def methodMissing(String name, args) {
if(!args.size()) return
map[name] = args[0]
}
def propertyMissing(String name) { name }
}
// Pass your closure to the utility and access the generated map
Map map = new ClosureToMap(your-closure-here)?.map
Now you can iterate through the map, perhaps adding methods to applicable MCL instance. For example, some of my domains have dynamic finders like:
def finders = {
userStatusPaid = { Boolean active = true->
eq {
active "$active"
paid true
}
}
}
I create a map using the ClosureToMap utility, and then iterate through, adding map keys (methods, like "userStatus") and values (in this case, closure "eq") to domain instance MCL, delegating the closure to our ORM, like so:
def injectFinders(Object instance) {
if(instance.hasProperty('finders')) {
Map m = ClosureToMap.new(instance.finders).map
m?.each{ String method, Closure cl->
cl.delegate = instance.orm
cl.resolveStrategy = Closure.DELEGATE_FIRST
instance.orm.metaClass."$method" = cl
}
}
}
In this way in controller scope I can do, say:
def actives = Orders.userStatusPaid()
and "eq" closure will delegate to the ORM and not domain Orders where an MME would occur.
Play around with it, hopefully I've given you some ideas for how to solve the problem. In Groovy, if you can't do it one way, try another ;--)
Good luck!
Original:
Your missingMethod is defined on string metaclass; in order for it to be invoked, you need "someString".foo()
If you simply call foo() by itself within your closure it will fail, regardless of delegation strategy used; i.e. if you don't use the (String) delegate, good luck. Case in point, do "".foo() and it works.
I don't fully understand the issue either, why will you not have access to the closure's delegate? You are setting the closure's delegate and will invoke the closure, which means you will have access to the delegate within the closure itself (and can just delegate.foo())
nope, you will not catch a missing method and redirect it to the delegate with metaclass magic.
the closure delegate is the chance to capture those calls and adapt them to the backing domain.
that means...
you should create your own delegate with the methods required by the dsl.
do not try to force a class to do delegate work if it's not designed for the task, or the code will get really messy in not time.
keep everything dsl related in a set of specially designed delegate classes and everything will suddenly become ridiculously simple and clear.

Resources