Can you pass variables to Groovy's evaluate(File file)? - groovy

Is it possible to pass scope variables to Groovy's evaluate(File file)? And therefore to access variable a in the file OtherScript.groovy listed below?
FirstScript.groovy:
a = 4
evaluate(new File('OtherScript.groovy'))
println 'I can see b = ${b}'
OtherScript.groovy:
b = 2 * a
When running this example the following exception is raised: groovy.lang.MissingPropertyException: No such property: a for class: OtherScript

I believe you can use some sort of Bean Scripting Framework (BSF) which supported by Groovy,
Groovy implement BSF in org.codehaus.groovy.bsf.GroovyEngine,here is a quick example to use BSFManager
#Grab('org.apache.bsf:bsf-api:3.1')
import org.apache.bsf.BSFManager
BSFManager manager = new BSFManager()
manager.declareBean("xyz", 4, Integer.class)
Object answer = manager.eval("groovy", "test.groovy", 0, 0, "xyz + 1")
assert 5 == answer
Example from the groovy doc.
This answer uses GroovyShell which is another possible solution for your example.
code tested with Groovy v2.5.1. Hope this helps.

your scripts are fine except one thing:
the string with println should be doublequoted to interpolate expressions
so, if you have
FirstScript.groovy
a = 4
evaluate(new File('OtherScript.groovy'))
println "I can see b = ${b}"
OtherScript.groovy
b = 2 * a
then the following command line works:
groovy FirstScript.groovy
and prints:
I can see b = 8

Related

Emit local variable values during Groovy Shell execution

Consider that we have the following Groovy script:
temp = a + b
temp * 10
Now, given that a and b are bound to the context with their respective values, and that the script is executed using a groovy shell script.
Is there a way I could obtain the value of thetemp variable assignment, without printing/logging the value to the console? For instance, given a=2 and b=3, I would like to not only know that the script returned 50 but also that temp=5. Is there a way to intercept every assignment to capture the values?
Any suggestions or alternatives are appreciated. Thanks in advance!
You can capture all bindings and assignments from the script by passing a Binding instance to the GroovyShell object. Consider following example:
def binding = new Binding()
def shell = new GroovyShell(binding)
def script = '''
a = 2
b = 3
temp = a + b
temp * 10
'''
println shell.run(script, 'script.groovy', [])
println binding.variables
Running this script prints following two lines to the console:
50
[args:[], a:2, b:3, temp:5]
If you want to access the value of temp script variable you simply do:
binding.variables.temp

Groovy replacing place holders of file content similar to multiline-string

Got the following script which replaces values in the multi line string.
def param1 = 'Groovy'
def param2 = 'Java'
def multiline = """
${param1} is closely related to ${param2},
so it is quite easy to make a transition.
"""
//output shows with the replaced values for param1 and param2
println multiline
Output is shown as expected:
Groovy is closely related to Java,
so it is quite easy to make a transition.
Issue:
Now I am trying to do the same using file instead of multi line string. i.e., copied the multi line string to a file and using the below script to do the same but not working(not giving the desired result).
I am sure, it must be something I am missing. Tried multiple way, but went futile.
Try #1: Script
def param1 = 'Groovy'
def param2 = 'Java'
def multiline = Eval.me(new File('test.txt').text)
println multiline
And it fails to run. Error follows:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script1.groovy: 1: expecting EOF, found ',' # line 1, column 42.
s closely related to ${param2},
^
1 error
Try #2
def param1 = 'Groovy'
def param2 = 'Java'
def multiline = new File('test.txt').text
def finalContent = """$multiline"""
println finalContent
And there is no difference in the output, just showing the file content as it is.
Output:
${param1} is closely related to ${param2},
so it is quite easy to make a transition.
Any pointers what am I missing?
Please note that at the moment I want to avoid file content modification using replace() method.
Not sure why it doesn't work, however what I may suggest here is that templating suits best here. Please have a look:
import groovy.text.SimpleTemplateEngine
def f = new File('lol.txt')
println f.text
def binding = [
param1: 'Groovy',
param2: 'Java',
]
def engine = new SimpleTemplateEngine()
def template = engine.createTemplate(f.text).make(binding)
println template.toString()
An explanation why file content is not evaluated may be found here.

Declare multiple variables in one line in Spock when: block

I would like to know how I can define, without initializing, multiple variables in one line in Spock spec as shown below.
I have tried:
import spock.lang.Specification
class exampleSpec extends Specification {
def "foo"() {
when:
def a, b
a = 0
b = 1
then:
a != b
}
}
But this fails when accessing b:
No such property: b for class: ....
I have managed to get the following to work:
def (a, b) = []
But I'd like something better.
Any help is greatly appreciated!
I am using:
Groovy Version: 2.4.3 JVM: 1.8.0_45 Vendor: Oracle Corporation OS: Linux
I fear that cannot be done in any of the blocks (like given:, setup:, when:). There is an easy work around.
#Grab(group='org.spockframework', module='spock-core', version='0.7-groovy-2.0')
import spock.lang.Specification
class ExampleSpec extends Specification {
def "foo"() {
def a, b
when:
a = 0
b = 1
then:
a != b
}
}
Move the declaration out of when:/given:/setup: block and have them as method variables. The setup:/given: label is optional and may be omitted, resulting in an implicit setup block.
Although this works, I would like to know the reason why this did not work otherwise. You can create an issue in github.
You should use the where: block to pass parameters. There's no def required because they're passed as an argument, if you're feeling fancy you can even have a test that is run multiple times.
def "Use where to pass test data"(){
expect: 1 == myNumber
2 == myOther1
where: myNumber = 1
myOther1 = 2
}
Here's a link to some other examples I've written that show how to pass data to your tests. If you really like multiple assignment (even though it's not a good idea) here's how you could use it in a where: block.
If you're curious about the various blocks here is a summary of everything I've read. Don't take my word for it, here's some official documentation.
You might be happy to know that this issue has been raised already, but it hasn't seen any activity in the last month. My assumption is that something about multiple assignment doesn't agree with the AST transformations that Spock uses on your code.

Calling groovy method from script

I'm writing a program in groovy, I want it to run a script with dynamically changing variables. The script is supposed to execute a method with its variables. This method is located in the program that runs the script.
The script would contain something like this:
// getAttribute is a method in my main program, the program which also runs the script
value = getValue("java.lang:type=OperatingSystem", "FreePhysicalMemorySize")
// do something with the value
if (value > 9000) { output = "ERROR" }
// the same thing maybe a few more times
value = getValue(...
I want to keep it as simple as possible.
I was working with these examples: http://groovy.codehaus.org/Embedding+Groovy
I already tried to use the GroovyScriptEngine, but it seems like the script only accepts Strings as input values. It would be great if I could hand over method pointers.
Just trying to hand over an integer like this:
def roots = 'PATH\\script.groovy'
def groscreng = new GroovyScriptEngine(roots)
def binding = new Binding()
def input = 42
binding.setVariable("input", input)
groscreng.run("script.groovy", binding)
println binding.getVariable("output")
With this script.groovy
output = ${input}
results in this error:
Caught: groovy.lang.MissingMethodException: No signature of method: script.$() is applicable for argument types: (script$_run_closure1) values: [script$_run_closure1#fcf50]
Possible solutions: is(java.lang.Object), run(), run(), any(), any(groovy.lang.Closure), use([Ljava.lang.Object;)
Receiving the value in the script as a String like "${input}" works just fine.
I know it has to work somehow and I would appreciate any help or other suggestions!
output = ${input}
isn't valid groovy
Try:
output = input

Groovy Dynamic arguments

I wonder how is this possible in groovy to start an array from the n element.
Look at the snippet :
static void main(args){
if (args.length < 2){
println "Not enough parameters"
return;
}
def tools = new BoTools(args[0])
def action = args[1]
tools."$action"(*args)
System.exit(1)
}
As you see am doing here a dynamic method invocation. The first 2 arguments are taken as some config and method name , the others I would like to use as method paramerts.
So how can I do something like this :
tools."$action"(*(args+2))
Edited : If not possilbe in native groovy Java syntax will do it :
def newArgs = Arrays.copyOfRange(args,2,args.length);
tools."$action"(*newArgs)
To remove items from the beginning of the args you can use the drop() method. The original args list is not changed:
tools."$action"(*args.drop(2))
Other option, like you are trying is to access from N element:
tools."$action"(*args[2..-1])

Resources