Jump to content

Module:Sandbox/Grufo

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Uzume (talk | contribs) at 00:59, 14 July 2025 (spoof "getParent"). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

-- Example module to demonstrate how it can detect how it is loaded and why such can be useful
require[[strict]]
local pkgname = ... or mw.getCurrentFrame():getTitle()
local pkgapi
if not ... then
	--[=[ '#invoke'
		This is where "wrapper" functionality should be so it is available to wikitext templates, etc.
	]=]
	pkgapi = require(pkgname) -- '#invoke' does not add the package to 'package.loaded'
	local mt = {}
	function mt.__index(_tab, key)
		return function(frame) --[=[
			This is the function from '{{#invoke:key|...}}', where 'key' is any valid string argument
			'#invoke' parses this first "function" argument differently (e.g., "=" is valid, etc.)
			and sending the rest to the frame parser
			Notice: '{{#invoke:main|...}}' is valid despite
			"main" being an API function accessible from require()
			]=]
			return pkgapi.main(frame.args, key)
		end
	end
	return setmetatable({}, mt)
else
	--[=[ 'require()' (and 'mw.loadData()'?)
		This is where "main" functionality should be so it is available to modules, including itself
	]=]
	pkgapi = {}
	function pkgapi.main(args, default)
		return pkgname .. ' yields ' .. (args[2] or args[1] or default)
	end
	function pkgapi.invoke(emu, frame, modname, fnname, modargs)
		if not emu then
			-- Copy "modargs" so we don't suddenly change them for the caller
			local parserfnargs = {}
			for k, v in pairs(modargs) do
				parserfnargs [k] = v
			end
			table.insert(parserfnargs, 1, fnname)
			table.insert(parserfnargs, 1, modname)
			return frame:callParserFunction('#invoke', parserfnargs)
		end
		--[=[
		Emulate a "#invoke" call with "require", a frameSpoof and a monkey patched "getCurrentFrame"
		providing a spoofed "args", "getParent" and "getTitle"
		falling back to creating a on demand "newChild" frame for other fields
		]=]
		-- Get as many checks out of the way as early as possible
		--XXX: mw.title.new(828, modname).prefixedText
		local modtitle = mw.site.namespaces.Module.canonicalName .. ':' .. modname
		local fn = assert(require(modtitle)[fnname],
			'{{#invoke:' .. modname .. '|' .. fnname .. '|...}} function not found')
		local frameSpoof = {}
		-- Monkey patch "getCurrentFrame" to return our "frameSpoof"
		local mptab, mpkey = require[[mw]], 'getCurrentFrame'
		local getCurrentFrame = rawget(mptab, mpkey)
		rawset(mptab, mpkey, function() return frameSpoof end)
		-- Punt to "newChild" frame on demand for all function fields; Spoofed fields are overwritten later
		for name in pairs(frame) do
			frameSpoof[name] = function(frameSpoof, ...)
				frameDelayed = frame:newChild{title = modtitle, args = modargs}
				-- Overwrite all fields on demand effectively caching this so it will only happen once
				for name in pairs(frameDelayed) do
					frameSpoof[name] = function(frameSpoof, ...)
						-- All future accesses will punt since we made an actual frame we lose nothing
						return frameDelayed[name](frameDelayed, ...)
					end
				end
				-- Overwrite "args" again because it is not a function
				frameSpoof.args = frameDelayed.args
				-- First punt after updating all fields to punt in the future
				return frameDelayed[name](frameDelayed, ...)
			end
		end
		-- Spoof "getParent"
		function frameSpoof:getParent()
			return frame
		end
		-- Spoof "getTitle"
		function frameSpoof:getTitle()
			return modtitle
		end
		-- Spoof "args" including recreating a metatable
		local args_mt = {}
		args_mt.__index = modargs
		function args_mt.__pairs()
			return pairs(modargs)
		end
		function args_mt.__ipairs()
			return ipairs(modargs)
		end
		frameSpoof.args = setmetatable({}, args_mt)
		-- Emulate the "#invoke" call with frameSpoof
		local ret = fn(frameSpoof)
		-- Revert earlier monkey patch of "getCurrentFrame"
		rawset(mptab, mpkey, getCurrentFrame)
		return ret
	end
	return pkgapi
end