モジュール:サンドボックス/MysteryPedia/Utility
表示
モジュールの解説[作成]
-- Utilities that do not depend on any project, and are mainly intended to be called by other modules.
local export = {}
function export.codepoint(frame)
local a = frame.args
return mw.ustring.codepoint(a[1] or error("Arguments need at least one."), a[2] or 1, a[3] or 1)
end
function export.categorizeFromData(data, title, sortKey)
local cats = {}
for catname, pages in pairs(data) do
for i, t in ipairs(pages) do
local id = table.concat({catname, title}, "|")
if title == t and not cats[id] then
cats[id] = true
table.insert(cats, "[[Category:" .. catname .. (sortKey and "|" .. sortKey or "") .. "]]")
end
end
end
if #cats > 0 then
return mw.getCurrentFrame():preprocess(table.concat(cats))
else -- Disables the line break just in case.
return mw.getCurrentFrame():preprocess('<nowiki />')
end
end
function export.startsWith(s, prefix)
return (mw.ustring.sub(s, 1, mw.ustring.len(prefix)) == prefix)
end
function export.endsWith(s, suffix)
return (mw.ustring.sub(s, mw.ustring.len(s) - mw.ustring.len(suffix) + 1) == suffix)
end
-- It's similar to IsValidPageName, but it returns 'true' or 'nil'.
function export.isValidTitle(str)
local succeeded, t = pcall(mw.title.new, str)
return (t and succeeded)
end
function export.pageExists(title)
local succeeded, t = pcall(mw.title.new, title)
return (succeeded and t.exists)
end
-- NOTE: the same purpose as [[w:en:Module:Array length]].
function export.size(data)
if data[1] == nil then
return 0
end
local low = 1
local high = 0xFFFFFFFF
while low <= high do
n = low + (high - low) / 2
if data[n] ~= nil and data[n + 1] == nil then
return n
elseif data[n] ~= nil then
low = n + 1
else
high = n - 1
end
end
end
function export.import(fullName)
local succeeded, instance = pcall(require, fullName)
if succeeded then
return instance
else -- Ignores the exception message.
mw.log("Could not load the module " .. fullName .. ".")
return nil
end
end
-- An ad hoc function. I'm too lazy to find out whether there's a standard way or not.
-- NOTE: There seems to be nothing.
local function getModuleNameByCallStack(depth)
local s = debug.traceback()
mw.log("Parsing: " .. s)
for line in mw.text.gsplit(s, "\n", true) do
local match = mw.ustring.match(line, "([^%c]+):%d+: in function") or mw.ustring.find(line, "(tail call):", 1, true)
if match ~= nil then
if depth > 1 then
depth = depth - 1
else
if type(match) == "number" then
error("Failed to detect the module name because of tail call optimization." +
" Please make its code a bit verbose to avoid it.")
end
return match
end
end
end
return nil
end
function export.getCurrentModuleName()
return getModuleNameByCallStack(3)
end
-- Distinguishing subpages and the sandbox's modules.
-- '(sandbox)/(username)' - in many of Wikipedias
-- '(User pseudo-namespace):(module name)' - in many of Wiktionaries
function export.isTopLevelModule(fullName)
-- Removes the '{{ns:Module}}' namespace.
return not mw.ustring.find(mw.title.new(fullName).text, '[/:]')
end
function getModuleName(baseModuleName, name)
if export.isTopLevelModule(baseModuleName) then
return "Module:" .. name
else -- NOTE: changes the last segment.
return (mw.ustring.gsub(baseModuleName, "/[^/]+$", '/' .. name))
end
end
-- PENDING:
function export.getModuleName(name)
if not name then
return getModuleNameByCallStack(3)
end
local fullName = getModuleName(getModuleNameByCallStack(3), name)
if not export.pageExists(fullName) then
fullName = getModuleName(getModuleNameByCallStack(1), name)
end
return fullName
end
local cache = {}
function export.const(expr)
local value = cache[expr]
if value ~= nil then
return value
end
local new = mw.getCurrentFrame():preprocess(expr)
cache[expr] = new
return new
end
function export.extend(target, incognito)
local mt = target
while true do
if incognito == mt then
return false
end
local child = getmetatable(mt)
if child == nil then
setmetatable(mt, { __index = incognito })
return true
end
mt = child
-- child = getmetatable(mt)
end
end
local gmt = {}
-- PENDING:
-- This function will be useful in some cases. However, you can use this module as a usual utility class, without the deep mechanism.
function export.using(globals, parent, ...)
export.extend(globals, gmt)
for i, obj in ipairs({...}) do
if type(obj) == "function" then
local found = false
for name, fun in pairs(parent) do
if fun == obj then
gmt[name] = fun
found = true
break
end
end
if not found then
error("Member not found.")
end
elseif type(obj) == "string" then
if parent[obj] == nil then
error("Invalid method name.")
end
gmt[obj] = parent[obj]
else
error("Unsupported type.")
end
end
end
local console = export.import(export.getModuleName("Console"))
do
if console then
console.attach(export)
console.define(export)
console.define({ ['cache'] = function() return cache end, })
end
end
return export