Closed cat-haines closed 9 years ago
I'm going to rewrite this entire class so it can act like a table!
I think there are serious issues with that. You may be able to get some table-y things but I think that we should still include the functions we have now.
I think this case doesn't work:
local options = {"serial":123}
p <- Persist();
p.init("options", options);
p.options.serial = 456; //Calls the read method of p and the write method of options so we never server.save the new options
Hmm, possibly. I'll poke around with metamethods and see what we can / can't do..
It looks like the _set, _get, _newslot, _delslot operators all work for nested operations..
Let me know what you think of this:
// Copyright (c) 2015 Electric Imp
// This file is licensed under the MIT License
// http://opensource.org/licenses/MIT
class Persist {
static version = [1,0,0];
_cache = null; // A local cache of the data
// Class ctor - creates object and loads data
constructor() {
_cache = server.load();
}
// Clears the table
function clear() {
_cache = {};
server.save(_cache);
}
function len() {
return _cache.len();
}
function _set(idx, val) {
if (!(idx in _cache)) throw null;
_cache[idx] = val;
server.save(_cache);
}
function _newslot(idx, val) {
_cache[idx] <- val;
server.save(_cache);
}
function _delslot(idx) {
if (!(idx in _cache)) throw "the index '"+idx+"' does not exist";
delete _cache[idx];
}
function _get(idx) {
if (!(idx in _cache)) throw null;
return _cache[idx];
}
}
// Initialize server.save table to a known value
server.save({});
// Persist creates the cache with the server.save data
db <- Persist();
assert(db.len() == 0);
// Slot operator for new slot works and persists
db["foo"] <- "bar";
assert("foo" in server.load());
assert(server.load().foo == "bar")
assert("foo" in db._cache);
assert(db._cache.foo = "bar");
// Slot operator of existing key works and persists
db.foo <- 5;
assert("foo" in server.load());
assert(server.load().foo == 5)
assert("foo" in db._cache);
assert(db._cache.foo = 5);
// Set operator for existing key works and persists
db.foo = "abc";
assert("foo" in server.load());
assert(server.load().foo == "abc")
assert("foo" in db._cache);
assert(db._cache.foo = "abc");
// Set operator of non-existing key fails
try {
db.abc = 5;
assert(false);
} catch (ex) {
assert(true);
}
// Deleting existing key works and persists
delete db["foo"];
assert(!("foo" in server.load()));
assert(!("foo" in db._cache));
// Deleting a non-existing key throws an error
try {
delete db["bar"];
assert(false);
} catch (ex) {
assert(true);
}
// Nested new slots work:
db["foo"] <- { "abc": 1, "xyz": 2 };
assert("abc" in db._cache["foo"]);
assert("abc" in server.load().foo);
assert(db._cache.foo.abc == 1);
assert(server.load().foo.abc == 1);
assert("xyz" in db._cache["foo"]);
assert("xyz" in server.load().foo);
assert(db._cache.foo.xyz == 2);
assert(server.load().foo.xyz == 2);
// Nested sets work
db.foo.abc = "bar";
assert(db._cache.foo.abc == "bar");
assert(server.load().foo.abc == "bar");
// Even more nested sets
db.foo.abc = { "test": 123, "bar": { "x": 123 } };
assert("test" in db._cache.foo.abc);
assert("bar" in db._cache.foo.abc);
assert("x" in db._cache.foo.abc.bar);
assert("test" in server.load().foo.abc);
assert("bar" in server.load().foo.abc);
assert("x" in server.load().foo.abc.bar);
assert(db._cache.foo.abc.test == 123);
assert(db._cache.foo.abc.bar.x == 123);
assert(server.load().foo.abc.bar.x == 123);
assert(server.load().foo.abc.bar.x == 123);
// Deleting nested keys
delete db.foo.abc.bar;
assert(!("bar" in db._cache.foo.abc));
assert(!("bar" in server.load().foo.abc));
// Clear
db.clear();
assert(db._cache.len() == 0);
assert(server.load().len() == 0);
// len
db["test"] <- 123;
assert(db._cache.len() == 1);
assert(server.load().len() == 1);
// in
assert("test" in db);
assert(!("test1" in db));
// Clear out server.save table at end of test and log success
server.save({});
server.log("All tests passed");
in
statement without allowing some dangerous read/write behaviourthis
to keep the class consistent (nothing else is returning this
, so it's not really a fluent class). @bbharris - ready for another review :)
@ersatzavian - Brandon's out so I'm assigning this to you :) Let me know if you want to go through it together.
Is there a reason why methods are returning data that was written to storage? A common pattern elsewhere is to return any deleted data on destructive operations.
It seems most relevant for remove()
, but could also be added to write()
or setDefault()
.