Crosscheck wheel spin

At the last meeting with Copenhagen.rb, I said I’d test out Crosscheck – a JavaScript testing framework, suited for continuous integration or command-line testing.

I had just tried getting it to run, following the slightly outdated Add Event tutorial. At last I got it to work, but I had to actually read the text, not just paste the examples. Better grab the example source code than read the slightly-outdated docs.

Tonight I have tested it in a little more real setting: I had a hunch of how I would be able to do a filtering function that worked like in Quicksilver or Textmate. A case-insensitive, very inclusive search that just narrows down based on the sequence of the characters, not on their immediate order. (Did that make sense?)

Expressed as a positive test case: “jy8″ should match “Jyri is happy to be 8 years of age”.

Anyway, I thought I could construct a regex on the fly, somehow. I itched to try. And I wanted to try it using tests along the way.

So, I entered the first, simplest case I could think of: equality. A list of one element should return only a single-element array. That was simple to do, so I could take on the next (that I could think of). And the rest was just implementation, until I hit something I had to test in Firebug.

The cool thing was that my experimentation was over as soon as I’d answered the question I had. Focus, the programmer’s holy grail. The promise of focus, at least. We’ll see about how that holds up in practice.

Here is the quite redundant, stupid list of test cases that I accumulated while trying things on:


crosscheck.onSetup(function(){
    crosscheck.load("scripts/quicksilverSearch.js");
});
crosscheck.addTest({
    testShouldFindSameMatchingString : function () {
        var outPut = quicksilverMatch('johnny', ['johnny']);
        var exp = ['johnny'];
        assertEquals( exp[0], outPut[0],
            'The same word could not be found in the simple array. Shame.' );
    },
    testShouldNotFindANonMatchingString : function () {
        var outPut = quicksilverMatch('jx', ['johnny']);
        assertTrue( outPut.length == 0, 'The word jx was found in johnny?! Shame.' );
    },
    testShouldMatchAllOnFirstLetter : function() {
        var outPut = quicksilverMatch('jo', ['johnny', 'jools', 'june']);
        assertTrue( outPut.length == 2,
            'Unable to match with indexOf on the first two letters. Shame.' );
    },
    testShouldMatchAcrossWords : function() {
        var outPut = quicksilverMatch('jy', ['jyri', 'johnny', 'june']);
        assertTrue( outPut.length == 2,
            'Unable to match across words. Shame.' );
    },
    testShouldMatchAcrossWordsCaseInsensitively : function() {
        var outPut = quicksilverMatch('jy8', ['Jyri 8 years', 'Johnny turns 8', 'june']);
        assertTrue( outPut.length == 2,
            'Unable to match case-ins. across words. Shame.' );
    }
});

So, the first thing that gets done before execution of each test case: load my script. That is specified in crosscheck.onSetup.

The least interesting part of this is my resulting code. After pulling it through JSLint and mulling over it a couple more times, it just shrunk. Which is the end goal of testing, I guess: to have me own as little code as possible. (We’ll come back to that.)


/**
 * "Quicksilver" filter search: returns an array of the haystack elements that
 * match case-insensitively the given term.
 * 2007-10-05, Olle
 */
function quicksilverMatch(term, haystack) {
    var matches = [];
    var straw; // The individual piece of hay
    // "t.*e.*r.*m" regular expression for loose matching
    var re = new RegExp(term.split("").join('.*'), "i");
    for (var idx = 0, len = haystack.length; idx < len; ++idx) {
        straw = haystack[idx];
        // Direct match inside word? Or: a case-ins. loose match
        if (straw.indexOf(term) > -1 || re.test(straw)) {
            matches.push(straw);
        }
    }
    return matches;
}

The take-home piece I got from this sitting was how to run Crosscheck. I created a wrapper shell script that I christened “crosschecka”, where I set my target browsers (“hosts”) and paths:


#!/bin/bash
#
# Run the crosscheck jar on given test path, or default
#
# We run on Firefox 1.5 and IE6: moz-1.8:ie-6
#
# Author: olle, 2007-09-26

CROSSCHECK_JAR=/Users/olle/lib/crosscheck-0.2.1/crosscheck.jar
# Colon-separated. Possible values are moz-1.7:moz-1.8:ie-6
HOSTS_TO_CHECK=moz-1.8:ie-6    

# Our test path is tests/eosweb/js, relative to the web app root
if [[ $1 == '' ]]; then
{
    java -jar $CROSSCHECK_JAR -hosts=$HOSTS_TO_CHECK tests/eosweb/js
    exit 0;
}
else
{
    java -jar $CROSSCHECK_JAR -hosts=$HOSTS_TO_CHECK $1
    exit 0;
}
fi

(I never got the file test in the else clause right, so I let Crosscheck complain for me.) The Rails-type win here is having a pre-set default, so I never have to think about it.

Thought: Maybe this whole testing thing is a way to get me to reduce the scope of my methods? To be testable, they have to be decomposed to parts that are cheap to test.

I said I’d come back to owning less code: now that I know these things about the behaviour of my code, do you think I should revisit and sharpen up my test-cases? For one thing, some of them might not even hit right. The first one, it seems very weak and situationally dependent.

Would you go back and change such stuff? Or delete such cases?

Copenhagen.rb: September meetup, minor review and or notes

Last night, a Ruby club meet, which was interesting, but the feeling I got was “you hadda have been at RailsConf”. And I wasn’t, so bummer.

Informal atmosphere allows one to play with one’s laptop while others are reminiscing:

ActiveWarehouse a data warehousing application. Could be very interesting. Funny: me and Isak had said “Let’s analyze our SVN logs, that a nice chunky dataset.” He showed his first steps, grabbing the XML output Subversion can report about its logs. They were rich enough to map to… a data warehouse.

And, as it ironically turns out, the seminal example for ActiveWarehouse is activewarehouse-example-with-rails-svn-logs. Going to play with this, when I get some time.

When I was not listening, I was struggling to get offline browser tests running with Crosscheck, which packages up IE6 and Firefox (1.5 and the older 1.0):

It’s just a JAR, so you run it on a folder with tests:

java -jar ~/Desktop/crosscheck-0.2.1.tar/crosscheck.jar \\
-hosts=moz-1.8:ie-6 \\
tests/eosweb/js/

I want to hook this into our Bitten installation.

Super-secret note, only for you: I also made a note about the Oresund Web Hacking club, which only exists on Facebook (as a local group), so befriend me there, or search for the above name. Something more public will get created.

Patch day for Rails, in Copenhagen

The inimitable Jakob tells all about the upcoming Rails patch night, so I won’t say more than “it is a great way of getting into Rails” (Rails, the Open Source project; and Rails, the codebase).

(I won’t be there, I have a family thing to travel to, but please fill my seat, you.)

Hm, I shoulda added this to Upcoming.org, too.

[tags]copenhagenrb,rails[/tags]

Copenhagen.rb meetup: Puppet and fringe activity

Juri presented the amazing Puppet framework for controlling servers. He controls about 20 servers with it.

Think of Puppet’s files as “setup Rakefiles for servers” which is version-controlled, monitored, and written in a very terse DSL. Seems slick. Juri might publish his nice slides, who knows.

While he was presenting, me and Lars SG were typing to each other, like kids in the back of the classroom. Quicksilver’s Large Type action was used a lot. It started when I noted he was working on the Counter example in Seaside, using a new Squeak. A fellow tourist in fringe-language-land!

We took quick detours into Io and CLisp (I had read about mapcar, and wanted to show what it was), and Lars turned out to be able to stomach even the most arcane crap. Perfect: a new special interest group in our Copenhagen Ruby Brigade. James Britt reports that this is a common Ruby programmer’s thing, and that it manifests itself in their meetups. See his link to fringedc for a picture of how to understand the “fringe” label.

[tags]ruby,copenhagenrb,fringe[/tags]

Copenhagen.rb meeting notes

Update: photos by Pelle.

Syndication with AtomPub, APP and GData… and Rails

Olle Jonsson “explores” some things you would want to do with Atom… and how Rails enables you to do it.

Update: My somewhat confused slides now available (PDF).

DHH on ActiveResource

…and then I got lucky, and David was able to give us “how to do all this painlessly in Rails.”

David HH presented the new ActiveResource stuff. Edge, plugins. “Undecided”, still, about if it’ll be in the standard distribution.

Suffix the URL with .xml (or any format your app responds_to!)

GET /posts/1.xml
POST /posts/1.xml {POST payload}

Clarifiction: The last line there, the POST payload, is not a Ruby block, it is what you send as the body of the HTTP request.

We saw the MIME-type format list that Rails’ ActiveResource can output. I missed the JSON format, mentioned it, and David showed how it would be simple to add JSON support to ActiveResource.

Let us cook that up! And David showed:

  • Register a new JSON mime-type
  • respond_to {} the :json, and spit that out
  • make .to_json()

There must be a plugin for this. And there is, we were informed.

Don’t Trust Yourself with Your Users’ Data

Pelle B. tells it like it is, and provides a plugin called EzCrypto. Short, simple presentation, very good. And also, notes on a couple of new developments from the productive programmer: secret URLs as a plugin.

JRuby at a glance

Teaser-like presentation (‘Part I’) by Morten Ch. from Aarhus.

[tags]ruby,rubyonrails,copenhagenrb,event[/tags]