Compile error when using modulus operator in MarkupTemplateEngine - groovy

I have been using the Groovy template engine without issue for about a year, but just ran into a strange issue and I'm not sure if I'm crazy, or this is expected behavior.
All things being equal, using this function throws a compile error when used inside a Groovy template.
def isEven(n) { n % 2 == 0 }
General error during class generation: org.codehaus.groovy.ast.Parameter cannot be cast to org.codehaus.groovy.ast.expr.VariableExpression
But if I do it this way...
def isEven(n) { n.mod(2) == 0 }
...everything works as expected.
I didn't notice any special case in the docs, but I could have missed it. Could anyone offer some insight into the meaning behind this error, and perhaps offer some guidance to avoid pitfalls like this in the future?
Cheers, and thank you!
Groovy version 2.5
Update
Here is a full example template that throws the error.
def isEven(n) { n % 2 == 0 }
def items = ["Zero", "One", "Two"]
yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
body {
ul {
items.eachWithIndex { item, i ->
if(isEven(i)) li(item)
}
}
}
}
And here is an example of how the template is rendered.
TemplateConfiguration config = new TemplateConfiguration();
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
Template template = engine.createTemplate(new File('test.tpl').newReader());
Map<String, Object> model = new HashMap<>();
Writable output = template.make(model);
output.writeTo(new File('test.html').newWriter());
Changing the isEven method to use Number.mod compiles just fine.

Related

SpockExecutionException: Data provider has no data

I've done a bunch of searching and, while I've found a few hits, like Why does Spock think my Data Provider has no data?, none of them seem to be very helpful.
I've only done a data provider a couple of times, but it seems perfect for this. I have the following static method:
static List<ContactPointType> getAddressTypes() {
List<ContactPointType> result = new ArrayList<>();
for (ContactPointType cpType : ContactPointType.values()) {
if (cpType.toString().endsWith("Addr")) {
result.add(cpType);
}
}
return result;
}
And then I'm trying to use it as a data provider for calling a function on my class:
#Unroll("#cpType should be address")
def "isAddress addresses"() {
expect: "isAddress() to be true"
contactPoint.isAddress(cpType)
where:
cpType << getAddressTypes()
}
When I run this, I get:
org.spockframework.runtime.SpockExecutionException: Data provider has no data
at org.spockframework.runtime.JUnitSupervisor.afterFeature(JUnitSupervisor.java:191)
at org.spockframework.runtime.BaseSpecRunner.runFeature(BaseSpecRunner.java:236)
Like I said, it seems pretty straightforward. Does anyone have any ideas?
Well, I've tried the data provider feature and it works as expected:
#Unroll("max(1, #cpType) == #cpType")
class MyFirstSpec extends Specification {
def "let's try this!"() {
expect:
Math.max(1, cpType) == cpType
where:
cpType << dataProvider()
}
List<Integer> dataProvider() {
[2,3,4]
}
}
However if I rewrite the dataProvider function like this, I see the exception that you've mentioned:
List<Integer> dataProvider() {
[] // returns an empty list
}
Yields:
org.spockframework.runtime.SpockExecutionException: Data provider has no data
at org.spockframework.runtime.JUnitSupervisor.afterFeature(JUnitSupervisor.java:180)
at org.spockframework.runtime.BaseSpecRunner.runFeature(BaseSpecRunner.java:239)
So my idea is that probably you end up with an empty list in the data provider implementation and that's why it doesn't work
Another possible (although slightly less realistic idea to be honest) is that you've messed something up with Groovy/Java interconnection
So in terms of resolution to wrap up:
Try to use some more straightforward data provider implementation and test it
If it doesn't work - just define data provider like me in Groovy and re-test

Groovy Script in Oracle Agile PLM Troubleshooting

I'm in the process of learning basic programming (now reading through SICP now) for the purpose of writing Groovy scripts to deploy in Oracle Agile PLM. I've gone through a couple Java tutorials online, and I'm reading through the Groovy Recipes book as well. I'm trying to deploy some basic scripts as I go, and I've run into some issues with an if else statement. I'm hoping someone can point me in the right direction. In this case 1272 and 1332 are attribute IDs. When attribute 1272 equals 'Stock' (an attribute populated by a list), then attribute 1332 (a text field) should equal 'AWESOME'. For all other values of attribute 1272, attribute 1332 should equal 'NOT AWESOME'. Here's what I have right now:
import com.agile.agileDSL.ScriptObj.IBaseScriptObj
// add other import statements here
void invokeScript(IBaseScriptObj obj) {
//script body starts here.
def session = obj.getAgileSDKSession()
def objectClassId = obj.getObjectClassId()
def objectNumber = obj.getObjectNumber()
def dataObject = session.getObject(objectClassId, objectNumber) {
if ((dataObject.getValue(1272)) == 'Stock') {
dataObject.setValue(1332, 'AWESOME')
} else {
dataObject.setValue(1332, 'NOT AWESOME') }
}
}
With this and other variations I've tried I keep getting the "groovy.lang.MissingMethodException: No signature of method" error.
Not sure on the libraries you're using but should you have the curly braces after getObject()?, maybe try...
def dataObject = session.getObject(objectClassId, objectNumber)
if ((dataObject.getValue(1272)) == 'Stock') {
dataObject.setValue(1332, 'AWESOME')
}
else {
dataObject.setValue(1332, 'NOT AWESOME')
}

Groovy - Type check AST generated code

I have a Groovy application that can be custimized by a small Groovy DSL I wrote. On startup, the application loads several Groovy scripts, applies some AST transformations and finally executes whatever was specified in the scripts.
One of the AST transformations inserts a couple of lines of code into certain methods. That works fine and I can see the different behavior during runtime. However, sometimes the generated code is not correct. Although I load the scripts with the TypeChecked customizer in place, my generated code is never checked for soundness.
To show my problem, I constructed an extreme example. I have the following script:
int test = 10
println test // prints 10 when executed without AST
I load this script and insert a new line of code between the declaration of test and println:
public void visitBlockStatement(BlockStatement block) {
def assignment = (new AstBuilder().buildFromSpec {
binary {
variable "test"
token "="
constant 15
}
}).first()
def newStmt = new ExpressionStatement(assignment)
newStmt.setSourcePosition(block.statements[1])
block.statements.add(2, newStmt)
super.visitBlockStatement(block)
}
After applying this AST, the script prints 15. When I use AstNodeToScriptVisitor to print the Groovy code of the resulting script, I can see the new assignment added to the code.
However, if I change the value of the assignment to a String value:
// ...
def assignment = (new AstBuilder().buildFromSpec {
binary {
variable "test"
token "="
constant "some value"
}
}).first()
// ...
I get a GroovyCastExcpetion at runtime. Although the resulting script looks like this:
int test = 10
test = "some value" // no compile error but a GroovyCastException at runtime here. WHY?
println test
no error is raised by TypeChecked. I read in this mailing list, that you need to set the source position for generated code to be checked, but I'm doing that an it still doesn't work. Can anyone provide some feedback of what I am doing wrong? Thank you very much!
Update
I call the AST by attaching it to the GroovyShell like this:
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new ASTTransformationCustomizer(TypeChecked)
)
config.addCompilationCustomizers(
new ASTTransformationCustomizer(new AddAssignmentAST())
)
def shell = new GroovyShell(config)
shell.evaluate(new File("./path/to/file.groovy"))
The class for the AST itself looks like this:
#GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class AddAssignmentAST implements ASTTransformation {
#Override
public void visit(ASTNode[] nodes, SourceUnit source) {
def transformer = new AddAssignmentTransformer()
source.getAST().getStatementBlock().visit(transformer)
}
private class AddAssignmentTransformer extends CodeVisitorSupport {
#Override
public void visitBlockStatement(BlockStatement block) {
// already posted above
}
}
}
Since my Groovy script only consists of one block (for this small example) the visitBlockStatement method is called exactly once, adds the assignment (which I can verify since the output changes) but does not ever throw a compile-time error.

Groovy JsonBuilder call methods to add information

The following code works fine
def json = new JsonBuilder()
json {
writeNumbers(delegate, "myNumbers")
}
println json.toPrettyString()
def writeNumbers(json, name) {
json."$name" {
"w" 32
"h" 32
}
}
But as soon as I move or add a writeNumbers call inside of another 'scope', I get a stackoverflow exception; just like so
def json = new JsonBuilder()
json {
scopes {
writeNumbers(delegate, "myNumbers")
}
}
println json.toPrettyString()
def writeNumbers(json, name) {
json."$name" {
"w" 32
"h" 32
}
}
Result:
Caught: java.lang.StackOverflowError
java.lang.StackOverflowError
Why is this happening and how can I get around it?
Thanks
I think this is caused by the underlying error that the method writeNumbers is unknown when building the chain of closures.
You need to change:
writeNumbers(delegate, "myNumbers")
to
this.writeNumbers(delegate, "myNumbers")
And it will work... Interesting though, this feels like a bug... I'll investigate if I get some free time ;-)
Edit: Found this previous question which shows the same thing

erazor set variable

a little question about erazor https://github.com/ciscoheat/erazor
i know this framwork is based on Razor template engine. http://weblogs.asp.net/scottgu/archive/2010/07/02/introducing-razor.aspx
i noticed the api doesn't fit exactly with Razor (ex: #for(a in p) differs from RAZOR)
this template system for haxe is very handy...
i just don't know how to setup a variable like we do in templo ( :: set mock="tada!":: )
//#scope is mycontroller;
#{var mock = scope.getMock()}
#if(mock!=null){
//display some html
}
any tips ?
thx
The following snippet works:
import erazor.Template;
import neko.Lib;
class Main {
static function main() {
var template = new Template("#{var mock = scope.getMock();} #if (mock != null) { #mock }");
Lib.print(template.execute( { scope : { getMock : function() return "hi" } } ));
}
}
What you missed is that inside a code block all the statements must be correctly closed (missing ;). Also erazor is loosely based on Razor and uses the Haxe syntax for expressions.

Resources