Tuesday, October 28, 2008

Continuous Integration


Here is an old diagram that was in my slide deck at OSCON 2007. Sorry I managed to cut off the .WAR file suffix for the cruise build artifacts. Anyway, I am publishing it here for easy reference.

Ruby VS Java

Recently in a conversation with a Ruby dev. I was trying to make an analogy about the differences between Ruby and Java. For some reason I used cars. It went something like this.
Going with Java is like driving a Ford Crown Victoria. The Vic. might not be much fun, but it is reliable, easy to find parts for, and boy, you sure can fit lots of stuff in the trunk. Going with Ruby is like driving a little roadster. It is fast, and you will have lots of fun with it until you try towing your boat. Oh, and by the way, finding a mechanic for that exotic Japaneese machine might be tough.
The main point of the analogy is that from a developer perspective programming with Ruby is often far more attractive than programming in Java. But it makes some other points as well: Java is ubiquitous, it has a library for everything and it is embraced by enterprise. It also highlights how powerful Ruby is, and how much smaller the developer base is. Unfortunately depicting Ruby as a sports car makes it sound impractical. It really isn't. Oh, and if you really know what you are doing you'll have no trouble towing that boat. Stupid analogies.

Thursday, October 23, 2008

Ruby Autocomplete in Vim


Vim and Ruby fit together like beer and pizza, music and dancing: each an art form unto itself, but together they transcend, uh, somersaulting goose bumps. If you aren't already getting to that level with Vim and Ruby you might want to look into the following two Vim plugins: rubycomplete and supertab. Rubycomplete gives you autocomplete for variable names, method names, etc., just like you get in your IDE. Supertab hooks that functionality to the tab key (among other awesomeness), so just like at the command line you can tab complete your thoughts. Wheee!

One side note. To use rubycomplete you need to have a version of vim compiled with ruby support, otherwise you get an error message like this: "E486: Pattern not found: ErrMsg( "Error: Required vim compiled with +ruby"). To get your own version of vim with ruby support compiled in use MacPorts. The precise command you want is 'sudo port install vim +ruby'. On linux you can just apt-get the vim-ruby package and it comes with the plugin by default (but you still have to get supertab yourself). Another technical detail: you need to modify your ~/.vimrc to support all this hotness.

"ruby
autocmd FileType ruby,eruby set omnifunc=rubycomplete#Complete
autocmd FileType ruby,eruby let g:rubycomplete_buffer_loading = 1
autocmd FileType ruby,eruby let g:rubycomplete_rails = 1
autocmd FileType ruby,eruby let g:rubycomplete_classes_in_global = 1
"improve autocomplete menu color
highlight Pmenu ctermbg=238 gui=bold

That last bit with the highlight command is there to fix the default rubycomplete color scheme. Out of the box it has a horrible, horrible pink background to the menu.

As an extra bonus, if you like textmate's cmd-T action you'll want to look into fuzzyfinder_textmate. Another awesome vim plugin. Also, because color is key, look into some good vim colorschemes. I use the vividchalk vim colorscheme on my mac and desert256 on linux (see screenshot). desert256 is my favorite but I can't get 256 color support working on my mac, so i settle for vividchalk.

Monday, October 13, 2008

Running Selenium From CruiseControl with DRB


Doesn't DRb sound like a hip-hop handle from the 80's? Yo! Yo! My favorite MC is DRb! Uh. Actually it is way cooler than that, because it stands for distributed ruby. But I'm getting ahead of myself. DRb is the solution. Let's look at the problem. I'm working on a Java project that has a Selenium suite. The problem solved below was how to best kick off Selenium remotely from Cruise Control. Many options were discussed including a separate cc.rb instance and using Net::SSH. In the end we decided on DRB because we thought it would require the least effort, be cross platformy, and reliable.

Enough talk. Here is what we did: First thing was to write the ruby code that would call from a Linux server over to a Windows server. (I mention the OSes only to highlight the fact that there is nothing special that we have to do to make this work cross platform.)

The following code runs on the linux server that runs Cruise Control. The most interesting line is the one that creates the DRbObject. Basically that line goes out over the network and retrieves whatever object the drb process at druby://seleniumblx:9999 is handing out. Then it calls the run method on that object.


#!/usr/bin/env ruby
require 'drb'

class InvokeFromCruise
def run_rake
remote_runner = DRbObject.new(nil, "druby://seleniumbox:9999")
remote_runner.run
end
end

invoker = InvokeFromCruise.new
invoker.run_rake


Over here on our Windows box running Selenium, the DRb.start_service call starts a ruby process that listens on port 9999. When some other DRb process connects to that port it will simply hand over an instance of RakeRunner. RakeRunner is a dumb class that basically makes a system call to run rake with our smoke task, thus invoking the smoke suite.


require 'drb'

class StartSuite
def start
@rake_runner = RakeRunner.new
DRb.start_service("druby://seleniumbox:9999", @rake_runner)
end
def daemon
DRb.thread.join
end
end

class RakeRunner
def run
`ruby.exe C:/ruby/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake.rb smoke`
end
end

starter = StartSuite.new
starter.start
starter.daemon


Disclaimer: The above code was slightly modified without subsequent tests.

Now to tie this together with our cruise build... First, get the listener up on the selenium box. To do that you invoke the StartSuite class from the directory that contains the selenium suite's rakefile. It will listen on port 9999 until you kill the process. Next, we tell cruise control to invoke the InvokeFromCruise class. Here is a snippet from cruise's config.xml. Notice it is simply calling a invokeSelenium task when the build is successful.


<onsuccess>
<antpublisher buildfile="/path/to/build.xml" target="invokeSelenium" anthome="/path/to/apache-ant">
</onsuccess>


Here is the invokeSelenium ant task that calls the InvokeFromCruise code. The spawn attribute of the exec tag simply tells ant not to wait around while the ruby executable runs.

<target name="invokeSelenium">
<exec executable="ruby" spawn="true">
<arg line="${parameterized-path-to-executable}/build/invoke_from_cruise.rb">
</exec>
</target>


That is pretty much it. The StartSuite class waits around on our windows box and hands out little remote control objects that can kick off a rake task. CruiseControl executes a ruby script that triggers a remote control object after every successful build. The results of the build are simply logged to a file that is in apache's web root so you can check there anytime you need to.

I was happy with how easy it was to do this with DRb. It is lightweight, cross-platform, easy to customize, and more fun than a couple of 80's rappers holding a doll house.

Thursday, October 9, 2008

A Mingle in every Agile shop's cupboard?


An ever popular topic of conversation at CHI.RB is 'how I quit my old-crappy-programming-job (OCPJ) and got a shiny-new-Ruby-programming-job (SNPJ)'. In fact just the other night I was listening to such a conversation. As the storyteller went down his list of new job hotness, he rattled off highlights such as: Ruby, Rails, Agile, Mingle, XP... Hey, back up! Mingle!?!

It is nice to come across people who like your* software, and even nicer to hear them drop the name among a group of programmers without anyone saying, "What is Mingle?"

* I use the possesive your here loosely. I'm not on the Mingle team, but feel slightly involved as I too work at ThoughtWorks and occasionally I send them suggestions which they blithely ignore. Thanks guys!

Sunday, October 5, 2008

Minchia, Arduino E' Grande.


Arduino
is an open source platform composed of a microcontroller, and a development environment. It is a one stop shop for realizing those electronics projects that have been kicking around in the back of your mind for the past few years. You know, the theremin alarm clock that requires you to play the theme to tetris to turn it off...

Evan Farrar's Lightning talk about Arduino inspired me to buy one. And it took me awhile to get around to playing with the thing, but I finally did, and it is fantastic! For my first little project I undertook to reproduce this light mixing project. It was a little too easy, so in order to make it more interesting I decided to implement the state of the 3 lights as a Ripple Carry Adder. And to control it all with a button. It wasn't too hard, although I do have some questions about the Arduino programming language. Their documentation doesn't give any examples of how to use arrays as parameters to functions. They say the language is based on C, but when I try a C style pointer reference I get a funky error. Also when I try to assign one array the value of another array I get errors. My code works around these problems, but if anyone can show me some examples of how to do this correctly in the Arduino language I would appreciate it. Here is my first arduino code:


int pinStates[] = {HIGH,LOW,LOW};
int fullAddResult[2]; //ugh, global since i can't seem to pass arrays around
int halfAddResult[2];
int greenPin = 12;
int redPin = 11;
int bluePin = 10;
int inPin = 2;
int inPinVal = 0;
int prev;
boolean autoPilot = false;

void setup(){
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(inPin, INPUT);
prev = digitalRead(inPin);
}

//current, as in electrical current
int currentVal(boolean a){
if (a == true){
return HIGH;
}
else{
return LOW;
}
}

boolean boolVal(int a){
if (a == HIGH){
return true;
}
else{
return false;
}
}

int nand(int a, int b){
return currentVal(!(boolVal(a) && boolVal(b)));
}

int exor(int a, int b){
int ab;
int aab;
int bab;
ab = nand(a, b);
aab = nand(a, ab);
bab = nand(ab, b);
return nand(aab, bab);
}

// method implements a half adder
void halfAdd(int a, int b){
halfAddResult[0] = currentVal(boolVal(a) && boolVal(b));
halfAddResult[1] = exor(a, b);
}

// implements a full adder
void fullAdd(int a, int b, int c){
int ab[2];
int abc[2];
halfAdd(a, b);
ab[0] = halfAddResult[0];//Lame way of copying array values
ab[1] = halfAddResult[1];
halfAdd(ab[1], c);
abc[0] = halfAddResult[0];//Still Lame
abc[1] = halfAddResult[1];
fullAddResult[0] = currentVal(boolVal(ab[0]) || boolVal(abc[0]));
fullAddResult[1] = abc[1];
}

// implements a ripple carry adder
// MSB is the 0th element of the pinStates array
void increment(){
int temp[2];
halfAdd(pinStates[2], HIGH);
temp[0] = halfAddResult[0];
temp[1] = halfAddResult[1];
fullAdd(pinStates[1], temp[0], LOW);
halfAdd(pinStates[0], fullAddResult[0]);
pinStates[0] = halfAddResult[1];
pinStates[1] = fullAddResult[1];
pinStates[2] = temp[1];
}

void loop(){
inPinVal = digitalRead(inPin);
digitalWrite(redPin, pinStates[0]);
digitalWrite(bluePin, pinStates[1]);
digitalWrite(greenPin, pinStates[2]);
if (pinStates[0] == LOW && pinStates[1] == LOW && pinStates[2] == LOW){
autoPilot = true;
}

if (prev != inPinVal){
increment();
prev = inPinVal;
autoPilot = false;
}

if (autoPilot){
increment();
delay(500);
}

}


One note is that xor seems to be a reserved word (although the compiler doesn't tell you that, it just barfs up some meaningless error) so that is why my xor function is named exor. Obviously I didn't bother with Arduino's bitwise functions. My Arduino code leaves something to be desired and so do my pictures, sorry they are blurry. The code could use some cleanup, but when you flash it over to the microcontroller, it works! Toggling the button causes the light mixer to shuffle through all possible color combinations by doing binary addition on a 3 bit number. Each bit of the number represents a different color and so by running from 0 to 7 you can go from no light to white light with all the colors in between. Actually you can't get to the no light state because the state where all the lights are off takes you into "Auto Pilot" mode and it cycles through all the colors with a half second delay between changes. Wheeeeee!

Thursday, October 2, 2008

Debating the Vice Debate


I made a transcript of Palin's answers from the Vice Presidential Debate tonight. Here it is:
Question: Blah, blah, blah Economy?
Answer: No Problem, Blah, blah, blah Alaska.

Question: Blah, blah, blah Foreign Policy?
Answer: Blah, blah Governor of an energy-producing state, blah, blah Alaska.

Question: Blah, blah, blah your weaknesses?
Answer: None. Blah, blah, blah executive experience, blah Alaska.
Hey there gal, you aren't running for Vice President of Alaska! What about the other 99.97 percent of Americans? Have you thought about us? I find her point of view irritating. While it is fine for her current job, it seems completely out of whack for somebody trying to become a representative for the entire nation.

Here are other assorted observations:
  • Palin showed complete disregard for the moderator's questions. She clearly has a degree from the George W. Bush school of debate. The fundamental principle of that school is "Say what you like, regardless of the question."
  • When asked about the causes of global warming she said she didn't want to talk about the causes, but then said the solution was cleaning up the planet and reducing pollution. I guess that means she thinks global warming is caused by humans after all. Just say it.
  • She is pointedly informal, and makes self deprecating remarks about her scant experience. The only case she makes for her qualification is basically that she has common sense. If George W. Bush has taught us anything, it is that any old dumbass can be president, but that dumbass presidents suck. Please, no more unqualified applicants for this position.