h8 0.4.8 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae98780cc495a9c2f15db1b3de81f81cc014978b
4
- data.tar.gz: ee00bd70c38b2f4b62ecf8beddb2e15fb6aacb61
3
+ metadata.gz: 1f5c5adc5319ca033ede79775cb4c46f72afff6c
4
+ data.tar.gz: fddfbb454e2e80d137012fe3c1b62a58712cded2
5
5
  SHA512:
6
- metadata.gz: 4a2be9806423de66eacb312891d1abcff2af0f24679e740af339689cae1ae50fd8dbb07d15d138fc13976bbb410b3acf92951d020bcb96df93c16ab9a5fd3b20
7
- data.tar.gz: 85f4398e82fc5a818654936cdb2b0fe5b51ee64c8e08284d131657d4f89c90b55585fbb8fc052dfe673a8371ce9ad7f69ab4a440c3d8301ec5a23d5777981250
6
+ metadata.gz: aa01ac0f07a92865c81034f4d2d983b85c7a2de368f146556568dbf2d775ed15e44be34bd83f84f1e1a5bf904728a004833858f9171355859a2a6ba9e72d2d11
7
+ data.tar.gz: 88e68a141d22a7a0f4eeab6e3d84af9d13e8d38408ae630a2e0d1b8dbd15b4f05697eec0fd30e3edfc4486a4a2bab28e02134a0194589338cf8239b92d989688
data/README.md CHANGED
@@ -1,59 +1,57 @@
1
1
  # Hybrid8, aka H8
2
2
 
3
- _Warning_ this gem is a public beta at the moment - beta testers are welcome!It means, it is not
4
- yet production stable - we haven't yet tried.
3
+ _Warning_ this gem is in public beta at the moment - beta testers are welcome! It means it's not
4
+ yet stable enough for running in production - we haven't tried it yet ourselves.
5
5
 
6
- _Current implementation is somewhat slower than it could by the price of letting ruby threads
7
- and javascript code in different H8::Context instances run in parallel in multicore hardware_.
8
- Let me know whether it worth degraded performance on ruby-to-js and back calls. In other word,
9
- in 8-core you can create 8 H8::Contexts and run them truly in parallel.
6
+ _Current implementation is somewhat slower than it could be, with the price of letting ruby threads
7
+ and javascript code in different H8::Context instances run in parallel on multicore hardware_.
8
+ Please let me know if it significantly degrades performance on ruby-to-js calls and back. In other words,
9
+ with 8-core cpu you can create 8 H8::Contexts and run them truly parallel.
10
10
 
11
11
  This gem was intended to replace therubyracer for many reasons:
12
12
 
13
- * therubyracer had critical bugs (that was hte reason for this) that are not fixed for a long time,
14
- under load it produces numerous frequent random crashes.
13
+ * therubyracer has/had critical bugs that are not being fixed for a long time. As the result it produces numerous and frequent random crashes under load.
15
14
 
16
- * therubyracer still uses antique version of V8, H8 uses the latest 3.31 branch, which has manu
15
+ * therubyracer still uses antique version of V8. H8 uses the latest 3.31 branch, which has many
17
16
  improvements, harmony support and so on.
18
17
 
19
- * H8 is designed to provide tight and integration of two allocation systems and object models,
20
- passing the same objects between different systems wrapping and unwrapping them rather than copying
21
- and changing them.
18
+ * H8 is designed to provide tight integration between two allocation systems and object models,
19
+ passing the same objects within different systems, wrapping and unwrapping them rather than copying
20
+ and changing.
22
21
 
23
- * We hope that by the cost non significant changes we will provide faster execution. And might v8
24
- modern branch will help us with it ;)
22
+ * We hope to provide faster execution with a tradeoff in non-significant changes. And let the modern branch of V8
23
+ help us with it ;)
25
24
 
26
25
  Special features:
27
26
 
28
- - care about wrapped Ruby objects lifespan (are kept until either ruby or javascript context
29
- reference wrapped object). In no way GC will not reclaim ruby object that is in use by the
30
- javascript context
27
+ - H8 takes care of wrapped Ruby objects lifespan. Objects live until either ruby or javascript context
28
+ references them. GC will not reclaim memory occupied by ruby object while it is used by javascript context.
31
29
 
32
- - care about wrapped Javascript objects lifetime the same. Referenced JS items will not be recycled
33
- while there are ruby objects referencing it. It also means that once created H8::Context will not
34
- be reclaimed as long as there is at least one active wrapped object returned from the script.
30
+ - H8 takes care of wrapped Javascript objects lifetime in the same way. Referenced JS items will not be recycled
31
+ while there are ruby objects referencing them. It also means that once created H8::Context will not
32
+ be reclaimed as long as there is at least one active wrapped object returned from a script.
35
33
 
36
- - you can pass ruby objects from ruby code called from javascript back to the ruby code intact.
34
+ - You can pass ruby objects from ruby code called from javascript back to the ruby code intact.
37
35
  Ruby objects are automatically wrapped in js code and unwrapped in ruby code (you might need to
38
- call #to_ruby)
36
+ call #to_ruby).
39
37
 
40
- - Uncaught ruby exceptions are thrown as javascript exceptions in javascript code. The same,
41
- uncaught javascript exceptions raise ruby error in ruby code.
38
+ - Uncaught ruby exceptions are thrown as javascript exceptions in javascript code. Likewise,
39
+ uncaught javascript exceptions raise ruby errors in ruby code.
42
40
 
43
- - Integrated CoffeeScript support
41
+ - Integrated CoffeeScript support.
44
42
 
45
- - H8 is thread safe. It releases gvl lock while executing ruby code and unlocks v8 isolate when
46
- calling ruby code thus allow maximum possibile parallel execution of ruby and javascript code
43
+ - H8 is thread safe. It releases gvl lock while executing ruby code and unlocks V8 isolate when
44
+ calling ruby code thus maximally allows the possibility of parallel execution of ruby and javascript code
47
45
  in separate threads.
48
46
 
49
- Due to v8 and ruby MRI limitations, only one ruby thread can access any given H8::Context (e.g.
50
- execute javascript code in it), and, as usual, all ruby threads are locked by a single mitex (gvl).
51
- Still, having multiple H8::Context in different ruby threads let you run many java/coffee scripts in
47
+ Due to V8 and ruby MRI limitations, only one ruby thread can access any given H8::Context (e.g.
48
+ execute javascript code in it) and, as usual, all ruby threads are locked by a single mutex (gvl).
49
+ Still, having multiple H8::Context in different ruby threads lets you run many java/coffee scripts in
52
50
  parallel with a single ruby thread - what you can not have with pure MRI ruby.
53
51
 
54
- ## Main difference from therubyracer/features not ready
52
+ ## Main differences from therubyracer and future features
55
53
 
56
- - lambda/proc passed as var to the context **does not receives first (this) argument
54
+ - lambda/proc passed as var to the context **does not receive first (this) argument
57
55
  automatically!**
58
56
 
59
57
  E.g. rubyracer code
@@ -64,20 +62,19 @@ Becomes
64
62
 
65
63
  cxt[:fn] = -> (a, b) { a + b }
66
64
 
67
- it looks prettier, doesn't it? And, if you really need `this` in the callable, just mention it in
65
+ Looks nicer, doesn't it? And if you really need `this` in the callable, just mention it in
68
66
  the call:
69
67
 
70
68
  cxt[:fn] = -> (this, a, b) { a + b + this.offset }
71
69
  cxt.eval 'fn(this, 10, 20)'
72
70
 
73
- This, again, is done for execution speed. Always wrapping `this` to pass it to ruby is a costly
74
- procedure which is always performed in rubyracer - no matter is it really needed. In H8 you can
75
- spend your resources only when it worth extra processing. From my experience, it is a rare situation
76
- when such a lambda needs javascript's this - but, no problem, pass it this, or whatever else
77
- you need ;)
71
+ This, again, is done to improve execution speed. Always wrapping `this` to pass it to ruby is a costly
72
+ procedure which is always performed in rubyracer - no matter if it is really needed or not. In H8 you can
73
+ spend your resources only when it is worth extra processing. From my experience, it is a rare situation
74
+ when such a lambda needs javascript's this - but you still have the possibility if you need it ;)
78
75
 
79
- - there is no 'Context object initialization' - it does not work well in rubyracer so it is not
80
- likely used widely. We can add it later, though - if you need it, add an issue.
76
+ - there is no 'Context object initialization'. It does not work well in rubyracer, so it's not
77
+ likely that it's widely used. We can add it later, though - if you need it, add an issue.
81
78
 
82
79
 
83
80
 
@@ -85,14 +82,13 @@ likely used widely. We can add it later, though - if you need it, add an issue.
85
82
 
86
83
  ### Prerequisites
87
84
 
88
- You should have installed libv8, use latest version with v8::Isolate and v8::Locker. This version
89
- may not find you installation, contact me if you have problems, I'll tune it up.
85
+ You should have libv8 installed, use the latest version with v8::Isolate and v8::Locker. It might not find your installation, contact me if you have problems - I'll tune it up.
90
86
 
91
87
  #### Macos (10.9 maybe+)
92
88
 
93
89
  The working process:
94
90
 
95
- install v8 from sources 3.31.77 (or try newer), then execute:
91
+ install V8 from sources 3.31.77 (or try newer), then execute:
96
92
 
97
93
  gclient update
98
94
  export CXXFLAGS='-std=c++11 -stdlib=libc++ -mmacosx-version-min=10.9'
@@ -100,16 +96,16 @@ install v8 from sources 3.31.77 (or try newer), then execute:
100
96
  make native
101
97
  export V8_3_31_ROOT=`pwd` # or somehow else set it
102
98
 
103
- Note that exporting symbols is a hack that may not be in need anymore. After that the gem should
99
+ Note that exporting symbols is a hack that may not be needed anymore. After that the gem should
104
100
  install normally.
105
101
 
106
102
  #### Debian and like
107
103
 
108
- Install first a valid v8 version. We provide a ready package!
104
+ First install a valid v8 version. We provide a ready-to-use package!
109
105
 
110
106
  sudo apt-get install libv8-3.31-dev
111
107
 
112
- Usually it is all you need. Rarely, You might also need to install GMP.
108
+ Usually it's all you need. In rare cases you might also need to install GMP.
113
109
 
114
110
  ### Setting up
115
111
 
@@ -127,14 +123,14 @@ Or install it yourself as:
127
123
 
128
124
  ## Usage
129
125
 
130
- Is generally like therubyracer gem. Create context, set variables, run scripts.
126
+ Is's generally like therubyracer gem. Create context, set variables, run scripts.
131
127
 
132
128
  require 'h8'
133
129
 
134
130
  res = H8::Context.eval "({foo: 'hello', bar: 'world'});"
135
131
  puts "#{res.foo} #{res.bar}"
136
132
 
137
- another way to access attributes:
133
+ Another way to access attributes:
138
134
 
139
135
  puts res['foo']
140
136
 
@@ -153,7 +149,7 @@ You can return function and call it from ruby:
153
149
  fun = cxt.eval "(function pi_n(n) { return pi * n; })"
154
150
  p fun(2)
155
151
 
156
- The same you can return objects and call its member functions - if a member is a function,
152
+ Likewise, you can return objects and call it's member functions. If a member is a function,
157
153
  it will be called with given arguments:
158
154
 
159
155
  res = H8::Context.eval <<-End
@@ -173,7 +169,7 @@ it will be called with given arguments:
173
169
 
174
170
  ## Contributing
175
171
 
176
- Note that at this early point of development you better first talk to me to not to reinvent the
172
+ Note that at this early point of development it's better that you talk to me first to not to reinvent the
177
173
  wheel.
178
174
 
179
175
  1. Fork it ( https://github.com/[my-github-username]/hybrid8/fork )
data/Rakefile CHANGED
@@ -1,2 +1,24 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rake/extensiontask"
3
+ require 'rubygems/package_task'
4
+
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ $gemspec = Bundler::GemHelper.gemspec
14
+
15
+ Gem::PackageTask.new($gemspec) do |pkg|
16
+ end
17
+
18
+ Rake::ExtensionTask.new "h8", $gemspec do |ext|
19
+ ext.lib_dir = "lib/h8"
20
+ ext.source_pattern = "*.{c,cpp,js}"
21
+ ext.gem_spec = $gemspec
22
+ end
23
+
2
24
 
@@ -17,3 +17,11 @@ Moreover, if you run optimized C++ version, you'll have:
17
17
  which is, in turn, *1.76 times slower than coffeescript!*
18
18
 
19
19
  The results are very inspiring, as for me.
20
+
21
+ ## Some ruby platform comarisons
22
+
23
+ ruby 2.1.5 : 13.767739 scaled: 68.838695
24
+ ruby 2.2.0 : 12.859456 scaled: 64.29728
25
+ rbx 2.1.0 : 9.35995 scaled: 46.799749999999996
26
+
27
+ Rbx, as expected, is way ahead of MRI but is still too bad to be compared to coffeescript/v8(h8)
@@ -0,0 +1,56 @@
1
+ require './tools'
2
+
3
+ class Solver
4
+
5
+ def initialize rank
6
+ @n, @nn = rank, rank*rank
7
+ @n2 = @n+@n
8
+ @desk = []
9
+ @n.times { @desk << [0] * @n }
10
+ @depth = 0
11
+ solve 0, 0
12
+ end
13
+
14
+ def solve r, c
15
+ @desk[r][c] = (@depth+=1)
16
+ return true if @depth >= @nn
17
+ moves(r, c) { |r1, c1|
18
+ return true if solve(r1, c1)
19
+ }
20
+ @desk[r][c] = 0
21
+ @depth -= 1
22
+ false
23
+ end
24
+
25
+ @@shifts = [[-2, +1], [-1, +2], [+1, +2], [+2, +1], [+2, -1], [+1, -2], [-1, -2], [-2, -1]]
26
+
27
+ def moves r, c
28
+ @@shifts.each { |sr, sc|
29
+ r1 = r + sr
30
+ if r1 >= 0 && r1 < @n
31
+ c1 = c + sc
32
+ if c1 >= 0 && c1 < @n
33
+ yield r1, c1 if @desk[r1][c1] == 0
34
+ end
35
+ end
36
+ }
37
+ end
38
+
39
+ def to_s
40
+ res = []
41
+ @n.times do |row|
42
+ res << @n.times.map { |col|
43
+ d = @desk[row][col]
44
+ d == 0 ? ' .' : ("%3d" % d)
45
+ }.join('')
46
+ end
47
+ res.join "\n"
48
+ end
49
+
50
+ end
51
+
52
+ if __FILE__ == $0
53
+ timing "#{RUBY_ENGINE} #{RUBY_VERSION}", 1, 5 do
54
+ Solver.new 7
55
+ end
56
+ end
@@ -1,52 +1,17 @@
1
1
  require './tools'
2
+ require './km'
2
3
 
3
- class Solver
4
4
 
5
- def initialize rank
6
- @n, @nn = rank, rank*rank
7
- @n2 = @n+@n
8
- @desk = []
9
- @n.times { @desk << [0] * @n }
10
- @depth = 0
11
- solve 0, 0
12
- end
13
-
14
- def solve r, c
15
- @desk[r][c] = (@depth+=1)
16
- return true if @depth >= @nn
17
- moves(r, c) { |r1, c1|
18
- return true if solve(r1, c1)
19
- }
20
- @desk[r][c] = 0
21
- @depth -= 1
22
- false
23
- end
24
-
25
- @@shifts = [[-2, +1], [-1, +2], [+1, +2], [+2, +1], [+2, -1], [+1, -2], [-1, -2], [-2, -1]]
26
-
27
- def moves r, c
28
- @@shifts.each { |sr, sc|
29
- r1 = r + sr
30
- if r1 >= 0 && r1 < @n
31
- c1 = c + sc
32
- if c1 >= 0 && c1 < @n
33
- yield r1, c1 if @desk[r1][c1] == 0
34
- end
35
- end
36
- }
37
- end
38
-
39
- def to_s
40
- res = []
41
- @n.times do |row|
42
- res << @n.times.map { |col|
43
- d = @desk[row][col]
44
- d == 0 ? ' .' : ("%3d" % d)
45
- }.join('')
46
- end
47
- res.join "\n"
48
- end
5
+ def js_context
6
+ cxt = H8::Context.new
7
+ cxt[:print] = -> (*args) { puts args.join(' ') }
8
+ cxt[:console] = Console
9
+ cxt
10
+ end
49
11
 
12
+ def coffee script_file_name
13
+ @base ||= File.dirname(File.expand_path(__FILE__))
14
+ H8::Coffee.compile open("#{@base}/#{script_file_name}.coffee").read
50
15
  end
51
16
 
52
17
  cs = js_context.eval coffee(:knightsmove)
@@ -1,4 +1,4 @@
1
- require 'h8'
1
+ # require 'h8'
2
2
 
3
3
  def timing name, repetitions = 1, scale = 1
4
4
  s = Time.now
@@ -20,14 +20,3 @@ class Console
20
20
  end
21
21
  end
22
22
 
23
- def js_context
24
- cxt = H8::Context.new
25
- cxt[:print] = -> (*args) { puts args.join(' ') }
26
- cxt[:console] = Console
27
- cxt
28
- end
29
-
30
- def coffee script_file_name
31
- @base ||= File.dirname(File.expand_path(__FILE__))
32
- H8::Coffee.compile open("#{@base}/#{script_file_name}.coffee").read
33
- end
@@ -47,6 +47,13 @@ void h8::JsTimeoutError::raise() const {
47
47
  rb_raise(js_timeout_exception, "timeout expired");
48
48
  }
49
49
 
50
+ void h8::H8::SetupGateTemplate(const Local<ObjectTemplate>& templ) {
51
+ templ->SetInternalFieldCount(2);
52
+ templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
53
+ templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet, 0, RubyGate::mapDelete);
54
+ templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
55
+ }
56
+
50
57
  h8::H8::H8()
51
58
  : self(Qnil)
52
59
  {
@@ -66,11 +73,7 @@ h8::H8::H8()
66
73
  ft->SetClassName(js("RubyGate"));
67
74
  Local<ObjectTemplate> templ = ft->InstanceTemplate();
68
75
 
69
- templ->SetInternalFieldCount(2);
70
- templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
71
- templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
72
- templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
73
-
76
+ SetupGateTemplate(templ);
74
77
  v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL,
75
78
  global);
76
79
 
@@ -122,12 +125,7 @@ void h8::H8::gate_class(VALUE name,VALUE callable) {
122
125
  ft->Inherit(getGateFunctionTemplate());
123
126
 
124
127
  Local<ObjectTemplate> templ = ft->InstanceTemplate();
125
-
126
- templ->SetInternalFieldCount(2);
127
- templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
128
- templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
129
- templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
130
-
128
+ SetupGateTemplate(templ);
131
129
  global->Set(class_name, ft->GetFunction());
132
130
  }
133
131
 
@@ -19,7 +19,7 @@ extern VALUE value_class;
19
19
  extern VALUE ruby_gate_class;
20
20
  extern VALUE Rundefined;
21
21
 
22
- extern ID id_is_a, id_safe_call, id_safe_proc_call;
22
+ extern ID id_is_a, id_safe_call, id_safe_proc_call, id_delete_handler;
23
23
 
24
24
  VALUE protect_ruby(const std::function<VALUE()> &block);
25
25
 
@@ -233,6 +233,7 @@ public:
233
233
  private:
234
234
  friend VALUE h8::context_alloc(VALUE klass);
235
235
  void invoke(v8::Handle<v8::Script> script, Local<Value>& result);
236
+ static void SetupGateTemplate(const Local<ObjectTemplate>& templ);
236
237
 
237
238
  Isolate *isolate;
238
239
  VALUE self;
@@ -8,6 +8,8 @@
8
8
  #ifndef JSCATCHER_H_
9
9
  #define JSCATCHER_H_
10
10
 
11
+ #include <functional>
12
+
11
13
  #include <include/v8.h>
12
14
  #include "h8.h"
13
15
 
@@ -14,7 +14,7 @@ VALUE ruby_gate_class;
14
14
  VALUE value_class;
15
15
  VALUE Rundefined;
16
16
 
17
- ID id_is_a, id_safe_call, id_safe_proc_call;
17
+ ID id_is_a, id_safe_call, id_safe_proc_call, id_delete_handler;
18
18
 
19
19
  VALUE protect_ruby(const std::function<VALUE()> &block) {
20
20
  try {
@@ -189,6 +189,7 @@ void Init_h8(void) {
189
189
  id_is_a = rb_intern("is_a?");
190
190
  id_safe_call = rb_intern("secure_call");
191
191
  id_safe_proc_call = rb_intern("safe_proc_call");
192
+ id_delete_handler = rb_intern("delete_handler");
192
193
 
193
194
  VALUE h8 = rb_define_module("H8");
194
195
 
@@ -92,6 +92,13 @@ void h8::RubyGate::mapSet(Local<String> name, Local<Value> value,
92
92
  rg->setProperty(name, value, info);
93
93
  }
94
94
 
95
+ void h8::RubyGate::mapDelete(Local<String> name,
96
+ const PropertyCallbackInfo<Boolean> &info) {
97
+ RubyGate *rg = RubyGate::unwrap(info.This());
98
+ assert(rg != 0);
99
+ rg->deleteProperty(name, info);
100
+ }
101
+
95
102
  void h8::RubyGate::indexGet(uint32_t index,
96
103
  const PropertyCallbackInfo<Value> &info) {
97
104
  RubyGate *rg = RubyGate::unwrap(info.This());
@@ -128,6 +135,11 @@ VALUE RubyGate::call(VALUE args) {
128
135
  return res;
129
136
  }
130
137
 
138
+ VALUE RubyGate::ruby_delete_handler(VALUE args) {
139
+ VALUE method = rb_ary_pop(args);
140
+ return rb_funcall(context_class, id_delete_handler, 2, method, args);
141
+ }
142
+
131
143
  VALUE RubyGate::secure_call(VALUE args) {
132
144
  VALUE method = rb_ary_pop(args);
133
145
  VALUE receiver = rb_ary_pop(args);
@@ -180,12 +192,12 @@ void h8::RubyGate::doObjectCallback(
180
192
  rb_ary_push(rb_args, context->ruby_context());
181
193
  rb_ary_push(rb_args, ruby_object);
182
194
  rescued_call(rb_args, call, [&] (VALUE res) {
183
- if( res == ruby_object )
184
- args.GetReturnValue().Set(args.This());
185
- else
186
- args.GetReturnValue().Set(context->to_js(res));
187
- });
188
- });
195
+ if( res == ruby_object )
196
+ args.GetReturnValue().Set(args.This());
197
+ else
198
+ args.GetReturnValue().Set(context->to_js(res));
199
+ });
200
+ });
189
201
  }
190
202
 
191
203
  void h8::RubyGate::getProperty(Local<String> name,
@@ -195,9 +207,9 @@ void h8::RubyGate::getProperty(Local<String> name,
195
207
  rb_ary_push(rb_args, ruby_object);
196
208
  rb_ary_push(rb_args, context->to_ruby(name));
197
209
  rescued_call(rb_args, secure_call, [&] (VALUE res) {
198
- if( res == ruby_object )
210
+ if( res == ruby_object )
199
211
  info.GetReturnValue().Set(info.This());
200
- else
212
+ else
201
213
  info.GetReturnValue().Set(context->to_js(res));
202
214
  });
203
215
  });
@@ -207,17 +219,31 @@ void h8::RubyGate::setProperty(Local<String> name, Local<Value> value,
207
219
  const PropertyCallbackInfo<Value> &info) {
208
220
  with_gvl(this, [&] {
209
221
  VALUE rb_args = rb_ary_new2(3);
210
- rb_ary_push(rb_args, context->to_ruby(value));
211
- rb_ary_push(rb_args, ruby_object);
222
+ rb_ary_push(rb_args, context->to_ruby(value)); // value
223
+ rb_ary_push(rb_args, ruby_object); // object
212
224
  VALUE method = context->to_ruby(name);
213
225
  method = rb_str_cat2(method, "=");
214
- rb_ary_push(rb_args, method);
226
+ rb_ary_push(rb_args, method); // name=
215
227
  rescued_call(rb_args, secure_call, [&] (VALUE res) {
216
228
  info.GetReturnValue().Set(context->to_js(res));
217
- });
229
+ });
218
230
  });
219
231
  }
220
232
 
233
+ void h8::RubyGate::deleteProperty(Local<String> name,
234
+ const PropertyCallbackInfo<Boolean> &info) {
235
+ with_gvl(this, [&] {
236
+ VALUE rb_args = rb_ary_new2(2);
237
+ rb_ary_push(rb_args, context->to_ruby(name)); // name
238
+ rb_ary_push(rb_args, ruby_object); // object
239
+
240
+ rescued_call(rb_args, ruby_delete_handler, [&] (VALUE res) {
241
+ auto success = Boolean::New(isolate(), res == Qnil ? false : true );
242
+ info.GetReturnValue().Set(success);
243
+ });
244
+ });
245
+ }
246
+
221
247
  void h8::RubyGate::getIndex(uint32_t index,
222
248
  const PropertyCallbackInfo<Value> &info) {
223
249
  with_gvl(this, [&] {
@@ -25,8 +25,8 @@ public:
25
25
  * @return wrapped RubyGate* or 0
26
26
  */
27
27
  static RubyGate* unwrap(v8::Handle<v8::Object> handle) {
28
- if (handle->InternalFieldCount() == 2
29
- && handle->GetAlignedPointerFromInternalField(1) == RUBYGATE_ID) {
28
+ if (handle->InternalFieldCount()
29
+ == 2 && handle->GetAlignedPointerFromInternalField(1) == RUBYGATE_ID) {
30
30
  return ObjectWrap::Unwrap<RubyGate>(handle);
31
31
  }
32
32
  return 0;
@@ -78,7 +78,7 @@ protected:
78
78
  * extra slots in array if need
79
79
  */
80
80
  template<class T>
81
- static VALUE ruby_args(H8* context,const T& args, unsigned extras = 0) {
81
+ static VALUE ruby_args(H8* context, const T& args, unsigned extras = 0) {
82
82
  unsigned n = args.Length();
83
83
  VALUE rb_args = rb_ary_new2(n + extras);
84
84
  for (unsigned i = 0; i < n; i++)
@@ -93,6 +93,11 @@ protected:
93
93
  */
94
94
  static VALUE call(VALUE args);
95
95
 
96
+ /**
97
+ * Call delete handler for ruby gate.
98
+ */
99
+ static VALUE ruby_delete_handler(VALUE args);
100
+
96
101
  /**
97
102
  * Call ruby method via H8::Context#secure_call
98
103
  */
@@ -108,12 +113,16 @@ protected:
108
113
  void setProperty(Local<String> name, Local<Value> value,
109
114
  const PropertyCallbackInfo<Value> &info);
110
115
 
116
+ void deleteProperty(Local<String> name,
117
+ const PropertyCallbackInfo<Boolean> &info);
118
+
111
119
  void getIndex(uint32_t index, const PropertyCallbackInfo<Value> &info);
112
120
  void setIndex(uint32_t index, Local<Value> value,
113
121
  const PropertyCallbackInfo<Value> &info);
114
122
 
115
123
  static void GateConstructor(const v8::FunctionCallbackInfo<Value>& args);
116
- static void ClassGateConstructor(const v8::FunctionCallbackInfo<Value>& args);
124
+ static void ClassGateConstructor(
125
+ const v8::FunctionCallbackInfo<Value>& args);
117
126
  private:
118
127
 
119
128
  void doObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -124,6 +133,8 @@ private:
124
133
  const PropertyCallbackInfo<Value> &info);
125
134
  static void mapSet(Local<String> name, Local<Value> value,
126
135
  const PropertyCallbackInfo<Value> &info);
136
+ static void mapDelete(Local<String> name,
137
+ const PropertyCallbackInfo<Boolean> &info);
127
138
 
128
139
  static void indexGet(uint32_t index,
129
140
  const PropertyCallbackInfo<Value> &info);
@@ -3,8 +3,7 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  require 'h8/version'
6
- require "rake/extensiontask"
7
- require 'rubygems/package_task'
6
+ require 'rake'
8
7
 
9
8
  spec = Gem::Specification.new do |spec|
10
9
  spec.name = "h8"
@@ -30,22 +29,24 @@ spec = Gem::Specification.new do |spec|
30
29
  spec.platform = Gem::Platform::RUBY
31
30
 
32
31
  spec.add_development_dependency "bundler", "~> 1.6"
33
- spec.add_development_dependency "rake"
34
- spec.add_development_dependency "rake-compiler"
32
+ # spec.add_development_dependency "rake"
33
+ # spec.add_development_dependency "rake-compiler"
35
34
  spec.add_development_dependency "rspec", '~> 3.1'
36
35
  spec.add_development_dependency 'hashie', '>= 0.1.2'
37
36
 
38
37
  spec.add_dependency 'pargser', '>= 0.1.2'
38
+ spec.add_dependency 'rake'
39
+ spec.add_dependency 'rake-compiler', '>= 0.9.4'
39
40
  end
40
41
 
41
- Gem::PackageTask.new(spec) do |pkg|
42
- end
43
-
44
- Rake::ExtensionTask.new "h8", spec do |ext|
45
- ext.lib_dir = "lib/h8"
46
- ext.source_pattern = "*.{c,cpp,js}"
47
- ext.gem_spec = spec
48
- end
42
+ # Gem::PackageTask.new(spec) do |pkg|
43
+ # end
44
+ #
45
+ # Rake::ExtensionTask.new "h8", spec do |ext|
46
+ # ext.lib_dir = "lib/h8"
47
+ # ext.source_pattern = "*.{c,cpp,js}"
48
+ # ext.gem_spec = spec
49
+ # end
49
50
 
50
51
  spec
51
52
 
@@ -113,6 +113,19 @@ module H8
113
113
  proc.call *args
114
114
  end
115
115
 
116
+ # :nodoc:
117
+ # Internal handler to properly delete fields/keys from ruby Hashes or
118
+ # OpenStruct
119
+ #
120
+ def self.delete_handler object, args
121
+ name = args[0]
122
+ if object.is_a?(OpenStruct)
123
+ object.delete_field name
124
+ else
125
+ object.delete name
126
+ end
127
+ end
128
+
116
129
  def self.can_access?(owner)
117
130
  owner != Object.class && owner != Kernel && owner != BasicObject.class
118
131
  end
@@ -206,7 +206,7 @@ end
206
206
 
207
207
  class Array
208
208
  # @return new array with all components converted to_ruby
209
- def to_ruby depth
209
+ def to_ruby depth=0
210
210
  depth += 1
211
211
  map { |x| x.to_ruby depth }
212
212
  end
@@ -1,3 +1,3 @@
1
1
  module H8
2
- VERSION = "0.4.8"
2
+ VERSION = "0.4.10"
3
3
  end
@@ -146,4 +146,11 @@ describe 'context' do
146
146
  }
147
147
  end
148
148
 
149
+ it 'should report syntax errors source' do
150
+ c = H8::Context.new
151
+ expect(->{ c.eval "debug('started'); shit happens!"}).to raise_error { |e|
152
+ p e.javascript_backtrace
153
+ }
154
+ end
155
+
149
156
  end
@@ -313,14 +313,39 @@ describe 'ruby gate' do
313
313
  c.coffee 'data.foo = "baz"'
314
314
  s.foo.should == 'baz'
315
315
  c.coffee 'data.h = { foo: "bar", bar: { baz: 1 } }'
316
+ c.coffee 'data.bad = "nonono"'
316
317
  s.h.foo.should == 'bar'
317
- c.coffee 'data.h.bar.baz == 1'
318
+ s.bad.should == 'nonono'
319
+ c.coffee 'assert data.h.bar.baz == 1'
320
+ c.coffee 'delete data.bad'
318
321
  s.h.bar.baz.should == 1
319
322
  c.coffee 'data.h.bar.arr = ["hello", { one: 2 }]'
320
323
  s.to_ruby.should == { 'test' => "bar", 'foo' => "baz", 'h' => { "foo" => "bar", "bar" => { "baz" => 1, "arr" => ["hello", { "one" => 2 }] } } }
321
324
  }
322
325
  end
323
326
 
327
+ it 'should delete gated prperties' do
328
+ s = OpenStruct.new 'foo' => 'bar', 'bar' => 'baz'
329
+ s.foo.should == 'bar'
330
+ c = H8::Context.new s: s
331
+ c.eval('s.foo').should == 'bar'
332
+ c.eval 'delete s.foo'
333
+ s.foo.should == nil
334
+
335
+ s = Hashie::Mash.new 'foo' => 'bar', 'bar' => 'baz'
336
+ s.foo.should == 'bar'
337
+ c = H8::Context.new s: s
338
+ c.eval('s.foo').should == 'bar'
339
+ c.eval 'delete s.foo'
340
+ s.foo.should == nil
341
+
342
+ s = { 'foo' => 'bar', 'bar' => 'baz' }
343
+ c = H8::Context.new s: s
344
+ c.eval('s.foo').should == 'bar'
345
+ c.eval 'delete s.foo'
346
+ s['foo'].should == nil
347
+ end
348
+
324
349
  it 'should access plain arrays (provide numeric indexes)' do
325
350
  cxt = H8::Context.new
326
351
  array = [10, 20, 30]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: h8
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.8
4
+ version: 0.4.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - sergeych
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-22 00:00:00.000000000 Z
11
+ date: 2015-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,75 +25,75 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '3.1'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '3.1'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake-compiler
42
+ name: hashie
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 0.1.2
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 0.1.2
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
56
+ name: pargser
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '3.1'
62
- type: :development
61
+ version: 0.1.2
62
+ type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '3.1'
68
+ version: 0.1.2
69
69
  - !ruby/object:Gem::Dependency
70
- name: hashie
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 0.1.2
76
- type: :development
75
+ version: '0'
76
+ type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 0.1.2
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: pargser
84
+ name: rake-compiler
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: 0.1.2
89
+ version: 0.9.4
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: 0.1.2
96
+ version: 0.9.4
97
97
  description: |2
98
98
  Synergy of ruby and javascript code, almost completely integrates both languages, their
99
99
  garbage collection models, object models and so on. Effective for tight ruby-javascript
@@ -115,6 +115,7 @@ files:
115
115
  - benchmark/README.md
116
116
  - benchmark/big.txt
117
117
  - benchmark/km.cpp
118
+ - benchmark/km.rb
118
119
  - benchmark/knightsmove.coffee
119
120
  - benchmark/knightsmove.rb
120
121
  - benchmark/process_text.coffee