Permalink
Browse files

reworked with underscore and new api in mind..more to come

  • Loading branch information...
1 parent 2dd4fbf commit 80644b2add213bf4125b201b0de3df74b635b796 @dhaigh committed Feb 6, 2012
Showing with 361 additions and 186 deletions.
  1. +3 −23 README.md
  2. +3 −1 example/script.js
  3. +1 −0 test/index.html
  4. +163 −34 test/tests.js
  5. +191 −128 xpert.js
View
26 README.md
@@ -3,7 +3,7 @@ Xpert
Xpert is a little thing for making expert systems.
-`var someSexyExpert = new Xpert( tree, questionCallback, resultFoundCallback );`
+`var someSexyExpert = new Xpert(tree, questionCallback, resultFoundCallback);`
Upon initialization, the question callback is immediately invoked.
@@ -76,28 +76,8 @@ Parsed:
So if the first tree was passed to the constructor, the first call to the question callback would be something like:
-`questionCallback( "Question", [["Answer 1", "Result"],["Answer 2", "Another result"],["Answer 3", "Moar result!"]] )`
+`questionCallback("Question", [["Answer 1", "Result"],["Answer 2", "Another result"],["Answer 3", "Moar result!"]])`
Then in your callback function, you take the question argument, display it, then take the answer tree, display each possible answer and keep track of the result of each answer.
-Either way, what you want to do is when an answer is selected, call the `.next()` method of your Xpert object, with the parameter being the result of that answer. Xpert will determine if more questions need to be asked or not, and runs the appropriate callback.
-
-Methods
--------
-
-`.next(tree)` - takes an Xpert object to a new state with a new tree and runs the appropriate callback
-
-`.getQuestions()` - returns an array of all the questions in the tree
-
-`.getResults()` - returns an array of all the results in the tree
-
-Helper functions
-----------------
-
-`Xpert.parseTree(tree)` - takes a raw tree as the only parameter and returns the tree parsed
-
-`Xpert.mapTree(tree, func)` - takes a parsed tree and maps to each response and returns the new tree
-
-`Xpert.getQuestions(tree)` - takes a parsed tree and returns an array of all the questions in the tree
-
-`Xpert.getResults(tree)` - takes a parsed tree and returns an array of all the results in the tree
+Either way, what you want to do is when an answer is selected, call the `.next()` method of your Xpert object, with the parameter being the result of that answer. Xpert will determine if more questions need to be asked or not, and runs the appropriate callback.
View
4 example/script.js
@@ -168,7 +168,9 @@
});
- expert.get('results').forEach( function (result) {
+ console.log(expert.get('results'));
+
+ _.forEach(expert.get('results'), function (result) {
// trim off the wiki part
results.push( result.split("|")[0] );
});
View
1 test/index.html
@@ -5,6 +5,7 @@
<title>QUnit Tests</title>
<link rel="stylesheet" href="qunit/qunit.css" media="screen">
<script src="qunit/qunit.js"></script>
+ <script src="../underscore-min.js"></script>
<script src="../xpert.js"></script>
</head>
<body>
View
197 test/tests.js
@@ -39,7 +39,7 @@ test( "Xpert", function () {
equal( expert1.displayResult, r1, "result callback registered" );
});
-test( "Xpert#next", function () {
+test( "Xpert#next()", function () {
expect( 4 );
expert1.next( tree1parsed[1][0] );
@@ -59,37 +59,157 @@ test( "Xpert#next", function () {
});
-test( "Xpert#mapTree", function () {
+test( "Xpert#map(callback)", function () {
expect( 2 );
- deepEqual( expert1.mapTree( function (response) {
+ deepEqual( expert1.map( function (response) {
return response + "x";
}), ["testing 123x",[["yesx","foox"],["nox","barx"]]]);
deepEqual( expert1.tree, ["testing 123",[["yes","foo"],["no","bar"]]], "tree not modified" );
});
-test( "Xpert#getQuestions", function () {
+test( "TODO: Xpert#map('questions', callback)", function () {
+
+});
+
+test( "TODO: Xpert#map('answers', callback)", function () {
+
+});
+
+test( "TODO: Xpert#map('results', callback)", function () {
+
+});
+
+test( "TODO: Xpert#get('questions')", function () {
+
+});
+
+test( "Xpert#get('questions')", function () {
+ expect( 5 );
+
+ deepEqual( expert1.get('questions'), ["testing 123"] );
+ deepEqual( expert2.get('questions'), ["testing 123","moar test?"] );
+ deepEqual( expert3.get('questions'), ["testing 123","can has foo?","sure?","moar test?"] );
+ deepEqual( expert4.get('questions'), ["whitespace test!!1"] );
+ deepEqual( expert5.get('questions'), ["x?","test..?","0.1+0.2?","bla?","blaa?","blaaaa?","who?","PhD?","5x2?","q?"] );
+});
+
+test( "TODO: Xpert#get('answers')", function () {
+
+});
+
+test( "Xpert#get('results')", function () {
expect( 5 );
- deepEqual( expert1.getQuestions(), ["testing 123"] );
- deepEqual( expert2.getQuestions(), ["testing 123","moar test?"] );
- deepEqual( expert3.getQuestions(), ["testing 123","can has foo?","sure?","moar test?"] );
- deepEqual( expert4.getQuestions(), ["whitespace test!!1"] );
- deepEqual( expert5.getQuestions(), ["x?","test..?","0.1+0.2?","bla?","blaa?","blaaaa?","who?","PhD?","5x2?","q?"] );
+ deepEqual( expert1.get('results'), ["foo","bar"] );
+ deepEqual( expert2.get('results'), ["foo","boo","hoo"] );
+ deepEqual( expert3.get('results'), ["fine, foo!","boo","hoo","goo"] );
+ deepEqual( expert4.get('results'), ["^_^"] );
+ deepEqual( expert5.get('results'), ["aye","correct!","durp?","yay","bla.","foo","c'mon children don't be shy","sounds good to me","fine","DR.","bar","nah","rawr","2","123","123"] );
});
-test( "Xpert#getResults", function () {
+test( "TODO: Xpert#each(callback)", function () {
+
+});
+
+test( "Xpert#each('questions', callback)", function () {
+ expect( 5 );
+
+ var questions1 = [],
+ questions2 = [],
+ questions3 = [],
+ questions4 = [],
+ questions5 = [];
+
+ expert1.each('questions', function (question) {
+ questions1.push(question);
+ });
+ expert2.each('questions', function (question) {
+ questions2.push(question);
+ });
+ expert3.each('questions', function (question) {
+ questions3.push(question);
+ });
+ expert4.each('questions', function (question) {
+ questions4.push(question);
+ });
+ expert5.each('questions', function (question) {
+ questions5.push(question);
+ });
+
+ deepEqual( questions1, expert1.get('questions') );
+ deepEqual( questions2, expert2.get('questions') );
+ deepEqual( questions3, expert3.get('questions') );
+ deepEqual( questions4, expert4.get('questions') );
+ deepEqual( questions5, expert5.get('questions') );
+});
+
+test( "Xpert#each('answers', callback)", function () {
expect( 5 );
- deepEqual( expert1.getResults(), ["foo","bar"] );
- deepEqual( expert2.getResults(), ["foo","boo","hoo"] );
- deepEqual( expert3.getResults(), ["fine, foo!","boo","hoo","goo"] );
- deepEqual( expert4.getResults(), ["^_^"] );
- deepEqual( expert5.getResults(), ["aye","correct!","durp?","yay","bla.","foo","c'mon children don't be shy","sounds good to me","fine","DR.","bar","nah","rawr","2","123","123"] );
+ var answers1 = [],
+ answers2 = [],
+ answers3 = [],
+ answers4 = [],
+ answers5 = [];
+
+ expert1.each('answers', function (answer) {
+ answers1.push(answer);
+ });
+ expert2.each('answers', function (answer) {
+ answers2.push(answer);
+ });
+ expert3.each('answers', function (answer) {
+ answers3.push(answer);
+ });
+ expert4.each('answers', function (answer) {
+ answers4.push(answer);
+ });
+ expert5.each('answers', function (answer) {
+ answers5.push(answer);
+ });
+
+ deepEqual( answers1, expert1.get('answers') );
+ deepEqual( answers2, expert2.get('answers') );
+ deepEqual( answers3, expert3.get('answers') );
+ deepEqual( answers4, expert4.get('answers') );
+ deepEqual( answers5, expert5.get('answers') );
});
-test( "Xpert.parseTree", function () {
+test( "Xpert#each('results', callback)", function () {
+ expect( 5 );
+
+ var results1 = [],
+ results2 = [],
+ results3 = [],
+ results4 = [],
+ results5 = [];
+
+ expert1.each('results', function (result) {
+ results1.push(result);
+ });
+ expert2.each('results', function (result) {
+ results2.push(result);
+ });
+ expert3.each('results', function (result) {
+ results3.push(result);
+ });
+ expert4.each('results', function (result) {
+ results4.push(result);
+ });
+ expert5.each('results', function (result) {
+ results5.push(result);
+ });
+
+ deepEqual( results1, expert1.get('results') );
+ deepEqual( results2, expert2.get('results') );
+ deepEqual( results3, expert3.get('results') );
+ deepEqual( results4, expert4.get('results') );
+ deepEqual( results5, expert5.get('results') );
+});
+
+test( "Xpert.parseTree(tree)", function () {
expect( 5 );
var expected1 = ["testing 123",[["yes", "foo"],["no", "bar"]]],
@@ -105,40 +225,49 @@ test( "Xpert.parseTree", function () {
deepEqual( tree5parsed, expected5, "14 levels of nesting" );
});
-test( "Xpert.mapTree", function () {
+test( "Xpert.map(tree)", function () {
expect( 3 );
- deepEqual( Xpert.mapTree(expert1.tree, function (response) {
+ deepEqual( Xpert.map(expert1.tree, function (response) {
return response + "x";
}), ["testing 123x",[["yesx","foox"],["nox","barx"]]]);
- deepEqual( Xpert.mapTree(tree1, function (response) {
+ deepEqual( Xpert.map(tree1, function (response) {
return response + "x";
}), ["testing 123x",[["yesx","foox"],["nox","barx"]]], "works for non-parsed trees" );
deepEqual( expert1.tree, ["testing 123",[["yes","foo"],["no","bar"]]], "tree not modified" );
});
-test( "Xpert.getQuestions", function () {
+test( "Xpert.get('questions', tree)", function () {
expect( 6 );
- deepEqual( Xpert.getQuestions(expert1.tree), ["testing 123"] );
- deepEqual( Xpert.getQuestions(tree1), ["testing 123"], "works for non-parsed trees" );
- deepEqual( Xpert.getQuestions(expert2.tree), ["testing 123","moar test?"] );
- deepEqual( Xpert.getQuestions(expert3.tree), ["testing 123","can has foo?","sure?","moar test?"] );
- deepEqual( Xpert.getQuestions(expert4.tree), ["whitespace test!!1"] );
- deepEqual( Xpert.getQuestions(expert5.tree), ["x?","test..?","0.1+0.2?","bla?","blaa?","blaaaa?","who?","PhD?","5x2?","q?"] );
+ deepEqual( Xpert.get('questions', expert1.tree), ["testing 123"] );
+ deepEqual( Xpert.get('questions', tree1), ["testing 123"], "works for non-parsed trees" );
+ deepEqual( Xpert.get('questions', expert2.tree), ["testing 123","moar test?"] );
+ deepEqual( Xpert.get('questions', expert3.tree), ["testing 123","can has foo?","sure?","moar test?"] );
+ deepEqual( Xpert.get('questions', expert4.tree), ["whitespace test!!1"] );
+ deepEqual( Xpert.get('questions', expert5.tree), ["x?","test..?","0.1+0.2?","bla?","blaa?","blaaaa?","who?","PhD?","5x2?","q?"] );
});
-test( "Xpert.getResults", function () {
+test( "TODO: Xpert.get('answers', tree)", function () {
+
+});
+
+test( "Xpert.get('results', tree)", function () {
expect( 6 );
- deepEqual( Xpert.getResults(expert1.tree), ["foo","bar"] );
- deepEqual( Xpert.getResults(tree1), ["foo","bar"], "works for non-parsed trees" );
- deepEqual( Xpert.getResults(expert2.tree), ["foo","boo","hoo"] );
- deepEqual( Xpert.getResults(expert3.tree), ["fine, foo!","boo","hoo","goo"] );
- deepEqual( Xpert.getResults(expert4.tree), ["^_^"] );
- deepEqual( Xpert.getResults(expert5.tree), ["aye","correct!","durp?","yay","bla.","foo","c'mon children don't be shy","sounds good to me","fine","DR.","bar","nah","rawr","2","123","123"] );
+ deepEqual( Xpert.get('results', expert1.tree), ["foo","bar"] );
+ deepEqual( Xpert.get('results', tree1), ["foo","bar"], "works for non-parsed trees" );
+ deepEqual( Xpert.get('results', expert2.tree), ["foo","boo","hoo"] );
+ deepEqual( Xpert.get('results', expert3.tree), ["fine, foo!","boo","hoo","goo"] );
+ deepEqual( Xpert.get('results', expert4.tree), ["^_^"] );
+ deepEqual( Xpert.get('results', expert5.tree), ["aye","correct!","durp?","yay","bla.","foo","c'mon children don't be shy","sounds good to me","fine","DR.","bar","nah","rawr","2","123","123"] );
});
-}() );
+
+
+
+
+
+}() );
View
319 xpert.js
@@ -1,56 +1,56 @@
var Xpert = (function () {
- "use strict";
+
+ 'use strict';
+
+
+ var Xpert,
+
+ getFunctions,
+ mapFunctions;
+
+
+
+ //////////////////////////////////////////////////////////////
+ // Helpers
+ //////////////////////////////////////////////////////////////
// length of a string with regex removed
function count(regex, str) {
- return str.length - str.replace(regex, "").length;
+ return str.length - str.replace(regex, '').length;
}
// get the indentation level
function getIndentation(str) {
return count(/^\s*/, str);
}
+ // strim trimmer
function trim(str) {
-
- }
- // the constructor - takes a raw tree and the two callbacks
- var Xpert = function (tree, displayQuestion, displayResult) {
+ if (String.prototype.trim) {
+ return str.trim();
+ }
- this.displayQuestion = displayQuestion;
- this.displayResult = displayResult;
+ return str.replace(/^\s+/).replace(/\s+$/);
- // parse the tree if it isn't already
- if (typeof tree === "string") {
- tree = parseTree(tree);
- }
+ }
- // display the first question
- this.next(tree);
- };
- Xpert.prototype.next = function (next) {
- // use for storing the current state of the tree
- this.tree = next;
- if (typeof next === "string") {
- this.displayResult(next);
- } else {
- this.displayQuestion(next[0], next[1]);
- }
+ //////////////////////////////////////////////////////////////
+ // Parser
+ //////////////////////////////////////////////////////////////
- };
function parseTree(tree) {
// assume initial indent is 0, this
// strips out all whitespace before
// the first question
- tree = tree.trim().split("\n"); // todo: whitespace newlines, shim
+ tree = trim(tree).split('\n');
// where the real parsing happens
// the actual process probably needs more explaining
@@ -97,131 +97,90 @@ var Xpert = (function () {
tree = parse(tree);
// clean tabs from the start of each question
- return mapAll(tree, function (response) {
- return response.trim(); //fill
+ tree = mapAll(tree, function (response) {
+ return trim(response);
});
- }
-
- Xpert.parseTree = parseTree;
-
-
-
-
- function getQuestions(tree) {
+ return tree;
- if (typeof tree === "string") {
- tree = Xpert.parseTree(tree);
- }
-
- // returns array of each possible question in a tree
- var get = function (tree) {
-
- // tree always starts with a question
- var questions = [tree[0]];
-
- _.forEach(tree[1], function (curr) {
- var currNext = curr[1];
+ }
- // more questions - result not found
- if (typeof currNext !== "string") {
- questions = questions.concat(getQuestions(currNext));
- }
- });
- return questions;
+ // parse a tree if it isn't already
+ function fix(tree) {
+ if (typeof tree === 'string') {
+ tree = parseTree(tree);
}
- return get(tree);
+ return tree;
}
- function getAnswers(tree) {
- }
- function getResults(tree) {
+ //////////////////////////////////////////////////////////////
+ // Xpert
+ //////////////////////////////////////////////////////////////
- var results = [];
-
- // was the final answer - result found
- if (typeof tree === "string") {
- return [tree];
- }
- // more questions
- tree[1].forEach(function (curr) {
- var currNext = curr[1];
- results = results.concat(getResults(currNext)); //todo:concat spprt?
- });
+ // the constructor - takes a raw tree and the two callbacks
+ Xpert = function (tree, displayQuestion, displayResult) {
- return results;
+ this.displayQuestion = displayQuestion;
+ this.displayResult = displayResult;
- }
+ tree = fix(tree);
- Xpert.get = function (type, tree) {
- var funcs = {
- 'questions' : getQuestions,
- 'answers' : getAnswers,
- 'results' : getResults
- };
+ // display the first question
+ this.next(tree);
- return funcs[type](tree);
};
- Xpert.prototype.get = function (type) {
- return Xpert.get(type, this.tree);
- };
-
-
+ Xpert.prototype.next = function (next) {
+ // current state of the tree
+ this.tree = next;
- Xpert.each = function (types, tree, callback) {
- if (arguments.length === 2) {
- func = tree;
- tree = types;
- mapAll(tree, callback);
+ if (typeof next === 'string') {
+ this.displayResult(next);
+ } else {
+ this.displayQuestion(next[0], next[1]);
}
- var funcs = {
- 'questions' : eachQuestion,
- 'answers' : eachAnswer,
- 'results' : eachResult
- };
-
- _.forEach(types, function (type) {
- funcs[type](tree, callback);
- });
-
};
- Xpert.prototype.each = function (types, callback) {
- Xpert.each(types, this.tree, callback);
- };
+
+ Xpert.parseTree = parseTree;
+ //////////////////////////////////////////////////////////////
+ // .map
+ //////////////////////////////////////////////////////////////
+
+ mapFunctions = {
+ 'questions': mapQuestions,
+ 'answers': mapAnswers,
+ 'results': mapResults
+ };
// applies a function to each response (question/possible answers/eventual result)
// used internally for cleaning the indentation white-space
function mapAll(tree, callback) {
- var map = function map(tree, callback) {
-
- if (typeof tree === "string") {
- tree = Xpert.parseTree(tree);
- }
+ tree = fix(tree);
+ function map(tree, callback) {
return _.map(tree, function (curr) {
// sets of question/answer/result are stored
// in arrays - apply the function to the
// actual string of each
- if (typeof curr === "string") {
+ if (typeof curr === 'string') {
// function called with the response as
// the argument and returns changes to it
return callback(curr);
@@ -230,49 +189,153 @@ var Xpert = (function () {
}
});
-
- };
-
- if (typeof tree === "string") {
- tree = parseTree(tree);
}
return map(tree, callback);
}
+ function mapQuestions(tree, callback) {
+
+ }
+
+ function mapAnswers(tree, callback) {
+
+ }
- // grand-daddy of maps
- Xpert.map = function (types, tree, callback) {
+ function mapResults(tree, callback) {
+
+ }
+
+
+ Xpert.map = function (type, tree, callback) {
if (arguments.length === 2) {
callback = tree;
- tree = types;
+ tree = type;
- mapAll(tree, callback);
+ return mapAll(tree, callback);
}
+ };
- var funcs = {
- 'questions' : mapQuestions,
- 'answers' : mapAnswers,
- 'results' : mapResults
- };
-
- _.forEach(types, function (type) {
- funcs[type](tree, callback);
- });
+ Xpert.prototype.map = function (type, callback) {
+ if (arguments.length === 1) {
+ callback = type;
+ return mapAll(this.tree, callback);
+ }
};
- Xpert.prototype.map = function (types, callback) {
- if (arguments.length === 1) {
- return Xpert.map(this.tree, callback);
+
+
+
+
+
+ //////////////////////////////////////////////////////////////
+ // .get
+ //////////////////////////////////////////////////////////////
+
+ // getAll?
+
+ function getQuestions(tree) {
+
+ tree = fix(tree);
+
+ // returns array of each possible question in a tree
+ function get(tree) {
+
+ // tree always starts with a question
+ var questions = [tree[0]];
+
+ _.forEach(tree[1], function (curr) {
+ var currNext = curr[1];
+
+ // more questions - result not found
+ if (typeof currNext !== 'string') {
+ questions = questions.concat(get(currNext));
+ }
+ });
+
+ return questions;
+
+ }
+
+ return get(tree);
+
+ }
+
+ function getAnswers(tree) {
+
+ tree = fix(tree);
+
+ // get answers
+
+ return tree;
+
+ }
+
+ function getResults(tree) {
+
+ tree = fix(tree);
+
+ function get(tree) {
+
+ var results = [];
+
+ // was the final answer - result found
+ if (typeof tree === 'string') {
+ return [tree];
+ }
+
+ // more questions
+ _.forEach(tree[1], function (curr) {
+ var currNext = curr[1];
+ results = results.concat(get(currNext));
+ });
+
+ return results;
+
}
- return Xpert.map(types, this.tree, callback);
+ return get(tree);
+
+ }
+ getFunctions = {
+ 'questions': getQuestions,
+ 'answers': getAnswers,
+ 'results': getResults
};
+ Xpert.get = function (type, tree) {
+ return getFunctions[type](tree);
+ };
+
+ Xpert.prototype.get = function (type) {
+ return getFunctions[type](this.tree);
+ };
+
+
+ //////////////////////////////////////////////////////////////
+ // .each
+ //////////////////////////////////////////////////////////////
+
+ // eachAll?
+
+ function each(type, tree, callback) {
+ _.forEach(getFunctions[type](tree), function (curr) {
+ callback(curr);
+ });
+ }
+
+
+ Xpert.each = each;
+
+ Xpert.prototype.each = function (type, callback) {
+ each(type, this.tree, callback);
+ };
+
+
return Xpert;
}());

0 comments on commit 80644b2

Please sign in to comment.