electricimp / AgentStorage

A library for easily managing the server.save table
MIT License
1 stars 0 forks source link

v1.0.0 #1

Closed cat-haines closed 9 years ago

cat-haines commented 9 years ago
cat-haines commented 9 years ago

I'm going to rewrite this entire class so it can act like a table!

bbharris commented 9 years ago

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
cat-haines commented 9 years ago

Hmm, possibly. I'll poke around with metamethods and see what we can / can't do..

cat-haines commented 9 years ago

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");
cat-haines commented 9 years ago
cat-haines commented 9 years ago

@bbharris - ready for another review :)

cat-haines commented 9 years ago

@ersatzavian - Brandon's out so I'm assigning this to you :) Let me know if you want to go through it together.

DoronShapiro commented 9 years ago

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().