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.

@@ -1,4 +1,20 @@
1
- === 0.0.1 2009-12-14
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
- * Initial release
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.
@@ -8,26 +8,127 @@ Embed the V8 Javascript interpreter into Ruby.
8
8
 
9
9
  == FEATURES/PROBLEMS:
10
10
 
11
- * Inspires fun.
12
- * Not close to production ready.
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
- Check back in February 2009 for an alpha release.
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 FIXME full name
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
@@ -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 = RARRAY(methods)->len;
54
+ int len = RARRAY_LEN(methods);
43
55
  for (int i = 0; i < len; i++) {
44
- VALUE method_name = RARRAY(methods)->ptr[i];
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
- tmpl->Set(keystr, RB2V8(method));
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
  }
@@ -6,6 +6,8 @@
6
6
  #include "convert_v8.h"
7
7
  #include <cstring>
8
8
 
9
+ extern VALUE V8_To;
10
+
9
11
  typedef RubyValueSource<V8LocalDest, v8::Local<v8::Value> > convert_rb_to_v8_t;
10
12
  typedef V8HandleSource<RubyValueDest, VALUE> convert_v8_to_rb_t;
11
13
 
@@ -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);
@@ -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 = V8_Ref_Get<Object>(self);
19
- VALUE keystr = rb_funcall(key,rb_intern("to_s"), 0);
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 = V8_Ref_Get<Object>(self);
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
+ }
@@ -8,4 +8,5 @@ extern VALUE V8_C_Object;
8
8
  VALUE v8_Object_New(VALUE clazz);
9
9
  VALUE v8_Object_Get(VALUE self, VALUE key);
10
10
  VALUE v8_Object_Set(VALUE self, VALUE key, VALUE value);
11
+ VALUE v8_Object_GetPropertyNames(VALUE self);
11
12
  #endif
data/lib/v8.rb CHANGED
@@ -2,7 +2,7 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  module V8
5
- VERSION = '0.4.2'
5
+ VERSION = '0.4.3'
6
6
  require 'v8/v8' #native glue
7
7
  require 'v8/to'
8
8
  require 'v8/context'
@@ -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
@@ -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
@@ -20,6 +20,10 @@ module V8
20
20
  value
21
21
  end
22
22
  end
23
+
24
+ def camelcase(str)
25
+ str.to_s.gsub(/_(\w)/) {$1.upcase}
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -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(Rhino::RunawayScriptError)
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
- @o["foo"] = 'bar'
334
- evaljs('o.foo').should == "bar"
335
- evaljs('o.blue = "blam"')
336
- @o["blue"].should == "blam"
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
- @o[:foo] = "bar"
341
- @o['foo'].should == "bar"
342
- @o['baz'] = "bang"
343
- @o[:baz].should == "bang"
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
- evaljs("o.foo = 'bar'; o.bang = 'baz'; o[5] = 'flip'")
362
- @o.inject({}) {|i,p| k,v = p; i.tap {i[k] = v}}.should == {"foo" => 'bar', "bang" => 'baz', 5 => 'flip'}
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
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{therubyracer}
5
- s.version = "0.4.1"
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-10}
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.2
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-10 00:00:00 +02:00
13
+ date: 2010-01-11 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency