summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-08-25 10:36:28 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2012-08-25 10:36:28 +0100
commita278292101608a2fc1c2ebb3a46f883b2ffc7076 (patch)
tree859cb7c6d96bd3e1886fc6ec4472dfd42885c076
parent8797eb9b4b15729396ea049f8459e351d3093728 (diff)
downloadclod-a278292101608a2fc1c2ebb3a46f883b2ffc7076.tar.gz
CLOD: Add rudimentary list support so we can convert Gitano's group configs
-rw-r--r--examples/configuration-lists.lua45
-rw-r--r--lib/clod.lua52
-rw-r--r--test/test-clod.lua75
3 files changed, 169 insertions, 3 deletions
diff --git a/examples/configuration-lists.lua b/examples/configuration-lists.lua
new file mode 100644
index 0000000..cfecc91
--- /dev/null
+++ b/examples/configuration-lists.lua
@@ -0,0 +1,45 @@
+-- examples/configuration-lists.lua
+--
+-- Demonstrate lists in clod configurations
+--
+-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
+--
+
+clod = require "clod"
+
+-- Clod's list support is a side-effect of its dictionary-like syntax
+-- Essentially lists are not supported. In practice, Clod simulates
+-- them by the ability to iterate subsets of the configuration.
+-- You cannot usefully reorder lists, they're just dictionaries with
+-- automatic keys. When Clod saves configurations, it changes trailing
+-- i_[0-9]+ keys into a ["*"]. This is a tad 'magical' but never mind.
+
+local configuration = [[
+group.members["*"] "dsilvers"
+group.members["*"] "liw"
+
+wont.see.this "ERROR"
+
+group.members["*"] "rjek"
+group.members["*"] "robtaylor"
+]]
+
+conf, err = clod.parse(configuration)
+if not conf then
+ error(err)
+end
+
+print("Input config:")
+io.stdout:write(configuration)
+print()
+print()
+
+print("Group members in the conf (by conf:each('group.members') iterator):")
+for key, value in conf:each("group.members") do
+ print(key, value)
+end
+print()
+print()
+
+print("Serialised output:")
+io.stdout:write(conf:serialise()) \ No newline at end of file
diff --git a/lib/clod.lua b/lib/clod.lua
index b1e5ccd..7696d5e 100644
--- a/lib/clod.lua
+++ b/lib/clod.lua
@@ -84,6 +84,20 @@ local function delete_entry(entry)
end
end
+local function has_key(confmeta, key)
+ return confmeta.settings[key] ~= nil
+end
+
+local function calculate_wild_key(confmeta, prefix)
+ local keyno = 1
+ local keystr = ("%si_%s"):format(prefix, keyno)
+ while has_key(confmeta, keystr) do
+ keyno = keyno + 1
+ keystr = ("%si_%s"):format(prefix, keyno)
+ end
+ return keystr
+end
+
function settings_mt:__newindex(subkey, value)
local meta = metadata[self]
local confmeta = metadata[meta.conf]
@@ -94,6 +108,11 @@ function settings_mt:__newindex(subkey, value)
if meta.prefix then
key = meta.prefix .. "." .. subkey
end
+ local wild_prefix, last_key_element = key:match("^(.-)([^.]+)$")
+ if last_key_element == "*" then
+ -- Wild insert, so calculate a unique key to use
+ key = calculate_wild_key(confmeta, wild_prefix)
+ end
if value == nil then
-- removing an entry...
if confmeta.settings[key] then
@@ -178,6 +197,10 @@ function methods:serialise()
local function serialise_entry(entry)
local key, value, line = entry.key, entry.value, ""
if key then
+ local wild_prefix = key:match("^(.-)%.i_[0-9]+$")
+ if wild_prefix then
+ key = wild_prefix .. '["*"]'
+ end
local vtype = type(value)
assert((vtype == "string" or vtype == "number" or vtype == "boolean"),
"Unexpected " .. vtype .. " in key: " .. key)
@@ -201,6 +224,24 @@ function methods:serialise()
return tconcat(retstr, "\n")
end
+function methods:each(prefix)
+ if prefix then
+ prefix = "^" .. prefix:gsub("%.", "%%.")
+ end
+ local function iterator(confmeta, prev_key)
+ local next_key, next_value = next(confmeta.settings, prev_key)
+ if prefix then
+ while next_key and not next_key:match(prefix) do
+ next_key, next_value = next(confmeta.settings, next_key)
+ end
+ end
+ if next_key and next_value then
+ return next_key, next_value.value
+ end
+ end
+ return iterator, metadata[self], nil
+end
+
-- Metamethods for clod instances
function clod_mt:__index(key)
if key == "settings" then
@@ -236,14 +277,19 @@ local function parse_config(conf, confname)
if type(value) == "table" or type(value) == "function" then
error("Clod does not support " .. type(value) .. "s as values")
end
- return self[key](value)
+ return self[key](value, 1)
end
- function parse_mt:__call(value)
+ function parse_mt:__call(value, offset)
if type(value) == "table" or type(value) == "function" then
error("Clod does not support " .. type(value) .. "s as values")
end
local key = assert(keys[self])
- local curline = getinfo(2, "Snlf").currentline
+ local wild_prefix, last_key_element = key:match("^(.-)([^.]+)$")
+ if last_key_element == "*" then
+ -- Wild insert, so calculate a unique key to use
+ key = calculate_wild_key({settings=settings}, wild_prefix)
+ end
+ local curline = getinfo(2 + (offset or 0), "Snlf").currentline
local entry = { key = key, value = value, lineno = curline }
while last_entry.lineno < (curline - 1) do
local empty = {
diff --git a/test/test-clod.lua b/test/test-clod.lua
index d64587c..f8baa77 100644
--- a/test/test-clod.lua
+++ b/test/test-clod.lua
@@ -181,6 +181,81 @@ function suite.insert_into_blank_two_similar_one_dissimilar_variant()
assert(conf:serialise() == 'person.name "Lars"\nperson.age = 42\n\npants.style "Y-Front"\n')
end
+function suite.parse_wild_key()
+ local conf = assert(clod.parse('foo["*"] "bar"'))
+ assert(conf.settings.foo.i_1 == "bar")
+end
+
+function suite.insert_wild_key()
+ local conf = assert(clod.parse(""))
+ conf.settings["foo.*"] = "bar"
+ assert(conf.settings.foo.i_1 == "bar")
+end
+
+function suite.insert_two_wild_keys()
+ local conf = assert(clod.parse(""))
+ conf.settings["foo.*"] = "bar"
+ conf.settings["foo.*"] = "baz"
+ assert(conf.settings.foo.i_1 == "bar")
+ assert(conf.settings.foo.i_2 == "baz")
+end
+
+function suite.serialise_wild_keys()
+ local input_str = 'foo["*"] "bar"\n'
+ local conf = assert(clod.parse(input_str))
+ assert(conf:serialise() == input_str)
+end
+
+function suite.iterate_all()
+ local input_str = [[
+project.name "Clod"
+project.description "Badgering stoats"
+
+owner.name "Daniel"
+]]
+ local conf = assert(clod.parse(input_str))
+ local had_name, had_desc, had_owner
+ for k, v in conf:each() do
+ if k == "project.name" then
+ had_name = true
+ elseif k == "project.description" then
+ had_desc = true
+ elseif k == "owner.name" then
+ had_owner = true
+ else
+ assert(false)
+ end
+ end
+ assert(had_name)
+ assert(had_desc)
+ assert(had_owner)
+end
+
+function suite.iterate_subset()
+ local input_str = [[
+project.name "Clod"
+project.description "Badgering stoats"
+
+owner.name "Daniel"
+]]
+ local conf = assert(clod.parse(input_str))
+ local had_name, had_desc, had_owner
+ for k, v in conf:each("project") do
+ if k == "project.name" then
+ had_name = true
+ elseif k == "project.description" then
+ had_desc = true
+ elseif k == "owner.name" then
+ had_owner = true
+ else
+ assert(false)
+ end
+ end
+ assert(had_name)
+ assert(had_desc)
+ assert(not had_owner)
+end
+
local count_ok = 0
for _, testname in ipairs(testnames) do
-- print("Run: " .. testname)