rehanift / engine.js

A scriptable task engine
MIT License
23 stars 4 forks source link

Feature: ability to persist context's between task runs #15

Open ericallam opened 12 years ago

ericallam commented 12 years ago

For the use case I am trying to use engine.js for It would be very helpful to be able to setup a context and then have multiple tasks run against that context. The use case (for me) is this:

A user submits code that somehow changes the context (let's say, incrementing a counter), does a console.log, and returns a certain value. I want to be able to verify three things:

Currently, I can only verify the first two in the above list. I think to give the most flexibility and to limit the changes to the scope and feature set of engine.js, we could allow multiple tasks to run against a single context. The api might look something like this (assuming the api changes discussed in pull request 14:

var context = client.createContext("(function(locals) { var count=0; return { incr: function(){ count+=1; return count }}})");

var task = client.createTask();
task.setContext(context);
task.setLocals({});
task.setCode("incr()");

client.run(task, function(err, result, logs){
  console.log(result); // 1

  var verifyTask = client.createTask();
  verifyTask.setContext(context);
  verifyTask.setLocals({});
  verifyTask.setCode("incr()");

  client.run(verifyTask, function(err, result, logs){
    console.log(result); // 2
  });
});

Please let me know if this even sounds doable.

rehanift commented 12 years ago

It sounds like the goal is to be able to read back the context's globals after a task has run. Here is a sketch of how I can see this working with the existing API. Would this work for your use-case?

// File: counting_context.js /////////////////////////////////////////

(function(locals) { 

    return {
        count: 3,
        hello: "world",
        incr_global: function(){ 
            this.count+=1;
            return this.count;
        }
        ,incr_local: function(){
            locals.count++;
            return locals.count;
        }
    };
});

//////////////////////////////////////////////////////////////////////

var engine = require("engine.js").engine,
    client = engine.client.create(),
    task = client.createTask(),
    fs = require("fs");

task.setContext(fs.readFileSync("./counting_context.js","utf-8"));
task.setCode("incr_global() + incr_local();");
task.setLocals({count:5, foo:"bar"});

task.on("eval", function(last_eval, globals, locals){
    /*
     * last_eval: The last evaluated expression from the context
     *   ie. 10
     * 
     * globals: An object-literal of all global variables 
     *             (except functions) from the context, after the 
     *             task has finished
     *   ie. {
     *         count: 4,
     *         hello: "world"
     *       }
     * 
     * locals: An object-literal of all local variables from the 
     *             task-run, after the task has finished
     *   ie. {
     *         count: 6,
     *         foo: "bar"
     *       }
     * 
     */

});

task.run();
ericallam commented 12 years ago

This would help a lot actually, and is probably much easier to implement than my suggestion. To move forward, should I just write up some pending specs, and document the feature?

rehanift commented 12 years ago

Ok. In preparation for this I started integrated contexify as the actual code execution strategy (rather than using the native vm module). I pushed my initial changes here d806a8dd30a831deb08f593f992ab9c38b99b839. All specs are passing, but I'm going to do some cleanup and refactoring.

If you could write a end-to-end test for this that would be a great way to get started.

ericallam commented 12 years ago

Awesome, that looks pretty handy! I'll hopefully get some time tonight to write that end to end spec.