How to Create Nested Builder Based DSLs on Groovy using FactoryBuilderSupport - groovy

I want to create a nested builder. Something like:
meta('JSON') {
// JSON content using JSON Builder in this case
}
content('HTML') {
// HTML content using MarkupTemplateEngine in this case
}
I don't want to have
JsonBuilder builder = new JsonBuilder()
builder.author {
}
or
def builder = new groovy.xml.MarkupBuilder()
builder.html {
}
How can initial builder and the nested builder be setup?
I have defined the DSL common elements as:
import groovy.transform.CompileStatic
import org.eclipse.collections.impl.map.mutable.UnifiedMap
#CompileStatic
abstract class DSLElement extends UnifiedMap {
DSLElement(Map attributes) {
putAll(attributes)
}
getAt(Object key) {
get(key)
}
putAt(Object key, Object value) {
put(key, value)
}
}
#CompileStatic
class BaseFactoryBuilder extends FactoryBuilderSupport {
BaseFactoryBuilder(boolean init = true) {
super(init)
}
}
#CompileStatic
class DSLFactoryBuilder
extends BaseFactoryBuilder
implements MetaRegistration,
ContentRegistration,
TemplateRegistration {
}
Individual elements I have defined as:
import groovy.transform.CompileStatic
import groovy.transform.InheritConstructors
import groovy.transform.SelfType
#CompileStatic
#InheritConstructors
class Meta extends DSLElement {}
#CompileStatic
class MetaFactory extends AbstractFactory {
boolean isLeaf() {
return false
}
Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)
throws InstantiationException, IllegalAccessException {
return new Meta(attributes)
}
}
#SelfType(DSLFactoryBuilder)
#CompileStatic
trait MetaRegistration {
registerMeta() {
registerFactory("meta", new MetaFactory())
}
}
and
import groovy.transform.CompileStatic
import groovy.transform.InheritConstructors
import groovy.transform.SelfType
#CompileStatic
#InheritConstructors
class Content extends DSLElement {
}
#CompileStatic
class ContentFactory extends AbstractFactory {
boolean isLeaf() {
return false
}
Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)
throws InstantiationException, IllegalAccessException {
return new Content(attributes)
}
}
#SelfType(DSLFactoryBuilder)
#CompileStatic
trait ContentRegistration {
registerMeta() {
registerFactory("content", new ContentFactory())
}
}
I have not figured you how to do the nesting another builder and the parameter 'JSON' or 'HTML'.
I am following the FactoryBuilderSupport example given in: Groovy for Domain-Specific Languages

maybe i did not fully understand the question - it's too long to read and code you provided not runnable (or not full)
i assume you want to achieve this:
def html = meta('HTML'){
person(name:'foo')
}
def json = meta('JSON'){
person(name:'bar')
}
if so, here is a code to implement it
String meta(String ctxName, Closure builderBody){
def builder = null
def writer = null
if(ctxName=='HTML'){
writer=new StringWriter()
builder=new groovy.xml.MarkupBuilder(writer)
}else if(ctxName=='JSON'){
builder=new groovy.json.JsonBuilder()
}
else throw new RuntimeException("unsupported meta=`$ctxName`")
def result = builder.with(builderBody)
if(ctxName=='HTML'){
return writer.toString()
}else if(ctxName=='JSON'){
return builder.toPrettyString()
}
}
println meta('HTML'){
person(name:'foo')
}
println meta('JSON'){
person(name:'bar')
}
result:
<person name='foo' />
{
"person": {
"name": "bar"
}
}

Related

How to override Groovy variable and method using anonymous class?

I have the following code. I have an abstract JobParams, a class extending that abstract GradleJobParams, and a gjp variable with value using anonymous class declaration.
I want to test the overriding behavior of groovy. I can override the method setupRoot() but not the property testVar, why is that?
Tested on: https://groovyconsole.appspot.com/script/5146436232544256
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + testVar);
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
testVar:3,
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
The result is:
Starting
The testVar:1
Override root
Java (and thus Groovy) does not support overriding fields from the parent class with subclassing. Instead, it uses a mechanism called hiding fields:
Hiding Fields
Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super, which is covered in the next section. Generally speaking, we don't recommend hiding fields as it makes code difficult to read.
Source: https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html
It can be simply illustrated with the following example in Java:
final class SubclassHiddingFieldExample {
static abstract class A {
int value = 10;
void printValue1() {
System.out.println(value);
}
void printValue2() {
System.out.println(this.value);
}
void printValue3() {
System.out.println(((B)this).value);
}
}
static class B extends A {
int value = 12;
}
public static void main(String[] args) {
final B b = new B();
b.printValue1();
b.printValue2();
b.printValue3();
}
}
Output:
10
10
12
As you can see, only printValue3 prints out 3, because it cast this explicitly to B class.
Now, if you look at the decompiled bytecode of your JobParams class, you can see that the printTestVar method code is an equivalent of the following Java code:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;
public abstract class JobParams implements GroovyObject {
private int root;
private Object testVar;
public JobParams() {
CallSite[] var1 = $getCallSiteArray();
byte var2 = 1;
this.testVar = Integer.valueOf(var2);
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
}
public Object setupRoot() {
CallSite[] var1 = $getCallSiteArray();
return var1[0].callCurrent(this, "The root");
}
public Object printTestVar() {
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
}
public MetaClass getMetaClass() {
MetaClass var10000 = this.metaClass;
if (var10000 != null) {
return var10000;
} else {
this.metaClass = this.$getStaticMetaClass();
return this.metaClass;
}
}
public void setMetaClass(MetaClass var1) {
this.metaClass = var1;
}
public Object invokeMethod(String var1, Object var2) {
return this.getMetaClass().invokeMethod(this, var1, var2);
}
public Object getProperty(String var1) {
return this.getMetaClass().getProperty(this, var1);
}
public void setProperty(String var1, Object var2) {
this.getMetaClass().setProperty(this, var1, var2);
}
public int getRoot() {
return this.root;
}
public void setRoot(int var1) {
this.root = var1;
}
public Object getTestVar() {
return this.testVar;
}
public void setTestVar(Object var1) {
this.testVar = var1;
}
}
You can see that the line that prints out the value of the testVar field is represented by:
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
It means that no matter what value of testVar your subclass defines, the printTestVar method uses testVar field defined in the JobParams class. Period.
Using Groovy auto getter methods
There is one way you to implement the expected behavior. Every class field in Groovy has a getter method associated with that field compiled by Groovy for you. It means that you can access testVar by calling the getTestVar() method generated by the Groovy compiler. You can use it to override the value returned by a getter method for any field from the subclass. Consider the following example:
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + getTestVar()); // <-- using a getTestVar() method instead a testVar field
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
getTestVar: 3, // <-- stubbing getTestVar() method to return a different value
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
Output:
Starting
The testVar:3
Override root

Best way to define a Map Object in GraphQL Schema?

I try to map a key string with arrays of Objects.
I can create a simple Object but i want to add easily an object in these arrays. The Map Object is perfect to do this.
Problem: I dont know how to define the Type Map for GraphQL :'(
#ObjectType()
export class Inventaire
#Field()
_id: string;
#Field()
stocks: Map<string, Article[]>;
}
GraphQL does not provide any kind of map type out of the box. A JSON blob of key-value pairs do not have a strong schema, so you can't have something like this:
{
key1: val1,
key2: val2,
key3: val3,
...
}
However, you can define a GraphQL Schema to have a key-value tuple type, and then define your property to return an array of those tuples.
type articleMapTuple {
key: String
value: Article
}
type Inventaire {
stocks: [articleMapTuple]
}
Then your return types would look something like this:
data [
{
key: foo1,
value: { some Article Object}
},
{
key: foo2,
value: { some Article Object}
},
{
key: foo3,
value: { some Article Object}
},
]
You can use this package https://www.npmjs.com/package/graphql-type-json.
Example:
import { makeExecutableSchema } from 'graphql-tools';
import GraphQLJSON, { GraphQLJSONObject } from 'graphql-type-json';
const typeDefs = `
scalar JSON
scalar JSONObject
type MyType {
myValue: JSON
myObject: JSONObject
}
# ...
`;
const resolvers = {
JSON: GraphQLJSON,
JSONObject: GraphQLJSONObject,
};
export default makeExecutableSchema({ typeDefs, resolvers });
GraphQL does not natively support Map type. You can create your own scalar for Map or use existing ObjectScalar defined in repo https://github.com/graphql-java/graphql-java-extended-scalars
import graphql.Assert;
import graphql.language.ArrayValue;
import graphql.language.BooleanValue;
import graphql.language.EnumValue;
import graphql.language.FloatValue;
import graphql.language.IntValue;
import graphql.language.NullValue;
import graphql.language.ObjectValue;
import graphql.language.StringValue;
import graphql.language.Value;
import graphql.language.VariableReference;
import graphql.language.ObjectField;
import graphql.scalars.util.Kit;
import graphql.schema.Coercing;
import graphql.schema.CoercingParseLiteralException;
import graphql.schema.CoercingParseValueException;
import graphql.schema.CoercingSerializeException;
import graphql.schema.GraphQLScalarType;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
#Component
public class ObjectScalar extends GraphQLScalarType {
public ObjectScalar() {
this("Object", "An object scalar");
}
ObjectScalar(String name, String description) {
super(name, description, new Coercing<Object, Object>() {
public Object serialize(Object input) throws CoercingSerializeException {
return input;
}
public Object parseValue(Object input) throws CoercingParseValueException {
return input;
}
public Object parseLiteral(Object input) throws CoercingParseLiteralException {
return this.parseLiteral(input, Collections.emptyMap());
}
public Object parseLiteral(Object input, Map<String, Object> variables)
throws CoercingParseLiteralException {
if (!(input instanceof Value)) {
throw new CoercingParseLiteralException("Expected AST type 'StringValue' but" +
" was '" + Kit.typeName(input) + "'.");
} else if (input instanceof NullValue) {
return null;
} else if (input instanceof FloatValue) {
return ((FloatValue)input).getValue();
} else if (input instanceof StringValue) {
return ((StringValue)input).getValue();
} else if (input instanceof IntValue) {
return ((IntValue)input).getValue();
} else if (input instanceof BooleanValue) {
return ((BooleanValue)input).isValue();
} else if (input instanceof EnumValue) {
return ((EnumValue)input).getName();
} else if (input instanceof VariableReference) {
String varName = ((VariableReference)input).getName();
return variables.get(varName);
} else {
List values;
if (input instanceof ArrayValue) {
values = ((ArrayValue)input).getValues();
return values.stream().map((v) -> {
return this.parseLiteral(v, variables);
}).collect(Collectors.toList());
} else if (input instanceof ObjectValue) {
values = ((ObjectValue)input).getObjectFields();
Map<String, Object> parsedValues = new LinkedHashMap();
values.forEach((fld) -> {
Object parsedValue = this.parseLiteral(((ObjectField)fld).getValue(),
variables);
parsedValues.put(((ObjectField)fld).getName(), parsedValue);
});
return parsedValues;
} else {
return Assert.assertShouldNeverHappen("We have covered all Value types",
new Object[0]);
}
}
}
});
}
}
scalar Object
type Result {
value : Object
}

How do I get multiple MockFor working in Groovy?

I am trying to get multiple mocks working in groovy. The only way I have managed to get this working is to create my own kind of mock - adding a meta method.
I have tried using nested use statements and also tried one use and one proxy with verify, neither of which worked. Both of these returned a failure - "junit.framework.AssertionFailedError: No more calls to 'pop' expected at this point. End of demands."
import groovy.mock.interceptor.MockFor
import org.junit.Test
class MockTest {
// results in No more calls to 'pop' expected at this point. End of demands.
#Test
public void testMock() {
MockFor pupilMock = new MockFor(Pupil)
MockFor bubbleMock = new MockFor(SomeService)
GroovyObject bubbleProxy = bubbleMock.proxyInstance()
pupilMock.demand.blowBubble { String colour ->
return bubbleProxy
}
bubbleMock.demand.pop {}
pupilMock.use {
bubbleMock.use {
Teacher teacher = new Teacher()
teacher.lesson("red")
}
}
}
// results in No more calls to 'pop' expected at this point. End of demands.
#Test
public void testProxy() {
MockFor pupilMock = new MockFor(Pupil)
MockFor bubbleMock = new MockFor(SomeService)
GroovyObject bubbleProxy = bubbleMock.proxyInstance()
pupilMock.demand.blowBubble { String colour ->
return bubbleProxy
}
bubbleMock.demand.pop {}
pupilMock.use {
Teacher teacher = new Teacher()
teacher.lesson("red")
}
bubbleMock.verify(bubbleProxy)
}
// only using a single mock so works
#Test
public void testMetaclass() {
MockFor pupilMock = new MockFor(Pupil)
SomeService.metaClass.pop = { println "pop was called" }
SomeService metaBubble = new SomeService("red")
pupilMock.demand.blowBubble { String colour ->
return metaBubble
}
pupilMock.use {
Teacher teacher = new Teacher()
teacher.lesson("red")
}
}
}
class Teacher {
public void lesson(String colour) {
Pupil pupil = new Pupil()
SomeService bubble = pupil.blowBubble(colour)
bubble.pop()
}
}
class Pupil {
SomeService blowBubble(String colour) {
SomeService child = new SomeService(colour)
return child
}
}
class SomeService {
String colour
SomeService(String colour) {
this.colour = colour
}
void pop() {
println "popped ${colour}"
}
}
EDIT: Re comment about mocking something constructed and returned from a method, this is how I do it...
#Test
public void testMockReturned() {
MockFor bubbleMock = new MockFor(SomeService)
bubbleMock.demand.pop {}
bubbleMock.use {
Pupil pupil = new Pupil()
SomeService service = pupil.blowBubble("red")
service.pop()
}
}
In this case, Pupil should be a stub since you're only using it to inject bubbleProxy to you can perform verification against it. Like this,
import groovy.mock.interceptor.*
import org.junit.Test
class MockTest {
#Test
public void testMock() {
StubFor pupilMock = new StubFor(Pupil)
MockFor bubbleMock = new MockFor(SomeService)
GroovyObject bubbleProxy = bubbleMock.proxyInstance()
pupilMock.demand.blowBubble { String colour ->
return bubbleProxy
}
bubbleMock.demand.pop {}
bubbleMock.use {
Teacher teacher = new Teacher()
teacher.lesson("red")
}
}
}
Also, I believe the demands are copied onto the proxy when proxyInstance() is called, so you need to have your demands configured before instantiating the proxy.
However, I don't think there's a problem with multiple mocks, I think you just can't mix instance and class mocks (which you are doing with SomeService). The smallest example I could think of that demonstrates this was
import groovy.mock.interceptor.MockFor
// this works
missyMock = new MockFor(Missy)
missyMock.demand.saySomethingNice {}
missy = missyMock.proxyInstance()
missy.saySomethingNice()
missyMock.verify(missy)
// as does this
missyMock = new MockFor(Missy)
missyMock.demand.saySomethingNice {}
missyMock.use {
new Missy().saySomethingNice()
}
// this don't
missyMock = new MockFor(Missy)
missyMock.demand.saySomethingNice {}
missy = missyMock.proxyInstance()
missyMock.use { // fails here in use()'s built-in verify()
missy.saySomethingNice()
}
missyMock.verify(missy)
class Missy {
void saySomethingNice() {}
}
To demonstrate that the multiple mocks with nested use closures works, look at this contrived example
import groovy.mock.interceptor.MockFor
import org.junit.Test
class MockTest {
#Test
public void testMock() {
MockFor lessonMock = new MockFor(Lesson)
MockFor pupilMock = new MockFor(Pupil)
lessonMock.demand.getLessonPlan {}
pupilMock.demand.getName {}
pupilMock.use {
lessonMock.use {
Teacher teacher = new Teacher()
Pupil pupil = new Pupil()
Lesson lesson = new Lesson()
teacher.teach(pupil, lesson)
}
}
}
}
class Teacher {
void teach(Pupil pupil, Lesson lesson) {
println "Taught ${pupil.getName()} $lesson by ${lesson.getLessonPlan()}"
}
}
class Pupil {
String name
}
class Lesson {
LessonPlan lessonPlan
static class LessonPlan {}
}

What's an elegant way to have a reusable metaclass code in Groovy?

I would like to apply a meta-programming transformation to some of my classes, let's say by adding printXxx methods, like this:
class Person {
String name
}
def p = new Person()
p.printName() // does something
I have a rough idea how this can be done once I have a metaclass:
Person.metaClass.methodMissing = { name, args ->
delegate.metaClass."$name" = { println delegate."${getPropName(name)}" }
delegate."$name"(*args)
}
Now how do I turn this code into a reusable "library"? I would like to do something like:
#HasMagicPrinterMethod
class Person {
String name
}
or
class Person {
String name
static {
addMagicPrinters()
}
}
Define the behaviour you want to add as a trait
trait MagicPrinter {
void printProperties() {
this.properties.each { key, val ->
println "$key = $val"
}
}
}
Then add this trait to a class
class Person implements MagicPrinter {
String name
}
Now use it!
new Person(name: 'bob').printProperties()
You can go for a mixin approach:
class AutoPrint {
static def methodMissing(obj, String method, args) {
if (method.startsWith("print")) {
def property = (method - "print").with {
it[0].toLowerCase() + it[1..-1]
}
"${obj.getClass().simpleName} ${obj[property]}"
}
else {
throw new NoSuchMethodException()
}
}
}
You can mix it with a static block:
class Person {
static { Person.metaClass.mixin AutoPrint }
String name
}
def p = new Person(name: "john doe")
try {
p.captain()
assert false, "should've failed"
} catch (e) {
assert true
}
assert p.printName() == "Person john doe"
Or with expandoMetaClass:
class Car {
String model
}
Car.metaClass.mixin AutoPrint
assert new Car(model: "GT").printModel() == "Car GT"
Because 7 months later is the new 'now' :-)

How to make a "StringWriterMarkupBuilder"

How can I make a type which is a MarkupBuilder but has default constructor which is initialized with a StringWriter and overrides toString() to call the toString() on the StringWriter?
The idea is like the following, but of course initializing instance variables before super constructor calls is not allowed:
class StringWriterMarkupBuilder extends MarkupBuilder {
final def sw = new StringWriter()
StringWriterMarkupBuilder() {
super(sw)
}
#Override String toString() {
sw.toString()
}
}
Sorry, I finally figured out a solution by digging into the Groovy source code for MarkupBuilder!:
class StringWriterMarkupBuilder extends MarkupBuilder {
StringWriterMarkupBuilder() {
//MarkupBuilder.this(new IndentPrinter(new PrintWriter(writer)))
super(new StringWriter())
}
#Override String toString() {
//IndentPrinter.PrintWriter.StringWriter
return super.getPrinter().out.out.toString()
}
}
You could also do this with the metaClass like so:
import groovy.xml.MarkupBuilder
MarkupBuilder createSWMB() {
new StringWriter().with { sw ->
new MarkupBuilder( sw ).with { mb ->
mb.metaClass.toString = { -> sw.toString() }
mb
}
}
}
MarkupBuilder mb = createSWMB()
mb.root {
names {
name( first:'tim', second:'yates' )
}
}
println mb.toString()

Resources