Skip to content

Loading…

Work in Progress: Adding docco comments to racer #59

Closed
wants to merge 3 commits into from

6 participants

@irnc

As an continuation of a derbyjs/derby#159 here is a n attempt to document racer's internals.

This pull request should be considered as a work in progress for which help and corrections are needed.

@irnc

@nateps @bnoguchi @codeparty What is your attitude towards such documentation? I think internals should be documented inline because it is hard to newcomers to understand how racer/derby works. And thus it is right to impossible for them to make fixes or additions to a platform.

@lefnire

+1 times infinity. Both @irnc's racer & derby docs.

@Ziink

+1

@switz
@mattbrun

+1 on the inline docco documentations, but I'd say that this kind of docs are just for the "pro" derby users (the kind of user who goes to the code to understand it if something doesn't work), not for those who want to use derby without knowing nuts and bolts about it... (the kind of user who choose another framework or asks someone else to fix it if something goes wrong).

Choosing to focus on the inline docs or the kind of docs as the derby home page or the wiki, in my opinion, depends on the kind of audience the project leaders would like to focus on. Personally I'd say that we need a strong and easy to use/understand basement for the "pro users", so that these users can make a good community also for the "not pro users".

Take my thoughts as thoughts, not as polemic argumentations ;)

@lefnire

@irnc - had a conversation with @codeparty a while back about this docco stuff. Evidently Racer is undergoing a total overhaul, so the documentation here will also require an overhaul, is why they're holding off. I'd like to convince them to just merge this while it's here, as well as the Derby docco, and you can tackle the new Racer documentation when the time comes.

@irnc

@lefnire Right now this pull request will fail to merge I guess, and I don't have time right now to merge and resolve conflicts. So I think it is better to wait, because this additions is work in progress (read crap :) anyway, and require additional work before merging.

So better to ask @codeparty to be more open with updates about their intentions and plans.

@lefnire

Gotcha. Well, keep up the good work in any case. There are a few of us in the community who have a clone of your docco branches for documentation lookup, they're not unnoticed.

@nateps

No longer relevant in 0.5

@nateps nateps closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 157 additions and 4 deletions.
  1. +15 −0 lib/Model.js
  2. +1 −0 lib/middleware.js
  3. +74 −1 lib/plugin.js
  4. +18 −0 lib/racer.js
  5. +9 −0 lib/racer.server.js
  6. +6 −0 lib/refs/index.js
  7. +4 −0 lib/session/session.Store.js
  8. +23 −3 lib/transaction.js
  9. +5 −0 lib/txns/index.js
  10. +2 −0 lib/txns/txns.Model.js
View
15 lib/Model.js
@@ -1,3 +1,16 @@
+// ## Model events
+//
+// All following event are emitted on the racer object with names prefixed with
+// `Model:`:
+//
+// * init
+// * middleware
+//
+// Emitted when the model is ready to middleware addition.
+//
+// * bundle
+// * socket
+//
var EventEmitter = require('events').EventEmitter
, Memory = require('./Memory')
, eventRegExp = require('./path').eventRegExp
@@ -7,6 +20,8 @@ var EventEmitter = require('events').EventEmitter
module.exports = Model;
+// Each *model object* has exactly one *memory object* accessible at `_memory`
+// property.
function Model (init) {
for (var k in init) {
this[k] = init[k];
View
1 lib/middleware.js
@@ -1,3 +1,4 @@
+// Exports `createMiddleware` function.
module.exports = function () {
var fns = [];
function run (req, res, done) {
View
75 lib/plugin.js
@@ -1,23 +1,67 @@
+// Exports *plugin interface* implementation which is used for making
+// extendable objects, for example see [`racer`](racer.html) module.
+//
+// This module allows to register any `object` as pluggable under a `name` via
+// call to `_makePlugable`.
+//
+// Register *pluggable object* is called a `target` and it is told to be
+// *decorated* by a plugin.
+//
+// Note: this module's exports are intended to be merged only into a *racer
+// object*. When merged into a different objects, `use` method will be working
+// incorrect in case when `plugin.decorate` will be set to "racer": it will
+// decorate `this` object instead.
+//
+// TODO: fix `use` method to not use `this` but instead use `plugable['racer']`.
+
var util = require('./util')
, mergeAll = util.mergeAll
, isServer = util.isServer
- // This tricks Browserify into not logging an error when bundling this file
+ /* This tricks Browserify into not logging an error when bundling this file */
, _require = require
+ // This module also acts like a repository for all pluggable objects
+ // registered via call to `_makePlugable`.
, plugable = {};
module.exports = {
+ // ## Making any object pluggable
+ //
+ // To register object as pluggable, just call `_makePlugable` specifying the
+ // `name` under which it will be placed in the registry and reference that
+ // `object` in a second parameter.
+ //
+ // This method only registers `object` as pluggable, to really make it
+ // pluggable, merge `plugin` module into it, i.e. add *plugin interface*
+ // implementation.
+ //
+ // Althought this method is exported and latter added to *racer object* it is
+ // a static method, meaning that it does not has any impact on the object it
+ // is called upon.
_makePlugable: function (name, object) {
plugable[name] = object;
}
+
+ // ## Adding plugins to racer object
+ //
+ // Later these pluggable objects can be instructed to use plugins by calls to
+ // `use(plugin, options)`.
+ //
+ // A function representing `plugin` must have a `decorate` property to
+ // specify which object to decorate, it can be `null` or a `String` used as a
+ // `name` in call to `_makePlugable()` (e.g. `racer` or `derby`).
/**
* @param {Function} plugin(racer, options)
* @param {Object} options that we pass to the plugin invocation
*/
, use: function (plugin, options) {
+ // `plugin` is expected to be a function. It can also be a string, on client
+ // such a call will be just omitted, while on server environment, this
+ // string will be used as a module name to require. Module's `exports` must
+ // be a function to be usef as a plugin.
if (typeof plugin === 'string') {
if (!isServer) return this;
plugin = _require(plugin);
@@ -36,6 +80,10 @@ module.exports = {
// Don't include a plugin more than once -- useful in tests where race
// conditions exist regarding require and clearing require.cache
+ /* Ensure that plugin isn't already added, push it to the target's
+ `_plugins` property and call it right away passing in `target` and
+ `options`.
+ */
if (-1 === plugins.indexOf(plugin)) {
plugins.push(plugin);
plugin(target, options);
@@ -43,6 +91,19 @@ module.exports = {
return this;
}
+ // ## Adding functionality to Model and Store objects
+ //
+ // Racer has a concept of *klass*: function with a prototype used as an object
+ // constructor.
+ //
+ // Currently only two *klasses* are using mixin functionality: `Store` and
+ // `Model`.
+ //
+ // Klasses can emit events on racer object to notify all attached mixins about
+ // state changes. Set of this events are klass-specific, with only one common
+ // event: `Klass:mixin` where `Klass` is a type of a klass. It is called every
+ // time a new mixin is merged into a klass.
+ //
// A mixin is an object literal with:
// type: Name of the racer Klass in which to mixin
// [static]: Class/static methods to add to Klass
@@ -57,10 +118,17 @@ module.exports = {
// method collections, e.g., added to `Klass.foo` and `Klass.bar`.
// This is useful for grouping several methods together.
// <other>: All other key-value pairings are added as properties of the method
+ //
+ // Note: most of plugins in racer set `useWith` property on a plugin function,
+ // but as source code suggests, it is of no use as of racer-0.3.13.
, mixin: function () {
var protected = this.protected;
for (var i = 0, l = arguments.length; i < l; i++) {
var mixin = arguments[i];
+
+ // On client, mixin referenced by a string are just omitted, while on
+ // server corresponding module will be required, and its `exports` object
+ // will be used as a mixin.
if (typeof mixin === 'string') {
if (!isServer) continue;
mixin = _require(mixin);
@@ -71,6 +139,9 @@ module.exports = {
var Klass = protected[type];
if (!Klass) throw new Error('Cannot find racer.protected.' + type);
+ // When adding mixin to the Klass for the first time, `mixinEmit` method
+ // is set on it's prototype. This method is used to trigger a specially
+ // named event on the racer object for which all Klass mixins can listen.
if (Klass.mixins) {
Klass.mixins.push(mixin);
} else {
@@ -87,6 +158,8 @@ module.exports = {
mergeAll(Klass, mixin.static);
mergeProto(mixin.proto, Klass);
+ // Mixin object can have a `server` property of `String` or `Object`. On
+ // server, it will be used to extend Klass prototype.
var server;
if (isServer && (server = mixin.server)) {
server = (typeof server === 'string')
View
18 lib/racer.js
@@ -1,14 +1,32 @@
+// This is the main module of the `racer` package.
+//
+// After creating aforementioned object, `./plugin` module is merged into it,
+// same with object literal consisting of: `version`, `isServer` & `isClient`
+// properties, links to `./util` and `./transaction` modules and `uuid`
+// function. So exports from `./plugin` module and specified properties can be
+// used right from `racer` module's exports (and thus from `derby` module's
+// exports).
+
var util = require('./util')
, mergeAll = util.mergeAll
, isServer = util.isServer
, isClient = !isServer;
+// This module is intended to be run on server and a broad rage of browsers, so
+// it uses `es5-shim` package to shim some new APIs of the ECMAScript 5 when
+// run on client.
if (isClient) require('es5-shim');
+// The core pattern used for extending the racer object is a *racer plugin*.
+// The [`plugin`](plugin.html) module exports an object which represents an
+// interface for making pluggable objects. See `plugin` module for details and
+// additional responsibilities on the module.
var EventEmitter = require('events').EventEmitter
, plugin = require('./plugin')
, uuid = require('node-uuid');
+// Module eports *racer object* which is an instance of `EventEmitter` extended
+// by `plugin` interface and some other properties.
var racer = module.exports = new EventEmitter();
mergeAll(racer, plugin, {
View
9 lib/racer.server.js
@@ -1,3 +1,5 @@
+// Server-side part of a main racer module is made as a plugin to it.
+
var fs = require('fs')
, browserify = require('browserify')
, socketioClient = require('socket.io-client')
@@ -104,6 +106,12 @@ function plugin (racer) {
/* Racer built-in features */
+ // ## Creating store on server
+ //
+ // Call `racer.createStore` to create process-wide store object on server.
+ // This will create new Store object, call `listen` on it if corresponding
+ // option will be set, emit 'createStore' event on `this` object (which can
+ // be racer or derby object) and will return *store object*.
racer.createStore = function (options) {
options || (options = {});
options.racer = this;
@@ -163,5 +171,6 @@ function plugin (racer) {
.use(require('./adapters/clientid-redis'))
.use(require('./adapters/clientid-rfc4122_v4'))
+ // since 0.3.12
racer.logPlugin = require('./log.server');
}
View
6 lib/refs/index.js
@@ -1,3 +1,6 @@
+//
+// Implemented using racer's plugin pattern. Plugin mixins into Model.
+//
var pathUtils = require('../path')
, regExpPathOrParent = pathUtils.regExpPathOrParent
, regExpPathsOrChildren = pathUtils.regExpPathsOrChildren
@@ -21,6 +24,9 @@ function plugin (racer) {
racer.mixin(mixin);
}
+// ## Model mixin
+//
+// Listens for `init` and `bundle` events from the Model klass.
var mixin = {
type: 'Model'
View
4 lib/session/session.Store.js
@@ -87,6 +87,10 @@ function sessionMiddleware(opts) {
*/
function modelMiddleware() {
var store = this;
+
+ // Notice that `_racerModel` property is set on `req` object. Model objects
+ // are created and stored per-request, so process serving requests will have
+ // one store object but multiple model objects.
function getModel() {
var model = this._racerModel;
if (model && model.store === store) {
View
26 lib/transaction.js
@@ -3,13 +3,32 @@ var noop = require('./util').noop
/**
* Transactions are represented as an Array
+ *
* [ ver = version at the time of the transaction
- * , transaction id
- * , method
- * , arguments]
+ * , transaction id = 'dot-delimited string', i.e. 'clientId.'
+ * , method = 'string'
+ * , arguments = ['path']
+ * , meta object = {c: 'context'}
+ * ]
*/
+// This module exports object with utility methods for working with
+// transactions: creating them; setting, getting and parsing it's attributes.
exports = module.exports = {
+
+ // Create transaction from an object, all properties of an object are
+ // optional.
+ //
+ // create({ ver: '', id: '', ops: '' })
+ // // => [version, id, ops]
+ //
+ // create({ ver: '', id: '', method: '', args: ''})
+ // // => [version, id, method, args]
+ //
+ // create({ ver: '', id: '', method: '', args: '', context: '' })
+ // // => [version, id, method, args, {c: context}]
+ //
+ // `create` returns *transaction array*.
create: function (obj) {
var txn = (obj.ops) ? [obj.ver, obj.id, obj.ops]
: [obj.ver, obj.id, obj.method, obj.args]
@@ -24,6 +43,7 @@ exports = module.exports = {
, getId: function (txn) { return txn[1]; }
, setId: function (txn, id) { return txn[1] = id; }
+ // Returns array ['string', number]
, clientIdAndVer: function (txn) {
var pair = this.getId(txn).split('.');
pair[1] = parseInt(pair[1], 10);
View
5 lib/txns/index.js
@@ -1,3 +1,8 @@
+// ## `txns` is for *transactions*.
+//
+// This is a racer plugin which mixins transaction handling methods into a
+// Model (on both server and client) and to a Store (only on server) klasses.
+//
var mixinModel = require('./txns.Model')
, mixinStore = __dirname + '/txns.Store';
View
2 lib/txns/txns.Model.js
@@ -27,6 +27,7 @@ module.exports = {
arrayMutator = Model.arrayMutator;
}
+ // `init` event handler adds `_removeTxn` and `_onTxn` methods to the model.
, init: function (model) {
// Add a promise that is checked at bundle time to make sure all
// transactions have been committed on the server before a model gets
@@ -81,6 +82,7 @@ module.exports = {
};
}
+ // Adds `txn` middleware chain to the `_model`.
, middleware: function (_model, middleware) {
middleware.txn = createMiddleware();
Something went wrong with that request. Please try again.