rufus-lua 1.1.1 → 1.1.2

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.
@@ -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