Using closures for specifying default parameter values in Groovy - grails

Let's consider the next function:
def generateUniqueIdent(String text, uniqueSuffix = {uid -> String.valueOf(uid)}) {
doSomething(text) + uniqueSuffix()
}
Now, when I try the next modification:
def generateUniqueIdent(String text, uniqueSuffix = { hash(text) }) {
doSomething(text) + uniqueSuffix()
}
..I got the next error:
| Error Fatal error during compilation
org.apache.tools.ant.BuildException: BUG! exception in phase 'class
generation' in source unit 'some path here' tried to get a variable
with the name text as stack variable, but a variable with this
name was not created (Use --stacktrace to see the full trace)
At the same time, if I try to use the name text as a parameter of the closure:
def generateUniqueIdent(String text, uniqueSuffix = {text -> hash(text) }) {
doSomething(text) + uniqueSuffix(text)
}
..then I got another error:
The current scope already contains a variable of the name text
The question is: can I somehow get access to other parameters from a closure, which is assigned as a default value to one of function parameters?
If no, then, why can't I use the same name as one of function parameters has inside the described closure?

You can use the default it parameter:
def generateUniqueIdent(String text, uniqueSuffix = { hash(it) }) {
doSomething(text) + uniqueSuffix(text)
}
(working example)
Or use a different name for the closure parameter instead of text:
def generateUniqueIdent(String text, uniqueSuffix = { x -> hash(x) }) {
doSomething(text) + uniqueSuffix(text)
}
(example)
Unfortunately, accessing the previous parameter from the closure is working for me in this case, so i don't know what the original problem is :S

Related

why is my for-each loop not working properly in parallel stages in jenkins scripted syntax? [duplicate]

In the context of Jenkins pipelines, I have some Groovy code that's enumerating a list, creating closures, and then using that value in the closure as a key to lookup another value in a map. This appears to be rife with some sort of anomaly or race condition almost every time.
This is a simplification of the code:
def tasks = [:]
for (platformName in platforms) {
// ...
tasks[platformName] = {
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
// ...
}
tasks.failFast = true
parallel(tasks)
platforms has two values. I will usually see two iterations and two tasks registered and the keys in tasks will be correct, but the echo statement inside the closure indicates that we're just running one of the platforms twice:
14:20:02 [platform2] Uploading for platform [platform1] to [some_path/platform1].
14:20:02 [platform1] Uploading for platform [platform1] to [some_path/platform1].
It's ridiculous.
What do I need to add or do differently?
It's the same issue as you'd see in Javascript.
When you generate the closures in a for loop, they are bound to a variable, not the value of the variable.
When the loop exits, and the closures are run, they will all be using the same value...that is -- the last value in the for loop before it exited
For example, you'd expect the following to print 1 2 3 4, but it doesn't
def closures = []
for (i in 1..4) {
closures << { -> println i }
}
closures.each { it() }
It prints 4 4 4 4
To fix this, you need to do one of two things... First, you could capture the value in a locally scoped variable, then close over this variable:
for (i in 1..4) {
def n = i
closures << { -> println n }
}
The second thing you could do is use groovy's each or collect as each time they are called, the variable is a different instance, so it works again:
(1..4).each { i ->
closures << { -> println i }
}
For your case, you can loop over platforms and collect into a map at the same time by using collectEntries:
def tasks = platforms.collectEntries { platformName ->
[
platformName,
{ ->
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
}
]
}
Hope this helps!

Replace console.log(), how to input multiple arguments? (NodeJS)

I want to make a new function to "replace" console.log(). Basically a print() function that lets you add a timestamp at the beginning of the text and also change the colors of the timestamp, followed by the text you want to print.
Example:
colors = require('colors')
function debug_print(str) {
console.log(new String(Date().getTime().brightBlue + ": " + str)
}
It works, however it doesn't have the feature where you can place multiple arguments into the function call, or so you can print out an object the way console.log does:
myObject = {"hello": "hey"}
console.log("myObject", myObject); // <-- Works, prints out "myObject {'hello' : 'hey'}"
debug_print("myObject", myObject); // <-- Doesn't work
How do I change my function to both allow multiple arguments and also print out objects the same way console.log does, all in one print line?
You can use spread operator when defining function arguments. You should not use arguments itself (unless you really know what you are doing by that).
function debug_print(...msg) {
console.log('whatever: ', ...msg);
}
You can use arguments object to do so.
In Javascript, arguments is a local JavaScript object variable that is available in all non-arrow functions.
It is an Array-like object, containing all the arguments passed.
function my_function() {
// convert Arguments to a normal Array
var args = Array.from(arguments);
console.log.apply(console, args)
}
my_function(1, 2, "custom text") // 1, 2, "custom text"
As you want to add a text at the beginning of the message, you can simply add an element at the beginning of the Array
args.unshift(timestamp + ": ");

Accessing config variables stored in maps by key

I have a variable in groovy like below:
project.Map
{
time.'1 1 * ?' = ['T1']
time.'2 1 * ?' = ['T2']
templates.'T1' = ['Z','X','Y']
templates.'T2' = ['Q']
}
Sorry but I am new to groovy ,when i try to access the individual
variable values in project.map how do i access them
i tried something like below
log.info(grailsApplication.config.project.Map.time[1])
log.info(grailsApplication.config.project.Map.get('time.'2 1 * ?'' ))
log.info(grailsApplication.config.project.Map.get('time[0]' ))
log.info(grailsApplication.config.project.Map.time.get('1 1 * ?'))
but they all print null value or object references.how do i access values for
time and templates both within a for loop and without it.
please see http://grails.org/doc/latest/guide/conf.html#config for the ways the config is allowed to nest. your outer syntax is especially mentioned to not be allowed:
However, you can't nest after using the dot notation. In other words, this won't work:
// Won't work!
foo.bar {
hello = "world"
good = "bye"
}
You have to write it as
project { Map { ... } }
The inner dotted parts (with the assignment) are ok (according to the doc)

is there aliasing in lua similar to ruby

Can you alias a function (not in a class) in LUA in a similar way to Ruby? In ruby you would do something like this:
alias new_name_for_method method()
def method()
new_name_for_method() # Call original method then do custom code
i = 12 # New code
end
I'm asking because I'm developing for a program that uses LUA scripting and I need to override a function that is declared in a default file.
In Lua, functions are values, treated like any other value (number, string, table, etc.) You can refer to a function value via as many variables as you like.
In your case:
local oldmethod = method
function method(...)
oldmethod(...)
i = 12 -- new code
end
keep in mind that
function method() end
is shorthand for:
method = function() end
function() end just creates a function value, which we assign to the variable method. We could turn around and store that same value in a dozen other variables, or assign a string or number to the method variable. In Lua, variables do not have type, only values do.
More illustration:
print("Hello, World")
donut = print
donut("Hello, World")
t = { foo = { bar = donut } }
t.foo.bar("Hello, World")
assert(t.foo.bar == print) -- same value
FYI, when wrapping a function, if you want its old behavior to be unaffected for now and forever, even if its signature changes, you need to be forward all arguments and return values.
For a pre-hook (new code invoked before the old), this is trivial:
local oldmethod = method
function method(...)
i = 12 -- new code
return oldmethod(...)
end
A post-hook (new code invoked after the old) is a bit more expensive; Lua supports multiple return values and we have to store them all, which requires creating a table:
local oldmethod = method
function method(...)
local return_values = { oldmethod(...) }
i = 12 -- new code
return unpack(return_values)
end
In lua, you can simply override a variable by creating a new function or variable with the same name.
function name_to_override()
print('hi')
end
If you still want to be able to call the old function:
local old_function = name_to_override
function name_to_override()
old_function()
print('hi')
end

How to parse text in Groovy

I need to parse a text (output from a svn command) in order to retrieve a number (svn revision).
This is my code. Note that I need to retrieve all the output stream as a text to do other operations.
def proc = cmdLine.execute() // Call *execute* on the strin
proc.waitFor() // Wait for the command to finish
def output = proc.in.text
//other stuff happening here
output.eachLine {
line ->
def revisionPrefix = "Last Changed Rev: "
if (line.startsWith(revisionPrefix)) res = new Integer(line.substring(revisionPrefix.length()).trim())
}
This code is working fine, but since I'm still a novice in Groovy, I'm wondering if there were a better idiomatic way to avoid the ugly if...
Example of svn output (but of course the problem is more general)
Path: .
Working Copy Root Path: /svn
URL: svn+ssh://svn.company.com/opt/svnserve/repos/project/trunk
Repository Root: svn+ssh://svn.company.com/opt/svnserve/repos
Repository UUID: 516c549e-805d-4d3d-bafa-98aea39579ae
Revision: 25447
Node Kind: directory
Schedule: normal
Last Changed Author: ubi
Last Changed Rev: 25362
Last Changed Date: 2012-11-22 10:27:00 +0000 (Thu, 22 Nov 2012)
I've got inspiration from the answer below and I solved using find(). My solution is:
def revisionPrefix = "Last Changed Rev: "
def line = output.readLines().find { line -> line.startsWith(revisionPrefix) }
def res = new Integer(line?.substring(revisionPrefix.length())?.trim()?:"0")
3 lines, no if, very clean
One possible alternative is:
def output = cmdLine.execute().text
Integer res = output.readLines().findResult { line ->
(line =~ /^Last Changed Rev: (\d+)$/).with { m ->
if( m.matches() ) {
m[ 0 ][ 1 ] as Integer
}
}
}
Not sure it's better or not. I'm sure others will have different alternatives
Edit:
Also, beware of using proc.text. if your proc outputs a lot of stuff, then you could end up blocking when the inputstream gets full...
Here is a heavily commented alternative, using consumeProcessOutput:
// Run the command
String output = cmdLine.execute().with { proc ->
// Then, with a StringWriter
new StringWriter().with { sw ->
// Consume the output of the process
proc.consumeProcessOutput( sw, System.err )
// Make sure we worked
assert proc.waitFor() == 0
// Return the output (goes into `output` var)
sw.toString()
}
}
// Extract the version from by looking through all the lines
Integer version = output.readLines().findResult { line ->
// Pass the line through a regular expression
(line =~ /Last Changed Rev: (\d+)/).with { m ->
// And if it matches
if( m.matches() ) {
// Return the \d+ part as an Integer
m[ 0 ][ 1 ] as Integer
}
}
}

Resources