From 42c913332fb30049d628fd663e8127d7475f7374 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 13 Jun 2015 16:43:05 +0200 Subject: [PATCH] Add a generic cache system as gears.cache Signed-off-by: Uli Schlachter --- lib/gears/cache.lua | 52 +++++++++++++++++++++++++++++++++ lib/gears/init.lua | 1 + spec/gears/cache_spec.lua | 61 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 lib/gears/cache.lua create mode 100644 spec/gears/cache_spec.lua diff --git a/lib/gears/cache.lua b/lib/gears/cache.lua new file mode 100644 index 00000000..4f75e205 --- /dev/null +++ b/lib/gears/cache.lua @@ -0,0 +1,52 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2015 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @classmod gears.cache +--------------------------------------------------------------------------- + +local select = select +local setmetatable = setmetatable +local unpack = unpack or table.unpack + +local cache = {} + +--- Get an entry from the cache, creating it if it's missing. +-- @param ... Arguments for the creation callback. These are checked against the +-- cache contents for equality. +-- @return The entry from the cache +function cache:get(...) + local result = self._cache + for i = 1, select("#", ...) do + local arg = select(i, ...) + local next = result[arg] + if not next then + next = {} + result[arg] = next + end + result = next + end + local ret = result._entry + if not ret then + ret = { self._creation_cb(...) } + result._entry = ret + end + return unpack(ret) +end + +--- Create a new cache object. A cache keeps some data that can be +-- garbage-collected at any time, but might be useful to keep. +-- @param creation_cb Callback that is used for creating missing cache entries. +-- @return A new cache object. +function cache.new(creation_cb) + return setmetatable({ + _cache = setmetatable({}, { __mode = "v" }), + _creation_cb = creation_cb + }, { + __index = cache + }) +end + +return setmetatable(cache, { __call = function(_, ...) return cache.new(...) end }) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/gears/init.lua b/lib/gears/init.lua index c565e7ff..696bed7b 100644 --- a/lib/gears/init.lua +++ b/lib/gears/init.lua @@ -15,6 +15,7 @@ return surface = require("gears.surface"); wallpaper = require("gears.wallpaper"); timer = require("gears.timer"); + cache = require("gears.cache"); } -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/spec/gears/cache_spec.lua b/spec/gears/cache_spec.lua new file mode 100644 index 00000000..e815a306 --- /dev/null +++ b/spec/gears/cache_spec.lua @@ -0,0 +1,61 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2015 Uli Schlachter +--------------------------------------------------------------------------- + +local cache = require("gears.cache") + +describe("gears.cache", function() + -- Make sure no cache is cleared during the tests + before_each(function() + collectgarbage("stop") + end) + after_each(function() + collectgarbage("restart") + end) + + describe("Zero arguments", function() + it("Creation cb is called", function() + local called = false + local c = cache(function() + called = true + end) + local res = c:get() + assert.is_nil(res) + assert.is_true(called) + end) + end) + + describe("Two arguments", function() + it("Cache works", function() + local num_calls = 0 + local c = cache(function(a, b) + num_calls = num_calls + 1 + return a + b + end) + local res1 = c:get(1, 2) + local res2 = c:get(1, 3) + local res3 = c:get(1, 2) + assert.is.equal(res1, 3) + assert.is.equal(res2, 4) + assert.is.equal(res3, 3) + assert.is.equal(num_calls, 2) + end) + + it("Cache invalidation works", function() + local num_calls = 0 + local c = cache(function(a, b) + num_calls = num_calls + 1 + return a + b + end) + local res1 = c:get(1, 2) + collectgarbage("collect") + local res2 = c:get(1, 2) + assert.is.equal(res1, 3) + assert.is.equal(res2, 3) + assert.is.equal(num_calls, 2) + end) + end) +end) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80