File: continuation_koans.rb

Project: throw_catch -- Throw/Catch Lab

#!/usr/bin/env ruby

# continuation_koans.rb is based on ara.t.howard's metakoans.rb utility.
#
# continuation_koans.rb is an arduous set of exercises designed to
# stretch continuation-programming muscle.  The focus is on
# implementing a simple catch/throw system, similar to what already
# exists in Ruby.
#
# The basic usage is:
#
#   result = cc_catch(:tag) {
#     do_something()
#     cc_throw(:tag, 1)
#     fail "You never get here"
#   }
#   assert_equal 1, result
#
# Yourw path, should you choose to follow it, is to write a single
# file 'throwcatch.rb' implementing all functionality required by the
# koans below.  As a student of meta-programming your course will be
# guided by a guru whose wisdom and pithy sayings will assist you on
# your journey.
#
# A successful student will eventually be able to do this
#
# harp:~ > ruby metakoans.rb knowledge.rb
# koan_01 has expanded your awareness
# koan_02 has expanded your awareness
# koan_03 has expanded your awareness
# koan_04 has expanded your awareness
# koan_05 has expanded your awareness
# koan_06 has expanded your awareness
# koan_07 has expanded your awareness
# koan_08 has expanded your awareness
# koan_09 has expanded your awareness
# koan_10 has expanded your awareness
# mountains are again merely mountains
#

module MetaKoans
  #
  # A catch block that does nothing should return nil
  #
  def koan_01
    result = cc_catch(:x) { }
    assert { result == nil }
  end
  #
  # A catch block with no throws should return the last value of the
  # block
  #
  def koan_02
    result = cc_catch(:x) { :default_value }
    assert { result == :default_value }
  end
  #
  # A throw should terminate the catch block immediately.  Nil should be
  # returned.
  #
  def koan_03
    result = cc_catch(:x) {
      cc_throw(:x)
      fail "Oops"
    }
    assert { result == nil }
  end
  #
  # A throw should terminate the catch block immediately.  The thrown
  # value should be returned.
  #
  def koan_04
    result = cc_catch(:x) {
      cc_throw(:x, :thrown_value)
      fail "Oops"
    }
    assert { result == :thrown_value }
  end
  #
  # A throw with no catch should raise a NameError
  #
  def koan_05
    begin
      cc_throw :x
      fail "Oops"
    rescue NameError => ex
      assert { ex.message == "uncaught throw `x'" }
    end
  end
  #
  # A throw with a different symbol should raise a NameError
  #
  def koan_06
    begin
      cc_catch(:y) {
	cc_throw :x
      }
      fail "Oops"
    rescue NameError => ex
      assert { ex.message == "uncaught throw `x'" }
    end
  end
  #
  # Nested throws should find the proper catch block
  #
  def koan_07
    result = cc_catch(:y) {
      cc_catch(:x) {
	cc_throw :y, 1
      }
      fail "Oops"
    }
    assert { result == 1 }
  end
  #
  # 
  #
  def koan_08
    result = cc_catch(:y) {
      cc_catch(:y) {
	cc_throw :y, 1
      }
      cc_throw :y, 2
    }
    assert { result == 2 }
  end
  #
  # into the void
  #
  def koan_09
    result = cc_catch(:y) {
      res = cc_catch(:x) {
	cc_catch(:y) {
	  cc_throw :x, :good
	}
	:bad
      }
      cc_throw :y, res
      :bad
    }
    assert { result == :good }
  end
  
  #
  # Beyond the void
  #
  def koan_10
    result = :ok
    begin
      cc_catch(:x) {
	fail "Oops"
      }
      result = :bad
    rescue RuntimeError
    end
    begin
      cc_throw(:x)
    rescue NameError
    end
    assert { result == :ok }
  end
  
  def assert()
    bool = yield
    abort "assert{ #{ caller.first[%r/^.*(?=:)/] } } #=> #{ bool.inspect }" unless bool
  end
end


class MetaStudent
  def initialize knowledge
    require knowledge
  end
  def ponder koan
    begin
      send koan
      true
    rescue => e
      STDERR.puts %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join 10.chr }]
      false
    end
  end
end


class MetaGuru
  require "singleton"
  include Singleton
  
  def enlighten student
    student.extend MetaKoans
    
    koans = student.methods.grep(%r/koan/).sort
    
    attainment = nil
    
    koans.each do |koan|
      awakened = student.ponder koan
      if awakened
	puts "#{ koan } has expanded your awareness"
	attainment = koan
      else
	puts "#{ koan } still requires meditation"
	break
      end
    end
    
    puts(
      case attainment
      when nil
	"mountains are merely mountains"
      when 'koan_01', 'koan_02'
	"learn the rules so you know how to break them properly"
      when 'koan_03', 'koan_04'
	"remember that silence is sometimes the best answer"
      when 'koan_05', 'koan_06'
	"sleep is the best meditation"
      when 'koan_07'
	"when you lose, don't lose the lesson"
      when 'koan_08', 'koan_09'
	"things are not what they appear to be: nor are they otherwise"
      else
	"mountains are again merely mountains"
      end
      )
  end
  def self::method_missing m, *a, &b
    instance.send m, *a, &b
  end
end

knowledge = ARGV.shift or abort "#{ $0 } knowledge.rb"
student = MetaStudent::new knowledge
MetaGuru.enlighten student


[ Index ][ Presentation ]
Generated by [ source2html ]