tomato 0.0.1.prealpha1 → 0.0.1.prealpha2

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.
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