Module:Sandbox/Grufo
Appearance
This is an example module to demonstrate how a module can detect how it is loaded and why such can be useful.
- Module:Sandbox/Grufo yields
- Module:Sandbox/Grufo yields
- Module:Sandbox/Grufo yields
- Module:Sandbox/Grufo yields b
- Module:Sandbox/Grufo yields =
- Module:Sandbox/Grufo yields =
- Module:Sandbox/Grufo yields b
-- 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
--XXX: mw.site.namespaces.Module.canonicalName .. ':' .. modname
local modtitle = 'Module:' .. modname
local fn = assert(require(modtitle)[fnname],
'{{#invoke:' .. modname .. '|' .. fnname .. '|...}} function not found')
local frameSpoof = {}
-- Punt to "newChild" frame on demand for all function fields; Spoofed fields are overwritten later
for name in pairs(frame) do
frameSpoof[name] = function(self, ...)
local 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(self, ...)
if self == frameSpoof then
self = frameDelayed -- Only redirect correct value and let error propagate
end
-- All future accesses will punt since we made an actual frame we lose nothing
return frameDelayed[name](self, ...)
end
end
-- Overwrite "args" again because it is not a function
frameSpoof.args = frameDelayed.args
if self == frameSpoof then
self = frameDelayed -- Only redirect correct value and let error propagate
end
-- First punt after updating all fields to punt in the future
return frameDelayed[name](self, ...)
end
end
-- Spoof "getParent"
function frameSpoof:getParent(...)
if self ~= frameSpoof then
return frame.getParent(self, ...) -- Let a real frame throw the error
end
return frame
end
-- Spoof "getTitle"
function frameSpoof:getTitle(...)
if self ~= frameSpoof then
return frame.getTitle(self, ...) -- Let a real frame throw the error
end
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)
-- Monkey patch "getCurrentFrame" to return our "frameSpoof"
local mptab, mpkey = mw, 'getCurrentFrame'
local getCurrentFrame = rawget(mptab, mpkey)
rawset(mptab, mpkey, function() return frameSpoof end)
-- 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