tomato 0.0.1.prealpha1 → 0.0.1.prealpha2

Sign up to get free protection for your applications and to get access to all the features.
Binary file
Binary file
@@ -13,6 +13,20 @@ static VALUE fTomato_execute(VALUE self, const char *javascript, const char *fil
13
13
  static void tomato_mark(V8Tomato *tomato);
14
14
  static void tomato_free(V8Tomato *tomato);
15
15
 
16
+ static v8::Handle<v8::Value> debug(const Arguments &args)
17
+ {
18
+ Handle<Value> arg;
19
+ V8Tomato *tomato = (V8Tomato *)(Handle<External>::Cast(args.Holder()->Get(String::New("_tomato")))->Value());
20
+ for (int i = 0; i < args.Length(); i++)
21
+ {
22
+ arg = args[i];
23
+ if (i > 0) printf(", ");
24
+ String::Utf8Value str(inspect_js(tomato, args[i]));
25
+ printf("<%s>", ToCString(str));
26
+ }
27
+ printf("\n");
28
+ return Null();
29
+ }
16
30
 
17
31
  extern "C"
18
32
  void Init_tomato(void)
@@ -37,7 +51,10 @@ void Init_tomato(void)
37
51
 
38
52
  /* instance method "bind_method" */
39
53
  rb_define_method(cTomato, "_bind_method", (ruby_method_vararg *)&fTomato_bind_method, -1);
40
-
54
+
55
+ /* instance method "_bind_class" */
56
+ rb_define_method(cTomato, "_bind_class", (ruby_method_vararg *)&fTomato_bind_class, 2);
57
+
41
58
  /* init error-specific junk */
42
59
  err_init();
43
60
  }
@@ -50,9 +67,11 @@ static VALUE fTomato_allocate(VALUE klass)
50
67
 
51
68
  HandleScope handle_scope;
52
69
  Handle<ObjectTemplate> global = ObjectTemplate::New();
70
+ global->Set(String::New("debug"), FunctionTemplate::New(debug));
71
+ global->Set(String::New("_tomato"), External::New(tomato), DontEnum);
53
72
  tomato->context = Context::New(NULL, global);
54
73
  tomato->rb_instance = instance;
55
-
74
+
56
75
  return instance;
57
76
  }
58
77
 
@@ -68,6 +87,7 @@ static VALUE fTomato_version(VALUE self)
68
87
  return rb_str_new2(V8::GetVersion());
69
88
  }
70
89
 
90
+ /* Runs a String of JavaScript code. */
71
91
  static VALUE fTomato_run(int argc, VALUE *argv, VALUE self)
72
92
  {
73
93
  if (argc == 0)
@@ -28,9 +28,13 @@ extern VALUE cTomato;
28
28
  extern VALUE cTomatoError;
29
29
  extern VALUE rb_cTime;
30
30
 
31
+ /* in object_chain.cpp */
32
+ extern Handle<Value> find_or_create_object_chain(V8Tomato *tomato, VALUE chain);
33
+ extern VALUE fTomato_bind_class(VALUE self, VALUE klass, VALUE chain);
34
+
31
35
  /* in conversions_to_rb.cpp */
32
36
  extern VALUE ruby_value_of(V8Tomato *tomato, Handle<Value> result);
33
- extern void inspect_js(V8Tomato *tomato, Handle<Value> obj);
37
+ extern Handle<Value> inspect_js(V8Tomato *tomato, Handle<Value> obj);
34
38
 
35
39
  /* in conversions_to_js.cpp */
36
40
  extern Handle<Value> js_value_of(V8Tomato *tomato, VALUE value);
Binary file
Binary file
@@ -1,12 +1,114 @@
1
1
  $LOAD_PATH << File.expand_path("../../ext/tomato", __FILE__)
2
2
  require 'tomato.so'
3
+ require 'sc-core-ext'
3
4
 
4
5
  class Tomato
5
- def bind_method(method_name)
6
- _bind_method(method_name)
6
+ # Bind a method to the JavaScript world. If no other arguments are given, the method will be called on this
7
+ # instance of Tomato. For instance, the following code:
8
+ # t = Tomato.new
9
+ # t.bind_method(:inspect)
10
+ # t.run("inspect();")
11
+ # ...will result in calling t.inspect.
12
+ #
13
+ # To bind the method to a specific object, pass the object itself in as a second argument or with an :object
14
+ # option:
15
+ # t.bind_method(:inspect, my_funky_object)
16
+ # t.run("inspect();")
17
+ # # -or-
18
+ # t.bind_method(:inspect, :object => my_funky_object)
19
+ # t.run("inspect();")
20
+ #
21
+ # Both of the above examples would result in a call to my_funky_object.inspect.
22
+ #
23
+ # You can also pass a :to option to bind the method to a specific object. If the specified object chain
24
+ # does not exist, Tomato will generate generic objects to suit. Examples:
25
+ #
26
+ # t.bind_method(:inspect, my_funky_object, :to => "funky")
27
+ # t.run("funky.inspect();")
28
+ #
29
+ # t.bind_method(:inspect, my_funky_object, :to => "funky_objects.my_object")
30
+ # t.run("funky_objects.my_object.inspect();")
31
+ #
32
+ def bind_method(method_name, *args)
33
+ options = args.extract_options!
34
+ receiver = options[:object] || args.first || self
35
+ chain = options[:to] ? split_chain(options[:to]) : nil
36
+ # Bind the method to JS.
37
+ _bind_method(method_name, receiver_index(receiver), chain)
38
+ end
39
+
40
+ # Binds an entire Ruby object to the specified JavaScript object chain.
41
+ #
42
+ # If the chain is omitted, and the object itself is a Class, then the chain will be the qualified name
43
+ # of the class.
44
+ #
45
+ # Example:
46
+ # tomato.bind_object(Time)
47
+ # tomato.run("Time.now()")
48
+ # #=> 2010-06-25 18:02:52 -0400
49
+ #
50
+ # The same goes for Modules.
51
+ #
52
+ # Finally, if the chain is omitted and the object is an instance of a Class and not a Class itself, then
53
+ # the object will be bound to "ruby.[unqualified_name]". If the unqualified name is already in use, it will
54
+ # be overwritten.
55
+ #
56
+ # Example:
57
+ # time = Time.now
58
+ # tomato.bind_object(time)
59
+ # tomato.run("ruby.time.to_s()")
60
+ # #=> "2010-06-25 18:08:06 -0400"
61
+ # tomato.bind_object(time)
62
+ # tomato.run("ruby.time.to_s()")
63
+ # #=> "2010-06-25 18:08:29 -0400"
64
+ def bind_object(obj, chain = nil)
65
+ if (obj.kind_of?(Class) || obj.kind_of?(Module))
66
+ if chain.nil?
67
+ chain = obj.name.gsub(/\:\:/, '.')
68
+ chain = chain[1..-1] if chain[0] == ?.
69
+ end
70
+ # This sets up an object chain with the last created object as a Function, which can
71
+ # then be instiated with "new [Object]()" in JS.
72
+ #
73
+ # Objects of the same name in the chain are replaced, but their sub-objects are copied over
74
+ # so it should be transparent to the user.
75
+ return false unless _bind_class(receiver_index(obj), split_chain(chain))
76
+ elsif chain.nil?
77
+ unqualified_name = obj.class.name.gsub(/.*\:\:([^\:])?$/, '\1').underscore
78
+ chain = "ruby.#{unqualified_name}"
79
+ end
80
+
81
+ obj.public_methods.each do |method_name|
82
+ bind_method(method_name, obj, :to => chain)
83
+ end
84
+ obj
85
+ end
86
+
87
+ # Attempts to bind this object to the JavaScript world. If it's a String or Symbol, it will be treated
88
+ # as a call to #bind_method; otherwise, a call to #bind_object.
89
+ def bind(target, *args)
90
+ if target.kind_of?(String) || target.kind_of?(Symbol)
91
+ bind_method(target, *args)
92
+ else
93
+ bind_object(target, *args)
94
+ end
7
95
  end
8
96
 
9
97
  def inspect
10
98
  "#<Tomato>"
11
99
  end
100
+
101
+ private
102
+ def split_chain(str)
103
+ str.split(/\./)
104
+ end
105
+
106
+ def receivers
107
+ @receivers ||= []
108
+ end
109
+
110
+ def receiver_index(receiver)
111
+ receivers << receiver unless receivers.include?(receiver)
112
+ receivers.index(receiver)
113
+ end
12
114
  end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Tomato bound objects" do
4
+ subject { Tomato.new }
5
+
6
+ class ::TestObject
7
+ attr_accessor :i
8
+
9
+ def initialize; @i = 1; end
10
+
11
+ def ==(other)
12
+ other.kind_of?(TestObject)
13
+ end
14
+ end
15
+
16
+ it "should map attribute getters to corresponding Ruby methods" do
17
+ subject.bind_object(TestObject, "TO")
18
+ subject.run('var v = new TO(); v.i;').should == 1
19
+ end
20
+
21
+ it "should map attribute setters to corresponding Ruby methods" do
22
+ subject.bind_object(TestObject, "TO")
23
+ result = subject.run('var v = new TO(); v.i = 5; v;')
24
+ result.i.should == 5
25
+ end
26
+
27
+ it "should be bind-able with an explicit chain" do
28
+ subject.bind_object(TestObject, "TO")
29
+ proc { subject.run("new TO();") }.should_not raise_error
30
+ end
31
+
32
+ it "should be instantiatable" do
33
+ subject.bind_object(TestObject)
34
+ subject.run("new TestObject();").should == TestObject.new
35
+ end
36
+
37
+ it "should not lose sub-objects" do
38
+ subject.bind_object("hello", "TestObject.string")
39
+ subject.bind_object(TestObject)
40
+ subject.run("TestObject.string.inspect()").should == '"hello"'
41
+ end
42
+
43
+ it "should allow sub-bindings" do
44
+ subject.bind_object(TestObject)
45
+ subject.bind_object("hello", "TestObject.string")
46
+ subject.run("TestObject.string.inspect()").should == '"hello"'
47
+ end
48
+ end
@@ -8,6 +8,26 @@ describe "Tomato bound methods" do
8
8
  proc { subject.run("(inspect());") }.should_not raise_error
9
9
  end
10
10
 
11
+ it "should map Ruby methods to JS on a specific object within JS" do
12
+ subject.bind_method(:inspect, "hello", :to => "greeting")
13
+ subject.run("greeting.inspect();").should == '"hello"'
14
+ end
15
+
16
+ it "should map Ruby methods to JS on an object chain within JS" do
17
+ subject.bind_method(:inspect, "hello", :to => "greeting.first")
18
+ subject.run("greeting.first.inspect();").should == '"hello"'
19
+ end
20
+
21
+ it "should map Ruby methods to Javascript on a specific object without a hash" do
22
+ subject.bind_method(:inspect, "hello").should == true
23
+ subject.run("(inspect());").should == '"hello"'
24
+ end
25
+
26
+ it "should map Ruby methods to Javascript on a specific object with a hash" do
27
+ subject.bind_method(:inspect, :object => "hello").should == true
28
+ subject.run("(inspect());").should == '"hello"'
29
+ end
30
+
11
31
  it "should accept arguments" do
12
32
  subject.bind_method(:echo)
13
33
  def subject.echo(i); i; end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Tomato bound objects" do
4
+ subject { Tomato.new }
5
+
6
+ it "should bind a Ruby class to an implicit chain" do
7
+ now = Time.now.to_i
8
+
9
+ subject.bind_object(Time)
10
+ subject.run("Time.at(#{now})").should == Time.at(now)
11
+ end
12
+
13
+ it "should bind a Ruby object to an explicit chain" do
14
+ time = Time.now
15
+ subject.bind_object(time, "current_time")
16
+ subject.run("current_time.to_s()").should == time.to_s
17
+ end
18
+
19
+ it "should bind a Ruby object's singleton methods" do
20
+ time = Time.now
21
+ def time.as_string; to_s; end
22
+ time.should respond_to(:as_string)
23
+
24
+ subject.bind_object(time, "current_time")
25
+ subject.run("current_time.as_string()").should == time.to_s
26
+ end
27
+
28
+ it "should bind a Ruby object to an implicit chain under 'ruby.[unqualified_object_class_name]'" do
29
+ time = Time.now
30
+ subject.bind_object(time)
31
+ subject.run("ruby.time.to_s()").should == time.to_s
32
+ end
33
+ end
@@ -1,9 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Tomato conversions" do
4
- context "from Ruby to JS to Ruby" do
5
- subject { Tomato.new }
4
+ subject { Tomato.new }
6
5
 
6
+ context "from JS to Ruby" do
7
+ it "should handle objects" do
8
+ pending "how to do it? JSON?"
9
+ end
10
+
11
+ it "should handle classes" do
12
+ pending "how to do it? Prototype?"
13
+ end
14
+
15
+ it "should handle modules" do
16
+ pending "how to do it? Prototype?"
17
+ end
18
+ end
19
+
20
+ context "from Ruby to JS to Ruby" do
7
21
  def handle(value)
8
22
  $test_value = value
9
23
  def subject.a_method; $test_value; end
@@ -27,18 +41,6 @@ describe "Tomato conversions" do
27
41
  handle(nil).should == nil
28
42
  end
29
43
 
30
- it "should handle objects" do
31
- pending "how to do it? JSON?"
32
- end
33
-
34
- it "should handle classes" do
35
- pending "how to do it? Prototype?"
36
- end
37
-
38
- it "should handle modules" do
39
- pending "how to do it? Prototype?"
40
- end
41
-
42
44
  it "should handle floats" do
43
45
  handle(1.05).should == 1.05
44
46
  end
@@ -87,28 +89,5 @@ describe "Tomato conversions" do
87
89
  it "should handle symbols" do
88
90
  handle(:symbol).should == :symbol
89
91
  end
90
- =begin
91
- case T_NIL :
92
- case T_OBJECT :
93
- //case T_CLASS :
94
- //case T_MODULE :
95
- case T_FLOAT :
96
- case T_STRING :
97
- case T_REGEXP :
98
- case T_ARRAY :
99
- case T_HASH :
100
- //case T_STRUCT :
101
- case T_BIGNUM :
102
- case T_FIXNUM :
103
- //case T_COMPLEX :
104
- //case T_RATIONAL:
105
- //case T_FILE :
106
- case T_TRUE :
107
- case T_FALSE :
108
- //case T_DATA :
109
- case T_SYMBOL :
110
- symbol_from(object)
111
- break;
112
- =end
113
92
  end
114
93
  end
data/t.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'lib/tomato'
2
+ t = Tomato.new
3
+ t.run("debug()")
4
+ t.bind_object(String, "rb.string")
5
+ t.run("debug(new rb.string());")
6
+ puts t.run("var v = new rb.string('Hi there!'); debug(v.to_s()); v;").inspect
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{tomato}
8
- s.version = "0.0.1.prealpha1"
8
+ s.version = "0.0.1.prealpha2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Colin MacKenzie IV"]
12
- s.date = %q{2010-06-25}
12
+ s.date = %q{2010-06-26}
13
13
  s.description = %q{Leverages Google's V8 JavaScript library to interface Ruby code with JavaScript code.}
14
14
  s.email = %q{sinisterchipmunk@gmail.com}
15
15
  s.extensions = ["ext/tomato/extconf.rb"]
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  "ext/tomato/IMPORTANT",
26
26
  "ext/tomato/Makefile",
27
27
  "ext/tomato/binding_methods.cpp",
28
+ "ext/tomato/binding_methods.h",
28
29
  "ext/tomato/binding_methods.o",
29
30
  "ext/tomato/conversions_to_js.cpp",
30
31
  "ext/tomato/conversions_to_js.o",
@@ -3330,6 +3331,8 @@ Gem::Specification.new do |s|
3330
3331
  "ext/tomato/external/v8/tools/windows-tick-processor.bat",
3331
3332
  "ext/tomato/external/v8/tools/windows-tick-processor.py",
3332
3333
  "ext/tomato/mkmf.log",
3334
+ "ext/tomato/object_chain.cpp",
3335
+ "ext/tomato/object_chain.o",
3333
3336
  "ext/tomato/tomato.bundle",
3334
3337
  "ext/tomato/tomato.cpp",
3335
3338
  "ext/tomato/tomato.h",
@@ -3339,10 +3342,13 @@ Gem::Specification.new do |s|
3339
3342
  "lib/tomato.rb",
3340
3343
  "pkg/tomato-0.0.1.gem",
3341
3344
  "pkg/tomato-0.0.1.prealpha1.gem",
3345
+ "spec/lib/bound_class_spec.rb",
3342
3346
  "spec/lib/bound_methods_spec.rb",
3347
+ "spec/lib/bound_object_spec.rb",
3343
3348
  "spec/lib/conversions_spec.rb",
3344
3349
  "spec/lib/tomato_spec.rb",
3345
3350
  "spec/spec_helper.rb",
3351
+ "t.rb",
3346
3352
  "tomato.gemspec"
3347
3353
  ]
3348
3354
  s.homepage = %q{http://www.thoughtsincomputation.com}
@@ -3351,7 +3357,9 @@ Gem::Specification.new do |s|
3351
3357
  s.rubygems_version = %q{1.3.7}
3352
3358
  s.summary = %q{Leverages Google's V8 JavaScript library to interface Ruby code with JavaScript code.}
3353
3359
  s.test_files = [
3354
- "spec/lib/bound_methods_spec.rb",
3360
+ "spec/lib/bound_class_spec.rb",
3361
+ "spec/lib/bound_methods_spec.rb",
3362
+ "spec/lib/bound_object_spec.rb",
3355
3363
  "spec/lib/conversions_spec.rb",
3356
3364
  "spec/lib/tomato_spec.rb",
3357
3365
  "spec/spec_helper.rb"
@@ -3362,11 +3370,14 @@ Gem::Specification.new do |s|
3362
3370
  s.specification_version = 3
3363
3371
 
3364
3372
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
3373
+ s.add_runtime_dependency(%q<sc-core-ext>, [">= 1.2.0"])
3365
3374
  s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
3366
3375
  else
3376
+ s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
3367
3377
  s.add_dependency(%q<rspec>, [">= 1.3.0"])
3368
3378
  end
3369
3379
  else
3380
+ s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
3370
3381
  s.add_dependency(%q<rspec>, [">= 1.3.0"])
3371
3382
  end
3372
3383
  end