Commits

firefly committed 65ba361

Translated wyke.coffee to JavaScript (through `coffee`)

Comments (0)

Files changed (4)

 #### /wyke/settings #######################################
 app.all '/wyke/settings', (req, res) ->
 	sendBack = ->
+		namespaceMap = {}
+
 		namespaces.find().toArray (err, namespaces) ->
-			namespaceMap  = {}
 			namespaceList = namespaces.map (ns) ->
 				namespaceMap[ns.name] = ns
 				extra = (val for key,val of ns when key not in ['_id', 'name'])
 				body  : body
 			}
 			
+			console.log "Rendering internal page..."
 			wyke.renderInternalPage opts, (err, result) ->
 				res.send result
 	
-	if req.body
-		if req.body.newNS
-			ns = req.body.newNS
-			ns["includer"] or= "Internal:DefaultIncluder"
-			ns["renderer"] or= "Internal:DefaultRenderer"
-			ns["mimetype"] or= "text/html"
-			
-			wyke.storeNamespace ns.name, ns, true, sendBack
-	
+	if req.body and req.body.newNS
+		ns = req.body.newNS
+		ns["includer"] or= "Internal:DefaultIncluder"
+		ns["renderer"] or= "Internal:DefaultRenderer"
+		ns["mimetype"] or= "text/html"
+		
+		wyke.storeNamespace ns.name, ns, true, sendBack
+
 	else
 		do sendBack
 

src/fakedb.coffee

 	@filename    = filename
 	@collections = {}
 	
-	@load()
+#	@load()
 	
 	this
 
 	console.log "Loaded #{@filename}!"
 
 exports.create = (filename) ->
-	new Database filename
+	new Database filename

src/wyke.coffee

-vm         = require 'vm'
-#Mongolian  = require 'mongolian'
-wykedown   = require './wykedown'
-utils      = require './utils'
-
-#### Database helper functions ############################
-createOrUpdate = (coll, query, value, isUpdate, callback) ->
-	coll.findOne query, (err, oldValue) ->
-		callback err if err and callback
-		
-		if oldValue
-			if isUpdate
-				coll.update query, value
-				console.log "Updating collection #{JSON.stringify query}..."
-		else
-			coll.insert value
-			console.log "Adding collection #{JSON.stringify query}..."
-		
-		callback null, value if callback
-	
-	value
-
-#### Other utility functions ##############################
-exports.parseIdent = (ident) ->
-	res = {}
-	[_, ns, name] = ident.match /(?:([A-Za-z_-]+):|)([A-Za-z][^\t\n\r#]*)/
-	
-	res["ident"]     = ident
-	res["namespace"] = ns or "Default"
-	res["name"]      = utils.wykenc.titleEncode name
-	res["fullname"]  = res["namespace"] + ":" + res["name"]
-	
-	if res["namespace"] == "Default"
-		res["pretty"] = res["name"]
-	else
-		res["pretty"] = res["namespace"] + ":" + res["name"]
-	
-	res
-#### The actual wyke object ###############################
-exports.Wyke = Wyke = (opts) ->
-	@database = opts['database']
-	@
-
-Wyke::storePage = (ident, body, isUpdate = true, callback) ->
-	title = exports.parseIdent ident
-	page =
-		ident : ident
-		title : title
-		body  : body
-	
-	if typeof isUpdate == 'function'
-		callback = isUpdate
-		isUpdate = true
-	
-	createOrUpdate @database.collection('pages'), {ident: ident}, page, isUpdate, callback
-
-Wyke::storeNamespace = (nsName, obj, isUpdate = true, callback) ->
-	obj["name"] = nsName
-	createOrUpdate @database.collection('namespaces'), {name: nsName}, obj, isUpdate, callback
-
-# Wraps a "raw" database page entry in a more OOP-y kind of object. This is the
-# preferred way to e.g. get the namespace of a page, since it caches the result.
-#   Trying to wrap an already wrapped page object simply returns the same value,
-# so it is safe to use this function even if you're unsure whether the page is
-# wrapped.
-wrapPage = (wyke, page) ->
-	#### page.getNamespace ################################
-	return page if page.getNamespace
-	
-	page.getNamespace = (callback) ->
-		page = this
-		whenDone = ->
-			callback null, page._namespace
-		
-		return do whenDone if @_namespace
-		
-		getNs = (name, notFoundCallback) ->
-			wyke.database.collection('namespaces').findOne {name: name}, (err, ns) ->
-				return callback err if err
-				return do notFoundCallback if not ns
-				
-				page._namespace = ns
-				do whenDone
-		
-		getNs @title.namespace, ->
-			getNs "Default", ->
-				err = new Error "Fatal: No Default namespace found!"
-				callback err
-	
-	page
-
-# Gets a specific page (by ident) from the database, or errors if not found.
-# This is the preferred way to fetch pages. Returns a wrapped page, so there is
-# no need to call Wyke#wrapPage on the page returned.
-Wyke::getPage = (ident, callback) ->
-	@database.collection('pages').findOne {ident: ident}, (err, page) =>
-		return callback err if err
-		
-		if not page
-			err = new Error "No such page"
-			err.errno = 404
-			return callback err
-		
-		callback null, wrapPage this, page
-
-# Includes a specific tepmlate through a specific includer on a specific
-# source tree.
-# TODO: This needs cleanup.
-applyIncluder = (source, sourceTree, template, includer, args) ->	# , callback
-	ctx =
-		source       : source
-		sourceTree   : sourceTree
-		template     : template
-		args         : args
-		
-		formatString : formatString.bind this
-		utils        : utils
-		vm           : vm
-		console      : console
-	
-#	console.log "Applying #{includer.ident} on #{template.ident}"
-	
-	res = vm.runInNewContext includer.body, ctx, "(includer) #{includer.ident}"
-
-# Renders a given page with a given namespace's renderer.
-applyRendererByNS = (page, str, ns, callback) ->
-	wyke = this
-	wyke.getPage ns.renderer, (err, renderer) ->
-		renderer.getNamespace (err, rNS) ->
-			wyke.getPage rNS.includer, (err, includer) ->
-				res = applyIncluder.call wyke, page, null, renderer, includer, ["FAKE-NULL", str]
-				callback null, res
-
-# Converts wykedown->HTML completely, including substitution of templates.
-formatString = (str, args, callback) ->
-	wyke = this
-	
-	res = wykedown.parse str
-	templatesQuery = {}
-	
-	# Process nodes that need post-processing. Also create a map of what
-	# templates are being included.
-	res = utils.tree.travel res, (nodeI) ->
-		[name, attrs] = this
-		# Process nodes here!
-		switch name
-			when 'header'
-				attrs['id'] = utils.wykenc.encode utils.tree.nodeToString this
-			
-			when 'wikilink'
-				@[0]           = 'a'
-				attrs['href']  = '/wyke/view/' + utils.wykenc.titleEncode attrs['target']
-				attrs['class'] = 'wikilink'
-				delete attrs['target']
-			
-			when 'template'
-				if attrs['name'][0] == ':'	# we're dealing with an argument (e.g. {{:1}})
-					prop  = attrs['name'][1..]
-					value = args[prop] or ["{{#{attrs['name']}}}"]
-					utils.tree.replaceNode nodeI, value.map (x) -> {original: x}
-				
-				else
-					ident = utils.wykenc.titleEncode attrs['name']
-					templatesQuery[ident] = ident
-	
-	# Fetch all the needed templates asynchronously, then perform the inclusion.
-	utils.async.map templatesQuery, wyke.getPage.bind(wyke), (err, templates) ->
-		return callback err if err
-		
-		mapper = (page, callback) ->
-			page.getNamespace (err, ns) ->
-				wyke.getPage ns.includer, callback
-		
-		utils.async.map templates, mapper, (err, includers) ->
-			res = utils.tree.travel res, (nodeI) ->
-				[name, attrs] = this
-				
-				if name == 'template'
-					ident        = utils.wykenc.titleEncode attrs['name']
-					includedPage = templates[ident]
-					includer     = includers[ident]
-					args         = [includedPage.ident].concat attrs['args']
-					
-					applyIncluder.call wyke, null, nodeI, includedPage, includer, args
-			
-			callback null, res
-
-# Renders a given page as HTML through its namespace's associated renderer.
-# First argument is either a Page or a string specifying the ident of the page.
-Wyke::renderPage = (page, callback) ->
-	wyke = this
-	
-	if typeof page == 'string'
-		pages.findOne {ident: page}, (err, page) ->
-			wyke.renderPage page, callback
-		return
-	
-	globalRendererNS = {renderer: 'Internal:GlobalRenderer'}
-	
-	page.getNamespace (err, ns) ->
-		applyRendererByNS.call wyke, page, page.body, ns, (err, res) ->
-			applyRendererByNS.call wyke, page, res, globalRendererNS, (err, res) ->
-				return callback err if err
-				formatString.call wyke, res, {}, (err, res) ->
-					callback null, wykedown.toHTML res
-
-# Renders an internal page. First argument is an options object, with the
-# following properties: {ident: string, title: string, body: string[, internal: boolean]}
-Wyke::renderInternalPage = (opts, callback) ->
-	page = {
-		ident    : opts.ident ? "--internal:unknown"
-		title    : {
-			ident     : opts.ident ? "--internal:unknown"
-			namespace : "--internal"
-			name      : opts.title ? "Internal page"
-			pretty    : opts.title ? "Internal page"
-		}
-		body     : opts.body
-		internal : opts.internal ? true
-	}
-	
-	getRenderer = (ident, callback) ->
-		if typeof ident != 'string'		# ident is already a renderer
-			return callback null, ident
-		
-		wyke.getPage ident, callback
-	
-	@getPage 'Internal:GlobalRenderer', (err, renderer) ->
-		ctx =
-			page     : page
-			content  : page.body
-			wykedown : wykedown
-		
-		try
-			res = vm.runInNewContext renderer.body, ctx, "(renderer) #{renderer.ident}"
-			callback null, res
-		
-		catch err
-			msg = "Error in renderer #{renderer.ident}:\n" + err.stack
-			callback new Error msg
-
-#### TEMPORARY: For debugging purposes ####################
-# require './app'
+var vm       = require('vm')
+  , wykedown = require('./wykedown') 
+  , utils    = require('./utils')
+
+function createOrUpdate(coll, query, value, isUpdate, callback) {
+	coll.findOne(query, function(err, oldValue) {
+		if (err && callback) return callback(err)
+
+		if (oldValue) {
+			if (isUpdate) {
+				coll.update(query, value)
+				console.log("Updating collection " + (JSON.stringify(query)) + "...")
+			}
+		} else {
+			coll.insert(value)
+			console.log("Adding collection " + (JSON.stringify(query)) + "...")
+		}
+
+		if (callback) {
+		return callback(null, value)
+		}
+	})
+	return value
+}
+
+exports.parseIdent = function(ident) {
+	var res = {}
+	var matches = ident.match(/(?:([A-Za-z_-]+):|)([A-Za-z][^\t\n\r#]*)/)
+	  , ns   = matches[1]
+	  , name = matches[2]
+
+	res["ident"]     = ident
+	res["namespace"] = ns || "Default"
+	res["name"]      = utils.wykenc.titleEncode(name)
+	res["fullname"]  = res["namespace"] + ":" + res["name"]
+
+	if (res["namespace"] == "Default") {
+		res["pretty"] = res["name"]
+	} else {
+		res["pretty"] = res["namespace"] + ":" + res["name"]
+	}
+
+	return res
+}
+
+var Wyke = exports.Wyke = function(opts) {
+	this.database = opts['database']
+}
+
+Wyke.prototype.storePage = function(ident, body, isUpdate, callback) {
+	if (isUpdate == null) {
+		isUpdate = true
+	}
+	if (typeof isUpdate === 'function') {
+		callback = isUpdate
+		isUpdate = true
+	}
+
+	var title = exports.parseIdent(ident)
+	var page = {
+		ident : ident,
+		title : title,
+		body  : body
+	}
+	return createOrUpdate(this.database.collection('pages'), {
+		ident: ident
+	}, page, isUpdate, callback)
+}
+
+Wyke.prototype.storeNamespace = function(nsName, obj, isUpdate, callback) {
+	if (isUpdate == null) isUpdate = true
+
+	obj["name"] = nsName
+
+	return createOrUpdate(this.database.collection('namespaces'), {
+		name: nsName
+	}, obj, isUpdate, callback)
+}
+
+function wrapPage(wyke, page) {
+	if (page.getNamespace) return page
+
+	page.getNamespace = function(callback) {
+		var self = this
+
+		function whenDone() {
+			return callback(null, self._namespace)
+		}
+
+		if (this._namespace) return whenDone()
+
+		function getNs(name, notFoundCallback) {
+			return wyke.database.collection('namespaces').findOne({
+				name: name
+			}, function(err, ns) {
+				if (err) return callback(err)
+				if (!ns) return notFoundCallback()
+
+				self._namespace = ns
+				return whenDone()
+			})
+		}
+
+		return getNs(this.title.namespace, function() {
+			return getNs("Default", function() {
+				var err = new Error("Fatal: No Default namespace found!")
+				return callback(err)
+			})
+		})
+	}
+
+	return page
+}
+
+Wyke.prototype.getPage = function(ident, callback) {
+	var self = this
+
+	return this.database.collection('pages').findOne({
+		ident: ident
+	}, function(err, page) {
+		if (err) return callback(err)
+
+		if (!page) {
+			err       = new Error("No such page")
+			err.errno = 404
+			return callback(err)
+		}
+
+		return callback(null, wrapPage(self, page))
+	})
+}
+
+function applyIncluder(source, sourceTree, template, includer, args) {
+	var ctx, res
+
+	ctx = {
+		source       : source,
+		sourceTree   : sourceTree,
+		template     : template,
+		args         : args,
+		formatString : formatString.bind(this),
+		utils        : utils,
+		vm           : vm,
+		console      : console
+	}
+
+	return vm.runInNewContext(includer.body, ctx, "(includer) " + includer.ident)
+}
+
+function applyRendererByNS(page, str, ns, callback) {
+	var wyke = this
+
+	return wyke.getPage(ns.renderer, function(err, renderer) {
+		return renderer.getNamespace(function(err, rNS) {
+			return wyke.getPage(rNS.includer, function(err, includer) {
+				var res = applyIncluder.call(wyke, page, null, renderer,
+						includer, ["FAKE-NULL", str])
+				callback(null, res)
+			})
+		})
+	})
+}
+
+function formatString(str, args, callback) {
+	var wyke = this
+	var res  = wykedown.parse(str)
+	var templatesQuery = {}
+
+	res = utils.tree.travel(res, function(nodeI) {
+		var ident, prop, value
+		var name  = this[0]
+		  , attrs = this[1]
+
+		switch (name) {
+			case 'header':
+				attrs['id'] = utils.wykenc.encode(utils.tree.nodeToString(this))
+				break
+
+			case 'wikilink':
+				this[0] = 'a'
+
+				attrs['href']  = '/wyke/view/' + utils.wykenc.titleEncode(attrs['target'])
+				attrs['class'] = 'wikilink'
+				delete attrs['target']
+				break
+
+			case 'template':
+				if (attrs['name'][0] === ':') {
+					prop = attrs['name'].slice(1)
+					value = args[prop] || ["{{" + attrs['name'] + "}}"]
+
+					utils.tree.replaceNode(nodeI, value.map(function(x) {
+						return { original: x }
+					}))
+				} else {
+					ident = utils.wykenc.titleEncode(attrs['name'])
+					templatesQuery[ident] = ident
+				}
+				break
+		}
+	})
+
+	return utils.async.map(templatesQuery, wyke.getPage.bind(wyke),
+			function(err, templates) {
+		if (err) return callback(err)
+
+		function mapper(page, callback) {
+			page.getNamespace(function(err, ns) {
+				wyke.getPage(ns.includer, callback)
+			})
+		}
+
+		utils.async.map(templates, mapper, function(err, includers) {
+			res = utils.tree.travel(res, function(nodeI) {
+				var name  = this[0]
+				  , attrs = this[1]
+
+				if (name == 'template') {
+					var ident        = utils.wykenc.titleEncode(attrs['name'])
+					  , includedPage = templates[ident]
+					  , includer     = includers[ident]
+
+					var args = [includedPage.ident].concat(attrs['args'])
+
+					return applyIncluder.call(wyke, null, nodeI, includedPage,
+							includer, args)
+				}
+			})
+
+			callback(null, res)
+		})
+	})
+}
+
+Wyke.prototype.renderPage = function(page, callback) {
+	var wyke = this
+
+	if (typeof page == 'string') {
+		pages.findOne({ ident: page }, function(err, page) {
+			wyke.renderPage(page, callback)
+		})
+
+		return
+	}
+
+	var globalRendererNS = {
+		renderer: 'Internal:GlobalRenderer'
+	}
+
+	page.getNamespace(function(err, ns) {
+		applyRendererByNS.call(wyke, page, page.body, ns, function(err, res) {
+			applyRendererByNS.call(wyke, page, res, globalRendererNS, function(err, res) {
+				if (err) return callback(err)
+
+				return formatString.call(wyke, res, {}, function(err, res) {
+					 callback(null, wykedown.toHTML(res))
+				})
+			})
+		})
+	})
+}
+
+Wyke.prototype.renderInternalPage = function(opts, callback) {
+	var page = {
+		ident: opts.indent || "--internal:unknown",
+		title: {
+			ident     : opts.ident || "--internal:unknown",
+			namespace : "--internal",
+			name      : opts.title || "Internal page",
+			pretty    : opts.title || "Internal page"
+		},
+		body: opts.body,
+		internal: opts.internal || (opts.internal == null)
+	}
+
+	function getRenderer(ident, callback) {
+		if (typeof ident !== 'string'){
+			callback(null, ident)
+		} else {
+			wyke.getPage(ident, callback)
+		}
+	}
+
+	this.getPage('Internal:GlobalRenderer', function(err, renderer) {
+		var ctx = {
+			page     : page,
+			content  : page.body,
+			wykedown : wykedown
+		}
+
+		try {
+			var res = vm.runInNewContext(renderer.body, ctx,
+					"(renderer) " + renderer.ident)
+			callback(null, res)
+		} catch (err) {
+			var msg = ("Error in renderer " + renderer.ident + ":\n") + err.stack
+			callback(new Error(msg))
+		}
+	})
+}