Skip to content

Commit

Permalink
added hotreload command
Browse files Browse the repository at this point in the history
  • Loading branch information
Francesco Zanitti authored and Francesco Zanitti committed Feb 27, 2011
1 parent e72b029 commit a090bfe
Show file tree
Hide file tree
Showing 20 changed files with 426 additions and 124 deletions.
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
# Wirez: runtime management of module dependencies for node

## Dependencies

### Wirez 0.0.1/2

- [wu](http://fitzgen.github.com/wu.js/ wu), 0.1.8

## Install

I am going to publish on npm, in the mean time you can download the code and npm-link it.
<strike>I am going to publish on npm, in the mean time you can download the code and npm-link it.</strike>

`npm install wirez`

## Features

Expand All @@ -27,8 +35,31 @@ This will start node and Wirez. If you change one of the module of your applicat

A *very* simple example is in the example directory. Just go there and type wirez.

## Wirez CLI interface

Wirez can be used to query and modify running wirez processes. Each wirez app starts a control server automatically. As of version 0.0.2, there is a single transport for the command server (unix domain).
In the directory where you started wirez, you can issue the following commands:

- `wirez list` displays the current installed wirez modules and their status (active, stopped)
- `wirez info` displays various info. As of versions 0.0.2, only the module dependencies are showed
- `wirez start <module>` start a stopped module
- `wirez stop <module>` stop an active module
- `wirez hotreload <module>` remove the code cache and reload the module
- `wirez install <module>` install a module
- `wirez shutdown` stop all modules and wirez itself

The "module" argument for some command is the file name of the module (without the .js extension), relative to the app directory; e.g. if wirez is running in the directory `/Users/foo/myapp/` and you want to install the module `/Users/foo/myapp/mylib/bar.js`, then the right command is: `wirez install mylib/bar`.

## Gotchas

I just started to build this "thing" (as of 23/02/2011), and for my needs is quite good. Of course there must be dozens of cases that I have not handled yet. If so and you badly want some feature, please either file a request or even better, collaborate on the project.

I currently managed to force a reload of a module code by deleting the cache in the `require` function. This is not very well supported by the node guys, and it might broke anytime. Also the way I get the "calling" module (the module where you write `require`) is rather tricky (I navigate through the function callers and find the `require` caller, in which I know where to find the filename of the module). If you want to see this, have a look at `lib/wirez/misc.js` and `lib/reloader/misc.js`)
I currently managed to force a reload of a module code by deleting the cache in the `require` function. This is not very well supported by the node guys, and it might broke anytime. Also the way I get the "calling" module (the module where you write `require`) is rather tricky (I navigate through the function callers and find the `require` caller, in which I know where to find the filename of the module). If you want to see this, have a look at `lib/wirez/misc.js` and `lib/reloader/misc.js`)

## Wish list

- Handle directory module dependencies
- Asynchronous start/stop of modules
- A decent CLI interface
- Supervisor behavior: if a module cause the app to crash, it should be stopped and uninstalled
- currently, if a module crashes, the entire app and wirez will crash too
55 changes: 51 additions & 4 deletions bin/wirez.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,56 @@
;(function () {
var util = require('util')
, wirez = require('wirez')
, path = require('path')
, fs = require('fs')
, wu = require('wu').wu

var args = process.argv
args.shift()
args.shift()

wirez.log('Start')
wirez.bootstrap.start( process.cwd() )
wirez.reloader.start()
wirez.log('Started')
var start = function( bootstrap,reloader,port ) {

wirez.log('Start')
wirez.bootstrap.start( process.cwd() )
wirez.reloader.start()
wirez.manager.start( process.cwd() )
wirez.manager.addComm( 'tcp',port,'ascii' )
wirez.log('Started')
}

var cli = function( port,args ) {
var client = wirez.manager.getClient( 'tcp',port,'ascii',function(resp) {
var response = resp.response
if(response instanceof Array) {
response = response.join('\n')
} else if( typeof response == 'object' ) {
var str = function(obj,tabs) {
var prf = ''; for(var i=0;i<tabs;i++){prf=prf+'\t'}
var tmp = []
wu(obj).eachply(function(k,v){
if( v instanceof Array ) { tmp.push(prf+k+': '+v.join(', ')) }
else if( typeof v == 'object') {
tmp.push(prf+k+': '); tmp.push( str(v,tabs+1) )
}
else { tmp.push(prf+k+': '+v)}
})
return tmp.join('\n')
}
var s = str(response,0)
response = s
}
console.log(response)
client.stop()
} )
client.RPC.apply(client,args)
}

if( args.length==0 ) {
start(true,true, 'wirezmanager.unixdgram' )
} else {
cli('wirezmanager.unixdgram',args)
}


})()
2 changes: 1 addition & 1 deletion example/a.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
var wirez = require('wirez')
, b = wirez.r('./b')
, log = wirez.newLogger('B')
, log = wirez.newLogger('A')


// magic function, called when this module starts
Expand Down
8 changes: 8 additions & 0 deletions example/c.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
var wirez = require('wirez')
, b = wirez.r('./b')
, log = wirez.newLogger('C')

module.exports.wirezStart = function() {log("Starting")}
module.exports.wirezStop = function() {log("Stopping")}

log("Hello from module 'c', " + b.who)
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
var Wirez = require('./lib/wirez').Wirez
var Reloader = require('./lib/reloader').Reloader
var Bootstrap = require('./lib/bootstrap').Bootstrap
var Manager = require('./lib/manager').Manager

module.exports = new Wirez()
module.exports.reloader = new Reloader(module.exports)
module.exports.bootstrap = new Bootstrap(module.exports)
module.exports.bootstrap = new Bootstrap(module.exports)
module.exports.manager = new Manager(module.exports)
6 changes: 4 additions & 2 deletions lib/bootstrap/bootstrap.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@

var p = require('path')

var Bootstrap = module.exports = function(wirez) {
this.wirez = wirez
}
Bootstrap.prototype.start = function( path ) {
require( path||this.wirez.wpath )
index = p.join((path||this.wirez.wpath),'index.js')
this.wirez.requireAbsolute( index )
//require( path||this.wirez.wpath )
}
13 changes: 13 additions & 0 deletions lib/manager/command/hotreload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
var misc = require('../../reloader/misc')

var path = require('path')

module.exports.handle = function( wirez,modname ) {
mod = wirez.getByName(modname)
if(!mod) { return {response: 'Cannot find module '+mod, done:false} }

misc.deleteCache( mod.mod + '.js' )
mod.reload()

return {response: 'ok'}
}
36 changes: 36 additions & 0 deletions lib/manager/command/info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var wu = require('wu').wu

module.exports.handle = function( wirez,modname ) {
if(modname) {
var mod = wirez.getByName(modname)
if(!mod) {
return {response: 'Cannot find module '+modname, done:false}
}
var out = {}
out.name = mod.name()
out.path = mod.mod
out.state = mod.state()
out.dependencies = []
wu( wirez.deps.dependencies(mod.mod) ).each(function(o) {
var m = o[0]
out.dependencies.push(m.name())
})
return {response: out}
}

var deps = wirez.deps
var graph = deps.graph

var out = {}
out.dependencies = {}
wu(graph.index).eachply(function(k,v){
var n = k
if(wirez.wirezes[k]){n=wirez.wirezes[k].name()}
out.dependencies[n] = []
wu( wirez.deps.dependencies(k) ).each(function(o) {
var m = o[0]
out.dependencies[n].push(m.name())
})
})
return {response:out}
}
7 changes: 7 additions & 0 deletions lib/manager/command/install.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var path = require('path')

module.exports.handle = function( wirez,modpath ) {
if(modpath[0]!='/') { modpath = path.join( wirez.wpath,modpath ) }
wirez.requireAbsolute(modpath)
return {response: 'ok'}
}
9 changes: 9 additions & 0 deletions lib/manager/command/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var wu = require('wu').wu

module.exports.handle = function( wirez ) {
return {response: wu(wirez.wirezes).mapply(
function(path,w) {
return w.name() + ' @' + w.state()
}
).toArray()}
}
6 changes: 6 additions & 0 deletions lib/manager/command/shutdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports.handle = function( wirez,modname ) {
return {
response: 'ok'
, onSent: function() { wirez.shutdown(1000) }
}
}
10 changes: 10 additions & 0 deletions lib/manager/command/start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var wu = require('wu').wu

module.exports.handle = function( wirez,modname ) {
var mod = wirez.getByName(modname)
if(!mod) {
return {response: 'Cannot find module '+modname, done:false}
}
mod.start()
return {response: mod.name() + ' @'+mod.state()}
}
10 changes: 10 additions & 0 deletions lib/manager/command/stop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var wu = require('wu').wu

module.exports.handle = function( wirez,modname ) {
var mod = wirez.getByName(modname)
if(!mod) {
return {response: 'Cannot find module '+modname, done:false}
}
mod.stop()
return {response: mod.name() + ' @'+mod.state()}
}
1 change: 1 addition & 0 deletions lib/manager/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports.Manager = require('./manager')
78 changes: 78 additions & 0 deletions lib/manager/io/tcp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
var util = require('util')
, net = require('net')

module.exports.newServerSide = function( mngr,port,enc ) {
return new Server(port,enc)
}
module.exports.newClientSide = function( port,enc,onresponse ) {
return new Client(port,enc,onresponse)
}

var Server = function(port,enc) {
var s = this
this.enc = enc
this.port = port
this.ssock = net.createServer(function(socket){s.handle(socket)})

this.closed=false
this.ssock.on('close',function(){s.closed=true})
}
Server.prototype.start = function(cb) {
this.ssock.listen(this.port)
cb(this)
}
Server.prototype.stop = function() {
if(this.closed) return
this.ssock.close()
}
Server.prototype.onRPC = function( callback,context ) {
this.RPCcallback = callback
this.onRPCContext = context || this
}
Server.prototype.handle = function(socket) {
var s = this
socket.setEncoding(this.enc)
socket.on('data',function(data) {
var command = JSON.parse( data )//msg.toString( this.enc ) )//msg.toString( this.enc ).split('\n')
var out = s.RPCcallback.apply( s.onRPCContext, [command] )
if(out.onSent){
var onsent = out.onSent
socket.on('end',onsent)
delete (out.onSent)
}
out = JSON.stringify(out)
socket.end(out)
})
}

var Client = function(port,enc,onresponse) {
var s = this
this.sock = net.createConnection(port)
this.sock.setEncoding(enc)
this.sock.on('data',function(data) {
onresponse(JSON.parse(data))
s.sock.end()
})
this.closed=false
this.sock.on('end',function(){s.closed=true})
}
Client.prototype.stop = function() {
if(this.closed) return
this.sock.end()
}
Client.prototype.RPC = function( cmd,args ) {
var command = { cmd:cmd }
if(args&&!(args instanceof Array)){ args = [args] }
command.args = args || []
var str = JSON.stringify(command)
this.sock.write(str)
}









Loading

0 comments on commit a090bfe

Please sign in to comment.