rufus-lua 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,18 @@
2
2
  = rufus-lua CHANGELOG.txt
3
3
 
4
4
 
5
+ == rufus-lua - 1.1.2 released 2014/09/13
6
+
7
+ - properly turn Ruby nils into Lua nils (Christopher Saunders)
8
+ - get rid of Lib.strlen (as suggested by Stas Ukolov)
9
+ - let Ruby callbacks accept Lua functions as arguments (Gian Perrone)
10
+ - add #set_error_handler (based on an initial request by Nathanael Jones)
11
+ - fix unstack issue when returning table (https://github.com/lywaterman)
12
+ - use "{filename}:#{lineno}" as eval chunk name (suggested by Nathanael Jones)
13
+ - wrap Ruby callbacks in CallbackState (Nathanael Jones)
14
+ - accept Ruby symbols containing \0 character (Nathanael Jones)
15
+
16
+
5
17
  == rufus-lua - 1.1.1 released 2014/07/01
6
18
 
7
19
  - move from bacon to rspec
@@ -11,9 +11,17 @@ Scott Persinger https://github.com/scottpersinger
11
11
 
12
12
  == contributors
13
13
 
14
+ Christopher Saunders https://github.com/csaunders
15
+ Gian Perrone https://github.com/quidrex
14
16
  Matthew Nielsen https://github.com/xunker
15
17
  Nathanael Jones https://github.com/nathanaeljones
16
18
 
19
+
20
+ == feedback
21
+
22
+ Stas Ukolov https://github.com/ukoloff
23
+
24
+
17
25
  == inspiration
18
26
 
19
27
  http://rubyluabridge.rubyforge.org/
data/README.md CHANGED
@@ -168,6 +168,87 @@ lua = Rufus::Lua::State.new
168
168
  lua.eval('print("hello")', nil, 'myluastuff/hello.lua', 77)
169
169
  ```
170
170
 
171
+ ### set_error_handler
172
+
173
+ `set_error_handler` gives a little bit of control on how error messages are prepared when errors occur in the Lua interpreter.
174
+
175
+ Here are set of examples for each of the possible error handler kind: Lua code, Ruby block, `:traceback`.
176
+
177
+ ```ruby
178
+ require 'rufus-lua'
179
+
180
+ lua = Rufus::Lua::State.new
181
+
182
+ #
183
+ # no error handler
184
+
185
+ begin
186
+ lua.eval('error("ouch!")')
187
+ rescue => e
188
+ puts(e)
189
+ end
190
+ # --> eval:pcall : '[string "line"]:1: ouch!' (2 LUA_ERRRUN)
191
+
192
+ #
193
+ # Lua error handler
194
+
195
+ lua.set_error_handler(%{
196
+ function (msg)
197
+ return 'something went wrong: ' .. string.gmatch(msg, ": (.+)$")()
198
+ end
199
+ })
200
+
201
+ begin
202
+ lua.eval('error("ouch!")')
203
+ rescue => e
204
+ puts(e)
205
+ end
206
+ # --> eval:pcall : 'something went wrong: ouch!' (2 LUA_ERRRUN)
207
+
208
+ #
209
+ # Ruby block error handler
210
+
211
+ lua.set_error_handler do |msg|
212
+ ([ msg.split.last ] * 3).join(' ')
213
+ end
214
+
215
+ begin
216
+ lua.eval('error("ouch!")')
217
+ rescue => e
218
+ puts(e)
219
+ end
220
+ # --> eval:pcall : 'ouch! ouch! ouch!' (2 LUA_ERRRUN)
221
+
222
+ #
223
+ # prepackaged :traceback handler
224
+
225
+ lua.set_error_handler(:traceback)
226
+
227
+ begin
228
+ lua.eval('error("ouch!")')
229
+ rescue => e
230
+ puts(e)
231
+ end
232
+ # -->
233
+ # eval:pcall : '[string "line"]:1: ouch!
234
+ # stack traceback:
235
+ # [C]: in function 'error'
236
+ # [string "line"]:1: in main chunk' (2 LUA_ERRRUN)
237
+
238
+ #
239
+ # unset the error handler
240
+
241
+ lua.set_error_handler(nil)
242
+
243
+ begin
244
+ lua.eval('error("ouch!")')
245
+ rescue => e
246
+ puts(e)
247
+ end
248
+ # --> eval:pcall : '[string "line"]:1: ouch!' (2 LUA_ERRRUN)
249
+ # (back to default)
250
+ ```
251
+
171
252
 
172
253
  ## compiling liblua.dylib
173
254
 
@@ -216,7 +297,7 @@ sudo cp src/liblua.dylib /usr/local/lib/liblua.5.1.4.dylib
216
297
 
217
298
  ## tested with
218
299
 
219
- ruby 1.8.7p72, ruby 1.9.1p0, jruby 1.2.0
300
+ ruby 1.9.1p0, jruby 1.2.0
220
301
  jruby 1.1.6 has an issue with errors raised inside of Ruby functions (callbacks)
221
302
 
222
303
  ruby-ffi 0.4.0 and 0.5.0
@@ -25,25 +25,29 @@
25
25
 
26
26
  module Rufus::Lua
27
27
 
28
+ # error codes from 0 to 5
29
+ #
30
+ LUA_ERRS = %w[ 0 LUA_YIELD LUA_ERRRUN LUA_ERRSYNTAX LUA_ERRMEM LUA_ERRERR ]
31
+
28
32
  #
29
33
  # An error class for rufus-lua.
30
34
  #
31
35
  class LuaError < RuntimeError
32
36
 
33
37
  attr_reader :kind, :errcode, :msg
34
- attr_reader :binding, :filename, :lineno
38
+ attr_reader :bndng, :filename, :lineno
35
39
 
36
40
  attr_reader :original_backtrace
37
41
 
38
- def initialize(kind, errcode, msg, binding, filename, lineno)
42
+ def initialize(kind, errcode, msg, bndng, filename, lineno)
39
43
 
40
- super("#{kind} : '#{msg}' (#{errcode})")
44
+ super("#{kind} : '#{msg}' (#{errcode} #{LUA_ERRS[errcode]})")
41
45
 
42
46
  @kind = kind
43
47
  @errcode = errcode
44
48
  @msg = msg
45
49
 
46
- @binding = binding
50
+ @bndng = bndng
47
51
  @filename = filename
48
52
  @lineno = lineno
49
53
  end
@@ -77,8 +77,6 @@ module Lua
77
77
  #
78
78
  # attach functions
79
79
 
80
- attach_function :strlen, [ :string ], :int
81
-
82
80
  attach_function :lua_close, [ :pointer ], :void
83
81
 
84
82
  attach_function :luaL_openlibs, [ :pointer ], :void
@@ -118,6 +116,16 @@ module Lua
118
116
  attach_function :lua_pushstring, [ :pointer, :string ], :pointer
119
117
  attach_function :lua_pushlstring, [ :pointer, :pointer, :int ], :pointer
120
118
 
119
+ attach_function :lua_remove, [ :pointer, :int ], :void
120
+ # removes the value at the given stack index, shifting down all elts above
121
+
122
+ #attach_function :lua_pushvalue, [ :pointer, :int ], :void
123
+ # pushes a copy of the value at the given index to the top of the stack
124
+ #attach_function :lua_insert, [ :pointer, :int ], :void
125
+ # moves the top elt to the given index, shifting up all elts above
126
+ #attach_function :lua_replace, [ :pointer, :int ], :void
127
+ # pops the top elt and override the elt at given index with it
128
+
121
129
  attach_function :lua_rawgeti, [ :pointer, :int, :int ], :void
122
130
 
123
131
  attach_function :luaL_newstate, [], :pointer
@@ -42,6 +42,7 @@ module Rufus::Lua
42
42
  @pointer = pointer
43
43
  @ref = Lib.luaL_ref(@pointer, LUA_REGISTRYINDEX)
44
44
  # this pops the object out of the stack !
45
+ @error_handler = 0
45
46
  end
46
47
 
47
48
  # Frees the reference to this object
@@ -60,7 +61,7 @@ module Rufus::Lua
60
61
  #
61
62
  def load_onto_stack
62
63
 
63
- fail RuntimeError.new(
64
+ raise RuntimeError.new(
64
65
  "#{self.class} got freed, cannot re-access it directly"
65
66
  ) unless @ref
66
67
 
@@ -286,7 +287,7 @@ module Rufus::Lua
286
287
  h = self.to_h
287
288
 
288
289
  pure && h.keys.find { |k| not [ Float ].include?(k.class) } &&
289
- raise("cannot turn hash into array, some keys are not numbers")
290
+ fail('cannot turn hash into array, some keys are not numbers')
290
291
 
291
292
  a_keys = (1..objlen).to_a.collect { |k| k.to_f }
292
293
  keys = a_keys + (h.keys - a_keys)
@@ -86,14 +86,15 @@ module Rufus::Lua
86
86
 
87
87
  # This method holds the 'eval' mechanism.
88
88
  #
89
- def loadstring_and_call(s, binding, filename, lineno)
89
+ def loadstring_and_call(s, bndng, filename, lineno)
90
90
 
91
91
  bottom = stack_top
92
+ chunk = filename ? "#{filename}:#{lineno}" : 'line'
92
93
 
93
- err = Lib.luaL_loadbuffer(@pointer, s, Lib.strlen(s), 'line')
94
- fail_if_error('eval:compile', err, binding, filename, lineno)
94
+ err = Lib.luaL_loadbuffer(@pointer, s, s.length, chunk)
95
+ fail_if_error('eval:compile', err, bndng, filename, lineno)
95
96
 
96
- pcall(bottom, 0, binding, filename, lineno) # arg_count is set to 0
97
+ pcall(bottom, 0, bndng, filename, lineno) # arg_count is set to 0
97
98
  end
98
99
 
99
100
  # Returns a string representation of the state's stack.
@@ -188,7 +189,8 @@ module Rufus::Lua
188
189
  def stack_pop
189
190
 
190
191
  r = stack_fetch
191
- stack_unstack
192
+ stack_unstack if r.class != Rufus::Lua::Table
193
+
192
194
  r
193
195
  end
194
196
 
@@ -200,7 +202,7 @@ module Rufus::Lua
200
202
  new_top = 0 if new_top < 0
201
203
  #
202
204
  # there are no safeguard in Lua, setting top to -2 work well
203
- # when the stack is crowed, but it has bad side effects when the
205
+ # when the stack is crowded, but it has bad side effects when the
204
206
  # stack is empty... Now safeguarding by ourselves.
205
207
 
206
208
  Lib.lua_settop(@pointer, new_top)
@@ -222,8 +224,8 @@ module Rufus::Lua
222
224
  when Fixnum then Lib.lua_pushinteger(@pointer, o)
223
225
  when Float then Lib.lua_pushnumber(@pointer, o)
224
226
 
225
- when String then Lib.lua_pushlstring(@pointer, o, o.unpack('C*').size)
226
- when Symbol then Lib.lua_pushstring(@pointer, o.to_s)
227
+ when String then Lib.lua_pushlstring(@pointer, o, o.bytesize)
228
+ when Symbol then Lib.lua_pushlstring(@pointer, o.to_s, o.to_s.bytesize)
227
229
 
228
230
  when Hash then stack_push_hash(o)
229
231
  when Array then stack_push_array(o)
@@ -269,6 +271,27 @@ module Rufus::Lua
269
271
  Lib.lua_getfield(@pointer, LUA_GLOBALSINDEX, name)
270
272
  end
271
273
 
274
+ # Loads a field of the table currently on the top of the stack
275
+ #
276
+ def stack_load_field(name)
277
+
278
+ Lib.lua_getfield(@pointer, -1, name)
279
+ end
280
+
281
+ # Loads a path (for example "debug.traceback") on top of the stack.
282
+ #
283
+ def stack_load_path(path)
284
+
285
+ ps = path.split('.')
286
+
287
+ stack_load_global(ps.shift)
288
+
289
+ while ps.first
290
+ stack_load_field(ps.shift)
291
+ Lib.lua_remove(@pointer, -2)
292
+ end
293
+ end
294
+
272
295
  # Loads the Lua object registered with the given ref on top of the stack
273
296
  #
274
297
  def stack_load_ref(ref)
@@ -294,14 +317,14 @@ module Rufus::Lua
294
317
  #
295
318
  # Will raise an error in case of failure.
296
319
  #
297
- def pcall(stack_bottom, arg_count, binding, filename, lineno)
320
+ def pcall(stack_bottom, arg_count, bndng, filename, lineno)
298
321
 
299
322
  #err = Lib.lua_pcall(@pointer, 0, 1, 0)
300
- # when there's only 1 return value, use LUA_MULTRET (-1) the
301
- # rest of the time
323
+ # When there's only 1 return value.
324
+ # Use LUA_MULTRET (-1) the rest of the time
302
325
 
303
- err = Lib.lua_pcall(@pointer, arg_count, LUA_MULTRET, 0)
304
- fail_if_error('eval:pcall', err, binding, filename, lineno)
326
+ err = Lib.lua_pcall(@pointer, arg_count, LUA_MULTRET, @error_handler)
327
+ fail_if_error('eval:pcall', err, bndng, filename, lineno)
305
328
 
306
329
  return_result(stack_bottom)
307
330
  end
@@ -320,21 +343,14 @@ module Rufus::Lua
320
343
  # This method will raise an error with err > 0, else it will immediately
321
344
  # return.
322
345
  #
323
- def fail_if_error(kind, err, binding, filename, lineno)
346
+ def fail_if_error(kind, err, bndng, filename, lineno)
324
347
 
325
348
  return if err < 1
326
349
 
327
- # TODO :
328
- #
329
- # LUA_ERRRUN: a runtime error.
330
- # LUA_ERRMEM: memory allocation error. For such errors, Lua does not call
331
- # the error handler function.
332
- # LUA_ERRERR: error while running the error handler function.
333
-
334
350
  s = Lib.lua_tolstring(@pointer, -1, nil).read_string
335
351
  Lib.lua_settop(@pointer, -2)
336
352
 
337
- fail LuaError.new(kind, err, s, binding, filename, lineno)
353
+ fail LuaError.new(kind, err, s, bndng, filename, lineno)
338
354
  end
339
355
 
340
356
  # Given the name of a Lua global variable, will return its value (or nil
@@ -347,6 +363,17 @@ module Rufus::Lua
347
363
  end
348
364
  end
349
365
 
366
+ class CallbackState
367
+ include StateMixin
368
+
369
+ def initialize(pointer)
370
+
371
+ @pointer = pointer
372
+ @callbacks = []
373
+ @error_handler = 0
374
+ end
375
+ end
376
+
350
377
  #
351
378
  # A Lua state, wraps a Lua runtime.
352
379
  #
@@ -389,13 +416,44 @@ module Rufus::Lua
389
416
  # garbage collection (Scott).
390
417
 
391
418
  @callbacks = []
419
+
420
+ @error_handler = 0
421
+ end
422
+
423
+ def set_error_handler(lua_code=nil, &block)
424
+
425
+ if lua_code == nil && block
426
+
427
+ function('_ruby_error_handler', &block)
428
+ stack_load_path('_ruby_error_handler')
429
+ @error_handler = stack_top
430
+
431
+ elsif lua_code == nil
432
+
433
+ Lib.lua_remove(@pointer, @error_handler) if @error_handler > 0
434
+ @error_handler = 0
435
+
436
+ elsif lua_code == :traceback
437
+
438
+ stack_load_path('debug.traceback')
439
+ @error_handler = stack_top
440
+
441
+ else
442
+
443
+ lua_code = lua_code.strip
444
+ lua_code = 'return ' + lua_code unless lua_code.match(/^return\s+/)
445
+
446
+ r = self.eval(lua_code)
447
+ r.send(:load_onto_stack)
448
+ @error_handler = stack_top
449
+ end
392
450
  end
393
451
 
394
452
  # Evaluates a piece (string) of Lua code within the state.
395
453
  #
396
- def eval(s, binding=nil, filename=nil, lineno=nil)
454
+ def eval(s, bndng=nil, filename=nil, lineno=nil)
397
455
 
398
- loadstring_and_call(s, binding, filename, lineno)
456
+ loadstring_and_call(s, bndng, filename, lineno)
399
457
  end
400
458
 
401
459
  # Returns a value set at the 'global' level in the state.
@@ -462,24 +520,24 @@ module Rufus::Lua
462
520
  #
463
521
  def function(name, opts={}, &block)
464
522
 
465
- raise 'please pass a block for the body of the function' unless block
523
+ fail 'please pass a block for the body of the function' unless block
466
524
 
467
525
  to_ruby = opts[:to_ruby]
468
526
 
469
527
  callback = Proc.new do |state|
470
528
 
529
+ s = CallbackState.new(state)
471
530
  args = []
472
531
 
473
532
  loop do
474
533
 
475
- break if stack_top == 0 # never touch stack[0] !!
534
+ break if s.stack_top == 0 # never touch stack[0] !!
476
535
 
477
- arg = stack_fetch
478
- break if arg.class == Rufus::Lua::Function
536
+ arg = s.stack_fetch
479
537
 
480
538
  args.unshift(arg)
481
539
 
482
- stack_unstack unless args.first.is_a?(Rufus::Lua::Table)
540
+ s.stack_unstack unless args.first.is_a?(Rufus::Lua::Ref)
483
541
  end
484
542
 
485
543
  while args.size < block.arity
@@ -491,7 +549,7 @@ module Rufus::Lua
491
549
 
492
550
  result = block.call(*args)
493
551
 
494
- stack_push(result)
552
+ s.stack_push(result)
495
553
 
496
554
  1
497
555
  end
@@ -538,7 +596,7 @@ module Rufus::Lua
538
596
  #
539
597
  def close
540
598
 
541
- raise "State already closed" unless @pointer
599
+ fail 'State already closed' unless @pointer
542
600
  Lib.lua_close(@pointer)
543
601
  @pointer = nil
544
602
  end
@@ -547,7 +605,7 @@ module Rufus::Lua
547
605
  #
548
606
  def gc_count
549
607
 
550
- raise "State got closed, cannot proceed" unless @pointer
608
+ fail 'State got closed, cannot proceed' unless @pointer
551
609
  Lib.lua_gc(@pointer, LUA_GCCOUNT, 0)
552
610
  end
553
611
 
@@ -555,7 +613,7 @@ module Rufus::Lua
555
613
  #
556
614
  def gc_collect!
557
615
 
558
- raise "State got closed, cannot proceed" unless @pointer
616
+ fail 'State got closed, cannot proceed' unless @pointer
559
617
  Lib.lua_gc(@pointer, LUA_GCCOLLECT, 0)
560
618
  end
561
619
 
@@ -563,7 +621,7 @@ module Rufus::Lua
563
621
  #
564
622
  def gc_stop
565
623
 
566
- raise "State got closed, cannot proceed" unless @pointer
624
+ fail 'State got closed, cannot proceed' unless @pointer
567
625
  Lib.lua_gc(@pointer, LUA_GCSTOP, 0)
568
626
  end
569
627
 
@@ -571,7 +629,7 @@ module Rufus::Lua
571
629
  #
572
630
  def gc_resume
573
631
 
574
- raise "State got closed, cannot proceed" unless @pointer
632
+ fail 'State got closed, cannot proceed' unless @pointer
575
633
  Lib.lua_gc(@pointer, LUA_GCRESTART, 0)
576
634
  end
577
635
 
@@ -48,6 +48,7 @@ module Rufus::Lua
48
48
  when Float then o.to_s
49
49
  when TrueClass then o.to_s
50
50
  when FalseClass then o.to_s
51
+ when NilClass then 'nil'
51
52
 
52
53
  when Hash then to_lua_table_s(o)
53
54
  when Array then to_lua_table_s(o)
@@ -26,7 +26,7 @@
26
26
  module Rufus
27
27
  module Lua
28
28
 
29
- VERSION = '1.1.1'
29
+ VERSION = '1.1.2'
30
30
  end
31
31
  end
32
32
 
@@ -68,15 +68,12 @@ describe Rufus::Lua::State do
68
68
  #
69
69
  it 'yields the right value' do
70
70
 
71
- pending 'yield across callback no worky on Lua 5.1.x'
72
-
73
71
  @s.function :host_function do
74
72
  'success'
75
73
  end
76
74
  r = @s.eval(%{
77
75
  function routine()
78
76
  coroutine.yield(host_function())
79
- --coroutine.yield("success") -- that works :-(
80
77
  end
81
78
  co = coroutine.create(routine)
82
79
  a, b = coroutine.resume(co)
@@ -85,6 +82,55 @@ describe Rufus::Lua::State do
85
82
 
86
83
  expect(r).to eq([ true, 'success' ])
87
84
  end
85
+
86
+ it 'executes a ruby function within a coroutine' do
87
+
88
+ run_count = 0
89
+
90
+ @s.function :host_function do
91
+ run_count += 1
92
+ end
93
+ r = @s.eval(%{
94
+ function routine()
95
+ host_function()
96
+ coroutine.yield()
97
+ host_function()
98
+ coroutine.yield()
99
+ end
100
+ co = coroutine.create(routine)
101
+ a, b = coroutine.resume(co)
102
+ a, b = coroutine.resume(co)
103
+ return { a, b }
104
+ }).to_ruby
105
+
106
+ expect(r).to eq([ true])
107
+ expect(run_count).to eq(2)
108
+ end
109
+
110
+ it 'executes a ruby function (with arguments) within a coroutine' do
111
+
112
+ run_count = 0
113
+ last_arguments = nil
114
+
115
+ @s.function :host_function, { :to_ruby => true } do |*args|
116
+ run_count += 1
117
+ last_arguments = args
118
+ 0
119
+ end
120
+ r = @s.eval(%{
121
+ function routine()
122
+ host_function("hi")
123
+ coroutine.yield()
124
+ end
125
+ co = coroutine.create(routine)
126
+ a, b = coroutine.resume(co)
127
+ return { a, b }
128
+ }).to_ruby
129
+
130
+ expect(r).to eq([ true ])
131
+ expect(run_count).to eq(1)
132
+ expect(last_arguments).to eq([ "hi" ])
133
+ end
88
134
  end
89
135
  end
90
136
 
@@ -0,0 +1,222 @@
1
+
2
+ #
3
+ # Specifying rufus-lua
4
+ #
5
+ # Tue Jul 22 21:07:10 JST 2014
6
+ #
7
+
8
+ require 'spec_base'
9
+
10
+
11
+ describe 'State and error handler' do
12
+
13
+ before do
14
+ @s = Rufus::Lua::State.new
15
+ end
16
+ after do
17
+ @s.close
18
+ end
19
+
20
+ describe '#set_error_handler(lua_code)' do
21
+
22
+ it 'registers a function as error handler' do
23
+
24
+ le = nil
25
+
26
+ @s.set_error_handler(%{
27
+ function (e)
28
+ return e .. '\\n' .. debug.traceback()
29
+ end
30
+ })
31
+
32
+ @s.eval(%{
33
+ function f ()
34
+ error("in f")
35
+ end
36
+ }, nil, 'mystuff.lua', 77)
37
+ begin
38
+ @s.eval('f()', nil, 'mymain.lua', 88)
39
+ rescue Rufus::Lua::LuaError => le
40
+ end
41
+
42
+ expect(le.message).to eq(%{
43
+ eval:pcall : '[string "mystuff.lua:77"]:3: in f
44
+ stack traceback:
45
+ [string "line"]:2: in function <[string "line"]:1>
46
+ [C]: in function 'error'
47
+ [string "mystuff.lua:77"]:3: in function 'f'
48
+ [string "mymain.lua:88"]:1: in main chunk' (2 LUA_ERRRUN)
49
+ }.strip)
50
+
51
+ expect(@s.send(:stack_top)).to eq(1)
52
+ end
53
+
54
+ it 'sets the error handler in a permanent way' do
55
+
56
+ le = nil
57
+
58
+ @s.set_error_handler(%{
59
+ function (e)
60
+ return 'something went wrong: ' .. string.gmatch(e, ": (.+)$")()
61
+ end
62
+ })
63
+
64
+ begin
65
+ @s.eval('error("a")')
66
+ rescue Rufus::Lua::LuaError => le
67
+ end
68
+
69
+ expect(le.msg).to eq('something went wrong: a')
70
+
71
+ begin
72
+ @s.eval('error("b")')
73
+ rescue Rufus::Lua::LuaError => le
74
+ end
75
+
76
+ expect(le.msg).to eq('something went wrong: b')
77
+ end
78
+
79
+ context 'CallbackState' do
80
+
81
+ # Setting an error handler on the calling state or even directly
82
+ # on the CallbackState doesn't have any effect.
83
+ # Any error handler seems bypassed.
84
+
85
+ it 'has no effect' do
86
+
87
+ e = nil
88
+
89
+ @s.set_error_handler(%{
90
+ function (e) return 'bad: ' .. string.gmatch(e, ": (.+)$")() end
91
+ })
92
+ f = @s.function(:do_fail) { fail('in style') }
93
+ begin
94
+ @s.eval('do_fail()')
95
+ rescue Exception => e
96
+ end
97
+
98
+ expect(e.class).to eq(RuntimeError)
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#set_error_handler(:traceback)' do
104
+
105
+ it 'sets a vanilla debug.traceback() error handler' do
106
+
107
+ le = nil
108
+
109
+ @s.set_error_handler(:traceback)
110
+
111
+ @s.eval(%{
112
+ function f ()
113
+ error("in f")
114
+ end
115
+ }, nil, 'mystuff.lua', 77)
116
+ begin
117
+ @s.eval('f()', nil, 'mymain.lua', 88)
118
+ rescue Rufus::Lua::LuaError => le
119
+ end
120
+
121
+ expect(le.message).to eq(%{
122
+ eval:pcall : '[string "mystuff.lua:77"]:3: in f
123
+ stack traceback:
124
+ [C]: in function 'error'
125
+ [string "mystuff.lua:77"]:3: in function 'f'
126
+ [string "mymain.lua:88"]:1: in main chunk' (2 LUA_ERRRUN)
127
+ }.strip)
128
+ end
129
+ end
130
+
131
+ describe '#set_error_handler(:backtrace)' do
132
+
133
+ it 'sets a special handler that provides a merged Ruby then Lua backtrace'
134
+ # does it make any sense?
135
+ end
136
+
137
+ describe '#set_error_handler(&ruby_block)' do
138
+
139
+ it 'sets a Ruby callback as handler' do
140
+
141
+ le = nil
142
+
143
+ @s.set_error_handler do |msg|
144
+ ([ msg.split.last ] * 3).join(' ')
145
+ end
146
+
147
+ begin
148
+ @s.eval('error("tora")')
149
+ rescue Rufus::Lua::LuaError => le
150
+ end
151
+
152
+ expect(le.msg).to eq('tora tora tora')
153
+ end
154
+ end
155
+
156
+ describe '#set_error_handler(nil)' do
157
+
158
+ it 'unsets the current error handler' do
159
+
160
+ le = nil
161
+
162
+ # set
163
+
164
+ @s.set_error_handler(%{
165
+ function (e)
166
+ return 'something went wrong: ' .. string.gmatch(e, ": (.+)$")()
167
+ end
168
+ })
169
+
170
+ begin
171
+ @s.eval('error("a")')
172
+ rescue Rufus::Lua::LuaError => le
173
+ end
174
+
175
+ expect(le.msg).to eq('something went wrong: a')
176
+
177
+ # unset
178
+
179
+ @s.set_error_handler(nil)
180
+
181
+ begin
182
+ @s.eval('error("b")')
183
+ rescue Rufus::Lua::LuaError => le
184
+ end
185
+
186
+ expect(le.msg).to eq('[string "line"]:1: b')
187
+ expect(@s.send(:stack_top)).to eq(0)
188
+ end
189
+ end
190
+
191
+ describe '#set_error_handler(x)' do
192
+
193
+ it 'pops the previous handler from the stack' do
194
+
195
+ le = nil
196
+
197
+ expect(@s.send(:stack_top)).to eq(0)
198
+
199
+ @s.set_error_handler(%{ function (e) return "a" end })
200
+ #@s.set_error_handler(:traceback)
201
+ #@s.set_error_handler do |msg| return msg * 2; end
202
+
203
+ #@s.send(:print_stack)
204
+
205
+ expect(@s.send(:stack_top)).to eq(1)
206
+
207
+ @s.set_error_handler(%{ function (e) return "b" end })
208
+
209
+ #@s.send(:print_stack)
210
+
211
+ expect(@s.send(:stack_top)).to eq(1)
212
+
213
+ begin
214
+ @s.eval('error("Z")')
215
+ rescue Rufus::Lua::LuaError => le
216
+ end
217
+
218
+ expect(le.msg).to eq('b')
219
+ end
220
+ end
221
+ end
222
+
@@ -72,6 +72,28 @@ describe Rufus::Lua::State do
72
72
  expect(@s.eval('return 1, 2')).to eq [ 1.0, 2.0 ]
73
73
  end
74
74
 
75
+ it 'returns multiple values (tables included)' do
76
+
77
+ r = @s.eval('return 1, 2, {}')
78
+
79
+ expect(r.class).to eq Array
80
+ expect(r[0, 2]).to eq [ 1.0, 2.0 ]
81
+ expect(r[2].class).to eq Rufus::Lua::Table
82
+ expect(r[2].size).to eq 0
83
+ end
84
+
85
+ it 'returns multiple values (tables included) (take 2)' do
86
+
87
+ r = @s.eval('return { "hello", "world" }, 2, { 3 }')
88
+
89
+ expect(r[0].class).to eq Rufus::Lua::Table
90
+ expect(r[0][1]).to eq 'hello'
91
+ expect(r[0][2]).to eq 'world'
92
+ expect(r[1]).to eq 2.0
93
+ expect(r[2].class).to eq Rufus::Lua::Table
94
+ expect(r[2][1]).to eq 3.0
95
+ end
96
+
75
97
  it 'returns false' do
76
98
 
77
99
  expect(@s.eval('return false')).to eq false
@@ -82,6 +104,17 @@ describe Rufus::Lua::State do
82
104
  expect(@s.eval('return true')).to eq true
83
105
  end
84
106
 
107
+ it 'returns tables' do
108
+
109
+ r = @s.eval('return { "hello", "world", 2 }')
110
+
111
+ expect(r.class).to eq Rufus::Lua::Table
112
+ expect(r[0]).to eq nil
113
+ expect(r[1]).to eq 'hello'
114
+ expect(r[2]).to eq 'world'
115
+ expect(r[3]).to eq 2.0
116
+ end
117
+
85
118
  it 'accepts a binding optional argument'
86
119
 
87
120
  it 'accepts a filename and a lineno optional arguments' do
@@ -92,6 +125,10 @@ describe Rufus::Lua::State do
92
125
  rescue Rufus::Lua::LuaError => le
93
126
  end
94
127
 
128
+ expect(le.kind).to eq('eval:pcall')
129
+ expect(le.msg).to eq('[string "/nada/virtual.lua:63"]:1: 77')
130
+ expect(le.errcode).to eq(2)
131
+
95
132
  expect(le.filename).to eq('/nada/virtual.lua')
96
133
  expect(le.lineno).to eq(63)
97
134
 
@@ -113,8 +150,11 @@ describe Rufus::Lua::State do
113
150
  expect(le.msg).to eq('[string "line"]:1: 77')
114
151
  expect(le.errcode).to eq(2)
115
152
 
153
+ expect(le.message).to eq(
154
+ "eval:pcall : '[string \"line\"]:1: 77' (2 LUA_ERRRUN)")
155
+
116
156
  expect(le.filename).to eq(__FILE__)
117
- expect(le.lineno).to eq(__LINE__ - 9)
157
+ expect(le.lineno).to eq(__LINE__ - 12)
118
158
 
119
159
  expect(le.original_backtrace.first).to match(/\/lua\/state\.rb:/)
120
160
  expect(le.backtrace.first).to match(/\/eval_spec\.rb:/)
@@ -52,6 +52,19 @@ describe Rufus::Lua::State do
52
52
  expect(message).to eq 'obandegozaimasu'
53
53
  end
54
54
 
55
+ it 'works with function arguments' do
56
+
57
+ message = nil
58
+
59
+ @s.function :exec do |fun|
60
+ message = fun.call()
61
+ end
62
+
63
+ @s.eval("exec(function() return 'foobar' end)")
64
+
65
+ expect(message).to eq 'foobar'
66
+ end
67
+
55
68
  it 'binds functions inside of Lua tables' do
56
69
 
57
70
  @s.eval('lib = {}')
@@ -147,11 +160,11 @@ describe Rufus::Lua::State do
147
160
  it 'raise exceptions (Ruby -> Lua -> Ruby and back)' do
148
161
 
149
162
  @s.function :do_fail do
150
- raise "fail!"
163
+ raise 'fail!'
151
164
  end
152
165
 
153
166
  expect(lambda {
154
- @s.eval("return do_fail()")
167
+ @s.eval('return do_fail()')
155
168
  }).to raise_error(RuntimeError)
156
169
  end
157
170
 
@@ -41,6 +41,20 @@ describe 'lua strings' do
41
41
 
42
42
  expect(f.call(s).to_h).to eq({ 's' => s, 'l' => 8.0 })
43
43
  end
44
+
45
+ it 'accept symbols containing the \0 character' do
46
+
47
+ s = [ 65, 66, 0, 67, 0, 0, 68, 0 ].pack('c*').to_sym
48
+
49
+ f = @s.eval(%{
50
+ f = function(s)
51
+ return { s = s, l = string.len(s) }
52
+ end
53
+ return f
54
+ })
55
+
56
+ expect(f.call(s).to_h).to eq({ 's' => s.to_s, 'l' => 8.0 })
57
+ end
44
58
  end
45
59
  end
46
60
 
@@ -31,6 +31,15 @@ describe Rufus::Lua do
31
31
  '{ ["a"] = "A", ["b"] = 2 }'
32
32
  )
33
33
  end
34
+
35
+ it 'turns Ruby NilClass into Lua nil representation' do
36
+
37
+ expect(Rufus::Lua.to_lua_s(
38
+ {'a' => nil}
39
+ )).to eq(
40
+ '{ ["a"] = nil }'
41
+ )
42
+ end
34
43
  end
35
44
  end
36
45
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rufus-lua
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-06-30 00:00:00.000000000 Z
14
+ date: 2014-09-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: ffi
@@ -85,6 +85,7 @@ files:
85
85
  - spec/eval_spec.rb
86
86
  - spec/state_spec.rb
87
87
  - spec/spec_base.rb
88
+ - spec/error_handler_spec.rb
88
89
  - spec/string_spec.rb
89
90
  - spec/gc_spec.rb
90
91
  - spec/lib_spec.rb