Compare commits

...

15 commits

26 changed files with 228 additions and 81 deletions

View file

@ -1,5 +1,8 @@
return {
_all = {
lpath = "?.lua;?/init.lua"
}
};
core = {
recursive = false
};
}

2
.editorconfig Normal file
View file

@ -0,0 +1,2 @@
[*]
indent_style = tab

3
.luarc.json Normal file
View file

@ -0,0 +1,3 @@
{
"workspace.checkThirdParty": false
}

View file

@ -1,28 +0,0 @@
package = "glass"
version = "dev-1"
source = {
url = "git+https://github.com/darkwiiplayer/glass"
}
description = {
homepage = "https://github.com/darkwiiplayer/glass",
license = "Unlicense"
}
dependencies = {
"luafilesystem"
}
build = {
type = "builtin",
modules = {
glass = "glass.lua",
["glass.cosmo"] = "glass/cosmo.lua",
["glass.discount"] = "glass/discount.lua",
["glass.environment"] = "glass/environment.lua",
["glass.json"] = "glass/json.lua",
["glass.lua"] = "glass/lua.lua",
["glass.moonhtml"] = "glass/moonhtml.lua",
["glass.raw"] = "glass/raw.lua",
["glass.skooma"] = "glass/skooma.lua",
["glass.table"] = "glass/table.lua",
["glass.yaml"] = "glass/yaml.lua"
}
}

32
glass-dev-2.rockspec Normal file
View file

@ -0,0 +1,32 @@
package = "glass"
version = "dev-2"
source = {
url = "git+https://github.com/darkwiiplayer/glass"
}
description = {
summary = "A library to load configuration files by accessing tables",
homepage = "https://github.com/darkwiiplayer/glass",
license = "Unlicense"
}
dependencies = {
"luafilesystem"
}
build = {
type = "builtin",
modules = {
glass = "glass.lua",
["glass.cosmo"] = "glass/cosmo.lua",
["glass.csv"] = "glass/csv.lua",
["glass.discount"] = "glass/discount.lua",
["glass.environment"] = "glass/environment.lua",
["glass.error"] = "glass/error.lua",
["glass.fennel"] = "glass/fennel.lua",
["glass.json"] = "glass/json.lua",
["glass.lua"] = "glass/lua.lua",
["glass.raw"] = "glass/raw.lua",
["glass.skooma.html"] = "glass/skooma/html.lua",
["glass.skooma.xml"] = "glass/skooma/xml.lua",
["glass.table"] = "glass/table.lua",
["glass.yaml"] = "glass/yaml.lua",
}
}

View file

@ -8,15 +8,21 @@ local config = {}
local __metatable = {}
function __metatable:__index(index)
if type(index)~="string" then return nil end
if type(index)~="string" then
return nil
end
local dot = index:find(".", 1, true)
if dot then
return self[index:sub(1, dot-1)][index:sub(dot+1)]
end
local path = self.__dir..'/'..index
local attributes = lfs.attributes(path)
if attributes and attributes.mode=='directory' then
return config.bind(path, self.__loaders)
else
for _, loader in ipairs(self.__loaders) do
local result = loader(path)
if result then
local success, result = loader(path)
if success then
rawset(self, index, result)
return result
end

View file

@ -2,7 +2,7 @@
-- @module glass.cosmo
local cosmo = require 'cosmo'
local readfile = require 'restia.config.readfile'
local raw = require 'glass.raw'
--- Loads a cosmo template from a file and returns the compiled template.
-- Returns nil if no template can be found.
@ -10,10 +10,8 @@ local readfile = require 'restia.config.readfile'
-- @function load
return function(name)
name = tostring(name) .. '.cosmo'
local text = readfile(name)
local text = raw(name)
if text then
return assert(cosmo.compile(text, name))
else
return nil
return true, assert(cosmo.compile(text, name))
end
end

15
glass/csv.lua Normal file
View file

@ -0,0 +1,15 @@
--- Loader for CSV-Data using streamcsv
-- @module glass.csv
local csv = require 'streamcsv'
--- Loads a CSV-File and returns a corresponding Lua table.
-- CSV file is assumed to have a header row.
-- @treturn table CSV-Data
-- @function load
return function(name)
local file = io.open(name..'.csv')
if file then
return true, csv.file(file)
end
end

View file

@ -14,10 +14,8 @@ return function(name)
local file = io.open(name)
if file then
local html = discount(file:read("*a"))
return function()
return true, function()
return html
end
else
return nil
end
end

View file

@ -4,5 +4,5 @@
--- Loads values from environment variables
-- @treturn string The value of the environment variable
return function(name)
return os.getenv(name:match("[^/].+$"))
return true, os.getenv(name:match("[^/].+$"))
end

3
glass/error.lua Normal file
View file

@ -0,0 +1,3 @@
return function(name)
error("Could not load config "..name.." with any loader", 2)
end

18
glass/fennel.lua Normal file
View file

@ -0,0 +1,18 @@
--- Loader for Fennel files
-- @module glass.fennel
local fennel = require "fennel"
local read = require 'glass.raw'
--- Loads and compiles a Fennel file and runs it.
-- Returns the result of running the Fennel file.
-- If running the code immediately is not desired,
-- it has to be returned as a function.
-- @return The result of the Fennel file after running it
-- @function load
return function(file)
local success, raw = read(file..'.fnl')
if success then
return true, fennel.eval(raw)
end
end

View file

@ -2,7 +2,7 @@
-- @module glass.json
local json = require 'cjson'
local readfile = require 'restia.cofnig.readfile'
local read = require 'glass.raw'
--- Loads a JSON-File and returns a corresponding Lua table.
-- May return non-table values for invalid JSON,
@ -11,8 +11,8 @@ local readfile = require 'restia.cofnig.readfile'
-- @treturn table JSON-Data
-- @function load
return function(file)
local raw = readfile(file..'.json')
if raw then
return json.decode(raw)
local success, raw = read(file..'.json')
if success then
return true, json.decode(raw)
end
end

View file

@ -9,5 +9,7 @@
-- @function load
return function(name)
local f = loadfile(name..'.lua')
return f and f() or nil
if f then
return true, f()
end
end

View file

@ -1,7 +1,6 @@
--- Loader for MoonHTML files
-- @module glass.moonhtml
local template = require 'restia.template'
--- Loads and compiles a moonhtml template.
@ -11,7 +10,7 @@ return function(name)
name = tostring(name) .. '.moonhtml'
local file = io.open(name)
if file then
return assert(template.loadmoon(file:read("*a"), name))
return true, assert(template.loadmoon(file:read("*a"), name))
else
return nil
end

View file

@ -6,8 +6,10 @@
-- @function load
return function(path)
local f = io.open(path)
if not f then return end
if not f then
return false
end
local result = f:read("*a")
f:close()
return result
return true, result
end

View file

@ -1,18 +0,0 @@
--- Loader for Skooma templates
-- @module glass.skooma
local skooma = require 'skooma'
--- Loads a Lua file with the Skooma environment and runs it.
-- Normally, the file should return a function
-- to follow restia template semantics.
-- @return The result of the template file.
-- @function load
return function(name)
name = tostring(name)..'.skooma'
local template = loadfile(name, "tb", skooma.default)
template = template and template()
if template then
return template
end
end

28
glass/skooma/html.lua Normal file
View file

@ -0,0 +1,28 @@
--- Loader for Skooma templates
-- @module glass.skooma
local html = require('skooma.env')('html')
local env = setmetatable({}, {__index = function(_, key)
if _G[key] ~= nil then
return _G[key]
else
return html[key]
end
end})
--- Loads a Lua file with the Skooma environment and runs it.
-- Normally, the file should return a function
-- to follow restia template semantics.
-- @return The result of the template file.
-- @function load
return function(name)
name = tostring(name)..'.html.skooma'
local template, err = loadfile(name, "t", env)
if template == nil then
return nil, err
end
if setfenv then
setfenv(template, env)
end
return true, template
end

28
glass/skooma/xml.lua Normal file
View file

@ -0,0 +1,28 @@
--- Loader for Skooma templates
-- @module glass.skooma
local xml = require('skooma.env')('xml')
local env = setmetatable({}, {__index = function(_, key)
if _G[key] ~= nil then
return _G[key]
else
return xml[key]
end
end})
--- Loads a Lua file with the Skooma environment and runs it.
-- Normally, the file should return a function
-- to follow restia template semantics.
-- @return The result of the template file.
-- @function load
return function(name)
name = tostring(name)..'.xml.skooma'
local template, err = loadfile(name, "t", env)
if template == nil then
return nil, err
end
if setfenv then
setfenv(template, env)
end
return true, template
end

View file

@ -7,6 +7,9 @@
-- @treturn function A loader function to be used with `glass.bind`
return function(input)
return function(name)
return input[name:match("[^/].+$")]
local result = input[name:match("[^/].+$")]
if result then
return true, result
end
end
end

View file

@ -2,7 +2,7 @@
-- @module glass.yaml
local yaml = require 'lyaml'
local readfile = require 'restia.config.readfile'
local read = require 'glass.raw'
--- Loads a YAML-File and returns a corresponding Lua table.
-- May return non-table values for invalid YAML,
@ -11,8 +11,11 @@ local readfile = require 'restia.config.readfile'
-- @treturn table YAML-Data
-- @function load
return function(file)
local raw = readfile(file..'.yml') or readfile(file..'.yaml')
if raw then
return yaml.load(raw)
local success, raw = read(file..'.yml')
if not success then
success, raw = read(file..'.yaml')
end
if success then
return true, yaml.load(raw)
end
end

View file

@ -17,6 +17,7 @@ Assume the file `app/config/settings.json` exists in your project directory with
local glass = require 'glass'
local config = glass.bind('app/config', {
(require 'glass.json');
(require 'glass.error'); -- Throw error if nothing matches
})
print(config.settings.user.name) -- prints "User"
```
@ -27,17 +28,24 @@ A glass loader is initialised with a list of loaders and will try each of them i
## Loaders
Glass offers the following loaders out of the box:
* `cosmo` loads cosmo templates
* `csv` loads csv files (with header line)
* `discount` loads markdown files\*.
* `json` loads a JSON file as a Lua table.
* `environment` loads environment variables.
* `fennel` loads and executes fennel files.
* `json` loads a JSON file as a Lua table. (requires `cjson`)
* `lua` loads and executes a Lua file.
* `moonhtml` loads a MoonHTML template and returns it as a function.
* `readfile` loads a file as a string.
* `skooma` loads a skooma template and returns it as a function.
* `skooma.html` loads a `.html.skooma` template and returns it as a function.
* `skooma.xml` same as above for `.xml.skooma`.
* `table` looks up values in a Lua table
* `yaml` loads a YAML file as a Lua table.
* `yaml` loads a YAML file as a Lua table. (requires `lyaml`)
* `error` pseudo-loader that throws an error.
\* For easier interoperability with other template loaders, the `discount` loader returns a static function which can be called to return the generated HTML. The markdown file is only parsed the first time.
Note that some of these loaders require additional dependencies that are not included with glass to keep the installation small.
### Custom Loaders
A glass loader is simply a Lua function that takes as its argument a path to a
@ -46,6 +54,8 @@ file and attempts to load it into a Lua value.
Loaders will typically add an extension to the given file name before checking
whether that file exists and can be loaded.
When a loader cannot find the expected file, it should return `nil` to let glass
continue the loader chain. When a loader returns a truthy value, this will be
used and no further loaders will be tried.
A successful loader should return `true` followed by its result.
When a loader can't load a key, it should return `false` and an optional
description of why.
When a loader encounters an error (file found but can't be parsed), it should
error.

View file

@ -0,0 +1,6 @@
describe 'fennel loader', ->
before_each -> export loader = require 'glass.fennel'
it 'loads Fennel files', ->
success, result = loader 'spec/fixtures/test'
assert.true success
assert.same { foo: 'bar', tab: {} }, result

2
spec/fixtures/test.fnl vendored Normal file
View file

@ -0,0 +1,2 @@
{"foo" "bar"
"tab" []}

View file

@ -1,9 +1,11 @@
describe 'raw loader', ->
before_each -> export loader = require 'glass.raw'
it 'loads files as plain text', ->
assert.same 'plain text\n', loader 'spec/fixtures/test'
assert.same {true, 'plain text\n'}, {loader 'spec/fixtures/test'}
describe 'lua loader', ->
before_each -> export loader = require 'glass.lua'
it 'loads Lua files', ->
assert.same { foo: 'bar', tab: {} }, loader 'spec/fixtures/test'
success, result = loader 'spec/fixtures/test'
assert.true success
assert.same { foo: 'bar', tab: {} }, result

30
tasks.lua Normal file
View file

@ -0,0 +1,30 @@
local task = require 'spooder' .task
task.test {
description = "Runs tests";
'rm luacov.stats.out || true';
'luacheck . *.rockspec';
'busted --coverage --lpath "?.lua"';
'luacov -r html glass.lua';
}
task.documentation {
description = "Builds and pushes the documentation";
depends = "test";
[[
hash=$(git log -1 --format=%h)
mkdir -p doc/coverage
cp -r luacov-html/* doc/coverage
ldoc .
cd doc
find . | treh -c
git add --all
if git log -1 --format=%s | grep "$hash$"
then git commit --amend --no-edit
else git commit -m "Update documentation to $hash"
fi
git push --force origin doc
cd ../
git stash pop || true
]];
}