Showing posts with label cyberdojo. Show all posts
Showing posts with label cyberdojo. Show all posts

nordevcon cyber-dojo presentation

It was a pleasure to speak at the recent norfolk developers conference. My talk was "cyber-dojo: executing your code for fun and not for profit". I spoke about cyber-dojo, demo'd its features, discussed its history, design, difficulties and underlying technology. Videos of the talk are now on the infoq website. The slide-sync is not right at the start of part 2 but it soon gets corrected.

cyber-dojo@KGISL Coimbatore

It was an honour and a pleasure to be invited to spend the day at KGISL Institute of Technology in Coimbatore, India. After a presentation on Software Professionalism: How to do deliberate practice (skip to slide 33 if you know how cyber-dojo runs) I helped run a cyber-dojo using a custom server KGISL had built supporting more than sixteen participants - using famous computer scientists as avatars instead of animals. Fantastic! Youngsters from the local community also participated, encouraged by Sudharsan who has been running a local CoderDojo. I was hugely impressed to see Ashok, a founder of KGISL Group roll his sleeves up, literally, and participate in the practice. A terrific example of leadership. Then lunch, and another cyber-dojo, and some more consulting. I look forward to returning. Thank you KGISL.





Atlantec conference

It was a pleasure and an honour to speak at the first Atlantec conference held in Galway, Ireland on May 15th. I talked about cyber-dojo and showed some statistics from a random sample of its 30,000+ cyber-dojos, together with a few examples of code/tests typically submitted, a few dashboard patterns, and wrapped up linking testing to Le Chatelier's Law and some of my favourite Systems Thinking quotes from Bradford Keeney.



some cyber-dojo measurements

cyber-dojo has hosted about 13,000 practice sessions so far. I've written a short ruby script to extract some measurements from a sample of 500 sessions. I was looking at transitions between red, amber, and green traffic-lights:
  • red means one or more tests failed
  • amber means the tests did not run (eg syntax error)
  • green means the tests ran and all passed
The first column is average number of lines added/deleted.
The second column is colour → colour transition.
The third column is sample size.

3.94 ambergreen 447
4.65 amberred 379
4.67 amberamber 1462
5.39 redgreen 607
6.01 redred 604
7.52 greenred 420
13.65 greenamber 436
17.67 redamber 432
22.18 greengreen 598

Here's how I interpret the results:
  • If you're at red or green and you make a small change (5.39,6.01,7.52) you're likely to stay at red or green.
  • If you're at red or green and you make a large change (13.65,17.67) you're likely to transition to amber.
  • There is a big spike in the number of amberamber transitions (1462). I speculate that long sequences of these transitions are occuring after a large 13.65 greenamber or 17.67 redamber transition.
  • I think the greengreen value of 22.18 is larger than it should be because it's including plain file renames.


dojo = way place

Tore Martin Hagen and some of his colleagues did their first team Cyber-Dojo recently. One team member, Manabu Mochida, is from Japan and he wrote a nice explanation of the term Dojo on the white board. Love it. Thanks Manubu.

cyber-dojo Raspberry Pies in action



Liam Friel, who helps to run a CoderDojoBray (in Ireland) asked me for some Raspberry Pies which I was more than happy to give him, paid for from the donations lots of you generous people have made from cyber-dojo.

Liam sent me this wonderful photo of a CoderDojoBray session and writes:

Your Pies have been getting a lot of use... We've got 8 Pies in total. Got a reasonably steady turnout at the dojo, 75-85 kids turning up each week.

Awesome. If, like Liam, you would like some Raspberry Pies to help kids learn about coding, please email. Thanks

Poker hands in Ruby

John Cleary (@TheRealBifter) is doing a nice project - The 12 TDD's of Xmas. Day 11 was Poker Hands. I did it in Ruby. Here's the code from traffic-light 116.

Tests first:
require './card'
require './hand'
require 'test/unit'

class TestUntitled < Test::Unit::TestCase

  def test_start
    hand = Hand.new("2H 4S 4C 2D 4H")
    assert_equal Card.new('2',:hearts),   hand[0]
    assert_equal Card.new('4',:spades),   hand[1]
    assert_equal Card.new('4',:clubs),    hand[2]
    assert_equal Card.new('2',:diamonds), hand[3]
    assert_equal Card.new('4',:hearts),   hand[4]
  end

  def test_card_has_pips_and_suit_set_on_creation
    card = Card.new('2',:hearts)
    assert_equal '2', card.pips
    assert_equal :hearts, card.suit
    card = Card.new('T',:hearts)
    assert_equal 'T', card.pips
    assert_equal :hearts, card.suit
    card = Card.new('J',:hearts)
    assert_equal 'J', card.pips
    assert_equal :hearts, card.suit
    card = Card.new('Q',:hearts)
    assert_equal 'Q', card.pips
    assert_equal :hearts, card.suit
    card = Card.new('K',:hearts)
    assert_equal 'K', card.pips
    assert_equal :hearts, card.suit
    card = Card.new('A',:hearts)
    assert_equal 'A', card.pips
    assert_equal :hearts, card.suit
  end

  def test_hand_ranked_three_of_a_kind
    assert_equal :three_of_a_kind, Hand.new("2H 4S 4C AD 4H").rank
  end

  def test_hand_ranked_one_pair
    assert_equal :one_pair, Hand.new("2H 4S 5C JD 4H").rank
  end

  def test_hand_ranked_two_pairs
    assert_equal :two_pairs, Hand.new("2H 4S 5C 2D 4H").rank
  end

  def test_hand_ranked_flush
    assert_equal :flush, Hand.new("2H 4H 6H 8H TH").rank
  end

  def test_hand_ranked_straight
    assert_equal :straight, Hand.new("2H 3C 4H 5H 6H").rank
  end

  def test_hand_ranked_full_house
    assert_equal :full_house, Hand.new("2H 4S 4C 2D 4H").rank
  end

  def test_hand_ranked_four_of_a_kind
    assert_equal :four_of_a_kind, Hand.new("2H 4S 4C 4D 4H").rank
  end

  def test_hand_ranked_straight_flush
    assert_equal :straight_flush, Hand.new("2H 4H 3H 5H 6H").rank
  end

  def test_hand_ranked_high_card
    assert_equal :high_card, Hand.new("2C 3H 4S 8C AH").rank
  end

  def test_full_house_beats_flush
    black = Hand.new("2H 4S 4C 2D 4H")    
    white = Hand.new("2S 8S AS QS 3S")
    assert_equal 1, black <=> white
  end

  def test_higher_card_wins_if_equal_rank
    black = Hand.new("2H 3D 5S 9C KD")
    assert_equal :high_card, black.rank
    white = Hand.new("2C 3H 4S 8C AH")
    assert_equal :high_card, white.rank
    assert_equal -1, black <=> white
  end

  def test_equal_hands
    black = Hand.new("2H 3D 5S 9C KD")
    assert_equal :high_card, black.rank
    white = Hand.new("2D 3H 5C 9S KH")
    assert_equal :high_card, white.rank
    assert_equal 0, black <=> white
  end

end
Code second:
class Hand

  def initialize(cards)
    @cards = 
      cards.gsub(/\s+/, "")
           .scan(/.{2}/)
           .map{|ch| Card.new(ch[0],suit(ch[1]))}
  end

  def [](n)
    return @cards[n]
  end

  def rank
    return :straight_flush if straight? && flush?
    return :flush          if flush?
    return :straight       if straight?

    pip_tallies = pip_counts.sort.reverse
    return {
      [4,1] => :four_of_a_kind,
      [3,2] => :full_house,
      [3,1] => :three_of_a_kind,
      [2,2] => :two_pairs,
      [2,1] => :one_pair,
      [1,1] => :high_card
    }[pip_tallies[0..1]]
  end

  def <=>(other)
    keys <=> other.keys
  end

  def keys
    [ranking,pip_counts]
  end

private

  def ranking
    ranks.index(rank)
  end

  def ranks
    [
      :high_card,
      :pair,
      :two_pairs,
      :three_of_a_kind,
      :straight,
      :flush, 
      :full_house,
      :four_of_a_kind,
      :straight_flush
    ]
  end

  def pip_counts
    "23456789TJQKA"
      .chars
      .collect {|pips| pip_count(pips)}
  end

  def pip_count(pips)
    @cards.count{|card| card.pips == pips}
  end

  def pip_flags
    pip_counts.map{|n| n > 0 ? 'T' : 'F'}.join
  end

  def straight?
    pip_flags.include? 'TTTTT'
  end

  def flush?
    suit_counts.any?{|n| n == 5}
  end

  def suit_counts
    suits.collect{|suit| suit_count(suit)}
  end

  def suits  
    [:clubs,:diamonds,:hearts,:spades]
  end

  def suit_count(suit)
    @cards.count{|card| card.suit == suit}
  end

  def suit(ch)
    return suits["CDHS".index(ch)]
  end

end
class Card

  def initialize(pips,suit)
    @pips,@suit = pips,suit
  end

  def ==(other)
    pips == other.pips && suit == other.suit
  end

  def pips
    @pips
  end

  def suit
    @suit
  end

end
You can replay my entire progression (warts and all) on Cyber-Dojo (naturally).

Phone Numbers in Ruby

John Cleary (@TheRealBifter) is doing a nice project - The 12 TDD's of Xmas. Day 10 was the Phone number prefix problem. I did it in Ruby. Here's the code from traffic-light 14.

Tests first (very minimal - I'm a bit pressed for time):
require './consistent'
require 'test/unit'

class TestUntitled < Test::Unit::TestCase

  def test_consistent_phone_list
    list = { 
      'Bob' => '91125426',
      'Alice' => '97625992',
    }
    assert consistent(list)
  end

  def test_inconsistent_phone_list
    list = { 
      'Bob' => '91125426',
      'Alice' => '97625992',
      'Emergency' => '911'
    }
    assert !consistent(list)
  end

end
Code second:
def consistent(list)
  list.values.sort.each_cons(2).none? { |pair| prefix(*pair) }
end

def prefix(lhs,rhs)
  rhs.start_with? lhs
end
The each_cons from the previous problem proved very handy here. As did none? I really feel I'm starting to get the hang of ruby. You can replay my entire progression (warts and all) on Cyber-Dojo (naturally).

Monty Hall in Ruby

John Cleary (@TheRealBifter) is doing a nice project - The 12 TDD's of Xmas. Day 4 was the Monty Hall problem. I did it in Ruby. Here's the code from traffic-light 111.

Tests first:
require './monty_hall'
require 'test/unit'

class TestMontyHall < Test::Unit::TestCase

  def test_either_goat_door_is_opened_when_you_choose_the_car_door
    check_either_goat([:car, :goat, :goat],
      { :chosen_door => 0, 
        :goat_doors  => [1, 2] 
      })
    check_either_goat([:goat, :car, :goat],
      { :chosen_door => 1, 
        :goat_doors  => [0, 2]
      })
    check_either_goat([:goat, :goat, :car],
      { :chosen_door => 2, 
        :goat_doors  => [0, 1]
      })
  end

  def test_other_goat_door_is_opened_when_you_choose_a_goat_door
    prizes = [:car, :goat, :goat]
    check_other_goat(prizes, 
      { :chosen_door  => 1, 
        :opened_door  => 2, 
        :offered_door => 0
      })
    check_other_goat(prizes, 
      { :chosen_door  => 2,  
        :opened_door  => 1, 
        :offered_door => 0 
      })

    prizes = [:goat, :car, :goat]
    check_other_goat(prizes, 
      { :chosen_door  => 0, 
        :opened_door  => 2, 
        :offered_door => 1
      })
    check_other_goat(prizes, 
      { :chosen_door  => 2, 
        :opened_door  => 0, 
        :offered_door => 1
      })

    prizes = [:goat, :goat, :car]
    check_other_goat(prizes, 
      { :chosen_door  => 0, 
        :opened_door  => 1, 
        :offered_door => 2
      })
    check_other_goat(prizes, 
      { :chosen_door  => 1,  
        :opened_door  => 0, 
        :offered_door => 2
      })
  end

  def test_strategy_of_sticking_with_chosen_door
    wins = Array.new(big) { MontyHall.new() }
                .count { |game| game.chosen_door == game.car_door }
    puts "Win car(sticking with chosen door):#{wins}/#{big}"
  end

  def test_strategy_of_switching_to_offered_door
    wins = Array.new(big) { MontyHall.new() }
                .count { |game| game.offered_door == game.car_door }
    puts "Win car(switching to offered door):#{wins}/#{big}"
  end

  #- - - - - - - - - - - - - - - - - - - - - - - -

  def check_either_goat(prizes, expected)
    chosen_door = expected[:chosen_door]
    goat_doors = expected[:goat_doors]
    check_params(prizes, chosen_door, goat_doors[0], goat_doors[1])
    
    goat_counts = [0,0]
    100.times do |n|
      game = MontyHall.new(prizes,chosen_door)

      opened_door = game.opened_door
      offered_door = game.offered_door

      assert_equal chosen_door, game.chosen_door
      assert_equal goat_doors.sort, [opened_door,offered_door].sort
      assert_equal doors, [chosen_door,opened_door,offered_door].sort
      assert_equal :car , prizes[chosen_door]
      assert_equal :goat, prizes[opened_door]
      assert_equal :goat, prizes[offered_door]     

      [0,1].each do |n|
        goat_counts[n] += (offered_door == goat_doors[n] ? 1 : 0)
      end
    end
    [0,1].each { |n| assert goat_counts[n] > 25 }
  end

  def check_other_goat(prizes, expected)
    chosen_door = expected[:chosen_door]
    opened_door = expected[:opened_door]
    offered_door = expected[:offered_door]

    check_params(prizes, chosen_door, opened_door, offered_door)

    game = MontyHall.new(prizes, chosen_door)

    assert_equal  chosen_door, game.chosen_door
    assert_equal  opened_door, game.opened_door
    assert_equal offered_door, game.offered_door

    assert_equal :goat, prizes[ chosen_door]
    assert_equal :goat, prizes[ opened_door]
    assert_equal :car , prizes[offered_door]
  end

  def check_params(prizes, door1, door2, door3)
    assert_equal 3, prizes.length
    prizes.each { |prize| assert [:goat,:car].include? prize }
    assert_equal doors, [door1,door2,door3].sort
  end

  def doors
    [0,1,2]
  end

  def big
    1000
  end

end
Code second:
class MontyHall

  def initialize(prizes = [:goat,:goat,:car].shuffle, 
                 chosen_door = doors.shuffle[0])
    @prizes = prizes
    @chosen_door = chosen_door
    @car_door = prizes.find_index { |prize| prize == :car }
    if prizes[chosen_door] == :car
      @opened_door = goat_doors.shuffle[0] 
    end
    if prizes[chosen_door] == :goat
      @opened_door = (goat_doors - [chosen_door])[0] 
    end
    @offered_door = (doors - [chosen_door, opened_door])[0]
  end

  def chosen_door
    @chosen_door
  end

  def car_door
    @car_door
  end

  def opened_door
    @opened_door
  end

  def offered_door
    @offered_door
  end

private 

  def doors
    [0,1,2]
  end

  def goat_doors
    doors.select { |door| @prizes[door] == :goat }
  end

end
You can replay my entire progression (warts and all) on Cyber-Dojo (naturally).

Bowling Game in Ruby

John Cleary (@TheRealBifter) is doing a nice project - The 12 TDD's of Xmas. Day 9 was the Bowling Game problem. I did it in Ruby. Here's the code from traffic-light 100.

Tests first:
require './score'
require 'test/unit'

class TestScore < Test::Unit::TestCase

  def test_score_uninteresting_game
    #           7     7     7     7    7 = 35
    #        6     6     6     6     6   = 30
    balls = "51|52|51|52|51|52|51|52|51|52"
    assert_equal 65, score(balls)
  end

  def test_score_all_frames_5_spare
    #           15    15    15    15    15 = 75
    #        15    15    15    15    15    = 75
    balls = "5/|5/|5/|5/|5/|5/|5/|5/|5/|5/|5"
    assert_equal 150, score(balls)
  end

  def test_perfect_score
    #          30  30  30  30  30 = 150
    #        30  30  30  30  30   = 150
    balls = "X|X|X|X|X|X|X|X|X|X|XX"
    assert_equal 300, score(balls)
  end

  def test_10_strikes_then_33
    #          30  30  30  30  16 = 136
    #        30  30  30  30  23   = 143
    balls = "X|X|X|X|X|X|X|X|X|X|33"
    assert_equal 279, score(balls)
  end

  def test_game_with_spare_in_middle_of_strikes
    #          20   30  30  30  30    = 140
    #        25  20   30  30  30      = 135
    balls = "X|X|5/|X|X|X|X|X|X|X|XX" 
    assert_equal 275, score(balls)
  end

  def test_game_with_strike_in_middle_of_spares
    #           15    20   15    13    20  = 83
    #        13    11    20   14    12     = 70
    balls = "2/|3/|5/|1/|X|3/|5/|4/|3/|2/|X"
    assert_equal 153, score(balls)
  end

  def test_game_with_zero_balls
    #           8     7     7     7     7 = 36
    #        6     5     6     6     6    = 29
    balls = "51|62|50|52|51|52|51|52|51|52"
    assert_equal 65, score(balls)
  end

  def test_game_with_dash_as_zero_balls
    #           8     7     7     7     8 = 37
    #        6     5     6     6     6    = 29
    balls = "51|62|5-|52|51|52|51|52|51|62"
    assert_equal 66, score(balls)
  end

end
Code second:
def score(balls)
  frames = (balls).split("|")
  while frames.length != 12
    frames << "0"
  end
  frames.each_cons(3).collect{ |frame| 
    frame_score(frame)
  }.inject(:+)
end

def frame_score(frames)
  if strike? frames[0]
    10 + strike_bonus(frames[1..2])
  elsif spare? frames[0]
    10 + ball_score(frames[1][0]) 
  else
    frames[0].chars.collect{ |ball| 
      ball_score(ball)
    }.inject(:+)
  end
end

def strike_bonus(frames)
  if frames[0] == "XX"
    20
  elsif strike? frames[0]
    10 + ball_score(frames[1][0])
  elsif spare? frames[0]
    10
  else
    ball_score(frames[0][0]) + ball_score(frames[0][1])
  end
end

def ball_score(ball)
  if strike? ball
    10
  else
    ball.to_i
  end
end

def strike?(frame)
  frame == "X"
end

def spare?(frame)
  frame[-1] == "/"
end
It took me a while to realize that each_slice should have been each_cons. You can replay my entire progression (warts and all) on Cyber-Dojo (naturally).

Agile on the beach

Last week I attended the excellent Agile on the beach conference in Falmouth, Cornwall. I'm really proud to have been the consultant who provided most of the technical-tdd agile training that has helped create 50 new jobs in Cornwall.

I thought Schalk Cronjé's talk was excellent. So was the one Ed Sykes did. And Seb Rose too. The highlight for me was Eben Upton's endnote. Inspiring. I'm going to visit my old secondary school and see if they want half-a-dozen Raspberry Pies. And I'm going to buy one for myself and put Cyber-Dojo onto it. I ran a double cyber-dojo session at the conference and several teachers said they wanted to run cyber-dojos as part of their course - fantastic.

no scaffolding means we're done

Suppose I'm doing the print-diamond kata in cyber-dojo in Java. I start with a test
    @Test
    public void diamond_A() {
        String[] expected = {
            "A"
        };
        String[] actual = new Diamond('A').toLines();
        assertArrayEquals(expected, actual);
    }
I slime a solution as follows
public class Diamond {

    private char widest;

    public Diamond(char widest) {
        this.widest = widest;
    }

    public String[] toLines() {
        return new String[]{ "A" };
    }
}
now I add a second test
    @Test
    public void diamond_B() {
        String[] expected = {
            " A ",
            "B B",
            " A ",
        };
        String[] actual = new Diamond('B').toLines();
        assertArrayEquals(expected, actual);
    }
and I slime again as follows
public class Diamond {

    private char widest;

    public Diamond(char widest) {
        this.widest = widest;
    }

    public String[] toLines() {
        if (widest == 'A')
            return new String[] { 
                "A" 
            };
        else
            return new String[] {
                " A ",
                "B B",
                " A ",
            };
    }
}
Like all techniques this approach has a certain style. In this case there is a small but definite asymmetry between the specificness of the tests (one for 'A' and another one for 'B') and the slightly less specificness of the code (an explicit if for 'A' but a default everything-else for 'B'). This is a style that is relaxed about the asymmetry, a style that emphasises this step as merely one temporary step on the path of many steps leading towards something more permanent. A style that recognises that code, by it's nature, is always going to be more general than tests.

But suppose you get hit by a bus tomorrow. How easily could your colleagues tell that this code was work in progress? Code that was not finished?

As an experiment I thought I would try an alternative style. One that is not so relaxed about the asymmetry. One that tries a bit harder to be more explicit about distinguishing code that's a temporary step still on the path from code that has reached its destination.

First I wrote a test that expresses the fact that nothing is implemented.
    @Test(expected = ScaffoldingException.class)
    public void scaffolding() {
        new Diamond('Z').toLines();
    }
I make this pass as follows
public class Diamond {

    private char widest;

    public Diamond(char widest) {
        this.widest = widest;
    }

    public String[] toLines() {
        throw new ScaffoldingException("not done");
    }
}
The scaffolding, as its name suggests, will have to be taken down once the code is done. While it remains, it indicates that the code is not done. Now I start. I add the diamond_A test (as before) and make it pass
public class Diamond {
    ...
    public String[] toLines() {
        if (widest == 'A') {
            return new String[]{ "A" };
        }
        throw new ScaffoldingException("not done");
    }
}
I add a second diamond_B test (as before) and make it pass
public class Diamond {
    ...
    public String[] toLines() {
        if (widest == 'A') 
            return new String[] { 
                "A" 
            };
        if (widest == 'B') 
            return new String[] {
                " A ",
                "B B",
                " A ",
            };
        throw new ScaffoldingException("not done");
    }
}
The scaffolding is still there. We haven't finished yet. Now suppose I refactor the slime (by deliberately duplicating) and end up with this
public class Diamond {
    ...
    public String[] toLines() {
        if (widest == 'A') {
            String[] inner = innerDiamond();
            String[] result = new String[inner.length-1];
            int mid = inner.length / 2;
            for (int dst=0,src=0; src != inner.length; src++)
                if (src != mid)
                    result[dst++] = inner[src];
            return result;
        } 
        if (widest == 'B') {
            String[] inner = innerDiamond();
            String[] result = new String[inner.length-1];
            int mid = inner.length / 2;
            for (int dst=0,src=0; src != inner.length; src++)
                if (src != mid)
                    result[dst++] = inner[src];
            return result;
        }
        throw new ScaffoldingException("not done");
    }
}
The code inside the two if statements is (deliberately) identical so I refactor to this
public class Diamond {
    ...
    public String[] toLines() {
        if (widest == 'A' || widest == 'B') {
            String[] inner = innerDiamond();
            String[] result = new String[inner.length-1];
            int mid = inner.length / 2;
            for (int dst=0,src=0; src != inner.length; src++)
                if (src != mid)
                    result[dst++] = inner[src];
            return result;
        } 
        throw new ScaffoldingException("not done");
    }
}
Now I add a new test for 'C'
    @Test
    public void diamond_C() {
        String[] expected = {
            "  A  ",
            " B B ",
            "C   C",
            " B B ",
            "  A  ",
        };
        String[] actual = new Diamond('C').toLines();
        assertArrayEquals(expected, actual);
    }
This fails. I make it pass by changing the line
        if (widest == 'A' || widest == 'B')
to
        if (widest == 'A' || widest == 'B' || widest == 'C')
Now I remove the if completely
public class Diamond {
    ...
    public String[] toLines() {
        String[] inner = innerDiamond();
        String[] result = new String[inner.length-1];
        int mid = inner.length / 2;
        for (int dst=0,src=0; src != inner.length; src++)
            if (src != mid)
                result[dst++] = inner[src];
        return result;
        throw new ScaffoldingException("not done");
    }
}
And it no longer compiles.
I have unreachable scaffolding.
Time for the scaffolding to come down.
I delete the throw statement.
public class Diamond {
    ...
    public String[] toLines() {
        String[] inner = innerDiamond();
        String[] result = new String[inner.length-1];
        int mid = inner.length / 2;
        for (int dst=0,src=0; src != inner.length; src++)
            if (src != mid)
                result[dst++] = inner[src];
        return result;
    }
}
Now the scaffolding() test fails. I delete that too.
We're green.
The scaffolding is gone.
We're done.

cyber-dojo in the cloud

http://cyber-dojo.com is now properly hosted in the Amazon cloud :-) I'll leave the old server, 81.31.112.23 (in my house, under the stairs, with the flaky internet connection - I live in a rural area) up for a few days, but will be gone soon.

A few people have been asking if they could donate something towards running Cyber-Dojo. Now that I'm paying for the hosting that seems a sensible idea, so I've added a donate button. Thanks.

cyber-dojo Skillsmatter podcast

I had the pleasure of running a Cyber-Dojo at Skillsmatter's 2 day Progressive Java tutorial last week. The session was video'd and is available here.

Fun and learning at Ericsson

I had the pleasure of teaching a TDD C++ course at Ericsson's Jorvas centre in Helsinki this week. As usual I made heavy use of cyber-dojo. Above is a screen shot of the dashboard of one of the practice katas. My evals were 5.8 for the course as a whole and 5.9 for teaching skills (out of 6). Some of the comments were:
  • Very clear.
  • You really got us understanding TDD.
  • Transformation of the mindset became clearly visible during the course.
  • Thanks!
  • Best course I've taken at Ericsson, thank you.
  • The cyberdojo is an excellent environment.
  • Very good hands on training.
  • cyber-dojo extremely nice.
  • It was fun!
  • You really need to try to do TDD in practice to see how beneficial and fun it is.
  • You made it visible in practice.
  • Fun fun fun.

Nicer HTML radio buttons

One of my pet peeves is plain html radio buttons. If I have a set of 5 radio buttons with 5 text labels of differing lengths then I want to be able to click anywhere on the shaded area and not just the text labels.

This is quite easy to achieve. Simply put each <input> into a <div>...
<div class="radio">
  <input type="radio" ... value="Red"/>
    <label>Red</label>
</div>
<div class="radio">
  <input type="radio" ... value="Amber"/>
    <label>Amber</label>
</div>
<div class="radio">
  <input type="radio" ... value="Green"/>
    <label>Green</label>
</div>
...and use a bit of jQuery
var $j = jQuery.noConflict();
$j(document).ready(function() {
  $j('div.Radio').each(function(n,node) {
    $j(node).click(function() {
      $j(this).children(':first').attr('checked', true);
    });
  });
});
Using a <div> changes the layout - so you might want to put each <div> into a table layout.

You can add some CSS to highlight the clickable whitespace...
  .Radio { background-color: Moccasin; }
  .Radio::after { content: "\00a0"; }
If you want to remove the bullet simply add the following
$j(document).ready(function() {
  $j('input[type=radio]').hide();
};
If you want to see an example of this, just try CyberDojo

Bare bones ruby unit testing

This morning I spent a happy hour exploring a little of ruby's Test::Unit::TestCase. I started with this:
require 'test/unit'

class MyTest < Test::Unit::TestCase

  def test_one_plus_one_equals_two
    assert_equal 2, 1+1.1
  end

end
I wanted to see how little I needed to write my own, super-minimal implementation of Test::Unit::TestCase...
require 'test/unit'

class MyTest < MyTestCase

  def test_one_plus_one_equals_two
    assert_equal 2, 1+1.1
  end

end
After 204 traffic lights in cyber-dojo I ended up with this...
require 'assertion_failed_error'

class MyTestCase

  def self.test_names
    public_instance_methods.select{|name| name =~ /^test_/}
  end
  
  def assert_equal( expected, actual )
    message = 
      "#{expected.inspect} expected but was\n" +
      "#{actual.inspect}\n"
    assert_block(message) { expected == actual }
  end

  def assert_block( message )
    if (! yield)
      raise AssertionFailedError.new(message.to_s)
    end
  end

end

at_exit do
  ::ObjectSpace.each_object(Class) do |klass|
    if (klass < MyTestCase)
      klass.test_names.each do |method_name| 
        begin
          klass.new.send method_name
        rescue AssertionFailedError => error
          print "#{klass.name}:#{method_name}:\n" +
                "#{error.message}"
        end
      end
    end
  end
end
class AssertionFailedError < RuntimeError; end
which allowed me write...
require 'my_test_case'

class MyTest < MyTestCase

  def test_one_plus_one_equals_two
    assert_equal 2, 1+1.1
  end

end
and finally, I added this...
class MyTestCase
  ...
  def self.test( name, &block )
    define_method("test_#{name}".to_sym, &block)
  end
  ...
end
which allowed me to rewrite the test as...
require 'my_test_case'

class MyTest < MyTestCase

  test "1+1 == 2" do
    assert_equal 2, 1+1.1
  end

end
Fun :-)

Agilis Deliberate Practice

Here's the slide-deck I presented at the Agilis conference in Iceland. It contains numerous examples of the kind of improvements a group of developers typically work through in just a few facilitated CyberDojo iterations.

Agile A-Z Keynote

I attended the excellent Agile .NET 2011 conference in Ghent, Belgium this week. Jason Gorman pulled out at the last minute and Erik asked me if I'd step in and do the keynote. I said yes of course and prepared this on the train+plane there.





More CyberDojos












I ran a CyberDojo at the excellent Tampere goes Agile conference recently (left photo). It got a 100% green card vote.

A few days later I ran another CyberDojo for the devs at Solita Oy, also in Tampere. A nicer bunch of people you couldn't ask to meet.

And a few days after that I ran yet another CyberDojo at the Ericsson Agile conference in Helsinki (right photo). @jussikm @htaubert and @karmolis tweeted that it was the most fun they'd had at work in 2011.