h8 0.4.5 → 0.4.8

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: da7dc92fe9f18d03e202dbd8566c5469f3b122b2
4
- data.tar.gz: 4c570745ab23ac22f705405742ff10b7fb9310df
3
+ metadata.gz: ae98780cc495a9c2f15db1b3de81f81cc014978b
4
+ data.tar.gz: ee00bd70c38b2f4b62ecf8beddb2e15fb6aacb61
5
5
  SHA512:
6
- metadata.gz: 3eade24933e29d3d5b124b4665a3eef82e4e6dee3cde4a7e62c1a5f68acf08ee5d060bdaa1f7a61eeedd53aea417b751618d847ffd67dd8f75dddbdd32f5cc56
7
- data.tar.gz: 90c14a2a879418134c41ce3958ce8b943cc5d86285811657e280af079e3802d275e1156860f6af4f3f664515ec37f19e9a51d07d8914a8e5eaa5c03218d20026
6
+ metadata.gz: 4a2be9806423de66eacb312891d1abcff2af0f24679e740af339689cae1ae50fd8dbb07d15d138fc13976bbb410b3acf92951d020bcb96df93c16ab9a5fd3b20
7
+ data.tar.gz: 85f4398e82fc5a818654936cdb2b0fe5b51ee64c8e08284d131657d4f89c90b55585fbb8fc052dfe673a8371ce9ad7f69ab4a440c3d8301ec5a23d5777981250
@@ -19,8 +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;
23
- extern ID id_safe_call;
22
+ extern ID id_is_a, id_safe_call, id_safe_proc_call;
24
23
 
25
24
  VALUE protect_ruby(const std::function<VALUE()> &block);
26
25
 
@@ -14,8 +14,7 @@ VALUE ruby_gate_class;
14
14
  VALUE value_class;
15
15
  VALUE Rundefined;
16
16
 
17
- ID id_is_a;
18
- ID id_safe_call;
17
+ ID id_is_a, id_safe_call, id_safe_proc_call;
19
18
 
20
19
  VALUE protect_ruby(const std::function<VALUE()> &block) {
21
20
  try {
@@ -189,6 +188,7 @@ void Init_h8(void) {
189
188
 
190
189
  id_is_a = rb_intern("is_a?");
191
190
  id_safe_call = rb_intern("secure_call");
191
+ id_safe_proc_call = rb_intern("safe_proc_call");
192
192
 
193
193
  VALUE h8 = rb_define_module("H8");
194
194
 
@@ -124,7 +124,7 @@ VALUE h8::RubyGate::rescue_callback(VALUE me, VALUE exception_object) {
124
124
  VALUE RubyGate::call(VALUE args) {
125
125
  VALUE callable = rb_ary_pop(args);
126
126
  VALUE context = rb_ary_pop(args);
127
- VALUE res = rb_funcall(context, rb_intern("safe_proc_call"), 2, callable, args);
127
+ VALUE res = rb_funcall(context, id_safe_proc_call, 2, callable, args);
128
128
  return res;
129
129
  }
130
130
 
@@ -33,6 +33,7 @@ spec = Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency "rake"
34
34
  spec.add_development_dependency "rake-compiler"
35
35
  spec.add_development_dependency "rspec", '~> 3.1'
36
+ spec.add_development_dependency 'hashie', '>= 0.1.2'
36
37
 
37
38
  spec.add_dependency 'pargser', '>= 0.1.2'
38
39
  end
data/lib/h8.rb CHANGED
@@ -7,3 +7,6 @@ require 'h8/coffee'
7
7
 
8
8
  # The native library should be required AFTER ruby defintions
9
9
  require 'h8/h8'
10
+
11
+ # Initialize dependencies
12
+ H8::Coffee.compile ''
@@ -18,7 +18,7 @@ module H8
18
18
  # compiler instance across all threads with a mutex.
19
19
  def self.eval src, ** kwargs
20
20
  @@mutex.synchronize {
21
- (@@compiler ||= Coffee.new).eval src, ** kwargs
21
+ (@@compiler ||= Coffee.new).eval src, **kwargs
22
22
  }
23
23
  end
24
24
 
@@ -29,13 +29,13 @@ module H8
29
29
  # compiler instance across all threads with a mutex.
30
30
  def self.compile src, ** kwargs
31
31
  @@mutex.synchronize {
32
- (@@compiler ||= Coffee.new).compile src, ** kwargs
32
+ (@@compiler ||= Coffee.new).compile src, **kwargs
33
33
  }
34
34
  end
35
35
 
36
36
  # Create compiler instance.
37
37
  def initialize
38
- @context = H8::Context.new
38
+ @context = H8::Context.new noglobals: true
39
39
  @context.eval read_script 'coffee-script.js'
40
40
  eval read_script('globals.coffee')
41
41
  end
@@ -70,5 +70,4 @@ module H8
70
70
  end
71
71
  end
72
72
 
73
-
74
73
  end
@@ -1,14 +1,17 @@
1
1
  require 'thread'
2
+ require 'h8'
2
3
 
3
4
  module H8
5
+
4
6
  class Context
5
7
  # Create new context optionally providing variables hash
6
- def initialize **kwargs
8
+ def initialize noglobals: false, **kwargs
7
9
  @idcount = 0
8
10
  set_all **kwargs
9
11
  _set_var '___create_ruby_class', -> (cls, args) {
10
12
  _do_create_ruby_class cls, args
11
13
  }
14
+ # noglobals or execute_script 'globals.coffee'
12
15
  end
13
16
 
14
17
  # set variables from keyword arguments to this context
@@ -48,13 +51,13 @@ module H8
48
51
  # If you need to execute same script more than once consider first H8::Coffee.compile
49
52
  # and cache compiled script.
50
53
  def coffee script, ** kwargs
51
- eval Coffee.compile script, **kwargs
54
+ eval Coffee.compile script, ** kwargs
52
55
  end
53
56
 
54
57
 
55
58
  # Execute script in a new context with optionally set vars. @see H8#set_all
56
59
  # @return [Value] wrapped object returned by the script
57
- def self.eval script, file_name: nil, **kwargs
60
+ def self.eval script, file_name: nil, ** kwargs
58
61
  Context.new(** kwargs).eval script, file_name: file_name
59
62
  end
60
63
 
@@ -67,12 +70,12 @@ module H8
67
70
  def self.secure_call instance, method, args=nil
68
71
  method = method.to_sym
69
72
  begin
70
- m = instance.public_method(method)
73
+ m = instance.public_method(method)
71
74
  owner = m.owner
72
75
  if can_access?(owner)
73
76
  return m.call(*args) if method[0] == '[' || method[-1] == '='
74
77
  if m.arity != 0
75
- return -> (*args) { m.call *args }
78
+ return ProcGate.new( -> (*args) { m.call *args } )
76
79
  else
77
80
  return m.call
78
81
  end
@@ -80,7 +83,7 @@ module H8
80
83
  rescue NameError
81
84
  # No exact method, calling []/[]= if any
82
85
  method, args = if method[-1] == '='
83
- [:[]=, [method[0..-2].to_s, args[0]] ]
86
+ [:[]=, [method[0..-2].to_s, args[0]]]
84
87
  else
85
88
  [:[], [method.to_s]]
86
89
  end
@@ -88,7 +91,11 @@ module H8
88
91
  m = instance.public_method(method)
89
92
  if can_access?(owner)
90
93
  if method == :[]
91
- return m.call(*args) || m.call(args[0].to_sym)
94
+ if instance.is_a?(Hash)
95
+ return m.call(*args) || m.call(args[0].to_sym)
96
+ else
97
+ return m.call(*args)
98
+ end
92
99
  else
93
100
  return m.call(*args)
94
101
  end
@@ -115,10 +122,13 @@ module H8
115
122
  # Set var that could be either a callable, class instance, simple value or a Class class
116
123
  # in which case constructor function will be created
117
124
  def set_var name, value
118
- if value.is_a?(Class)
119
- _gate_class name.to_s, -> (*args) { value.new *args }
120
- else
121
- _set_var name, value
125
+ case value
126
+ when Class
127
+ _gate_class name.to_s, -> (*args) { value.new *args }
128
+ when Proc
129
+ _set_var name, ProcGate.new(value)
130
+ else
131
+ _set_var name, value
122
132
  end
123
133
  end
124
134
 
@@ -127,6 +137,36 @@ module H8
127
137
  def _do_create_ruby_class(klass, arguments)
128
138
  klass.new *H8::arguments_to_a(arguments.to_ruby.values)
129
139
  end
140
+
141
+
142
+ @@base = File.expand_path File.join(File.dirname(__FILE__), '../scripts')
143
+ @@cache = {}
144
+
145
+ def execute_script name
146
+ p [:exs, name]
147
+ script = @@cache[name] ||= begin
148
+ p 'cache miss'
149
+ script = open(File.join(@@base, name), 'r').read
150
+ name.downcase.end_with?('.coffee') ? H8::Coffee.compile(script) : script
151
+ end
152
+ eval script
153
+ end
154
+
155
+ end
156
+
157
+ # The gate for Ruby's callable to support javascript's 'apply' functionality
158
+ class ProcGate
159
+ def initialize callable
160
+ @callable = callable
161
+ end
162
+
163
+ def apply this, args
164
+ @callable.call *args
165
+ end
166
+
167
+ def call *args
168
+ @callable.call *args
169
+ end
130
170
  end
131
171
 
132
172
  end
@@ -1,3 +1,5 @@
1
+ require 'ostruct'
2
+
1
3
  module H8
2
4
 
3
5
  # Wrapper for javascript objects.
@@ -179,6 +181,37 @@ module H8
179
181
 
180
182
  end
181
183
 
184
+ class OpenStruct
185
+ # OpenStruct converts to plain ruby hash in depth. Primary usage
186
+ # is when it was used bu javascript and could contain gated objects.
187
+ def to_ruby depth=0
188
+ to_h.to_ruby depth+1
189
+ end
190
+ end
191
+
192
+ class Hash
193
+ # Hash copies in depth converting its data. Primary usage
194
+ # is when it was used bu javascript and could contain gated objects.
195
+ #
196
+ # Important! that converted keys are turned to string even of were
197
+ # pure ruby symbols. This is done to remove ambiguity: work the same
198
+ # with ruby hashes, javasctipt objects, OpenStruct and Hashie::Mash instances
199
+ def to_ruby depth=0
200
+ res = {}
201
+ depth += 1
202
+ each { |k,v| res[k.to_ruby(depth).to_s] = v.to_ruby depth }
203
+ res
204
+ end
205
+ end
206
+
207
+ class Array
208
+ # @return new array with all components converted to_ruby
209
+ def to_ruby depth
210
+ depth += 1
211
+ map { |x| x.to_ruby depth }
212
+ end
213
+ end
214
+
182
215
  # Ruby object's t_ruby does nothing (tree conversion optimization)
183
216
  class Object
184
217
  # It is already a ruby object. Gate objects should override
@@ -1,3 +1,3 @@
1
1
  module H8
2
- VERSION = "0.4.5"
2
+ VERSION = "0.4.8"
3
3
  end
@@ -1,4 +1,5 @@
1
1
  @.puts ?= ->
2
2
  # Do nothing if not set
3
3
 
4
- @.globalsIncluded = true
4
+ @globalsIncluded = true
5
+
@@ -35,7 +35,7 @@ describe 'coffeescript' do
35
35
  # above is bad
36
36
  END
37
37
  # pending
38
- expect(->{
38
+ expect(-> {
39
39
  H8::Coffee.compile script, file_name: 'test.coffee'
40
40
  }).to raise_error(H8::JsError) { |e| e.to_s.should =~ /test.coffee\:4/ }
41
41
  end
@@ -63,15 +63,49 @@ describe 'coffeescript' do
63
63
  // CoffeeScript.sourceMaps['inner'] = res.sourceMap
64
64
  eval(res.js);
65
65
  END
66
- cxt = H8::Coffee.new.context
66
+ cxt = H8::Coffee.new.context
67
67
  cxt[:puts] = -> (*args) { puts args.join(' ') }
68
- cxt[:src] = src
68
+ cxt[:src] = src
69
69
  # cxt[:src] = 'return "hello"'
70
70
  begin
71
- res = cxt.eval script, file_name: 'extest.coffee'
72
- rescue Exception=>e
71
+ res = cxt.eval script, file_name: 'extest.coffee'
72
+ rescue Exception => e
73
73
  puts e
74
74
  end
75
75
  end
76
76
 
77
+ context 'realword' do
78
+ class Room
79
+ end
80
+
81
+ it 'should process varargs' do
82
+ c = H8::Context.new
83
+ c[:puts] = ->(*args) { puts "> "+args.join('') }
84
+ c.coffee <<-End
85
+ @.fn1 = (args...) ->
86
+ args.join(',')
87
+
88
+ @.test = (args...) ->
89
+ fn1 'first', args...
90
+ End
91
+ c.coffee('return test(1,2,3);').should == 'first,1,2,3'
92
+
93
+ # Real world example
94
+ c[:r3] = -> (*args) {
95
+ @last_args = args
96
+ }
97
+ c[:r4] = -> (first, second=0) {
98
+ [first+100, second]
99
+ }
100
+ script = <<-End
101
+ @r1 = (args...) ->
102
+ r3 'done', args[0..-2]...
103
+ @r2 = (args...) ->
104
+ r4 args...
105
+ End
106
+ c.coffee script
107
+ c.eval('r1( "now", 1, 2, 4);').should == ['done', 'now', 1, 2]
108
+ c.eval('r2( 100, 200);').should == [200, 200]
109
+ end
110
+ end
77
111
  end
@@ -1,5 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'h8'
3
+ require 'ostruct'
4
+ require 'hashie'
3
5
 
4
6
  describe 'ruby gate' do
5
7
 
@@ -173,7 +175,7 @@ describe 'ruby gate' do
173
175
  end
174
176
 
175
177
  class Test < Base
176
- attr :ro
178
+ attr :ro, :val
177
179
  attr_accessor :rw
178
180
 
179
181
  def initialize
@@ -216,7 +218,7 @@ describe 'ruby gate' do
216
218
  end
217
219
 
218
220
  it 'should access object properties and methods' do
219
- cxt = H8::Context.new
221
+ cxt = H8::Context.new
220
222
  cxt.eval('RubyGate.prototype.test2 = function() { return "ttt"; }; null;');
221
223
  cxt[:foo] = Test.new
222
224
  cxt.eval('foo.ro').should == 'readonly'
@@ -231,19 +233,11 @@ describe 'ruby gate' do
231
233
  cxt.eval('foo.object_id').should == H8::Undefined
232
234
  cxt.eval('foo.prot_method').should == H8::Undefined
233
235
  cxt.eval('foo.priv_method').should == H8::Undefined
234
- cxt.eval('foo.test_args').should be_kind_of(Proc)
236
+ cxt.eval('foo.test_args').should be_kind_of(H8::ProcGate)
235
237
  cxt.eval('foo.test_args("hi", "you")').should == 'hi-you'
236
238
  cxt.eval('foo instanceof RubyGate').should == true
237
239
  end
238
240
 
239
- it 'should set ruby properties' do
240
- cxt = H8::Context.new
241
- cxt[:foo] = t = Test.new
242
- cxt.eval('foo.rw="hello";')
243
- t.rw.should == 'hello'
244
- cxt.eval('foo.rw').should == 'hello'
245
- end
246
-
247
241
  context 'do interceptors' do
248
242
  class Test2 < Base
249
243
  attr :ro
@@ -280,7 +274,7 @@ describe 'ruby gate' do
280
274
  cxt.eval('foo.send').should == H8::Undefined
281
275
  cxt.eval('foo.prot_method').should == H8::Undefined
282
276
  cxt.eval('foo.priv_method').should == H8::Undefined
283
- cxt.eval('foo.test_args').should be_kind_of(Proc)
277
+ cxt.eval('foo.test_args').should be_kind_of(H8::ProcGate)
284
278
  cxt.eval('foo.test_args("hi", "you")').should == 'hi-you'
285
279
  end
286
280
 
@@ -294,15 +288,37 @@ describe 'ruby gate' do
294
288
  end
295
289
 
296
290
  it 'should add and intercept property access' do
297
- # pending
298
291
  t = Test.new
299
292
  t.do_throw = true
300
293
  cxt = H8::Context.new t: t
294
+
295
+ # see Test class implementation: this is a valid test
301
296
  cxt.eval("t['foo'];").should == 'init[]'
297
+ cxt.eval("t.foo").should == 'init[]'
302
298
  expect(-> { cxt.eval("t['foo1'];") }).to raise_error(RuntimeError)
303
- cxt.eval("t['foo']='bar'");
304
- # p t['foo']
305
- # p cxt.eval "t['foo'] = 'bar';"
299
+ cxt.eval("t.foo='bar'");
300
+ cxt.eval("t.foo;").should == 'bar'
301
+ t.val.should == 'bar'
302
+ end
303
+
304
+ it 'should allow adding data to ruby hash' do
305
+ [OpenStruct.new, Hashie::Mash.new].each { |s|
306
+ s.test = 'foo'
307
+ c = H8::Context.new
308
+ c[:data] = s
309
+ c[:assert] = -> (cond) { !cond and raise "assertion failed" }
310
+ c.coffee 'assert data.test == "foo"'
311
+ c.coffee 'data.test = "bar"; assert data.test == "bar"'
312
+ s.test.should == 'bar'
313
+ c.coffee 'data.foo = "baz"'
314
+ s.foo.should == 'baz'
315
+ c.coffee 'data.h = { foo: "bar", bar: { baz: 1 } }'
316
+ s.h.foo.should == 'bar'
317
+ c.coffee 'data.h.bar.baz == 1'
318
+ s.h.bar.baz.should == 1
319
+ c.coffee 'data.h.bar.arr = ["hello", { one: 2 }]'
320
+ s.to_ruby.should == { 'test' => "bar", 'foo' => "baz", 'h' => { "foo" => "bar", "bar" => { "baz" => 1, "arr" => ["hello", { "one" => 2 }] } } }
321
+ }
306
322
  end
307
323
 
308
324
  it 'should access plain arrays (provide numeric indexes)' do
@@ -341,9 +357,9 @@ describe 'ruby gate' do
341
357
  end
342
358
 
343
359
  it 'should gate classes through API' do
344
- c = H8::Context.new
360
+ c = H8::Context.new
345
361
  la = -> (*args) {
346
- { 'hello' => 'world'}
362
+ { 'hello' => 'world' }
347
363
  }
348
364
  c._gate_class 'Tec', la
349
365
  c.eval("var res = new Tec()")
@@ -372,6 +388,10 @@ describe 'ruby gate' do
372
388
  self
373
389
  end
374
390
 
391
+ def testm a1, a2='???'
392
+ "#{a1} - #{a2}"
393
+ end
394
+
375
395
  def to_str
376
396
  inspect
377
397
  end
@@ -391,18 +411,24 @@ describe 'ruby gate' do
391
411
  cxt.eval('rc.init_args').should == ['hello', 'world']
392
412
  end
393
413
 
414
+ it 'should provide apply to gated class and instance' do
415
+ c = H8::Context.new RClass: Gated
416
+ c.eval('new RClass().testm(1,"d");').should == '1 - d'
417
+ c.eval('new RClass().testm.apply( null, [1,"n"]);').should == '1 - n'
418
+ end
419
+
394
420
  it 'should not die on calling wrong arity' do
395
421
  cxt = H8::Context.new RClass: Gated
396
- g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
422
+ g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
397
423
 
398
424
  # We call gated ruby object with wrong number of args
399
425
  # which in turn causes attempt to call not callable result:
400
- expect(-> {cxt.eval('g1.checkself(12)')}).to raise_error(NoMethodError)
426
+ expect(-> { cxt.eval('g1.checkself(12)') }).to raise_error(NoMethodError)
401
427
  end
402
428
 
403
429
  it 'should return self from gated class' do
404
430
  cxt = H8::Context.new RClass: Gated
405
- g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
431
+ g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
406
432
  g1.should be_a(Gated)
407
433
  g2 = cxt.eval 'g1.checkself'
408
434
  g2.should be_a(Gated)
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.5
4
+ version: 0.4.8
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-21 00:00:00.000000000 Z
11
+ date: 2015-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: hashie
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.2
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: pargser
71
85
  requirement: !ruby/object:Gem::Requirement