<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>test/fixtures/simplest_102.ofx</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,7 +1,7 @@
 == OFXRB, the Pure Ruby OFX (Open Financial Exchange) Library
 
 OFXRB is *the* solution to parsing and generating OFX documents in Ruby.
-If you didn't know what OFX was, you probably wouldn't be looking at this,
+If you don't know what OFX is, you probably wouldn't be looking at this,
 but, to be complete: The Open Financial Exchange specification defines an
 information exchange protocol for financial applications. That essentially
 means that we now have an agreed upon way to represent what is in our bank</diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -10,6 +10,25 @@ module OFXRB
     &quot;OFXRB::Parser#{version}&quot;.constantize.parse(ofx_doc)
   end
 
+  # The basic OFX document event handler, used to create an OFX model from
+  # an OFX document
+  class OfxHandler
+    # All handlers answer the OFX object model that they are building
+    attr_reader :ofx_object
+
+    def initialize
+      @ofx_object = BankStatement.new
+    end
+
+    def header_event(name, value)
+      @ofx_object.properties[name] = value
+    end
+
+    def property_event(name, value)
+    end
+
+  end
+
   class OfxObject
     
     # Maps humane attr_readers to the element names of the OFX specification.
@@ -26,14 +45,34 @@ module OFXRB
 
   end
 
+  class BankStatement &lt; OfxObject
+    attr_reader :properties, :credit_cards
 
-  class CreditCardStatement &lt; OfxObject
-    attr_reader :properties, :transactions
-    
     ofx_attrs :version =&gt; 'VERSION'
 
     def initialize
       @properties = {}
+      @credit_cards = []
+    end
+
+    def ccstmtrs(properties)
+      @credit_cards &lt;&lt; cc = CreditCard.new(properties)
+      cc.transactions = @transactions
+      
+    end
+
+    def method_missing(name, *args)
+      # p &quot;Need to handle #{name} in #{self.class.name}&quot;
+    end
+
+  end
+
+
+  class CreditCard &lt; OfxObject
+    attr_reader :properties, :transactions
+
+    def initialize
+      @properties = {}
       @transactions = []
     end
   
@@ -42,7 +81,9 @@ module OFXRB
     end
   
     def method_missing(name, *args)
+      p &quot;Need to handle #{name} in #{self.class.name}&quot;
     end
+
   end
 
 
@@ -52,6 +93,7 @@ module OFXRB
     def initialize(properties)
       @properties = properties
     end
+
   end
 
 end
\ No newline at end of file</diff>
      <filename>lib/ofx.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,47 +14,30 @@ module OFXRB
 
   class Parser102 &lt; Racc::Parser
 
-module_eval &lt;&lt;'..end ofx_102.y modeval..id94261ec6f5', 'ofx_102.y', 20
-class Property
-  attr_accessor :key, :value
-  def initialize(name, value)
-    @key = name
-    @value = value
-  end
-end
-
-class Element
-  attr_reader :name, :properties
-  def initialize(start_name, elements, end_name)
-    raise &quot;Element #{start_name} is being closed as #{end_name}&quot; if start_name != end_name
-    @name, @properties = start_name, {}
-    elements.each { |e| @properties.store(e.key, e.value) if e.is_a?(Property) }
-  end
-end
-
+module_eval &lt;&lt;'..end ofx_102.y modeval..iddc2bd249fa', 'ofx_102.y', 35
 def name_from_ofx(tag)
   $1 if tag =~ /&lt;\/?(\w+)&gt;/
 end
 
-def property(val)
-  Property.new(name_from_ofx(val[0]), val[1])
+def end_tag_event(tag)
+  tag_event(tag, 'end')
 end
 
-# When an OFXRB::Element has been created, the current handler will receive a
-# a method send where the name is the downcased name of the OFXRB::Element.
-def element(val)
-  e = Element.new(name_from_ofx(val[0]), val[1], name_from_ofx(val[2]));
-  @root_object.send(e.name.downcase.to_sym, e.properties)
-  e
+def start_tag_event(tag)
+  tag_event(tag, 'start')
 end
 
-def self.parse(ofx_doc, root_object = CreditCardStatement.new)
+def tag_event(tag, type)
+  method = &quot;#{name_from_ofx(tag).downcase}_#{type}_event&quot;.to_sym
+  @event_handler.send(method) if @event_handler.respond_to?(method)
+end
+
+def self.parse(ofx_doc, root_object = OfxHandler.new)
   new.parse(ofx_doc, root_object)
 end
 
-# Implements the Racc#parse method using a StringScanner to lex
-def parse(ofx_doc, root_object)
-  @root_object = root_object
+def parse(ofx_doc, event_handler)
+  @event_handler = event_handler
 
   @match_tokens = {
     :START_TAG =&gt; /&lt;\w+&gt;/,
@@ -78,14 +61,14 @@ def parse(ofx_doc, root_object)
 
   #@yydebug = true
   do_parse
-  @root_object
+  @event_handler.ofx_object
 end
 
 private
 def next_token
   @tokens.shift
 end
-..end ofx_102.y modeval..id94261ec6f5
+..end ofx_102.y modeval..iddc2bd249fa
 
 ##### racc 1.4.5 generates ###
 
@@ -95,44 +78,57 @@ racc_reduce_table = [
  2, 8, :_reduce_none,
  1, 8, :_reduce_none,
  3, 10, :_reduce_4,
- 2, 9, :_reduce_5,
- 1, 9, :_reduce_6,
- 1, 11, :_reduce_none,
- 1, 11, :_reduce_none,
- 2, 13, :_reduce_9,
- 3, 12, :_reduce_10 ]
-
-racc_reduce_n = 11
-
-racc_shift_n = 19
+ 2, 9, :_reduce_none,
+ 1, 9, :_reduce_none,
+ 1, 9, :_reduce_none,
+ 3, 11, :_reduce_none,
+ 2, 11, :_reduce_none,
+ 2, 12, :_reduce_none,
+ 2, 12, :_reduce_none,
+ 1, 12, :_reduce_none,
+ 2, 15, :_reduce_13,
+ 2, 16, :_reduce_none,
+ 1, 16, :_reduce_none,
+ 1, 13, :_reduce_16,
+ 1, 14, :_reduce_17 ]
+
+racc_reduce_n = 18
+
+racc_shift_n = 24
 
 racc_action_table = [
-     1,    15,    10,    10,    10,    18,    13,     6,    14,     5,
-    10,     1 ]
+    17,    15,    10,    19,     1,     1,    10,    14,     6,    10,
+     5,    10,    19 ]
 
 racc_action_check = [
-     3,    10,     3,    10,    16,    16,     5,     2,     6,     1,
-    11,     0 ]
+     9,     6,     9,     9,     3,     0,     3,     5,     2,     7,
+     1,    11,    20 ]
 
 racc_action_pointer = [
-     9,     6,     7,    -2,   nil,     4,     8,   nil,   nil,   nil,
-    -1,     6,   nil,   nil,   nil,   nil,     0,   nil,   nil ]
+     3,     7,     8,     2,   nil,     5,     1,     5,   nil,    -2,
+   nil,     7,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+     7,   nil,   nil,   nil ]
 
 racc_action_default = [
-   -11,   -11,   -11,   -11,    -3,   -11,   -11,    -6,    -7,    -8,
-   -11,    -1,    -2,    -4,    19,    -9,   -11,    -5,   -10 ]
+   -18,   -18,   -18,   -18,    -3,   -18,   -18,    -7,    -6,   -18,
+   -16,   -12,    -1,    -2,    -4,    24,    -5,   -13,    -9,   -17,
+   -18,    -6,   -11,    -8 ]
 
 racc_goto_table = [
-    11,    17,     4,     3,     2,    12,    17,    16 ]
+    18,    12,     3,     4,    21,    16,    13,    20,     2,    22,
+   nil,    23 ]
 
 racc_goto_check = [
-     3,     5,     4,     2,     1,     4,     5,     3 ]
+     8,     3,     2,     4,     6,     3,     4,     3,     1,     3,
+   nil,     8 ]
 
 racc_goto_pointer = [
-   nil,     4,     3,    -3,     2,   -10,   nil,   nil ]
+   nil,     8,     2,    -2,     3,   nil,    -7,   nil,    -9,   nil,
+   nil ]
 
 racc_goto_default = [
-   nil,   nil,   nil,   nil,   nil,     7,     8,     9 ]
+   nil,   nil,   nil,   nil,   nil,     7,     8,     9,   nil,    11,
+   nil ]
 
 racc_token_table = {
  false =&gt; 0,
@@ -172,13 +168,16 @@ Racc_token_to_s_table = [
 '$start',
 'root',
 'headers',
-'elements',
+'objects',
 'key_value_pair',
-'element',
-'closed_element',
-'one_line_element']
+'object',
+'properties',
+'start_tag',
+'end_tag',
+'property',
+'end_tags']
 
-Racc_debug_parser = true
+Racc_debug_parser = false
 
 ##### racc system variables end #####
 
@@ -190,41 +189,50 @@ Racc_debug_parser = true
 
  # reduce 3 omitted
 
-module_eval &lt;&lt;'.,.,', 'ofx_102.y', 5
+module_eval &lt;&lt;'.,.,', 'ofx_102.y', 7
   def _reduce_4( val, _values, result )
-@root_object.properties.store(val[0], val[2])
+@event_handler.header_event(val[0], val[2])
    result
   end
 .,.,
 
-module_eval &lt;&lt;'.,.,', 'ofx_102.y', 7
-  def _reduce_5( val, _values, result )
-result &lt;&lt; val[1]
-   result
-  end
-.,.,
+ # reduce 5 omitted
+
+ # reduce 6 omitted
+
+ # reduce 7 omitted
+
+ # reduce 8 omitted
 
-module_eval &lt;&lt;'.,.,', 'ofx_102.y', 8
-  def _reduce_6( val, _values, result )
-result = [val[0]]
+ # reduce 9 omitted
+
+ # reduce 10 omitted
+
+ # reduce 11 omitted
+
+ # reduce 12 omitted
+
+module_eval &lt;&lt;'.,.,', 'ofx_102.y', 20
+  def _reduce_13( val, _values, result )
+@event_handler.property_event(name_from_ofx(val[0]), val[1])
    result
   end
 .,.,
 
- # reduce 7 omitted
+ # reduce 14 omitted
 
- # reduce 8 omitted
+ # reduce 15 omitted
 
-module_eval &lt;&lt;'.,.,', 'ofx_102.y', 11
-  def _reduce_9( val, _values, result )
-result = property(val)
+module_eval &lt;&lt;'.,.,', 'ofx_102.y', 25
+  def _reduce_16( val, _values, result )
+start_tag_event(val[0])
    result
   end
 .,.,
 
-module_eval &lt;&lt;'.,.,', 'ofx_102.y', 12
-  def _reduce_10( val, _values, result )
-result = element(val)
+module_eval &lt;&lt;'.,.,', 'ofx_102.y', 27
+  def _reduce_17( val, _values, result )
+end_tag_event(val[0])
    result
   end
 .,.,</diff>
      <filename>lib/ofx_102.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,62 +1,60 @@
 class OFXRB::Parser102
 rule
-	root: headers elements
+	root: headers objects
+
 	headers: headers key_value_pair
 	       | key_value_pair
-	key_value_pair: STRING COLON STRING {@root_object.properties.store(val[0], val[2])}
+
+	key_value_pair: STRING COLON STRING {@event_handler.header_event(val[0], val[2])}
 	
-	elements: elements element {result &lt;&lt; val[1]}
-	        | element {result = [val[0]]}
+	objects: object objects
+	       | properties
+	       | object
+
+  object: start_tag objects end_tag
+        | start_tag end_tag
+  
+  properties: property properties
+            | property objects
+            | property
+  	          
+	property: start_tag STRING {@event_handler.property_event(name_from_ofx(val[0]), val[1])}
 	
-	element: closed_element | one_line_element
-	one_line_element: START_TAG STRING {result = property(val)}
-	closed_element: START_TAG elements END_TAG {result = element(val)}
+	end_tags: end_tag end_tags
+	          | end_tag
+
+	start_tag: START_TAG {start_tag_event(val[0])}
+
+  end_tag: END_TAG {end_tag_event(val[0])}
 end
 
 ---- header ----
 require 'strscan'
 
 ---- inner ----
-class Property
-  attr_accessor :key, :value
-  def initialize(name, value)
-    @key = name
-    @value = value
-  end
-end
-
-class Element
-  attr_reader :name, :properties
-  def initialize(start_name, elements, end_name)
-    raise &quot;Element #{start_name} is being closed as #{end_name}&quot; if start_name != end_name
-    @name, @properties = start_name, {}
-    elements.each { |e| @properties.store(e.key, e.value) if e.is_a?(Property) }
-  end
-end
-
 def name_from_ofx(tag)
   $1 if tag =~ /&lt;\/?(\w+)&gt;/
 end
 
-def property(val)
-  Property.new(name_from_ofx(val[0]), val[1])
+def end_tag_event(tag)
+  tag_event(tag, 'end')
+end
+
+def start_tag_event(tag)
+  tag_event(tag, 'start')
 end
 
-# When an OFXRB::Element has been created, the current handler will receive a
-# a method send where the name is the downcased name of the OFXRB::Element.
-def element(val)
-  e = Element.new(name_from_ofx(val[0]), val[1], name_from_ofx(val[2]));
-  @root_object.send(e.name.downcase.to_sym, e.properties)
-  e
+def tag_event(tag, type)
+  method = &quot;#{name_from_ofx(tag).downcase}_#{type}_event&quot;.to_sym
+  @event_handler.send(method) if @event_handler.respond_to?(method)
 end
 
-def self.parse(ofx_doc, root_object = CreditCardStatement.new)
+def self.parse(ofx_doc, root_object = OfxHandler.new)
   new.parse(ofx_doc, root_object)
 end
 
-# Implements the Racc#parse method using a StringScanner to lex
-def parse(ofx_doc, root_object)
-  @root_object = root_object
+def parse(ofx_doc, event_handler)
+  @event_handler = event_handler
 
   @match_tokens = {
     :START_TAG =&gt; /&lt;\w+&gt;/,
@@ -80,7 +78,7 @@ def parse(ofx_doc, root_object)
 
   #@yydebug = true
   do_parse
-  @root_object
+  @event_handler.ofx_object
 end
 
 private</diff>
      <filename>lib/ofx_102.y</filename>
    </modified>
    <modified>
      <diff>@@ -4,13 +4,13 @@ class OfxTest &lt; Test::Unit::TestCase
   include OfxTestHelp
 
   def test_it
-    ofx = OFXRB.import(fixture_credit_card_statement_102)
-    assert_equal('102', ofx.version)
+    ofx_model = OFXRB.import(fixture_credit_card_statement_102)
+    assert_equal('102', ofx_model.version)
 
-    ofx = OFXRB.import(fixture_checking_and_savings_102)
-    assert_equal('102', ofx.version)
+    ofx_model = OFXRB.import(fixture_checking_and_savings_102)
+    assert_equal('102', ofx_model.version)
 
-    ofx = OFXRB.import(fixture_checking_and_savings_200)
-    assert_equal('200', ofx.version)
+    ofx_model = OFXRB.import(fixture_checking_and_savings_200)
+    assert_equal('200', ofx_model.version)
   end
 end
\ No newline at end of file</diff>
      <filename>test/unit/ofx_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,12 +3,74 @@ require File.dirname(__FILE__) + &quot;/test_help&quot;
 class Parser102Test &lt; Test::Unit::TestCase
   include OfxTestHelp
 
-  def test_parse
-    ofx = OFXRB::Parser102.parse(fixture_credit_card_statement_102)
-    assert_equal '102', ofx.version
-    assert_equal '42.74', ofx.transactions.first.amount
+  def test_parse_events
+    my_handler = Object.new
+    class &lt;&lt; my_handler
 
-    ofx = OFXRB::Parser102.parse(fixture_checking_and_savings_102)
-    assert_equal '102', ofx.version
+      # Keeping in line with API
+      def ofx_object
+      end
+
+      def bankmsgsrsv1_start_event
+        events &lt;&lt; 'bankmsgsrsv1_start'
+      end
+
+      def bankmsgsrsv1_end_event
+        events &lt;&lt; 'bankmsgsrsv1_end'
+      end
+      
+      def ofx_start_event
+        events &lt;&lt; 'ofx_start'
+      end
+
+      def ofx_end_event
+        events &lt;&lt; 'ofx_end'
+      end
+      
+      def signonmsgsrsv1_start_event
+        events &lt;&lt; 'signonmsgsrsv1_start'
+      end
+      
+      def signonmsgsrsv1_end_event
+        events &lt;&lt; 'signonmsgsrsv1_end'
+      end
+
+      def property_event(name, value)
+        events &lt;&lt; 'property'
+      end
+
+      def events
+        @events ||= []
+      end
+
+      def header_event(name, value)
+        events &lt;&lt; 'header'
+      end
+
+    end
+
+    OFXRB::Parser102.parse(fixture_simplest_102, my_handler)
+    assert_events([['header',1], 'ofx_start', 'property', 'signonmsgsrsv1_start', 'signonmsgsrsv1_end', 'property', 'bankmsgsrsv1_start', 'bankmsgsrsv1_end', 'ofx_end'], my_handler.events)
+    
+    OFXRB::Parser102.parse(fixture_credit_card_statement_102, my_handler)    
+    OFXRB::Parser102.parse(fixture_checking_and_savings_102, my_handler)
+  end
+    
+  def assert_events(expected, events)
+    actual = events.dup
+    expected.each do |expectation|
+      if expectation.is_a?(Array)
+        expected_name, expected_occurrences = expectation
+        occurrences = 0
+        until actual.first != expected_name
+          actual_name = actual.shift
+          occurrences += 1
+        end
+        assert_equal(expected_occurrences, occurrences)
+      else
+        assert_equal expectation, actual.shift
+      end
+    end
   end
+    
 end
\ No newline at end of file</diff>
      <filename>test/unit/parser102_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>f78097189af9721918cd04a6ffb1ae4a811bc9de</id>
    </parent>
  </parents>
  <author>
    <name>Adam Williams</name>
    <email>adam@thewilliams.ws</email>
  </author>
  <url>http://github.com/aiwilliams/ofxrb/commit/fa27546073e70edc793e719b77550832b8066cb3</url>
  <id>fa27546073e70edc793e719b77550832b8066cb3</id>
  <committed-date>2006-08-14T13:17:03-07:00</committed-date>
  <authored-date>2006-08-14T13:17:03-07:00</authored-date>
  <message>Working with Paul Kristoff to:
re-write the parser for 1.02 to be a SAX-type parser (event driven).</message>
  <tree>79628b4663a52e73bdd5a607698a1a64cdd6ecaa</tree>
  <committer>
    <name>Adam Williams</name>
    <email>adam@thewilliams.ws</email>
  </committer>
</commit>
