how to pass parameters to the closure if use #DelegatesTo annotation? - groovy

if i change the code in Groovy DSL Doc here.
add some string 'hello world' to email, like this
email('hello world') { // change here
from 'dsl-guru#mycompany.com'
to 'john.doe#waitaminute.com'
subject 'The pope has resigned!'
body {
p 'Really, the pope has resigned!'
}
}
and change
def email(def name, #DelegatesTo(EmailSpec) Closure cl) { // change here
def email = new EmailSpec()
def code = cl.rehydrate(email, this, this)
code.resolveStrategy = Closure.DELEGATE_ONLY
code.call(name) // change here
}
so, how to modify the class EmailSpec to get the string 'hello world' ??

To tell the compile that the closure will be called with a parameter you need to add the ClosureParams annotation.
To stick with your example:
def email(def name,
#ClosureParams(value = SimpleType, options = "java.lang.String")
#DelegatesTo(EmailSpec) Closure cl) {
def email = new EmailSpec()
def code = cl.rehydrate(email, this, this)
code.resolveStrategy = Closure.DELEGATE_ONLY
code.call(name) // change here
}
will tell the compiler that the first parameter is a String.
For more details have a look at the section The #ClosureParams annotation in the groovy documentation.

Yes, i found a way, but not perfect.
Simple
new EmailSpec(name) // change to
however, i really want to use groovy function call(name) to solve it

Related

Groovy closure return of value to variable

Very basic question but I cannot find an answer:
I have the below code in a file g.groovy, and it functions in printing output:
#! /usr/env/groovy
def matchFiles = { match ->
new File(".").eachFile() {
if (it.name =~ match) {
println it
}
}
}
matchFiles('.groovy') prints ./g.groovy to screen.
But I want to capture the output of the closure in a variable and use it elsewhere, e.g.
def fileMatches = matchFiles('.groovy')
but cannot figure this out.
Tried changing println it to return it and then running
def fileMatches = matchFiles('.groovy')
fileMatches.println { it }
but this prints something like g$_run_closure2#4b168fa9
Any help is much appreciated, sorry for any incorrect nomenclature, very new to Groovy
according to the name matchFiles I assume you want to return all matched files
so, you have to define an array result variable where you are going to store each matched file
and then return this result variable after eachFile{...} closure
def matchFiles = { match ->
def result=[]
new File(".").eachFile {
if (it.name =~ match) {
result.add(it)
}
}
return result
}
println matchFiles(/.*/)

define global variables and functions in build.gradle

Is there a way to define global variables in build.gradle and make them accessible from everywhere.
I mean something like this
def variable = new Variable()
def method(Project proj) {
def value = variable.value
}
Because that way it tells me that it cannot find property.
Also I'd like to do the same for the methods.
I mean something like this
def methodA() {}
def methodB() { methodA() }
Use extra properties.
ext.propA = 'propAValue'
ext.propB = propA
println "$propA, $propB"
def PrintAllProps(){
def propC = propA
println "$propA, $propB, $propC"
}
task(runmethod) << { PrintAllProps() }
Running runmethod prints:
gradle runmethod
propAValue, propAValue
:runmethod
propAValue, propAValue, propAValue
Read more about Gradle Extra Properties here.
You should be able to call functions from functions without doing anything special:
def PrintMoreProps(){
print 'More Props: '
PrintAllProps()
}
results in:
More Props: propAValue, propAValue, propAValue

Writing dynamic query results into file

I am trying to write a generic program in Groovy that will get the SQL from config file along with other parameters and put them into file.
here is the program:
def config = new ConfigSlurper().parse(new File("config.properties").toURL())
Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver);
def fileToWrite = new File(config.copy.location)
def writer = fileToWrite.newWriter()
writer.write(config.file.headers)
sql.eachRow(config.sql){ res->
writer.write(config.file.rows)
}
in the config the sql is something like this:
sql="select * from mydb"
and
file.rows="${res.column1}|${res.column2}|${res.column3}\n"
when I run it I get
[:]|[:]|[:]
[:]|[:]|[:]
[:]|[:]|[:]
in the file. If I substitute
writer.write(config.file.rows)
to
writer.write("${res.column1}|${res.column2}|${res.column3}\n")
it outputs the actual results. What do I need to do different to get the results?
You accomplish this by using lazy evaluation of the Gstring combined with altering the delegate.
First make the Gstring lazy by making the values be the results of calling Closures:
file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
Then prior to evaluating alter the delegate of the closures:
config.file.rows.values.each {
if (Closure.class.isAssignableFrom(it.getClass())) {
it.resolveStrategy = Closure.DELEGATE_FIRST
it.delegate = this
}
}
The delegate must have the variable res in scope. Here is a full working example:
class Test {
Map res
void run() {
String configText = '''file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
sql="select * from mydb"'''
def slurper = new ConfigSlurper()
def config = slurper.parse(configText)
config.file.rows.values.each {
if (Closure.class.isAssignableFrom(it.getClass())) {
it.resolveStrategy = Closure.DELEGATE_FIRST
it.delegate = this
}
}
def results = [
[column1: 1, column2: 2, column3: 3],
[column1: 4, column2: 5, column3: 6],
]
results.each {
res = it
println config.file.rows.toString()
}
}
}
new Test().run()
The good news is that the ConfigSlurper is more than capable of doing the GString variable substitution for you as intended. The bad news is that it does this substitution when it calls the parse() method, way up above, long before you have a res variable to substitute into the parser. The other bad news is that if the variables being substituted are not defined in the config file itself, then you have to supply them to the slurper in advance, via the binding property.
So, to get the effect you want you have to parse the properties through each pass of eachRow. Does that mean you have to create a new ConfigSlurper re-read the file once for every row? No. You will have to create a new ConfigObject for each pass, but you can reuse the ConfigSlurper and the file text, as follows:
def slurper = new ConfigSlurper();
def configText = new File("scripts/config.properties").text
def config = slurper.parse(configText)
Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver);
def fileToWrite = new File(config.copy.location)
def writer = fileToWrite.newWriter()
writer.write(config.file.headers)
sql.eachRow(config.sql){ result ->
slurper.binding = [res:result]
def reconfig = slurper.parse(configText)
print(reconfig.file.rows)
}
Please notice that I changed the name of the Closure parameter from res to result. I did this to emphasize that the slurper was drawing the name res from the binding map key, not from the closure parameter name.
If you want to reduce wasted "reparsing" time and effort, you could separate the file.rows property into its own separate file. i would still read in that file text once and reuse the text in the "per row" parsing.

Groovy: evaluate a property with a variable in it

A bit new to groovy, I am trying to match a variable string to a property pulled from a file using ConfigSlurper. I have the slurper part working fine, but can't seem to figure out the right way to evaluate a property with a variable in it. I think I was getting warm when I found evaluating-code-dynamically-in-groovy but I am not entirely sure.
//properties.groovy
jobs {
foo {
email="foo#email.com"
}
}
//myscript.groovy
def config = new ConfigSlurper().parse(new File('properties.groovy').toURI().toURL())
List jobs = (ArrayList) BazAPI.getArtifacts(bucket) // list of objects, foo is one
ListIterator jobIterator = jobs.listIterator();
while (jobIterator.hasNext()) {
Object j = jobIterator.next();
job_name = "${j.name}" //
email = config.jobs."${job_name}".email /* NEED TO FIGURE OUT HOW TO EVAL */
foo_email = config.jobs.foo.email //evaluates to the correct property in properties.groovy
//these values get fed to a DSL but to illustrate
println "${job_name}" // prints foo
println "${email}" // prints [:]
println "${foo_email}" // prints foo#email.com
}
Have you tried
config.jobs[ j.name ].email

How to pass variable parameters to an XPages SSJS function?

If I have a function in SSJS and I want to pass one "firm" parameter and a list of others that can change, what's the best way to do that? With some kind of hashMap or JSON or something else?
for example given something like:
myfunction( code:string, paramList:??) {
// do stuff here
}
Basically the function will create a document. And sometimes I'll have certain fields I'll want to pass in right away and populate and other times I'll have different fields I will want to populate.
How would you pass them in and then parse out in the function?
Thanks!
Use the arguments parameter... In JavaScript you are not required to define any of your parameters in the function block itself. So, for example, the following call:
myFunction(arg1, arg2, arg3, arg4);
can legally be passed to the following function:
myFunction () {
// do stuff here...
}
when I do this, I usually place a comment in the parens to indicate I am expecting variable arguments:
myFunction (/* I am expecting variable arguments to be passed here */) {
// do stuff here...
}
Then, you can access those arguments like this:
myFunction (/* I am expecting variable arguments to be passed here */) {
if (arguments.length == 0) {
// naughty naughty, you were supposed to send me things...
return null;
}
myExpectedFirstArgument = arguments[0];
// maybe do something here with myExpectedFirstArgument
var whatEvah:String = myExpectedFirstArgument + ": "
for (i=1;i<arguments.length;i++) {
// now do something with the rest of the arguments, one
// at a time using arguments[i]
whatEvah = whatEvah + " and " + arguments[i];
}
// peace.
return whatEvah;
}
Wallah, variable arguments.
But, more to the point of your question, I don't think you need to actually send variable arguments, nor go through the hassle of creating actual JSON (which is really a string interpretation of a javascript object), just create and send the actual object then reference as an associative array to get your field names and field values:
var x = {};
x.fieldName1 = value1;
x.fieldName2 = value2;
// ... etc ...
then in your function, which now needs only two parameters:
myFunction(arg1, arg2) {
// do whatever with arg1
for (name in arg2) {
// name is now "fieldName1" or "fieldName2"
alert(name + ": " + x[name]);
}
}
Hope this helps.
I would do this with a JSON object as the second parameter...
function myfunction(code:String, data) {
// do stuff here...
var doc:NotesDocument = database.CreateDocument();
if(data) {
for (x in data) {
doc.replaceItemValue(x, data[x]);
}
}
// do more stuff
doc.save(true, false);
}
Then you call the function like this:
nyfunction("somecode", {form:"SomeForm", subject:"Whatever",uname:#UserName()});
Happy coding.
/Newbs
I don't think that is possible in SSJS. I think the best option you have is to pass a hashmap or your own (java) object. I think a custom java object would be the nicest option because you can define some 'structure' on how your function can process it. A hashmap can be easily extended but it is not easy if you have a lot of code that create a lot of different hashmap structures...

Resources