Archive for the 'neuromancer' Category

Neuromancer 0.6.5 Released

Neuromancer 0.6.5 has been released, and is available for download.  Along with the changes I've mentioned in previous posts, there are a few goodies from Rob in there.

We're in the process of finalizing some API changes that will comprise the next release.  As the project has evolved, the initial design has put forth a few limitations that we're going to address to make future growth easier, and to make the API a more consistent.

On a sad note, Rob's nightly build script doesn't run on the new Apple JVM for some unknown reasons, so nightly builds are out of commission for the moment.  Hopefully they'll be back up and running soon.  As always, you can get the bleeding edge from Subversion.

Neuromancer Update

Rob did a bunch of work with Neuromancer last night, including appying a nice template to the site, setting up a wiki (available from the site), and committing a bunch of changes that he'd made since the 0.6.0beta was released in December, but which hadn't made it into Subversion yet.  Finally, he added automated nightly builds to the site, so you can get the bleeding-edge source without having to hit the Subversion repository, and I won't have to blog patches anymore.

Neuromancer Again

While working on my CFUG preso for this month, I found another bug: dates weren't handled properly coming back from web service calls.  So I added support for them.  Patch file for js/io/RemoteObject.js below, or you can get the update and supporting test scripts from Subversion.

Index: RemoteObject.js
===================================================================
— RemoteObject.js (revision 10)
+++ RemoteObject.js (revision 11)
@@ -26,6 +26,7 @@
var DATATYPE_BOOLEAN = "soapenc:boolean";
var DATATYPE_NUMBER = "soapenc:double";
var DATATYPE_NUMBER2 = "xsd:double";
+var DATATYPE_DATE_TIME = "xsd:dateTime";

/**
* Variable: REMOTE_OBJECT_VERSION
@@ -85,6 +86,10 @@
{
value = parseFloat(dItem.item(z).firstChild.nodeValue);
}
+ else if (xsiType == DATATYPE_DATE_TIME)
+ {
+ value = DefaultHandler.xmlDate2JSDate(dItem.item(z).firstChild.nodeValue);
+ }
else if(xsiType == DATATYPE_MAP)
{
value = new Map();
@@ -95,7 +100,7 @@
}
}
}
- nstruct.put(key,value);
+ nstruct.put(key,value);
}
}
}
@@ -716,6 +721,10 @@
{
eval(__dfh__variable + " = parseFloat(resvalnodes.item(0).firstChild.nodeValue)");
}
+ else if (returntype == DATATYPE_DATE_TIME)
+ {
+ eval(__dfh__variable + " = DefaultHandler.xmlDate2JSDate(resvalnodes.item(0).firstChild.nodeValue)");
+ }
//}
//this is a structure (a coldfusion struct)
//else if(complextypeid != null && complextypeid.length > 1)
@@ -765,6 +774,8 @@
//show the value to stdout
if(__dfh__variable == "__neuro__myvar__")
{
+ // TODO: this throws not defined errors. Probably should be if(typeof neuro_SystemOut == "function") instead
+ // Hopefully, however, no one will ever get here, because they'll always be using a callback.
if(neuro_SystemOut != null)
{
neuro_SystemOut("\n");
@@ -774,4 +785,29 @@
neuro_Runner("");
}
}
+};
+
+DefaultHandler.xmlDate2JSDate = function __xmlDate2JSDate(xmlDate) {
+ var val = xmlDate.split("T");
+ // split it into date and time portions
+ var date = val[0];
+ var time = val[1];
+ date = date.split("-");
+ time = time.split(":");
+ // rip out the date portions
+ var year = date[0];
+ var month = date[1] - 1; // JS uses 0-11, not 1-12
+ var day = date[2];
+ // rip out the time portions
+ var hours = time[0];
+ var minutes = time[1];
+ var seconds = parseFloat(time[2]);
+ // convert fractional seconds to milliseconds
+ var millis = Math.round((seconds - Math.floor(seconds) ) * 1000);
+ seconds = Math.floor(seconds);
+ // assemble the completed date
+ var completeDate = new Date(year, month, day, hours, minutes, seconds, millis);
+ // adjust the time from UTC (Zulu) to local time
+ // TODO: change the to check for the appropriate adjustment, rather than blindly assuming UTC
+ return new Date(completeDate.getTime() - (completeDate.getTimezoneOffset() * 60 * 1000));
};

Neuromancer [Re]Addition

A while back I need to do a standard form POST via Neuromancer, so I'd added a doFormPostRequest method to the JSRemote object.  I just needed it again, and for whatever reason, it hadn't made it's way into the core distribution.  So I merged my modded sources in (I love version control ; )), and thought I'd share.

As before, the updates are in the Subversion repo, or you may use the patch below.  Note that this time it's for js/io/Gateway.js, not RemoteObject.js.

Index: Gateway.js
===================================================================
— Gateway.js (revision 8)
+++ Gateway.js (revision 9)
@@ -161,6 +161,38 @@
};

/**
+ * Method: JSRemote.doFormPostRequest
+ * This method implements a multi-field form submission via a POST,
+ * using the 'fields' object as a set of name:value pairs to pass as
+ * the form fields. It simply delegates to doPostRequest for the
+ * actual processing; the only functionality is serializing the fields.
+ *
+ * Note that this method's parameter ordering does NOT correspond to
+ * doPostRequest's.
+ *
+ * Parameters:
+ * url - the url to POST to
+ * fields - the form fields to POST
+ * handler - the callback function to send results to
+ */
+JSRemote.prototype.doFormPostRequest = function _doFormPostRequest(url, fields, handler) {
+ var body = "";
+ var headers = new Object();
+ var boundary = "neuro" + Math.random();
+
+ for (var i in fields) {
+ body += "–" + boundary + "\nContent-Disposition: form-data;name=\"" + i + "\"\n";
+ body += "\n";
+ body += fields[i] + "\n";
+ body += "\n";
+ }
+ body += "–" + boundary + "–";
+
+ headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
+ this.doPostRequest(url, handler, body, headers);
+}
+
+/**
* Method: JSRemote.doPostRequest
* Does a simple post request, passing the bodyinfo as the body of the
* request - meaning the only way to get the bodyinfo out is to do
@@ -171,7 +203,7 @@
* func_handler - the callback function to send the results to
* bodyinfo - what to send in the body of the POST
*/
-JSRemote.prototype.doPostRequest = function _doPostRequest(url, func_handler, bodyinfo)
+JSRemote.prototype.doPostRequest = function _doPostRequest(url, func_handler, bodyinfo, extraHeaders)
{
log.info("doPostRequest to " + url);
log.info("using pipe: " + this.connectionid);
@@ -211,6 +243,11 @@
conn.setRequestHeader("XLibrary", "Neuromancer 1.5beta");
if(bodyinfo.indexOf("

Another Neuromancer Bug Fixed

Chris Philips found another Bug in Neuromancer today.  Computed numbers, returned as members of a struct, were always deserialized as null.  Literal numbers, however, were handled correctly.  While debugging the problem, I realized the methods with returntype="numeric" simply refused to run as well, for much the same reason.  Both glitches are now fixed.

As before, the zip on SourceForge has not been updated, though the changes are available in the Subversion repository.  I've included a patch below for js/io/RemoteObject.js that you can use to get the fix as well.  The patch should work with or without last night's update, but I'd recommend applying them both in order.  If you want the updated test cases, you'll have to hit the SVN repository.

Index: RemoteObject.js
===================================================================
— RemoteObject.js (revision 6)
+++ RemoteObject.js (revision 7)
@@ -24,6 +24,8 @@
var DATATYPE_STRING2 = "xsd:string";
var DATATYPE_ARRAY = "soapenc:Array";
var DATATYPE_BOOLEAN = "soapenc:boolean";
+var DATATYPE_NUMBER = "soapenc:double";
+var DATATYPE_NUMBER2 = "xsd:double";

/**
* Variable: REMOTE_OBJECT_VERSION
@@ -74,11 +76,16 @@
//if(dItem.item(z).getAttribute("href")== null
// || typeof dItem.item(z).getAttribute("href") == "undefined"
// || dItem.item(z).getAttribute("href") == "")
- if(dItem.item(z).getAttribute("xsi:type") == DATATYPE_STRING)
+ var xsiType = dItem.item(z).getAttribute("xsi:type")
+ if(xsiType == DATATYPE_STRING || xsiType == DATATYPE_STRING2)
{
value = dItem.item(z).firstChild.nodeValue;
}
- else if(dItem.item(z).getAttribute("xsi:type") == DATATYPE_MAP)
+ else if (xsiType == DATATYPE_NUMBER || xsiType == DATATYPE_NUMBER2)
+ {
+ value = parseFloat(dItem.item(z).firstChild.nodeValue);
+ }
+ else if(xsiType == DATATYPE_MAP)
{
value = new Map();
//value = "!ref! " + dItem.item(z).getAttribute("href");
@@ -705,6 +712,10 @@
eval(__dfh__variable + " = resvalnodes.item(0).firstChild.nodeValue");
}
}
+ else if (returntype == DATATYPE_NUMBER || returntype == DATATYPE_NUMBER2)
+ {
+ eval(__dfh__variable + " = parseFloat(resvalnodes.item(0).firstChild.nodeValue)");
+ }
//}
//this is a structure (a coldfusion struct)
//else if(complextypeid != null && complextypeid.length > 1)

Neuromancer Bug Fixed

 Chris Phillips found a bug in the Neuromancer 0.6.0beta today.  If you used the RemoteObjectLoader class (recommended), instead of the raw RemoteObjectFactory (way nasty), it was impossible to create multiple remote objects on a single page, because the RemoteObjectLoader class was not thread safe.  The first remote object created was always returned by every RemoteObjectLoader, ragerdless of what remote object it was supposed to load.

Unfortunately, Rob and I are still getting the project all set up on the SourceForge system, so a new build with the fix included hasn't been released.  As such, the bug fix is only available via Subversion or via the patch file below.  Subversion instructions can be found on this page, and as they suggest, you will want to append "/trunk" to the URL listed in the command.

Since that's not very user friendly, I've included a patch for js/io/RemoteObject.js that will update an existing 0.6.0beta version with the bug fix.  Note that you want to patch just that one file, not the whole directory tree.

Index: RemoteObject.js
===================================================================
— RemoteObject.js (revision 3)
+++ RemoteObject.js (revision 4)
@@ -136,8 +136,10 @@
if(typeof async == "undefined")
async = false;
- RemoteObjectLoader.RemoteObjectFactory.setAsync(async);
- RemoteObjectLoader.RemoteObjectFactory.createObject(RemoteObjectLoader.HTTPConnectFactory.getInstance(), url);
+ this.remoteObjectFactory = new RemoteObjectFactory();
+
+ this.remoteObjectFactory.setAsync(async);
+ this.remoteObjectFactory.createObject(RemoteObjectLoader.HTTPConnectFactory.getInstance(), url);
var self = this;
@@ -143,7 +145,7 @@
checkLoaded = function()
{
- var remObj = RemoteObjectLoader.RemoteObjectFactory.getObject();
+ var remObj = self.remoteObjectFactory.getObject();
var fieldCount = 0;
for(var i in remObj)
{
@@ -160,7 +162,6 @@
this.loadInterval = setInterval(checkLoaded, 100);
}
RemoteObjectLoader.HTTPConnectFactory = new HTTPConnectFactory();
-RemoteObjectLoader.RemoteObjectFactory = new RemoteObjectFactory();
/////////////////////////////////////////////////////////////////////////////////////

Any decent IDE (cough, Eclipse, cough) will have a patch function, or you can use the command line patch utility.