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.
@@ -20,44 +20,105 @@ hope to release it as a real gem after it's reached a sufficient level of functi
20
20
  When JS code is executed, it'll do its thing internally and then return the result:
21
21
 
22
22
  tomato.run("(1+1);") # => 2
23
+
24
+ ==== Bindings
25
+
26
+ You can bind Ruby methods to JavaScript, as well. You can bind an instance method of Tomato like so:
27
+
28
+ tomato.bind_method(:inspect)
29
+ tomato.run("inspect();") #=> "#<Tomato>"
23
30
 
24
- When JS code encounters an error of some kind, it'll get raised in Ruby:
31
+ Or, perhaps more usefully, you can bind an instance method of some other object:
25
32
 
26
- tomato.run("throw 'error';")
33
+ tomato.bind_method(:inspect, 5)
34
+ tomato.run("inspect();") #=> "5"
27
35
 
36
+ It's also easy to bind methods to arbitrary JavaScript objects. If a JS object in the chain doesn't exist, Tomato will
37
+ silently generate it for you on-the-fly:
38
+
39
+
40
+ def say(something)
41
+ puts something
42
+ end
43
+
44
+ tomato.bind_method(:say, self, :to => "person.mouth")
45
+
46
+ # Tomato generated both the "person" and "mouth" objects for us:
47
+ tomato.run "person.mouth.say('Hello there');"
48
+
28
49
  # Produces:
29
- #
30
- # Tomato::Error: (dynamic):error
31
- # throw 'error';
32
- # ^
33
50
  #
34
- # from (irb):3:in `run'
35
- # from (irb):3
51
+ # Hello there
52
+ # => nil
53
+
54
+ puts tomato.run("this")
36
55
 
37
- You can bind Ruby methods to JavaScript, as well. CURRENTLY only Tomato instance methods are supported. This will be
38
- extended in the near future to include any method on any object. Current implementation of method binding looks like
39
- this:
56
+ # Produces:
57
+ #
58
+ # {"person":{"mouth":{}}}
59
+ # => nil
40
60
 
41
- tomato.bind_method(:inspect)
42
- tomato.run("inspect();") #=> "#<Tomato>"
61
+ In the spirit of true Ruby dynamicism, (is that a word?), you can feel free to re-bind new methods over old ones:
62
+
63
+ tomato.bind_method(:say)
64
+ tomato.run("say('something');")
65
+ #=> error! say is not an instance method of Tomato
43
66
 
44
- For now (until a better method binding is implemented) you can add methods. You can do this before OR after binding it:
67
+ tomato.bind_method(:say, self)
68
+ def say(something); puts something; end
69
+ tomato.run("say('something');")
70
+ #=> something
71
+
72
+ You can also easily bind an entire object to JavaScript:
45
73
 
46
- tomato.bind_method(:say_hello)
47
- def tomato.say_hello
48
- puts "Hi there!"
74
+ # By default objects are mapped to 'ruby.[object_class_name]'
75
+ tomato.bind_object(Time.now)
76
+ tomato.run("ruby.time.to_s();")
77
+ #=> "2010-06-25 18:12:23 -0400"
78
+
79
+ # Or give it an object name or chain of names:
80
+ tomato.bind_object(Time.now, "time.current")
81
+ tomato.run("time.current.to_s();")
82
+ #=> "2010-06-25 18:12:23 -0400"
83
+
84
+ Even better, you can also bind a whole class and instantiate it from within JavaScript. If you return the object to
85
+ Ruby, it'll be seamlessly converted into the corresponding Ruby object.
86
+
87
+ class Person
88
+ def initialize(name)
89
+ @name = name
90
+ end
91
+ attr_accessor :name, :age
49
92
  end
50
- tomato.run("say_hello();")
93
+
94
+ tomato.bind_object(Person, "world.Person")
95
+ tomato.run <<-end_js
96
+ var colin = new world.Person("Colin");
97
+ colin.age = 25;
98
+ colin;
99
+ end_js
100
+
101
+ #=> #<Person:0x00000100dbbc50 @name="Colin", @age=25>
102
+
103
+ ==== Error Handling
104
+
105
+ When JS code encounters an error of some kind, it'll get raised in Ruby:
106
+
107
+ tomato.run("throw 'error';")
51
108
 
52
109
  # Produces:
110
+ #
111
+ # Tomato::Error: (dynamic):error
112
+ # throw 'error';
113
+ # ^
53
114
  #
54
- # Hi there!
55
- # => nil
115
+ # from (irb):3:in `run'
116
+ # from (irb):3
56
117
 
57
118
  You can also catch Ruby errors in JavaScript:
58
119
 
59
- tomato.bind_method(:raise_err)
60
- def tomato.raise_err
120
+ tomato.bind_method(:raise_err, self)
121
+ def raise_err
61
122
  raise ArgumentError, "Not what I meant!"
62
123
  end
63
124
  tomato.run("try { raise_err(); } catch(e) {}");
@@ -68,8 +129,8 @@ You can also catch Ruby errors in JavaScript:
68
129
 
69
130
  Or let them bubble up to Ruby:
70
131
 
71
- tomato.bind_method(:raise_err)
72
- def tomato.raise_err
132
+ tomato.bind_method(:raise_err, self)
133
+ def raise_err
73
134
  raise ArgumentError, "Not what I meant!"
74
135
  end
75
136
  begin
@@ -83,8 +144,6 @@ Or let them bubble up to Ruby:
83
144
  # Error: Not what I meant!
84
145
  # => nil
85
146
 
86
- That's all I've implemented so far, but I think it looks pretty cool.
87
-
88
147
  === Object Types
89
148
 
90
149
  Some objects in Ruby don't exist in JS. So far the ones I've implemented are Hashes and Symbols.
@@ -92,7 +151,17 @@ Some objects in Ruby don't exist in JS. So far the ones I've implemented are Has
92
151
  ==== Symbols
93
152
 
94
153
  A Symbol is converted to an object in JS with a "symbol" attribute and a "toString()" method. That same object, if
95
- returned to Ruby, is converted back to a Symbol.
154
+ returned to Ruby, is converted back to a Symbol. Examples:
155
+
156
+ def fetch_a_symbol
157
+ :a_symbol
158
+ end
159
+
160
+ t = Tomato.new
161
+ t.bind_method(:fetch_a_symbol, self)
162
+ t.run("fetch_a_symbol()") #=> :a_symbol
163
+ t.run("fetch_a_symbol().symbol") #=> "a_symbol"
164
+ t.run("fetch_a_symbol().toString()") #=> "a_symbol"
96
165
 
97
166
 
98
167
  ==== Hashes
@@ -127,11 +196,26 @@ That should be all there is to it.
127
196
 
128
197
  == Performance
129
198
 
130
- Too early to tell. Benchmarking will be done as I get closer to a real release.
199
+ ruby-1.9.1-p378 > javascript = <<-end_js
200
+ ruby-1.9.1-p378"> var str = "";
201
+ ruby-1.9.1-p378"> for (var i = 0; i < 1000000; i++)
202
+ ruby-1.9.1-p378"> str = 'a';
203
+ ruby-1.9.1-p378"> end_js
204
+ ruby-1.9.1-p378 > require 'benchmark'
205
+ ruby-1.9.1-p378 > require 'tomato'
206
+ ruby-1.9.1-p378 > t = Tomato.new
207
+ ruby-1.9.1-p378 >
208
+ ruby-1.9.1-p378 > # Setup complete, let's start the test
209
+ ruby-1.9.1-p378 > puts Benchmark.measure { for i in 1..1000000; str = 'a'; end }
210
+ ruby-1.9.1-p378 > 0.350000 0.000000 0.350000 ( 0.358106)
211
+ ruby-1.9.1-p378 >
212
+ ruby-1.9.1-p378 > puts Benchmark.measure { t.run(javascript) }
213
+ ruby-1.9.1-p378 > 0.010000 0.000000 0.010000 ( 0.012603)
214
+
215
+ 'Nuff said.
131
216
 
132
- Since V8's JavaScript code is compiled into byte code, this MAY (untested) have a positive impact on performance
133
- in some applications. Just translate your code to JavaScript and let V8 compile it to byte code. I'm hoping for some
134
- cool results.
217
+ Since V8's JavaScript code is compiled into byte code, this can have a positive impact on performance
218
+ in some applications. Just translate your code to JavaScript and let Tomato compile it into byte code.
135
219
 
136
220
  == Note on Patches/Pull Requests
137
221
 
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ begin
11
11
  gem.email = "sinisterchipmunk@gmail.com"
12
12
  gem.homepage = "http://www.thoughtsincomputation.com"
13
13
  gem.authors = ["Colin MacKenzie IV"]
14
+ gem.add_dependency "sc-core-ext", ">= 1.2.0"
14
15
  gem.add_development_dependency "rspec", ">= 1.3.0"
15
16
  gem.files = FileList['**/*']
16
17
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1.prealpha1
1
+ 0.0.1.prealpha2
@@ -4,19 +4,17 @@ SHELL = /bin/sh
4
4
  #### Start of system configuration section. ####
5
5
 
6
6
  srcdir = .
7
- topdir = /Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1
8
- hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1
9
- arch_hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.2-preview3/include/ruby-1.9.1/$(arch)
7
+ topdir = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1
8
+ hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1
9
+ arch_hdrdir = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/$(arch)
10
10
  VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
11
- prefix = $(DESTDIR)/Users/colin/.rvm/rubies/ruby-1.9.2-preview3
12
- rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
11
+ prefix = $(DESTDIR)/Users/colin/.rvm/rubies/ruby-1.9.1-p378
13
12
  exec_prefix = $(prefix)
14
13
  vendorhdrdir = $(rubyhdrdir)/vendor_ruby
15
14
  sitehdrdir = $(rubyhdrdir)/site_ruby
16
- rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
17
- vendordir = $(rubylibprefix)/vendor_ruby
18
- sitedir = $(rubylibprefix)/site_ruby
19
- ridir = $(datarootdir)/$(RI_BASE_NAME)
15
+ rubyhdrdir = $(includedir)/$(RUBY_INSTALL_NAME)-$(ruby_version)
16
+ vendordir = $(libdir)/$(RUBY_INSTALL_NAME)/vendor_ruby
17
+ sitedir = $(libdir)/$(RUBY_INSTALL_NAME)/site_ruby
20
18
  mandir = $(datarootdir)/man
21
19
  localedir = $(datarootdir)/locale
22
20
  libdir = $(exec_prefix)/lib
@@ -36,7 +34,7 @@ datarootdir = $(prefix)/share
36
34
  libexecdir = $(exec_prefix)/libexec
37
35
  sbindir = $(exec_prefix)/sbin
38
36
  bindir = $(exec_prefix)/bin
39
- rubylibdir = $(rubylibprefix)/$(ruby_version)
37
+ rubylibdir = $(libdir)/$(ruby_install_name)/$(ruby_version)
40
38
  archdir = $(rubylibdir)/$(arch)
41
39
  sitelibdir = $(sitedir)/$(ruby_version)
42
40
  sitearchdir = $(sitelibdir)/$(sitearch)
@@ -53,35 +51,34 @@ OUTFLAG = -o
53
51
  COUTFLAG = -o
54
52
 
55
53
  RUBY_EXTCONF_H =
56
- cflags = $(optflags) $(debugflags) $(warnflags)
57
- optflags = -O3
58
- debugflags = -ggdb
59
- warnflags = -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long
60
- CFLAGS = -fno-common $(cflags) -fno-common -pipe
54
+ cflags = $(optflags) $(debugflags) $(warnflags)
55
+ optflags = -O2
56
+ debugflags = -g
57
+ warnflags = -Wall -Wno-parentheses
58
+ CFLAGS = -fno-common $(cflags) -fno-common -pipe -fno-common
61
59
  INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) -I/Users/colin/projects/gems/tomato/ext/tomato/external/build/v8/include
62
60
  DEFS =
63
- CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags) -Wall -g -rdynamic
64
- CXXFLAGS = $(CFLAGS) $(cxxflags)
61
+ CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags) -Wall -g -rdynamic
62
+ CXXFLAGS = $(CFLAGS) $(cxxflags)
65
63
  ldflags = -L. -L/usr/local/lib
66
- dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -Wl,-flat_namespace
67
- ARCH_FLAG =
68
- DLDFLAGS = $(ldflags) $(dldflags)
69
- LDSHARED = $(CC) -dynamic -bundle
70
- LDSHAREDXX = $(CXX) -dynamic -bundle
64
+ dldflags =
65
+ archflag =
66
+ DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
67
+ LDSHARED = cc -dynamic -bundle -undefined suppress -flat_namespace
68
+ LDSHAREDXX = $(LDSHARED)
71
69
  AR = ar
72
70
  EXEEXT =
73
71
 
74
- RUBY_BASE_NAME = ruby
75
72
  RUBY_INSTALL_NAME = ruby
76
- RUBY_SO_NAME = ruby.1.9.1
77
- arch = x86_64-darwin10.4.0
78
- sitearch = $(arch)
73
+ RUBY_SO_NAME = ruby
74
+ arch = i386-darwin10.4.0
75
+ sitearch = i386-darwin10.4.0
79
76
  ruby_version = 1.9.1
80
- ruby = /Users/colin/.rvm/rubies/ruby-1.9.2-preview3/bin/ruby
77
+ ruby = /Users/colin/.rvm/rubies/ruby-1.9.1-p378/bin/ruby
81
78
  RUBY = $(ruby)
82
79
  RM = rm -f
83
80
  RM_RF = $(RUBY) -run -e rm -- -rf
84
- RMDIRS = rmdir -p
81
+ RMDIRS = $(RUBY) -run -e rmdir -- -p
85
82
  MAKEDIRS = mkdir -p
86
83
  INSTALL = /usr/bin/install -c
87
84
  INSTALL_PROG = $(INSTALL) -m 0755
@@ -104,9 +101,9 @@ extout =
104
101
  extout_prefix =
105
102
  target_prefix =
106
103
  LOCAL_LIBS =
107
- LIBS = $(LIBRUBYARG_SHARED) -lobjc -lpthread -lpthread -ldl -lobjc -lv8
108
- SRCS = binding_methods.cpp conversions_to_js.cpp conversions_to_rb.cpp errors.cpp tomato.cpp v8.cpp
109
- OBJS = binding_methods.o conversions_to_js.o conversions_to_rb.o errors.o tomato.o v8.o
104
+ LIBS = $(LIBRUBYARG_SHARED) -lobjc -lpthread -lpthread -ldl -lobjc -lv8
105
+ SRCS = binding_methods.cpp conversions_to_js.cpp conversions_to_rb.cpp errors.cpp object_chain.cpp tomato.cpp v8.cpp
106
+ OBJS = binding_methods.o conversions_to_js.o conversions_to_rb.o errors.o object_chain.o tomato.o v8.o
110
107
  TARGET = tomato
111
108
  DLLIB = $(TARGET).bundle
112
109
  EXTSTATIC =
@@ -125,8 +122,6 @@ CLEANOBJS = *.o *.bak
125
122
 
126
123
  all: $(DLLIB)
127
124
  static: $(STATIC_LIB)
128
- .PHONY: all install static install-so install-rb
129
- .PHONY: clean clean-so clean-rb
130
125
 
131
126
  clean-rb-default::
132
127
  clean-rb::
@@ -148,8 +143,7 @@ install: install-so install-rb
148
143
  install-so: $(RUBYARCHDIR)
149
144
  install-so: $(RUBYARCHDIR)/$(DLLIB)
150
145
  $(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
151
- @-$(MAKEDIRS) $(@D)
152
- $(INSTALL_PROG) $(DLLIB) $(@D)
146
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
153
147
  install-rb: pre-install-rb install-rb-default
154
148
  install-rb-default: pre-install-rb-default
155
149
  pre-install-rb: Makefile
@@ -185,14 +179,15 @@ $(DLLIB): $(OBJS) Makefile
185
179
 
186
180
 
187
181
  ###
188
- binding_methods.o: binding_methods.cpp tomato.h
182
+ binding_methods.o: binding_methods.cpp tomato.h binding_methods.h
189
183
  conversions_to_js.o: conversions_to_js.cpp tomato.h
190
184
  conversions_to_rb.o: conversions_to_rb.cpp tomato.h
191
185
  errors.o: errors.cpp tomato.h
186
+ object_chain.o: object_chain.cpp tomato.h binding_methods.h
192
187
  tomato.o: tomato.cpp tomato.h
193
188
  v8.o: v8.cpp tomato.h
194
189
 
195
190
  test: all
196
191
  @echo Running specs...
197
- spec -O spec/spec.opts spec
192
+ spec spec -c
198
193
 
@@ -1,9 +1,7 @@
1
1
  #include "tomato.h"
2
+ #include "binding_methods.h"
2
3
 
3
4
  static VALUE bound_method_call(VALUE args);
4
- static Handle<Value> bound_method(const Arguments& args);
5
- static int store_rb_message(const Arguments &args, V8Tomato **out_tomato, VALUE *out_receiver, ID *out_method_id);
6
-
7
5
 
8
6
  Handle<Value> bound_method(const Arguments& args)
9
7
  {
@@ -13,12 +11,17 @@ Handle<Value> bound_method(const Arguments& args)
13
11
  ID rb_method_id;
14
12
  V8Tomato *tomato;
15
13
  int code = store_rb_message(args, &tomato, &receiver, &rb_method_id);
16
- if (code == -1)
17
- return ThrowException(String::New("Error: _tomato is not an object (BUG: please report)"));
14
+ switch(code)
15
+ {
16
+ case -1: return ThrowException(String::New("Error: _tomato is not an object (BUG: please report)"));
17
+ case -2: return ThrowException(String::New("Error: _tomato_receiver_index is not an Int32 (BUG: please report)"));
18
+ case -3: return ThrowException(String::New("Error: _tomato_receiver_index is greater than @receivers.length (BUG: please report)"));
19
+ };
18
20
 
19
- VALUE rbargs = rb_ary_new2(2);
21
+ VALUE rbargs = rb_ary_new2(2+args.Length());
20
22
  rb_ary_store(rbargs, 0, receiver);
21
23
  rb_ary_store(rbargs, 1, ID2SYM(rb_method_id));
24
+ store_args(tomato, rbargs, args);
22
25
 
23
26
  int error;
24
27
  VALUE result = rb_protect(bound_method_call, rbargs, &error);
@@ -30,27 +33,50 @@ Handle<Value> bound_method(const Arguments& args)
30
33
  return js_value_of(tomato, result);
31
34
  }
32
35
 
36
+ void store_args(V8Tomato *tomato, VALUE rbargs, const Arguments &args)
37
+ {
38
+ int length = args.Length();
39
+ int offset = RARRAY_LEN(rbargs);
40
+ for (int i = 0; i < length; i++)
41
+ rb_ary_store(rbargs, offset+i, ruby_value_of(tomato, args[i]));
42
+ }
43
+
33
44
  static VALUE bound_method_call(VALUE args)
34
45
  {
35
- VALUE receiver = rb_ary_entry(args, 0);
36
- VALUE rb_method_id = SYM2ID(rb_ary_entry(args, 1));
37
-
38
- VALUE result = rb_funcall(receiver, rb_method_id, 0);
39
-
46
+ VALUE receiver = rb_ary_shift(args);
47
+ VALUE rb_method_id = SYM2ID(rb_ary_shift(args));
48
+ VALUE result = rb_funcall2(receiver, rb_method_id, RARRAY_LEN(args), RARRAY_PTR(args));
40
49
  return result;
41
50
  }
42
51
 
43
52
  int store_rb_message(const Arguments &args, V8Tomato **out_tomato, VALUE *out_receiver, ID *out_method_id)
44
53
  {
54
+ // get the function
45
55
  Handle<Function> function = args.Callee();
46
- Handle<String> _tomato = String::New("_tomato");
47
- if (!function->Get(_tomato)->IsExternal())
48
- return -1;
56
+
57
+ // pull the binding data from the function (stored there by fTomato_bind_method)
58
+ Local<Value> v8_tomato = function->Get(String::New("_tomato"));
59
+ Local<Value> v8_receiver_index = function->Get(String::New("_tomato_receiver_index"));
60
+
61
+ // make sure the data is what we expect it to be
62
+ if (!v8_tomato->IsExternal()) return -1;
63
+ if (!v8_receiver_index->IsInt32()) return -2;
49
64
 
50
- V8Tomato *tomato = (V8Tomato *)Local<External>::Cast(function->Get(_tomato))->Value();
51
- VALUE receiver = tomato->rb_instance;
65
+ // find the tomato
66
+ V8Tomato *tomato = (V8Tomato *)Local<External>::Cast(v8_tomato)->Value();
67
+
68
+ // find the receiver index, and make sure it's a valid index
69
+ int receiver_index = v8_receiver_index->Int32Value();
70
+ VALUE receivers = rb_iv_get(tomato->rb_instance, "@receivers"); //rb_funcall(tomato->rb_instance, rb_intern("receivers"));
71
+ if (RARRAY_LEN(receivers) < receiver_index) return -3;
72
+
73
+ // get the receiver
74
+ VALUE receiver = (RARRAY_PTR(receivers)[receiver_index]);
75
+
76
+ // get the method name from the function name
52
77
  String::Utf8Value method_name(function->GetName());
53
78
 
79
+ // store the output and return success.
54
80
  *out_tomato = tomato;
55
81
  *out_method_id = rb_intern(ToCString(method_name));
56
82
  *out_receiver = receiver;
@@ -59,22 +85,23 @@ int store_rb_message(const Arguments &args, V8Tomato **out_tomato, VALUE *out_re
59
85
 
60
86
  VALUE fTomato_bind_method(int argc, VALUE *argv, VALUE self)
61
87
  {
62
- if (argc != 1) rb_raise(rb_eArgError, "Expected name of method");
88
+ if (argc != 3) rb_raise(rb_eArgError,
89
+ "Expected: _bind_method(method_name<str>, receiver_index<int>, object_chain<array[string] or nil>)");
63
90
  const char *method_name = rb_id2name(rb_to_id(argv[0]));
64
91
 
65
92
  V8Tomato *tomato;
66
93
  Data_Get_Struct(self, V8Tomato, tomato);
67
94
 
68
95
  HandleScope handle_scope;
69
- Context::Scope context_scope(tomato->context);
70
- Handle<Value> value = tomato->context->Global();
96
+ Context::Scope context_scope(tomato->context);
97
+ Handle<Value> value = find_or_create_object_chain(tomato, argv[2]);
71
98
  if (value->IsObject())
72
99
  {
73
100
  Handle<Object> object = Handle<Object>::Cast(value);
74
- Handle<Value> proto_value = object->GetPrototype();
75
101
  Handle<Function> function = FunctionTemplate::New(bound_method)->GetFunction();
76
102
 
77
103
  function->Set(String::New("_tomato"), External::New(tomato));
104
+ function->Set(String::New("_tomato_receiver_index"), Int32::New(FIX2INT(argv[1])));
78
105
  function->SetName(String::New(method_name));
79
106
 
80
107
  object->Set(String::New(method_name), function);