Hi again!

Here is one silly idea for tree-like data traversal with Ruby.
Following three files (if attachments works) are:
  - navigator.rb - the main file and implementation
  - garden.rb - silly test environment
  - NavigatorTests.rb - Lapidary unit tests for navigator.rb

Test environment contains following hierarchy:
Earth -> Garden -> Tree -> Branch -> Leaf ( -> and bugs )

Idea is this. If we have handle for one leaf, we could use
that leaf to find out all leaves in every garden on earth
using following Ruby command:
	
	leaves = leaf.earthGardensTreesBranchesLeaves
	leaves.each { |found| puts found.to_s }

[Or filter what ever intrests us!]

Or if gardens have weather accessors, we could change
weather in every garden, just using one leaf:

	leaf.gardensWeather = 'Sunny'

-Jippo

In our series of silly examples, we just saw episode: silly examples.

-- 
 __________________________________________________________________
[   pub 1024/951AFAF5 1995/12/12   Juha Pohjalainen <jmp / iki.fi>   ]
[   fingerprint 41 56 7F F9 8E EC 16 35  BB 42 EF A7 DF 19 FA 31   ]
[__ http://www.iki.fi/jmp/ ________________ GSM +358 40 570 1179 __]
#!/usr/local/bin/ruby

module Navigator
  attr_accessor :container
  def tryturn value
    yield value if value
  end
  def breakMethodName name
    parts = name.to_s.split /([A-Z]{1}[a-z0-9=]+)/
    parts.collect! { |item| item.downcase if not item.empty? }
    return parts.compact
  end
  def shortname
    allParts = type.to_s.split ':'
    return allParts[-1].downcase
  end
  def localHandler target, name, parameters
    return nil if not target or not name
    return target.method( name ).call( *parameters ).to_a if target.respond_to? name
    return target.to_a if target.respond_to? :shortname and name == target.shortname
  end
  def containerFinder target, name, parameters
    return( localHandler( target, name, parameters ) ||
	   ( containerFinder target.container, name, parameters if target.respond_to? :container ) )
  end
  def commonFinder group, workingResult = [ ]
    results = [ ]
    group.each do 
      |currentTarget|
      workingResult = yield workingResult, currentTarget
      workingResult.to_a.each { |item| results.push item if item }
    end
    return results
  end
  def partFinder targets, name, parameters
    return commonFinder( targets ) { |ignored, target| localHandler target, name, parameters }
  end
  def handleFirstPart name, parameters
    tryturn( containerFinder self, name, parameters ) {|t| return t}
    throw :rollback
  end
  def handleMiddleParts targets, parts
    return targets if not parts or parts.empty?
    return commonFinder( parts, targets ) { |result, target| partFinder result, target, nil }
  end
  def method_missing methodName, *parameters
    catch :rollback do
      parts = breakMethodName methodName
      return handleFirstPart parts.shift, parameters if parts.length == 1
      targets = handleFirstPart parts.shift, nil
      lastPart = parts.pop
      targets = handleMiddleParts targets, parts
      return partFinder targets, lastPart, parameters
    end
    raise NameError, "can not navigate '#{methodName}' for '#{self}'"
  end
end
#!/usr/local/bin/ruby

require 'navigator'

module BigGarden
  class Earth
    include Navigator
    attr_reader :gardens
    def echo text
      ucase = text.upcase
      return "ECHO: '#{text}' #{text} #{ucase}"
    end
    def initialize
      @gardens = Array.new
    end
    def addGarden target
      @gardens.push target
    end
  end
  
  class Garden
    include Navigator
    attr_reader :trees
    attr_accessor :name
    def initialize( earth, name )
      @trees = Array.new
      earth.addGarden self
      @container = earth
      @name = name
    end
    def addTree target
      @trees.push target
    end
    
    def worms one, two, three
      "Here we are at #{@name}! Worms #{one}, #{two} and little #{three}."
    end
  end
  
  class Tree
    include Navigator
    attr_reader :branches
    def initialize( garden )
      @branches = Array.new
      garden.addTree self
      @container = garden
    end
    def addBranch target
      @branches.push target
    end
  end
  
  class Branch
    include Navigator
    attr_reader :leaves
    def initialize( tree )
      @leaves = Array.new
      tree.addBranch self
      self.container = tree
    end
    def addLeaf target
      @leaves.push target
    end
  end
  
  class Leaf
    attr_accessor :bugs
    include Navigator
    def initialize( branch )
      branch.addLeaf self
      self.container = branch
    end
  end
end
#!/usr/local/bin/ruby

require 'Lapidary/TestCase'
require 'Lapidary/UI/Console/TestRunner'

require 'garden'

class NavigatorTests < Lapidary::TestCase
  def setup
    @earth = BigGarden::Earth.new

    # first anonymous garden
    garden = BigGarden::Garden.new @earth, 'First'
    tree = BigGarden::Tree.new garden
    branch = BigGarden::Branch.new tree
    BigGarden::Leaf.new branch
    BigGarden::Leaf.new branch
    BigGarden::Leaf.new branch

    # second garden, which is known
    @garden = BigGarden::Garden.new @earth, 'Second'
    @tree = BigGarden::Tree.new @garden
    @branch = BigGarden::Branch.new @tree
    @leaf = BigGarden::Leaf.new @branch

    @targets = [ @earth, @garden, @tree, @branch, @leaf ]
  end

  def testNotFound
    assertRaises( NameError, 'dummies' ) { @leaf.dummies }
    assertRaises( NameError, 'dummiesFound' ) { @leaf.dummiesFound }
    assertRaises( NameError, 'dummiesMummiesFound' ) { @leaf.dummiesMummiesFound }
  end

  # this is a very silly test, but possible, but do not do this, PLEASE
  def testSillyTest
    assertEqual 8, @earth.gardensGardenGardenGardenGardenName.length
  end

  # with this test, you can change names of all gardens
  def testNameChange
    assertEqual ['First','Second'], @earth.gardensName
    @leaf.earthGardensName = 'Ruined'
    assertEqual ['Ruined','Ruined'], @earth.gardensName
  end

  # with this test, all bugs are set to 0 at all leaves around the earth
  def testBugs
    assertEqual [], @earth.earthGardensTreesBranchesLeavesBugs
    @earth.earthGardensTreesBranchesLeavesBugs = 0
    assertEqual [0,0,0,0], @earth.earthGardensTreesBranchesLeavesBugs
    @leaf.bugs = 3
    assertEqual [0,0,0,3], @earth.earthGardensTreesBranchesLeavesBugs
  end

  # from any point of chain, earth echo can be found
  def testEchosAnyone
    @targets.each do |who|
      assertEqual ["ECHO: 'piip' piip PIIP"], who.earthEcho 'piip'
    end
  end

  # those worms are taking pictures of themselves, when they travel
  def testWorms
    expects = [ 'Here we are at First! Worms Joe, Moe and little Pike.', 
      'Here we are at Second! Worms Joe, Moe and little Pike.' ]
    assertEqual expects, @leaf.earthGardensWorms 'Joe', 'Moe', 'Pike'
  end

  # in this garden, there is only one tree
  def testGardenTrees
    assertEqual [@tree], @leaf.gardenTrees
  end

  # but all gardens have one more, and both found thru one leaf
  def testGardensTrees
    assertEqual 2, @leaf.gardensTrees.length
  end

  # two ways to find that branch
  def testGardensTreesBranches
    assertEqual [@branch], @leaf.gardenTreesBranches
    assertEqual [@branch], @leaf.treesBranches
  end

  # can we count all those leaves thru one leaf?
  def testLeaves
    assertEqual 4, @leaf.earthGardensTreesBranchesLeaves.length
  end

  # does a leaf know names of all gardens?
  def testGardensName
    assertEqual ['First','Second'], @leaf.earthGardensName
  end

  # does a leaf know all gardens?
  def testGarden
    assertEqual 2, @leaf.gardens.length
  end
end

Lapidary::UI::Console::TestRunner.run(NavigatorTests)