Module:BuildingLink: Difference between revisions

From Against the Storm Official Wiki
(streamlined the code, added better comments)
(Updated to new data model!)
 
(23 intermediate revisions by the same user not shown)
Line 1: Line 1:
-------------------------------------------------------------------------------
--- @module BuildingLink
-- This module renders the {{Building_link}} template.
-- https://hoodedhorse.com/wiki/Against_the_Storm/Template:Building_link
--
-- The template #invokes BuildingLink.renderLink(frame), below.
--
-- The template requires an argument, the name of the building to which to link.
-- Optionally, the template accepts a second argument to render an icon next to
-- the link. The building is passed to Module:BuildingData, which is used to
-- identify the wiki page to link to. It also handles some basic normalization
-- to tolerate some small mistakes in spelling, spacing, or punctuation.
--
-- Using the data returned, this module creates an icon and the name of the
-- building, and wraps both in a link to the building's wiki page. If no icon
-- size is specified, only the name of the building will be rendered by the
-- template.
--
-- If there is a known building that does not have an icon (might be
-- the case if the data is being updated), then a question-mark icon will take
-- the place of the building's icon. This is so that people using this template
-- will not mistake a missing icon for an issue with this template and can
-- efficienty troubleshoot.
--
-- The icons are sized consistently by using existing wiki templates, {{ImgS}},
-- {{ImgM}}, etc. The sizes of the icons are not stored or known by this module.
-- @module BuildingLink
local BuildingLink = {}
local BuildingLink = {}


--
-- Dependencies
--
local BuildingData = require("Module:BuildingData") -- lookup table for buildings




--region Dependencies


--
local BuildingDataProxy = require("Module:BuildingDataProxy")
-- Constants
--
-- names of the templates used to size the icons consistently
local TEMPLATE_IMGSMALL = "ImgS"
local TEMPLATE_IMGMED = "ImgM"
local TEMPLATE_IMGLARGE = "ImgL"
local TEMPLATE_IMGHUGE = "ImgH"


-- string options for icon size arguments
local VIEW_TEMPLATE = "Building_link/view"
local S_SML = "small"
local S_MED = "med"
local S_LRG = "large"
local S_HUG = "huge"


-- filename to use if there is no known icon for a known building
--endregion
local REPLACEMENT_FILENAME = "Question_mark.png"






--
--region Private constants
-- Main rendering function
--


-------------------------------------------------------------------------------
local VALID_SIZE = {
-- Renders an icon the name of a building linked to a wiki page corresponding
["none"] = true,
-- to the provided building.
["small"] = true,
--
["medium"] = true,
-- Uses MediaWiki markup to display the name of a wiki page and an icon that has
["large"] = true,
-- been uploaded to the wiki. Both must be known by Module:BuildingData, or
["huge"] = true,
-- the template's behavior may be different than expected.
}
-- @param frame a table describing the MediaWiki frame; frame['args'] is a table
 
-- containg the template arguments; frame['args']['product'] is the first
local VALID_PLURAL = {
-- argument, assigned to the key 'product'; frame['args']['building'] is the
["s"] = true,
-- second argument, assigned to the key 'building'
["es"] = true,
-- @return a string containing the wiki markup for an icon and an internal wiki
}
-- page link (or an error if a problem occured)
 
function BuildingLink.renderLink(frame)
--endregion
 
-- extract the arguments we care about from the frame
 
local argBuildingName = frame.args.building
 
local argIconsize = frame.args.iconsize
--region Private methods
 
-- validate that there's a building name to use
---tryData retrieves the icon from the provided building interface. Based on the interface, if name is nil or there is no icon, this method will return nil
if not argBuildingName then
---
return "Building_Link Error: no building given"
---@param name string the display name of a building
---@param buildingInterface table a data module require'd and passed in
---@return string the filename of the icon, if any is found
local function tryData(name, buildingInterface)
 
local buildingID = buildingInterface.getID(name)
 
return buildingInterface.getIcon(buildingID)
end
 
 
 
---validateBuildingName attempts to find in each of the data modules the name specified. If the name is not found, an error is thrown. If the name is found in one of the data modules, it will return the icon filename. So we return that as proof the building was found (and to use it, of course).
---
---@param name string the display name of a building
---@return string the filename of the icon for that building
local function validateBuildingName(name)
 
local id = BuildingDataProxy.getID(name)
if not id then
error("No building found with name: " .. name .. ". Please see the template documentation for how to use the parameters")
end
 
local validatedIcon = BuildingDataProxy.getIcon(id)
if not validatedIcon then
error("Could not validate building with name: " .. name .. ". Please see the template documentation for how to use the parameters")
end
return validatedIcon
end
 
--endregion
 
 
 
--region Public methods
 
---main
--- Extracts parameters, validates the building by getting its icon, and then sends the data to the view template for rendering.
---
--- @param frame table the template's context, with arguments
--- @return string wiki markup
function BuildingLink.main(frame)
 
local name = frame.args.name
local iconSize = frame.args.size
local display = frame.args.display
local needsPlural
 
if not name or name == "" then
error("You must specify a building. Please see the template documentation for how to use the parameters")
end
end
 
-- get the page name to make sure there was one
-- Initialize the array of unnamed parameters, up to 3, so several may be nil.
local building = BuildingData.getPagename(argBuildingName)
local parent = frame:getParent()
if not building or "" == building then
if parent then
return "Building_Link Error: " .. argBuildingName .." not found"
local parameterStack = { parent.args[1], parent.args[2], parent.args[3], }
-- Then empty the stack and try to assign them as our variables.
for _, nextParameter in ipairs(parameterStack) do
if nextParameter and nextParameter ~= "" then
-- Prioritize setting the name first.
if not name or name == "" then
name = nextParameter
else
-- These could happen in either order, because their valid values are non-overlapping
if (not iconSize or iconSize == "") and VALID_SIZE[nextParameter] then
iconSize = nextParameter
else
if not needsPlural and VALID_PLURAL[nextParameter] then
needsPlural = nextParameter
end
end
end
end
end
end
end
 
-- the wiki markup for the internal link to the building's wiki page
-- Validate the name before we continue any further.
local linkPart = "[[" .. building .. "]]"
validatedIcon = validateBuildingName(name)
 
-- If there's no icon size specified, or if it's specified to be small
-- Handle default icon size.
-- (which is too small for building icons), then we can return the wiki link
if not iconSize or iconSize == "" or not VALID_SIZE[iconSize] then
-- right away.
iconSize = "none"
-- This is the default behavior.
if not argIconsize or "" == argIconsize or "small" == argIconsize then
return linkPart
end
end
 
--
-- The only valid value for mustBePlural is "s," so then make on check to see if name ends in a way that we need to add "es" at the end instead of just "s". For names that end in "y", we swap out the last character for "ies" instead. (This will require one-time setup for redirect pages.)
-- At this point, an icon size is requested.
if needsPlural == "s" or needsPlural == "es" then
--
if string.match(name,"y$") then
name = string.sub(name,1,-2) .. "ies"
-- store the requested size for use in a moment
needsPlural = nil -- unset now, since it's incorporated into the name itself
local size = ""
else
local needsSyllable = string.match(name,"[sxz]$") or string.match(name,"sh$") or string.match(name,"ch$")
-- Check the requested size against the allowed options. Expand the
needsPlural = needsSyllable and "es" or "s"
-- corresponding template.
end
if S_MED == argIconsize then
size = frame:expandTemplate{title=TEMPLATE_IMGMED}
elseif S_LRG == argIconsize then
size = frame:expandTemplate{title=TEMPLATE_IMGLARGE}
elseif S_HUG == argIconsize then
size = frame:expandTemplate{title=TEMPLATE_IMGHUGE}
else
else
-- However, if the argument did not match one of the valid sizes, then
needsPlural = nil
-- we just return the link itself, with no icon.
return linkPart
end
end
 
-- If the icon filename doesn't exist, we can still continue by substituting
-- The args to pass to the view.
-- a question mark icon instead.
viewParameters = {
local icon = BuildingData.getIconFilename(argBuildingName)
["name"] = name,
if not icon or "" == icon then
["plural"] = needsPlural,
icon = REPLACEMENT_FILENAME
["iconfilename"] = validatedIcon,
end
["iconsize"] = iconSize,
["display"] = display,
-- combine the size and filename to form the iconPart
}
local iconPart = "[[File:" .. icon .. "|" .. size .. "|link=" .. building .. "|alt=" .. building .. "|" .. building .. "]]"
 
return frame:expandTemplate{
-- combine the file part with the link part, separated by one space
title = VIEW_TEMPLATE,
return iconPart .. " " .. linkPart
args = viewParameters,
}
end
end


 
--endregion


return BuildingLink
return BuildingLink

Latest revision as of 01:43, 30 October 2024

Documentation for this module may be created at Module:BuildingLink/doc

--- @module BuildingLink
local BuildingLink = {}



--region Dependencies

local BuildingDataProxy = require("Module:BuildingDataProxy")

local VIEW_TEMPLATE = "Building_link/view"

--endregion



--region Private constants

local VALID_SIZE = {
	["none"] = true,
	["small"] = true,
	["medium"] = true,
	["large"] = true,
	["huge"] = true,
}

local VALID_PLURAL = {
	["s"] = true,
	["es"] = true,
}

--endregion



--region Private methods

---tryData retrieves the icon from the provided building interface. Based on the interface, if name is nil or there is no icon, this method will return nil
---
---@param name string the display name of a building
---@param buildingInterface table a data module require'd and passed in
---@return string the filename of the icon, if any is found
local function tryData(name, buildingInterface)

	local buildingID = buildingInterface.getID(name)

	return buildingInterface.getIcon(buildingID)
end



---validateBuildingName attempts to find in each of the data modules the name specified. If the name is not found, an error is thrown. If the name is found in one of the data modules, it will return the icon filename. So we return that as proof the building was found (and to use it, of course).
---
---@param name string the display name of a building
---@return string the filename of the icon for that building
local function validateBuildingName(name)

	local id = BuildingDataProxy.getID(name)
	if not id then
		error("No building found with name: " .. name .. ". Please see the template documentation for how to use the parameters")
	end

	local validatedIcon = BuildingDataProxy.getIcon(id)
	if not validatedIcon then
		error("Could not validate building with name: " .. name .. ". Please see the template documentation for how to use the parameters")
	end
	return validatedIcon
end

--endregion



--region Public methods

---main
--- Extracts parameters, validates the building by getting its icon, and then sends the data to the view template for rendering.
---
--- @param frame table the template's context, with arguments
--- @return string wiki markup
function BuildingLink.main(frame)

	local name = frame.args.name
	local iconSize = frame.args.size
	local display = frame.args.display
	local needsPlural

	if not name or name == "" then
		error("You must specify a building. Please see the template documentation for how to use the parameters")
	end

	-- Initialize the array of unnamed parameters, up to 3, so several may be nil.
	local parent = frame:getParent()
	if parent then
		local parameterStack = { parent.args[1], parent.args[2], parent.args[3], }
		-- Then empty the stack and try to assign them as our variables.
		for _, nextParameter in ipairs(parameterStack) do
			if nextParameter and nextParameter ~= "" then
				-- Prioritize setting the name first.
				if not name or name == "" then
					name = nextParameter
				else
					-- These could happen in either order, because their valid values are non-overlapping
					if (not iconSize or iconSize == "") and VALID_SIZE[nextParameter] then
						iconSize = nextParameter
					else
						if not needsPlural and VALID_PLURAL[nextParameter] then
							needsPlural = nextParameter
						end
					end
				end
			end
		end
	end

	-- Validate the name before we continue any further.
	validatedIcon = validateBuildingName(name)

	-- Handle default icon size.
	if not iconSize or iconSize == "" or not VALID_SIZE[iconSize] then
		iconSize = "none"
	end

	-- The only valid value for mustBePlural is "s," so then make on check to see if name ends in a way that we need to add "es" at the end instead of just "s". For names that end in "y", we swap out the last character for "ies" instead. (This will require one-time setup for redirect pages.)
	if needsPlural == "s" or needsPlural == "es" then
		if string.match(name,"y$") then
			name = string.sub(name,1,-2) .. "ies"
			needsPlural = nil -- unset now, since it's incorporated into the name itself
		else
			local needsSyllable = string.match(name,"[sxz]$") or string.match(name,"sh$") or string.match(name,"ch$")
			needsPlural = needsSyllable and "es" or "s"
		end
	else
		needsPlural = nil
	end

	-- The args to pass to the view.
	viewParameters = {
		["name"] = name,
		["plural"] = needsPlural,
		["iconfilename"] = validatedIcon,
		["iconsize"] = iconSize,
		["display"] = display,
	}

	return frame:expandTemplate{
		title = VIEW_TEMPLATE,
		args = viewParameters,
	}
end

--endregion

return BuildingLink