howl.bundle
after_each ->
_G.bundles = {}
bundle.dirs = {}
with_bundle_dir = (name, f) ->
with_tmpdir (dir) ->
b_dir = dir / name
b_dir\mkdir!
f b_dir
bundle_init = (info = {}, spec = {}) ->
mod = author: 'bundle_spec', description: 'spec_bundle', license: 'MIT'
mod[k] = v for k,v in pairs info
ret = 'return { info = {'
ret ..= table.concat [k .. '="' .. v .. '"' for k,v in pairs mod], ','
ret ..= '}, '
if spec.unload
ret ..= "unload = #{spec.unload} }"
else
ret ..= 'unload = function() end }'
ret
.unloaded holds the adjusted names of any unloaded bundles
with_tmpdir (dir) ->
bundle.dirs = {dir}
for name in *{'foo-bar', 'frob_nic'}
b_dir = dir / name
b_dir\mkdir!
b_dir\join('init.lua').contents = bundle_init :name
assert.same { 'foo_bar', 'frob_nic' }, bundle.unloaded
bundle.load_by_name 'foo_bar'
assert.same { 'frob_nic' }, bundle.unloaded
bundle.unload 'foo_bar'
assert.same { 'foo_bar', 'frob_nic' }, bundle.unloaded
load_from_dir(dir)
raises an error if dir is not a directory
assert.raises 'directory', -> bundle.load_from_dir File '/not-a-directory'
raises an error if the bundle init file is missing or incomplete
with_tmpdir (dir) ->
assert.raises 'find file', -> bundle.load_from_dir dir
init = dir / 'init.lua'
init\touch!
assert.raises 'Incorrect bundle', -> bundle.load_from_dir dir
init.contents = 'return {}'
assert.raises 'info missing', -> bundle.load_from_dir dir
init.contents = 'return { info = {} }'
assert.raises 'missing info field', -> bundle.load_from_dir dir
assigns the returned bundle table to bundles using the dir basename
mod = author: 'bundle_spec', description: 'spec_bundle', license: 'MIT'
with_bundle_dir 'foo', (dir) ->
dir\join('init.lua').contents = bundle_init mod
bundle.load_from_dir dir
assert.same bundles.foo.info, mod
assert.is_equal 'function', type bundles.foo.unload
massages the assigned module name to fit with naming standards if necessary
with_bundle_dir 'Test-hello 2', (dir) ->
dir\join('init.lua').contents = bundle_init!
bundle.load_from_dir dir
assert.not_nil bundles.test_hello_2
raises an error if the bundle is already loaded
with_bundle_dir 'two_times', (dir) ->
dir\join('init.lua').contents = bundle_init!
bundle.load_from_dir dir
assert.raises 'loaded', -> bundle.load_from_dir dir
raises an error upon implicit global writes
with_tmpdir (dir) ->
dir\join('init.lua').contents = [[
file = bundle_file('bundle_aux.lua')
return {
info = {
author = 'spec',
description = 'desc',
license = 'MIT',
},
file = file
}
]]
assert.raises 'implicit global', -> bundle.load_from_dir dir
(exposed bundle helpers)
bundle_file provides access to bundle files
with_bundle_dir 'test', (dir) ->
dir\join('init.lua').contents = [[
local file = bundle_file('bundle_aux.lua')
return {
info = {
author = 'spec',
description = 'desc',
license = 'MIT',
},
unload = function() end,
file = file
}
]]
bundle.load_from_dir dir
assert.equal bundles.test.file, dir / 'bundle_aux.lua'
load_all()
loads all found bundles in all directories in bundle.dirs
with_tmpdir (dir) ->
bundle.dirs = {dir}
for name in *{'foo', 'bar'}
b_dir = dir / name
b_dir\mkdir!
b_dir\join('init.lua').contents = bundle_init :name
bundle.load_all!
assert.not_nil bundles.foo
assert.not_nil bundles.bar
skips any hidden entries
with_tmpdir (dir) ->
bundle.dirs = {dir}
b_dir = dir / '.hidden'
b_dir\mkdir!
b_dir\join('init.lua').contents = bundle_init name: 'hidden'
bundle.load_all!
assert.same [name for name, _ in pairs _G.bundles], {}
load_by_name(name)
loads the bundle with the specified name, if not already loaded
with_tmpdir (dir) ->
bundle.dirs = {dir}
b_dir = dir / 'named'
b_dir\mkdir!
b_dir\join('init.lua').contents = bundle_init name: 'named'
bundle.load_by_name 'named'
assert.not_nil _G.bundles.named
assert.raises 'loaded', -> bundle.load_by_name 'named'
raises an error if the bundle could not be found
assert.raises 'not found', -> bundle.load_by_name 'oh_bundle_where_art_thouh'
unload(name)
raises an error if no bundle with the given name exists
assert.raises 'not found', -> bundle.unload 'serenity'
(for an existing bundle)
mod = name: 'bunny'
calls the bundle unload function and removes the bundle from _G.bundles
with_bundle_dir 'bunny', (dir) ->
dir\join('init.lua').contents = bundle_init mod, unload: 'function() _G.bunny_bundle_unload = true end'
bundle.load_from_dir dir
bundle.unload 'bunny'
assert.is_true _G.bunny_bundle_unload
assert.is_nil bundles.bunny
returns early with an error if the unload function raises an error
with_bundle_dir 'bad_seed', (dir) ->
dir\join('init.lua').contents = bundle_init mod, unload: 'function() error("barf!") end'
bundle.load_from_dir dir
assert.raises 'barf!', -> bundle.unload 'bad_seed'
assert.is_not_nil bundles.bad_seed
with_bundle_dir 'dash-love', (dir) ->
dir\join('init.lua').contents = bundle_init name: 'dash-love'
bundle.load_from_dir dir
assert.no_error -> bundle.unload 'dash-love'
from_file(file)
returns the adjusted name of the containing bundle if any
with_tmpdir (dir) ->
bundle.dirs = {dir}
b_dir = dir / 'my-bundle'
init = b_dir\join('init.lua')
assert.equal 'my_bundle', bundle.from_file(init)
assert.is_nil bundle.from_file(File('/bin/ls'))
assert.is_nil bundle.from_file(dir\join('directly_under_root'))