Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
243 lines (192 sloc) 6.5 KB
class CoffeeTalkClass
constructor: (props) ->
@name =
@extends = props.extends
@package = props.package
@namespace = if props.namespace? and props.namespace.length then props.namespace else "GLOBAL"
@description = props.description
@slots = []
@id = "#{@package}.#{@namespace}.#{@name}"
toClassString: ->
classString = "class #{if @namespace then "#{namespace}." else ""}#{@name}#{if @extends then " extends #{@extends}" else ""}\n"
for slot in @slots
classString += "\t#{if slot.type is "class" then "@" else ""}#{}: "
for line in slot.body.split("\n")
classString += "\t\t#{line}\n\n"
toJSON: ->
id: @id
name: @name
extends: @extends
package: @package
namespace: @namespace
description: @description
slots: @slots
class CoffeeTalkSlot
constructor: (props) ->
@name =
@type = props.type
@version = props.version
@protocol = props.protocol
@body = props.body
@description = props.description
@classId = props.classId
@id = "#{@classId}.#{@name}"
toJSON: ->
id: @id
name: @name
type: @type
version: @version
protocol: @protocol
body: @body
description: @description
class CoffeeTalkPersistance
getClassList: -> "child responsibility"
saveClass: -> "child responsibility"
saveSlot: -> "child responsibility"
class CoffeeTalkFile
@getClassById: (id) ->
[pkg, nameSpace, className] = id.split '.'
name = [className]
if nameSpace isnt "GLOBAL" then name.unshift nameSpace
(new CoffeeTalkFile name.join('/') + "/class.json").asClass()
@getSlotById: (id) ->
[pkg, nameSpace, className, slotName] = id.split '.'
name = [className, "slots"]
if nameSpace isnt "GLOBAL" then name.unshift nameSpace
(new CoffeeTalkFile name.join('/') + "/#{slotName}.json").asSlot([pkg, nameSpace, className].join '.')
constructor: (name) ->
@name = name
@parts = name.replace(/\//g, '.').split('.')
@fs = require "fs"
@wrench = require "wrench"
isClass: ->
@parts[@parts.length - 2] is "class" and @isJsonExt()
isJsonExt: ->
@parts[@parts.length - 1] is "json"
readAsJson: ->
return JSON.parse @fs.readFileSync "#{CoffeeTalkFile.classDir}/#{@name}", "UTF8"
catch e
console.log e
return false
asSlot: (classId) ->
props = @readAsJson()
if not props then return false
props.body = @fs.readFileSync "#{CoffeeTalkFile.classDir}/#{@parts[0..-2].join "/"}.coffee", "UTF8"
props.classId = classId
new CoffeeTalkSlot props
asClass: ->
classDef = @readAsJson()
if not classDef then return false
coffeeTalkClass = new CoffeeTalkClass classDef
slotDir = "#{@parts[0..-3].join "/"}/slots"
for slotFileName in @wrench.readdirSyncRecursive("#{CoffeeTalkFile.classDir}/#{slotDir}")
slotFile = new CoffeeTalkFile "#{slotDir}/#{slotFileName}"
if slotFile.isJsonExt()
newSlot = slotFile.asSlot(
if newSlot then coffeeTalkClass.slots.push newSlot
class CoffeeTalkPersistanceFlatFile extends CoffeeTalkPersistance
constructor: (props) ->
@wrench = require "wrench"
@fs = require "fs"
@classDir = props.classDir
CoffeeTalkFile.classDir = @classDir
getClassList: ->
classes = []
for fileName in @wrench.readdirSyncRecursive(@classDir)
file = new CoffeeTalkFile fileName
if file.isClass()
newClass = file.asClass()
if newClass then classes.push newClass
_getClassDir: (_class) ->
"#{@classDir}/#{if _class.namespace isnt "GLOBAL" then "#{_class.namespace}/" else ""}#{}/"
saveClass: (data) ->
if not data.namespace? or data.namespace.trim().length is 0
data.namespace = "GLOBAL"
newClass = new CoffeeTalkClass data
newClassDir = @_getClassDir newClass
oldClass = CoffeeTalkFile.getClassById
oldClassDir = @_getClassDir oldClass
if oldClassDir isnt newClassDir
wrench.rmdirSyncRecursive oldClassDir, true
@wrench.mkdirSyncRecursive "#{newClassDir}slots"
classDef = newClass.toJSON()
delete classDef.slots
@fs.writeFileSync "#{newClassDir}class.json", JSON.stringify classDef
(new CoffeeTalkFile "#{newClassDir}class.json").asClass()
deleteClass: (data) ->
oldClass = CoffeeTalkFile.getClassById
baseClassDir = @_getClassDir oldClass
@wrench.rmdirSyncRecursive baseClassDir, true
saveSlot: (data) ->
#first get old by id
if not
newSlot = new CoffeeTalkSlot data
oldSlot = CoffeeTalkFile.getSlotById
data.classId = oldSlot.classId
newSlot = new CoffeeTalkSlot data
#build base dirs
baseClassDir = @_getClassDir CoffeeTalkFile.getClassById data.classId
baseSlotDir = baseClassDir + "/slots/"
#if id is different than delete old first
if oldSlot? and isnt
@fs.unlinkSync "#{baseSlotDir}#{}.coffee"
@fs.unlinkSync "#{baseSlotDir}#{}.json"
@fs.writeFileSync "#{baseSlotDir}#{}.coffee", newSlot.body
slotDef = newSlot.toJSON()
delete slotDef.body
@fs.writeFileSync "#{baseSlotDir}#{}.json", JSON.stringify slotDef
deleteSlot: (data) ->
oldSlot = CoffeeTalkFile.getSlotById
#build base dirs
baseClassDir = @_getClassDir CoffeeTalkFile.getClassById oldSlot.classId
baseSlotDir = baseClassDir + "/slots/"
@fs.unlinkSync "#{baseSlotDir}#{}.coffee"
@fs.unlinkSync "#{baseSlotDir}#{}.json"
class CoffeeTalkServer
constructor: ->
@express = require "express"
@socketio = require ""
args = require("optimist")
.usage("Pass port as -p (to use port 80 you need to sudo)")
.default("p", "6655")
.alias("c", "classDir")
.default("c", "Class")
.alias("d", "debug")
.alias("p", "port")
@ctpFlatFile = new CoffeeTalkPersistanceFlatFile classDir: args.c
@debug = args.d
@port = args.p
start: ->
app = @express.createServer()
io = @socketio.listen(app)
io.set "log level", 1
app.listen @port
app.configure =>
app.use @express.static(__dirname + '/public')
io.sockets.on 'connection', (socket) =>
socket.emit "classList", classes: @ctpFlatFile.getClassList()
socket.on 'saveNode', (req) =>
method = if req.method in ["create", "update"] then "save" else req.method
responseData = @ctpFlatFile[method + req.type](
socket.emit "updateNode",
method: req.method
type: req.type
data: responseData
reqID: req.reqID
console.log "go to http://localhost:#{@port}/"
exports.CoffeeTalkServer = CoffeeTalkServer