therubyracer 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of therubyracer might be problematic. Click here for more details.
- data/History.txt +18 -2
- data/README.rdoc +107 -6
- data/ext/v8/converters.cpp +19 -3
- data/ext/v8/converters.h +2 -0
- data/ext/v8/v8.cpp +4 -0
- data/ext/v8/v8_obj.cpp +17 -4
- data/ext/v8/v8_obj.h +1 -0
- data/lib/v8.rb +1 -1
- data/lib/v8/context.rb +16 -1
- data/lib/v8/object.rb +15 -1
- data/lib/v8/to.rb +4 -0
- data/spec/redjs/jsapi_spec.rb +17 -11
- data/therubyracer.gemspec +2 -2
- metadata +2 -2
data/History.txt
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
=== 0.
|
|
1
|
+
=== 0.4.3 2010-10-11
|
|
2
|
+
* N major enhancements:
|
|
3
|
+
* access properties on Ruby objects with their camel case equivalents
|
|
4
|
+
* reflect JavaScript objects into Ruby and access their properties
|
|
5
|
+
* load JavaScript source from an IO object or by filename
|
|
2
6
|
|
|
7
|
+
=== 0.4.2 2010-10-10
|
|
3
8
|
* 1 major enhancement:
|
|
4
|
-
*
|
|
9
|
+
* embed Ruby Objects into Javascript and call their methods
|
|
10
|
+
|
|
11
|
+
=== 0.4.1 2010-01-09
|
|
12
|
+
|
|
13
|
+
* 3 major enhancements:
|
|
14
|
+
* embed bare Proc and Method objects into JavaScript and call them
|
|
15
|
+
* catch JavaScript exceptions from Ruby
|
|
16
|
+
|
|
17
|
+
=== 0.4.0 2009-12-21
|
|
18
|
+
|
|
19
|
+
* 1 major enhancements:
|
|
20
|
+
* evaluate JavaScript code from inside Ruby.
|
data/README.rdoc
CHANGED
|
@@ -8,26 +8,127 @@ Embed the V8 Javascript interpreter into Ruby.
|
|
|
8
8
|
|
|
9
9
|
== FEATURES/PROBLEMS:
|
|
10
10
|
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Evaluate Javascript from with in Ruby
|
|
12
|
+
* Embed your Ruby objects into the Javascript world
|
|
13
|
+
* Manipulate JavaScript objects and call JavaScript functions from Ruby
|
|
14
|
+
* API compatible with the The Ruby Rhino (for JRuby: http://github.com/cowboyd/therubyrhino)
|
|
15
|
+
* Currently ALPHA software.
|
|
13
16
|
|
|
14
17
|
== SYNOPSIS:
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
require 'v8'
|
|
20
|
+
|
|
21
|
+
# evaluate some simple javascript
|
|
22
|
+
|
|
23
|
+
V8::Context.open do |cxt|
|
|
24
|
+
cxt['foo'] = "bar"
|
|
25
|
+
cxt.eval('foo') # => "bar"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# evaluate a ruby function from javascript
|
|
29
|
+
|
|
30
|
+
V8::Context.open do |context|
|
|
31
|
+
context["say"] = lambda {|word, times| word * times}
|
|
32
|
+
context.eval("say("Hello", 3)") #=> HelloHelloHello
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# embed a ruby object into your javascript environment
|
|
36
|
+
|
|
37
|
+
class MyMath
|
|
38
|
+
def plus(lhs, rhs)
|
|
39
|
+
lhs + rhs
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
V8::Context.open do |context|
|
|
44
|
+
context["math"] = MyMath.new
|
|
45
|
+
context.eval("math.plus(20,22)") #=> 42
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#COMING SOON!
|
|
49
|
+
# make a ruby object *be* your javascript environment
|
|
50
|
+
math = MyMath.new
|
|
51
|
+
V8::Context.open(:with => math) do |context|
|
|
52
|
+
context.eval("plus(20,22)") #=> 42
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#or the equivalent
|
|
56
|
+
|
|
57
|
+
math.eval_js("plus(20,22)")
|
|
58
|
+
|
|
59
|
+
# Configure your embedding setup
|
|
60
|
+
|
|
61
|
+
#COMING SOON!
|
|
62
|
+
# Make your standard objects (Object, String, etc...) immutable
|
|
63
|
+
V8::Context.open(:sealed => true) do |context|
|
|
64
|
+
context.eval("Object.prototype.toString = function() {}") # this is an error!
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
#COMING SOON!
|
|
68
|
+
#limit the number of instructions that can be executed in order to prevent
|
|
69
|
+
#rogue scripts
|
|
70
|
+
V8::Context.open do |context|
|
|
71
|
+
context.instruction_limit = 100000
|
|
72
|
+
context.eval("while (true);") # => Error!
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
==== Different ways of loading javascript source
|
|
76
|
+
|
|
77
|
+
In addition to just evaluating strings, you can also use streams such as files.
|
|
78
|
+
|
|
79
|
+
# evaluate bytes read from any File/IO object:
|
|
80
|
+
File.open("mysource.js") do |file|
|
|
81
|
+
eval_js file, "mysource.js"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# or load it by filename
|
|
85
|
+
V8::Context.open do |context|
|
|
86
|
+
context.load("mysource.js")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
=== Safe by default
|
|
90
|
+
|
|
91
|
+
The Ruby Racer is designed to let you evaluate javascript as safely as possible unless you tell it to do something more
|
|
92
|
+
dangerous. The default context is a hermetically sealed javascript environment with only the standard javascript objects
|
|
93
|
+
and functions. Nothing from the ruby world is accessible at all.
|
|
94
|
+
|
|
95
|
+
For ruby objects that you explicitly embed into javascript, only the +public+ methods *defined in their classes* are
|
|
96
|
+
exposed by default. E.g.
|
|
97
|
+
|
|
98
|
+
class A
|
|
99
|
+
def a
|
|
100
|
+
"a"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class B < A
|
|
105
|
+
def b
|
|
106
|
+
"b"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
V8::Context.open do |cxt|
|
|
112
|
+
cxt['a'] = A.new
|
|
113
|
+
cxt['b'] = B.new
|
|
114
|
+
cxt.eval("a.a()") # => 'a'
|
|
115
|
+
cxt.eval("b.b()") # => 'b'
|
|
116
|
+
cxt.eval("b.a()") # => 'TypeError: undefined property 'a' is not a function'
|
|
117
|
+
end
|
|
118
|
+
|
|
18
119
|
== REQUIREMENTS:
|
|
19
120
|
|
|
20
121
|
* libv8 >= 0.4.0
|
|
21
122
|
|
|
22
123
|
== INSTALL:
|
|
23
|
-
|
|
124
|
+
* download, build and install V8: http://code.google.com/apis/v8/build.html
|
|
24
125
|
* sudo gem install therubyracer
|
|
25
126
|
|
|
26
127
|
== LICENSE:
|
|
27
128
|
|
|
28
129
|
(The MIT License)
|
|
29
130
|
|
|
30
|
-
Copyright (c) 2009
|
|
131
|
+
Copyright (c) 2009 Charles Lowell
|
|
31
132
|
|
|
32
133
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
33
134
|
a copy of this software and associated documentation files (the
|
data/ext/v8/converters.cpp
CHANGED
|
@@ -9,6 +9,8 @@ namespace {
|
|
|
9
9
|
std::string UNDEFINED_STR("undefined");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
VALUE V8_To;
|
|
13
|
+
|
|
12
14
|
VALUE V82RB(Handle<Value>& value) {
|
|
13
15
|
convert_v8_to_rb_t convert;
|
|
14
16
|
VALUE result;
|
|
@@ -16,6 +18,16 @@ VALUE V82RB(Handle<Value>& value) {
|
|
|
16
18
|
return result;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
if (value->IsArray()) {
|
|
22
|
+
Local<Array> array(Array::Cast(*value));
|
|
23
|
+
VALUE rb_array = rb_ary_new2(array->Length());
|
|
24
|
+
for (unsigned int i = 0; i < array->Length(); i++) {
|
|
25
|
+
Local<Value> value = array->Get(Integer::New(i));
|
|
26
|
+
rb_ary_push(rb_array, V82RB(value));
|
|
27
|
+
}
|
|
28
|
+
return rb_array;
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
if (value->IsObject()) {
|
|
20
32
|
Local<Object> object(Object::Cast(*value));
|
|
21
33
|
return V8_Ref_Create(V8_C_Object, value);
|
|
@@ -39,12 +51,16 @@ Local<Value> RB2V8(VALUE value) {
|
|
|
39
51
|
|
|
40
52
|
Local<ObjectTemplate> tmpl = ObjectTemplate::New();
|
|
41
53
|
VALUE methods = rb_funcall(value, rb_intern("public_methods"), 1, Qfalse);
|
|
42
|
-
int len =
|
|
54
|
+
int len = RARRAY_LEN(methods);
|
|
43
55
|
for (int i = 0; i < len; i++) {
|
|
44
|
-
VALUE method_name =
|
|
56
|
+
VALUE method_name = RARRAY_PTR(methods)[i];
|
|
57
|
+
VALUE camel_method_name = rb_funcall(V8_To, rb_intern("camelcase"), 1, method_name);
|
|
45
58
|
VALUE method = rb_funcall(value, rb_intern("method"), 1, method_name);
|
|
46
59
|
Local<String> keystr = (String *)*RB2V8(method_name);
|
|
47
|
-
|
|
60
|
+
Local<String> camelstr = (String *)*RB2V8(camel_method_name);
|
|
61
|
+
Local<Value> fun = RB2V8(method);
|
|
62
|
+
tmpl->Set(keystr, fun);
|
|
63
|
+
tmpl->Set(camelstr, fun);
|
|
48
64
|
}
|
|
49
65
|
return tmpl->NewInstance();
|
|
50
66
|
}
|
data/ext/v8/converters.h
CHANGED
data/ext/v8/v8.cpp
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include "v8_script.h"
|
|
7
7
|
#include "v8_template.h"
|
|
8
8
|
#include "v8_standalone.h"
|
|
9
|
+
#include "converters.h"
|
|
9
10
|
|
|
10
11
|
#include <stdio.h>
|
|
11
12
|
|
|
@@ -29,6 +30,8 @@ extern "C" {
|
|
|
29
30
|
|
|
30
31
|
rb_mModule = rb_define_module("V8");
|
|
31
32
|
rb_define_singleton_method(rb_mModule, "what_is_this?", (VALUE(*)(...)) v8_what_is_this, 1);
|
|
33
|
+
|
|
34
|
+
V8_To = rb_define_module_under(rb_mModule, "To");
|
|
32
35
|
|
|
33
36
|
//native module setup
|
|
34
37
|
VALUE rb_mNative = rb_define_module_under(rb_mModule, "C");
|
|
@@ -63,6 +66,7 @@ extern "C" {
|
|
|
63
66
|
rb_define_singleton_method(V8_C_Object, "new", (VALUE(*)(...))v8_Object_New, 0);
|
|
64
67
|
rb_define_method(V8_C_Object, "Get", (VALUE(*)(...))v8_Object_Get, 1);
|
|
65
68
|
rb_define_method(V8_C_Object, "Set", (VALUE(*)(...))v8_Object_Set, 2);
|
|
69
|
+
rb_define_method(V8_C_Object, "GetPropertyNames", (VALUE(*)(...)) v8_Object_GetPropertyNames, 0);
|
|
66
70
|
|
|
67
71
|
V8_C_Message = rb_define_class_under(rb_mNative, "Message", rb_cObject);
|
|
68
72
|
rb_define_method(V8_C_Message, "Get", (VALUE(*)(...))v8_Message_Get, 0);
|
data/ext/v8/v8_obj.cpp
CHANGED
|
@@ -8,6 +8,12 @@ using namespace v8;
|
|
|
8
8
|
|
|
9
9
|
VALUE V8_C_Object;
|
|
10
10
|
|
|
11
|
+
namespace {
|
|
12
|
+
Local<Object> unwrap(VALUE robj) {
|
|
13
|
+
return V8_Ref_Get<Object>(robj);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
VALUE v8_Object_New(VALUE clazz) {
|
|
12
18
|
HandleScope handles;
|
|
13
19
|
return V8_Ref_Create(clazz, Object::New());
|
|
@@ -15,17 +21,24 @@ VALUE v8_Object_New(VALUE clazz) {
|
|
|
15
21
|
|
|
16
22
|
VALUE v8_Object_Get(VALUE self, VALUE key) {
|
|
17
23
|
HandleScope handles;
|
|
18
|
-
Local<Object> obj =
|
|
19
|
-
VALUE keystr =
|
|
24
|
+
Local<Object> obj = unwrap(self);
|
|
25
|
+
VALUE keystr = rb_str_to_str(key);
|
|
20
26
|
Local<Value> value = obj->Get(RB2V8(keystr));
|
|
21
27
|
return V82RB(value);
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
VALUE v8_Object_Set(VALUE self, VALUE key, VALUE value) {
|
|
25
31
|
HandleScope handles;
|
|
26
|
-
Local<Object> obj =
|
|
32
|
+
Local<Object> obj = unwrap(self);
|
|
33
|
+
|
|
27
34
|
VALUE keystr = rb_funcall(key, rb_intern("to_s"), 0);
|
|
28
|
-
|
|
29
35
|
obj->Set(RB2V8(keystr), RB2V8(value));
|
|
30
36
|
return Qnil;
|
|
31
37
|
}
|
|
38
|
+
|
|
39
|
+
VALUE v8_Object_GetPropertyNames(VALUE self) {
|
|
40
|
+
HandleScope handles;
|
|
41
|
+
Local<Object> object = unwrap(self);
|
|
42
|
+
Local<Value> names = object->GetPropertyNames();
|
|
43
|
+
return V82RB(names);
|
|
44
|
+
}
|
data/ext/v8/v8_obj.h
CHANGED
data/lib/v8.rb
CHANGED
data/lib/v8/context.rb
CHANGED
|
@@ -10,7 +10,10 @@ module V8
|
|
|
10
10
|
end if block_given?
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def eval(javascript)
|
|
13
|
+
def eval(javascript, sourcename = '<eval>', line = 1)
|
|
14
|
+
if IO === javascript || StringIO === javascript
|
|
15
|
+
javascript = javascript.read()
|
|
16
|
+
end
|
|
14
17
|
@native.eval(javascript).tap do |result|
|
|
15
18
|
raise JavascriptError.new(result) if result.kind_of?(C::Message)
|
|
16
19
|
return To.ruby(result)
|
|
@@ -21,6 +24,16 @@ module V8
|
|
|
21
24
|
self.eval(*args)
|
|
22
25
|
end
|
|
23
26
|
|
|
27
|
+
def load(filename)
|
|
28
|
+
File.open(filename) do |file|
|
|
29
|
+
evaluate file, filename, 1
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def [](key)
|
|
34
|
+
To.ruby(@native.Global().Get(key.to_s))
|
|
35
|
+
end
|
|
36
|
+
|
|
24
37
|
def []=(key, value)
|
|
25
38
|
value.tap do
|
|
26
39
|
@native.Global().tap do |scope|
|
|
@@ -41,4 +54,6 @@ module V8
|
|
|
41
54
|
super(v8_message.Get())
|
|
42
55
|
end
|
|
43
56
|
end
|
|
57
|
+
class RunawayScriptError < ContextError
|
|
58
|
+
end
|
|
44
59
|
end
|
data/lib/v8/object.rb
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
|
|
2
2
|
module V8
|
|
3
3
|
class Object
|
|
4
|
+
include Enumerable
|
|
5
|
+
|
|
4
6
|
def initialize(native)
|
|
5
7
|
@native = native
|
|
6
8
|
end
|
|
7
9
|
|
|
8
10
|
def [](key)
|
|
9
|
-
To.ruby(@native.Get(key))
|
|
11
|
+
To.ruby(@native.Get(key.to_s))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def []=(key, value)
|
|
15
|
+
value.tap do
|
|
16
|
+
@native.Set(key.to_s, value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def each
|
|
21
|
+
for prop in @native.GetPropertyNames()
|
|
22
|
+
yield prop, self[prop]
|
|
23
|
+
end
|
|
10
24
|
end
|
|
11
25
|
end
|
|
12
26
|
end
|
data/lib/v8/to.rb
CHANGED
data/spec/redjs/jsapi_spec.rb
CHANGED
|
@@ -269,7 +269,7 @@ describe "Ruby Javascript API" do
|
|
|
269
269
|
cxt.eval('while (true);')
|
|
270
270
|
end
|
|
271
271
|
end
|
|
272
|
-
}.should raise_error(
|
|
272
|
+
}.should raise_error(RunawayScriptError)
|
|
273
273
|
end
|
|
274
274
|
|
|
275
275
|
it "has a private constructor" do
|
|
@@ -330,17 +330,21 @@ describe "Ruby Javascript API" do
|
|
|
330
330
|
end
|
|
331
331
|
|
|
332
332
|
it "can have its properties manipulated via ruby style [] hash access" do
|
|
333
|
-
@
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
333
|
+
@cxt.open do
|
|
334
|
+
@o["foo"] = 'bar'
|
|
335
|
+
evaljs('o.foo').should == "bar"
|
|
336
|
+
evaljs('o.blue = "blam"')
|
|
337
|
+
@o["blue"].should == "blam"
|
|
338
|
+
end
|
|
337
339
|
end
|
|
338
340
|
|
|
339
341
|
it "doesn't matter if you use a symbol or a string to set a value" do
|
|
340
|
-
@
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
342
|
+
@cxt.open do
|
|
343
|
+
@o[:foo] = "bar"
|
|
344
|
+
@o['foo'].should == "bar"
|
|
345
|
+
@o['baz'] = "bang"
|
|
346
|
+
@o[:baz].should == "bang"
|
|
347
|
+
end
|
|
344
348
|
end
|
|
345
349
|
|
|
346
350
|
it "returns nil when the value is null, null, or not defined" do
|
|
@@ -358,8 +362,10 @@ EOJS
|
|
|
358
362
|
end
|
|
359
363
|
|
|
360
364
|
it "is enumenable" do
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
@cxt.open do
|
|
366
|
+
evaljs("o.foo = 'bar'; o.bang = 'baz'; o[5] = 'flip'")
|
|
367
|
+
@o.inject({}) {|i,p| k,v = p; i.tap {i[k] = v}}.should == {"foo" => 'bar', "bang" => 'baz', 5 => 'flip'}
|
|
368
|
+
end
|
|
363
369
|
end
|
|
364
370
|
end
|
|
365
371
|
|
data/therubyracer.gemspec
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = %q{therubyracer}
|
|
5
|
-
s.version = "0.4.
|
|
5
|
+
s.version = "0.4.3"
|
|
6
6
|
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
8
8
|
s.authors = ["Charles Lowell", "Bill Robertson"]
|
|
9
|
-
s.date = %q{2010-01-
|
|
9
|
+
s.date = %q{2010-01-11}
|
|
10
10
|
s.description = %q{Embed the V8 Javascript interpreter into Ruby.}
|
|
11
11
|
s.email = ["cowboyd@thefrontside.net", "billrobertson42@gmail.com"]
|
|
12
12
|
s.extensions = ["ext/v8/extconf.rb"]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: therubyracer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Charles Lowell
|
|
@@ -10,7 +10,7 @@ autorequire:
|
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
12
|
|
|
13
|
-
date: 2010-01-
|
|
13
|
+
date: 2010-01-11 00:00:00 +02:00
|
|
14
14
|
default_executable:
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|