Reputation: 1566
I'm experimenting with the Java scripting engine and Ruby, and I'm having trouble setting some instance variables in a ruby script. This could be my lack of understanding of Ruby or my lack of understanding of how to use ruby classes in the scripting engine. With the following code:
public class App {
public static void main( String[] args ) throws Exception{
ScriptEngineManager sm = new ScriptEngineManager();
ScriptEngine se = sm.getEngineByName("jruby");
StringBuilder sb = new StringBuilder();
sb.append("class Test\n");
sb.append(" attr_accessor :a, :b\n");
sb.append(" def str\n");
sb.append(" \"#{a}, #{b} is a test.\"\n");
sb.append(" end\n");
sb.append("end\n");
sb.append("o = Test.new\n");
Object o = se.eval(sb.toString());
se.put("@a", "A");
se.put("@b", "B");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));
}
}
I'd expect the output to be 'A, B is a test'
Instead, the output is ', is a test'.
How should I be setting variables a, b in this code?
Edit: Just to be clear, ideally I don't want to be setting the variables by appending them to this StringBuilder - this is just for illustration. In practice, I'll be loading scripts from some source, and then want to set properties and call methods on that Ruby object afterwards. I'm sure I'm just missing some crucial step that everyone else knows about :). Thanks to Gareth Davis' answer I've found I can use bindings and global variables successfully, but that isn't going to work with all scripts. Would really appreciate any links to good articles that go beyond 'hello world' type usage, as I've not found any decent ones.
Second edit: This is the working, final code, with the crucial line that I knew must be missing :)-
public class App {
public static void main( String[] args ) throws Exception{
//Must set this property if you want to call eval multiple times!
System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
ScriptEngineManager sm = new ScriptEngineManager();
ScriptEngine se = sm.getEngineByName("jruby");
StringBuilder sb = new StringBuilder();
sb.append("class Test\n");
sb.append(" attr_accessor :a, :b\n");
sb.append(" def str\n");
sb.append(" \"#{a}, #{b} is a test.\"\n");
sb.append(" end\n");
sb.append("end\n");
sb.append("o = Test.new\n");
Object o = se.eval(sb.toString());
se.eval("o.a = \"A\"");
se.eval("o.b = \"B\"");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));
}
}
Upvotes: 1
Views: 338
Reputation: 28059
That won't work like that. The only way to set the values of a & b is to evaluate o.a = 'A'
and o.b = 'B'
.
The first solution is to amend the script to populate the values thus:
sb.append("o.a = 'A'\n");
sb.append("o.b = 'B'\n");
sb.append("o");
Object o = se.eval(sb.toString());
I've created a working example on github.com
keeping with the question the following can be used (credit to @Mick Sear):
System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
// .. snip
Object o = se.eval(sb.toString());
se.eval("o.a = 'A'");
se.eval("o.b = 'B'");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));
Upvotes: 1