Categories
Ruby

Naresh Jain’s refactoring teaser

Naresh Jain recently posted a refactoring teaser. The original code was in Java, but I thought I’d have a go at refactoring it in Ruby instead. I deliberately didn’t look at Naresh’s solution beforehand, so mine goes in a rather different direction.

My code’s here (with the specs in the same file for simplicity), and you can step through the history to see the state at each step of the refactoring. The links in the text below take you to the relevant version of the file, and the Δs next to them link to the diffs.

Firstly, I converted the code to Ruby, and made sure the tests (which I converted to RSpec) still passed. It’s a fairly straight conversion, although I made a couple of changes while I was at it – mainly turning it into a module which I mixed into String. I changed the name of the method to phrases, to avoid conflicting with the built-in String#split. The regular expression to split on is much simpler too, because Ruby didn’t understand the original, and I had no idea what it was supposed to do anyway.

Here’s my initial Ruby version:

#!/usr/bin/env ruby
#
#See http://blogs.agilefaqs.com/2009/07/08/refactoring-teaser-part-1/

require 'spec'

module StringExtensions
  REGEX_TO_SPLIT_ALONG_WHITESPACES = /\s+/
 
  def phrases(number)
    list_of_keywords = ""
    count = 0
    strings = split(REGEX_TO_SPLIT_ALONG_WHITESPACES)
    all_strings = single_double_triple_words(strings)
    size = all_strings.size
    all_strings.each do |phrase|
      break if count == number
      list_of_keywords += "'" + phrase + "'"
      count += 1
      if (count < size && count < number)
        list_of_keywords += ", "
      end
    end
    return list_of_keywords
  end
 
  private
 
  def single_double_triple_words(strings)
    all_strings = []
    num_words = strings.size
 
    return all_strings unless has_enough_words(num_words)
 
    # Extracting single words. Total size of words == num_words
 
    # Extracting single-word phrases.
    (0...num_words).each do |i|
      all_strings << strings[i]
    end
 
    # Extracting double-word phrases
    (0...num_words - 1).each do |i|
      all_strings << "#{strings[i]} #{strings[i + 1]}"
    end
 
    # Extracting triple-word phrases
    (0...num_words - 2).each do |i|
      all_strings << "#{strings[i]} #{strings[i + 1]} #{strings[i + 2]}"
    end
    return all_strings
  end
  
  def has_enough_words(num_words)
    num_words >= 3
  end
end

String.send(:include, StringExtensions)

describe StringExtensions do
  it 'finds all phrases' do
    'Hello World Ruby'.phrases(6).should == "'Hello', 'World', 'Ruby', 'Hello World', 'World Ruby', 'Hello World Ruby'"
  end

  it 'returns all phrases when asked for more than exist' do
    'Hello World Ruby'.phrases(10).should == "'Hello', 'World', 'Ruby', 'Hello World', 'World Ruby', 'Hello World Ruby'"
  end

  it 'returns the first n phrases when asked for fewer than exist' do
    'Hello World Ruby'.phrases(4).should == "'Hello', 'World', 'Ruby', 'Hello World'"
  end

  it 'returns the first word when asked for one phrase' do
    'Hello World Ruby'.phrases(1).should == "'Hello'"
  end
end

I didn’t change the specs at all during the refactoring (because I didn’t change the API or add any new public methods or classes), and made sure they all passed at each step.

The first thing Δ I changed was to simplify that big iterator in phrases that loops through the list of phrases, formatting the output string. Basically all this does is to put each phrase in quotes, then stitch them all together separated by a comma and a space. The first of those tasks is a simple map, and the second is a join. The whole method collapses down to this (ruby methods automatically return the result of their last statement):

def phrases(number)
  strings = split(REGEX_TO_SPLIT_ALONG_WHITESPACES)
  all_strings = single_double_triple_words(strings)
  all_strings[0, number].map {|s| "'#{s}'"}.join(', ')
end

Next Δ I remembered that by default String#split splits at whitespace anyway, so I did away with the regular expression. Then Δ I renamed the strings variable to words to make its purpose a little clearer, leaving the phrases method looking like this:

def phrases(number)
  words = split
  all_strings = single_double_triple_words(words)
  all_strings[0, number].map {|s| "'#{s}'"}.join(', ')
end

The section of single_double_triple_words that extracted the single words seemed redundant, as we already had that list – the original words. I removed it Δ, and initialised all_strings to the word list instead (not forgetting to rename single_double_triple_words to match its new behaviour):

module StringExtensions
  def phrases(number)
    words = split
    all_strings = words
    all_strings += double_triple_words(words)
    all_strings[0, number].map {|s| "'#{s}'"}.join(', ')
  end
 
  private
 
  def double_triple_words(strings)
    all_strings = []
    num_words = strings.size
 
    return all_strings unless has_enough_words(num_words)
 
    # Extracting double-word phrases
    (0...num_words - 1).each do |i|
      all_strings << "#{strings[i]} #{strings[i + 1]}"
    end
 
    # Extracting triple-word phrases
    (0...num_words - 2).each do |i|
      all_strings << "#{strings[i]} #{strings[i + 1]} #{strings[i + 2]}"
    end
    return all_strings
  end
  
  def has_enough_words(num_words)
    num_words >= 3
  end
end

That has_enough_words method seemed a bit odd – particularly the way it was only called once, rather than after extracting each set of phrases. I decided it was probably a premature and incomplete attempt at optimisation, and removed it Δ for now.

My next target was the duplication in the blocks that calculate double- and triple-word phrases. First Δ I extracted them into separate methods:

module StringExtensions
  def phrases(number)
    words = split
    all_strings = words
    all_strings += double_words(words)
    all_strings += triple_words(words)
    all_strings[0, number].map {|s| "'#{s}'"}.join(', ')
  end
 
  private
 
  def double_words(strings)
    all_strings = []
    num_words = strings.size
 
    # Extracting double-word phrases
    (0...num_words - 1).each do |i|
      all_strings << "#{strings[i]} #{strings[i + 1]}"
    end
    return all_strings
  end
 
  def triple_words(strings)
    all_strings = []
    num_words = strings.size
 
    (0...num_words - 2).each do |i|
      all_strings << "#{strings[i]} #{strings[i + 1]} #{strings[i + 2]}"
    end
    return all_strings
  end
end

I decided that the num_words variable in the two new methods wasn't really necessary (it was only used once, and I think strings.size expresses intent perfectly clearly), so I inlined it Δ (and the same in triple_words):

def double_words(strings)
  all_strings = []

  # Extracting double-word phrases
  (0...strings.size - 1).each do |i|
    all_strings << "#{strings[i]} #{strings[i + 1]}"
  end
  return all_strings
end

Most of the code in double_words and triple_words was obviously very similar, so I created Δ a general extract_phrases method, and called it from both. The new method uses the start position and length version of Array#[] to extract the appropriate number of words, then Array#join to string them together separated by spaces:

def double_words(strings)
  extract_phrases(strings, 2)
end

def triple_words(strings)
  extract_phrases(strings, 3)
end

def extract_phrases(strings, number_of_words)
  result = []
  (0...strings.size - number_of_words + 1).each do |i|
    phrase = strings[i, number_of_words].join(' ')
    result << phrase
  end
  result
end

At this point double_words and triple_words have become just dumb wrappers around extract_phrases, so I removed them Δ and just called extract_phrases directly:

def phrases(number)
  words = split
  all_strings = words
  all_strings += extract_phrases(words, 2)
  all_strings += extract_phrases(words, 3)
  all_strings[0, number].map {|s| "'#{s}'"}.join(', ')
end

Rather than hardcoding the calls for two and three words, I changed it Δ to use a loop:

def phrases(number)
  words = split
  all_strings = words
  (2..words.size).each do |number_of_words|
    all_strings += extract_phrases(words, number_of_words)
  end
  all_strings[0, number].map {|s| "'#{s}'"}.join(', ')
end

I decided this was the point to put the optimisation back Δ and stop looking for phrases once we had enough:

def phrases(number)
  words = split
  all_strings = words
  (2..words.size).each do |number_of_words|
    break if all_strings.length >= number
    all_strings += extract_phrases(words, number_of_words)
  end
  all_strings[0, number].map {|s| "'#{s}'"}.join(', ')
end

I decided that number_of_words wasn't particularly clear, so changed it Δ to phrase_length, then Δ made the iterator in extract_phrases more ruby-like by using map:

def extract_phrases(strings, phrase_length)
  (0...strings.size - phrase_length + 1).map do |i|
    strings[i, phrase_length].join(' ')
  end
end

I then noticed that I hadn't been consistent in changing strings to words, so fixed that Δ.

Lastly Δ, I decided that even though it was only one line, the code that formats the output deserved pulling out into a method.

Here's my final version of the code:

module StringExtensions
  def phrases(number)
    words = split
    all_strings = words
    (2..words.size).each do |phrase_length|
      break if all_strings.length >= number
      all_strings += extract_phrases(words, phrase_length)
    end
    format_output(all_strings[0, number])
  end
 
  private
 
  def extract_phrases(words, phrase_length)
    (0...words.size - phrase_length + 1).map do |i|
      words[i, phrase_length].join(' ')
    end
  end

  def format_output(phrases)
    phrases.map {|s| "'#{s}'"}.join(', ')
  end
end
Categories
rspec

A couple of rspec mocking gotchas

Just a couple of things that have caused a bit of head-scratching lately when writing RSpec specs using the built-in mocking framework.

Catching StandardError

Watch out if the code you’re testing catches StandardError (of course you’re not catching Exception, right?). Try this:

require 'rubygems'
require 'spec'
 
class Foo
  def self.foo
    Bar.bar
  rescue StandardError
    # do something here and don't re-raise
  end
end
 
class Bar
  def self.bar
  end
end
 
describe 'Calling a method that catches StandardError' do
  it 'calls Bar.bar' do
    Bar.should_receive :bar
    Foo.foo
  end
end

Nothing particularly exciting there. Let’s run it and check that it passes:

$ spec foo.rb 
.

Finished in 0.001862 seconds

1 example, 0 failures

However, what if we change the example to test the opposite behaviour?

describe 'Calling a method that catches StandardError' do
  it 'does NOT call Bar.bar' do
    Bar.should_not_receive :bar
    Foo.foo
  end
end
$ spec foo.rb 
.

Finished in 0.001865 seconds

1 example, 0 failures

Wait, surely they can’t both pass? Let’s take out the rescue and see what’s going on:

class Foo
  def self.foo
    Bar.bar
  end
end
$ spec foo.rb 
F

1)
Spec::Mocks::MockExpectationError in 'Calling a method that catches StandardError does NOT call Bar.bar'
 expected :bar with (no args) 0 times, but received it once
./foo.rb:6:in `foo'
./foo.rb:18:

Finished in 0.002276 seconds

1 example, 1 failure

That’s more like it.

Of course, what’s really happening here is that Spec::Mocks::MockExpectationError is a subclass of StandardError, so is being caught and silently discarded by our method under test.

If you’re doing TDD properly, this won’t result in a useless test (at least not immediately), but it might cause you to spend a while trying to figure out how to get a failing test before you add the call to Foo.foo (assuming the method with the rescue already existed). Generally you can solve the problem by making the code a bit more selective about which exception class(es) it catches, but I wonder whether RSpec exceptions are special cases which ought to directly extend Exception.

Checking receive counts on previously-stubbed methods

It’s quite common to stub a method on a collaborator in a before block, then check the details of the call to the method in a specific example. This doesn’t work quite as you would expect if for some reason you want to check that the method is only called a specific number of times:

require 'rubygems'
require 'spec'

class Foo
  def self.foo
    Bar.bar
    Bar.bar
  end
end

class Bar
  def self.bar
  end
end

describe 'Checking call counts for a stubbed method' do
  before do
    Bar.stub! :bar
  end

  it 'only calls a method once' do
    Bar.should_receive(:bar).once
    Foo.foo
  end
end
$ spec foo.rb 
.

Finished in 0.001867 seconds

1 example, 0 failures

I think what’s happening here is that the mock object would normally receive an unexpected call, causing the expected :bar with (any args) once, but received it twice error that you’d expect. Unfortunately the second call to the method is handled by the stub, so never triggers the error.

You can fix it, but it’s messy:

it 'only calls a method once' do
  Bar.send(:__mock_proxy).reset
  Bar.should_receive(:bar).once
  Foo.foo
end
$ spec foo.rb 
F

1)
Spec::Mocks::MockExpectationError in 'Checking call counts for a stubbed method only calls a method once'
 expected :bar with (any args) once, but received it twice
./foo.rb:23:

Finished in 0.002542 seconds

1 example, 1 failure

Does anyone know a better way?

The full example code is in this gist.

Categories
rspec

Helpful message from rspec

Just came across an interesting error message from rspec. I had a spec that looked like this:

it "should not mass-assign 'confirmed'" do
  Blog.new(:confirmed => true).confirmed.should_not be_true
end

Obviously it failed, as I hadn’t written the code yet, but there was more in the error message than I expected:

..........F

1)
RuntimeError in 'Blog should not mass-assign 'confirmed''
'should_not be  true' not only FAILED,
it is a bit confusing.
It might be more clearly expressed in the positive?
.../spec/models/blog_spec.rb:20:

Finished in 0.06192 seconds

11 examples, 1 failure

In fact, rewriting this as should be_false wouldn’t work, as the expected value is nil. I took the hint though, and rewrote it as should be_nil.

Categories
Agile Software

Design by Example or Test-Driven Development?

Brad Wilson argues the case for renaming TDD to ‘Design by Example’ (or DbE), to emphasise the fact that TDD (and BDD, which is really just TDD done well) are about design rather than testing.

I agree with the intention, but I’m not sure I agree. I’m happy to lose the ‘test’ &ndash it’s losing the ‘development’ that bothers me. I’ve encountered far too many ‘designers’ who haven’t written a line of code in years, and I worry that they’d read ‘design by example’ as simply starting their design document with a few test cases. I’ve already seen TDD redefined as test-driven design and interpreted that way.

Of course for people who already grok TDD this isn’t an issue, but it’s not them that a name change is aimed at. Test-driven development might not be the perfect name, but at least there are already plenty of people who do understand what it really means to correct any misapprehension. The term BDD (which I use myself from time to time) has already started sowing confusion, without adding yet another.

On the other hand, anything that gets people discussing how to get the most from the practice has to be a good thing.

Categories
Ruby

API vs RSI

World’s-most-famous-twitterer Stephen Fry has a system for handling follow requests: you tweet using the #followmestephen hashtag, and he wades diligently through them, manually following people.

This seems an odd sort of thing to do – most people choose whom to follow based on whether they know them or like what they say, rather than on request – but I suppose when you have over a quarter of a million followers things work a little differently. It also creates lots of work , and looks like an ideal candidate for automation.

I thought I’d have a quick play with the Twitter API this morning (no doubt I’m not the only one), and cobbled together the script below, which you can also download as follow_me_stephen.rb (although if you’re not Mr Fry I’m not sure why you would want to). Save the file, and run using ruby follow_me_stephen.rb.

I wanted to avoid having too many dependencies, so I didn’t use the twitter gem, or the excellent httparty, but I was too lazy to figure out all the XPaths to handle the Atom version of the API. This means you need to have the JSON gem installed, which is as simple as sudo gem install json (omit the sudo on Windows).

The script’s pretty dumb, in that it grabs the whole set of search results every time, and blindly requests to follow everyone, regardless of whether you’re already following them.

#!/usr/bin/env ruby

require 'net/http'

begin
  require 'json'
rescue LoadError
  STDERR.puts <

		
Categories
Software

Software Craftsmanship 2009

Last Thursday I attended the SC2009 conference at the BBC Media Centre in London. Here are a few notes (assuming I get round to finishing the post – I see I still have an unfinished draft about a session from XPDay2007).

MappingPersonal Practices (Ade Oshineye)

This was a simple exercise where everyone spent five minutes drawing a mind-map of the software development practices they find most important to them, then spent 90 seconds each listing and describing them. The intention was to then go round again and say which practices you’d heard others mention that you wanted to try, but time ran out.

Here’s my selection, converted to Graphviz to save you the pain of trying to decipher my handwriting:

Personal practices

Of the practices mentioned by others, the ones I noted down that I ought to try are speaking at conferences, learning other languages (I haven’t played with a new one for a while), getting feedback from others in the team and reading classic journal papers (conveniently Michael Feathers has just posted a list of recommended ones).

Ruby Kata & Sparring (Micah Martin)

Micah (who had come all the way from Chicago just for this conference!) described the coding kata, linking it to the martial arts practice from which the idea was taken. Trivia fact: the name of Micah’s company, 8th Light, is the literal translation of Hakkoryu, the school of Jujitsu that Micah followed.

He mentioned two different views of katas: Dave Thomas’s suggestion to practice solving the same problem many times and reflect on what you are learning, and Uncle Bob Martin’s approach where you watch a master solve the problem and mimic their process (the latter seems closer to the practice of kata in martial arts). Micah suggested that both of these are missing an important aspect of martial art kata: performance.

Performing a code kata in front of other craftsman gives you a purpose for practicing it. You can receive feedback, learn and address your weaknesses and measure your skill against others, and it shows respect for the craft.

Micah performed a code kata himself, implementing Langston’s Ant in Ruby and asking for a score out of ten at the end (most people gave seven or eight). Finally he mentioned sparring as another technique borrowed from martial arts, and talked about the Battleship tournament which he set up. Apparently this will be repeated soon, using Hangman as the problem to solve.

If you want to see more, there’s a video of Micah making the same presentation at RubyConf 2008, and also a screencast of the Langston’s Ant kata.

Three Paradigms: Taking An Extreme Position on Code Style in a Safe Environment (Keith Braithwaite)

The purpose of this session was to take a piece of contrived “enterprisey” code, make three separate refactorings on it and see what effect they had on the ease of making changes to the code. The refactorings were basically removing conditional logic, removing getters and setters, and making certain classes unmodifiable.

The example code was available in Java and C# (the so-called country-and-western languages – “We got both kinds…”). Unfortunately, like many people there my Java skills were a bit rusty (and my C# skills non-existent), so by the time I’d grabbed the code off one of the memory sticks that were floating round, set up a project in Eclipse, realised that I didn’t have junit.jar and found someone to copy it from, I didn’t really have enough time to complete any of the refactorings. I also spent a while puzzling over what the purpose of the UserDTO class was, as it seemed like it would have been easier to just pass User objects around. Turns out that the point was to show that there was no point (if you see what I mean). Fortunately I seem to have avoided being exposed to the kind of pattern-obsessed enterprise code where these useless nojos are considered good practice. It doesn’t surprise me though – to quote Keith, “Curly bracket languages make you stupid”.

I don’t think anyone got as far as finishing all the refactorings, or making behavioural changes to the resulting code, but there was some interesting discussion about ways to achieve the various refactorings (including one person who admitted to removing getters and setters by making the fields public!) It’s a shame there wasn’t a bit more time available.

Responsibility-driven Design with Mock Objects (Willem van den Ende & Marc Evers)

This was a quick introduction to the use of mock objects as a design tool (refreshingly a show of hands indicated that everyone present practiced TDD, and most used mocks), followed by a randori-style session (in Ruby, by popular vote) designing some classes for a simple adventure game.

As with the previous session, time constraints meant that we didn’t progress that far with the code, but there was a lot of discussion around various design decisions, which of course is the really valuable part of this kind of exercise. A lot of this was kicked off by Steve Freeman (co-inventor of mock objects, and co-author of the excellent Mock Roles, not Objects, where the proper use of mocks is explained), who piped up when someone suggested moving when there was some debate about the correct name for a method. From what I can remember, his comment was something along the lines of “No! Getting the right names for things is the whole point – if you aren’t going to do that, you might as well not bother!” I was reminded of the quote from Phil Karlton: “There are only two hard problems in Computer Science: cache invalidation and naming things.”

The main thing I took away from this session was some confirmation that the way I currently try to do TDD with mocks is fairly close to what seems to be considered good practice. Interestingly the number of people who agreed that the techniques shown were similar to how they currently used mocks was a fraction of the number that had earlier identified themselves as mock users, but unfortunately there wasn’t time to find out how their approaches differed.

My Defining Moments (Steve Freeman)

This was similar in a way to Ade’s, but instead of everyone talking for 90 seconds about their personal practices, a dozen or so volunteers talked for up to five minutes about a ‘breakthrough experience’ where they’ve learned something important, and how that breakthrough happened. Here are some of the ones I noted down (apologies if I’ve misrepresented anyone, but I only wrote down a few words for each and I may have forgotten the details):

Steve himself talked about when he was at DEC, where ‘everything just worked’. He described the best sysadmins he’d ever seen, who would, unasked, perform ‘random acts of helpfulness’. The main lesson he learnt from this was that despite the mediocrity found in most organisations, it was possible to be that good if you had the right people.

Micah described how some ways of working when he joined ObjectMentor hadn’t felt quite right to him, and his realisation that in the end you can’t impose one person’s standards on another: we all have to develop our own which we’re comfortable with.

Ade talked about his first project at ThoughtWorks. He described how his boss had shouted at him in a client meeting for describing himself as “just a developer”. His main point though was about working with a team of developers from Dixons, whose sole Java experience was a two-week course from Dan North. Obviously the lack of experience wasn’t ideal, but the fact that they’d learnt everything they knew from Dan meant that they only knew the right way to do things. For example if you suggested to them that something was too simple to need a test, they’d say “No, you have to start with a test!”

Willem van den Ende described an internship spent refactoring a hideous, bug-ridden, copy-and-pasted mudball of GUI code, which contained 40,000 lines of code and which no-one dared touch. In four months, by slowly removing duplication and cleaning up the code, he reduced it to a tenth of the size. He also found that he fixed most of the bugs without really trying, as a side-effect of removing duplication. The original code had taken two people three months to write.

Gojko Adzic talked about a project for the National Grid, where a graphical map view they’d added at the end of the project was by far the most popular feature of the software, despite the fact that the customer hadn’t asked for it. He highlighted the discrepancy between this and the common agile view that says you should only work on features (user stories) that the customer has explicitly asked for. My take on this would be that it’s part of the developers’ job to propose new or alternative solutions to the customer, but generally you should still get their agreement.

Finally John Daniels talked about a ‘lightbulb moment’ when moving from ADA to Smalltalk, where he suddenly understood what OO was really about. He said it was 20 years before he saw another language with a development environment as good as the Smalltalk one (something I’ve heard from others too).

I didn’t put my name down to talk, but for the record my defining moment would have actually have been a couple of events which have influenced my understanding and practice of TDD. From my initial one-test-per-method attempts at unit testing, reading about TestDox and RSpec introduced me to the idea of tests as specification. Then there were a couple of sessions from XPDay06, especially Are your tests really driving your development, which made me realise that my tests still weren’t expressing intent as well as they could have done. Finally the aforementioned Mock Roles, not Objects showed me the right way to use mocks and stubs.

Test-driven Development of Asynchronous Systems (Nat Pryce)

I’d already read the notes from this presentation, which Nat originally gave at XPDay08, but seeing the talk in person made it much clearer. I don’t think there’s much I can add to the notes.

General observations

Firstly, it was great to spend a day with a hundred-odd people who obviously cared about their work (not just their career) as software developers. The other thing that struck me was that, despite this not being explicitly an Agile or XP conference, it seemed to be taken for granted that practices like TDD and refactoring were basic necessities, not optional extras.

More randomly, there were far more non-Apple laptops in evidence than you would see at a Ruby conference or a Barcamp, presumably because a lot of delegates work for large companies and brought their work laptop. That said, most of the ones I saw seemed to be running Linux rather than Windows, but I wasn’t keeping count. More Java and .NET people too, although a fair proportion working in Ruby and other languages. On the phone front though, I don’t think I’ve ever seen so many iPhones in the same room! It seemed like every other person had one (even Ade, who was also showing off his Android phone, a Christmas present from his employer).

finally, a huge thank you to Jason Gorman and everyone else involved in organising the day, and to the BBC for hosting it.

Categories
Software

Chaining SSH Tunnels

[Update 18/3/2009: added single-tunnel example]

This post is mainly for my own benefit, because every time I need to do this I find I’ve forgotten how and need to look it up again.

Say you want to reach dest, but have to tunnel through foo because you don’t have direct access to port 22 on dest.

ssh -NL 65001:dest:22 foo &
ssh localhost -p 65001
Welcome to dest!
$

If you need to tunnel through multiple gateways to reach the machine you want to connect to, this is how to do it. Now let’s say you have to jump through foo, bar and wibble to get to dest.

ssh -NL 65001:bar:22 foo &
ssh -NL 65002:wibble:22 localhost -p 65001 &
ssh -NL 65003:dest:22 localhost -p 65002 &
ssh localhost -p 65003
Welcome to dest!
$

Obviously you don’t need to use ports starting with 65001, but can pick any convenient unused local ports.

You can use different usernames and SSH ports if necessary, eg if you have to connect to wibble as dave on port 222, that line becomes:

ssh -NL 65002:dave@wibble:222 localhost -p 65001

[tags]ssh, chain, tunnel[/tags]

Categories
General nonsense Rails Ruby

My (very!) small part in the Array#forty_two controversy

For those outside the Rails community who have no idea what I’m on about, some people got a bit upset about Rails 2.2 defining Array#second up to Array#tenth, so for example you can call foo.fifth instead of foo[4] (you can already call foo.first instead of foo[0]). One of the last changes to be committed before 2.2 was released was to slim the list down to just second, third, fourth and fifth, but adding Array#forty_two (the ultimate answer) instead.

dhh_tweet.png

commit_1.png

kerry_reply.png

dhh_reply.png

commit_2.png

Categories
Rails Ruby

Rails 2.2 Envycast Review

I’ve been a fan of the RailsEnvy guys (Gregg Pollack and Jason Seifer) ever since their “Hi, I’m Ruby on Rails” spoof of the “I’m a Mac” ads, and have been listening to their podcast ever since. I even got a mention on it once. Well that’s not strictly true – I wasn’t actually mentioned, but a patch I’d contributed to Capistrano was, which is close enough.

Recently, Gregg and Jason have branched out into screencasts, but I hadn’t actually watched one because (understandably) they charge for them, and I was too tight to cough up the cash. £1200 for a new MacBook, no problem. A fiver for a screencast? What am I, made of money?

Anyhow, when I saw that they were looking for people to review their Ruby on Rails Envycast, covering the latest goodness in Rails 2.2, I jumped at the chance to get a free copy. A wonderful example of cognitive bias, given that I wouldn’t have agreed to write a review just to be paid $16.

What do I get for the money?

The basic $9 gets you the screencast and a set of code samples to go with it, or for $16 they’ll throw in Carlos Brando’s Ruby on Rails 2.2 PDF too. Alternatively the PDF is available on its own, also for $9.

Video

The video is available in Quicktime or Ogg formats at a resolution of 569×480, as well as a version optimised for iPhones and iPods. Total running time is just under 45 minutes, and incredibly the first 39½ of those go by before Jason makes any claims about Rails’s scalability.

I don’t know whether it’s unique, but the Envycast style of having the presenters chroma-keyed onto the Keynote presentation generally works very well. The visuals themselves are professional, although sometimes the ‘sparkle’ effect is a bit overused for my taste. The presentation style is much as you’d expect if you’ve listened to the podcasts, with plenty of cheesy humour to keep things interesting. I think having two people present in a conversational style is a big help.

The screencast is split into sections, each covering the new features for a different component (ActiveRecord, ActiveSupport, ActionPack, ActionController, Railties, internationalization and performance). The Quicktime version (not sure about the others) has bookmarks, making it easy to jump to a particular section. The whole thing is set against a variety of city skylines to liven the background up a little – by the way guys, that’s Tower Bridge, not London Bridge.

Each new feature is introduced with an example, generally contrasting the ‘old’ way of doing something with the equivalent in 2.2. There’s enough detail to get the idea of what’s changed, without dwelling too long on each one. One tiny gripe with the code snippets on screen: the pedant in me hates seeing curly quotes in code, because I know if I typed puts ‘foo’ into irb instead of puts 'foo', it wouldn’t work.

Code samples

The screencast comes with a set of code samples to illustrate all the features discussed in the screencast. These take the form of sample classes with Test::Unit test cases, along with rakefiles to run them. The sample directory contains a frozen installation of Rails 2.2, so all you need to do to run them is add the appropriate values to database.yml. I had trouble running them initially because they were inside a directory with a space in its name, but other than that it all worked nicely.

PDF

The PDF that comes with the $16 bundle is by Carlos Brando, well-known for his free Rails 2.1 book. It’s available in the original Portugese, or translated to English by Carl Youngblood. The book weighs in at 118 pages, and as you would expect goes into more detail than the screencast. It claims to cover all the major changes in Rails 2.2 (I haven’t checked!), and contains clear descriptions with examples.

Conclusion

So is it worth it? On balance, I think the answer is yes, although I wonder whether they’d sell more at $5 rather than $9 – after all, I can buy (to pick an example at random) the entire Naked Gun trilogy on DVD for roughly the same amount, and Gregg and Jason aren’t that funny. The value is in collecting all the information in one place – you could trawl through the release notes and lighthouse tickets to get all the same information, but if you value your time at all, the screencast and PDF pay for themselves many times over.

Should you buy the PDF, the video or both? If you just want the hard facts, go for the PDF, but if you want to be entertained too (assuming you find the Rails Envy podcasts entertaining), get the video as well. The next episode, Scaling Ruby, is out now, and I might buy it just to see if Jason finally admits that Rails might actually be able to scale.

[tags]railsenvy screencast rails2.2[/tags]

Categories
General nonsense

Microsoft Ad

“Roger that Houston, I’m a PC.”

I’m a PC

Yeah? and how’s that working out for you?

[tags]Microsoft, Windows, PC, Advert[/tags]