h8 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/h8/h8.h +3 -0
- data/ext/h8/js_gate.h +11 -0
- data/ext/h8/main.cpp +10 -1
- data/ext/h8/ruby_gate.cpp +41 -1
- data/ext/h8/ruby_gate.h +7 -0
- data/hybrid8.gemspec +14 -10
- data/lib/h8/context.rb +64 -7
- data/lib/h8/value.rb +35 -11
- data/lib/h8/version.rb +1 -1
- data/spec/context_spec.rb +1 -1
- data/spec/js_gate_spec.rb +19 -0
- data/spec/ruby_gate_spec.rb +135 -6
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b76a42b3323f67318a62c30a351b47af58ed43c
|
4
|
+
data.tar.gz: ee264fbcde18819ba8056828f6cda504f4f36e56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77365dcf82c9ff0e7832cb915080b18aacc280e9a9e91cddb917da72168afdbf53be7445082721c539ca71a424e6fb013958ae118317a3274c0b1629c3341842
|
7
|
+
data.tar.gz: 397d66435fbd04f1ec3b72f5cce6b1d876e316533c4063c8a9288bab96ee97c68022313181075283aa614e7365f02084387793e9325e6402650adcc22fc7dde0
|
data/README.md
CHANGED
@@ -57,7 +57,7 @@ install v8 from sources 3.31.77 (or try newer), then execute:
|
|
57
57
|
export CXXFLAGS='-std=c++11 -stdlib=libc++ -mmacosx-version-min=10.9'
|
58
58
|
export LDFLAGS=-lc++
|
59
59
|
make native
|
60
|
-
|
60
|
+
export V8_3_31_ROOT=`pwd` # or somehow else set it
|
61
61
|
|
62
62
|
Note that exporting symbols is a hack that may not be in need anymore. After that the gem should
|
63
63
|
install normally.
|
@@ -70,7 +70,7 @@ Install first a valid v8 version. We provide a ready package!
|
|
70
70
|
|
71
71
|
It should install prerequisites, if not, manually install
|
72
72
|
|
73
|
-
|
73
|
+
sudo apt-get install libicu-dev
|
74
74
|
|
75
75
|
You might also need to install GMP.
|
76
76
|
|
data/ext/h8/h8.h
CHANGED
@@ -159,8 +159,11 @@ public:
|
|
159
159
|
return v8::Undefined(isolate);
|
160
160
|
case T_NIL:
|
161
161
|
return v8::Null(isolate);
|
162
|
+
case T_ARRAY:
|
163
|
+
case T_HASH:
|
162
164
|
case T_DATA:
|
163
165
|
case T_OBJECT:
|
166
|
+
case T_CLASS:
|
164
167
|
return gateObject(ruby_value);
|
165
168
|
default:
|
166
169
|
VALUE msg = rb_str_new2("can't gate to js (unknown): ");
|
data/ext/h8/js_gate.h
CHANGED
@@ -124,6 +124,17 @@ public:
|
|
124
124
|
return h8->to_ruby(object()->Get(v8_name));
|
125
125
|
}
|
126
126
|
|
127
|
+
/**
|
128
|
+
* Set JS object attribute to ruby value
|
129
|
+
*
|
130
|
+
*/
|
131
|
+
void set_attribute(VALUE name, VALUE value) {
|
132
|
+
H8::Scope scope(h8);
|
133
|
+
Local<Value> v8_name = v8::String::NewFromUtf8(isolate(),
|
134
|
+
StringValueCStr(name));
|
135
|
+
object()->Set(v8_name, h8->to_js(value));
|
136
|
+
}
|
137
|
+
|
127
138
|
VALUE get_index(VALUE index) {
|
128
139
|
H8::Scope scope(h8);
|
129
140
|
return h8->to_ruby(object()->Get(NUM2INT(index)));
|
data/ext/h8/main.cpp
CHANGED
@@ -79,6 +79,13 @@ static VALUE rvalue_get_index(VALUE self, VALUE index) {
|
|
79
79
|
return rv(self)->get_index(index);
|
80
80
|
}
|
81
81
|
|
82
|
+
static VALUE rvalue_set_attr(VALUE self, VALUE name, VALUE value) {
|
83
|
+
return protect_ruby([&] {
|
84
|
+
rv(self)->set_attribute(name, value);
|
85
|
+
return Qnil;
|
86
|
+
});
|
87
|
+
}
|
88
|
+
|
82
89
|
static VALUE rvalue_is_string(VALUE self) {
|
83
90
|
return rv(self)->is_string();
|
84
91
|
}
|
@@ -170,7 +177,7 @@ void Init_h8(void) {
|
|
170
177
|
ruby_gate_class = rb_define_class_under(h8, "RubyGate", rb_cObject);
|
171
178
|
rb_define_alloc_func(context_class, context_alloc);
|
172
179
|
rb_define_method(context_class, "_eval", (ruby_method) context_eval, 2);
|
173
|
-
rb_define_method(context_class, "
|
180
|
+
rb_define_method(context_class, "_set_var", (ruby_method) context_set_var,
|
174
181
|
2);
|
175
182
|
|
176
183
|
value_class = rb_define_class_under(h8, "Value", rb_cObject);
|
@@ -191,6 +198,8 @@ void Init_h8(void) {
|
|
191
198
|
1);
|
192
199
|
rb_define_method(value_class, "_get_index", (ruby_method) rvalue_get_index,
|
193
200
|
1);
|
201
|
+
rb_define_method(value_class, "_set_attr", (ruby_method) rvalue_set_attr,
|
202
|
+
2);
|
194
203
|
rb_define_method(value_class, "_call", (ruby_method) rvalue_call, 1);
|
195
204
|
rb_define_method(value_class, "_apply", (ruby_method) rvalue_apply, 2);
|
196
205
|
rb_define_method(value_class, "context",
|
data/ext/h8/ruby_gate.cpp
CHANGED
@@ -14,7 +14,8 @@ h8::RubyGate::RubyGate(H8* _context, VALUE object) :
|
|
14
14
|
templ->SetInternalFieldCount(2);
|
15
15
|
templ->SetCallAsFunctionHandler(&ObjectCallback);
|
16
16
|
|
17
|
-
templ->SetNamedPropertyHandler(
|
17
|
+
templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
|
18
|
+
templ->SetIndexedPropertyHandler(RubyGate::indexGet,RubyGate::indexSet);
|
18
19
|
|
19
20
|
v8::Handle<v8::Object> handle = templ->NewInstance();
|
20
21
|
handle->SetAlignedPointerInInternalField(1, RUBYGATE_ID);
|
@@ -36,6 +37,21 @@ void h8::RubyGate::mapSet(Local<String> name,
|
|
36
37
|
rg->setProperty(name, value, info);
|
37
38
|
}
|
38
39
|
|
40
|
+
void h8::RubyGate::indexGet(uint32_t index,
|
41
|
+
const PropertyCallbackInfo<Value> &info) {
|
42
|
+
RubyGate *rg = RubyGate::unwrap(info.This());
|
43
|
+
assert(rg != 0);
|
44
|
+
rg->getIndex(index, info);
|
45
|
+
}
|
46
|
+
|
47
|
+
void h8::RubyGate::indexSet(uint32_t index,
|
48
|
+
Local<Value> value,
|
49
|
+
const PropertyCallbackInfo<Value> &info) {
|
50
|
+
RubyGate *rg = RubyGate::unwrap(info.This());
|
51
|
+
assert(rg != 0);
|
52
|
+
rg->setIndex(index, value, info);
|
53
|
+
}
|
54
|
+
|
39
55
|
void h8::RubyGate::ObjectCallback(
|
40
56
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
41
57
|
v8::HandleScope scope(args.GetIsolate());
|
@@ -115,3 +131,27 @@ void h8::RubyGate::setProperty(Local<String> name,
|
|
115
131
|
});
|
116
132
|
}
|
117
133
|
|
134
|
+
void h8::RubyGate::getIndex(uint32_t index,
|
135
|
+
const PropertyCallbackInfo<Value> &info) {
|
136
|
+
VALUE rb_args = rb_ary_new2(3);
|
137
|
+
rb_ary_push(rb_args, INT2FIX(index));
|
138
|
+
rb_ary_push(rb_args, ruby_object);
|
139
|
+
rb_ary_push(rb_args, rb_str_new2("[]"));
|
140
|
+
return rescued_call(rb_args, secure_call, [&] (VALUE res) {
|
141
|
+
info.GetReturnValue().Set(context->to_js(res));
|
142
|
+
});
|
143
|
+
}
|
144
|
+
|
145
|
+
void h8::RubyGate::setIndex(uint32_t index,
|
146
|
+
Local<Value> value,
|
147
|
+
const PropertyCallbackInfo<Value> &info) {
|
148
|
+
VALUE rb_args = rb_ary_new2(4);
|
149
|
+
rb_ary_push(rb_args, INT2FIX(index));
|
150
|
+
rb_ary_push(rb_args, context->to_ruby(value));
|
151
|
+
rb_ary_push(rb_args, ruby_object);
|
152
|
+
rb_ary_push(rb_args, rb_str_new2("[]="));
|
153
|
+
return rescued_call(rb_args, secure_call, [&] (VALUE res) {
|
154
|
+
info.GetReturnValue().Set(context->to_js(res));
|
155
|
+
});
|
156
|
+
}
|
157
|
+
|
data/ext/h8/ruby_gate.h
CHANGED
@@ -94,14 +94,21 @@ protected:
|
|
94
94
|
|
95
95
|
void getProperty(Local<String> name, const PropertyCallbackInfo<Value> &info);
|
96
96
|
void setProperty(Local<String> name, Local<Value> value,const PropertyCallbackInfo<Value> &info);
|
97
|
+
|
98
|
+
void getIndex(uint32_t index, const PropertyCallbackInfo<Value> &info);
|
99
|
+
void setIndex(uint32_t index, Local<Value> value,const PropertyCallbackInfo<Value> &info);
|
97
100
|
private:
|
98
101
|
|
99
102
|
void doObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
|
100
103
|
|
101
104
|
static void ObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
|
105
|
+
|
102
106
|
static void mapGet(Local<String> name, const PropertyCallbackInfo<Value> &info);
|
103
107
|
static void mapSet(Local<String> name, Local<Value> value,const PropertyCallbackInfo<Value> &info);
|
104
108
|
|
109
|
+
static void indexGet(uint32_t index, const PropertyCallbackInfo<Value> &info);
|
110
|
+
static void indexSet(uint32_t index, Local<Value> value, const PropertyCallbackInfo<Value> &info);
|
111
|
+
|
105
112
|
void throw_js();
|
106
113
|
|
107
114
|
|
data/hybrid8.gemspec
CHANGED
@@ -7,14 +7,18 @@ require "rake/extensiontask"
|
|
7
7
|
require 'rubygems/package_task'
|
8
8
|
|
9
9
|
spec = Gem::Specification.new do |spec|
|
10
|
-
spec.name
|
11
|
-
spec.version
|
12
|
-
spec.authors
|
13
|
-
spec.email
|
14
|
-
spec.summary
|
15
|
-
spec.description
|
16
|
-
|
17
|
-
|
10
|
+
spec.name = "h8"
|
11
|
+
spec.version = H8::VERSION
|
12
|
+
spec.authors = ["sergeych"]
|
13
|
+
spec.email = ["real.sergeych@gmail.com"]
|
14
|
+
spec.summary = %q{Minimalistic and fast Ruby <--> V8 integration}
|
15
|
+
spec.description = <<-End
|
16
|
+
Synergy of ruby and javascript code, almost completely integrates both languages, their
|
17
|
+
garbage collection models, object models and so on. Effective for tight ruby-javascript
|
18
|
+
integration where objects can be passed between different languages several times.
|
19
|
+
End
|
20
|
+
spec.homepage = "https://github.com/sergeych/hybrid8"
|
21
|
+
spec.license = "MIT"
|
18
22
|
|
19
23
|
spec.files = `git ls-files -z`.split("\x0")
|
20
24
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
@@ -37,9 +41,9 @@ Gem::PackageTask.new(spec) do |pkg|
|
|
37
41
|
end
|
38
42
|
|
39
43
|
Rake::ExtensionTask.new "h8", spec do |ext|
|
40
|
-
ext.lib_dir
|
44
|
+
ext.lib_dir = "lib/h8"
|
41
45
|
ext.source_pattern = "*.{c,cpp}"
|
42
|
-
ext.gem_spec
|
46
|
+
ext.gem_spec = spec
|
43
47
|
end
|
44
48
|
|
45
49
|
spec
|
data/lib/h8/context.rb
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
module H8
|
2
2
|
class Context
|
3
3
|
# Create new context optionally providing variables hash
|
4
|
-
def initialize
|
4
|
+
def initialize **kwargs
|
5
|
+
@idcount = 0
|
5
6
|
set_all **kwargs
|
7
|
+
_set_var '___create_ruby_class', -> (cls, args) {
|
8
|
+
_do_cretate_ruby_class cls, args
|
9
|
+
}
|
6
10
|
end
|
7
11
|
|
8
12
|
# set variables from keyword arguments to this context
|
13
|
+
# see #[]= for details
|
9
14
|
def set_all **kwargs
|
10
15
|
kwargs.each { |name, value|
|
11
16
|
set_var(name.to_s, value)
|
12
17
|
}
|
13
18
|
end
|
14
19
|
|
15
|
-
# Set variable for the context
|
20
|
+
# Set variable/class for the context. It can set variable to hold:
|
21
|
+
# * primitive type (like string, integer, nil, float and like) - by value!
|
22
|
+
# * any other ruby object - by reference
|
23
|
+
# * ruby Class - creating a javascript constructor function that creates ruby
|
24
|
+
# class instance (any arguments) and gates it to use with js.
|
16
25
|
def []= name, value
|
17
26
|
set_all name => value
|
18
27
|
end
|
19
28
|
|
20
29
|
# Execute a given script on the current context with optionally limited execution time.
|
21
30
|
#
|
22
|
-
# @param [
|
31
|
+
# @param [Int] timeout if is not 0 then maximum execution time in millis (therubyracer
|
32
|
+
# compatibility)
|
33
|
+
# @param [Float] max_time if is not 0 then maximum execution time in seconds. Has precedence
|
34
|
+
# over timeout
|
35
|
+
#
|
23
36
|
# @return [Value] wrapped object returned by the script
|
24
37
|
# @raise [H8::TimeoutError] if the timeout was set and expired
|
25
|
-
def eval script, timeout: 0
|
26
|
-
|
38
|
+
def eval script, max_time: 0, timeout: 0
|
39
|
+
timeout = max_time * 1000 if max_time > 0
|
40
|
+
_eval script, timeout.to_i
|
27
41
|
end
|
28
42
|
|
29
43
|
# Execute script in a new context with optionally set vars. @see H8#set_all
|
@@ -33,13 +47,17 @@ module H8
|
|
33
47
|
end
|
34
48
|
|
35
49
|
# Secure gate for JS to securely access ruby class properties (methods with no args)
|
36
|
-
# and methods.
|
50
|
+
# and methods. This class implements security policy! Overriding this method could
|
51
|
+
# breach security and provide full access to ruby object trees.
|
52
|
+
#
|
53
|
+
# It has very complex logic so the security model update should be done somehow
|
54
|
+
# else.
|
37
55
|
def self.secure_call instance, method, args=nil
|
38
56
|
method = method.to_sym
|
39
57
|
begin
|
40
58
|
m = instance.public_method(method)
|
41
59
|
if m.owner == instance.class
|
42
|
-
return m.call(*args) if method[-1] == '='
|
60
|
+
return m.call(*args) if method[0] == '[' || method[-1] == '='
|
43
61
|
if m.arity != 0
|
44
62
|
return -> (*args) { m.call *args }
|
45
63
|
else
|
@@ -47,8 +65,47 @@ module H8
|
|
47
65
|
end
|
48
66
|
end
|
49
67
|
rescue NameError
|
68
|
+
# No exact method, calling []/[]= if any
|
69
|
+
method, args = if method[-1] == '='
|
70
|
+
[:[]=, [method[0..-2].to_s, args[0]] ]
|
71
|
+
else
|
72
|
+
[:[], [method.to_s]]
|
73
|
+
end
|
74
|
+
begin
|
75
|
+
m = instance.public_method(method)
|
76
|
+
if m.owner == instance.class
|
77
|
+
return m.call(*args)
|
78
|
+
end
|
79
|
+
rescue NameError
|
80
|
+
# It means there is no [] or []=, e.g. undefined
|
81
|
+
end
|
50
82
|
end
|
51
83
|
H8::Undefined
|
52
84
|
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Set var that could be either a callable, class instance, simple value or a Class class
|
89
|
+
# in which case constructor function will be created
|
90
|
+
def set_var name, value
|
91
|
+
if value.is_a?(Class)
|
92
|
+
clsid = "__ruby_class_#{@idcount += 1}"
|
93
|
+
_set_var clsid, value
|
94
|
+
eval <<-End
|
95
|
+
function #{name.to_s}() {
|
96
|
+
return ___create_ruby_class(#{clsid}, arguments);
|
97
|
+
}
|
98
|
+
End
|
99
|
+
else
|
100
|
+
_set_var name, value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# create class instance passing it arguments stored in javascript 'arguments' object
|
105
|
+
# (so it repacks them first)
|
106
|
+
def _do_cretate_ruby_class(klass, arguments)
|
107
|
+
klass.new *arguments.to_ruby.values
|
108
|
+
end
|
109
|
+
|
53
110
|
end
|
54
111
|
end
|
data/lib/h8/value.rb
CHANGED
@@ -10,6 +10,10 @@ module H8
|
|
10
10
|
# pass values between context, as they often map native Javascript objects. If you need, you
|
11
11
|
# can copy, for example, by converting it H8::Value#to_ruby first. Another approach is to
|
12
12
|
# use JSON serialization from the script.
|
13
|
+
#
|
14
|
+
# Interesting thing about H8::Value is that when passed back to some javascript environment
|
15
|
+
# (say, callback parameted or as a global variable), it unwraps source javascript object -
|
16
|
+
# no copying, no modifying.
|
13
17
|
class Value
|
14
18
|
|
15
19
|
include Comparable
|
@@ -23,23 +27,40 @@ module H8
|
|
23
27
|
#
|
24
28
|
# @return [H8::Value] instance, which is .undefined? if does not exist
|
25
29
|
def [] name_index
|
26
|
-
name_index.is_a?(Fixnum) ? _get_index(name_index) : _get_attr(name_index)
|
30
|
+
name_index.is_a?(Fixnum) ? _get_index(name_index) : _get_attr(name_index.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set js object attribute by either name or index (should be Fixnum instance).
|
34
|
+
def []= name_index, value
|
35
|
+
# name_index.is_a?(Fixnum) ? _get_index(name_index) : _get_attr(name_index.to_s)
|
36
|
+
_set_attr(name_index.to_s, value)
|
27
37
|
end
|
28
38
|
|
29
|
-
#
|
30
|
-
# automatically. Use val['attr'] to get callable instead
|
39
|
+
# Optimizing JS member access. Support class members and member functions - will call them
|
40
|
+
# automatically. Use val['attr'] to get callable instead.
|
41
|
+
#
|
42
|
+
# First invocation creates accessor method so future calls happen much faster
|
31
43
|
def method_missing(method_sym, *arguments, &block)
|
32
44
|
name = method_sym.to_s
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
45
|
+
if name[-1] != '='
|
46
|
+
instance_eval <<-End
|
47
|
+
def #{name} *args, **kwargs
|
48
|
+
res = _get_attr('#{name}')
|
49
|
+
(res.is_a?(H8::Value) && res.function?) ? res.apply(self,*args) : res
|
50
|
+
end
|
51
|
+
End
|
52
|
+
else
|
53
|
+
instance_eval <<-End
|
54
|
+
def #{name} value
|
55
|
+
_set_attr('#{name[0..-2]}', value)
|
56
|
+
end
|
57
|
+
End
|
58
|
+
end
|
39
59
|
send method_sym, *arguments
|
40
60
|
end
|
41
61
|
|
42
|
-
# Compare to other object, either usual or another Value instance.
|
62
|
+
# Compare to other object, either usual or another Value instance. Tries its best.
|
63
|
+
# be sure to use it wisely and report any problems
|
43
64
|
def <=> other
|
44
65
|
other = other.to_ruby if other.is_a?(H8::Value)
|
45
66
|
to_ruby <=> other
|
@@ -103,7 +124,8 @@ module H8
|
|
103
124
|
values.each
|
104
125
|
end
|
105
126
|
|
106
|
-
# Tries to convert wrapped JS object to ruby primitive (Fixed, String, Float, Array, Hash)
|
127
|
+
# Tries to convert wrapped JS object to ruby primitive (Fixed, String, Float, Array, Hash).
|
128
|
+
# Note that this conversion looses information about source javascript class (if any)
|
107
129
|
def to_ruby
|
108
130
|
case
|
109
131
|
when integer?
|
@@ -114,6 +136,8 @@ module H8
|
|
114
136
|
to_f
|
115
137
|
when array?
|
116
138
|
_get_attr('length').to_i.times.map { |i| _get_index(i).to_ruby }
|
139
|
+
when function?
|
140
|
+
to_proc
|
117
141
|
when object?
|
118
142
|
to_h
|
119
143
|
else
|
data/lib/h8/version.rb
CHANGED
data/spec/context_spec.rb
CHANGED
data/spec/js_gate_spec.rb
CHANGED
@@ -237,5 +237,24 @@ describe 'js_gate' do
|
|
237
237
|
}).to raise_error(MyException)
|
238
238
|
end
|
239
239
|
|
240
|
+
it 'should dynamically add and remove properties to js objects' do
|
241
|
+
cxt = H8::Context.new
|
242
|
+
jobj = cxt.eval('jobj = {one: 1};')
|
243
|
+
jobj.one.should == 1
|
244
|
+
jobj['one'].should == 1
|
245
|
+
jobj[:one].should == 1
|
246
|
+
jobj.one = 101
|
247
|
+
cxt.eval('jobj.one').should == 101
|
248
|
+
jobj[:one].should == 101
|
249
|
+
jobj.two = 2
|
250
|
+
jobj[:two].should == 2
|
251
|
+
cxt.eval('jobj.two').should == 2
|
252
|
+
jobj[:three] = 3
|
253
|
+
jobj.three.should == 3
|
254
|
+
cxt.eval('jobj.three').should == 3
|
255
|
+
jobj[101] = 202
|
256
|
+
jobj[101].should == 202
|
257
|
+
cxt.eval('jobj[101]').should == 202
|
258
|
+
end
|
240
259
|
|
241
260
|
end
|
data/spec/ruby_gate_spec.rb
CHANGED
@@ -19,7 +19,7 @@ describe 'ruby gate' do
|
|
19
19
|
it 'should gate callables with varargs' do
|
20
20
|
cxt = H8::Context.new
|
21
21
|
cxt[:fn] = -> (*args) {
|
22
|
-
args.reduce(0){|all,x| all+x }
|
22
|
+
args.reduce(0) { |all, x| all+x }
|
23
23
|
}
|
24
24
|
|
25
25
|
cxt.eval('fn(11, 22, 100);').should == 133
|
@@ -102,6 +102,8 @@ describe 'ruby gate' do
|
|
102
102
|
def base
|
103
103
|
raise "It should not be called"
|
104
104
|
end
|
105
|
+
|
106
|
+
attr_accessor :do_throw
|
105
107
|
end
|
106
108
|
|
107
109
|
class Test < Base
|
@@ -109,14 +111,30 @@ describe 'ruby gate' do
|
|
109
111
|
attr_accessor :rw
|
110
112
|
|
111
113
|
def initialize
|
112
|
-
@ro
|
113
|
-
@rw
|
114
|
+
@ro = 'readonly'
|
115
|
+
@rw = 'not initialized'
|
116
|
+
@val = 'init[]'
|
117
|
+
self.do_throw = false
|
114
118
|
end
|
115
119
|
|
116
120
|
def test_args *args
|
117
121
|
args.join('-')
|
118
122
|
end
|
119
123
|
|
124
|
+
def [] val
|
125
|
+
if val != 'foo'
|
126
|
+
raise('not foo') if do_throw
|
127
|
+
H8::Undefined
|
128
|
+
else
|
129
|
+
@val
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def []= val, x
|
134
|
+
val != 'foo' && do_throw and raise "not foo"
|
135
|
+
@val = x
|
136
|
+
end
|
137
|
+
|
120
138
|
protected
|
121
139
|
|
122
140
|
def prot_method
|
@@ -152,8 +170,119 @@ describe 'ruby gate' do
|
|
152
170
|
cxt.eval('foo.rw').should == 'hello'
|
153
171
|
end
|
154
172
|
|
155
|
-
|
156
|
-
|
173
|
+
context 'no interceptors' do
|
174
|
+
class Test2 < Base
|
175
|
+
attr :ro
|
176
|
+
attr_accessor :rw
|
177
|
+
|
178
|
+
def initialize
|
179
|
+
@ro = 'readonly'
|
180
|
+
@rw = 'not initialized'
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_args *args
|
184
|
+
args.join('-')
|
185
|
+
end
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
def prot_method
|
190
|
+
raise 'should not be called'
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def priv_method
|
196
|
+
raise 'should not be called'
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should access object properties and methods' do
|
201
|
+
cxt = H8::Context.new
|
202
|
+
cxt[:foo] = Test2.new
|
203
|
+
cxt.eval('foo.ro').should == 'readonly'
|
204
|
+
cxt.eval('foo.rw').should == 'not initialized'
|
205
|
+
cxt.eval('foo.base').should == H8::Undefined
|
206
|
+
cxt.eval('foo.send').should == H8::Undefined
|
207
|
+
cxt.eval('foo.prot_method').should == H8::Undefined
|
208
|
+
cxt.eval('foo.priv_method').should == H8::Undefined
|
209
|
+
cxt.eval('foo.test_args').should be_kind_of(Proc)
|
210
|
+
cxt.eval('foo.test_args("hi", "you")').should == 'hi-you'
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should set ruby properties' do
|
214
|
+
cxt = H8::Context.new
|
215
|
+
cxt[:foo] = t = Test2.new
|
216
|
+
cxt.eval('foo.rw="hello";')
|
217
|
+
t.rw.should == 'hello'
|
218
|
+
cxt.eval('foo.rw').should == 'hello'
|
219
|
+
end
|
220
|
+
end
|
157
221
|
|
158
|
-
|
222
|
+
it 'should add and intercept property access' do
|
223
|
+
# pending
|
224
|
+
t = Test.new
|
225
|
+
t.do_throw = true
|
226
|
+
cxt = H8::Context.new t: t
|
227
|
+
cxt.eval("t['foo'];").should == 'init[]'
|
228
|
+
expect(-> { cxt.eval("t['foo1'];") }).to raise_error(RuntimeError)
|
229
|
+
cxt.eval("t['foo']='bar'");
|
230
|
+
# p t['foo']
|
231
|
+
# p cxt.eval "t['foo'] = 'bar';"
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'should access plain arrays (provide numeric indexes)' do
|
235
|
+
cxt = H8::Context.new
|
236
|
+
array = [10, 20, 30]
|
237
|
+
cxt[:a] = array
|
238
|
+
cxt.eval('a.length').should == 3
|
239
|
+
cxt.eval('a[1]').should == 20
|
240
|
+
cxt.eval('a[0] = 100; a[0]').should == 100
|
241
|
+
array[0].should == 100
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'should access plain hashes' do
|
245
|
+
cxt = H8::Context.new
|
246
|
+
h = { 'one' => 2 }
|
247
|
+
cxt[:h] = h
|
248
|
+
cxt.eval("h['one']").should == 2
|
249
|
+
eval("h['one']=1;")
|
250
|
+
h['one'].should == 1
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should pass varargs' do
|
254
|
+
cxt = H8::Context.new
|
255
|
+
cxt[:test] = -> (args) {
|
256
|
+
# Sample how to deal with javascript 'arguments' vararg object:
|
257
|
+
args.to_ruby.values.join(',')
|
258
|
+
}
|
259
|
+
res = cxt.eval <<-End
|
260
|
+
function test2() {
|
261
|
+
return test(arguments);
|
262
|
+
}
|
263
|
+
test2(1,2,'ho');
|
264
|
+
End
|
265
|
+
res.should == '1,2,ho'
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'should gate classes' do
|
269
|
+
class Gated
|
270
|
+
attr :init_args
|
271
|
+
|
272
|
+
def initialize *args
|
273
|
+
@init_args = args
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
cxt = H8::Context.new RClass: Gated
|
278
|
+
c = cxt.eval 'new RClass()'
|
279
|
+
c.should be_a(Gated)
|
280
|
+
c.init_args.should == []
|
281
|
+
|
282
|
+
c = cxt.eval 'rc = new RClass("hello", "world")'
|
283
|
+
c.should be_a(Gated)
|
284
|
+
c.init_args.should == ['hello', 'world']
|
285
|
+
cxt.eval('rc.init_args').should == ['hello', 'world']
|
286
|
+
end
|
287
|
+
end
|
159
288
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: h8
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergeych
|
@@ -66,8 +66,10 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 2.14.0
|
69
|
-
description:
|
70
|
-
|
69
|
+
description: |2
|
70
|
+
Synergy of ruby and javascript code, almost completely integrates both languages, their
|
71
|
+
garbage collection models, object models and so on. Effective for tight ruby-javascript
|
72
|
+
integration where objects can be passed between different languages several times.
|
71
73
|
email:
|
72
74
|
- real.sergeych@gmail.com
|
73
75
|
executables: []
|
@@ -106,7 +108,7 @@ files:
|
|
106
108
|
- spec/js_gate_spec.rb
|
107
109
|
- spec/ruby_gate_spec.rb
|
108
110
|
- spec/spec_helper.rb
|
109
|
-
homepage:
|
111
|
+
homepage: https://github.com/sergeych/hybrid8
|
110
112
|
licenses:
|
111
113
|
- MIT
|
112
114
|
metadata: {}
|
@@ -130,7 +132,7 @@ rubyforge_project:
|
|
130
132
|
rubygems_version: 2.2.2
|
131
133
|
signing_key:
|
132
134
|
specification_version: 4
|
133
|
-
summary: Minimalistic and
|
135
|
+
summary: Minimalistic and fast Ruby <--> V8 integration
|
134
136
|
test_files:
|
135
137
|
- spec/context_spec.rb
|
136
138
|
- spec/js_gate_spec.rb
|