Module:SOM.meta.SimpleAttribute
From SunshinePPS Wiki
Utilities to define a Simple Attribute class.
- generate(
args
) - Generates a Simple Attribute class.
- args:
class_name
: the class name, e.g.SOM.Simple.HasSomeValue
docstring
: description of the classproperty_root
: root of the SMW property used for attaching the value, e.g.Has some value
value_input_type
: (nil, string) input type to use for a PageForms fieldvalue_extra_args
: (nil, string) extra args to use for a PageForms fieldmultivalued
: (nil, boolean) if true, allow multiple (comma-separated) valuesrender_formatter
: (nil, function) custom formatting function, to be invoked by the class'render()
method. This function will be called with the same arguments passed torender()
, and should return wikitext or nil.- returns:
- Returns the new class (as a table).
require("Module:No globals")
local base = require("Module:SOM.meta.AttributeBase")
local util = require("Module:SOM.util")
local StringBuffer = require("Module:SOM.util.StringBuffer")
--local function make_value_property(root)
-- return "SOM:" .. root
--end
local function make_missing_if_nil(record, key)
if record[key] == nil then
record[key] = util.MISSING_VALUE
end
end
local p = { docs = {} }
function p.docs.docs()
return {
module_name = "SOM.meta.SimpleAttribute",
docstring = "Utilities to define a Simple Attribute class."
}
end
-- generate({
-- class_name = "SOM.Intrinsic.BelongsToSchoolDistrict",
-- property_root = "Belongs to school district",
-- value_arg = "district",
-- value_lua_type = "string",
-- value_label = "School District",
-- value_input_type = "combobox",
-- value_input_category = "School District"
-- attribute_label = "School District",
-- instance_template = "SOM.Intrinsic.BelongsToSchoolDistrict/Instance"
-- })
function p.docs.generate()
return {
desc = "Generates a Simple Attribute class.",
needs_args = true,
args = {
{"class_name", "the class name, e.g. <code>SOM.Simple.HasSomeValue</code>"},
{"docstring", "description of the class"},
{"property_root", "root of the SMW property used for attaching the value, e.g. <code>Has some value</code>"},
{"value_input_type", "(nil, string) input type to use for a PageForms field"},
{"value_extra_args", "(nil, string) extra args to use for a PageForms field"},
{"multivalued", "(nil, boolean) if true, allow multiple (comma-separated) values"},
{"render_formatter", "(nil, function) custom formatting function, to be invoked by the class' <code>render()</code> method. This function will be called with the same arguments passed to <code>render()</code>, and should return wikitext or nil."},
},
returns = "Returns the new class (as a table)."
}
end
function p.generate(args)
local class_name = args.class_name
local docstring = args.docstring
---- local category_name = args.class_name
local property_root = args.property_root
---- local value_arg = args.value_arg
---- local value_lua_type = args.value_lua_type
---- local value_label = args.value_label
local value_input_type = args.value_input_type
local value_extra_args = args.value_extra_args
local multivalued = args.multivalued
local render_formatter = args.render_formatter
---- local attribute_label = args.attribute_label
---- local value_formatter = args.value_formatter or ""
local single_value_property =
property_root and base.make_value_property(property_root)
local class = {}
class.docs = {}
-- class.module_name = class_name
function class.docs.docs()
local buffer = StringBuffer.new()
buffer:add_uformat("This module defines the %ssimple attribute class <code>%s</code>.",
util.string_if(multivalued, "multivalued, "),
class_name):nl()
if docstring then
buffer:nl():add(mw.text.trim(docstring)):nl()
end
if single_value_property then
buffer:nl():add_uformat([=[
The value%s will be attached as the SMW property [[Property:%s|%s]].]=],
util.string_if(multivalued, "(s)"),
single_value_property, single_value_property):nl()
end
return {
module_name = class_name,
docstring = buffer:output(),
}
end
------------------------------
function class.is_composite()
return false
end
function class.docs.is_composite()
return { desc = "Returns false, indicating that this class is not composite." }
end
------------------------------
function class.attach(args)
local single_value = args.value
local empty_as = args.empty_as or "error"
util.assertf(
(empty_as == "error") or
(empty_as == "missing"),
"Invalid value for empty_as: %s", empty_as)
util.assertf((type(single_value) == "string") or (single_value == nil),
"Value for %s must be string or nil, not %s.",
class_name, type(single_value))
if (single_value == nil) or (single_value == "") then
if empty_as == "error" then
util.assertf(false, "Value for %s cannot be empty.", class_name)
elseif empty_as == "missing" then
if single_value_property then
util.note_missing_field(single_value_property)
end
return nil
else
util.unreachable()
end
end
local subvalues = {single_value}
if multivalued then
-- Split value at commas, ','
subvalues = mw.text.split(single_value, ",%s*")
end
if single_value_property then
local ds = {}
if multivalued then
-- -- Split value at commas, ','
-- local subvalues = mw.text.split(single_value, ",%s*")
ds[single_value_property] = subvalues
else
ds[single_value_property] = single_value
end
util.assert_smw_set(ds)
-- util.assert_smw_set({
-- [single_value_property] = single_value,
-- })
end
-- TODO maddog Should we simply always return the subvalues list???
if multivalued then
return { value = subvalues }
else
return { value = single_value }
end
end
function class.docs.attach()
local desc = StringBuffer.new()
if multivalued then
desc:add("Attaches attribute values to the page")
else
desc:add("Attaches an attribute value to the page")
end
if single_value_property then
desc:add_uformat(", via the property [[Property:%s|%s]].",
single_value_property, single_value_property)
else
desc:add(".")
end
desc:nl():nl()
desc:add([[
<code>empty_as</code> specifies how a missing/empty <code>value</code> shall
be handled. If <code>error</code> (the default), an error will be raised;
otherwise, ]])
if single_value_property then
desc:add([[
a missing field will be flagged on the page for the property.]])
else
desc:add([[the missing value will be ignored.]])
end
desc:nl():nl()
if multivalued then
desc:add([[
The <code>value</code> string may contain multiple comma-separated values.
The string will be split by commas, and whitespace will be trimmed from
each value.]]):nl():nl()
desc:add([[
Returns a table with a single key <code>value</code> that resolves to the
list of split values.]])
else
desc:add([[
Returns a table with a single key <code>value</code> that resolves to the value.]])
end
desc:add([[ But, returns <code>nil</code> if <code>value</code> is empty/nil.]])
return {
desc = desc:output(),
needs_args = true,
args = {
{ "value", "(string or nil) the value for the attribute" },
{ "empty_as", "(nil or 'missing' or 'error') how to handle a nil/empty value" },
}
}
end
-- function class.note_as_missing()
-- util.note_missing_field(single_value_property)
-- end
------------------------------
function class.docs.render()
local args
if render_formatter then
args = {
{"value", "value instance (<code>nil</code> interpreted as missing)"},
{"args", "additional parameter forwarded to the class' render_formatter"},
}
else
args = {
{"value", "(nil, string) value instance (<code>nil</code> interpreted as missing)"},
{"ignored", "ignored parameter"},
}
end
return {
desc = "Render a value instance to wikitext.",
returns = [[
Returns a wikitext string. <code>nil</code> will be rendered as "missing" and
add a category tag for the missing-data category.]],
args = args,
}
end
function class.render(value, args)
if render_formatter then
value = render_formatter(value, args)
end
if multivalued and (value ~= nil) then
return table.concat(value, ", ")
end
return util.show_nil_as_missing(value)
end
-- function class.show_single (frame)
-- -- {{#ask:
-- -- [[{{FULLPAGENAME}}]]
-- -- [[Has website::+]]
-- -- |?Has website=
-- -- |mainlabel=-
-- -- }}
-- local query = {}
-- table.insert(query,
-- mw.ustring.format("[[%s]][[%s::+]]",
-- mw.title.getCurrentTitle().fullText,
-- single_value_property))
-- table.insert(query,
-- mw.ustring.format("?%s%s=", single_value_property, value_formatter))
------------ "?" .. single_value_property .. "=")
-- table.insert(query, "mainlabel=-")
--
-- local result = mw.smw.ask(query)
-- if false then
-- return mw.dumpObject(result)
-- end
-- -- result should be a table with one subtable (corresponding to one
-- -- matched page). Subtable should have a single string element, but if
-- -- there are multiple property values then the subtable will have a
-- -- single sub-subtable with each string value.
-- if (result == nil) or (#result == 0) then
-- util.note_missing_field(single_value_property)
-- return util.MISSING_VALUE
-- end
-- -- This should always be true:
-- assert(#result == 1, "More than one matching page.")
-- -- TODO maddog Deal with multiple results.
-- assert((#result[1] == 1) and (type(result[1][1]) == value_lua_type),
-- "More than one single value.")
-- return result[1][1]
-- end
------------------------------
function class.form_field(args)
local field_name = args.field_name
local label = args.label -- nil ok
local result = StringBuffer.new()
:add_uformat("{{{field|%s|input type=%s", field_name, value_input_type)
if label then
result:add_uformat("|label=%s", label)
end
for k, v in pairs(value_extra_args) do
if type(k) == "number" then
result:add_uformat("|%s", v)
else
result:add_uformat("|%s=%s", k, v)
end
end
result:add("}}}")
return result:output()
end
function class.docs.form_field()
return {
desc = [[
Returns a PageForms field definition string for this attribute, to be used in
a PageForms form template.]],
needs_args = true,
args = {
{"field_name", "(string) the name of the field in the form"},
{"label", "(string, nil) optional label argument for the field"},
}
}
end
return class
end
return p