【问题标题】:JRuby class and Java Scripting Engine unexpected resultsJRuby 类和 Java 脚本引擎的意外结果
【发布时间】:2012-05-21 21:33:35
【问题描述】:

我正在试验 Java 脚本引擎和 Ruby,但在 ruby​​ 脚本中设置一些实例变量时遇到了问题。这可能是我对 Ruby 缺乏了解,或者我对如何在脚本引擎中使用 ruby​​ 类缺乏了解。使用以下代码:

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"));
    }
}

我希望输出是“A,B 是一个测试”

相反,输出是 ', is a test'。

我应该如何在这段代码中设置变量 a、b?

编辑:为了清楚起见,理想情况下,我不想通过将变量附加到这个 StringBuilder 来设置变量——这只是为了说明。在实践中,我将从某个源加载脚本,然后想要在该 Ruby 对象上设置属性和调用方法。我确定我只是错过了其他人都知道的一些关键步骤:)。感谢 Gareth Davis 的回答,我发现我可以成功使用绑定和全局变量,但这不适用于所有脚本。非常感谢任何超越“hello world”类型用法的好文章的链接,因为我没有找到任何像样的文章。

第二次编辑:这是工作的最终代码,我知道必须缺少关键行:)-

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"));
    }
}

【问题讨论】:

    标签: java ruby jruby jsr223


    【解决方案1】:

    那样不行。设置 a & b 值的唯一方法是评估 o.a = 'A'o.b = 'B'

    第一个解决方案是修改脚本以填充值:

        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

    可以使用以下问题(感谢@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"));
    

    【讨论】:

    • 谢谢 - 我希望能够在 java 中对脚本进行参数化,而不是在执行之前对脚本进行处理。如果脚本存储在一个地方而参数存储在另一个地方,那么您的原始方法对我来说看起来更有用。在接受之前,我将尝试使用绑定方法,看看它能让我得到什么。
    • Gareth,我尝试了绑定方法,它适用于全局变量,但恐怕它不适用于 Ruby 类的实例变量。我尝试使用绑定将“o”绑定回上下文,但没有运气。还是谢谢。
    • OK,最终解决。缺少的关键行是: System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");这使得多个评估使用相同的状态。然后,您可以使用原始帖子中的方法: se.eval("o.a = \"A\"");作为一个单独的调用。
    • Gareth,既然你指出了我正确的方向,如果你想重新编辑你的答案并将系统属性粘贴进去,我会将你的答案标记为正确。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-27
    相关资源
    最近更新 更多