For better or worse, the data in existing JavaScript ICE Code Editor installs (like that at http://gamingjs.com/ice) store their data in browser localStorage with a single localStore key:
codeeditor
. The value pointed to by the codeeditor
key is a JSON string that lists all of the projects that have been stored.The UI lists projects by title:
Titles are also guaranteed to be unique, so it might make sense to index the localStorage for the Dart version of ICE Code Editor by title. Doing so would make all the more sense since Dart has such a nice
HashMap
-based interface into localStorage.As nice as it might be to switch the storage strategy, I think switching an entire app from JavaScript to Dart is enough of a change. So for now, I will create a new
Store
class that works with the existing JavaScript storage. In addition to not rocking the boat too much, this also give me the change to deploy the Dart application side-by-side with the JavaScript version so that I can test it out before making the official switch over. If I deploy to the http://gamingjs.com/ice-beta URL, it will still have access to the same localStorage store since both the /ice
and /ice-beta
URLs resides on the same server.Still, it makes sense for
Store
to implement the HashMap
interface just like Dart's localStorage does—that way my code will not need to change much should I ever decide to migrate. At the risk of prematurely optimizing, I will not implement the store with the same HashMap<String, String>
signature. Instead, the values returned will be of a self-defined Project
class:library ice; import 'dart:collection'; class Store implements HashMap<String, Project> { // Need code here } class Project { String name, code; bool autoupdate = true; String type = 'text/plain'; }In other words,
Store
will be responsible for serializing and de-serializing projects.By implementing the HashMap interface, I give myself a built-in TODO list:
ice-code-editor git:(master) ✗ dart_analyzer lib/store.dart file:/home/chris/repos/ice-code-editor/lib/store.dart:7:7: Concrete class Store has unimplemented member(s) # From Map: Iterable<String> keys Iterable<Project> values int length bool isEmpty bool containsValue(Project) bool containsKey(String) Project [](String) void []=(String, Project) Project putIfAbsent(String, () -> Project) Project remove(String) void clear() void forEach((String, Project) -> void) # From HashMap: void addAll(Map<String, Project>) 6: 7: class Store implements HashMap<String, Project> { ~~~~~It makes the most sense to start with the two square bracket operators for lookup and setup.
So I start with a test:
import 'package:unittest/unittest.dart'; import 'package:ice_code_editor/store.dart'; import 'dart:html'; main() { // ... group("store/retrieve", (){ test("it can store data", (){ var it = new Store(); it['foo'] = {'bar': 42}; expect(it['foo']['bar'], equals(42)); }); }); }I am not testing the underlying implementation of localStorage here—just the interface. In fact, I may skip testing that the data persists in localStorage. Rather I may get the test passing first, then switch to localStorage while continuing to keep the test passing.
First steps, first: I need to get the test passing:
class Store implements HashMap<String, Object> { Store() { } HashMap _docs = {}; void operator []=(String key, Object data) { _docs[key] = data; } Project operator [](String key) => _docs[key]; }With that, I have my passing test:
PASS: store/retrieve it can store dataNow, it is time to vary the implementation.
Which I do with my #pairwithme pair!
Update: While pairing, it became apparent that the
Project
class was premature. We ended up deleting it and returning just HashMaps. We also found it necessary to add a test for localStorage after all. But it works. Another successful pairing -- yay!Day #747
No comments:
Post a Comment