Module:Sandbox/Mr. Stradivarius/sandbox3: Difference between revisions

Content deleted Content added
fix bug in assessmentScale:editGrade
use a class-making function for new classes, use a _type field and _inherits table to track object type and inheritance, revamp checkObject and makeCheckSelfFunction
Line 15:
-- Helper functions
----------------------------------------------------------
 
-- Makes a simple function that raises an error if the dot syntax is used with methods.
-- It is not 100% reliable, but will catch the majority of cases, and works with inheritance.
-- The uniqueMethod variable should be the name of a method unique to the class.
local function makeCheckSelfFunction( libraryName, varName, selfObjDesc, uniqueMethod )
return function( self, method )
if type( self ) ~= 'table' or type( self.new ) ~= 'function' or type( self[ uniqueMethod ] ) ~= 'function' then
error( mw.ustring.format(
'%s: invalid %s. Did you call %s with a dot instead of a colon, i.e. %s.%s() instead of %s:%s()?',
libraryName, selfObjDesc, method, varName, method, varName, method
), 3 )
end
end
end
 
-- Checks to see if a given variable is a non-blank string. Returns the string if true,
Line 35 ⟶ 21:
if type( s ) == 'string' and s ~= '' then
return s
else
return false
end
end
 
-- Checks to see if a given variable is an object of some kind. Returns the object if true,
-- and returns false if not.
local function checkObject( obj )
if type( obj ) == 'table' and type( obj.new ) == 'function' then
return obj
else
return false
Line 71 ⟶ 47:
end
 
-- Checks to see if a given variable is an object that has inherited from type objType.
----------------------------------------------------------
-- Returns the object if true, and returns false if not.
-- Define the banner class
local function checkObject( obj, objType )
----------------------------------------------------------
if type( obj ) == 'table' and type( obj._inherits ) == 'table' and obj._inherits[ objType ] then
 
return obj
local banner = {}
else
banner.__index = banner
return false
local checkSelfBanner = makeCheckSelfFunction( 'Module:WikiProjectBanner', 'banner', 'banner object', 'exportCategories' )
 
function banner:new( init )
init = type( init ) == 'table' and init or {}
local obj = {}
 
obj.objectName = init.objectName
if not obj.objectName then
error( [[No object name specified. Please use "banner:new{ objectName = 'myObject' }".]], 2 )
end
 
-- Set the project name and exit if its value is absent or invalid.
obj.project = init.project
if type( obj.project ) ~= 'string' or obj.project == '' then return end
 
-- Set the index metamethod and the metatable.
self.__index = self
return setmetatable( obj, self )
end
 
local function makeClass( classType )
-- Sets the banner's main image.
if not checkString( classType ) then
function banner:setImage( s )
error( 'makeClass: no class type specified, or the class type was invalid. Class types should be a non-blank string' )
checkSelfBanner( self, 'setImage' )
s = checkString( s )
if s then
self.image = s
end
local class = {}
end
 
-- Set the class type and start the _inherits table. These track the type of the current object and what classes it has
-- Gets the banner's main image.
-- inherited from. Used in checkObject and in error messages.
function banner:getImage()
class._type = classType
checkSelfBanner( self, 'getImage' )
class._inherits = { [ classType ] = true }
return self.image
end
 
function class:new( init )
-- Adds one category to the banner's "categories" table.
init = type( init ) == 'table' and init or {}
function banner:addCategory( s )
local obj = {}
checkSelfBanner( self, 'addCategory' )
self.categories = addStringToArray( self.categories, s )
end
 
-- Set the object type if it is specified.
-- Adds all categories in a "categories" array to the banner's "categories" table.
if checkString( init.type ) then
function banner:addCategories( categoryTable )
obj._type = init.type
checkSelfBanner( self, 'addCategories' )
else
if type( categoryTable ) == 'table' then
for i, category in ipairs( rowObjectobj.categories_type )= doself._type
self:addCategory( category )
end
end
end
 
-- Copy the _inherits table and add the new object type.
-- Returns the banner object's category table.
obj._inherits = {}
function banner:getCategories()
for k, v in pairs( self._inherits ) do
checkSelfBanner( self, 'getCategories' )
obj._inherits[ k ] = v
return self.categories or {}
end
 
-- Returns a string of category links for the categories in obj.categories.
function banner:exportCategories()
checkSelfBanner( self, 'exportCategories' )
if type( self.categories ) == 'table' then
local ret = {}
for i, category in ipairs( self.categories ) do
category = checkString( category )
if category then
table.insert( ret, mw.ustring.format(
'[[Category:%s|%s]]',
category,
type( self.page ) == 'string' and self.page or currentTitleObject.text
) )
end
end
returnobj._inherits[ tableobj.concat(_type ] ret= )true
end
return ''
end
 
-- Set the index metamethod and the metatable.
-- Adds a row object to the banners "rows" table, and adds the row object's categories
self.__index = self
-- to the banners "categories" table. Categories are deleted from the row object after
setmetatable( obj, self )
-- they have been transferred.
function banner:addRow( rowObject )
return obj
checkSelfBanner( self, 'addRow' )
self.rows = self.rows or {}
rowObject = checkObject( rowObject )
if rowObject then
self:addCategories( rowObject.categories )
rowObject.categories = nil -- Erase the categories from the row object to make sure we don't add the same categories twice.
table.insert( self.rows, rowObject )
end
end
 
-- Makes a function that raises an error if the dot syntax is used with methods. It only checks
-- Adds an array of row objects to the banner object.
-- that the object is in the right object family, not that it is the right object, but it will catch
function banner:addRows( t )
-- the majority of cases, and it works with inheritance.
checkSelfBanner( self, 'addRows' )
function class:makeCheckSelfFunction()
if type( t ) == 'table' then
local classType = self._type -- Here, "self" refers to the class used to make the checkSelf function.
for i, rowObject in ipairs( t ) do
return function( self:addRow(, rowObjectmethod )
if type( self ) ~= 'table' or type( self._inherits ) ~= 'table' or not self._inherits[ classType ] then -- Here, "self" refers to the first parameter passed to the method being checked.
error( mw.ustring.format(
'%s: invalid %s object. Did you call %s with a dot instead of a colon, i.e. %s.%s() instead of %s:%s()?',
classType, classType, method, classType, method, classType, method
), 3 )
end
end
end
end
 
return class
function banner:export()
checkSelfBanner( self, 'export' )
end
 
----------------------------------------------------------
-- Define the row class
----------------------------------------------------------
 
local row = {}
row.__index = row
local checkSelfRow = makeCheckSelfFunction( 'Module:WikiProjectBanner', 'row', 'row object', 'getCategories' ) -- TODO: replace getCategories with a less generic function.
 
function row:new( init )
init = type( init ) == 'table' and init or {}
local obj = {}
 
-- Set the index metamethod and the metatable.
self.__index = self
return setmetatable( obj, self )
end
 
-- Adds a category to the "categories" table of the row object. Creates the categories table if it doesn't exist.
function row:addCategory( s )
checkSelfRow( self, 'addCategory' )
self.categories = addStringToArray( self.categories, s )
end
 
-- Adds all categories in an array to the row's "categories" table.
function row:addCategories( t )
checkSelfRow( self, 'addCategories' )
if type( t ) == 'table' then
for i, category in ipairs( t ) do
self:addCategory( category )
end
end
end
 
-- Returns the row object's category table.
function row:getCategories()
checkSelfRow( self, 'getCategories' )
return self.categories or {}
end
 
function row:export()
checkSelfRow( self, 'export' )
 
-- Get the result of the icon and content export functions, and check the results.
local rowIconOutput = type( self.exportRowIcon ) == 'function' and self:exportRowIcon()
rowIconOutput = checkString( rowIconOutput )
local rowContentOutput = type( self.exportRowContent ) == 'function' and self:exportRowContent()
rowContentOutput = checkString( rowContentOutput )
 
-- Export the row html.
local ret = htmlBuilder.create()
if rowIconOutput and rowContentOutput then
ret
.tag( 'tr' )
.tag( 'td' )
.wikitext( rowIconOutput )
.done()
.tag( 'td' )
.attr( 'colspan', '2' )
.wikitext( rowContentOutput )
elseif rowContentOutput and not rowIconOutput then
ret
.tag( 'tr' )
.tag( 'td' )
.attr( 'colspan', '3' )
.wikitext( rowContentOutput )
end
return tostring( ret )
end
 
Line 259 ⟶ 119:
----------------------------------------------------------
 
local assessmentGrade = {}makeClass( 'assessmentGrade' )
assessmentGrade.__indexlocal checkSelfAssessmentGrade = assessmentGrade:makeCheckSelfFunction()
local checkSelfAssessmentGrade = makeCheckSelfFunction( 'Module:WikiProjectBanner', 'assessmentGrade', 'assessmentGrade object', 'getGradeName' )
 
function assessmentGrade:new( init )
init = type( init ) == 'table' and init or {}
local obj = {}
 
-- Set the index metamethod and the metatable.
self.__index = self
return setmetatable( obj, self )
end
 
-- Sets the name of the assessment grade. For example, for a C-class quality grade,
Line 362 ⟶ 212:
----------------------------------------------------------
 
local qualityGrade = assessmentGrade:new(){ type = 'qualityGrade' }
 
----------------------------------------------------------
Line 368 ⟶ 218:
----------------------------------------------------------
 
local importanceGrade = assessmentGrade:new(){ type = 'importanceGrade' }
 
----------------------------------------------------------
Line 379 ⟶ 229:
----------------------------------------------------------
 
local assessmentScale = {}makeClass( 'assessmentScale' )
assessmentScale.__indexlocal checkSelfAssessmentScale = assessmentScale:makeCheckSelfFunction()
local checkSelfAssessmentScale = makeCheckSelfFunction( 'Module:WikiProjectBanner', 'assessmentScale', 'assessmentScale object', 'addGrade' )
 
function assessmentScale:new( init )
init = type( init ) == 'table' and init or {}
local obj = {}
 
-- Set the index metamethod and the metatable.
self.__index = self
return setmetatable( obj, self )
end
 
-- Sets the name of the parameter that accepts trigger values from assessmentGrade objects.
Line 426 ⟶ 266:
 
-- Adds an assessment grade object.
function assessmentScale:addGrade( gradeObjectobj )
checkSelfAssessmentScale( self, 'addGrade' )
gradeObjectobj = checkObject( gradeObjectobj, 'assessmentGrade' )
if gradeObjectobj then
local gradeName = gradeObjectobj:getGradeName()
if not checkString( gradeName ) then
error( mw.ustring.format( 'Grade name not found (was type "%s"). Please set a grade name with the "setGradeName" method of the assessment grade object' , type( gradeName ) ), 2 )
Line 438 ⟶ 278:
error( mw.ustring.format( 'Attempted to add assessment grade %s, but assessment grade %s was already defined. Assessment grades can only be defined once', gradeName, gradeName ), 2 )
else
self.grades[ gradeName ] = gradeObjectobj
end
end
Line 495 ⟶ 335:
method = checkString( method )
if not ( gradeName and method ) then return end
local gradeObjectgradeObj = self.grades[ gradeName ]
if not checkObject( gradeObjectgradeObj, 'assessmentGrade' ) then
error( mw.ustring.format( 'editGrade: no assessment grade object found with grade name "%s"', gradeName ), 2 )
end
if type( gradeObjectgradeObj[ method ] ) ~= 'function' then
error( mw.ustring.format( 'editGrade: method "%s" was not found in the "%s" grade object', method, gradeName ), 2 )
end
Line 513 ⟶ 353:
local exists = {}
for name, gradeObj in pairs( grades ) do
gradeObj = checkObject( gradeObj, 'assessmentGrade' )
if gradeObj then
local triggers = type( gradeObj.getTriggers ) == 'function' and gradeObj:getTriggers()
Line 523 ⟶ 363:
error( mw.ustring.format(
'Duplicate trigger value "%s" detected in the assessment grade objects "%s" and "%s"',
trigger, exists[ trigger ], name
), 2 )
else
Line 539 ⟶ 379:
----------------------------------------------------------
 
local qualityScale = assessmentScale:new(){ type = 'qualityScale' }
local checkSelfQualityScale = qualityScale:makeCheckSelfFunction( 'Module:WikiProjectBanner', 'qualityScale', 'qualityScale object', 'setFaGrade' )
 
-- Sets the FA (Featured Article) grade with default settings.
Line 768 ⟶ 608:
----------------------------------------------------------
 
local importanceScale = assessmentScale:new(){ type = 'importanceScale' }
local checkSelfImportanceScale = importanceScale:makeCheckSelfFunction( 'Module:WikiProjectBanner', 'importanceScale', 'importanceScale object', 'setTopGrade' )
 
-- Sets the Top grade with default settings.
Line 859 ⟶ 699:
self:setLowGrade()
self:setNaGrade()
end
 
----------------------------------------------------------
-- Define the row class
----------------------------------------------------------
 
local row = makeClass( 'row' )
local checkSelfRow = row:makeCheckSelfFunction()
 
-- Adds a category to the "categories" table of the row object. Creates the categories table if it doesn't exist.
function row:addCategory( s )
checkSelfRow( self, 'addCategory' )
self.categories = addStringToArray( self.categories, s )
end
 
-- Adds all categories in an array to the row's "categories" table.
function row:addCategories( t )
checkSelfRow( self, 'addCategories' )
if type( t ) == 'table' then
for i, category in ipairs( t ) do
self:addCategory( category )
end
end
end
 
-- Returns the row object's category table.
function row:getCategories()
checkSelfRow( self, 'getCategories' )
return self.categories or {}
end
 
function row:export()
checkSelfRow( self, 'export' )
 
-- Get the result of the icon and content export functions, and check the results.
local rowIconOutput = type( self.exportRowIcon ) == 'function' and self:exportRowIcon()
rowIconOutput = checkString( rowIconOutput )
local rowContentOutput = type( self.exportRowContent ) == 'function' and self:exportRowContent()
rowContentOutput = checkString( rowContentOutput )
 
-- Export the row html.
local ret = htmlBuilder.create()
if rowIconOutput and rowContentOutput then
ret
.tag( 'tr' )
.tag( 'td' )
.wikitext( rowIconOutput )
.done()
.tag( 'td' )
.attr( 'colspan', '2' )
.wikitext( rowContentOutput )
elseif rowContentOutput and not rowIconOutput then
ret
.tag( 'tr' )
.tag( 'td' )
.attr( 'colspan', '3' )
.wikitext( rowContentOutput )
end
return tostring( ret )
end
 
----------------------------------------------------------
-- Define the qualityRow class
----------------------------------------------------------
 
local qualityRow = row:new{ type = 'qualityRow' }
local checkSelfQualityRow = qualityRow:makeCheckSelfFunction()
 
-- Sets the quality scale for the quality row. The input must be a qualityScale object.
function qualityRow:setQualityScale( obj )
checkSelfQualityRow( self, 'setQualityScale' )
obj = checkObject( obj, 'qualityScale' )
if obj and type( obj.setFaGrade ) == 'function' then
self.qualityScale = obj
end
end
 
-- Sets the standard quality scale with default settings.
function qualityRow:setStandardQualityScale()
checkSelfQualityRow( self, 'setStandardQualityScale' )
local standard = qualityScale:new()
standard:setStandardQualityScale()
self:setQualityScale( standard )
end
 
-- Sets the extended quality scale with default settings.
function qualityRow:setExtendedQualityScale()
checkSelfQualityRow( self, 'setExtendedQualityScale' )
local extended = qualityScale:new()
extended:setExtendedQualityScale()
self:setQualityScale( extended )
end
 
-- Gets the quality scale object if it is set.
function qualityRow:getQualityScale()
checkSelfQualityRow( self, 'getQualityScale' )
return self.qualityScale
end
 
----------------------------------------------------------
-- Define the importanceRow class
----------------------------------------------------------
 
local importanceRow = row:new{ type = 'importanceRow' }
local checkSelfImportanceRow = importanceRow:makeCheckSelfFunction()
 
-- Sets the importance scale for the importance row. The input must be a importanceScale object.
function importanceRow:setImportanceScale( obj )
checkSelfImportanceRow( self, 'setImportanceScale' )
obj = checkObject( obj, 'importanceScale' )
if obj and type( obj.setTopGrade ) == 'function' then
self.importanceScale = obj
end
end
 
-- Sets the standard importance scale with default settings.
function importanceRow:setStandardImportanceScale()
checkSelfImportanceRow( self, 'setStandardImportanceScale' )
local standard = importanceScale:new()
standard:setStandardImportanceScale()
self:setImportanceScale( standard )
end
 
-- Gets the importance scale object if it is set.
function importanceRow:getImportanceScale()
checkSelfImportanceRow( self, 'getImportanceScale' )
return self.importanceScale
end
 
Line 866 ⟶ 833:
 
local taskForce = row:new()
 
----------------------------------------------------------
-- Define the banner class
----------------------------------------------------------
 
local banner = makeClass( 'banner' )
local checkSelfBanner = banner:makeCheckSelfFunction()
 
-- Sets the project name.
function banner:setProject( s )
checkSelfBanner( self, 'setProject' )
s = checkString( s )
if s then
self.project = s
end
end
 
-- Gets the project name.
function banner:getProject()
checkSelfBanner( self, 'getProject' )
return self.project
end
 
-- Sets the banner's main image.
function banner:setImage( s )
checkSelfBanner( self, 'setImage' )
s = checkString( s )
if s then
self.image = s
end
end
 
-- Gets the banner's main image.
function banner:getImage()
checkSelfBanner( self, 'getImage' )
return self.image
end
 
-- Adds one category to the banner's "categories" table.
function banner:addCategory( s )
checkSelfBanner( self, 'addCategory' )
self.categories = addStringToArray( self.categories, s )
end
 
-- Adds all categories in a "categories" array to the banner's "categories" table.
function banner:addCategories( categoryTable )
checkSelfBanner( self, 'addCategories' )
if type( categoryTable ) == 'table' then
for i, category in ipairs( rowObject.categories ) do
self:addCategory( category )
end
end
end
 
-- Returns the banner object's category table.
function banner:getCategories()
checkSelfBanner( self, 'getCategories' )
return self.categories or {}
end
 
-- Returns a string of category links for the categories in obj.categories.
function banner:exportCategories()
checkSelfBanner( self, 'exportCategories' )
if type( self.categories ) == 'table' then
local ret = {}
for i, category in ipairs( self.categories ) do
category = checkString( category )
if category then
table.insert( ret, mw.ustring.format(
'[[Category:%s|%s]]',
category,
type( self.page ) == 'string' and self.page or currentTitleObject.text
) )
end
end
return table.concat( ret )
end
return ''
end
 
-- Adds a row object to the banners "rows" table, and adds the row object's categories
-- to the banners "categories" table. Categories are deleted from the row object after
-- they have been transferred.
function banner:addRow( rowObj )
checkSelfBanner( self, 'addRow' )
self.rows = self.rows or {}
rowObj = checkObject( rowObj, 'row' )
if rowObj and type( rowObj.getCategories ) == 'function' then
self:addCategories( rowObj:getCategories() )
rowObj.categories = nil -- Erase the categories from the row object to make sure we don't add the same categories twice.
table.insert( self.rows, rowObj )
end
end
 
-- Adds an array of row objects to the banner object.
function banner:addRows( t )
checkSelfBanner( self, 'addRows' )
if type( t ) == 'table' then
for i, rowObj in ipairs( t ) do
self:addRow( rowObj )
end
end
end
 
function banner:setQualityRow( obj )
checkSelfBanner( self, 'setQualityRow' )
end
 
function banner:setImportanceRow( obj )
end
 
function banner:export()
checkSelfBanner( self, 'export' )
local rowOutput = {}
for i, row in ipairs( banner.rows ) do
table.insert( ret, row:export() )
end
rowOutput = table.concat( rowOutput )
end
 
-----------------------------------------------------------------