Skip to content

Commit

Permalink
Showing 2 changed files with 112 additions and 79 deletions.
118 changes: 64 additions & 54 deletions core/src/main/java/org/jruby/embed/ScriptingContainer.java
Original file line number Diff line number Diff line change
@@ -29,16 +29,14 @@
*/
package org.jruby.embed;

import java.io.UnsupportedEncodingException;
import org.jruby.embed.internal.LocalContextProvider;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jruby.CompatVersion;
import org.jruby.Profile;
import org.jruby.Ruby;
@@ -53,6 +51,7 @@
import org.jruby.embed.internal.EmbedRubyInterfaceAdapterImpl;
import org.jruby.embed.internal.EmbedRubyObjectAdapterImpl;
import org.jruby.embed.internal.EmbedRubyRuntimeAdapterImpl;
import org.jruby.embed.internal.LocalContextProvider;
import org.jruby.embed.internal.SingleThreadLocalContextProvider;
import org.jruby.embed.internal.SingletonLocalContextProvider;
import org.jruby.embed.internal.ThreadSafeLocalContextProvider;
@@ -80,61 +79,69 @@
* For example, a local context scope, local variable behavior, load paths are
* per-container properties. Please see {@link PropertyName} and {@link AttributeName}
* for more details. Be aware that the per-container properties should be set prior to
* get Ruby runtime be instantiated; otherwise, default values are applied to.
* get Ruby runtime being instantiated; otherwise, default values are applied to.
*
* ScriptingContainer delays Ruby runtime initialization as much as possible to
* improve startup time. When values are put into the ScriptingContainer, or runScriptlet
* method gets run Ruby runtime is created internally. However, the default, singleton
* local context scope behave slightly different. If Ruby runtime has been already instantiated
* by another ScriptingContainer, application, etc, the same runtime will be used.
* method gets run the runtime is created internally. However, the default, singleton
* local context scope behave slightly different. If a Ruby runtime has been already
* instantiated by another ScriptingContainer, application, etc, the same runtime
* will be re-used.
*
* Below are examples.
*
* The first Example is a very simple Hello World. After initializing a ScriptingContainer,
* a Ruby script, puts "Hello World!", runs and produces "Hello World!."
* <pre>Example 1:
*
* ScriptingContainer container = new ScriptingContainer();
* container.runScriptlet("puts \"Hello World!\"");
*
* <pre>
* Example 1:
* {@code
* ScriptingContainer container = new ScriptingContainer();
* container.runScriptlet("puts \"Hello World!\"");
* }
* Produces:
* Hello World!</pre>
* Hello World!
* </pre>
*
* The second example shows how to share variables between Java and Ruby.
* In this example, a local variable "x" is shared. To make this happen, a local variable
* behavior should be transient or persistent. As for JSR223 JRuby engine, set these
* types using System property, org.jruby.embed.localvariable.behavior. If the local
* variable behavior is one of transient or persistent,
* In this example, a local variable {@code "x"} is shared. To make this happen,
* a local variable behavior should be transient or persistent.
* As for JSR223 JRuby engine, set these types using a System property,
* <b>org.jruby.embed.localvariable.behavior</b>.
* If the local variable behavior is one of transient or persistent,
* Ruby's local, instance, global variables and constants are available to share
* between Java and Ruby. (A class variable sharing does not work on current version)
* Thus, "x" in Java is also "x" in Ruby.
*
* <pre>Example 2:
*
* ScriptingContainer container = new ScriptingContainer();
* container.put("x", 12345);
* container.runScriptlet("puts x.to_s(2)");
* Thus, {@code "x"} in Java is also {@code "x"} in Ruby.
*
* <pre>
* Example 2:
* {@code
* ScriptingContainer container = new ScriptingContainer();
* container.put("x", 12345);
* container.runScriptlet("puts x.to_s(2)");
* }
* Produces:
* 11000000111001</pre>
* 11000000111001
* </pre>
*
* The third examples shows how to keep local variables across multiple evaluations.
* This feature simulates BSF engine for JRuby. In terms of Ruby semantics,
* local variables should not survive after the evaluation has completed. Thus,
* this behavior is optional, and users need to specify LocalVariableBehavior.PERSISTENT
* when the container is instantiated.
*
* <pre>Example 3:
*
* ScriptingContainer container = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
* container.runScriptlet("p=9.0");
* container.runScriptlet("q = Math.sqrt p");
* container.runScriptlet("puts \"square root of #{p} is #{q}\"");
* System.out.println("Ruby used values: p = " + container.get("p") +
* ", q = " + container.get("q"));
*
* <pre>
* Example 3:
* {@code
* ScriptingContainer container = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
* container.runScriptlet("p=9.0");
* container.runScriptlet("q = Math.sqrt p");
* container.runScriptlet("puts \"square root of #{p} is #{q}\"");
* System.out.println("Ruby used values: p = " + container.get("p") + ", q = " + container.get("q"));
* }
* Produces:
* square root of 9.0 is 3.0
* Ruby used values: p = 9.0, q = 3.0</pre>
* Ruby used values: p = 9.0, q = 3.0
* </pre>
*
* Also, ScriptingContainer provides better i18n support. For example,
* Unicode Escape Sequence can be included in Ruby scripts.
@@ -143,28 +150,31 @@
* invoking methods defined by Ruby, and getting an instance of a specified interface
* that has been implemented by Ruby.
*
* <pre>Example 4:
* ScriptingContainer container = new ScriptingContainer();
* script =
* "def message\n" +
* "\"message: #{@message}\"\n" +
* "end\n" +
* "message";
* container.put("@message", "What's up?");
* EvalUnit unit = container.parse(script);
* IRubyObject ret = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(ret));
* container.put("@message", "Fabulous!");
* ret = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(ret));
* container.put("@message", "That's the way you are.");
* ret = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(ret));
*
* <pre>
* Example 4:
* {@code
* ScriptingContainer container = new ScriptingContainer();
* String script =
* "def message\n" +
* "\"message: #{@message}\"\n" +
* "end\n" +
* "message";
* container.put("@message", "What's up?");
* JavaEmbedUtils.EvalUnit unit = container.parse(script);
* IRubyObject msg = unit.run(); // a RubyString instance
* System.out.println(JavaEmbedUtils.rubyToJava(msg));
* container.put("@message", "Fabulous!");
* msg = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(msg));
* container.put("@message", "That's the way you are.");
* msg = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(msg));
* }
* Produces:
* message: What's up?
* message: Fabulous!
* message: That's the way you are.</pre>
* message: That's the way you are.
* </pre>
*
* See more details at project's
* {@see <a href="https://github.com/jruby/jruby/wiki/RedBridge">Wiki</a>}
73 changes: 48 additions & 25 deletions core/src/test/java/org/jruby/embed/ScriptingContainerTest.java
Original file line number Diff line number Diff line change
@@ -29,8 +29,6 @@
*/
package org.jruby.embed;

import org.jruby.embed.internal.ConcurrentLocalContextProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -63,6 +61,7 @@
import org.jruby.RubyInstanceConfig.LoadServiceCreator;
import org.jruby.ast.Node;
import org.jruby.embed.internal.BiVariableMap;
import org.jruby.embed.internal.ConcurrentLocalContextProvider;
import org.jruby.embed.internal.LocalContextProvider;
import org.jruby.embed.internal.SingleThreadLocalContextProvider;
import org.jruby.embed.internal.SingletonLocalContextProvider;
@@ -205,7 +204,7 @@ public void testGetProvider() {
result = instance.getProvider();
assertTrue(result instanceof SingletonLocalContextProvider);
instance = null;

instance = new ScriptingContainer(LocalContextScope.CONCURRENT);
instance.setError(pstream);
instance.setOutput(pstream);
@@ -227,7 +226,7 @@ public void testGetRuntime() {
instance.setOutput(pstream);
instance.setWriter(writer);
instance.setErrorWriter(writer);
Ruby runtime = JavaEmbedUtils.initialize(new ArrayList());
Ruby runtime = JavaEmbedUtils.initialize(new ArrayList());
Ruby result = instance.getProvider().getRuntime();
Class expClazz = runtime.getClass();
Class resultClazz = result.getClass();
@@ -267,7 +266,7 @@ public void testGetVarMap() {
assertEquals(1.2345, instance.getVarMap().get("parameter"));
result.put("@coefficient", 4);
assertEquals(4, instance.getVarMap().get("@coefficient"));

result.clear();
instance = null;
}
@@ -281,15 +280,15 @@ public void testGetAttributeMap() {
ScriptingContainer instance = new ScriptingContainer(LocalContextScope.THREADSAFE);
instance.setError(pstream);
instance.setOutput(pstream);

Map result = instance.getAttributeMap();
Object obj = result.get(AttributeName.READER);
assertEquals(obj.getClass(), java.io.InputStreamReader.class);
obj = result.get(AttributeName.WRITER);
assertEquals(obj.getClass(), java.io.PrintWriter.class);
obj = result.get(AttributeName.ERROR_WRITER);
assertEquals(obj.getClass(), java.io.PrintWriter.class);

result.put(AttributeName.BASE_DIR, "/usr/local/lib");
assertEquals("/usr/local/lib", result.get(AttributeName.BASE_DIR));

@@ -369,7 +368,7 @@ public void testGet() {
Object expResult = null;
Object result = instance.get(key);
assertEquals(expResult, result);

instance.put("@name", "camellia");
assertEquals("camellia", instance.get("@name"));
instance.put("COLOR", "red");
@@ -398,7 +397,7 @@ public void testGet() {
instance.setErrorWriter(writer);
instance.runScriptlet("ivalue = 200000");
assertEquals(200000L, instance.get("ivalue"));

instance.getVarMap().clear();
instance = null;
}
@@ -582,7 +581,7 @@ public void testParse_3args_2() {
instance.setWriter(writer);
instance.setErrorWriter(writer);
EmbedEvalUnit result;

try {
result = instance.parse(type, filename, lines);
} catch (Throwable t) {
@@ -862,7 +861,7 @@ public void testNewRuntimeAdapter() {
instance.setWriter(writer);
instance.setErrorWriter(writer);
EmbedRubyRuntimeAdapter result = instance.newRuntimeAdapter();
String script =
String script =
"def volume\n"+
" (Math::PI * (@r ** 2.0) * @h)/3.0\n" +
"end\n" +
@@ -1053,12 +1052,12 @@ public void testCallMethod_4args_3() {
instance.setOutput(pstream);
instance.setWriter(writer);
instance.setErrorWriter(writer);

// Verify that empty message name returns null
Object result = instance.callMethod(null, "", returnType, unit);
assertEquals(null, result);

String text =
String text =
"songs:\n"+
"- Hey Soul Sister\n" +
"- Who Says\n" +
@@ -1554,7 +1553,7 @@ public void testGetLoadPaths() {
List result = instance.getLoadPaths();
assertTrue(result != null);
assertTrue(result.size() > 0);

instance = null;
}

@@ -1941,7 +1940,7 @@ public void testSetEnvironment() {

instance.setEnvironment(environment);
assertEquals(environment, instance.getEnvironment());

instance = null;
}

@@ -1986,15 +1985,15 @@ public void testSetCurrentDirectory() {
instance = new ScriptingContainer();
instance.setCurrentDirectory(directory);
assertEquals(directory, instance.getCurrentDirectory());

instance = new ScriptingContainer(LocalContextScope.CONCURRENT);
instance.setError(pstream);
instance.setOutput(pstream);
instance.setWriter(writer);
instance.setErrorWriter(writer);
instance.setCurrentDirectory(directory);
assertEquals(directory, instance.getCurrentDirectory());

instance = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
instance.setError(pstream);
instance.setOutput(pstream);
@@ -2162,7 +2161,7 @@ public void testSetProfile() {
profile = Profile.ALL;
instance.setProfile(profile);
assertEquals(profile, instance.getProfile());

instance = null;
}

@@ -2244,7 +2243,7 @@ public void testSetArgv() {
//instance.setErrorWriter(writer);
argv = new String[] {"tree", "woods", "forest"};
instance.setArgv(argv);
String script =
String script =
"def print_argv\n" +
"all_of_them = \"\"\n" +
"ARGV.each { |item| all_of_them += item }\n" +
@@ -2636,7 +2635,7 @@ public void testSharingVariableWithCompileMode() {
instance.setError(pstream);
instance.setOutput(pstream);
instance.setErrorWriter(writer);

instance.put("my_var", "Hullo!");

StringWriter sw = new StringWriter();
@@ -2646,7 +2645,7 @@ public void testSharingVariableWithCompileMode() {
assertEquals("Hullo!", sw.toString().trim());

// need to put the lvar again since lvars vanish after eval on a transient setting
instance.put("my_var", "Hullo!");
instance.put("my_var", "Hullo!");
sw = new StringWriter();
instance.setWriter(sw);
instance.setCompileMode(CompileMode.JIT);
@@ -2662,7 +2661,7 @@ public void testSharingVariableWithCompileMode() {

instance = null;
}

public void testEmbedEvalUnitCompileModes() {
org.jruby.embed.ScriptingContainer container = new org.jruby.embed.ScriptingContainer();
container.setCompileMode(CompileMode.OFF);
@@ -2711,7 +2710,7 @@ public void testNullToContextClassLoader() {
instance = null;
}
}

/**
* Test of setClassLoader method, of SystemPropertyCatcher.
* This method is only used in JSR223 but tested here. Since, JSR223
@@ -2724,13 +2723,13 @@ public void testSystemPropertyCatcherSetClassloader() {
ScriptingContainer instance = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
org.jruby.embed.util.SystemPropertyCatcher.setClassLoader(instance);
assertEquals(instance.getClass().getClassLoader(), instance.getClassLoader());

System.setProperty(PropertyName.CLASSLOADER.toString(), "context");
instance = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
org.jruby.embed.util.SystemPropertyCatcher.setClassLoader(instance);
assertEquals(Thread.currentThread().getContextClassLoader(), instance.getClassLoader());
}

/**
* Test of setClassLoader method, of SystemPropertyCatcher.
* This method is only used in JSR223 but tested here. Since, JSR223
@@ -2770,4 +2769,28 @@ public void testContainerScrubsStdioDescriptors() {

assertEquals(orig, ChannelDescriptor.getFilenoDescriptorMapReadOnly());
}

@Test
public void testJavadocExample4() {
ScriptingContainer container = new ScriptingContainer();
String script =
"def message\n" +
"\"message: #{@message}\"\n" +
"end\n" +
"message";
container.put("@message", "What's up?");
JavaEmbedUtils.EvalUnit unit = container.parse(script);
IRubyObject msg = unit.run(); // a RubyString instance
assertEquals("message: What's up?", JavaEmbedUtils.rubyToJava(msg));
//System.out.println(JavaEmbedUtils.rubyToJava(msg));
container.put("@message", "Fabulous!");
msg = unit.run();
assertEquals("message: Fabulous!", JavaEmbedUtils.rubyToJava(msg));
//System.out.println(JavaEmbedUtils.rubyToJava(msg));
container.put("@message", "That's the way you are.");
msg = unit.run();
assertEquals("message: That's the way you are.", JavaEmbedUtils.rubyToJava(msg));
//System.out.println(JavaEmbedUtils.rubyToJava(msg));
}

}

0 comments on commit 67b96a0

Please sign in to comment.