I have a groovysh issue, where I've noticed that you can't use goovysh commands inside a loop context or inside functions. It seems that the commands get evaluated at parse time instead of runtime.
Is there some magic syntax to work around this?
Here is an example of this:
import org.codehaus.groovy.tools.shell.CommandSupport
import org.codehaus.groovy.tools.shell.Groovysh
class Rand extends CommandSupport {
private Random random = new Random()
protected Rand(final Groovysh shell) {
super(shell, 'rand', 'r')
}
public Integer execute(List args) {
random.nextInt()
}
}
:register Rand
(1..3).each {
println "number ${it}"
rand
foo = _
println "Random number is ${foo}"
}
When executed you see that the random number doesn't change and you can see that it evaluated when I pasted the code into the console, but before it went through the loop:
Groovy Shell (2.4.11, JVM: 1.8.0_51)
Type ':help' or ':h' for help.
-----------------------------------------------------------------------------------------------------------------------
groovy:000> import org.codehaus.groovy.tools.shell.CommandSupport
===> org.codehaus.groovy.tools.shell.CommandSupport
groovy:000> import org.codehaus.groovy.tools.shell.Groovysh
===> org.codehaus.groovy.tools.shell.CommandSupport, org.codehaus.groovy.tools.shell.Groovysh
groovy:000>
groovy:000> class Rand extends CommandSupport {
groovy:001> private Random random = new Random()
groovy:002>
groovy:002> protected Rand(final Groovysh shell) {
groovy:003> super(shell, 'rand', 'r')
groovy:004> }
groovy:005>
groovy:005> public Integer execute(List args) {
groovy:006> random.nextInt()
groovy:007> }
groovy:008>
groovy:008> }
===> true
groovy:000>
groovy:000> :register Rand
===> true
groovy:000>
groovy:000> (1..3).each {
groovy:001> println "number ${it}"
groovy:002> rand
===> -1321819102
groovy:002> foo = _
groovy:003> println "Random number is ${foo}"
groovy:004> }
number 1
Random number is -1321819102
number 2
Random number is -1321819102
number 3
Random number is -1321819102
===> [1, 2, 3]
groovy:000>
I'm hoping that there is some way to refer to the custom command via some other syntax that directly references the shell or something.
Ok, I just came up with a bit of a hacky solution. Getting a hold of the Groovysh
instance means I can evaluate when I feel like it:
import org.codehaus.groovy.tools.shell.CommandSupport
import org.codehaus.groovy.tools.shell.Groovysh
class Rand extends CommandSupport {
private Random random = new Random()
protected Rand(final Groovysh shell) {
super(shell, 'rand', 'r')
}
public Integer execute(List args) {
random.nextInt()
}
}
:register Rand
class Shell extends CommandSupport {
private Groovysh shellint
protected Shell(final Groovysh shell) {
super(shell, 'shell', 's')
shellint = shell
}
public Groovysh execute(List args) {
shellint
}
}
:register Shell
shell
myshell = _
(1..3).each {
println "number ${it}"
foo = myshell.execute("rand")
println "Random number is ${foo}"
}
With the output of:
Groovy Shell (2.4.11, JVM: 1.8.0_51)
Type ':help' or ':h' for help.
-----------------------------------------------------------------------------------------------------------------------
groovy:000> import org.codehaus.groovy.tools.shell.CommandSupport
===> org.codehaus.groovy.tools.shell.CommandSupport
groovy:000> import org.codehaus.groovy.tools.shell.Groovysh
===> org.codehaus.groovy.tools.shell.CommandSupport, org.codehaus.groovy.tools.shell.Groovysh
groovy:000>
groovy:000> class Rand extends CommandSupport {
groovy:001> private Random random = new Random()
groovy:002>
groovy:002> protected Rand(final Groovysh shell) {
groovy:003> super(shell, 'rand', 'r')
groovy:004> }
groovy:005>
groovy:005> public Integer execute(List args) {
groovy:006> random.nextInt()
groovy:007> }
groovy:008>
groovy:008> }
===> true
groovy:000>
groovy:000> :register Rand
===> true
groovy:000>
groovy:000> class Shell extends CommandSupport {
groovy:001>
groovy:001> private Groovysh shellint
groovy:002>
groovy:002> protected Shell(final Groovysh shell) {
groovy:003> super(shell, 'shell', 's')
groovy:004> shellint = shell
groovy:005> }
groovy:006>
groovy:006> public Groovysh execute(List args) {
groovy:007> shellint
groovy:008> }
groovy:009>
groovy:009> }
===> true
groovy:000>
groovy:000> :register Shell
===> true
groovy:000>
groovy:000> shell
===> org.codehaus.groovy.tools.shell.Groovysh@16ec132
groovy:000> myshell = _
===> org.codehaus.groovy.tools.shell.Groovysh@16ec132
groovy:000>
groovy:000> (1..3).each {
groovy:001> println "number ${it}"
groovy:002> foo = myshell.execute("rand")
groovy:003> println "Random number is ${foo}"
groovy:004> }
number 1
===> -666149132
Random number is -666149132
number 2
===> -1675600826
Random number is -1675600826
number 3
===> 412144734
Random number is 412144734
===> [1, 2, 3]
Is there another way to do this? In the context that I need this, the groovysh is a customised one with :register
removed
I modifed the groovysh jar file to add the :register
command back in, and then I could use the above solution. I did this by looking at https://github.com/groovy/groovy-core/blob/master/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/Groovysh.groovy and seeing that org/codehaus/groovy/tools/shell/commands.xml
contains the list of commands, and I added <command>org.codehaus.groovy.tools.shell.commands.RegisterCommand</command>
to the list.