remoteStorage

Intermediate-level JavaScript techniques and other inside knowledge used throughout our core library code

here is a pretty random collection some things you should be aware of when diving into our core code. hopefully it’s useful!

  • promises: To avoid ‘callback hell’, our code uses promises. Read up on them on Mozilla Developer Network (“MDN”) before trying to read the code, otherwise you won’t understand much of it. In some places we’re not catching errors properly, which can lead to ‘possibly uncaught error’ showing up in the console. This is due to how errors work with promises, and it’s good if you learn about that first, as well.
  • closures and variable scope: Read up on these two topics as well on MDN. specifically, we sometimes define a function within a loop, to create a local function closure that can “capture” a variable from outside the iteration.
  • this and that: we sometimes write “var self = this;” at the top of a function, so that “self” is available in callbacks. Other times, we do “.bind(this)” on the callback, in which case “this” is available inside the callback.
  • files: each file is wrapped in a “(function() { … })();” closure to make sure it doesn’t leak variable into the global scope, and implements one feature. On pageload, these features are loaded one by one. The build process uses a Makefile in which features are enabled and disabled for various build targets. If you add a new file to src/ then you’ll also need to add it into the applicable build targets in the Makefile.
  • tests: we use a testing framework called ‘teste’ for our unit tests. It’s not widely used, but pretty similar to most other unit testing frameworks. If you want
    to read the code, you don’t need to look at the test suites (although it might help you), but if you want to write code then you will also have to write tests for
    the code you contribute. Each file in test/unit/ corresponds roughly to a file in src/, and aims to cover most of the code in that file. Our tests are run automatically on travis-ci, from a github commit hook. see also our contribution guidelines about pull requests and tests.
  • events: each feature (file) can emit events and listen to events from other features. If you’re not familiar with event-driven programming (as opposed to functional, object-oriented, or procedural programming), it’s a good idea to read about them, although it’s pretty straight forward what they do. The way the events are wired between the features is quite intricate, and there are quite a lot of events that have similar meanings, for instance ‘features-loaded’, ‘connected’, and ‘ready’. The ‘change’ events used through all levels of caching are also an important feature of how incoming changes are propagated. In a one-line summary: once all features are loaded, the main pageload bootstrap goes through the ‘authorize’ feature, which will configure the ‘remote’ feature, after which ‘connected’ or ‘not-connected’ are fired, which leads to ‘ready’ being fired, and then fireInitial fires the initial ‘change’ events for data that’s already locally cached.
  • double bang is sometimes used as a trick to cast to Boolean: !!(storage) will be true if the variable storage is defined and non-zero, and false otherwise.
  • objects: there is a little bit of object-oriented programming going on here and there in our code base, using the prototypical inheritance. If you don’t know how JavaScript function prototypes work, read up on them on MDN.
  • there are two ways to build a module: using getListing and getObject everywhere, or keeping a copy of the data in memory, and updating it whenever a change event comes in from the baseclient. Both have merits: with getListing/getObject you save memory usage, but it’s also slower (data has to come from disk in an IndexedDB transaction) and forces the module to expose an asynchronous interface (either with callbacks or promises or events), rather than a synchronous one which can make app development much simpler. In the core code you’ll see mechanism for supporting both paradigms at the same time, which sometimes makes our baseclient architecture look a bit schizOfrenic. For instance, the ‘waitForPath’ function makes it possible to call baseclient.getObject during pageload, and it will automatically make sure that the cache is up to date before returning anything. That makes the library usable as a more or less transparent cache whenever there is connectivity. At the same time, the library is usable as a decentralized versioning systems if you catch all the ‘change’ and ‘conflict’ events and deal with them accordingly in module code.

Thanks for the summary. Would be great to have links for everything mentioned. “Read up on MDN” is not a great pointer in a lot of cases. There are way better resources for learning JS and explaining these idioms and techniques.

feel free to edit & expand!

Doesn’t this ask to be put into some kind of documentation, e.g. the (currently disabled) wiki at GitHub or any other reworked combination of the messy documentation and/or the CONTRIBUTING.md?

I’d even go so far to propose to get rid of the /doc/ folder (that doesn’t represent a useful documentation) and move things into Wiki, then linking there from README, CONTRIBUTING and the website.

This forum doesn’t seem appropriate enough to be the place, sincerely.

Yes. Feel free to help. Maybe discuss on IRC with someone first.

Yes, I’ll wrap my head around this documentation question soon. Had to think myself into the current ones first.

Also, I suppose I should have a look at the responsive revamp of the website prior continuing to reconstruct a consistent structure.