Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit c1d43fbf25dca5d518920df14b60a9535005fdd5 @eldargab committed Sep 3, 2012
Showing with 615 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +1 −0 index.js
  3. +138 −0 lib/app.js
  4. +80 −0 lib/box.js
  5. +19 −0 lib/parse-dependencies.js
  6. +21 −0 lib/path-resolve.js
  7. +16 −0 package.json
  8. +280 −0 test/app.js
  9. +1 −0 test/mocha.opts
  10. +39 −0 test/parse-dependencies.js
  11. +17 −0 test/path-resolve.js
@@ -0,0 +1,3 @@
+*.sublime-project
+*.sublime-workspace
+node_modules/
@@ -0,0 +1 @@
+module.exports = require('./lib/app')
@@ -0,0 +1,138 @@
+var resolve = require('./path-resolve')
+var parseDeps = require('./parse-dependencies')
+var Box = require('./box')
+
+module.exports = App
+
+function App () {}
+
+App.prototype.use = function (fn, var_args) {
+ fn.apply(this, [].slice.call(arguments, 1))
+ return this
+}
+
+App.prototype.on = function (path, deps, fn) {
+ if (typeof deps == 'function') {
+ fn = deps
+ deps = fn.deps || parseDeps(fn)
+ }
+ fn = fn || NOOP
+ deps = deps || []
+ var p = this._resolve(path)
+ this['_box_proto_' + p] = new Box(p, deps, fn)
+ return this
+}
+
+App.prototype.run = function (path) {
+ var instance = Object.create(this)
+ path && instance.eval(this._resolve(path))
+ return instance
+}
+
+App.prototype.eval = function (path, cb) {
+ var box = this._box(this._resolve(path))
+ cb = cb || NOOP
+ if (box.isReady) {
+ cb(box.val)
+ return
+ }
+ box.eval(this, cb)
+ return this
+}
+
+App.prototype._box = function (p) {
+ var box = this['_box_' + p]
+ if (box) return box
+ var proto = this['_box_proto_' + p]
+ if (!proto) throw new Error('Box ' + p + ' is not defined')
+ return this['_box_' + p] = Object.create(proto)
+}
+
+App.prototype.get = function (path) {
+ var p = this._resolve(path)
+ var box = this._box(p)
+ if (!box.isReady) throw new Error('Box ' + p + 'is not yet evaluated')
+ return box.val
+}
+
+App.prototype.set = function (path, val) {
+ this['_box_' + this._resolve(path)] = new Box.Evaluated(val)
+ return this
+}
+
+App.prototype.isReady = function (path) {
+ var key = '_box_' + this._resolve(path)
+ return !!this[key] && this[key].isReady
+}
+
+App.prototype.onerror = function (path, fn) {
+ if (typeof path == 'function') {
+ fn = path
+ path = '&'
+ }
+ this['_onerror_' + this._resolve(path)] = fn
+ return this
+}
+
+App.prototype._raise = function (p, e) {
+ var handler, parent = p, self = this
+
+ do {
+ p = parent
+ handler = this['_onerror_' + p]
+ parent = p.replace(/\/[^\/]*$/g, '') // trim last path segment
+ if (parent == p) parent = ''
+ } while (!handler)
+
+ var proxy = p && (new Proxy(this, p))
+
+ function raise (err) {
+ if (err == null) return
+ if (p == parent) throw err
+ self._raise(parent, err)
+ }
+
+ try {
+ handler.call(proxy, e, raise)
+ } catch (err) {
+ raise(err)
+ }
+}
+
+App.prototype._onerror_ = function (e) {
+ throw e
+}
+
+App.prototype._resolve = function (path) {
+ return resolve('', path)
+}
+
+
+App.prototype.prefix = function (p) {
+ return new Proxy(this, p)
+}
+
+
+function Proxy (app, prefix) {
+ this.prefix = prefix
+ this.app = app
+}
+
+;['get', 'set', 'isReady'].forEach(function (meth) {
+ Proxy.prototype[meth] = function (path) {
+ path = resolve(this.prefix, path)
+ return this.app[meth].apply(this.app, arguments)
+ }
+})
+
+;['on', 'onerror', 'eval'].forEach(function (meth) {
+ Proxy.prototype[meth] = function (path) {
+ path = resolve(this.prefix, path)
+ this.app[meth].apply(this.app, arguments)
+ return this
+ }
+})
+
+Proxy.prototype.use = App.prototype.use
+
+function NOOP () {}
@@ -0,0 +1,80 @@
+var resolve = require('./path-resolve')
+
+module.exports = Box
+module.exports.Evaluated = EvaluatedBox
+
+function Box (path, deps, fn) {
+ this.isReady = false
+ this.evaluating = false
+ this.fn = fn
+ this.path = path
+ this.isSync = fn.length < 2
+ this.deps = deps.map(function (dep) {
+ return resolve(path, dep)
+ })
+}
+
+Box.prototype.eval = function (app, cb) {
+ cb && this.onready(cb)
+ if (this.evaluating) return
+ this.evaluating = true
+ this.evalDependencies(app, 0, this.execute)
+}
+
+Box.prototype.evalDependencies = function (app, index, cb) {
+ var self = this, sync = true
+ while (sync) {
+ var dep = this.deps[index++]
+ if (!dep) return cb.call(this, app)
+ var box = app._box(dep)
+ if (box.isReady) continue
+ var done = false
+ box.eval(app, function () {
+ done = true
+ if (sync) return
+ self.evalDependencies(app, index, cb)
+ })
+ sync = done
+ }
+}
+
+Box.prototype.execute = function (app) {
+ var proxy = app.prefix(this.path)
+ var self = this
+
+ function get (p) {
+ return proxy.get(p)
+ }
+
+ try {
+ this.isSync
+ ? this.done(this.fn.call(proxy, get))
+ : this.fn.call(proxy, get, function (error, val) {
+ if (error) return app._raise(self.path, error)
+ self.done(val)
+ })
+ } catch (e) {
+ app._raise(this.path, e)
+ }
+}
+
+Box.prototype.done = function (val) {
+ this.isReady = true
+ this.val = val
+ this.callbacks && this.callbacks.forEach(function (cb) {
+ cb(val)
+ })
+ this.callbacks = null
+}
+
+Box.prototype.onready = function (cb) {
+ this.callbacks = this.callbacks || []
+ this.callbacks.push(cb)
+}
+
+
+function EvaluatedBox (val) {
+ this.val = val
+}
+
+EvaluatedBox.prototype.isReady = true
@@ -0,0 +1,19 @@
+
+module.exports = function parseDependencies (fn) {
+ var deps = []
+ var src = fn.toString()
+
+ src = src.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg, '') // remove comments
+
+ var m = /^function(?:\s+\w+)?\s*\((\w+)/.exec(src) // determine the name of the get function
+ if (!m) return deps
+ var get = m[1]
+
+ var regex = new RegExp('(\\.\\s*)?' + get + '\\(\\s*["\']([^\'"\\s]+)["\']\\s*\\)', 'g')
+
+ src.replace(regex, function (_, isProp, dep) {
+ !isProp && deps.push(dep)
+ })
+
+ return deps
+}
@@ -0,0 +1,21 @@
+
+module.exports = function resolvePath (from, to) {
+ if (to[0] != '.' && to[0] != '&') return to
+
+ var path = from.split('/')
+ var segs = to.split('/')
+ if (to[0] != '&') path.pop()
+ segs.forEach(function (seg, index) {
+ if (seg == '.' || seg == '&') return
+ if (seg == '..') {
+ path.pop()
+ return
+ }
+ path.push(seg)
+ })
+ return trimSlash(path.join('/'))
+}
+
+function trimSlash (s) {
+ return s[0] == '/' ? s.slice(1) : s
+}
@@ -0,0 +1,16 @@
+{
+ "name": "the-box",
+ "version": "0.0.0",
+ "description": "Build systems style computational model for the web",
+ "scripts": {
+ "test": "mocha -R spec"
+ },
+ "repository": "https://github.com/eldargab/the-box.git",
+ "devDependencies": {
+ "should": "*",
+ "mocha": "*",
+ "sinon": "*"
+ },
+ "author": "Eldar Gabdullin <eldargab@gmail.com>",
+ "license": "MIT"
+}
Oops, something went wrong.

0 comments on commit c1d43fb

Please sign in to comment.