--- --- Created Created by Swav Swiac on 23/07/2015. --- --- Allows to run Redis Lua script in a debuggable environment resembling one provided by Redis --- --- Usage: --- --- local runner = require 'redis_runner'.connect(host, port) --- --- local KEYS = {} --- local ARGV = {} --- local result = runner.runDebug('my_func.lua', KEYS, ARGV) --- runner.dump(result) --- -- default redis connection details local default_host = "127.0.0.1" local default_port = 6379 local P = {} setmetatable(P, { __index = _G }) setfenv(1, P) -- libs included in Redis local table = require 'table' local string = require 'string' local math = require 'math' local debug = require 'debug' local struct = require 'struct' local cjson = require 'cjson' local cmsgpack = require 'cmsgpack' local bit = require 'bit' --local sha1hex = require 'redis.sha1hex' local redis = require 'redis' local run = function(debug, client, scriptfile, KEYS, ARGV) -- Simulate Redis Lua environment by adding globals and functions local globals = { KEYS = KEYS, ARGV = ARGV, table = table, string = string, math = math, debug = debug, struct = struct, cjson = cjson, cmsgpack = cmsgpack, bit = bit } globals.client = client -- add lua-cjson and lua-cmsgpack globals.cjson = cjson globals.cmsgpack = cmsgpack globals.redis = { LOG_DEBUG = 'DEBUG', LOG_VERBOSE = 'VERBOSE', LOG_NOTICE = 'NOTICE', LOG_WARNING = 'WARNING' } -- add redis.call globals.redis.call = function(cmd, ...) if debug then print(cmd .. ' ' .. ...) end -- fetch correct command from redis-lua library local command = client[string.lower(cmd)] assert(command, 'Unknown command ' .. cmd) -- execute it, first argument must be client object to simulate "client:" call local result = command(client, ...) -- convert result back to Redis style of {k1, v1, k2, v2 ...} if necessary if type(result) == 'table' then local fixed_result = {} local i = 1; for k, v in pairs(result) do if type(k) ~= 'number' then fixed_result[i] = k fixed_result[i + 1] = v i = i + 2 else fixed_result[i] = v i = i + 1 end end result = fixed_result end return result end -- add redis.log globals.redis.log = function(level, message) print('[' .. level .. '] ' .. message) end local env = setmetatable(globals, { __index = _G }) local err, result = assert(pcall(setfenv(assert(loadfile(scriptfile)), env))) setmetatable(env, nil) return result end -- prints dump of given variable local dump = function(var) if type(var) == 'table' then require 'pl.pretty'.dump(var) elseif type(var) == 'string' then print('"' .. var .. '"') else print(tostring(var)) end end -- connect to Redis function connect(host, port) host = host or default_host port = port or default_port local client = redis.connect(host, port) return { -- run script without debug info run = function(scriptfile, KEYS, ARGV) return run(false, client, scriptfile, KEYS, ARGV) end, -- run script with extra debug info runDebug = function(scriptfile, KEYS, ARGV) return run(true, client, scriptfile, KEYS, ARGV) end, dump = dump } end return P