2013-06-16 08:30:03 -04:00
|
|
|
# therubyrhino
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
Embed the Mozilla Rhino JavaScript interpreter into Ruby
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
## REQUIREMENTS:
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
* JRuby >= 1.6.8
|
|
|
|
|
|
|
|
## INSTALL:
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
`jruby -S gem install therubyrhino`
|
|
|
|
|
|
|
|
## FEATURES/PROBLEMS:
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2012-04-13 02:26:54 -04:00
|
|
|
* Evaluate JavaScript from with in Ruby
|
|
|
|
* Embed your Ruby objects into the JavaScript world
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
## SYNOPSIS:
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2012-04-13 02:26:54 -04:00
|
|
|
1. JavaScript goes into Ruby
|
|
|
|
2. Ruby Objects goes into JavaScript
|
|
|
|
3. Our shark's in the JavaScript!
|
2009-09-24 21:37:06 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
require 'rhino'
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
|
|
|
|
|
|
|
* evaluate some simple JavaScript using `eval_js`:
|
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
eval_js "7 * 6" #=> 42
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-11-20 10:38:31 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
* that's quick and dirty, but if you want more control over your environment,
|
|
|
|
use a `Context`:
|
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open do |context|
|
|
|
|
context['foo'] = "bar"
|
|
|
|
context.eval('foo') # => "bar"
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-11-20 10:58:15 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
* evaluate a Ruby function from JavaScript:
|
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open do |context|
|
|
|
|
context["say"] = lambda {|word, times| word * times}
|
|
|
|
context.eval("say("Hello", 3)") #=> HelloHelloHello
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-11-13 12:33:08 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
* embed a Ruby object into your JavaScript environment
|
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
class MyMath
|
|
|
|
def plus(a, b)
|
|
|
|
a + b
|
2009-11-13 12:33:08 -05:00
|
|
|
end
|
2013-06-16 10:16:09 -04:00
|
|
|
end
|
2009-11-13 12:33:08 -05:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open do |context|
|
|
|
|
context["math"] = MyMath.new
|
|
|
|
context.eval("math.plus(20, 22)") #=> 42
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
|
|
|
|
2014-02-20 14:38:45 -05:00
|
|
|
* make a Ruby object be your JavaScript environment
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
math = MyMath.new
|
|
|
|
Rhino::Context.open(:with => math) do |context|
|
|
|
|
context.eval("plus(20, 22)") #=> 42
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
# or the equivalent
|
2013-06-16 08:30:03 -04:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
math.eval_js("plus(20, 22)")
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-10-06 10:18:13 -04:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
### Context Configuration
|
2009-10-06 10:18:13 -04:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
* make your standard objects (Object, String, etc...) immutable:
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open(:sealed => true) do |context|
|
|
|
|
context.eval("Object.prototype.toString = function() {}") # this is an error!
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
* turn on Java integration from JavaScript (probably a bad idea):
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open(:java => true) do |context|
|
|
|
|
context.eval("java.lang.System.exit()") # it's dangerous!
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-11-20 10:58:15 -05:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
* limit the number of instructions that can be executed to prevent rogue code:
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open(:restrictable => true) do |context|
|
|
|
|
context.instruction_limit = 100000
|
|
|
|
context.eval("while (true);") # => Rhino::RunawayScriptError
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2012-02-15 05:43:48 -05:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
* limit the time a script executes (rogue scripts):
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open(:restrictable => true, :java => true) do |context|
|
|
|
|
context.timeout_limit = 1.5 # seconds
|
|
|
|
context.eval %Q{
|
|
|
|
for (var i = 0; i < 100; i++) {
|
|
|
|
java.lang.Thread.sleep(100);
|
|
|
|
}
|
|
|
|
} # => Rhino::ScriptTimeoutError
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-11-20 12:48:55 -05:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
### Loading JavaScript Source
|
2009-11-20 10:58:15 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
In addition to just evaluating strings, you can also use streams such as files:
|
2009-11-20 10:58:15 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
* evaluate bytes read from any File/IO object:
|
|
|
|
```ruby
|
2009-11-20 10:58:15 -05:00
|
|
|
File.open("mysource.js") do |file|
|
|
|
|
eval_js file, "mysource.js"
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-11-20 10:58:15 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
* or load it by filename:
|
|
|
|
```ruby
|
2009-11-20 10:58:15 -05:00
|
|
|
Rhino::Context.open do |context|
|
|
|
|
context.load("mysource.js")
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-11-20 10:58:15 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
### Configurable Ruby access
|
2012-02-15 05:43:48 -05:00
|
|
|
|
2012-04-13 02:26:54 -04:00
|
|
|
By default accessing Ruby objects from JavaScript is compatible with *therubyracer*:
|
2012-02-15 05:43:48 -05:00
|
|
|
https://github.com/cowboyd/therubyracer/wiki/Accessing-Ruby-Objects-From-JavaScript
|
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
Thus you end-up calling arbitrary no-arg methods as if they were JavaScript properties,
|
2012-02-15 05:43:48 -05:00
|
|
|
since instance accessors (properties) and methods (functions) are indistinguishable:
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open do |context|
|
|
|
|
context['Time'] = Time
|
|
|
|
context.eval('Time.now')
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2012-02-15 05:43:48 -05:00
|
|
|
|
|
|
|
However, you can customize this behavior and there's another access implementation
|
|
|
|
that attempts to mirror only attributes as properties as close as possible:
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
class Foo
|
|
|
|
attr_accessor :bar
|
2013-06-16 08:30:03 -04:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
def initialize
|
|
|
|
@bar = "bar"
|
2012-02-15 05:43:48 -05:00
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
def check_bar
|
|
|
|
bar == "bar"
|
2012-02-15 05:43:48 -05:00
|
|
|
end
|
2013-06-16 10:16:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
Rhino::Ruby::Scriptable.access = :attribute
|
|
|
|
Rhino::Context.open do |context|
|
|
|
|
context['Foo'] = Foo
|
|
|
|
context.eval('var foo = new Foo()')
|
|
|
|
context.eval('foo.bar') # get property using reader
|
|
|
|
context.eval('foo.bar = null') # set property using writer
|
|
|
|
context.eval('foo.check_bar()') # called like a function
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2012-02-15 05:43:48 -05:00
|
|
|
|
2012-04-13 02:26:54 -04:00
|
|
|
If you happen to come up with your own access strategy, just set it directly :
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Ruby::Scriptable.access = FooApp::BarAccess.instance
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2012-04-13 02:26:54 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
## Safe by default
|
2009-11-20 12:48:55 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
The Ruby Rhino is designed to let you evaluate JavaScript as safely as possible
|
|
|
|
unless you tell it to do something more dangerous. The default context is a
|
|
|
|
hermetically sealed JavaScript environment with only the standard objects and
|
|
|
|
functions. Nothing from the Ruby world is accessible.
|
2009-11-20 12:48:55 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
For Ruby objects that you explicitly embed into JavaScript, only the +public+
|
2012-04-13 02:26:54 -04:00
|
|
|
methods "defined in their classes" are exposed by default e.g.
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
class A
|
|
|
|
def a; 'a'; end
|
|
|
|
end
|
2009-11-20 12:48:55 -05:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
class B < A
|
|
|
|
def b; 'b'; end
|
|
|
|
end
|
2009-11-20 12:48:55 -05:00
|
|
|
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.open do |context|
|
|
|
|
context['a'] = A.new
|
|
|
|
context['b'] = B.new
|
|
|
|
context.eval("a.a()") # => 'a'
|
|
|
|
context.eval("b.b()") # => 'b'
|
|
|
|
context.eval("b.a()") # => 'TypeError: undefined property 'a' is not a function'
|
|
|
|
end
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2012-08-27 04:55:15 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
## Context Customizations
|
2012-12-05 04:46:41 -05:00
|
|
|
|
|
|
|
Just like the JVM packaged Rhino scripting engine, therubyrhino gem supports
|
|
|
|
specifying JavaScript context properies (optimization level and language version)
|
|
|
|
using system properties e.g. to force interpreted mode :
|
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
`jruby -J-Drhino.opt.level=-1 -rtherubyrhino -S ...`
|
2012-12-05 04:46:41 -05:00
|
|
|
|
|
|
|
You might also set these programatically as a default for all created contexts :
|
2013-06-16 08:30:03 -04:00
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
Rhino::Context.default_optimization_level = 1
|
|
|
|
Rhino::Context.default_javascript_version = 1.6
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2012-12-05 04:46:41 -05:00
|
|
|
|
|
|
|
Or using plain old JAVA_OPTS e.g. when setting JavaScript version :
|
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
`-Drhino.js.version=1.7`
|
2012-12-05 04:46:41 -05:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
## Rhino
|
2012-04-13 02:26:54 -04:00
|
|
|
|
|
|
|
Rhino is currently maintained at https://github.com/mozilla/rhino
|
|
|
|
Release downloads are available at http://www.mozilla.org/rhino/download.html
|
|
|
|
Rhino is licensed under the MPL 1.1/GPL 2.0 license.
|
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
### Using a custom Rhino version
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
Officially supported versions of Rhino's _js.jar_ are packaged separately as
|
|
|
|
**therubyrhino_jar** gem. Make sure you're using the latest gem version if you
|
|
|
|
feel like missing something available with Rhino. For experimenters the jar can
|
|
|
|
be overriden by defining a `Rhino::JAR_PATH` before `require 'rhino'` e.g. :
|
|
|
|
```ruby
|
2013-06-16 10:16:09 -04:00
|
|
|
module Rhino
|
|
|
|
JAR_PATH = File.expand_path('lib/rhino/build/rhino1_7R5pre/js.jar')
|
|
|
|
end
|
|
|
|
# ...
|
|
|
|
require 'rhino'
|
2013-06-16 08:30:03 -04:00
|
|
|
```
|
2009-09-24 20:06:31 -04:00
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
## LICENSE:
|
2009-09-24 20:06:31 -04:00
|
|
|
|
|
|
|
(The MIT License)
|
|
|
|
|
2013-06-16 08:30:03 -04:00
|
|
|
Copyright (c) 2009-2013 Charles Lowell
|
2009-09-24 20:06:31 -04:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
a copy of this software and associated documentation files (the
|
|
|
|
'Software'), to deal in the Software without restriction, including
|
|
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
2013-06-16 08:30:03 -04:00
|
|
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|