<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>tests/gedcoms/TGC551.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/TGC551LF.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/TGC55C.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/TGC55CLF.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/george_ross.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/mini1-ansel.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/mini1-ansi.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/mini1-unicode.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/mini1-utf8.ged</filename>
    </added>
    <added>
      <filename>tests/gedcoms/simple.ged</filename>
    </added>
    <added>
      <filename>tests/parser_spec.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -17,131 +17,109 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 # -------------------------------------------------------------------------
 
-#require '_gedcom'
 require 'gedcom_date'
 
 module GEDCOM
 
   # Possibly a better way to do this?
-  VERSION = &quot;0.0.1&quot;
+  VERSION = &quot;0.2.1&quot;
 	
   class Parser
-    def defaultHandler( data, cookie, parm )
+    def initialize &amp;block
+      @before = {}
+      @after = {}
+      @ctxStack = []
+      @dataStack = []
+      @curlvl = -1
+      instance_eval(&amp;block) if block_given?
     end
 
-    def initialize( cookie = nil )
-      @cookie = cookie
-      @pre_handler = Hash.new( [ method( &quot;defaultHandler&quot; ), nil ] )
-      @post_handler = Hash.new( [ method( &quot;defaultHandler&quot; ), nil ] )
+    def before tag, proc=nil, &amp;block
+      proc = check_proc_or_block proc, &amp;block
+      @before[[tag].flatten] = proc
     end
 
-    def setPreHandler( context, func, parm = nil )
-      @pre_handler[ context ] = [ func, parm ]
+    def after tag, proc=nil, &amp;block
+      proc = check_proc_or_block proc, &amp;block
+      @after[[tag].flatten] = proc
     end
 
-    def setPostHandler( context, func, parm = nil )
-      @post_handler[ context ] = [ func, parm ]
-    end
-
-    def callPreHandler( context, data, cookie )
-      func, parm = @pre_handler[ context ]
-      func.call( data, cookie, parm )
+    def parse( file )
+      case file
+      when String
+        parse_file(file)
+      when IO
+        parse_io(file)
+      else
+        raise ArgumentError.new(&quot;requires a String or IO&quot;)
+      end
     end
 
-    def callPostHandler( context, data, cookie )
-      func, parm = @post_handler[ context ]
-      func.call( data, cookie, parm )
+    def context
+      @ctxStack
     end
 
-    # The parser is based on a stack.  Every time a new level is found that is
-    # greater than the level of the previously seen item, it is pushed onto the
-    # stack.  If the next item seen is of a lower level than previously seen
-    # items, those previously seen items are popped off the stack and their post
-    # handlers are called.
 
-    def parse( file )
-      ctxStack = []
-      dataStack = []
-      curlvl = -1
-      File.open( file, &quot;r&quot; ) do |f|
-        f.each_line do |line|
-          level, tag, rest = line.chop.split( ' ', 3 )
-          while level.to_i &lt;= curlvl
-            callPostHandler( ctxStack, dataStack.last, @cookie )
-            ctxStack.pop
-            dataStack.pop
-            curlvl -= 1
-          end
-
-          tag, rest = rest, tag if tag =~ /@.*@/
-
-          ctxStack.push tag
-          dataStack.push rest
-          curlvl = level.to_i
-
-          callPreHandler( ctxStack, dataStack.last, @cookie )
-        end 
+    protected
+    
+    def check_proc_or_block proc, &amp;block
+      unless proc or block_given?
+        raise ArgumentError.new(&quot;proc or block required&quot;)
       end
+      proc = method(proc) if proc.kind_of? Symbol
+      proc ||= Proc.new(&amp;block)
     end
-  end
-
-  class DatePart
-    def &lt;=&gt;( dp )
-      return -1 if has_year? and !dp.has_year?
-      return 1 if !has_year? and dp.has_year?
 
-      if has_year? and dp.has_year?
-        rc = ( year &lt;=&gt; dp.year )
-        return rc unless rc == 0
+    def parse_file(file)
+      File.open( file, &quot;r&quot; ) do |io|
+        parse_io(io)
       end
+    end
 
-      return -1 if dp.has_month? and !dp.has_month?
-      return 1 if !dp.has_month? and dp.has_month?
+    def parse_io(io)
+      io.each_line do |line|
+        level, tag, rest = line.chop.split( ' ', 3 )
+        level = level.to_i
+        unwind_to level
 
-      if has_month? and dp.has_month?
-        rc = ( month &lt;=&gt; dp.month )
-        return rc unless rc == 0
-      end
+        tag, rest = rest, tag if tag =~ /@.*@/
 
-      return -1 if dp.has_day? and !dp.has_day?
-      return 1 if !dp.has_day? and dp.has_day?
+        @ctxStack.push tag
+        @dataStack.push rest
+        @curlvl = level
 
-      if has_day? and dp.has_day?
-        rc = ( day &lt;=&gt; dp.day )
-        return rc unless rc == 0
+        do_before @ctxStack, rest
       end
-
-      return 0
+      unwind_to -1
     end
-  end
-  
-  class Date
-    def Date.safe_new( parm )
-      Date.new( parm ) { |errmsg| }
+
+    def unwind_to level
+      while level &lt;= @curlvl
+        do_after @ctxStack, @dataStack.last
+        @ctxStack.pop
+        @dataStack.pop
+        @curlvl -= 1
+      end
     end
 
-    def &lt;=&gt;( d )
-      if is_date? and d.is_date?
-        rc = ( first &lt;=&gt; d.first )
-        return rc unless rc == 0
-
-        if is_range? and d.is_range?
-          return ( last &lt;=&gt; d.last )
-        elsif is_range?
-          return 1
-        elsif d.is_range?
-          return -1
-        end
-
-        return 0
-      elsif is_date?
-        return -1
-      elsif d.is_date?
-        return 1
+    def do_before tag, data
+      if proc = @before[tag]
+        proc.call data
+      elsif proc = @before[ANY]
+        proc.call tag, data
       end
+    end
 
-      return format &lt;=&gt; d.format
+    def do_after tag, data
+      if proc = @after[tag]
+        proc.call data
+      elsif proc = @after[ANY]
+        proc.call tag, data
+      end
     end
-  end
-end
+
+    ANY = [:any]
+  end #/ Parser
+
+end #/ GEDCOM
 </diff>
      <filename>lib/gedcom.rb</filename>
    </modified>
    <modified>
      <diff>@@ -19,6 +19,7 @@
 #
 require 'gedcom_date_parser'
 module GEDCOM
+
     class DatePart &lt; GEDCOM_DATE_PARSER::GEDDate
       
       # Flags
@@ -97,7 +98,34 @@ module GEDCOM
         GEDCOM_DATE_PARSER::DateParser.build_gedcom_date_part_string( self )
       end
       
-    end
+      def &lt;=&gt;( dp )
+        return -1 if has_year? and !dp.has_year?
+        return 1 if !has_year? and dp.has_year?
+        
+        if has_year? and dp.has_year?
+          rc = ( year &lt;=&gt; dp.year )
+          return rc unless rc == 0
+        end
+        
+        return -1 if dp.has_month? and !dp.has_month?
+        return 1 if !dp.has_month? and dp.has_month?
+        
+        if has_month? and dp.has_month?
+          rc = ( month &lt;=&gt; dp.month )
+          return rc unless rc == 0
+        end
+        
+        return -1 if dp.has_day? and !dp.has_day?
+        return 1 if !dp.has_day? and dp.has_day?
+        
+        if has_day? and dp.has_day?
+          rc = ( day &lt;=&gt; dp.day )
+          return rc unless rc == 0
+        end
+        
+        return 0
+      end
+    end #/ DatePart
     
     class Date &lt; GEDCOM_DATE_PARSER::GEDDateValue
       # Calendar types
@@ -126,6 +154,10 @@ module GEDCOM
       DNSCAN = GEDCOM_DATE_PARSER::GCDNSCAN
       DEAD = GEDCOM_DATE_PARSER::GCDEAD
 
+      def Date.safe_new( parm )
+        Date.new( parm ) { |errmsg| }
+      end
+
       def initialize ( date_str, calendar=DateType::DEFAULT )
         begin
           @date1 = DatePart.new
@@ -173,6 +205,28 @@ module GEDCOM
         (@flags &amp; (BETWEEN | FROMTO)) != 0 ? true : false
       end
       
+      def &lt;=&gt;( d )
+        if is_date? and d.is_date?
+          rc = ( first &lt;=&gt; d.first )
+          return rc unless rc == 0
+          
+          if is_range? and d.is_range?
+            return ( last &lt;=&gt; d.last )
+          elsif is_range?
+            return 1
+          elsif d.is_range?
+            return -1
+          end
+          
+          return 0
+        elsif is_date?
+          return -1
+        elsif d.is_date?
+          return 1
+        end
+        
+        return format &lt;=&gt; d.format
+      end
     end
     
     class DateType
@@ -188,4 +242,4 @@ module GEDCOM
     class DateFormatException &lt; Exception
       
     end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/gedcom_date.rb</filename>
    </modified>
    <modified>
      <diff>@@ -39,39 +39,33 @@ class Birthday
 end
 
 
-class BirthdayExtractor &lt; GEDCOM::Parser
+class BirthdayExtracter &lt; GEDCOM::Parser
   def initialize
     super
-
-    setPreHandler  [ &quot;INDI&quot; ], method( :startPerson )
-    setPreHandler  [ &quot;INDI&quot;, &quot;NAME&quot; ], method( :registerName )
-    setPreHandler  [ &quot;INDI&quot;, &quot;BIRT&quot;, &quot;DATE&quot; ], method( :registerBirthdate )
-    setPostHandler [ &quot;INDI&quot; ], method( :endPerson )
-
     @currentPerson = nil
     @allBirthdays = []
-  end
-
-  def startPerson( data, state, parm )
-    @currentPerson = Birthday.new
-  end
+    
+    before %(INDI) do
+      @currentPerson = Birthday.new
+    end
 
-  def registerName( data, state, parm )
-    @currentPerson.name = data if @currentPerson.name == nil
-  end
+    before %w(INDI NAME) do |data|
+      @currentPerson.name = data if @currentPerson.name == nil
+    end
 
-  def registerBirthdate( data, state, parm )
-    if @currentPerson.date == nil
-      d = GEDCOM::Date.safe_new( data )
-      if d.is_date? and d.first.has_year? and d.first.has_month?
-        @currentPerson.date = d
+    before %w(INDI BIRT DATE) do |data|
+      if @currentPerson.date == nil
+        d = GEDCOM::Date.safe_new( data )
+        if d.is_date? and d.first.has_year? and d.first.has_month?
+          @currentPerson.date = d
+        end
       end
     end
-  end
 
-  def endPerson( data, state, parm )
-    @allBirthdays.push @currentPerson if @currentPerson.date != nil
-    @currentPerson = nil
+    after %w(INDI) do
+      @allBirthdays.push @currentPerson if @currentPerson.date != nil
+      @currentPerson = nil
+    end
   end
 
   def showPeopleBornIn( month )
@@ -88,7 +82,9 @@ class BirthdayExtractor &lt; GEDCOM::Parser
   def showPeopleBornOn( month, day )
     count = 0
     @allBirthdays.sort.each do |ind|
-      if ind.date.first.month == month &amp;&amp; ind.date.first.has_day? &amp;&amp; ind.date.first.day == day
+      if ind.date.first.month == month &amp;&amp;
+          ind.date.first.has_day? &amp;&amp;
+          ind.date.first.day == day
         count += 1
         showPerson( ind )
       end
@@ -110,7 +106,7 @@ end
 
 puts &quot;Parsing '#{ARGV[0]}'...&quot;
 
-parser = BirthdayExtractor.new
+parser = BirthdayExtracter.new
 parser.parse ARGV[0]
 
 now = Time.now</diff>
      <filename>samples/birthdays.rb</filename>
    </modified>
    <modified>
      <diff>@@ -24,37 +24,26 @@
 
 require 'gedcom'
 
-class IndividualCounter &lt; GEDCOM::Parser
-  attr_reader :individuals
-  attr_reader :families
-
-  def initialize
-    super
+if ARGV.length &lt; 1
+  puts &quot;Please specify the name of a GEDCOM file.&quot;
+  exit(0)
+end
 
-    @individuals = 0
-    @families = 0
+puts &quot;Parsing '#{ARGV[0]}'...&quot;
 
-    setPreHandler [ &quot;INDI&quot; ], method( :countPerson )
-    setPreHandler [ &quot;FAM&quot; ],  method( :countFamily )
-  end
+individuals = 0
+families = 0
 
-  def countPerson( data, state, parm )
-    @individuals += 1
+parser = GEDCOM::Parser.new do
+  before &quot;INDI&quot; do
+    individuals += 1
   end
 
-  def countFamily( data, state, parm )
-    @families += 1
+  before &quot;FAM&quot; do
+    families += 1
   end
 end
 
-if ARGV.length &lt; 1
-  puts &quot;Please specify the name of a GEDCOM file.&quot;
-  exit(0)
-end
-
-puts &quot;Parsing '#{ARGV[0]}'...&quot;
-
-parser = IndividualCounter.new
 parser.parse ARGV[0]
 
-puts &quot;There are #{parser.individuals} individuals and #{parser.families} families in '#{ARGV[0]}'.&quot;
+puts &quot;There are #{individuals} individuals and #{families} families in '#{ARGV[0]}'.&quot;</diff>
      <filename>samples/count.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>43148e8d41bd80482c7c50aba4bbd897d8789f00</id>
    </parent>
  </parents>
  <author>
    <name>Jeremy Slade</name>
    <email>jeremy@jkslade.net</email>
  </author>
  <url>http://github.com/timshadel/gedcom-ruby/commit/cca43d79679d8dce3c10137972cb13deb909ec9c</url>
  <id>cca43d79679d8dce3c10137972cb13deb909ec9c</id>
  <committed-date>2008-12-10T22:34:00-08:00</committed-date>
  <authored-date>2008-12-10T22:34:00-08:00</authored-date>
  <message>Replaced setPreHandler and setPostHandler with before/after; allow before/after to be defined with blocks; reworked sample scripts to match new API; added specs for GEDCOM::Parser, including some test gedcoms</message>
  <tree>0f141d537f641298eec9ed80e40a5105e752af73</tree>
  <committer>
    <name>Jeremy Slade</name>
    <email>jeremy@jkslade.net</email>
  </committer>
</commit>
