Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

Method resolution in Ruby : OR how to find Mr. Right method?

Before I begin: Some of the stuff below may be incorrect (Corrections welcome.), however it helps me understand ruby method resolution. I have come up with this model based on my reading of the RubyProgrammingLanguage book (must-read for anyone learning Ruby) by Flanagan & Matz and some tinkering.. it may not be accurate (if that is what you're after)


Basket of Facts:
  • A Ruby type is an object of type 'Class' (henceforth referred to as a class-object)
  • An object has a class attribute pointing to the right class-object.
  • An object has an "eigenclass"/"metaclass" that holds singleton methods (methods that are only available to that instance.)

class-objects are special in a few ways:
  • a class-object has its class attribute pointing to the 'Class' type in Ruby
  • a class-object has a superclass
  • the "eigenclass" of a class-object also has a superclass; the eigenclass of its superclass
  • a class-object has an included modules list
  • a class-object has a table of methods (instance methods defined in the class definition)
  • class methods are singleton methods of the class-object
  • a class-object is generally associated with a constant viz. it's class name.



So now let's get started. Don't let the following mouse-drawn picture scare you - it'll all make (some?) sense in sometime. In the meantime, right click on it and open it up in a new tab and have the picture handy for reference while you descend into this post.



Next jump right over this big puddle of source code. You can copy it into your editor if you want to try it out with me..

module Kernel
def breadcrumb
puts "Kernel#breadcrumb"
Module.nesting[0].instance_eval{ remove_method :breadcrumb }
end
end
class Object
def breadcrumb
puts "Object#breadcrumb"
Module.nesting[0].instance_eval{ remove_method :breadcrumb }
end
def self.breadcrumb
puts "Object.breadcrumb"
class << Module.nesting[0]
instance_eval { remove_method :breadcrumb }
end
end
end

module MyModule
def breadcrumb
puts "MyModule#breadcrumb"
Module.nesting[0].instance_eval{ remove_method :breadcrumb }
end

end

class Base
MY_CONST = :b
include MyModule

def breadcrumb
puts "Base#breadcrumb";
Base.class_eval{ remove_method :breadcrumb }
# Base.instance_eval{ remove_method :breadcrumb } UH-OH! why does this remove the inst method
end
def self.breadcrumb
puts "Base.breadcrumb"
class << Module.nesting[0]
instance_eval{ remove_method :breadcrumb}
end
end
end

class Derived < Base
def breadcrumb
puts "Derived#breadcrumb";
Module.nesting[0].instance_eval{ remove_method :breadcrumb }
end
def self.breadcrumb
puts "Derived.breadcrumb"
class << self
instance_eval{ remove_method :breadcrumb}
end
end
end

class Class
def breadcrumb
puts "Class#breadcrumb";
Class.class_eval{ remove_method :breadcrumb }
end
end
class Module
def breadcrumb
puts "Module#breadcrumb";
Module.class_eval{ remove_method :breadcrumb }
end
end



****LANDING ZONE****
Nice Jump!

I have defined a new class called 'Derived' which derives from 'Base' and includes 'MyModule'.
Unless a base type is explicitly specified, all classes derive from Object (which includes Kernel) (and derives from BasicObject - new in Ruby 1.9). e.g. Base derives from Object. This can be verified via


d = Derived.new
p d.class.ancestors # => [Derived, Base, MyModule, Object, Kernel, BasicObject]



Base and Derived each have defined a class method (self.breadcrumb) and an instance method (breadcrumb). MyModule just defines a module method of the same name.
Convention: Class/Singleton methods would print receiver.breadcrumb. Instance methods would print receiver#breadcrumb.

Take a deep breath and dive in...

First lets create a new instance of Derived and add a singleton method to it. (i.e. a method that is present only in that instance. Another instance of Derived would not have this method)
Here's how we do that...


d = Derived.new

class << d
def breadcrumb
puts "DerivedObj[id:%d].breadcrumb" % object_id
class << self
instance_eval{ remove_method :breadcrumb}
end
end
end


'Call once': once the method is called, the method disappears (is 'undefined'). This will help us uncover the order in Ruby tries to find a method. I have added such methods in all types up the hierarchy (and then some) to help us spot the trail.
(Note: Instance methods use class_eval to remove themselves. Singleton/class methods use instance_eval. Refer to the RPL book)

And now for the moment of truth


puts "Method call on an object".center(40, "*")
6.times{ d.breadcrumb }


Output:
********Method call on an object********
DerivedObj[id:6186928].breadcrumb
Derived#breadcrumb
Base#breadcrumb
MyModule#breadcrumb
Object#breadcrumb
Kernel#breadcrumb


Refer to the image (I was reading Why's Nobody know Shoes; which might explain some of the weird imagery and the helpfulness of it all ;) So here's what happened in the sequence illustrated by our 'call-once' breadcrumbs:

To the extreme left is our object d,
  • Call1: Find the eigenclass of d, look for a singleton method of that name. Found!
  • Call2: .. not found in eigenclass, find the class-object of d via the class attribute. Look in the methods defined in class Derived. Found!
  • Call3: .. not found in eigenclass/class. Look in included modules of D (most recently included one first).. there are none. Travel via the superclass portal to reach Base. Look for inst method. Found!
  • Call4: .. not found in eigen/class/incl module/superclass. Try included modules of superclass e.g. MyModule. Found!
  • Call5: .. not found in eigen/class-n-its-modules/superclass-n-its-modules. Go down the superclass portal again to reach Object. Look at instance methods. Found!
  • Call6: .. look in incl. modules of Object. Kernel. Found!



If you try calling the method again, Ruby would not be able to find a method of that name and would now try looking for a method_missing method (in the same manner as it tried looking for breadcrumb. However this time it is guaranteed to find it because Kernel has a method_missing implementation that raises the NoMethodError. Need to see it to believe it?


temp.rb:84:in `block in
': undefined method `breadcrumb' for # (NoMethodError)
from temp.rb:84:in `times'
from temp.rb:84:in `
'


So lets recap the steps.
1. Look in the eigenclass (golden arrow) for the class
2. If not found, traverse the class attribute (purple arrow) to find the class-object.
3. Look in the instance methods for this class
4. If not found, try included modules of the class (in reverse order of mixin)
5. If not found, check if we have a superclass (pink arrow). If yes, repeat steps 3-5 till we run out of superclasses
6. If still not found, go back to Step1 but this time look for a 'method_missing' method'


By the way, we just saw how instance methods are inherited. (Snuck that one on you.) If Derived doesn't define an instance method, all its ancestors are looked up in order ; inheritance works.

Take a break, have some lemonade before we set out again... Ready? Off we go again. This time instead of calling a method on an object, we call a class method. Comment the previous 2 lines...


puts "Method call on a class-object".center(40, "*")
7.times{ Derived.breadcrumb }


Output:
*****Method call on a class-object******
Derived.breadcrumb
Base.breadcrumb
Object.breadcrumb
Class#breadcrumb
Module#breadcrumb
Object#breadcrumb
Kernel#breadcrumb


Hey! we see some instance methods being invoked when we call a class method. What the... Deep breaths 1..2..3..pfffhoooo (u got to get the pfffhoooo right)

Let's see if our image can help us out, find the object pointed to by the constant Derived (3rd box from the left - first row)

  • Call1: Start at Derived. Follow the golden arrow to the Derived's eigenclass - Derived". Look for a singleton method. Found!
  • Call2: .. not found in eigenclass. But eigenclasses of class-objects have superclasses. Follow the golden arrow to Base's eigenclass. Found!
  • Call3: .. not found in Derived" or Base". But we still have a golden arrow to Object". Found!
  • Call4: .. not found in Derived"/Base"/Object".Try BasicObject" too. Not found. Now we're out of superclasses. Now we follow the purple arrow to the class-object of Derived i.e. Class. Look for instance methods defined there. Found!
  • Call5: .. not found in the eigenclass hierarchy or class-object. Try included modules in Class. not found. But wait, we have a superclass. Down the pink arrow to Module. Look for an instance method... found!
  • Call6: .. not found in eigenclass hierarchy/Class-n-included-modules/Module. Try included modules in Module. Nope. Down the pink arrow to Object. Found an instance method!
  • Call7: .. not found in eigenclass hierarchy/Class-n-incl-modules/Module-n-incl-modules/Object. Look in included modules of Object.. aha Kernel. Look for method.. found!



If you make one more call, you should now be guessing that a NoMethodError should show up from Kernel#method_missing(similar to the previous example)

So lets refactor the steps.
  1. Look for the method in the eigenclass (yellow arrow). Until we run out of superclasses for the eigenclass, inspect each eigenclass for the method.
  2. If not found, traverse the class attribute (purple arrow) to find the class-object.
  3. Look in the instance methods for this class
  4. If not found, try included modules of the class (in reverse order of mixin)
  5. If not found, check if we have a superclass (pink arrow). If yes, repeat steps 3-5 till we run out of superclasses
  6. If still not found, go back to Step1 but this time look for a 'method_missing' method

So the only new tidbits were
  • class methods are singleton methods of class-objects. They live in the green clouds tied with the yellow arrows to the class.
  • eigenclass of a class-object is special. These special 'green clouds' have superclasses.
And that is the secret behind class method inheritance... See the first three lines of the output. (Snuck another one.. {snicker} No more I promise.)

And that also explains how class method calls can result in instance methods being called. (Lines4-7)

And that's it folks! You will wake up enlightened or confused when I snap my fingers :)


As an exercise to people who like to stay and flex some meta-programming muscles. Try and explain this predicament.

Module defines a constants method, which returns the list of all known constants (incl. class names).
Module.constants => 102
Any user-defined class e.g. Base or Derived inherits this method ; however Base.constants returns only the constants defined inside Base (and its superclasses). e.g. If I add a constant inside Base.
Base.constants => 1
How does Module's constants behave differently in the Base (which inherits this functionality)?

Hint: Check Module.singleton_methods and Module.public instance_methods
Hint2: Hit Stackoverflow and search for the answer once you have taken a good crack at it.

How to compile ruby from source on Windows?

I wanted to play with the latest version of ruby. I got the win32 binary zip of 1.9.1 p0 - however irb used to popup an error message on startup - used it for sometime but then I decided to fix it. Checked back on ruby's download page and found an updated version p129. Downloaded it but it was a source zip.

I wasn't sure that compiling it from source would be trivial... however it reinforced the simplicity of the ruby way. Summarizing the steps I found on this post at ruby-forum.com

  1. Get the source zip from the download page and unzip it locally to say c:\ruby-src
  2. Open the Visual Studio Command prompt (I have VS2008 on WinXP SP2)
  3. Make a folder for your build say c:\ruby-build. Move into it / change dir
  4. c:\ruby-build> c:\ruby-src\ruby-1.9.1-p129\win32\configure.bat
  5. c:\ruby-build> nmake
  6. c:\ruby-build> nmake test
  7. c:\ruby-build> nmake DESTDIR=c:/ruby-build install
That's it.. you'll find the binaries within a usr folder within the ruby-build folder.

Ruby is slow... until you learn to write good code.

I wrote up a small program to find prime-numbers between x and y ; implemented the Sieve of Eratosthenes.
The SOE is basically where you take all numbers (say 1-100) into an array. Cross out 1. Loop from 2-Sqrt(N) i.e. 2-10. Now cross out every multiple of 2 in the array. Next cross out every multiple of 3.. and so on. Finally all the numbers that aren't crossed out are prime.

However I did a very simplistic implementation (test-driven) to boot. All the tests did pass.. however running it to get all primes between 10K and 100K took 34 secs to execute. And I was on the new n improved ruby 1.9 with a better VM. I switched to the stable ruby 1.8.6 and it took ~60 secs. So now I turned back to my primary Weapon C#. The same steps implemented in C# took 722 msecs. So now there were 2 possibilities
1. Ruby is sloooow (the usual gripe)
2. Something in my algorithm is messed up.

So I posted my source onto StackOverflow - (my current fave time sink :) to get some eyeballs to look at the problem.
So here's my naive implementation


class PrimeGenerator
def self.get_primes_between( x, y)
sieve_array = Array.new(y) {|index|
(index == 0 ? 0 : index+1)
}

position_when_we_can_stop_checking = Math.sqrt(y).to_i
(2..position_when_we_can_stop_checking).each{|factor|
sieve_array[(factor).. (y-1)].each{|number|
sieve_array[number-1] = 0 if isMultipleOf(number, factor)
}
}

sieve_array.select{|element|
( (element != 0) && ( (x..y).include? element) )
}
end
def self.isMultipleOf(x, y)
return (x % y) == 0
end
end


# Benchmarking code

require 'benchmark'
Benchmark.bm(30) do |r|
r.report("Gishu") { a = PrimeGenerator.get_primes_between( 1_000, 10_000) }
end



A few people got back with what might be the problem.
One of the first and most voted issue was the slicing the array to get a new subset array. That's got to be expensive inside a loop for an array of this size. Obviously I was using for loops in C# (since C# doesn't have Ruby's each)


for index in factor..y-1 do
sieve_array[index] = 0 if isMultipleOf(index+1, factor)
end


That shaved off 8 secs - 24%. But still 26 secs. Another optimization was to reduce iterations - instead of checking each number with IsMultiple(number, _loopVar), set the 'step' for the loop to equal _loopVar (so from 2 you go 4,6,8,10...)

And finally Mike Woodhouse dispatched a homer. He posted a tiny snippet that did the same thing in under 1 sec. No that's not a typo.


def sieve_to(n)
s = (0..n).to_a
s[0]=s[1]=nil
s.each do |p|
next unless p
break if p * p > n
(p*p).step(n, p) { |m| s[m] = nil }
end
s.compact
end

# Usage:
# puts sieve_to(11).select{|x| x>=3}.inspect



user system total real
Gishu 27.219000 0.000000 27.219000 ( 27.375000)
Mike 0.141000 0.000000 0.141000 ( 0.140625)

I then had to know how he did it..

  1. Smart#1: He skips if array[_loopvar] is already 0. whereas if array[6] is crossed out due to 3, I did a scanning run to set all multiples of 6 to 0 which were already 0.(now down to 6 secs)

  2. Smart#2: Use the Numeric#step trick to jump to the next multiple vs traversing entire list with an IsMultiple check (now down to 0.25 secs)

  3. Smart#3: He begins the inner loop with Square of p. e.g if _loopvar is 7, I used to check from 8->end. Mike checks from 49->end. This one was a little tricky to figure out.. the reason is 7x2, 7x3, 7x4, 7x5, 7x6 have already been crossed out when _loopVar was 2,3,4,5,6 respectively. (now down to 0.21 secs)

  4. Smart#4: Smart use of Ruby's nil for crossed out vs my choice of 0. That followed by Array#compact reduces the number of elements for the final select operation to get primes between x and y (now down to 0.18 secs.)



So after all that it's Ruby 0.15 secs and C# (with same optimizations) 0.028 secs, much more easy to take in.

An interesting thought to end this post is that even with the naive first implementation C# performed reasonably well... under a sec. I'd have moved on to the next task. Ruby on the other hand slowed to a crawl forcing me to take another look for optimizations. In a indirect sort of way, C#'s superior performance could be hiding bad/substandard solutions and letting programmers get away with it...

MergeSort in Ruby

Finally got the ball rolling on the 'Introduction to Algorithms' book.. What I figured was that I implement the sorting algorithms in ruby - help me get better at ruby AND algorithms.
I've got to confess.. I'm test-infected so I'm gonna TDD my way out. This series of posts are not going to be in a walkthrough style of narration.. I'm just going to post complete samples. The order of methods indicate how the code was built up.. (the first method is the 'oldest'.

Here's merge_sort_test.rb [The Test Fixture]
--
require 'test/unit'
require 'merge_sort'
class TestMergeSort < Test::Unit::TestCase
def test_partition_array_with_even_number_of_elements
left, right = MergeSort.partition_array [11, 5, 9, 15]
assert_equal [11, 5], left
assert_equal [9, 15], right
end
def test_partition_array_with_odd_number_of_elements
left, right = MergeSort.partition_array [11, 5, 45, 9, 15]
assert_equal [11, 5], left
assert_equal [45, 9, 15], right
end

def test_merge_arrays
assert_equal( [10,20,30,40],
MergeSort.merge_arrays( [10,30], [20,40] ) )

assert_equal( [1, 3, 5, 9],
MergeSort.merge_arrays( [1, 5, 9], [3] ) )
assert_equal( [1, 3, 5, 9],
MergeSort.merge_arrays( [3], [1,5,9] ) )
end

def test_sort
assert_equal [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
MergeSort.sort([ 8, 3, 4, 0, 9, 1, 2, 7, 6, 5])
end
end


--
merge_sort.rb [The Implementation]
--
class MergeSort 
def self.partition_array( input_array)
mid_index = (input_array.length / 2) - 1
[input_array[0..mid_index], input_array[(mid_index+1)..(input_array.length-1)]]
end

def self.merge_arrays(array1, array2)
merged_array = []

until (array1.empty? || array2.empty?) do
if (array1[0] < array2[0])
merged_array << array1.shift
else
merged_array << array2.shift
end
end

merged_array + array1 + array2
end

def self.sort( input_array )
return input_array if (input_array.length < 2)

left, right = self.partition_array input_array
sortedLeft = self.sort left
sortedRight = self.sort right
y = self.merge_arrays( sortedLeft, sortedRight )
end
end



--
A command line 'runner'/'main' function
--
# Console runner / main
if !ARGV[0].nil?
input = []
ARGV.each{|x| input << x.to_i}
print MergeSort.sort(input).inspect
end


--
>ruby merge_sort.rb 90, 100, 40, 10, 45
[10, 40, 45, 90, 100]

And we're done! IMHO The Ruby Array class really shines here w.r.t. readability.

Graphs in Ruby On Rails


Well I was building my personal expense tracking web app and soon enough I need to graph the top expense categories... A little searching led to gruff and it worked right outta the box.


First up, Gruff needs RMagick which needs ImageMagick. Tricky.. so go here and I hit Q#7
Soon you should land up
here download the rmagick-win32 gem zip. This archive has both RMagick gem and the install for the right version of ImageMagick. Installing the latest version of ImageMagick may not work.. (I didnt try.. the docs warned me off).
So install ImageMagick first. Next install the gem with 'gem install rmagick --local' in the folder where you unzipped the archive.
Now switch to your Rails Dev Environment. Open up config/environment.rb - Add this line at the end 'require gruff'
Add a new action to the controller of your choice.



  def showTopN
g = Gruff::Bar.new
g.title = "Hello Gruff"
g.theme_37signals

Expense.get_top_n_categories(:limit=>10).each{ |category|
g.data(category.name, category.total_amount.to_f)
}

send_data g.to_blob, :disposition=>'inline', :type=>'image/png', :filename=>'top_n.pdf'
end




Now close all command windows. Restart Webrick (your Rails Web Server) to take into account changes made to your PATH env var.
Fire up a browser.. in my case to http://localhost:3000/outflow/showTopN

Update:Normal people would be overjoyed at this point.. but not me. I wanted to create this graph at runtime .. So when I click on a link that I imagine will be there, I callback on my controller (yes using AJAX), create my graph and then slot it into a div tag on the same page. This will help me chart different sub-sets of data without leaving the page.
But how? so I post a question on stackoverflow
Solution:
report_controller.rb
require 'gruff'
class ReportController < ApplicationController
def showTopNCategories

end

def drawBarChart
g = Gruff::Bar.new
g.title = "Hello Gruff"
g.theme_37signals

Expense.get_top_n_categories(:limit=>10).each{ |category|
g.data(category.name, category.total_amount.to_f)
}
g.write('public/images/top_n.png')
render(:text=>'top_n.png')
end
end



showTopNCategories.rhtml


<%content_for("page_scripts") do -%>
function updateImg(id, imageFile)
{
$(id).innerHTML = '<img src="/images/' + imageFile + '"/>';
}
<% end -%>

<!-- some controls -->
<%= link_to_remote("Refresh Chart",
:complete => "updateImg('div_barchart', request.responseText)",
:url=>{:action=>'drawBarChart'})%>

<div id="div_barchart">

</div>



application.rhtml


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<!-- the usual -->

<script type="text/javascript"> <%= @content_for_page_scripts %> </script>
</head>
<!-- rest of the usual -->




The quirk here was that
  • gruff considers the Rails Project folder as the base folder.. so you need public/images/FileName.png as the parameter to g.write. Rails views however have public as the base folder. So image paths are relative.. /images/FileName.png
  • the javascript function didn't wire up correctly. It dies silently if you make a typo for instance. Use alert("Hit!"); liberally to know if the function is being hit.

Optional parameters in Ruby

Well I keep forgetting this. There's a way to pass in a hash of optional parameters as the last parameter to a method.. how do I do that? I'll save you some time. Here's how.

  def Expense.get_top_n_categories options={}
sQuery = "SELECT categories.name, sum(amount) as total_amount
from expenses
join categories on category_id = categories.id
group by category_id
order by total_amount desc
";
sQuery += " limit #{options[:limit].to_i}" if !options[:limit].nil?
Expense.find_by_sql(sQuery)
end

# Now you see a param.. top 10
obCategories = Expense.get_top_n_categories :limit => 10

# now you don't
obCategories = Expense.get_top_n_categories

The cleverness lies in defaulting the options param to an empty hash. Now you can pass any number of params using the param => value notation.. everything comes neatly packaged to you as an options hash.

How did you manage to get the code formatted so pretty?
http://blog.wolfman.com/articles/2006/05/26/howto-format-ruby-code-for-blogs

ShowMeTheMoney-9 - What a Row...Fixture

Its time to take on RowFixture.. A few seconds to limber up. OK. LET’S DO THIS..

Lets look at the Fitnesse Tables.. a little modding and executable specs ready

So the first table (from the prev posts) adds 3 credit entries and deletes the last one. The next table then queries the credit entries, expecting the right description and amount. The FIT Server would use the cell values to match the records/rows. The first column is blank – I’m expecting it to be filled with actual creation dates as advertised (also this field would not be used to match expected and actual rows) So we need to write the fixture code now..











file: ShowMeTheMoney/AcceptanceTests/get_inflow_records.rb

require File.dirname(__FILE__) + '/../test/test_helper'

require 'fit/row_fixture'

module AcceptanceTests
class GetInflowRecords < Fit::RowFixture
def query
recordSet = []
Credit.find(:all, :order => 'id').each{|creditEntry|
recordSet << \
InflowRecord.new(creditEntry.created_at, creditEntry.description, creditEntry.amount )
recordSet
}

end

def get_target_class
InflowRecord
end

end
end


class InflowRecord
attr_reader :received_on, :description, :amount

def initialize dtReceivedOn, sDescription, fAmount
@created_at = dtReceivedOn
@description = sDescription
@amount = fAmount
puts @amount
end
@@recordMetaData = {
'received_on' => Date,
'description' => String ,
'amount' => Float }
def InflowRecord.metadata
@@recordMetaData
end
end



Now let me explain how I understand this thing works. RowFixture classes need to query a set of records and match it with an expected set of records, which are specified in the Fitnesse table. It uses the values in the table to perform the matching.
  • The query method of the fixture should be overridden to obtain a set of records. i.e. we use the Credit model class to retrieve the records, wrap them into InflowRecord objects and stuff them into the output array
  • The get_target_class method identifies the type of objects in the set. i.e. InflowRecord here.
  • The InflowRecord class ( more of a data-struct ) should have reader methods to query individual attributes and a metadata static/class method to help the fixture map the attributes to data types.
I have translated Credit’s created_at attribute into InflowRecord’s received_on because it makes more sense in the table. Also the received_on column is blank. The values cannot be predicted and hence should not be used to compare the records. I expect the blank cells to be filled with the current attribute values as advertised in the docs/examples
And here comes RubyFit storming out of the corner..


  • The next row in the test-output is more of the same. For some reason it is trying to call received_on on the Credit instance, even though I am giving it a hand-made InflowRecord object to play with.
  • The blank rows show up as “missing” and surplus records are added to the table.
After some cajoling, no luck.. won’t happen. I secede. I change the column name in the table to ‘created_at’. Find&Replace in the fixture code AND some thing that fits in one screenful this time.

So, none of the blank cell filling goodness for me. After some time trying to find out why; I decide to drop that column from the table..








The Green glow almost makes up for the fact that my arms feel like Dr. Reed from all the arm twisting by Fit. What a Row!! Good news is that I can mark my tasks as complete. THE STORY IS DONE.. YAY!!... Well except for the UI Design task. But that would be just a little of view html tweaking …. I hope (GULP!)

Lets update our chart, we got 3 ideal hours worth of work done. One more day to go in the iteration.. we are no where close to our initial estimates... However this is our first iteration - so some uncertainity is to be expected.

Show Me The Money-4 - Light at the end of the AccTest Tunnel

Iteration#1 : Day 3/10

The day is lost.. after quite a bit of grappling with RubyFit and Fitnesse.. all I get is 2 Exceptions and some command line output captured.. and I don’t see what the problem is. Error reporting has scope and need for improvement.
[I switch OFF the project clock. Overtime.. just this once!!]
Time to hit the forums after lots of fiddling with the paths and what nots.. (Days pass waiting for someone to answer … no answers.)


Let me go over the material that I have again with a fine-comb.
First stop Ron’s article. Most of it looks ok.. I’m doing it right. Wait.. the wise ron has worked in small steps.. He has a small wiki table to test something that comes along with the Ruby Fit Server implementation. So if that thing turns out green, I can be assured that Fitnesse is able to talk to my copy of Ruby FitServer. So I copy the table right out of Ron’s article and paste it into my Fitnesse test page..
!define COMMAND_PATTERN {ruby -v -I %p "L:/ruby_home/lib/ruby/gems/1.8/gems/fit-1.1/bin/FitServer.rb"}
!path "L:/Gishu/Ruby/ShowMeTheMoney"

!|eg.ArithmeticFixture|
|x|y|+|-|*|/|
|1|1|2|0|1|1|

!|AcceptanceTests.TestTrackInflows|
|amountReceived|currentBalance()|
|100|100|
|500|600|
|1200|1800|

Save and Click the test button and lets see what happens.. fingers crossed.

Now you tell me!! As we can see the ArithmeticFixture comes out all green.. conclusion Ruby FitServer is reachable. Now all of a sudden, we have all the information telling us what is wrong.. Strange. Spend another 20 mins trying to make it see the fixture by twisting the path directive this way and that … editing FitServer.rb to print out Ruby’s $LOAD_PATH variable to see if my directory has made it to the FitServer. All OK.. but “no cigar”.

I take a crack at it the next day. I scan material on the Ruby FitServer’s home page http://fit.rubyforge.org/basics.html
and BAM!
“In RubyFIT, that also is the name of the file containing the fixture, where the camel cased words are lied without capital letters and separated by underscores. Programmers can start creating a file named calculate_discount.rb and writing the skeleton of the CalculateDiscount fixture:”

Both Ron and Cory used one-word fixture names like People and Division… they don’t call em experts for nothing. RubyFit like Rails uses this internal conversion scheme of converting camelCase words to camel_case words
So I quickly go and rename my Ruby File from TestTrackInflows to test_track_inflows.rb
Click Test and now I get an error saying that my method ‘amount_received=’ is undefined. Ahh.. more of the same. Change the method names so that we have…

require 'fit/column_fixture'

module AcceptanceTests

class TestTrackInflows < Fit::ColumnFixture
attr_reader :balance
attr_accessor :amount_received
def initialize
@balance = 0
end
def current_balance
@balance
end
end

end


Re-test and we have red-hot ignition! WooHoo… please excuse me while I do that once again. WOO HOO !!!

It’ll be a while till I glue my application code to the test-table. So we’ll fake out the first result and see if we get a small green.. just to keep our spirits high. Sneaky tweak to the fixture method

def current_balance
#@balance
100
end
Sure enough!! We have a green. Rollback that temp hack.

Its time.. to do the victory jig.

Update:

Iteration#1 : Day 4/10

I’m penalizing one day to reflect reality. I did spend an awful amount of time trying to get all this running.






Show Me The Money - 3 - Fitnesse + Fit + Ruby oh my!

Iteration#1 Day 2/10

So lets start with Task : writing the acceptance tests for story#6 - tracking inflows
Voice of Customer (VOC) : Basically I'd be getting bags of money from different sources and it should add up to my current balance
Is that detailed enough? Lets find out by writing some executable specs a.k.a. acceptance tests to expose any hidden reqs. For that we need to get Fitnesse running with Ruby. That's another non-story task that we have. We already have fitnesse downloaded last time. Now we need to install a FitServer for Ruby.
  • d:\>gem install fit
I start the Fitnesse server by double clicking my (port- modified) run.bat file under the Fitnesse folder. Fire up a browser and verify it is up - http://localhost:2345

Next create a Main page for our Acc tests. Edit the URL to "http//localhost:2345/ShowMeTheMoneyMain"

Now click on the link show to create this page.
Now I need to add a comment - a quick search gets me this page. Yes the Fitnesse wiki has a Search button on the left just like Edit.. its not seen on the online version (to prevent web spiders apps) but its activated on your local copy of the wiki pages. I need to prefix the line with a # for a comment. Lets call the first page. TestsForTrackingInflowsS6. So we type that in and hit Save.
#This is the Main Page with links to all the acceptance tests for the app to track my money better
#
# ShowMeTheMoney
# ^TestsForTrackingInflowsS6
Now before we create the TestsForTrackingInflowS6 subwiki page, We need to mark this page as a Test Suite. We do that by opening up the page Properties (Button on the left), check the Suite property and click ‘Save Properties’. Now we click on the question mark link after the TestsForTrackingInflowsS6 to create the subwiki page. Here’s where we start writing our tests. VOC: Well its pretty simple, Every time I receive an amount, my current balance must be incremented by the same amount. Dev: Let me quickly write that up
!|TestTrackInflows|
!|AcceptanceTests.TestTrackInflows|
|amountReceived|currentBalance()|
|100|100|
|500|600|
|1200|1800|
Dev: Is this what we mean ?
VOC: Yup.

Ok so I save the page. Bid adieu to the customer for now.
We also need to check the Test property in Page Properties. Wait a sec, its already showing a Test button – seems like subwiki pages under a suite page have their Test property set by default... Cool. Lets try clicking that shiny blue button.
Dirty Yellow. "Could not find fixture : AccTestTrackInflows"

Ok. So lets create that ruby fixture – the glue between the wiki tests and our application code. A ColumnFixture should do the trick. Hmm.. seems like the
Ruby variant has some quirks of its own.
  • We need to rename the fixture as X.Y e.g.AcceptanceTests.TestTrackInflows.
  • Create a directory called X e.g. AcceptanceTests and place the ruby class file under it.
  • The class should be within a module under the same name as the parent folder - X has to be a module and Y a class within the module as seen in the example below
So we do all that. Phew! I create a folder under the rails project dir. Open up SCITE and create the fixture class - L:\Gishu\Ruby\ShowMeTheMoney\AcceptanceTests\TestTrackInflows.rb

require 'fit/column_fixture'

module AcceptanceTests

class TestTrackInflows < Fit::ColumnFixture
attr_reader :balance
attr_accessor :amountReceived
def initialize
@balance = 0
end
def currentBalance
@balance
end
end

end


Next we need to make Fitnesse run the Ruby Fit Server instead of the default one that talks Java. This is the page we need to read for details. (Thanks Andy D.)
We need to define the COMMAND_PATTERN as follows
Now I need to help the Wiki page find the fixture. I do that with the following directive at the top of the TestTrackingInflowsS5 page.
!define COMMAND_PATTERN {ruby -I %p "L:\ruby_home\lib\ruby\gems\1.8\gems\fit-1.1\bin\FitServer.rb" -v}
!path "L:\Gishu\Ruby\ShowMeTheMoney"

But the darn thing won’t work.

[Update: Cory Foy found out that the -v option must be at the "end" of the COMMAND pattern. See http://www.nabble.com/Re%3A-Re%3A-Trouble-getting-Fitnesse-to-play-with-Ruby-FIT-server-p14782769.html
]









Show Me The Money - 2

Iteration Day 1/10
First step we need some version control and a CIS...
the book mentioned a project named DamageControl but that looks like it has closed shop for now. Some more surfing... CIA.. Cerebrus.. CruiseControl.rb aha!
I download the latest cruisecontrolrb-1.2.1.tgz, that along with the Getting Started page
was all that I needed.
I created a rails project on my machine with
  • l:\Gishu\Ruby>rails ShowMeTheMoney
then used Tortoise SVN to import the folder into the repository. You could also do it manually with
  • l:\Gishu\Ruby\ShowMeTheMoney> svn import -m "First Rails App into Version Control" svn://babybox/ShowMeTheMoney/trunk
Next I add a proj to cruisecontrol.rb with
  • D:\cruisecontrolrb-1.2.1>cruise add ShowMeTheMoney --url svn://babybox/ShowMeTheMoney/trunk
and start up the webrick server with
  • D:\cruisecontrolrb-1.2.1>cruise start
Yup that's what it says.. should work.. but didn’t. WWOOTB – won’t work out of the box.
Crazy gem of activesupport won't load.. seems like it wont load the gem version that comes along with cruisecontrol.rb.. Precious time is lost reinstalling various components, trying mailing lists and basically trying to beat the thing into submission.

[Many days later.. inertia is a leech]
And I make the mistake of waiting for cc.rb to be operational. Suddenly one day on my way back from work.. I have a pair of stunning revelations just like that
#1: There is nothing to build like in C#/Java
#2: I only need a CIS to run all the tests and inform me if someone breaks the build. Since I'm the only one working, I just have to watch myself.

I can proceed without cc.rb working right now. Now wiser and 1 day of our iteration time later, I trudge on to start ‘delivering value’.. we gotta start with that sometime soon if we want to be 'agile'.

[Alexey V. spent some time trying to get a handle on this thing.. ruby versions, gem versions, stack traces,... all in vain.]

Test driving Conway's Game of Life in Ruby : Part 2 of 2

Contd... Part 2
Do the evolution (with due apologies to Pearl Jam)
Let’s model our simulation with a file aptly titled ‘GameOfLife.rb’

Rule#1 : Any live cell with fewer than two live neighbours dies, as if by loneliness.
Red
Create new test case class

ts_GameOfLife.rb
require 'test/unit'
require 'Colony'
require 'GameOfLife'

class Test_GameOfLife < Test::Unit::TestCase

def test_Evolve_Loneliness
@obColony = Colony.readFromFile("Colony1.txt");
@obColony = GameOfLife.evolve(@obColony)
assert( !@obColony.isCellAlive(1, 0) )
assert( @obColony.isCellAlive(1, 1) )
assert( !@obColony.isCellAlive(2, 2) )
end
end


Since we want to run all the tests from multiple files now, we can create a “test suite” It is as easy as creating a new file named ts_GameOfLife.rb.
Naming conventions : Prefix ts_ for test suites..
ts_GameOfLife.rb
require 'test/unit'
require 'tc_Colony'
require 'tc_GameOfLife'


We can then run the test suite with
ruby ts_GameOfLife.rb

Green
We traverse each cell in the colony and kill the cell if it is alive
class GameOfLife

def evolve ( obColony )
(0..maxRows).each{ |iRow|
(0..maxCols).each{ |iCol|
killCellIfLonely(obColony, iRow, iCol)
}
}
return obColony;
end

private
def killCellIfLonely(obColony, iRow, iCol)
if (obColony.isCellAlive(iRow, iCol) && ( obColony.getLiveNeighbourCount( iRow, iCol ) < 2 ) )
obColony.markCell( iRow, iCol, false )
end
end

end


Detour
Hmm but we don’t have getLiveNeighbourCount() and markCell(). Let’s test drive
Small red

tc_Colony.rb
def test_GetLiveNeighbourCount()
assert_equal( 2, @obColony.getLiveNeighbourCount(0,0) )
assert_equal( 0, @obColony.getLiveNeighbourCount(0,3) )
assert_equal( 1, @obColony.getLiveNeighbourCount(1,3) )
assert_equal( 0, @obColony.getLiveNeighbourCount(2,3) )
end


Small green
Colony.rb
def getLiveNeighbourCount( iRow, iCol )
iCountOfLiveNeighbours = 0

obCoordinatesToExamine = [
[iRow-1, iCol-1], [iRow-1, iCol], [iRow-1, iCol+1],
[iRow, iCol-1], [iRow, iCol+1],
[iRow+1, iCol-1], [iRow+1, iCol ], [iRow+1, iCol+1] ]

obCoordinatesToExamine.each{ |curRow, curCol|
iCountOfLiveNeighbours = iCountOfLiveNeighbours+1 if (isCellInBounds(curRow, curCol) && isCellAlive(curRow, curCol))
}
return iCountOfLiveNeighbours
end

def isCellInBounds( iRow, iCol)
return ((0...@maxRows) === iRow) &amp;& ((0…@maxCols) === iCol)
end


Small Red
def test_MarkCell()
assert( !@obColony.isCellAlive(0,0) )

@obColony.markCell( 0, 0, true )
assert( @obColony.isCellAlive(0,0), "Should have born now!" )

@obColony.markCell( 0, 0, false )
assert( !@obColony.isCellAlive(0,0), "Should have died now!" )
end


Small Green
def markCell( iRow, iCol, bIsCellAlive )
@colonyGrid[iRow][iCol] = ( bIsCellAlive ? "1" : "0" )
return
end


Refactor
I see some duplication with “1” and “0” – but I’ll refactor with Strike 3.

Hmmm still Red is my evolve test case. Had to use a couple of traces to find the bugger .. I left out a dot in the bold section below. 2 dots – inclusive range, 3 dots – max bound is not part of the range.

def evolve ( obColony )
obNewColony = obColony.clone()

(0...obColony.maxRows).each{ |iRow|
(0...obColony.maxCols).each{ |iCol|
#print "Count = " + obColonySnapshot.getLiveNeighboursCount( iRow, iCol ).to_s
if ( obColony.getLiveNeighboursCount( iRow, iCol ) < 2 )
obNewColony.markCell( iRow, iCol, false )
end
}
}

return obNewColony;
end

Colony.rb
def clone()
#return Colony.new( @maxRows, @maxCols, Array.new(@colonyGrid) )
return Colony.new( @maxRows, @maxCols, @colonyGrid.map{ |elem| elem.clone } )
end


Refactor

Well I got bit with the ranges. I used 0..obColony.maxRows instead of 0…obColony.maxRows and it blew up my test. I think traversing the Colony should be handled / provided by the colony itself. I don’t want anyone making the same mistake.

Let’s move that into a ruby iterator style Colony.each() method. So here it is
Colony.rb
def each
(0...@maxRows).each{ |iRow|
(0...@maxCols).each{ |iCol|
yield iRow, iCol
}
}
end


so now I can use this in GameOfLife#evolve, Colony#to_s and Test_Colony#test_IsCellAlive. Code is even smaller!!

def evolve ( obColony )
obNewColony = obColony.clone()

obColony.each{ |iRow, iCol|
if ( obColony.getLiveNeighboursCount( iRow, iCol ) < 2 )
obNewColony.markCell( iRow, iCol, false )
end
}

return obNewColony;
end

Colony.rb
def to_s
sGridListing = ""
each{ |iRow, iCol|
sGridListing += ( isCellAlive( iRow, iCol) ? "@ " : ". " )
sGridListing += "\n" if iCol.succ == @maxCols
}
return sGridListing
end


Next we need a test for Colony#clone that we sneakily added in to fix this test. We need to check if the cloned colony is similar to the original ..
tc_Colony.rb
def test_Clone
clonedColony = @obColony.clone()
assert_not_equal( clonedColony.object_id, @obColony.object_id, "Cloned object is the same as original" )

@obColony.each{ |iRow, iCol|
assert_equal( @obColony.isCellAlive(iRow, iCol),
clonedColony.isCellAlive(iRow, iCol),
"Contents differ at " + iRow.to_s + ", " + iCol.to_s )
}
end


Ok let’s take a look at our next rule.
Rule#2 - Any live cell with more than three live neighbours dies, as if by overcrowding.
Let’s create a test colony for this rule.

Colony_Overcrowding.txt
3, 4
0 0 1 0
1 1 1 1
0 0 1 1


Red
tc_GameOfLife.rb
def test_Evolve_Overcrowding
arrExpected = [false, false, true, false,
false, false, false, false,
false, false, false, true]

@obColony = Colony.readFromFile("Colony_Overcrowding.txt");
@obColony = GameOfLife.new.evolve(@obColony)

iLooper = 0
@obColony.each{ |iRow, iCol|
assert_equal( arrExpected[iLooper], @obColony.isCellAlive(iRow, iCol),
"Mismatch at " + iRow.to_s + ", " + iCol.to_s )
iLooper += 1
}
end


Green
Ok so we kill em off even if there are too many of them in an area ! Just another OR clause …
def evolve ( obColony )
obNewColony = obColony.clone()

obColony.each{ |iRow, iCol|
if obColony.isCellAlive(iRow, iCol) and
( (obColony.getLiveNeighboursCount(iRow,iCol) < 2) or (obColony.getLiveNeighboursCount(iRow,iCol) > 3) )
obNewColony.markCell( iRow, iCol, false )
end
}
return obNewColony;
end



Rule#3 - Any dead cell with exactly three live neighbours comes to life.
Red

Colony_Birth.txt
5, 5
0 0 0 0 1
1 1 0 0 1
0 0 1 0 0
0 0 1 0 0
0 0 1 0 0

tc_GameOfLife.rb
def test_Evolve_Birth
arrExpected = [false, false, false, false, false,
false, true, false, true, false,
false, false, true, true, false,
false, true, true, true, false,
false, false,false, false, false
]

@obColony = Colony.readFromFile("Colony_Birth.txt");
@obColony = GameOfLife.new.evolve(@obColony)

iLooper = 0
@obColony.each{ |iRow, iCol|
assert_equal( arrExpected[iLooper], @obColony.isCellAlive(iRow, iCol),
"Mismatch at " + iRow.to_s + ", " + iCol.to_s )
iLooper += 1
}
end


Green
GameOfLife.rb
def evolve ( obColony )
obNewColony = obColony.clone()

obColony.each{ |iRow, iCol|
if obColony.isCellAlive(iRow, iCol)
if ( (obColony.getLiveNeighboursCount(iRow,iCol) < 2) or (obColony.getLiveNeighboursCount(iRow,iCol) > 3) ) then
obNewColony.markCell( iRow, iCol, false )
end
else
obNewColony.markCell( iRow, iCol, true ) if ( obColony.getLiveNeighboursCount(iRow,iCol) == 3 )
end
}
return obNewColony;
end


Hmm… that broke my overcrowding test. Oh yeah, as per the new rule, there is a newborn in that colony. Let me just update the expected results as

arrExpected = [false, false, true, true,
false, false, false, false,
false, false, false, true]

Refactor
The tests are duplicating code to traverse and check the Colony with the expected results/array. Extract method
private
def assert_colonies_are_similar( arrExpectedValues, obColony )
iLooper = 0
obColony.each{ |iRow, iCol|
assert_equal( arrExpectedValues[iLooper], obColony.isCellAlive(iRow, iCol),
"Mismatch at " + iRow.to_s + ", " + iCol.to_s )
iLooper += 1
}
end


refactored test looks like :
def test_Evolve_Overcrowding
arrExpected = [false, false, true, true,
false, false, false, false,
false, false, false, true]

obColony = Colony.readFromFile("Colony_Overcrowding.txt");

assert_colonies_are_similar( arrExpected, GameOfLife.new.evolve(obColony) )
end


And that’s a wrap !!
Finally I quickly write up what I learn is called a “Driver”
ARGV is an array that contains the command line parameters. I’ll take two.. thank you!
One for the input colony text file and the second for the number of generations.
We then just evolve and keep printing the colony.

if (ARGV.length != 2) then
puts "Usage : ruby GameOfLife.rb [ColonyTextFile] [No of generations]"
exit
end
if (!File.exist?(ARGV[0])) then
puts "Specified Colony File does not exist"
exit
end


obColony = Colony.readFromFile(ARGV[0]);

0.upto(ARGV[1].to_i) { |iGenNo|
puts obColony.to_s + "Gen:" + iGenNo.to_s

obColony = GameOfLife.new.evolve(obColony)
}

Run it as
L:\Gishu\Ruby\GameOfLife>ruby GameOfLife.rb TestColony.txt 100

You can also redirect output to a file
L:\Gishu\Ruby\GameOfLife>ruby GameOfLife.rb TestColony.txt 100 > a.txt


Open it up in Notepad for example… resize the height such that you can see one line below the Gen : X line. Now use the PgUp or PgDown keys to see evolution in action!!

By the way the TestColony.txt file contains the colony called Gosper’s glider gun. Watch the output and read the wiki to know why ! 

Hey this program is slow for 100+ generations.. I am at chapter 2 and find that there is another better way to implement this… a different perspective on this problem that cuts down the grid traversal for better performance.

Till next time….
Gishu
Feb 18, 2006

[Update : ]
A special Thanks to http://blog.wolfman.com/articles/2006/05/26/howto-format-ruby-code-for-blogs
for a good tip and an even cooler ruby script on pretty-formatting ruby code for html display.

Also the ruby-185-21 windows package seems to be broken. the gem install fails with a
"getaddrinfo: no address associated with hostname". I went back to 1.8.2 to get this done today !