From 42d3b747a1382e17e2be7500c89d02bf0970cb12 Mon Sep 17 00:00:00 2001 From: Julian bilcke Date: Sun, 10 Jun 2012 15:08:51 +0200 Subject: [PATCH] updated test --- README.md | 55 ++++++++++++++++++++++++++++++--- lib/recorder.js | 74 ++++++++++++++++++++++++++++++++++++--------- src/recorder.coffee | 62 +++++++++++++++++++++++++++---------- test/main.coffee | 32 +++++++++++++++++--- 4 files changed, 184 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 2b1f520..47bf33a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,11 @@ This library is still in development so expect heavy refactoring and sparse documentation until I have more time to settle everything. +### TODO / Wishlist + + * insertion of event at arbitrary timesteps (eg. when working with incomplete time-series) + * reverse playback! + ### License BSD @@ -45,10 +50,52 @@ ## Documentation -### How it works +### Example + +``` coffeescript +# myapp.coffee +Recorder = require 'recorder' + +record = new Recorder() +record.on 'event', (event) -> log "#{event.timestamp}: #{event.data}" + +# coffee-style timeouts +delay = (t,f) -> setTimeout f, t + +# record some dummy events +log "recording events.." +delay 100, -> record.rec companyhelpdesk: "hi how can I help you" +delay 500, -> record.rec facebook: "wow! this was a big earthquake" +delay 1000, -> record.rec twitter: "just saw my dead neighbor walking in my street. It's weird. wait I'm gonna check it out" +delay 1500, -> record.rec twitter: "ZOMBIE APOCALYPSE!!1!!" + +delay 2000, -> + log "playing events back.." + record.play() + +delay 5000, -> + log "playing events back. and faster." + record.play 5.0 # 2.0x + +``` + + which should output something like: + +``` + +10 Jun 14:57:49 - recording events.. +10 Jun 14:57:51 - playing events back.. +10 Jun 14:57:51 - 1339333069383: { companyhelpdesk: 'hi how can I help you' } +10 Jun 14:57:51 - 1339333069783: { facebook: 'wow! this was a big earthquake' } +10 Jun 14:57:52 - 1339333070284: { twitter: 'just saw my dead neighbor walking in my street. It\'s weird. wait I\'m gonna check it out' } +10 Jun 14:57:52 - 1339333070784: { twitter: 'ZOMBIE APOCALYPSE!!1!!' } +10 Jun 14:57:54 - playing events back. and faster. +10 Jun 14:57:54 - 1339333069383: { companyhelpdesk: 'hi how can I help you' } +10 Jun 14:57:54 - 1339333069783: { facebook: 'wow! this was a big earthquake' } +10 Jun 14:57:54 - 1339333070284: { twitter: 'just saw my dead neighbor walking in my street. It\'s weird. wait I\'m gonna check it out' } +10 Jun 14:57:54 - 1339333070784: { twitter: 'ZOMBIE APOCALYPSE!!1!!' } - Add some documentation here +``` - alert (from node.js docs): + You can see it here but the second batch is two times faster - It is important to note that your callback will probably not be called with the exact temporal sequence - Node.js makes no guarantees about the exact timing of when the callback will fire, nor of the ordering things will fire in. The callback will be called as close as possible to the time specified. \ No newline at end of file diff --git a/lib/recorder.js b/lib/recorder.js index 714eb69..f22f17a 100644 --- a/lib/recorder.js +++ b/lib/recorder.js @@ -1,15 +1,24 @@ // Generated by CoffeeScript 1.3.3 (function() { - var BinaryTree, InMemory, PlaybackModule, RecordModule, contains, error, inspect, log, moment, _, _ref; + var BinaryTree, InMemory, PlaybackModule, RecordModule, contains, delay, error, events, inspect, log, moment, _, _ref, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; _ref = require('util'), log = _ref.log, error = _ref.error, inspect = _ref.inspect; + events = require('events'); + BinaryTree = require('./btree'); _ = require('underscore'); moment = require('moment'); + delay = function(t, f) { + return setTimeout(f, t); + }; + contains = function(item, text) { return text.indexOf(item) !== -1; }; @@ -17,17 +26,28 @@ InMemory = (function() { function InMemory() { + this.push = __bind(this.push, this); this.events = []; + this.first = false; + this.last = false; } InMemory.prototype.push = function(event) { var first, last; first = _.first(this.events); + if (!first) { + first = event; + } first.previous = event; - event.last = first; + event.next = first; + this.first = first; last = _.last(this.events); + if (!last) { + last = event; + } last.next = event; event.previous = last; + this.last = last; return this.events.push(event); }; @@ -43,19 +63,39 @@ this.cursor = 0; this.running = false; this.rate = 1.0; - fire = function(event) {}; + this.looped = false; + fire = function(event) { + var delta, next, now; + main.emit('event', { + timestamp: event.timestamp, + data: event.data + }); + next = event.next; + if (next === main.database.first) { + if (!_this.looped) { + return; + } + } + delta = (next.timestamp - event.timestamp) / _this.rate; + now = moment(); + next.expected = now + delta; + return delay(delta, function() { + return fire(next); + }); + }; main.play = function(rate) { var first; if (rate == null) { - rate = 1.0; + rate = false; + } + if (rate) { + _this.rate = rate; } - _this.rate = rate; first = main.database.first; if (first) { - log("firing first event"); return fire(first); } else { - return log("no event to fire"); + return 1; } }; } @@ -67,13 +107,16 @@ RecordModule = (function() { function RecordModule(main) { - main.record = function(data) { - return main.database.push({ - timestamp: moment(), + main.rec = function(data) { + var timestamp; + timestamp = moment(); + main.database.push({ + timestamp: timestamp, data: data }); + return timestamp; }; - main.recordAt = function(timestamp, data) { + main.overwrite = function(timestamp, data) { throw "Not Implemented"; }; } @@ -82,7 +125,9 @@ })(); - module.exports = (function() { + module.exports = (function(_super) { + + __extends(exports, _super); function exports(url) { if (url == null) { @@ -92,7 +137,8 @@ if (contains("file://", url)) { this.database = new SimpleFile(url); } else { - log("using default database (in-memory)"); + 1; + } new RecordModule(this); new PlaybackModule(this); @@ -100,6 +146,6 @@ return exports; - })(); + })(events.EventEmitter); }).call(this); diff --git a/src/recorder.coffee b/src/recorder.coffee index f67ee95..3f3547e 100644 --- a/src/recorder.coffee +++ b/src/recorder.coffee @@ -25,26 +25,35 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {log,error,inspect} = require 'util' +events = require 'events' BinaryTree = require './btree' _ = require 'underscore' moment = require 'moment' +delay = (t,f) -> setTimeout f, t + contains = (item, text) -> (text.indexOf(item) isnt -1) class InMemory constructor: () -> @events = [] + @first = no + @last = no - push: (event) -> + push: (event) => # for the moment, we can only manage insertion at the end # TODO later: use https://github.com/vadimg/js_bintrees first = _.first @events + first = event unless first first.previous = event - event.last = first + event.next = first + @first = first last = _.last @events + last = event unless last last.next = event event.previous = last + @last = last @events.push event @@ -55,39 +64,59 @@ class PlaybackModule @cursor = 0 @running = false @rate = 1.0 + @looped = no + fire = (event) => - #BROKEN - #next = main.database.nextr event.timestamp - #delta = event.timestamp - next.timestamp - #setTimeout delta, -> + #log "fire! #{inspect event}" + main.emit 'event', timestamp: event.timestamp, data: event.data + #log "checking next event.." + next = event.next + if next is main.database.first + #log "reched the end. checking if we should loop.." + return unless @looped + #log "looping.." + delta = (next.timestamp - event.timestamp) / @rate + now = moment() + next.expected = now + delta + #log "event.timestamp: #{event.timestamp} next.timestamp: #{next.timestamp} now: #{now} delta: #{delta} next.expected: #{next.expected}" + + delay delta, -> + #log "setTimeout triggered. calling fire next" + fire next # @fire nextEvent # give playback capabilities to the main class - main.play = (rate=1.0) => - @rate = rate + main.play = (rate=no) => + @rate = rate if rate + #log "PlaybackModule: playing at rate #{@rate}" first = main.database.first if first - log "firing first event" + #log "firing first event" fire first else - log "no event to fire" + #log "no event to fire" + 1 class RecordModule constructor: (main) -> # give record capabilities to the main class - main.record = (data) -> + main.rec = (data) -> + #log "RecordModule: pushing into database the event" + timestamp = moment() main.database.push - timestamp: moment() + timestamp: timestamp data: data + #log "RecordModule: db is #{inspect main.database.events}" + timestamp - main.recordAt = (timestamp, data) -> + main.overwrite = (timestamp, data) -> throw "Not Implemented" return -class module.exports +class module.exports extends events.EventEmitter constructor: (url="") -> @database = new InMemory() @@ -95,9 +124,10 @@ class module.exports if contains "file://", url @database = new SimpleFile url else - log "using default database (in-memory)" + #log "using default database (in-memory)" + 1 # now initialize the sub-controllers new RecordModule @ new PlaybackModule @ - + diff --git a/test/main.coffee b/test/main.coffee index fad555a..a494d15 100644 --- a/test/main.coffee +++ b/test/main.coffee @@ -1,10 +1,32 @@ -recorder = require '../lib/recorder' +Recorder = require '../lib/recorder' +{log,error,inspect} = require 'util' +delay = (t,f) -> setTimeout f, t + +# these are not real tests, it's more like a demo +# more complete tests should check for returned values!!! # our tests describe 'Recorder', -> describe '#new Recorder()', -> it 'should work', (done) -> - recorder = new Recorder() - recorder.insert foo: 'bar' - #if (err) throw err - done() \ No newline at end of file + + record = new Recorder() + record.on 'event', (event) -> log "#{event.timestamp}: #{inspect event.data}" + + # record some dummy events + log "recording events.." + delay 100, -> record.rec companyhelpdesk: "hi how can I help you" + delay 500, -> record.rec facebook: "wow! this was a big earthquake" + delay 1000, -> record.rec twitter: "just saw my dead neighbor walking in my street. It's weird. wait I'm gonna check it out" + delay 1500, -> record.rec twitter: "ZOMBIE APOCALYPSE!!1!!" + + delay 1600, -> + log "playing events back.." + record.play() + + delay 100, -> + log "playing events back. and faster." + record.play 5.0 # 2.0x + done() + + #if (err) throw err