From a260aa27ab2af0c71d29b3e18cfa30569d3bcd7d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Fri, 27 Dec 2013 20:50:41 +0100
Subject: Sluift: Add with() function

Change-Id: Ife0a7748c2b354017bec5cfdddb0d096950dd15b

diff --git a/Sluift/Examples/EchoBot_With.lua b/Sluift/Examples/EchoBot_With.lua
new file mode 100644
index 0000000..1f7d0bb
--- /dev/null
+++ b/Sluift/Examples/EchoBot_With.lua
@@ -0,0 +1,32 @@
+--[[
+	Copyright (c) 2010-2013 Remko Tronçon
+	Licensed under the GNU General Public License v3.
+	See Documentation/Licenses/GPLv3.txt for more information.
+--]]
+
+--[[
+	
+	Alternative version of EchoBot that uses with()
+
+	This script logs into an XMPP server, sends initial presence,
+	and then waits for incoming messages, and echoes them back.
+	
+	The following environment variables are used:
+	* SLUIFT_JID, SWIFT_PASS: JID and password to log in with
+	* SLUIFT_DEBUG: Sets whether debugging should be turned on
+
+--]]
+
+require "sluift"
+
+sluift.debug = os.getenv("SLUIFT_DEBUG") or false
+
+client = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS"))
+sluift.with(client, function ()
+	connect()
+	set_version{name = "EchoBot", version = "0.1"}
+	send_presence("Send me a message")
+	for message in messages() do
+		send_message{to = message["from"], body = message["body"]}
+	end
+end)
diff --git a/Sluift/core.lua b/Sluift/core.lua
index aeb3286..f387354 100644
--- a/Sluift/core.lua
+++ b/Sluift/core.lua
@@ -97,10 +97,31 @@ local function copy(object)
 	end
 end
 
+local function clear(table)
+	setmetatable(table, nil)
+	for key, value in pairs(table) do
+		rawset(table, key, nil)
+	end
+end
+
 local function trim(string)
 	return string:gsub("^%s*(.-)%s*$", "%1")
 end
 
+local function keys(table)
+	local result = {}
+	for key in pairs(table) do
+		result[#result+1] = key
+	end
+	return result
+end
+
+local function insert_all(table, values)
+	for _, value in pairs(values) do
+		table[#table+1] = value
+	end
+end
+
 --------------------------------------------------------------------------------
 -- Help
 --------------------------------------------------------------------------------
@@ -405,7 +426,9 @@ end
 _H = {
 	[[ Client interface ]]
 }
-local Client = {}
+local Client = {
+	_with_prompt = function(client) return client:jid() end
+}
 Client.__index = Client
 register_class_table_help(Client, "Client")
 
@@ -424,6 +447,90 @@ local PubSubNode = {}
 PubSubNode.__index = PubSubNode
 register_class_table_help(PubSubNode, "PubSubNode")
 
+
+--------------------------------------------------------------------------------
+-- with
+--------------------------------------------------------------------------------
+
+local original_G
+
+local function with (target, f)
+	-- Dynamic scope
+	if f then
+		with(target)
+		return call{f, finally = function() with() end}
+	end
+
+	-- No scope
+	if target then
+		if not original_G then
+			original_G = copy(_G)
+			setmetatable(original_G, getmetatable(_G))
+			clear(_G)
+		end
+
+		setmetatable(_G, { 
+			__index = function(_, key)
+				local value = target[key]
+				if value then
+					if type(value) == 'function' then
+						-- Add 'self' argument to all functions
+						return function(...) return value(target, ...) end
+					else
+						return value
+					end
+				else
+					return original_G[key]
+				end
+			end,
+			__newindex = original_G,
+			_completions = function ()
+				local result = {}
+				if type(target) == "table" then
+					insert_all(result, keys(target))
+				end
+				local mt = getmetatable(target)
+				if mt and type(mt.__index) == 'table' then
+					insert_all(result, keys(mt.__index))
+				end
+				insert_all(result, keys(original_G))
+				return result
+			end
+		})
+
+		-- Set prompt
+		local prompt = nil
+		
+		-- Try '_with_prompt' in metatable
+		local target_metatable = getmetatable(target)
+		if target_metatable then
+			if type(target_metatable._with_prompt) == "function" then
+				prompt = target_metatable._with_prompt(target)
+			else
+				prompt = target_metatable._with_prompt
+			end
+		end
+
+		if not prompt then
+			-- Use tostring()
+			local target_string = tostring(target)
+			if string.len(target_string) > 25 then
+				prompt = string.sub(target_string, 0, 22) .. "..."
+			else
+				prompt = target_string
+			end
+		end
+		rawset(_G, "_PROMPT", prompt .. '> ')
+	else
+		-- Reset _G
+		clear(_G)
+		for key, value in pairs(original_G) do
+			_G[key] = value
+		end
+		setmetatable(_G, original_G)
+	end
+end
+
 --------------------------------------------------------------------------------
 -- Client
 --------------------------------------------------------------------------------
@@ -884,4 +991,5 @@ return {
 	help = help,
 	extra_help = extra_help,
 	copy = copy,
+	with = with
 }
diff --git a/Sluift/main.cpp b/Sluift/main.cpp
index e2fa9c8..76ba572 100644
--- a/Sluift/main.cpp
+++ b/Sluift/main.cpp
@@ -14,6 +14,7 @@
 #include <boost/program_options.hpp>
 #include <boost/version.hpp>
 #include <boost/numeric/conversion/cast.hpp>
+#include <boost/assign/list_of.hpp>
 #include <Sluift/Console.h>
 #include <Sluift/StandardTerminal.h>
 #include <Sluift/sluift.h>
@@ -151,8 +152,13 @@ int main(int argc, char* argv[]) {
 		if (arguments.count("interactive") || arguments.count("script") == 0) {
 			// Import some useful functions into the global namespace
 			lua_getglobal(L, "sluift");
-			lua_getfield(L, -1, "help");
-			lua_setglobal(L, "help");
+			std::vector<std::string> globalImports = boost::assign::list_of
+				("help")("with");
+			foreach (const std::string& globalImport, globalImports) {
+				lua_getfield(L, -1, globalImport.c_str());
+				lua_setglobal(L, globalImport.c_str());
+			}
+			lua_pop(L, 1);
 			
 			std::cout << SLUIFT_WELCOME_STRING << std::endl;
 #ifdef HAVE_EDITLINE
diff --git a/Sluift/sluift.cpp b/Sluift/sluift.cpp
index b2bdc29..39b92fc 100644
--- a/Sluift/sluift.cpp
+++ b/Sluift/sluift.cpp
@@ -126,7 +126,7 @@ static int sluift_index(lua_State* L) {
 			lua_pushnumber(L, Sluift::globals.timeout);
 			return 1;
 		}
-		throw Lua::Exception("Unknown property");
+		return 0;
 	}
 	catch (const std::exception& e) {
 		return luaL_error(L, e.what());
@@ -339,7 +339,7 @@ SLUIFT_API int luaopen_sluift(lua_State* L) {
 	// Register convenience functions
 	lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex);
 	std::vector<std::string> coreLibExports = boost::assign::list_of
-		("tprint")("disco")("help")("get_help")("copy");
+		("tprint")("disco")("help")("get_help")("copy")("with");
 	foreach (const std::string& coreLibExport, coreLibExports) {
 		lua_getfield(L, -1, coreLibExport.c_str());
 		lua_setfield(L, -3, coreLibExport.c_str());
-- 
cgit v0.10.2-6-g49f6