class TransparentLua
Constants
- SUPPORTED_SIMPLE_DATATYPES
- VERSION
Attributes
logger[R]
sandbox[R]
state[R]
Public Class Methods
new(sandbox, options = {})
click to toggle source
@param [Object] sandbox The object which will be made visible to the lua script @param [Hash] options @option options [Lua::State] :state (Lua::State.new) a lua state to use @option options [Boolean] :leak_globals When true, all locals from the lua scope are set in the sandbox.
The sandbox must store the values itself or an error will be raised. When false the locals are not reflected in the sandbox
# File lib/transparent_lua.rb, line 24 def initialize(sandbox, options = {}) @sandbox = sandbox @state = options.fetch(:state) { Lua::State.new } @logger = options.fetch(:logger) { Logger.new('/dev/null') } leak_locals = options.fetch(:leak_globals) { false } setup(leak_locals) end
Public Instance Methods
call(script, script_name = nil)
click to toggle source
@param [String] script a lua script @param [String] script_name the name of the lua script (see Lua::State.__eval) @return [Object] the return value of the lua script
# File lib/transparent_lua.rb, line 35 def call(script, script_name = nil) v = state.__eval(script, script_name) lua2rb(v) end
Private Instance Methods
can_require_module?(modname)
click to toggle source
# File lib/transparent_lua.rb, line 65 def can_require_module?(modname) return false unless sandbox.respond_to? :can_require_module? sandbox.can_require_module? modname end
delegation_table(object = nil, hash)
click to toggle source
# File lib/transparent_lua.rb, line 131 def delegation_table(object = nil, hash) tab = Lua::Table.new(@state) tab.__rb_object_id = -> { object.__id__.to_f } if object tab.__metatable = hash tab end
get_method(object, method_name)
click to toggle source
# File lib/transparent_lua.rb, line 111 def get_method(object, method_name) method_name = get_ruby_method_name(method_name) object.method(method_name.to_sym) rescue NameError fail NoMethodError, "#{object}##{method_name.to_s} is not a method (but might be a valid message which is not supported)" end
get_ruby_method_name(lua_method_name)
click to toggle source
@param [Symbol] lua_method_name @return [Symbol] ruby method name
# File lib/transparent_lua.rb, line 162 def get_ruby_method_name(lua_method_name) lua_method_name = String(lua_method_name) case lua_method_name when /^is_(.*)$/, /^has_(.*)$/ return :"#{$1}?" else return lua_method_name.to_sym end end
getter_table(object)
click to toggle source
# File lib/transparent_lua.rb, line 78 def getter_table(object) return object if is_supported_simple_datatype? object metatable = { '__index' => index_table(object), '__newindex' => newindex_table(object), } delegation_table(object, metatable) end
has_rb_object_id?(o)
click to toggle source
# File lib/transparent_lua.rb, line 153 def has_rb_object_id?(o) o.__rb_object_id true rescue NoMethodError false end
index_table(object)
click to toggle source
# File lib/transparent_lua.rb, line 94 def index_table(object) ->(t, k, *newindex_args) do method = get_method(object, k) k = get_ruby_method_name(k) logger.debug { "Dispatching method #{method}(#{method.parameters})" } case method when ->(m) { m.arity == 0 } logger.debug { "Creating a getter table for #{method}" } getter_table(object.public_send(k.to_sym, *newindex_args)) else logger.debug { "Creating a method table for #{method}" } method_table(method) end end end
is_supported_simple_datatype?(object)
click to toggle source
# File lib/transparent_lua.rb, line 172 def is_supported_simple_datatype?(object) return true if SUPPORTED_SIMPLE_DATATYPES.include? object.class return true if Array === object && object.all? { |i| is_supported_simple_datatype?(i) } return true if Hash === object && object.keys.all? { |k| is_supported_simple_datatype?(k) } && object.values.all? { |v| is_supported_simple_datatype?(v) } false end
lua2rb(v)
click to toggle source
# File lib/transparent_lua.rb, line 138 def lua2rb(v) case v when ->(t) { has_rb_object_id?(t) } ObjectSpace._id2ref(Integer(v.__rb_object_id)) when ->(t) { Lua::Table === t && t.to_hash.keys.all? { |k| k.is_a? Numeric } } v.to_hash.values.collect { |v| lua2rb(v) } when Lua::Table v.to_hash.each_with_object({}) { |(k, v), h| h[lua2rb(k)] = lua2rb(v) } when Float (Integer(v) == v) ? Integer(v) : v else v end end
method_table(method)
click to toggle source
@param [Method] method
# File lib/transparent_lua.rb, line 119 def method_table(method) delegation_table( '__call' => ->(t, *args) do converted_args = args.collect do |arg| lua2rb(arg) end getter_table(method.call(*converted_args)) end ) end
newindex_table(object)
click to toggle source
# File lib/transparent_lua.rb, line 88 def newindex_table(object) ->(t, k, v) do getter_table(object.public_send(:"#{k}=", lua2rb(v))) end end
require_module(modname)
click to toggle source
# File lib/transparent_lua.rb, line 71 def require_module(modname) fail NoMethodError, "#{sandbox} must respond to #require_module because it responds to #can_require_module?" unless sandbox.respond_to? :require_module String(sandbox.require_module(modname)) end
setup(leak_globals = false)
click to toggle source
# File lib/transparent_lua.rb, line 41 def setup(leak_globals = false) state.__load_stdlib :all global_metatable = { '__index' => index_table(sandbox) } global_metatable['__newindex'] = newindex_table(sandbox) if leak_globals state._G.__metatable = global_metatable state.package.loaders = Lua::Table.new(state) state.package.loaders[1] = ->(modname) do return "\n\tno module '#{modname}' available in sandbox" unless can_require_module? modname loader = ->(modname) do source = require_module(modname) state.__eval(source, "=#{modname}") end state.package.loaded[modname] = loader loader end end