Saturday, November 13, 2010

Running Jasmine BDD Specs in the Terminal

I had a pretty bad week with Ruby on Windows 7 64 bit: I tried to set up DBI and ODBC on my work machine but I did not have any luck with that.

I needed something to feel good about, so I decided to set up Jasmine BDD spec execution in the terminal on OS X.

I downloaded the standalone zip file from Jasmine's web site and made sure that the sample specs are executing fine with the SpecRunner.html file in the browser.


I wanted to execute the exact same specs but instead of running it in the browser, I wanted to do it in terminal.

Michael Hines' blog post was a pretty good starting point. He used JazzMoney, so I tried it myself.

I easily found JazzMoney's installation instructions on their github page.


It has prerequisites: I had to install harmony first.

I picked ruby-1.9.2-head from RVM, created a new gemset called "jasmine" and I got started.
Harmony has dependencies as well, I had to get stackdeck and johnson before I installed harmony.
$ gem install stackdeck
$ gem install johnson -v "2.0.0.pre3"
This is where it turned ugly. Stackdeck got installed fine, but johnson had some issues.

Building native extensions. This could take a while...
ERROR: Error installing johnson:
ERROR: Failed to build gem native extension.

After Googling the error I found out that johnson is not playing nice with Ruby beyond 1.8.7. I went back to RVM and started out by installing a new version of Ruby.
Here is what I did:
$ rvm install 1.8.7-p249
$ rvm 1.8.7-p249
$ rvm gemset create jasmine
$ rvm 1.8.7-p249@jasmine // switched to jasmine gemset
$ gem install stackdeck
$ gem install johnson -v "2.0.0.pre3"
$ gem install harmony // I tested harmony with a quick test in IRB, worked fine
$ gem install jazz_money
I did not have any problems with installing the gems under 1.8.7.

I had to create a Ruby script that sets up the test suite and this is the file I ran in the terminal. My run_specs.rb file was placed into the root folder right next to SpecRunner.html:
require 'rubygems'
require 'jazz_money'

javascript_files = [
  'spec/SpecHelper.js',
  'spec/PlayerSpec.js'
]

jasmine_spec_files = [
  'src/Player.js',
  'src/Song.js'
]

JazzMoney::Runner.new(javascript_files, jasmine_spec_files).call
I ran the file with the following parameters:
$  ruby run_specs.rb -f n -c
Success! This is the output I received in the terminal:


I did one more thing: I created a shell script, this way I did not have to remember all the command line arguments.
I saved this in the specrunner.sh file:
ruby run_specs.rb -f n -c
I can now invoke my specs by running "sh specrunner.sh" in the terminal.

Tuesday, November 9, 2010

JavaScript Closures for DRY-ing Up The Logic

Our #Hackibou today was a real blast. We focused on JavaScript development using the great Jasmine BDD framework. Since most of us were a little rusty on JS, we decided to start with something very simple: a Calculator. Our task was to develop a calculator that accepts an array of integer numbers in its add(), subtract(), multiply() and divide() functions.

We started out with a couple of very simple specs:
describe("Calculator", function() {
  var calculator;
  beforeEach(function() {
   calculator = new Calculator();
  });

  describe("Arithmetic operations on an array input", function() {
   it("creates a new Calculator object", function() {
    expect(calculator).not.toBeNull();
   });

   it("adds two numbers together", function() {
    expect(calculator.add([1,0])).toEqual(1);
   });

   it("adds three numbers together", function() {
    expect(calculator.add([1,2,3])).toEqual(6);
   });

   it("multiplies two numbers", function(){
    expect(calculator.multiply([1,2])).toEqual(2);
   });
  });
});
And our - not too elegant - solution was this:
function Calculator() {
  this.add = function(input) {
    var result = 0;
    for(i = 0; i<input.length; ++i) {
      result += input[i];
    }
    return result;
  }

  this.multiply = function(input) {
    var result = 1;
    for(i=0; i<input.length; ++i) {
      result *= input[i];
    }
    return result;
  }
}
Look at the code above. 90% of the code is duplicated there. One of us suggested assigning the first element of the array to the result right on the declaration. With this change the only difference between the two functions is the operation. One uses addition and the other multiplication. I played with JS closures a little bit before, so I proposed this:
function Calculator() {
 var operator = function(result, input) { return result + input; };
 this.add = function(input){
  return operation(input, operator);
 };

 this.multiply = function(input){
  return operation(input, function(result, input){return result*input;});
 }

 function operation(input, operator) {
  var result = input[0];
  for(i = 1; i < input.length; i++){
   result = operator(result, input[i]);
  }
  return result;
 }
}
Check out the operation() function. It uses two parameters, the first one is the array of integers and the other is a function object that holds the calculation logic. It's invoked on line 14. The variable result is both passed in as the first input and is assigned as the result of the function call. One of us suggested using the shift() function on the input array, this way we did not have to start our for loop with the second element of the array. Our operation() function now looked like this:
function operation(input, operator) {
 var result = input.shift();
 for(i = 0; i < input.length; i++){
  result = operator(result, input[i]);
 }
 return result;
}
Adding subtraction and division was very simple:
this.subtract = function(input){
  return operation(input, function(result, input){return result-input;});
 }

 this.divide = function(input){
  return operation(input, function(result, input){return result/input;});
 }
Please note that there is no if statement in the Calculator object.

Our final solution can be found in this gist.