Monday, June 26, 2017

React: Dynamically Rendering Different Components without Switch: the Capitalized Reference Technique

React in the form we usually see doesn't really look like it can make components dynamically. Most people end up using switch with case blocks to "choose" the type of component that will be rendered when there are multiple possibilities, which I'd say is an anti-pattern.

For example:

We don't want to have to have that switch statement ... as the number of components we might output grows, it's going to start getting really ugly and hard to maintain. The page JSX in Depth in the React documentation holds the key to understanding how to create components dynamically instead, in the section marked "Choosing the Type at Runtime". That page doesn't come up high in searches, though, and it doesn't do a very good job of explaining the technique.

I like to call this technique the Capitalized reference technique.

Here's an explanation of how to use the technique:

  1. Import the components we might use
  2. Add references to the components to an object literal
  3. Create a reference to the dynamic component type we want by:
    1. Creating a new variable (reference) with a first letter that is Capitalized
    2. Using the component type as the key, get the corresponding value from the object literal
    3. Assign the value from the literal, a reference to the component, to the Capitalized reference
  4. Include the dynamic component using the Capitalized reference from step 3.1 in our JSX.

The fundamental 'magic' here is that when JSX sees the Capitalized reference, it dereferences back to whatever component the reference is pointed at. Yay references!

In the simplest form:

Requiring us to import any component we might want to render and add it to an object literal isn't very maintainable. We want to keep the list of possible components to render outside of this dynamic component renderer. To do that, we'll send a components dictionary in as part of props. After we externalize the set of possible components to render, we'll achieve good maintainability.

We probably aren't rendering just one component this way, but rendering a wrapper around any number of dynamically-defined children. To work on a collection of dynamic component instances, we need to:

  1. Iterate over the collection in JSX
  2. For each item:
    1. Reassign the value we get from Components[ component.type ] to the Capitalized reference we've created
    2. Use that Capitalized reference in our JSX in order to render the correct component type

The method for iterating over a collection in JSX is also covered on the page JSX in Depth, this time in a section titled "javascript expressions as children".

Now that we can easily render components based on a configuration, we've achieved a basic kind of polymorphism -- the type of a component can be changed by changing a value in props. Doing it based off of a collection proves that we can use any kind of technique that walks an object tree to render a hierarchy in order to achieve composition. The arrangement and functioning of an application can be driven based on the collection.

A complete sample project and demo can be found here: react-dynamic-component-demo.

Wednesday, December 4, 2013

Class Warfare: OrientDB + Tinkerpop / Blueprints

Just a quick note to others starting out with OrientDB and Blueprints ... if you receive an error "Exception during remote processsing" the cause is conflicting versions of OrientDB, Blueprints, etc.

For myself, I kept OrientDB at 1.6.1, and bumped all my Tinkerpop stack jars up to 2.5.0-SNAPSHOT.

Here's the relevant section of the maven pom.xml:


<repositories>
 ...
 <repository>
 <id>sonatype-oss-snapshots</id>
 <url>https://oss.sonatype.org/content/repositories/snapshots</url>
 <releases><enabled>false</enabled></releases>
 <snapshots><enabled>true</enabled></snapshots>
 </repository>
</repositories>
<dependencies>
 ...
 <dependency>
  <groupId>com.tinkerpop.blueprints</groupId>
  <artifactId>blueprints-core</artifactId>
  <version>2.5.0-SNAPSHOT</version>
 </dependency>
 <dependency>
  <groupId>com.tinkerpop</groupId>
  <artifactId>frames</artifactId>
  <version>2.5.0-SNAPSHOT</version>
 </dependency>
 <dependency>
  <groupId>com.tinkerpop.furnace</groupId>
  <artifactId>furnace</artifactId>
  <version>0.1.0-SNAPSHOT</version>
 </dependency>
 <dependency>
  <groupId>com.tinkerpop.gremlin</groupId>
  <artifactId>gremlin-java</artifactId>
  <version>2.5.0-SNAPSHOT</version>
 </dependency>
 <dependency>
  <groupId>com.tinkerpop.gremlin</groupId>
  <artifactId>gremlin-groovy</artifactId>
  <version>2.5.0-SNAPSHOT</version>
 </dependency>
 <dependency>
  <groupId>com.orientechnologies</groupId>
  <artifactId>orientdb-client</artifactId>
  <version>1.6.1</version>
 </dependency>
 <dependency>
  <groupId>com.orientechnologies</groupId>
  <artifactId>orient-commons</artifactId>
  <version>1.6.1</version>
 </dependency>
 <dependency>
  <groupId>com.orientechnologies</groupId>
  <artifactId>orientdb-core</artifactId>
  <version>1.6.1</version>
 </dependency>
 <dependency>
  <groupId>com.orientechnologies</groupId>
  <artifactId>orientdb-enterprise</artifactId>
  <version>1.6.1</version>
 </dependency>
 <dependency>
  <groupId>com.tinkerpop.blueprints</groupId>
  <artifactId>blueprints-orient-graph</artifactId>
  <version>2.5.0-SNAPSHOT</version>
 </dependency>
</dependencies>

Thursday, June 20, 2013

jQuery's extend() Method and Prototypes - A Deadly Combination

I recently have been working with some code where an object hierarchy was useful to me, and I implemented it using this sort of pattern:

function ObjectParent () {
}
ObjectParent.prototype = {
    constructor: ObjectParent,
    method: function () { ... },
    property: true,
    objectProperty: {
        subMethod: function () { ... },
        subProperty: true
    }
};
function ObjectChild () {
}
ObjectChild.prototype = new ObjectParent();
    $.extend(true, ObjectChild.prototype, {
        childMethod: function () { ... },
        childProperty: false
    });
function ObjectGrandChild () {
}
ObjectGrandChild.prototype = new ObjectGrandChild();
    $.extend(true, ObjectGrandChild.prototype, {
        grandChildMethod: function () { ... },
        childProperty: 'Now this is just getting weird.',
        objectProperty: {
            grandchildSubProperty: true
        }
    });
And it worked ... kind of.   Mostly?  Sort of?
I was seeing stuff from the grand child in the children and in the parent.  Why?  Well, first I took off the deep copy flag $.extend(true, ... ) becomes $.extend(...) and that made everything work.... sort of.

I had those sub-objects that I wanted to be extending and they weren't making it through properly.  So I made an explicit shallow copy for them, which looks like this:

$.extend(ObjectGrandChild.prototype.objectProperty, { grandchildsubProperty: true });

The question becomes ... where is ObjectGrandChild.prototype.objectProperty located?  If that property is actually coming from some previous level of prototypal inheritance, when we extend it in this way, we extend the carrying prototype and tend to pollute our prototype chain.

What we're actually doing here is saying:  take the item referenced by X (ObjectGrandChild.prototype.objectProperty -> ObjectParent.prototype.objectProperty) and update it with values from Y

I like the pattern I wrote, but the consequences of using correct prototypal inheritance are not always obvious.  Remember that object properties coming from the prototype are references and so changes pass through.  There is no way to overlay string / primitive data type properties in the fashion I was attempting.  You will always have to make a 'clone' of the prototype in order to extend it.

Which is easy enough:

ObjectGrandChild.prototype.objectProperty = $.extend({},
    ObjectGrandChild.prototype.objectProperty,
    { grandchildsubProperty: true });

Note that what we're doing is this:  redefine the reference A (ObjectGrandChild.prototype.objectProperty) to point to the new object B ({}) which has been created by taking all the direct properties of X (ObjectGrandChild.prototype.objectProperty -> ObjectParent.prototype.objectProperty) and adding / replacing them with the direct properties of Y.

Monday, October 22, 2012

Compass / SASS for Placing Elements on a Circle

Need to put some elements on a circle?  Already using Compass / SASS to compile your CSS?

Woot!  Compass has you covered with trigonometric sine and cosine functions and of course the SASSy @for iterator helps, too.

This jsfiddle demonstrates the compiled result.

Your HTML document is composed of a container for the circle, an element for the circle, a container for items placed on the circle, and the items which will appear on the circle.

Your SASS file is composed of the following.  Modify the $positions, $ringSize, and $itemSize variables according to your needs:

// Use either Compass' border-radius mix-in or prefixfree plugin
@import "compass/css3/border-radius";

/**
 * on-circle takes a radius, position on a circle, number of possible positions and returns top and left properties
 *
 * $radius {Number}    radius of the circle in pixels
 * $ordinalPosition {Number} the position of the item on the circle, counting from 1 (North) through N
 * $positions {Number}   the number of positions on the circle
 * $originX {Number}   optional X origin point for the circle, defaults to the radius
 * $originY {Number}   optional Y origin point for the circle, defaults to the radius
 * $offsetX {Number}   optional X offset for the item, you might use 1/2 of the width of an item, default is 0
 * $offsetY {Number}   optional Y offset for the item, you might use 1/2 of the height of an item, default is 0
 */
@mixin on-circle ($radius, $ordinalPosition, $positions, $originX: $radius, $originY: $radius, $offsetX: 0, $offsetY: 0) {

 /*
 Determine the angle for the position:
  Multiply the adjusted zero-based index of the position by the degrees-per-position (360 degrees divided by the 
  number of positions) and subtract 90 degrees (adjusting to begin at North)
  */
 $positionAngleDegrees: ($ordinalPosition - 1)*360/$positions - 90;
 /*
 Convert the angle to radians:
  Multiply the angle by pi and then divide by 180 degrees.

 NOTE: This step is necessary because of a bug in handling of the degrees unit when doing iterations, AFAICT
  */
 $positionAngleRadians: $positionAngleDegrees * pi() / 180;

 /*
 Apply the parametric equation of the circle,
  http://en.wikipedia.org/wiki/Circle#Equations
 via:
  http://stackoverflow.com/questions/839899/how-do-i-calculate-a-point-on-a-circles-circumference

  x = [origin x] + (r * cos angle)
  y = [origin y] + (r * sin angle)
  */
 top: #{$originY + $offsetY + $radius * sin($positionAngleRadians)}px;
 left: #{$originX + $offsetX + $radius * cos($positionAngleRadians)}px;

}

// How many positions will there be on the circle?
$positions: 13;

// How big is the circle (diameter)?
$ringSize: 180;

// How big is an item?  Offsets and margins are based on this value
// such that the center of an item is located on the circle, rather than
// the top left point
$itemSize: 28;

body {
 margin: 50px;
}

.ring-container {
 border: solid 1px black;
 display: inline-block;
}

.ring {
 position: relative;
 // Use border-box so that margin and border are not included and the size of the element containing the
 // rendered circle is the circle size + border + ...
 box-sizing: border-box;
 width: #{$ringSize}px;
 height: #{$ringSize}px;
 margin: #{$itemSize/2}px;
 border: solid 1px black;

 // Make any square into a circle by setting the border radius to it's full width/height
 @include border-radius(#{$ringSize}px);
}

.ring-positions {
 position: relative;
 width: #{$ringSize}px;
 height: #{$ringSize}px;
 top: #{$itemSize/-2}px;
 left: #{$itemSize/-2}px;
}

.ring-position {
 position: absolute;
 width: #{$itemSize}px;
 height: #{$itemSize}px;
 @include border-radius(#{$itemSize}px);

 // Use box-sizing: border-box if you'll have a border
 box-sizing: border-box;
 border-color: black;
 border-style: solid;
}

@for $i from 1 through $positions {
 .ring-position-#{$i} {
  @include on-circle($radius: $ringSize / 2, $ordinalPosition: $i, $positions: $positions);

  // Just for demonstration of placement
  border-width: #{$i/2+3}px;
 }
}

Thursday, August 16, 2012

html5 boilerplate build Woes

On Friday I spent a few hours trying to debug doing a build on my current project using the html5 boilerplate build script (ant based).  Didn't happen.

Spent a couple more hours on Monday modifying my project to mirror the configuration for asset directories that the build script uses as its defaults, such as "js" for the scripts directory, where before I was using "javascripts" based on the html5 boilerplate template that I installed with (I forget which) Scout or compass.app.  Thought it would build after changing to the structure it expects / expected.  Didn't happen.

Well, the woes seem to be caused by changes to the default structure that have happened at the same time as the splitting of the build script out into it's own project / repository.

Today I put a little bit more time into the build and got it working by removing my exclusions from the project.properties file and adding the slug.libs property in.

So that's the deal ... if you're going to use something other than "vendors" as your libs directory, you *must* specify not only the dir.js.libs property but the slug.libs property.

Here are the settings I have in my project properties, which seem to match the html5 boilerplate template:

dir.source = ./public
file.stylesheets  = 
dir.js = js
dir.js.main = ${dir.js}
dir.js.libs = ${dir.js}/libs
slug.libs = libs
dir.js.modules = ${dir.js}/modules
dir.css = css
dir.images = img
file.root.stylesheet = style.css
file.root.script = script.js

Good luck with your html5 boilerplate project!

UPDATE:

I also had issues with the error "js/modules/_all.js was specified as an input resource."
I could get past the error by doing an "ant clean" before the "ant build", but obviously the error means there's a problem.

To get past it, modify your build.xml around line 486 to fix the module concatenation which is used to create a checksum, as follows, adding an exclusion for _all.js.

<concat destfile="./${dir.intermediate}/${dir.js.modules}/_all.js" overwrite="no">
    <fileset dir="./${dir.intermediate}/${dir.js.modules}/">
        <include name="*.js">
        <exclude name="_all.js">
    </exclude></include></fileset>
</concat>


UPDATE the Second:

Subdirectories of /libs are not being copied.  :/  This might help ... testing it now...

<copy todir="${dir.publish}/${dir.js}">
  <fileset
        dir="${dir.intermediate}/${dir.js}"
        includes="${file.js.bypass}, ${slug.libs}/*/**, ${slug.modules}/*/**">
        <exclude name="scripts-concat.js"/>
        <exclude name="scripts-concat.min.js"/>
        <exclude name="otherscripts-concat.js"/>
        <exclude name="plugins.js"/>
        <exclude name="${file.root.script}"/>
    </fileset>
    <regexpmapper from="^([^/])*/(.*)$$" to="\1/\2" handledirsep="true"/>
</copy>

Looks good. That's a change for the task around line 532 of build.xml.

Wednesday, August 15, 2012

Making a Compass Site Relative

I'm creating what at least for now is a single-page webapp and using, among other things, Compass.

One of the extensions I'm using (via Compass.app) is thomas-mcdonald-bootstrap-sass.  I've modified it to use the Font-Awesome icon font which replaces and extends the standard twitter icon library, as well as to add an IcoMoon icon font for even more icons.

To add the icon fonts to the compass bootstrap extension, you need to specify the path to the font.  In the instructions, you're told to put the absolute path to the font (such as "/fonts/fontawesome".  But we don't want to do that, we want to have all relative paths in our files.  If we don't include a beginning "/" in the $fontAwesomePath variable, then Compass will assume that the path is relative to the compass project's font directory (because of how the font-file function is coded).

The default font directory is the css directory plus "/fonts".

That made things awkward for using relative paths, so I did some digging and found a post where someone mentions using "font_dir" to configure the font directory path ... well, it's "fonts_dir", folks, not "font_dir".

To make a long story short, using the image-url function for my image paths, and trying to remove the absolute pathing by changing "http_path" to "" didn't work.

These are the settings in my config.rb that *did* work:

http_path = "./"
css_dir = "css"
sass_dir = "sass"
images_dir = "../img"
javascripts_dir = "js"
fonts_dir = "../fonts"

Of course you should modify that to use the same paths that you're using, such as "javascripts" and "stylesheets" instead of "js" and "css".  Just make sure that you have "./" as the http_path and "../" in front of your images and fonts directory paths.  When those are used, it's in the CSS context and your generated CSS file is going to end up in a directory that's parallel to them.

If somehow your final CSS file is in a different place, make sure that the images and fonts variables are changed accordingly.

Monday, July 23, 2012

Pandora, Stardock Objectdock, Large Number of Chrome Processes

I've been using Stardock's Objectdock for a while, because I'm now coding on a Windows 7 machine.  I also have a subscription to Pandora One ... at some point I saw about a bajillion chrome processes coming up in the dock and making it useless.

Turns out that it's because of Pandora One.  Chrome is my default browser and it seems like Pandora is using it and not closing the handle or something.  Who knows.  Problem solved by not using their standalone Adobe AIR application and going back to having one window open for listening.

UPDATE:  Apparently you have to start Pandora One BEFORE you start Chrome and the problem will not occur.  (Thanks to Pandora tech support for providing the workaround)

Thursday, November 24, 2011

Simultaneous Background Process Monitoring in Bash using Multitail

I'm working on an installer for the development server environment we use at www.salsalabs.com

There are time-consuming tasks which are part of the installation process which can be done simultaneously: installation of program dependencies (with brew) and download of code repositories (using git).

I backgrounded the first task in my installation script using the usual & and let the script continue on and complete the second task "normally". After the second task completed, a wait command makes sure that the script does not proceed until the first task is also complete.

That's all well and good. It works just dandy... except that the output of these two processes is intermixed and due to each process providing feedback about the process of individual steps ... that output is very very very very very messy.

So I looked for a solution and I found MultiTail. Awesome! And available via brew! Awesome!

Except ... I don't have log files, and I don't want to generate log files. I want to have two different output streams and use multitail to view them simultaneously in one terminal window.

Two different output streams is easy enough, we've got file descriptors for that, right? (check out the exercise here, it makes my brain hurt). No, we need two different files that are going to act like streams. Oh! We want buffers!

(Cue the superhero music)

Here come FIFOs to the rescue! Use the handy-dandy mkfifo command and you can turn any output stream like stdout, stderr into a buffer that looks like a file to the operating system. Yay!!!!

So, our code, something like this:


install_dependencies.sh &
install_packages.sh
wait

install_something_else.sh


becomes:


mkdir /tmp/fifos
mkfifo "/tmp/fifos/Dependencies"
mkfifo "/tmp/fifos/Code Installation"

install_dependencies.sh > "/tmp/fifos/Dependencies" &
install_packages.sh > "/tmp/fifos/Code Installation"
wait

install_something_else.sh


And we add on multitail:


mkdir /tmp/fifos
mkfifo "/tmp/fifos/Dependencies"
mkfifo "/tmp/fifos/Code Installation"

install_dependencies.sh > "/tmp/fifos/Dependencies" &
install_packages.sh > "/tmp/fifos/Code Installation" &
multitail -ts --basename "/tmp/fifos/Dependencies" "/tmp/fifos/Code Installation"
wait

install_something_else.sh


The problem, now? multitail is never going to exit on it's own, we'll have to press "q" to end the process when the other stuff is done ... assuming we can tell for sure. So we never get to the wait command and we never get on to the next steps of our installation. Let's send an email to the developer and ask him for that feature ... but we'll make our own solution in the meantime.

So, here presented for you is my solution for monitoring a group of parallel background processes using multitail and ending the monitoring when all processes are complete.


PROCESS_COMPLETED_COUNT_TMP_FILE=$(mktemp -t "proc-count")
echo 0 > $PROCESS_COMPLETED_COUNT_TMP_FILE

function killtail {
    MUST_COMPLETE=$1
    PROCESS_COMPLETED=$(cat $PROCESS_COMPLETED_COUNT_TMPO_FILE)
    ((PROCESS_COMPLETED++))

    if [ PROCESS_COMPLETED -ge $MUST_COMPLETE ]; then
        # dangerous if there are multiple multitails running at the same time
        TAIL_PID=$(pidof multitail)
        kill $TAIL_PID
        rm -rf $PROCESS_COMPLETED_COUNT_TMP_FILE
    fi
}

mkdir /tmp/fifos
mkfifo "/tmp/fifos/Dependencies"
mkfifo "/tmp/fifos/Code Installation"

# subshell for everything involved with installing dependencies
(
    install_dependencies.sh > "/tmp/fifos/Dependencies"
    killtail 2
)> "/tmp/fifos/Dependencies" &

# subshell for everything involved with installing code
(
    install_packages.sh > "/tmp/fifos/Code Installation" &
    killtail 2
)> "/tmp/fifos/Code Installation" &

multitail -ts --basename "/tmp/fifos/Dependencies" "/tmp/fifos/Code Installation" 2> /dev/null
wait

rm "/tmp/fifos/Dependencies"
rm "/tmp/fifos/Code Installation"

install_something_else.sh


Now the subshells are backgrounded instead of the scripts themselves. Both "steps" are backgrounded, not just the first one. The command to increment the kill counter and check for the appropriate number of processes to have completed is added to the block of code. As well as backgrounding the subshells, we're directing stdout output from the subshells into our FIFOs, instead of directing the output of individual commands.

We pass the --basename option to multitail so that we see only the name of our FIFO in the multitail windows... and we've used nice human readable names for these temporary buffer files, so isn't that special?

When the kill counter hits the magic number, a SIGTERM will be sent to the multitail that we get back from pidof multitail. This makes multitail error, so we're throwing away the error output from multitail.

Finally, we still have a "wait" statement, just in case our multitail monitoring of the processes doesn't work for some reason. We still want to make sure they complete before moving forward with more steps. We remove the FIFOs and move on to the next steps in our installation.

Happy process monitoring!

Monday, December 21, 2009

Question: How do you convert a Java String object to a Javascript String in Rhino?

Convert any Java object with a toString() method into a JS string by calling String(object) in JS


var javaString = new java.lang.String("Java String");
var javascriptString = String(javaString);

Wednesday, December 16, 2009

Javascript for the Mad Scientist: advanced javascript for jQuery

I had a great time last night presenting a talk on advanced js for jQuery to the Frederick Web Tech. Meetup.

Christopher Thatcher of env.js and jQuery-Claypool fame and I hung out after the meeting and had drinks and lots of great conversation. Because of family and holidays and such-like many of the regulars couldn't come or couldn't stay for after-meeting socializing.

You can view or download/fork the presentation "slides" if you are interested in looking at the material.

Saturday, December 12, 2009

jQuery Namespacing / Child Plugins Update

I've updated the example namespacing plugin to use new arguments.callee() instead of extending the method and prototype individually.  This should allow for proper inheritance.

It does require that the method also wrap the plugin behavior in

if (this.jquery) { ... }

Tuesday, December 8, 2009

jQuery Namespacing / Child Plugins / Modularization

When writing jQuery plugins, you'll find that you want to group related functionality into a single "namespace" or "module", that acts as a parent for multiple child plugins.

There is not a lot of information readily accessible about the subject, from what I can tell.  So little in fact that only after I came up with a quick and dirty method for myself did I dive deep enough to find anything.

Friday, December 4, 2009

Splitting Directives - Modular "Molecule" Configurations of Apache httpd

At my 9 to 5, I am currently wearing a sys admin hat (this is something between a welder's mask and sherlock holmes' tweed cap, I think). I love process automation (the foundation of programming, I'd say) so everything is done with a bash script for maximum reuse.

My vision is to be able to rebuild a new version of my httpd configurations and deploy them to the target servers in one command (the testing process happens BEFORE the final build and deploy, silly!)

Why? One of the problems that I've seen in past projects is lack of configuration management and revision control for infrastructure applications like httpd or websphere. Worse, different tiers in the enterprise (boldly going, anyone?) and different servers might have different configurations because of lack of strictness in implementing changes methodically.

Sunday, November 29, 2009

Google Chrome Frame

Since my Web Worker shim relies on Google Gears plugin and the Google Chrome Frame plugin may make Gears obsolete (at least for this use case), I have temporarily suspended work on my html5 shims while I wait for that issue to shake out.

I may push harder for a non-Gears, native solution based on Statified JS. There is also a possibility to offer server-side worker implementations, which dovetails nicely with my recent research about the state of server-side JS.

People seem to be very excited about Node, but I prefer env.js and the Claypool JS framework from what I can see.

I think that server-side DOM emulation is the "BIG DEAL™" and that server-side JS should build on an existing server stack (which was one of the nicest points in Jaxer's favor when it was in active development).

Is anyone working on making any of the other server-side JS servers apache httpd modules? I haven't seen any indication that they are interested in this kind of integrated approach.

Friday, September 18, 2009

First Test Suite Complete

The first version of the test suite is completed now and everything is passing, testing 6 levels of child worker nesting, and some core functionalities such as importScripts, self alias for the worker global scope, the lack of "window" and hiding the worker global scope constructors.

The results of the test suite are this:

worker received: worker
worker: JSUNITY: Running unnamed test suite
worker: JSUNITY: 4 tests found
worker: JSUNITY: [PASSED] testImport
worker: JSUNITY: [PASSED] testWindow
worker: JSUNITY: [PASSED] testSelf
worker: JSUNITY: [PASSED] testWGSVisibility
worker: JSUNITY: 4 tests passed
worker: JSUNITY: 0 tests failed
worker: JSUNITY: 2 milliseconds elapsed
[object Object]
worker: JSUNITY: Running unnamed test suite
worker: JSUNITY: 1 test found
worker: JSUNITY: [PASSED] testInnerWorkerExists
worker: JSUNITY: 1 test passed
worker: JSUNITY: 0 tests failed
worker: JSUNITY: 0 milliseconds elapsed
[object Object]
worker-child queue: worker-child
worker received: another message from parent
worker received: worker-child received: worker-child
worker received: worker-child: JSUNITY: Running unnamed test suite
worker received: worker-child: JSUNITY: 4 tests found
worker received: worker-child: JSUNITY: [PASSED] testImport
worker received: worker-child: JSUNITY: [PASSED] testWindow
worker received: worker-child: JSUNITY: [PASSED] testSelf
worker received: worker-child: JSUNITY: [PASSED] testWGSVisibility
worker received: worker-child: JSUNITY: 4 tests passed
worker received: worker-child: JSUNITY: 0 tests failed
worker received: worker-child: JSUNITY: 3 milliseconds elapsed
worker received: [object Object]
worker received: worker-child: JSUNITY: Running unnamed test suite
worker received: worker-child: JSUNITY: 1 test found
worker received: worker-child: JSUNITY: [PASSED] testInnerWorkerExists
worker received: worker-child: JSUNITY: 1 test passed
worker received: worker-child: JSUNITY: 0 tests failed
worker received: worker-child: JSUNITY: 0 milliseconds elapsed
...

I need to add tests for the other portions of the API that are completed as well as the portions which are not. Once I do that, I will start working on using a MessagePort implementation for the communication between the worker and worker global scope. This should allow me to plug in communication based on local storage or web database, which is necessary for implementing SharedWorker.

The work I have been doing on the postMessage and onmessage events and how they queue when the objects are not ready should make it simpler to implement the MessagePort / MessageChannel structure.

Right now the most glaring problem is that there is significant duplication of the onmessage code between DedicatedWorker and WorkerGlobalScope. I was allowing this because the code in WorkerGlobalScope uses a closure to remember the scope inside the workerPool onmessage method and the DedicatedWorker didn't need it, but there shouldn't be any reason not to just store the scope reference for DedicatedWorker, too.

I need to experiment with where I can store that function for both objects to use, however. I am finding that the structure of the current objects and how they are passed around is proving quite limiting in terms of allowing for shared functions. Solving this will be even more important as I design a larger framework that includes other parts of the HTML5 and related APIs, available inside and outside of the worker scope.

I will try to create an object called html5shims and attach all of the shim API implementations to the global scope from within the closure surrounding html5shims. The current implementation of Worker uses a style of function assignment which is not compatible, so I will have to check again which browser was requiring that and see if I can find a different way.

Here's that style if you're curious:

Worker = (function InitWorker(window,navigator,wgsSource) {
                  function DedicatedWorker (url) {
...
                  }
              return DedicatedWorker;
          })(this,...);

Thursday, September 17, 2009

with (foo) { function bar() {} } fubared

The last two days I've been working on a test suite for my webworker shim and found a couple of wrinkles.

The first was that messages were being lost when they were sent immediately after the worker creation.

For example:

var w = new Worker("foo.js");
w.onmessage = function (event) {
    alert(event.data);
};
w.postMessage("foo");

The message "foo" would never reach the worker. This was solved simply enough by queueing messages when the communication channel is not ready. Which brought me back to looking at the spec for MessagePort and MessageChannel, but that's not important right now.

The really wrinkly thing that I'm seeing now is based on this test:

function testImportScripts () {
   importScripts("../scripts/import.js"); // declares function importedFunction
   assertNotUndefined(importedFunction,"imported function is defined");
   ...
}

First I was getting reference errors telling me that importScripts was undefined. I was able to get around that by surrounding the call to importScripts in a with (this) {} block. That worried me. And importedFunction was still undefined.

So I write a simple worker script:

onmessage = function (event) {
 postMessage("received: " + event.data);
};

importScripts("../scripts/import.js");
importedFunction();

No importedFunction. However, with importedFunction assigned instead of declared -- viola! function declarations inside of with blocks are not supported according to ECMA-262 3rd edition. It makes sense, but it puts a bit of a crimp in my plans. (I found this post which confirmed my suspicion, I didn't actually dig through the spec to find out!

The current version of the WorkerGlobalScope uses this code inside of it's _executed method for all internal executions:

Function("with (this) { " + source + " }").call(this);

I strongly prefer to maintain the use of with so for now I am modifying the code passed to the worker to make function declarations function assigments. The latest version of the code and the tests so far are checked in to SVN.

Sunday, September 13, 2009

Web Worker API Shim Demo Posted

I finally got the Web Worker API shim demo posted on Google Code tonight.

There is an interesting trick to posting the demo HTML page where you need to set the svn:mime-type property to "text/html" or it won't be served as HTML. Makes sense but confused me for a while and made me sidetrack looking at cleaning up my old portfolio site.

Not that that wouldn't be a great idea, too.

Web Worker Demo

To see the shim, visit that URL with a non-supporting browser that has Google Gears installed. You may also be interested in comparing it against browser implementing web workers natively such as FF 3.5.

The shim is implemented via two classes. Probably the simplest way to understand how they work is from the inside out. Implementing the Worker object and the worker-to-worker-thread messaging mechanism on top of Gears is pretty simple. But the API doesn't just cover instantiating a worker, giving it some work to do and talking to it. The API defines a window-like environment that the worker thread operates in, where other APIs are available:

WorkerGlobalScope

Once the WorkerGlobalScope is created, the Worker itself is a dispatcher, sending and receiving messages. So what does the WorkerGlobalScope look like and how do we make one?

The HTML5 Web Workers API spec covers the WorkerGlobalScope in detail. WorkerGlobalScope.js is my implementation. In it, we have constructor functions for each of the WorkerGlobalScopes (Dedicated, Shared and the "base") which handle setting instance variables both private and public. We also have DedicatedWorkerGlobalScope onmessage prototype and a prototype object which handles all of the methods available in any WGS, including empty Worker and SharedWorker constructor methods.

But the WorkerGlobalScope established by the constructor and prototype is incomplete -- not only because I haven't finished everything! It is missing the very crucial piece of providing a working Worker implementation.

Of course, we can't have a WorkerGlobalScope without having already created a Worker. The implementation of Worker is provided not directly by WorkerGlobalScope but by the calling Worker. The DedicatedWorker.js file bootstraps the Worker into the originating window environment with the InitWorker method that returns the DedicatedWorker constructor.

Simplified:

Worker = (function InitWorker(window) {
function DedicatedWorker(url) {
...
}
return DedicatedWorker;
})(this,navigator,WorkerGlobalScopeSource);


The InitiWorker method provides closure around the bootstrap parameters. "this", the global scope for workers. "navigator", the navigator object passed through from the global scope. The source which can be used to create the WGS. Because it is named, it can be easily passed as a string via function decompilation into the Gears worker pool thread. It is my understanding that because it is defined as part of an assignment operation, it does not pollute the namespace (I think there are some finer points there, but I don't know them as well as I would like).

When we create the code to execute our worker's payload in the workerPool, which must instantiate the worker global scope and make all of the APIs available to the worker, we basically repeat the exact same construction. There it is text and not source code, but it is still the same thing. In this way, we share the source code for Workers and WorkerGlobalScope with all child instances even though we cannot directly pass the objects.

The code which is executed by the Gears workerPool thread:


this._source = [
wgsSource,
"var wgs = new DedicatedWorkerGlobalScope(\""+url+"\");",
"wgs.navigator = " + toSource.call(navigator)+";",
"wgs.navigator.online=true;",
"Worker=wgs.Worker=("+InitWorker+")(wgs,"+ toSource.call(navigator) +",'"+ escapeQuotes(wgsSource)+"');",
"wgs._loadSource({url: ['" + url + "'], "+
"callback: function(scripts){ wgs._scripts = scripts; wgs._execute('importScripts(\""+url+"\")'); } });"
].join("\n");


Matters are complicated by the fact that the HttpRequest object (analog to XHR) available in Gears does not allow for synchronous operations but that is a story for another day.

As the credits on the demo say, thanks to Andrea Giammarchi for pointing me at the need for this shim.

Thursday, September 10, 2009

HTML5 Shims, Shivs, Fallbacks and Compatibility Layers

I am working on my implementation of HTML5 WebWorker shim via Google Gears plugin (currently). Part of what is interesting is that the WebWorker has a lot of the other HTML5 APIs available internally, so I am also looking to bring in other shims / write my own if I want the project to be complete and provide an actual fully-functioning Worker-alike and SharedWorker-alike.

As a result, I've decided to start a project to collect the available shims. Rather than just the Web Worker, I will try to bring together as much of the HTML5 js apis as possible. The project is called "html5-shims" and is hosted on Google Code.

I have added a page to the wiki for my worker shim/html5 shims project which lists other shims available, the relevant specs, etc.

HTML5 Shims : Links & Resources