Module:Arguments
Appearance
Documentation for this module may be created at Module:Arguments/doc
-- This module provides easy processing of arguments passed to Scribunto from-- #invoke. It is intended for use by other Lua modules, and should not be-- called from #invoke directly.locallibraryUtil=require('libraryUtil')localcheckType=libraryUtil.checkTypelocalarguments={}-- Generate four different tidyVal functions, so that we don't have to check the-- options every time we call it.localfunctiontidyValDefault(key,val)iftype(val)=='string'thenval=val:match('^%s*(.-)%s*$')ifval==''thenreturnnilelsereturnvalendelsereturnvalendendlocalfunctiontidyValTrimOnly(key,val)iftype(val)=='string'thenreturnval:match('^%s*(.-)%s*$')elsereturnvalendendlocalfunctiontidyValRemoveBlanksOnly(key,val)iftype(val)=='string'thenifval:find('%S')thenreturnvalelsereturnnilendelsereturnvalendendlocalfunctiontidyValNoChange(key,val)returnvalendfunctionarguments.getArgs(frame,options)checkType('getArgs',1,frame,'table',true)checkType('getArgs',2,options,'table',true)frame=frameor{}options=optionsor{}--[[ -- Get the argument tables. If we were passed a valid frame object, get the -- frame arguments (fargs) and the parent frame arguments (pargs), depending -- on the options set and on the parent frame's availability. If we weren't -- passed a valid frame object, we are being called from another Lua module -- or from the debug console, so assume that we were passed a table of args -- directly, and assign it to a new variable (luaArgs). --]]localfargs,pargs,luaArgsiftype(frame.args)=='table'andtype(frame.getParent)=='function'thenifoptions.wrappersthen--[[ -- The wrappers option makes Module:Arguments look up arguments in -- either the frame argument table or the parent argument table, but -- not both. This means that users can use either the #invoke syntax -- or a wrapper template without the loss of performance associated -- with looking arguments up in both the frame and the parent frame. -- Module:Arguments will look up arguments in the parent frame -- if it finds the parent frame's title in options.wrapper; -- otherwise it will look up arguments in the frame object passed -- to getArgs. --]]localparent=frame:getParent()ifnotparentthenfargs=frame.argselselocaltitle=parent:getTitle():gsub('/sandbox$','')localfound=falseiftype(options.wrappers)=='table'thenfor_,vinpairs(options.wrappers)doifv==titlethenfound=truebreakendendelseifoptions.wrappers==titlethenfound=trueend-- We test for false specifically here so that nil (the default) acts like true.iffoundoroptions.frameOnly==falsethenpargs=parent.argsendifnotfoundoroptions.parentOnly==falsethenfargs=frame.argsendendelse-- options.wrapper isn't set, so check the other options.ifnotoptions.parentOnlythenfargs=frame.argsendifnotoptions.frameOnlythenlocalparent=frame:getParent()pargs=parentandparent.argsornilendendifoptions.parentFirstthenfargs,pargs=pargs,fargsendelseluaArgs=frameend-- Set the order of precedence of the argument tables. If the variables are-- nil, nothing will be added to the table, which is how we avoid clashes-- between the frame/parent args and the Lua args. localargTables={fargs}argTables[#argTables+1]=pargsargTables[#argTables+1]=luaArgs--[[ -- Generate the tidyVal function. If it has been specified by the user, we -- use that; if not, we choose one of four functions depending on the -- options chosen. This is so that we don't have to call the options table -- every time the function is called. --]]localtidyVal=options.valueFunciftidyValtheniftype(tidyVal)~='function'thenerror("bad value assigned to option 'valueFunc'"..'(function expected, got '..type(tidyVal)..')',2)endelseifoptions.trim~=falsethenifoptions.removeBlanks~=falsethentidyVal=tidyValDefaultelsetidyVal=tidyValTrimOnlyendelseifoptions.removeBlanks~=falsethentidyVal=tidyValRemoveBlanksOnlyelsetidyVal=tidyValNoChangeendend--[[ -- Set up the args, metaArgs and nilArgs tables. args will be the one -- accessed from functions, and metaArgs will hold the actual arguments. Nil -- arguments are memoized in nilArgs, and the metatable connects all of them -- together. --]]localargs,metaArgs,nilArgs,metatable={},{},{},{}setmetatable(args,metatable)localfunctionmergeArgs(iterator,tables)--[[ -- Accepts multiple tables as input and merges their keys and values -- into one table using the specified iterator. If a value is already -- present it is not overwritten; tables listed earlier have precedence. -- We are also memoizing nil values, but those values can be -- overwritten. --]]for_,tinipairs(tables)doforkey,valiniterator(t)doifmetaArgs[key]==nilthenlocaltidiedVal=tidyVal(key,val)iftidiedVal==nilthennilArgs[key]=trueelsemetaArgs[key]=tidiedValendendendendend--[[ -- Define metatable behaviour. Arguments are memoized in the metaArgs table, -- and are only fetched from the argument tables once. Fetching arguments -- from the argument tables is the most resource-intensive step in this -- module, so we try and avoid it where possible. For this reason, nil -- arguments are also memoized, in the nilArgs table. Also, we keep a record -- in the metatable of when pairs and ipairs have been called, so we do not -- run pairs and ipairs on the argument tables more than once. We also do -- not run ipairs on fargs and pargs if pairs has already been run, as all -- the arguments will already have been copied over. --]]metatable.__index=function(t,key)--[[ -- Fetches an argument when the args table is indexed. First we check -- to see if the value is memoized, and if not we try and fetch it from -- the argument tables. When we check memoization, we need to check -- metaArgs before nilArgs, as both can be non-nil at the same time. -- If the argument is not present in metaArgs, we also check whether -- pairs has been run yet. If pairs has already been run, we return nil. -- This is because all the arguments will have already been copied into -- metaArgs by the mergeArgs function, meaning that any other arguments -- must be nil. --]]localval=metaArgs[key]ifval~=nilthenreturnvalelseifmetatable.donePairsornilArgs[key]thenreturnnilendfor_,argTableinipairs(argTables)dolocalargTableVal=tidyVal(key,argTable[key])ifargTableVal==nilthennilArgs[key]=trueelsemetaArgs[key]=argTableValreturnargTableValendendreturnnilendmetatable.__newindex=function(t,key,val)-- This function is called when a module tries to add a new value to the-- args table, or tries to change an existing value.ifoptions.readOnlythenerror('could not write to argument table key "'..tostring(key)..'"; the table is read-only',2)elseifoptions.noOverwriteandargs[key]~=nilthenerror('could not write to argument table key "'..tostring(key)..'"; overwriting existing arguments is not permitted',2)elseifval==nilthen--[[ -- If the argument is to be overwritten with nil, we need to erase -- the value in metaArgs, so that __index, __pairs and __ipairs do -- not use a previous existing value, if present; and we also need -- to memoize the nil in nilArgs, so that the value isn't looked -- up in the argument tables if it is accessed again. --]]metaArgs[key]=nilnilArgs[key]=trueelsemetaArgs[key]=valendendmetatable.__pairs=function()-- Called when pairs is run on the args table.ifnotmetatable.donePairsthenmergeArgs(pairs,argTables)metatable.donePairs=truemetatable.doneIpairs=trueendreturnpairs(metaArgs)endmetatable.__ipairs=function()-- Called when ipairs is run on the args table.ifnotmetatable.doneIpairsthenmergeArgs(ipairs,argTables)metatable.doneIpairs=trueendreturnipairs(metaArgs)endreturnargsendreturnarguments