h8 0.0.4 → 0.0.5
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.
- 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
|