therubyrhino 1.72.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.gitmodules +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +16 -0
- data/History.txt +43 -0
- data/README.rdoc +157 -0
- data/Rakefile +11 -0
- data/lib/rhino/context.rb +208 -0
- data/lib/rhino/java.rb +24 -0
- data/lib/rhino/native_function.rb +23 -0
- data/lib/rhino/native_object.rb +71 -0
- data/lib/rhino/object.rb +8 -0
- data/lib/rhino/rhino-1.7R2.jar +0 -0
- data/lib/rhino/ruby_function.rb +14 -0
- data/lib/rhino/ruby_object.rb +65 -0
- data/lib/rhino/version.rb +4 -0
- data/lib/rhino/wormhole.rb +36 -0
- data/lib/rhino.rb +15 -0
- data/spec/redjs_helper.rb +5 -0
- data/spec/rhino/context_spec.rb +46 -0
- data/spec/rhino/wormhole_spec.rb +135 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/jruby.rake +7 -0
- data/tasks/rspec.rake +13 -0
- data/therubyrhino.gemspec +19 -0
- metadata +98 -0
data/.gitmodules
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/History.txt
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
=== 1.72.7
|
2
|
+
* N major enhancements:
|
3
|
+
* N minor enhancements:
|
4
|
+
|
5
|
+
=== 1.72.6 2009-11-30
|
6
|
+
* 2 major enhancements:
|
7
|
+
* evaluate an IO object such as a file or an socket input stream
|
8
|
+
* load javascript sources directly into the context with the file system
|
9
|
+
|
10
|
+
=== 1.72.5 2009-11-12
|
11
|
+
* 2 major enhancements:
|
12
|
+
* evaluate javascript with a ruby object as it's scope using Context#open(:with => object)
|
13
|
+
* add eval_js() method to Object to evaluate in the context of that object
|
14
|
+
|
15
|
+
=== 1.72.4 2009-11-12
|
16
|
+
* 3 major enhancements:
|
17
|
+
* automatically wrap/unwrap ruby and javascript arrays
|
18
|
+
* automatically convert ruby method objects and Proc objects into javascript functions
|
19
|
+
* Make functions defined in javascript callable from ruby
|
20
|
+
|
21
|
+
=== 1.72.3 2009-11-11
|
22
|
+
* 4 major enhancements:
|
23
|
+
* greatly simplified interface to context by unifying context and scope
|
24
|
+
* remove Context#open_std()
|
25
|
+
* remove Context#standard
|
26
|
+
* remove Context#init_standard_objects
|
27
|
+
|
28
|
+
=== 1.72.2 2009-11-10
|
29
|
+
* 1 major enhancement:
|
30
|
+
* ability to limit the instruction count for a context
|
31
|
+
|
32
|
+
=== 1.72.1 2009-11-09
|
33
|
+
* 4 major enhancements:
|
34
|
+
* easily manipulate javascript objects from ruby (NativeObject)
|
35
|
+
* make NativeObject Enumerable
|
36
|
+
* to_h and to_json for NativeObject
|
37
|
+
* embed ruby instances and call methods from javascript
|
38
|
+
|
39
|
+
=== 1.72.0 2009-09-24
|
40
|
+
|
41
|
+
* 2 major enhancements:
|
42
|
+
* evaluate javascript in jruby
|
43
|
+
* embed callable ruby objects in javascript
|
data/README.rdoc
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
= therubyrhino
|
2
|
+
|
3
|
+
* http://github.com/cowboyd/therubyrhino
|
4
|
+
* irc://irc.freenode.net/therubyrhino
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
Embed the Mozilla Rhino Javascript interpreter into Ruby
|
9
|
+
|
10
|
+
== FEATURES/PROBLEMS:
|
11
|
+
|
12
|
+
* Evaluate Javascript from with in Ruby
|
13
|
+
* Embed your Ruby objects into the Javascript world
|
14
|
+
|
15
|
+
== SYNOPSIS:
|
16
|
+
|
17
|
+
1. Javascript goes into Ruby
|
18
|
+
1. Ruby Objects goes into Javascript
|
19
|
+
1. Our shark's in the Javascript!
|
20
|
+
|
21
|
+
require 'rhino'
|
22
|
+
|
23
|
+
# evaluate some simple javascript
|
24
|
+
eval_js "7 * 6" #=> 42
|
25
|
+
|
26
|
+
# that's quick and dirty, but if you want more control over your
|
27
|
+
# environment, use a Context:
|
28
|
+
Rhino::Context.open do |cxt|
|
29
|
+
cxt['foo'] = "bar"
|
30
|
+
cxt.eval('foo') # => "bar"
|
31
|
+
end
|
32
|
+
|
33
|
+
# evaluate a ruby function from javascript
|
34
|
+
|
35
|
+
Rhino::Context.open do |context|
|
36
|
+
context["say"] = lambda {|word, times| word * times}
|
37
|
+
context.eval("say("Hello", 3)") #=> HelloHelloHello
|
38
|
+
end
|
39
|
+
|
40
|
+
# embed a ruby object into your javascript environment
|
41
|
+
|
42
|
+
class MyMath
|
43
|
+
def plus(lhs, rhs)
|
44
|
+
lhs + rhs
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Rhino::Context.open do |context|
|
49
|
+
context["math"] = MyMath.new
|
50
|
+
context.eval("math.plus(20,22)") #=> 42
|
51
|
+
end
|
52
|
+
|
53
|
+
# make a ruby object *be* your javascript environment
|
54
|
+
math = MyMath.new
|
55
|
+
Rhino::Context.open(:with => math) do |context|
|
56
|
+
context.eval("plus(20,22)") #=> 42
|
57
|
+
end
|
58
|
+
|
59
|
+
#or the equivalent
|
60
|
+
|
61
|
+
math.eval_js("plus(20,22)")
|
62
|
+
|
63
|
+
# Configure your embedding setup
|
64
|
+
|
65
|
+
# Make your standard objects (Object, String, etc...) immutable
|
66
|
+
Rhino::Context.open(:sealed => true) do |context|
|
67
|
+
context.eval("Object.prototype.toString = function() {}") # this is an error!
|
68
|
+
end
|
69
|
+
|
70
|
+
#Turn on Java integration from javascript (probably a bad idea)
|
71
|
+
Rhino::Context.open(:java => true) do |context|
|
72
|
+
context.eval("java.lang.System.exit()") #it's dangerous!
|
73
|
+
end
|
74
|
+
|
75
|
+
#limit the number of instructions that can be executed in order to prevent
|
76
|
+
#rogue scripts
|
77
|
+
Rhino::Context.open do |context|
|
78
|
+
context.instruction_limit = 100000
|
79
|
+
context.eval("while (true);") # => Error!
|
80
|
+
end
|
81
|
+
|
82
|
+
==== Different ways of loading javascript source
|
83
|
+
|
84
|
+
In addition to just evaluating strings, you can also use streams such as files.
|
85
|
+
|
86
|
+
# evaluate bytes read from any File/IO object:
|
87
|
+
File.open("mysource.js") do |file|
|
88
|
+
eval_js file, "mysource.js"
|
89
|
+
end
|
90
|
+
|
91
|
+
# or load it by filename
|
92
|
+
Rhino::Context.open do |context|
|
93
|
+
context.load("mysource.js")
|
94
|
+
end
|
95
|
+
|
96
|
+
=== Safe by default
|
97
|
+
|
98
|
+
The Ruby Rhino is designed to let you evaluate javascript as safely as possible unless you tell it to do something more
|
99
|
+
dangerous. The default context is a hermetically sealed javascript environment with only the standard javascript objects
|
100
|
+
and functions. Nothing from the ruby world is accessible at all.
|
101
|
+
|
102
|
+
For ruby objects that you explicitly embed into javascript, only the +public+ methods *defined in their classes* are
|
103
|
+
exposed by default. E.g.
|
104
|
+
|
105
|
+
class A
|
106
|
+
def a
|
107
|
+
"a"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class B < A
|
112
|
+
def b
|
113
|
+
"b"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
Rhino::Context.open do |cxt|
|
119
|
+
cxt['a'] = A.new
|
120
|
+
cxt['b'] = B.new
|
121
|
+
cxt.eval("a.a()") # => 'a'
|
122
|
+
cxt.eval("b.b()") # => 'b'
|
123
|
+
cxt.eval("b.a()") # => 'TypeError: undefined property 'a' is not a function'
|
124
|
+
end
|
125
|
+
|
126
|
+
== REQUIREMENTS:
|
127
|
+
|
128
|
+
* JRuby >= 1.3.0
|
129
|
+
|
130
|
+
== INSTALL:
|
131
|
+
|
132
|
+
* jgem install therubyrhino
|
133
|
+
|
134
|
+
== LICENSE:
|
135
|
+
|
136
|
+
(The MIT License)
|
137
|
+
|
138
|
+
Copyright (c) 2009 Charles Lowell
|
139
|
+
|
140
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
141
|
+
a copy of this software and associated documentation files (the
|
142
|
+
'Software'), to deal in the Software without restriction, including
|
143
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
144
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
145
|
+
permit persons to whom the Software is furnished to do so, subject to
|
146
|
+
the following conditions:
|
147
|
+
|
148
|
+
The above copyright notice and this permission notice shall be
|
149
|
+
included in all copies or substantial portions of the Software.
|
150
|
+
|
151
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
152
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
153
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
154
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
155
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
156
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
157
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
|
5
|
+
# ==Overview
|
6
|
+
# All Javascript must be executed in a context which represents the execution environment in
|
7
|
+
# which scripts will run. The environment consists of the standard javascript objects
|
8
|
+
# and functions like Object, String, Array, etc... as well as any objects or functions which
|
9
|
+
# have been defined in it. e.g.
|
10
|
+
#
|
11
|
+
# Context.open do |cxt|
|
12
|
+
# cxt['num'] = 5
|
13
|
+
# cxt.eval('num + 5') #=> 10
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# == Multiple Contexts.
|
17
|
+
# The same object may appear in any number of contexts, but only one context may be executing javascript code
|
18
|
+
# in any given thread. If a new context is opened in a thread in which a context is already opened, the second
|
19
|
+
# context will "mask" the old context e.g.
|
20
|
+
#
|
21
|
+
# six = 6
|
22
|
+
# Context.open do |cxt|
|
23
|
+
# cxt['num'] = 5
|
24
|
+
# cxt.eval('num') # => 5
|
25
|
+
# Context.open do |cxt|
|
26
|
+
# cxt['num'] = 10
|
27
|
+
# cxt.eval('num') # => 10
|
28
|
+
# cxt.eval('++num') # => 11
|
29
|
+
# end
|
30
|
+
# cxt.eval('num') # => 5
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# == Notes
|
34
|
+
# While there are many similarities between Rhino::Context and Java::OrgMozillaJavascript::Context, they are not
|
35
|
+
# the same thing and should not be confused.
|
36
|
+
|
37
|
+
class Context
|
38
|
+
attr_reader :scope
|
39
|
+
|
40
|
+
class << self
|
41
|
+
|
42
|
+
# initalize a new context with a fresh set of standard objects. All operations on the context
|
43
|
+
# should be performed in the block that is passed.
|
44
|
+
def open(options = {}, &block)
|
45
|
+
new(options).open(&block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def eval(javascript)
|
49
|
+
new.eval(javascript)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# Create a new javascript environment for executing javascript and ruby code.
|
55
|
+
# * <tt>:sealed</tt> - if this is true, then the standard objects such as Object, Function, Array will not be able to be modified
|
56
|
+
# * <tt>:with</tt> - use this ruby object as the root scope for all javascript that is evaluated
|
57
|
+
# * <tt>:java</tt> - if true, java packages will be accessible from within javascript
|
58
|
+
def initialize(options = {}) #:nodoc:
|
59
|
+
ContextFactory.new.call do |native|
|
60
|
+
@native = native
|
61
|
+
@global = NativeObject.new(@native.initStandardObjects(nil, options[:sealed] == true))
|
62
|
+
if with = options[:with]
|
63
|
+
@scope = To.javascript(with)
|
64
|
+
@scope.setParentScope(@global.j)
|
65
|
+
else
|
66
|
+
@scope = @global
|
67
|
+
end
|
68
|
+
unless options[:java]
|
69
|
+
for package in ["Packages", "java", "javax", "org", "com", "edu", "net"]
|
70
|
+
@global.j.delete(package)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Read a value from the global scope of this context
|
77
|
+
def [](k)
|
78
|
+
@scope[k]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Set a value in the global scope of this context. This value will be visible to all the
|
82
|
+
# javascript that is executed in this context.
|
83
|
+
def []=(k, v)
|
84
|
+
@scope[k] = v
|
85
|
+
end
|
86
|
+
|
87
|
+
# Evaluate a string of javascript in this context:
|
88
|
+
# * <tt>source</tt> - the javascript source code to evaluate. This can be either a string or an IO object.
|
89
|
+
# * <tt>source_name</tt> - associated name for this source code. Mainly useful for backtraces.
|
90
|
+
# * <tt>line_number</tt> - associate this number with the first line of executing source. Mainly useful for backtraces
|
91
|
+
def eval(source, source_name = "<eval>", line_number = 1)
|
92
|
+
self.open do
|
93
|
+
begin
|
94
|
+
scope = To.javascript(@scope)
|
95
|
+
if IO === source || StringIO === source
|
96
|
+
result = @native.evaluateReader(scope, IOReader.new(source), source_name, line_number, nil)
|
97
|
+
else
|
98
|
+
result = @native.evaluateString(scope, source.to_s, source_name, line_number, nil)
|
99
|
+
end
|
100
|
+
To.ruby result
|
101
|
+
rescue J::RhinoException => e
|
102
|
+
raise Rhino::JavascriptError, e
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def evaluate(*args) # :nodoc:
|
108
|
+
self.eval(*args)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Read the contents of <tt>filename</tt> and evaluate it as javascript. Returns the result of evaluating the
|
112
|
+
# javascript. e.g.
|
113
|
+
#
|
114
|
+
# Context.open do |cxt|
|
115
|
+
# cxt.load("path/to/some/lib.js")
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
def load(filename)
|
119
|
+
File.open(filename) do |file|
|
120
|
+
evaluate file, filename, 1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Set the maximum number of instructions that this context will execute.
|
125
|
+
# If this instruction limit is exceeded, then a Rhino::RunawayScriptError
|
126
|
+
# will be raised
|
127
|
+
def instruction_limit=(limit)
|
128
|
+
@native.setInstructionObserverThreshold(limit);
|
129
|
+
@native.factory.instruction_limit = limit
|
130
|
+
end
|
131
|
+
|
132
|
+
# Set the optimization level that this context will use. This is sometimes necessary
|
133
|
+
# in Rhino, if the bytecode size of the compiled javascript exceeds the 64KB limit.
|
134
|
+
# By using the -1 optimization level, you tell Rhino to run in interpretative mode,
|
135
|
+
# taking a hit to performance but escaping the Java bytecode limit.
|
136
|
+
def optimization_level=(level)
|
137
|
+
@native.setOptimizationLevel(level)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Enter this context for operations. Some methods such as eval() will
|
141
|
+
# fail unless this context is open
|
142
|
+
def open
|
143
|
+
begin
|
144
|
+
@native.factory.enterContext(@native)
|
145
|
+
yield self
|
146
|
+
ensure
|
147
|
+
J::Context.exit()
|
148
|
+
end if block_given?
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
class IOReader < Java::JavaIo::Reader #:nodoc:
|
154
|
+
|
155
|
+
def initialize(io)
|
156
|
+
@io = io
|
157
|
+
end
|
158
|
+
|
159
|
+
def read(charbuffer, offset, length)
|
160
|
+
begin
|
161
|
+
str = @io.read(length)
|
162
|
+
if str.nil?
|
163
|
+
return -1
|
164
|
+
else
|
165
|
+
jstring = Java::JavaLang::String.new(str)
|
166
|
+
for i in 0 .. jstring.length - 1
|
167
|
+
charbuffer[i + offset] = jstring.charAt(i)
|
168
|
+
end
|
169
|
+
return jstring.length
|
170
|
+
end
|
171
|
+
rescue StandardError => e
|
172
|
+
raise Java::JavaIo::IOException.new, "Failed reading from ruby IO object"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class ContextFactory < J::ContextFactory # :nodoc:
|
178
|
+
|
179
|
+
def observeInstructionCount(cxt, count)
|
180
|
+
raise RunawayScriptError, "script exceeded allowable instruction count" if count > @limit
|
181
|
+
end
|
182
|
+
|
183
|
+
def instruction_limit=(count)
|
184
|
+
@limit = count
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class ContextError < StandardError # :nodoc:
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
class JavascriptError < StandardError # :nodoc:
|
193
|
+
def initialize(native)
|
194
|
+
@native = native
|
195
|
+
end
|
196
|
+
|
197
|
+
def message
|
198
|
+
@native.cause.details
|
199
|
+
end
|
200
|
+
|
201
|
+
def javascript_backtrace
|
202
|
+
@native.getScriptStackTrace()
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class RunawayScriptError < StandardError # :nodoc:
|
207
|
+
end
|
208
|
+
end
|
data/lib/rhino/java.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'rhino/rhino-1.7R2.jar'
|
3
|
+
|
4
|
+
module Rhino
|
5
|
+
# This module contains all the native Rhino objects implemented in Java
|
6
|
+
# e.g.
|
7
|
+
# Rhino::J::NativeObject # => org.mozilla.javascript.NativeObject
|
8
|
+
module J
|
9
|
+
import "org.mozilla.javascript"
|
10
|
+
|
11
|
+
module Regexp
|
12
|
+
import "org.mozilla.javascript.regexp"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
unless Object.method_defined?(:tap)
|
18
|
+
class Object #:nodoc:
|
19
|
+
def tap
|
20
|
+
yield self
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Rhino
|
3
|
+
|
4
|
+
# Wraps a function that has been defined in Javascript so that it can
|
5
|
+
# be referenced and called from javascript. e.g.
|
6
|
+
#
|
7
|
+
# plus = Rhino::Context.open do |cx|
|
8
|
+
# cx.eval('function(lhs, rhs) {return lhs + rhs}')
|
9
|
+
# end
|
10
|
+
# plus.call(5,4) # => 9
|
11
|
+
#
|
12
|
+
class NativeFunction < NativeObject
|
13
|
+
def call(*args)
|
14
|
+
begin
|
15
|
+
cxt = J::Context.enter()
|
16
|
+
scope = @j.getParentScope() || cxt.initStandardObjects()
|
17
|
+
@j.call(cxt, scope, scope, args.map {|o| To.javascript(o)})
|
18
|
+
ensure
|
19
|
+
J::Context.exit()
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
module Rhino
|
3
|
+
# Wraps a javascript object and makes its properties available from ruby.
|
4
|
+
class NativeObject
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
# The native java object wrapped by this NativeObject. This will generally
|
8
|
+
# be an instance of org.mozilla.javascript.Scriptable
|
9
|
+
attr_reader :j
|
10
|
+
|
11
|
+
def initialize(j=nil) # :nodoc:
|
12
|
+
@j = j || J::NativeObject.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# get a property from this javascript object, where +k+ is a string or symbol
|
16
|
+
# corresponding to the property name
|
17
|
+
# e.g.
|
18
|
+
# jsobject = Context.open do |cxt|
|
19
|
+
# cxt.eval('({foo: 'bar', 'Take me to': 'a funky town'})')
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# jsobject[:foo] # => 'bar'
|
23
|
+
# jsobject['foo'] # => 'bar'
|
24
|
+
# jsobject['Take me to'] # => 'a funky town'
|
25
|
+
def [](k)
|
26
|
+
To.ruby J::ScriptableObject.getProperty(@j,k.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
# set a property on the javascript object, where +k+ is a string or symbol corresponding
|
30
|
+
# to the property name, and +v+ is the value to set. e.g.
|
31
|
+
#
|
32
|
+
# jsobject = eval_js "new Object()"
|
33
|
+
# jsobject['foo'] = 'bar'
|
34
|
+
# Context.open(:with => jsobject) do |cxt|
|
35
|
+
# cxt.eval('foo') # => 'bar'
|
36
|
+
# end
|
37
|
+
|
38
|
+
def []=(k,v)
|
39
|
+
J::ScriptableObject.putProperty(@j, k.to_s, To.javascript(v))
|
40
|
+
end
|
41
|
+
|
42
|
+
# enumerate the key value pairs contained in this javascript object. e.g.
|
43
|
+
#
|
44
|
+
# eval_js("{foo: 'bar', baz: 'bang'}").each do |key,value|
|
45
|
+
# puts "#{key} -> #{value} "
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# outputs foo -> bar baz -> bang
|
49
|
+
def each
|
50
|
+
for id in @j.getAllIds() do
|
51
|
+
yield id,@j.get(id,@j)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Converts the native object to a hash. This isn't really a stretch since it's
|
56
|
+
# pretty much a hash in the first place.
|
57
|
+
def to_h
|
58
|
+
{}.tap do |h|
|
59
|
+
each do |k,v|
|
60
|
+
v = To.ruby(v)
|
61
|
+
h[k] = self.class === v ? v.to_h : v
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convert this javascript object into a json string.
|
67
|
+
def to_json(*args)
|
68
|
+
to_h.to_json(*args)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/rhino/object.rb
ADDED
Binary file
|
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
module Rhino
|
3
|
+
class RubyObject < J::ScriptableObject
|
4
|
+
include J::Wrapper
|
5
|
+
|
6
|
+
def initialize(object)
|
7
|
+
super()
|
8
|
+
@ruby = object
|
9
|
+
end
|
10
|
+
|
11
|
+
def unwrap
|
12
|
+
@ruby
|
13
|
+
end
|
14
|
+
|
15
|
+
def getClassName()
|
16
|
+
@ruby.class.name
|
17
|
+
end
|
18
|
+
|
19
|
+
def getPrototype()
|
20
|
+
Prototype::Generic
|
21
|
+
end
|
22
|
+
|
23
|
+
def getIds()
|
24
|
+
puts "getIds()"
|
25
|
+
require 'java'
|
26
|
+
@ruby.public_methods(false).map {|m| m.gsub(/(.)_(.)/) {::JavaLang::String.new("#{$1}#{$2.upcase}")}}.tap {|ids| puts "ids: #{ids.inspect}"}.to_java
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
"[Native #{@ruby.class.name}]"
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :prototype, :getPrototype
|
34
|
+
|
35
|
+
|
36
|
+
class Prototype < J::ScriptableObject
|
37
|
+
|
38
|
+
def get(name, start)
|
39
|
+
robject = To.ruby(start)
|
40
|
+
if name == "toString"
|
41
|
+
return RubyFunction.new(lambda { "[Ruby #{robject.class.name}]"})
|
42
|
+
end
|
43
|
+
rb_name = name.gsub(/([a-z])([A-Z])/) {"#{$1}_#{$2.downcase}"}
|
44
|
+
if (robject.public_methods(false).include?(rb_name))
|
45
|
+
method = robject.method(rb_name)
|
46
|
+
if method.arity == 0
|
47
|
+
To.javascript(method.call)
|
48
|
+
else
|
49
|
+
RubyFunction.new(method)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
super(name, start)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def has(name, start)
|
57
|
+
rb_name = name.gsub(/([a-z])([A-Z])/) {"#{$1}_#{$2.downcase}"}
|
58
|
+
To.ruby(start).public_methods(false).respond_to?(rb_name) ? true : super(name,start)
|
59
|
+
end
|
60
|
+
|
61
|
+
Generic = new
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
module Rhino
|
3
|
+
module To
|
4
|
+
JS_UNDEF = [J::Scriptable::NOT_FOUND, J::Undefined]
|
5
|
+
|
6
|
+
def ruby(object)
|
7
|
+
case object
|
8
|
+
when *JS_UNDEF then nil
|
9
|
+
when J::Wrapper then object.unwrap
|
10
|
+
when J::NativeArray then array(object)
|
11
|
+
when J::Regexp::NativeRegExp then object
|
12
|
+
when J::Function then NativeFunction.new(object)
|
13
|
+
when J::Scriptable then NativeObject.new(object)
|
14
|
+
else object
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def javascript(object)
|
19
|
+
case object
|
20
|
+
when String,Numeric then object
|
21
|
+
when TrueClass,FalseClass then object
|
22
|
+
when Array then J::NativeArray.new(object.to_java)
|
23
|
+
when Proc,Method then RubyFunction.new(object)
|
24
|
+
when NativeObject then object.j
|
25
|
+
when J::Scriptable then object
|
26
|
+
else RubyObject.new(object)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def array(native)
|
31
|
+
native.length.times.map {|i| ruby(native.get(i,native))}
|
32
|
+
end
|
33
|
+
|
34
|
+
module_function :ruby, :javascript, :array
|
35
|
+
end
|
36
|
+
end
|
data/lib/rhino.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
|
5
|
+
module Rhino
|
6
|
+
VERSION = '1.72.7'
|
7
|
+
require 'rhino/java'
|
8
|
+
require 'rhino/object'
|
9
|
+
require 'rhino/context'
|
10
|
+
require 'rhino/wormhole'
|
11
|
+
require 'rhino/ruby_object'
|
12
|
+
require 'rhino/ruby_function'
|
13
|
+
require 'rhino/native_object'
|
14
|
+
require 'rhino/native_function'
|
15
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
include Rhino
|
4
|
+
|
5
|
+
describe Rhino::Context do
|
6
|
+
|
7
|
+
describe "Initalizing Standard Javascript Objects" do
|
8
|
+
it "provides the standard objects without java integration by default" do
|
9
|
+
Context.open do |cxt|
|
10
|
+
cxt["Object"].should_not be_nil
|
11
|
+
cxt["Math"].should_not be_nil
|
12
|
+
cxt["String"].should_not be_nil
|
13
|
+
cxt["Function"].should_not be_nil
|
14
|
+
cxt["Packages"].should be_nil
|
15
|
+
cxt["java"].should be_nil
|
16
|
+
cxt["org"].should be_nil
|
17
|
+
cxt["com"].should be_nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "provides unsealed standard object by default" do
|
22
|
+
Context.open do |cxt|
|
23
|
+
cxt.eval("Object.foop = 'blort'")
|
24
|
+
cxt["Object"]['foop'].should == 'blort'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "allows you to seal the standard objects so that they cannot be modified" do
|
29
|
+
Context.open(:sealed => true) do |cxt|
|
30
|
+
lambda {
|
31
|
+
cxt.eval("Object.foop = 'blort'")
|
32
|
+
}.should raise_error(Rhino::RhinoError)
|
33
|
+
|
34
|
+
lambda {
|
35
|
+
cxt.eval("Object.prototype.toString = function() {}")
|
36
|
+
}.should raise_error(Rhino::RhinoError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "allows java integration to be turned on when initializing standard objects" do
|
41
|
+
Context.open(:java => true) do |cxt|
|
42
|
+
cxt["Packages"].should_not be_nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
include Rhino
|
4
|
+
|
5
|
+
describe Rhino::To do
|
6
|
+
describe "ruby translation" do
|
7
|
+
it "converts javascript NOT_FOUND to ruby nil" do
|
8
|
+
To.ruby(J::Scriptable::NOT_FOUND).should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "converts javascript arrays to ruby arrays" do
|
12
|
+
J::NativeObject.new.tap do |o|
|
13
|
+
To.ruby(o).tap do |ruby_object|
|
14
|
+
ruby_object.should respond_to(:j)
|
15
|
+
ruby_object.j.should be(o)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "wraps native javascript arrays into a ruby NativeArray wrapper" do
|
21
|
+
J::NativeArray.new([1,2,4].to_java).tap do |a|
|
22
|
+
To.ruby(a).should == [1,2,4]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "wraps native javascript functions into a ruby NativeFunction wrapper" do
|
27
|
+
|
28
|
+
c = Class.new(J::BaseFunction).class_eval do
|
29
|
+
self.tap do
|
30
|
+
def call(cxt, scope, this, args)
|
31
|
+
args.join(',')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
c.new.tap do |f|
|
37
|
+
To.ruby(f).tap do |o|
|
38
|
+
o.should_not be_nil
|
39
|
+
o.should be_kind_of(NativeObject)
|
40
|
+
o.should be_respond_to(:call)
|
41
|
+
o.call(1,2,3).should == "1,2,3"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
it "leaves native ruby objects alone" do
|
48
|
+
Object.new.tap do |o|
|
49
|
+
To.ruby(o).should be(o)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "it unwraps wrapped java objects" do
|
54
|
+
Context.open do |cx|
|
55
|
+
scope = cx.scope
|
56
|
+
Java::JavaLang::String.new("Hello World").tap do |str|
|
57
|
+
J::NativeJavaObject.new(scope.j, str, str.getClass()).tap do |o|
|
58
|
+
To.ruby(o).should == "Hello World"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "converts javascript undefined into nil" do
|
65
|
+
To.ruby(J::Undefined.instance).should be_nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "javascript translation" do
|
70
|
+
|
71
|
+
it "passes primitives through to the js layer to let jruby and rhino do he thunking" do
|
72
|
+
to(1).should be(1)
|
73
|
+
to(2.5).should == 2.5
|
74
|
+
to("foo").should == "foo"
|
75
|
+
to(true).should be(true)
|
76
|
+
to(false).should be(false)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "unwraps wrapped ruby objects before passing them to the javascript runtime" do
|
80
|
+
J::NativeObject.new.tap do |o|
|
81
|
+
To.javascript(NativeObject.new(o)).should be(o)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "leaves native javascript objects alone" do
|
86
|
+
J::NativeObject.new.tap do |o|
|
87
|
+
To.javascript(o).should be(o)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "converts ruby arrays into javascript arrays" do
|
92
|
+
To.javascript([1,2,3,4,5]).tap do |a|
|
93
|
+
a.should be_kind_of(J::NativeArray)
|
94
|
+
a.get(0,a).should be(1)
|
95
|
+
a.get(1,a).should be(2)
|
96
|
+
a.get(2,a).should be(3)
|
97
|
+
a.get(3,a).should be(4)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "converts procs and methods into native functions" do
|
102
|
+
to(lambda {|lhs,rhs| lhs * rhs}).tap do |f|
|
103
|
+
f.should be_kind_of(J::Function)
|
104
|
+
f.call(nil, nil, nil, [7,6]).should be(42)
|
105
|
+
end
|
106
|
+
to("foo,bar,baz".method(:split)).tap do |m|
|
107
|
+
m.should be_kind_of(J::Function)
|
108
|
+
To.ruby(m.call(nil, nil, nil, ',')).should == ['foo', 'bar', 'baz']
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it "creates a prototype for the object based on its class" do
|
113
|
+
Class.new.tap do |c|
|
114
|
+
c.class_eval do
|
115
|
+
def foo(one, two)
|
116
|
+
"1: #{one}, 2: #{two}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
To.javascript(c.new).tap do |o|
|
121
|
+
o.should be_kind_of(RubyObject)
|
122
|
+
o.prototype.tap do |p|
|
123
|
+
p.should_not be_nil
|
124
|
+
p.get("foo", p).should_not be_nil
|
125
|
+
p.get("toString", p).should_not be_nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def to(object)
|
133
|
+
To.javascript(object)
|
134
|
+
end
|
135
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
data/tasks/jruby.rake
ADDED
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
desc "Run the specs under spec/models"
|
5
|
+
Spec::Rake::SpecTask.new do |t|
|
6
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
7
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
rescue LoadError
|
10
|
+
desc "bundle install to run rspecs"
|
11
|
+
task :spec
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rhino/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{therubyrhino}
|
7
|
+
s.version = Rhino::VERSION
|
8
|
+
s.authors = ["Charles Lowell"]
|
9
|
+
s.description = %q{Call javascript code and manipulate javascript objects from ruby. Call ruby code and manipulate ruby objects from javascript.}
|
10
|
+
s.email = %q{cowboyd@thefrontside.net}
|
11
|
+
s.extra_rdoc_files = ["README.rdoc"]
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.homepage = %q{http://github.com/cowboyd/therubyrhino}
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
s.rubyforge_project = %q{therubyrhino}
|
16
|
+
s.summary = %q{Embed the Rhino JavaScript interpreter into JRuby}
|
17
|
+
|
18
|
+
s.add_development_dependency "rspec"
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: therubyrhino
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 72
|
8
|
+
- 7
|
9
|
+
version: 1.72.7
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Charles Lowell
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-10 00:00:00 -06:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
segments:
|
27
|
+
- 0
|
28
|
+
version: "0"
|
29
|
+
requirement: *id001
|
30
|
+
prerelease: false
|
31
|
+
type: :development
|
32
|
+
description: Call javascript code and manipulate javascript objects from ruby. Call ruby code and manipulate ruby objects from javascript.
|
33
|
+
email: cowboyd@thefrontside.net
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README.rdoc
|
40
|
+
files:
|
41
|
+
- .gitignore
|
42
|
+
- .gitmodules
|
43
|
+
- Gemfile
|
44
|
+
- Gemfile.lock
|
45
|
+
- History.txt
|
46
|
+
- README.rdoc
|
47
|
+
- Rakefile
|
48
|
+
- lib/rhino.rb
|
49
|
+
- lib/rhino/context.rb
|
50
|
+
- lib/rhino/java.rb
|
51
|
+
- lib/rhino/native_function.rb
|
52
|
+
- lib/rhino/native_object.rb
|
53
|
+
- lib/rhino/object.rb
|
54
|
+
- lib/rhino/rhino-1.7R2.jar
|
55
|
+
- lib/rhino/ruby_function.rb
|
56
|
+
- lib/rhino/ruby_object.rb
|
57
|
+
- lib/rhino/version.rb
|
58
|
+
- lib/rhino/wormhole.rb
|
59
|
+
- spec/redjs_helper.rb
|
60
|
+
- spec/rhino/context_spec.rb
|
61
|
+
- spec/rhino/wormhole_spec.rb
|
62
|
+
- spec/spec.opts
|
63
|
+
- spec/spec_helper.rb
|
64
|
+
- tasks/jruby.rake
|
65
|
+
- tasks/rspec.rake
|
66
|
+
- therubyrhino.gemspec
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: http://github.com/cowboyd/therubyrhino
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
requirements: []
|
91
|
+
|
92
|
+
rubyforge_project: therubyrhino
|
93
|
+
rubygems_version: 1.3.6
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: Embed the Rhino JavaScript interpreter into JRuby
|
97
|
+
test_files: []
|
98
|
+
|