edi4r 0.9.4.1 → 0.9.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/AuthorCopyright +3 -3
- data/{ChangeLog → Changelog} +60 -0
- data/README +15 -10
- data/Tutorial +2 -3
- data/VERSION +1 -1
- data/bin/edi2xml.rb +12 -16
- data/bin/editool.rb +9 -5
- data/bin/sedas2eancom02.rb +1385 -0
- data/bin/xml2edi.rb +7 -12
- data/data/edifact/iso9735/SDCD.20000.csv +1 -0
- data/data/edifact/iso9735/SDCD.3for2.csv +1 -0
- data/data/edifact/iso9735/SDED.20000.csv +6 -0
- data/data/edifact/iso9735/SDED.30000.csv +43 -43
- data/data/edifact/iso9735/SDED.3for2.csv +6 -0
- data/data/edifact/iso9735/SDED.40000.csv +129 -129
- data/data/edifact/iso9735/SDED.40100.csv +130 -130
- data/data/edifact/iso9735/SDMD.20000.csv +6 -0
- data/data/edifact/iso9735/SDMD.30000.csv +6 -6
- data/data/edifact/iso9735/SDMD.3for2.csv +6 -0
- data/data/edifact/iso9735/SDMD.40000.csv +17 -17
- data/data/edifact/iso9735/SDMD.40100.csv +17 -17
- data/data/edifact/iso9735/SDSD.20000.csv +5 -0
- data/data/edifact/iso9735/SDSD.3for2.csv +5 -0
- data/data/edifact/untdid/EDMD.d01b.csv +1 -1
- data/data/sedas/EDCD..csv +0 -0
- data/data/sedas/EDED..csv +859 -0
- data/data/sedas/EDMD..csv +16 -0
- data/data/sedas/EDSD..csv +44 -0
- data/lib/edi4r.rb +147 -67
- data/lib/edi4r/ansi_x12-rexml.rb +91 -0
- data/lib/edi4r/ansi_x12.rb +1684 -0
- data/lib/edi4r/diagrams.rb +75 -14
- data/lib/edi4r/edifact-rexml.rb +4 -3
- data/lib/edi4r/edifact.rb +505 -202
- data/lib/edi4r/rexml.rb +13 -7
- data/lib/edi4r/sedas.rb +854 -0
- data/lib/edi4r/standards.rb +150 -33
- data/test/damaged_file.edi +1 -0
- data/test/eancom2webedi.rb +1 -0
- data/test/groups.edi +1 -1
- data/test/test_basics.rb +16 -9
- data/test/test_edi_split.rb +30 -0
- data/test/test_loopback.rb +7 -2
- data/test/test_rexml.rb +34 -2
- data/test/test_service_messages.rb +190 -0
- data/test/test_streaming.rb +167 -0
- data/test/test_tut_examples.rb +3 -1
- data/test/webedi2eancom.rb +1 -0
- metadata +121 -77
    
        data/lib/edi4r/diagrams.rb
    CHANGED
    
    | @@ -1,10 +1,11 @@ | |
| 1 | 
            +
            # -*- encoding: iso-8859-1 -*-
         | 
| 1 2 | 
             
            # Classes related to message diagrams and validation
         | 
| 2 3 | 
             
            # for the EDI module "edi4r", a class library
         | 
| 3 4 | 
             
            # to parse and create UN/EDIFACT and other EDI data
         | 
| 4 5 | 
             
            #
         | 
| 5 6 | 
             
            # :include: ../../AuthorCopyright
         | 
| 6 7 | 
             
            #
         | 
| 7 | 
            -
            # $Id: diagrams.rb,v 1.8 2006/05/26 16:57:37 werntges Exp $
         | 
| 8 | 
            +
            # $Id: diagrams.rb,v 1.8 2006/05/26 16:57:37 werntges Exp werntges $
         | 
| 8 9 | 
             
            #--
         | 
| 9 10 | 
             
            # $Log: diagrams.rb,v $
         | 
| 10 11 | 
             
            # Revision 1.8  2006/05/26 16:57:37  werntges
         | 
| @@ -97,18 +98,25 @@ module EDI::Diagram | |
| 97 98 | 
             
                # std:: The syntax standard key. Currently supported:
         | 
| 98 99 | 
             
                #       - 'E' (EDIFACT),
         | 
| 99 100 | 
             
                #       - 'I' (SAP IDOC)
         | 
| 101 | 
            +
                #       - 'S' (SEDAS, experimental)
         | 
| 102 | 
            +
                #       - 'A' (ANSI X.12, limited)
         | 
| 100 103 | 
             
                # params:: A hash of parameters that uniquely identify the selected diagram.
         | 
| 101 104 | 
             
                #          Internal use only - see source code for details.
         | 
| 102 105 | 
             
                #
         | 
| 103 106 | 
             
                def Diagram.create( std, params )
         | 
| 104 107 | 
             
                  case std
         | 
| 105 108 | 
             
                  when 'E' # UN/EDIFACT
         | 
| 106 | 
            -
                    par = { | 
| 107 | 
            -
                       | 
| 109 | 
            +
                    par = {
         | 
| 110 | 
            +
                      :d0051 => 'UN', 
         | 
| 111 | 
            +
                      :d0057 => nil,
         | 
| 108 112 | 
             
                      :is_iedi => false }.update( params )
         | 
| 109 113 | 
             
                  when 'I' # SAP IDocs
         | 
| 110 114 | 
             
                    par = params
         | 
| 111 115 | 
             
                    #      raise "Not implemented yet!"
         | 
| 116 | 
            +
                  when 'S' # SEDAS
         | 
| 117 | 
            +
                    par = params
         | 
| 118 | 
            +
                  when 'A' # ANSI X12
         | 
| 119 | 
            +
                    par = params
         | 
| 112 120 | 
             
                  else
         | 
| 113 121 | 
             
                    raise "Unsupported syntax standard: #{std}"
         | 
| 114 122 | 
             
                  end
         | 
| @@ -120,7 +128,7 @@ module EDI::Diagram | |
| 120 128 | 
             
                    key = par.sort {|a,b| a.to_s <=> b.to_s}.hash
         | 
| 121 129 | 
             
                    obj = @@cache[key]
         | 
| 122 130 | 
             
                    return obj unless obj.nil?
         | 
| 123 | 
            -
             | 
| 131 | 
            +
             | 
| 124 132 | 
             
                    obj = new( std, par )
         | 
| 125 133 | 
             
                    @@cache[key] = obj # cache & return it
         | 
| 126 134 |  | 
| @@ -132,13 +140,30 @@ module EDI::Diagram | |
| 132 140 |  | 
| 133 141 | 
             
                def initialize( std, par ) # :nodoc:
         | 
| 134 142 | 
             
                  case std
         | 
| 143 | 
            +
                  when 'A' # ANSI X12
         | 
| 144 | 
            +
                    @base_key = [par[:ST01], # msg type, e.g. 837
         | 
| 145 | 
            +
                      par[:GS08][0,3], # version
         | 
| 146 | 
            +
                      par[:GS08][3,2], # release, 
         | 
| 147 | 
            +
                      par[:GS08][5,1], # sub-version
         | 
| 148 | 
            +
                      '',
         | 
| 149 | 
            +
                      # par[:GS08][6..-1], # assoc. assigned code (subset)
         | 
| 150 | 
            +
                      ''].join(':')
         | 
| 151 | 
            +
                    @msg_type = par[:ST01]
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    @dir = EDI::Dir::Directory.create(std, par )
         | 
| 154 | 
            +
            #                                          :d0065 => @msg_type,
         | 
| 155 | 
            +
            #                                          :d0052 => par[:d0052], 
         | 
| 156 | 
            +
            #                                          :d0054 => par[:d0054], 
         | 
| 157 | 
            +
            #                                          :d0051 => par[:d0051], 
         | 
| 158 | 
            +
            #                                          :d0057 => par[:d0057], 
         | 
| 159 | 
            +
            #                                          :is_iedi => par[:is_iedi])
         | 
| 135 160 | 
             
                  when 'E' # UN/EDIFACT
         | 
| 136 161 | 
             
                    @base_key = [par[:d0065], # msg type
         | 
| 137 162 | 
             
                      par[:d0052], # version
         | 
| 138 163 | 
             
                      par[:d0054], # release, 
         | 
| 139 164 | 
             
                      par[:d0051], # resp. agency
         | 
| 140 | 
            -
                      '',
         | 
| 141 | 
            -
                       | 
| 165 | 
            +
                      # '',
         | 
| 166 | 
            +
                      par[:d0057], # assoc. assigned code (subset)
         | 
| 142 167 | 
             
                      ''].join(':')
         | 
| 143 168 | 
             
                    @msg_type = par[:d0065]
         | 
| 144 169 |  | 
| @@ -160,10 +185,23 @@ module EDI::Diagram | |
| 160 185 | 
             
                    @msg_type = par[:IDOCTYPE]
         | 
| 161 186 |  | 
| 162 187 | 
             
                    @dir = EDI::Dir::Directory.create(std,
         | 
| 163 | 
            -
                                                      : | 
| 188 | 
            +
                                                      :IDOCTYPE => @msg_type,
         | 
| 164 189 | 
             
                                                      :EXTENSION => par[:EXTENSION], 
         | 
| 165 190 | 
             
                                                      :SAPTYPE => par[:SAPTYPE])
         | 
| 166 191 | 
             
                    #      raise "Not implemented yet!"
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                  when 'S' # SEDAS
         | 
| 194 | 
            +
                    @base_key = [par[:SEDASTYPE],
         | 
| 195 | 
            +
                      '',
         | 
| 196 | 
            +
                      '',
         | 
| 197 | 
            +
                      '',
         | 
| 198 | 
            +
                      '',
         | 
| 199 | 
            +
                      #                   par[:d0057],  # assoc. assigned code (subset)
         | 
| 200 | 
            +
                      ''].join(':')
         | 
| 201 | 
            +
                    @msg_type = par[:SEDASTYPE]
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                    @dir = EDI::Dir::Directory.create(std)
         | 
| 204 | 
            +
             | 
| 167 205 | 
             
                  else
         | 
| 168 206 | 
             
                    raise "Unsupported syntax standard: #{std}"
         | 
| 169 207 | 
             
                  end
         | 
| @@ -219,20 +257,31 @@ module EDI::Diagram | |
| 219 257 | 
             
                attr_reader :sg_name
         | 
| 220 258 |  | 
| 221 259 | 
             
                # A new Branch object is uniquely identified by the +key+ argument
         | 
| 222 | 
            -
                # that selects the directory entry and its +sg_name+ (if  | 
| 260 | 
            +
                # that selects the directory entry and its +sg_name+ (if not top branch).
         | 
| 223 261 | 
             
                # +root+ is a reference to the Diagram it belongs to.
         | 
| 224 262 |  | 
| 225 263 | 
             
                def initialize(key, sg_name, root)
         | 
| 226 | 
            -
                  # | 
| 264 | 
            +
                  # warn "Creating branch for key `#{key||''+sg_name||''}'..."
         | 
| 227 265 | 
             
                  @key = key
         | 
| 228 266 | 
             
                  @sg_name = sg_name
         | 
| 229 267 | 
             
                  @root = root
         | 
| 230 268 |  | 
| 231 269 | 
             
                  @nodelist=[]
         | 
| 232 270 | 
             
                  b = @root.dir.message( key+sg_name.to_s )
         | 
| 233 | 
            -
             | 
| 271 | 
            +
            =begin
         | 
| 272 | 
            +
                  # UN/EDIFACT subsets only:
         | 
| 273 | 
            +
                  if b.nil? && key =~ /(\w{6}:\w+:\w+:\w+:)(.+?)(:.*)/
         | 
| 274 | 
            +
            	puts "2: #{key}"
         | 
| 275 | 
            +
            	@key = key = $1+$3 # Discard the subset DE
         | 
| 276 | 
            +
            	puts "3: #{key}"
         | 
| 277 | 
            +
            	EDI::logger.warn "Subset #{$2} data not found - trying standard instead..."
         | 
| 278 | 
            +
                    b = @root.dir.message( key+sg_name.to_s )
         | 
| 279 | 
            +
                  end
         | 
| 280 | 
            +
            =end
         | 
| 281 | 
            +
                  raise EDI::EDILookupError, "Lookup failed for key `#{key+sg_name.to_s}' - known names: #{@root.dir.message_names.join(', ')}" unless b
         | 
| 234 282 | 
             
                  @desc = b.desc
         | 
| 235 283 | 
             
                  b.each {|obj| @nodelist << Node.create( obj.name, obj.status, obj.maxrep )}
         | 
| 284 | 
            +
                  raise "Empty branch! key, sg = #{key}, #{sg_name}" if @nodelist.empty?
         | 
| 236 285 | 
             
                end
         | 
| 237 286 |  | 
| 238 287 | 
             
                #
         | 
| @@ -297,6 +346,13 @@ module EDI::Diagram | |
| 297 346 | 
             
                  @nodelist.size
         | 
| 298 347 | 
             
                end
         | 
| 299 348 |  | 
| 349 | 
            +
                # Returns TRUE if branch is empty. Example: 
         | 
| 350 | 
            +
                #  The tail of a segment group that consists of just the trigger segment
         | 
| 351 | 
            +
                #
         | 
| 352 | 
            +
                def empty?
         | 
| 353 | 
            +
                  @nodelist.size==0
         | 
| 354 | 
            +
                end
         | 
| 355 | 
            +
             | 
| 300 356 | 
             
              end
         | 
| 301 357 |  | 
| 302 358 |  | 
| @@ -327,7 +383,8 @@ module EDI::Diagram | |
| 327 383 |  | 
| 328 384 | 
             
                def initialize(name, status, rep) # :nodoc:
         | 
| 329 385 | 
             
                  @name, @status, @maxrep = name, status, rep
         | 
| 330 | 
            -
                  # | 
| 386 | 
            +
                  # @template = EDI::Segment.new(name, nil, nil)
         | 
| 387 | 
            +
                  # warn "Creating node: #{self.to_s}"
         | 
| 331 388 | 
             
                end
         | 
| 332 389 |  | 
| 333 390 | 
             
                def to_s
         | 
| @@ -430,7 +487,7 @@ module EDI::Diagram | |
| 430 487 | 
             
                #
         | 
| 431 488 | 
             
                def name;   node.name;   end
         | 
| 432 489 | 
             
                def status; node.status; end
         | 
| 433 | 
            -
                def maxrep; node.maxrep; end | 
| 490 | 
            +
                def maxrep; node.maxrep; end
         | 
| 434 491 | 
             
                def index;  node.index;  end
         | 
| 435 492 |  | 
| 436 493 | 
             
                # Returns this node instance's level in the diagram.
         | 
| @@ -475,10 +532,14 @@ module EDI::Diagram | |
| 475 532 | 
             
                  #    name = (seg.is_a? String) ? seg : seg.name
         | 
| 476 533 | 
             
                  begin
         | 
| 477 534 | 
             
                    node = self.node
         | 
| 478 | 
            -
                    #  | 
| 535 | 
            +
                    # warn "Looking for #{name} in #{self.name} @ level #{self.level} while node.maxrep=#{node.maxrep}..."
         | 
| 479 536 | 
             
                    #
         | 
| 480 537 | 
             
                    # Case "match"
         | 
| 481 538 | 
             
                    #
         | 
| 539 | 
            +
                    if node.nil?
         | 
| 540 | 
            +
                      warn "#{name}: #{@coord.offset} #{@coord.branch.sg_name} #{@coord.branch.desc} #{@coord.branch.size}"
         | 
| 541 | 
            +
                      raise "#{self}: no node!"
         | 
| 542 | 
            +
                    end
         | 
| 482 543 | 
             
                    if name === node.name # == name
         | 
| 483 544 | 
             
                      #        puts "match!"
         | 
| 484 545 | 
             
                      @coord.inst_cnt += 1
         | 
| @@ -532,7 +593,7 @@ module EDI::Diagram | |
| 532 593 | 
             
                # Returns +self+, or +nil+ if there is no tail node.
         | 
| 533 594 | 
             
                def down!
         | 
| 534 595 | 
             
                  this_node = self.node
         | 
| 535 | 
            -
                  return nil if (tail=this_node.tail).nil?
         | 
| 596 | 
            +
                  return nil if (tail=this_node.tail).nil? or tail.empty?
         | 
| 536 597 | 
             
                  # Save current co-ordinates on stack
         | 
| 537 598 | 
             
                  @coord_stack.push( @coord )
         | 
| 538 599 | 
             
                  # Init. co-ordinates for the new level:
         | 
    
        data/lib/edi4r/edifact-rexml.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # -*- encoding: iso-8859-1 -*-
         | 
| 1 2 | 
             
            # UN/EDIFACT add-ons to EDI module,
         | 
| 2 3 | 
             
            #   Methods for XML support for the UN/EDIFACT module
         | 
| 3 4 | 
             
            #
         | 
| @@ -36,12 +37,12 @@ module EDI::E | |
| 36 37 | 
             
                  _header  = _root.elements["Header"]
         | 
| 37 38 | 
             
                  _trailer = _root.elements["Trailer"]
         | 
| 38 39 | 
             
                  _una  = _header.elements["Parameter[@name='UNA']"]
         | 
| 39 | 
            -
                  _una = _una.text if _una
         | 
| 40 | 
            +
                  _una = _una.text.strip if _una
         | 
| 40 41 | 
             
                  raise "Empty UNA" if _una and _una.empty? # remove later!
         | 
| 41 42 | 
             
                  # S001: Works for both batch and interactive EDI:
         | 
| 42 43 | 
             
                  _s001 =  _header.elements["Segment/CDE[@name='S001']"]
         | 
| 43 44 | 
             
                  _version = _s001.elements["DE[@name='0002']"].text.to_i
         | 
| 44 | 
            -
                  _charset = _s001.elements["DE[@name='0001']"].text
         | 
| 45 | 
            +
                  _charset = _s001.elements["DE[@name='0001']"].text.strip
         | 
| 45 46 | 
             
                  params = { :charset => _charset, :version => _version }
         | 
| 46 47 | 
             
                  if _una
         | 
| 47 48 | 
             
                    params[:una_string] = _una
         | 
| @@ -83,7 +84,7 @@ module EDI::E | |
| 83 84 | 
             
                  # S001: Works for both batch and interactive EDI:
         | 
| 84 85 | 
             
                  _s001 =  _header.elements["Segment/CDE[@name='S001']"]
         | 
| 85 86 | 
             
                  _version = _s001.elements["DE[@name='0002']"].text.to_i
         | 
| 86 | 
            -
                  _charset = _s001.elements["DE[@name='0001']"].text
         | 
| 87 | 
            +
                  _charset = _s001.elements["DE[@name='0001']"].text.strip
         | 
| 87 88 | 
             
                  params = { :charset => _charset, :version => _version }
         | 
| 88 89 | 
             
                  if _una
         | 
| 89 90 | 
             
                    params[:una_string] = _una
         | 
    
        data/lib/edi4r/edifact.rb
    CHANGED
    
    | @@ -1,46 +1,9 @@ | |
| 1 | 
            +
            # -*- encoding: iso-8859-1 -*-
         | 
| 1 2 | 
             
            # UN/EDIFACT add-ons to EDI module,
         | 
| 2 3 | 
             
            # API to parse and create UN/EDIFACT data
         | 
| 3 4 | 
             
            #
         | 
| 4 5 | 
             
            # :include: ../../AuthorCopyright
         | 
| 5 6 | 
             
            #
         | 
| 6 | 
            -
            # $Id: edifact.rb,v 1.10 2006/08/01 11:14:07 werntges Exp $
         | 
| 7 | 
            -
            #--
         | 
| 8 | 
            -
            # $Log: edifact.rb,v $
         | 
| 9 | 
            -
            # Revision 1.10  2006/08/01 11:14:07  werntges
         | 
| 10 | 
            -
            # Release 0.9.4.1 -- see ChangeLog
         | 
| 11 | 
            -
            #
         | 
| 12 | 
            -
            # Revision 1.9  2006/05/26 16:56:41  werntges
         | 
| 13 | 
            -
            # V 0.9.3 snapshot. Many improvements (see ChangeLog), RDoc, more I-EDI support
         | 
| 14 | 
            -
            #
         | 
| 15 | 
            -
            # Revision 1.8  2006/05/01 22:23:55  werntges
         | 
| 16 | 
            -
            # Preparing for 0.9.2: See ChangeLog for new features
         | 
| 17 | 
            -
            #
         | 
| 18 | 
            -
            # Revision 1.7  2006/04/28 14:31:50  werntges
         | 
| 19 | 
            -
            # 0.9.1 snapshot
         | 
| 20 | 
            -
            #
         | 
| 21 | 
            -
            # Revision 1.6  2006/03/28 22:23:40  werntges
         | 
| 22 | 
            -
            # changed to using symbols as parameter keys, e.g. :charset
         | 
| 23 | 
            -
            # implemented as new module EDI::E, abandoning Interchange_E and alike
         | 
| 24 | 
            -
            # bug fixes re. UNA (@una, setters)
         | 
| 25 | 
            -
            #
         | 
| 26 | 
            -
            # Revision 1.5  2006/03/22 16:52:42  werntges
         | 
| 27 | 
            -
            # snapshot after edi4r-0.8.2.gem
         | 
| 28 | 
            -
            #
         | 
| 29 | 
            -
            # Revision 1.4  2004/02/19 17:31:52  heinz
         | 
| 30 | 
            -
            # HWW: Snapshot after REMADV mapping
         | 
| 31 | 
            -
            #
         | 
| 32 | 
            -
            # Revision 1.3  2004/02/14 12:10:19  heinz
         | 
| 33 | 
            -
            # HWW: Minor improvements
         | 
| 34 | 
            -
            #
         | 
| 35 | 
            -
            # Revision 1.2  2004/02/11 23:31:59  heinz
         | 
| 36 | 
            -
            # HWW: First release after finishing basic tests
         | 
| 37 | 
            -
            #
         | 
| 38 | 
            -
            # Revision 1.1  2004/02/10 00:25:13  heinz
         | 
| 39 | 
            -
            # Initial revision
         | 
| 40 | 
            -
            #
         | 
| 41 | 
            -
            #
         | 
| 42 | 
            -
            # Derived from "edi.rb" V 1.11 on 2004-02-09 by HWW
         | 
| 43 | 
            -
            #
         | 
| 44 7 | 
             
            # To-do list:
         | 
| 45 8 | 
             
            #	validate	- add functionality
         | 
| 46 9 | 
             
            #	charset		- check for valid chars (add UNOD-UNOZ)
         | 
| @@ -81,6 +44,11 @@ module EDI::E | |
| 81 44 | 
             
              #  CDE = "1234::567"		 --> ['1234','','567']
         | 
| 82 45 | 
             
              #  CDE = ":::SOMETEXT"	 --> ['','','','SOMETEXT']
         | 
| 83 46 | 
             
              #  Seg = "TAG+1++2:3:4+A?+B=C" --> ['TAG','1','','2:3:4','A+B=C']
         | 
| 47 | 
            +
              #  Seg = "FTX+PUR+1++P:aP?: 120,00 ??:nP?: 100,10??"
         | 
| 48 | 
            +
              #				 --> ['FTX','PUR','1','','P:aP?: 120,00 ??:nP?: 100,10??']
         | 
| 49 | 
            +
              #	** OR **		 --> ['FTX','PUR','1','','P:aP?: 120,00 ??:nP?: 100,10?']
         | 
| 50 | 
            +
              #  CDE = "P:aP?: 120,00 ??:nP?: 100,10??"
         | 
| 51 | 
            +
              #				 --> ['P','aP: 120,00 ?','nP: 100,10?']
         | 
| 84 52 | 
             
              #
         | 
| 85 53 | 
             
              # NOTE: This function might be a good candidate for implementation in "C"
         | 
| 86 54 | 
             
              #
         | 
| @@ -102,12 +70,12 @@ module EDI::E | |
| 102 70 | 
             
                  item += str[start...match_at]
         | 
| 103 71 | 
             
                  # Count escapes in front of separator. No real separator if odd!
         | 
| 104 72 | 
             
                  escapes = count_escapes( item, e )
         | 
| 105 | 
            -
                  if escapes | 
| 73 | 
            +
                  if escapes.odd?
         | 
| 106 74 | 
             
                    raise EDISyntaxError, "Pending escape char in #{str}" if match_at == str.length
         | 
| 107 | 
            -
                    (escapes/2+1).times {item.chop!} # chop off duplicate escapes
         | 
| 75 | 
            +
                    # (escapes/2+1).times {item.chop!} # chop off duplicate escapes
         | 
| 108 76 | 
             
                    item << s # add separator as regular character
         | 
| 109 77 | 
             
                  else # even
         | 
| 110 | 
            -
                    (escapes/2).times {item.chop!}  # chop off duplicate escapes
         | 
| 78 | 
            +
                    # (escapes/2).times {item.chop!}  # chop off duplicate escapes
         | 
| 111 79 | 
             
                    results << item
         | 
| 112 80 | 
             
                    item = ''
         | 
| 113 81 | 
             
                  end
         | 
| @@ -152,8 +120,9 @@ module EDI::E | |
| 152 120 | 
             
              #
         | 
| 153 121 | 
             
              # Currently supported formats: 101, 102, 201, 203, 204
         | 
| 154 122 |  | 
| 155 | 
            -
              class ::Time
         | 
| 156 | 
            -
             | 
| 123 | 
            +
              class EDI::Time
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                @@to_s_callbacks << :to_s_edifact
         | 
| 157 126 |  | 
| 158 127 | 
             
                def Time.edifact(str, fmt=102)
         | 
| 159 128 | 
             
                  msg = "Time.edifact: #{str} does not match format #{fmt}"
         | 
| @@ -163,29 +132,29 @@ module EDI::E | |
| 163 132 | 
             
                    raise msg unless rc and rc==0; warn msg if $4
         | 
| 164 133 | 
             
                    year = $1.to_i
         | 
| 165 134 | 
             
                    year += (year < 69) ? 2000 : 1900 # See ParseDate
         | 
| 166 | 
            -
                    dtm = Time.local(year, $2, $3)
         | 
| 135 | 
            +
                    dtm = EDI::Time.local(year, $2, $3)
         | 
| 167 136 |  | 
| 168 137 | 
             
                  when '102'
         | 
| 169 138 | 
             
                    rc = str =~ /(\d\d\d\d)(\d\d)(\d\d)(.+)?/
         | 
| 170 139 | 
             
                    raise msg unless rc and rc==0; warn msg if $4
         | 
| 171 | 
            -
                    dtm = Time.local($1, $2, $3)
         | 
| 140 | 
            +
                    dtm = EDI::Time.local($1, $2, $3)
         | 
| 172 141 |  | 
| 173 142 | 
             
                  when '201'
         | 
| 174 143 | 
             
                    rc = str =~ /(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(.+)?/
         | 
| 175 144 | 
             
                    raise msg unless rc and rc==0; warn msg if $6
         | 
| 176 145 | 
             
                    year = $1.to_i
         | 
| 177 146 | 
             
                    year += (year < 69) ? 2000 : 1900 # See ParseDate
         | 
| 178 | 
            -
                    dtm = Time.local(year, $2, $3, $4, $5)
         | 
| 147 | 
            +
                    dtm = EDI::Time.local(year, $2, $3, $4, $5)
         | 
| 179 148 |  | 
| 180 149 | 
             
                  when '203'
         | 
| 181 150 | 
             
                    rc = str =~ /(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(.+)?/
         | 
| 182 151 | 
             
                    raise msg unless rc and rc==0; warn msg if $6
         | 
| 183 | 
            -
                    dtm = Time.local($1, $2, $3, $4, $5)
         | 
| 152 | 
            +
                    dtm = EDI::Time.local($1, $2, $3, $4, $5)
         | 
| 184 153 |  | 
| 185 154 | 
             
                  when '204'
         | 
| 186 155 | 
             
                    rc = str =~ /(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(.+)?/
         | 
| 187 156 | 
             
                    raise msg unless rc and rc==0; warn msg if $7
         | 
| 188 | 
            -
                    dtm = Time.local($1, $2, $3, $4, $5, $6)
         | 
| 157 | 
            +
                    dtm = EDI::Time.local($1, $2, $3, $4, $5, $6)
         | 
| 189 158 |  | 
| 190 159 | 
             
                  else
         | 
| 191 160 | 
             
                    raise "Time.edifact: Format #{fmt} not supported - sorry"
         | 
| @@ -194,10 +163,7 @@ module EDI::E | |
| 194 163 | 
             
                  dtm
         | 
| 195 164 | 
             
                end
         | 
| 196 165 |  | 
| 197 | 
            -
                 | 
| 198 | 
            -
             | 
| 199 | 
            -
                def to_s
         | 
| 200 | 
            -
                  return to_s_orig unless @format
         | 
| 166 | 
            +
                def to_s_edifact
         | 
| 201 167 | 
             
                  case @format.to_s
         | 
| 202 168 | 
             
                  when '101'
         | 
| 203 169 | 
             
                    "%02d%02d%02d" % [year % 100, mon, day]
         | 
| @@ -209,9 +175,8 @@ module EDI::E | |
| 209 175 | 
             
                    "%04d%02d%02d%02d%02d" % [year, mon, day, hour, min]
         | 
| 210 176 | 
             
                  when '204'
         | 
| 211 177 | 
             
                    "%04d%02d%02d%02d%02d%2d" % [year, mon, day, hour, min, sec]
         | 
| 212 | 
            -
                  else | 
| 213 | 
            -
             | 
| 214 | 
            -
            } not supported - sorry"
         | 
| 178 | 
            +
                  else
         | 
| 179 | 
            +
            	nil # nil indicates that there was no matching format
         | 
| 215 180 | 
             
                  end
         | 
| 216 181 | 
             
                end
         | 
| 217 182 | 
             
              end
         | 
| @@ -341,8 +306,10 @@ module EDI::E | |
| 341 306 | 
             
                  special_chars.push @rep_sep if root.version == 4
         | 
| 342 307 | 
             
                  special_chars = special_chars.map{|c| c.chr}
         | 
| 343 308 | 
             
                  @pattern_esc = Regexp.new( [ '([', special_chars, '])' ].flatten.join)
         | 
| 309 | 
            +
                  esc_str = @esc_char.chr
         | 
| 310 | 
            +
                  esc_str << '\\' if esc_str == '\\' # Must escape '\' in a regex
         | 
| 344 311 | 
             
                  @pattern_unesc = Regexp.new( [ 
         | 
| 345 | 
            -
                                                 '([^',  | 
| 312 | 
            +
                                                 '([^', esc_str, ']?)', '[', esc_str,
         | 
| 346 313 | 
             
                                                 ']([', special_chars,'])' 
         | 
| 347 314 | 
             
                                               ].flatten.join )
         | 
| 348 315 | 
             
                  root.show_una = true
         | 
| @@ -397,11 +364,11 @@ module EDI::E | |
| 397 364 | 
             
                #
         | 
| 398 365 | 
             
                # === Notes
         | 
| 399 366 | 
             
                # * Date and time in S004 are set to the current values automatically.
         | 
| 400 | 
            -
                # * Add or change any data element later | 
| 367 | 
            +
                # * Add or change any data element later, except those in S001.
         | 
| 401 368 | 
             
                #
         | 
| 402 369 | 
             
                # === Examples:
         | 
| 403 370 | 
             
                # - ic = EDI::E::Interchange.new  # Empty interchange, default settings
         | 
| 404 | 
            -
                # - ic = EDI::E::Interchange.new(:charset=>'UNOC' | 
| 371 | 
            +
                # - ic = EDI::E::Interchange.new(:charset=>'UNOC', :output_mode=>:linebreak)
         | 
| 405 372 |  | 
| 406 373 | 
             
                def initialize( user_par={} )
         | 
| 407 374 | 
             
                  super( user_par ) # just in case...
         | 
| @@ -437,7 +404,7 @@ module EDI::E | |
| 437 404 | 
             
                    @header.cS001.d0002 = par[:version]
         | 
| 438 405 |  | 
| 439 406 | 
             
                    @header.cS002.d0004 = par[:sender] unless par[:sender].nil?
         | 
| 440 | 
            -
                    @header.cS003.d0010 = par[:recipient] unless par[: | 
| 407 | 
            +
                    @header.cS003.d0010 = par[:recipient] unless par[:recipient].nil?
         | 
| 441 408 | 
             
                    @header.cS302.d0300 = par[:interchange_control_reference]
         | 
| 442 409 | 
             
                    # FIXME: More to do in S302...
         | 
| 443 410 |  | 
| @@ -450,6 +417,7 @@ module EDI::E | |
| 450 417 | 
             
                    @trailer.d0036 = 0
         | 
| 451 418 | 
             
                    ch, ct = @header.cS302, @trailer.cS302
         | 
| 452 419 | 
             
                    ct.d0300, ct.d0303, ct.d0051, ct.d0304 = ch.d0300, ch.d0303, ch.d0051, ch.d0304 
         | 
| 420 | 
            +
             | 
| 453 421 | 
             
                  else # Batch EDI
         | 
| 454 422 |  | 
| 455 423 | 
             
                    @header = new_segment('UNB')
         | 
| @@ -457,7 +425,7 @@ module EDI::E | |
| 457 425 | 
             
                    @header.cS001.d0001 = par[:charset]
         | 
| 458 426 | 
             
                    @header.cS001.d0002 = par[:version]
         | 
| 459 427 | 
             
                    @header.cS002.d0004 = par[:sender] unless par[:sender].nil?
         | 
| 460 | 
            -
                    @header.cS003.d0010 = par[:recipient] unless par[: | 
| 428 | 
            +
                    @header.cS003.d0010 = par[:recipient] unless par[:recipient].nil?
         | 
| 461 429 | 
             
                    @header.d0020 = par[:interchange_control_reference]
         | 
| 462 430 |  | 
| 463 431 | 
             
                    x= :application_reference;    @header.d0026 = par[x] unless par[x].nil?
         | 
| @@ -473,108 +441,14 @@ module EDI::E | |
| 473 441 | 
             
                  end
         | 
| 474 442 | 
             
                end
         | 
| 475 443 |  | 
| 476 | 
            -
             | 
| 477 444 | 
             
                #
         | 
| 478 445 | 
             
                # Reads EDIFACT data from given stream (default: $stdin),
         | 
| 479 446 | 
             
                # parses it and returns an Interchange object
         | 
| 480 447 | 
             
                #
         | 
| 481 448 | 
             
                def Interchange.parse( hnd=$stdin, auto_validate=true )
         | 
| 482 | 
            -
                   | 
| 483 | 
            -
                   | 
| 484 | 
            -
                   | 
| 485 | 
            -
             | 
| 486 | 
            -
                  ic, segment_list = Interchange.parse_buffer( buf )
         | 
| 487 | 
            -
                  # Remember to update ndb to SV4-1 now if d0076 of UNB/S001 tells so
         | 
| 488 | 
            -
             | 
| 489 | 
            -
                  # Deal with 'trash' after UNZ
         | 
| 490 | 
            -
             | 
| 491 | 
            -
                  if ic.is_iedi?
         | 
| 492 | 
            -
                    init_seg = Regexp.new('^UIB'); tag_init = 'UIB'
         | 
| 493 | 
            -
                    exit_seg = Regexp.new('^UIZ'); tag_exit = 'UIZ'
         | 
| 494 | 
            -
                  else
         | 
| 495 | 
            -
                    init_seg = Regexp.new('^UNB'); tag_init = 'UNB'
         | 
| 496 | 
            -
                    exit_seg = Regexp.new('^UNZ'); tag_exit = 'UNZ'
         | 
| 497 | 
            -
                  end
         | 
| 498 | 
            -
                  
         | 
| 499 | 
            -
                  last_seg = nil
         | 
| 500 | 
            -
                  loop do
         | 
| 501 | 
            -
                    last_seg = segment_list.pop
         | 
| 502 | 
            -
                    case last_seg
         | 
| 503 | 
            -
                    when /^[A-Z]{3}/ # Segment tag?
         | 
| 504 | 
            -
                      unless last_seg =~ exit_seg
         | 
| 505 | 
            -
                        raise "Parse error: #{tag_exit} is not last segment! Found: #{last_seg}"
         | 
| 506 | 
            -
                      end
         | 
| 507 | 
            -
                      break
         | 
| 508 | 
            -
                    when /\n/, /\r\n/, ''
         | 
| 509 | 
            -
                      # ignore linebreaks at end of file, do not warn.
         | 
| 510 | 
            -
                    else
         | 
| 511 | 
            -
                      warn "WARNING: Data found after #{tag_exit} segment - ignored!"
         | 
| 512 | 
            -
                      warn "Found: \'#{last_seg}\'"
         | 
| 513 | 
            -
                    end
         | 
| 514 | 
            -
                  end
         | 
| 515 | 
            -
                  trailer = Segment.parse(ic, last_seg, tag_exit)
         | 
| 516 | 
            -
             | 
| 517 | 
            -
                  # Assure that there is only one UNB/UNZ or UIB/UIZ
         | 
| 518 | 
            -
             | 
| 519 | 
            -
                  err_flag = false
         | 
| 520 | 
            -
                  segment_list.each do |seg|
         | 
| 521 | 
            -
                    if seg =~ init_seg
         | 
| 522 | 
            -
                      warn "ERROR: Another interchange header found in file!"
         | 
| 523 | 
            -
                      err_flag = true
         | 
| 524 | 
            -
                    end
         | 
| 525 | 
            -
                    if seg =~ exit_seg
         | 
| 526 | 
            -
                      warn "ERROR: Another interchange trailer found in file!"
         | 
| 527 | 
            -
                      err_flag = true
         | 
| 528 | 
            -
                    end
         | 
| 529 | 
            -
                  end
         | 
| 530 | 
            -
                  raise "FATAL ERROR - exiting" if err_flag
         | 
| 531 | 
            -
             | 
| 532 | 
            -
                  # OK, ready to deal with content now:
         | 
| 533 | 
            -
             | 
| 534 | 
            -
                  case segment_list[0]
         | 
| 535 | 
            -
                  when /^UNH/
         | 
| 536 | 
            -
                    init_seg = Regexp.new('^UNH')
         | 
| 537 | 
            -
                    exit_seg = Regexp.new('^UNT')
         | 
| 538 | 
            -
                    group_mode = false
         | 
| 539 | 
            -
                  when /^UNG/
         | 
| 540 | 
            -
                    init_seg = Regexp.new('^UNG')
         | 
| 541 | 
            -
                    exit_seg = Regexp.new('^UNE')
         | 
| 542 | 
            -
                    group_mode = true
         | 
| 543 | 
            -
                  when /^UIH/ # There is no 'UIG'!
         | 
| 544 | 
            -
                    init_seg = Regexp.new('^UIH')
         | 
| 545 | 
            -
                    exit_seg = Regexp.new('^UIT')
         | 
| 546 | 
            -
                    group_mode = false
         | 
| 547 | 
            -
                  else
         | 
| 548 | 
            -
                    raise "Expected: UNH, UNG, or UIH. Found: #{segment_list[0]}"
         | 
| 549 | 
            -
                  end
         | 
| 550 | 
            -
                  
         | 
| 551 | 
            -
                  while segbuf = segment_list.shift
         | 
| 552 | 
            -
                    case segbuf
         | 
| 553 | 
            -
             | 
| 554 | 
            -
                    when init_seg
         | 
| 555 | 
            -
                      sub_list = Array.new
         | 
| 556 | 
            -
                      sub_list.push segbuf
         | 
| 557 | 
            -
             | 
| 558 | 
            -
                    when exit_seg
         | 
| 559 | 
            -
                      sub_list.push segbuf	
         | 
| 560 | 
            -
                      if group_mode
         | 
| 561 | 
            -
                        ic.add( MsgGroup.parse(ic, sub_list), auto_validate )
         | 
| 562 | 
            -
                      else
         | 
| 563 | 
            -
                        ic.add( Message.parse(ic, sub_list), auto_validate )
         | 
| 564 | 
            -
                      end
         | 
| 565 | 
            -
             | 
| 566 | 
            -
                    else
         | 
| 567 | 
            -
                      sub_list.push segbuf	
         | 
| 568 | 
            -
                    end
         | 
| 569 | 
            -
             | 
| 570 | 
            -
                  end # while
         | 
| 571 | 
            -
             | 
| 572 | 
            -
                  # Finally add the trailer from the originally read data,
         | 
| 573 | 
            -
                  # thereby overwriting the temporary interchange trailer.
         | 
| 574 | 
            -
                  # Note that the temporary trailer got modified by add()ing 
         | 
| 575 | 
            -
                  # to the interchange.
         | 
| 576 | 
            -
                  ic.trailer = trailer
         | 
| 577 | 
            -
                  ic
         | 
| 449 | 
            +
                  builder = StreamingBuilder.new( auto_validate )
         | 
| 450 | 
            +
                  builder.go( hnd )
         | 
| 451 | 
            +
                  builder.interchange
         | 
| 578 452 | 
             
                end
         | 
| 579 453 |  | 
| 580 454 | 
             
                #
         | 
| @@ -586,47 +460,24 @@ module EDI::E | |
| 586 460 | 
             
                # Intended use: 
         | 
| 587 461 | 
             
                #   Efficient routing by reading just UNB data: sender/recipient/ref/test
         | 
| 588 462 | 
             
                #
         | 
| 589 | 
            -
                def Interchange.peek(hnd=$stdin,  | 
| 590 | 
            -
                   | 
| 591 | 
            -
                   | 
| 592 | 
            -
             | 
| 593 | 
            -
             | 
| 594 | 
            -
                  # Create a dummy trailer
         | 
| 595 | 
            -
                  tag = ic.is_iedi? ? 'UIZ' : 'UNZ'
         | 
| 596 | 
            -
                  trailer_string = tag.dup << ic.una.de_sep << '0' << ic.una.de_sep << '0'
         | 
| 597 | 
            -
                  ic.trailer= Segment.parse(ic, trailer_string, tag)
         | 
| 598 | 
            -
             | 
| 599 | 
            -
                  ic
         | 
| 600 | 
            -
                end
         | 
| 601 | 
            -
             | 
| 602 | 
            -
                #
         | 
| 603 | 
            -
                # INTERNAL USE ONLY:
         | 
| 604 | 
            -
                # Turn buffer into array of segments (array size <= s_max),
         | 
| 605 | 
            -
                # read UNB/UIB, create an Interchange object with a header,
         | 
| 606 | 
            -
                # return this interchange and the array of segments
         | 
| 607 | 
            -
                #
         | 
| 608 | 
            -
                def Interchange.parse_buffer( buf, s_max=0 ) # :nodoc:
         | 
| 609 | 
            -
                  case buf
         | 
| 610 | 
            -
                    # UN/EDIFACT case
         | 
| 611 | 
            -
                  when /^(UNA......)?\r?\n?U([IN])B.(UNO[A-Z]).([1-4])/
         | 
| 612 | 
            -
                    par = @@interchange_defaults.dup
         | 
| 613 | 
            -
                    par[:una_string], par[:charset], par[:version], par[:i_edi] =
         | 
| 614 | 
            -
                      $1, $3, $4.to_i, $2=='I'
         | 
| 615 | 
            -
                    ic = Interchange.new( par )
         | 
| 616 | 
            -
                    buf.sub!(/^UNA....../,'') # remove pseudo segment
         | 
| 617 | 
            -
                    
         | 
| 463 | 
            +
                def Interchange.peek(hnd=$stdin, params={}) # Handle to input stream
         | 
| 464 | 
            +
                  builder = StreamingBuilder.new( false )
         | 
| 465 | 
            +
                  if params[:deep_peek]
         | 
| 466 | 
            +
                    def builder.on_segment( s, tag )
         | 
| 467 | 
            +
                    end
         | 
| 618 468 | 
             
                  else
         | 
| 619 | 
            -
                     | 
| 469 | 
            +
                    def builder.on_ung( s )
         | 
| 470 | 
            +
                      throw :done
         | 
| 471 | 
            +
                    end
         | 
| 472 | 
            +
                    def builder.on_unh_uih( s, tag )
         | 
| 473 | 
            +
                      throw :done  # FIXME: UNZ??
         | 
| 474 | 
            +
                    end
         | 
| 620 475 | 
             
                  end
         | 
| 621 | 
            -
             | 
| 622 | 
            -
                   | 
| 623 | 
            -
                  # Remove <cr><lf> (some sources are not EDIFACT compliant)
         | 
| 624 | 
            -
                  segments.each {|s| s.sub!(/\s*(.*)/, '\1')}
         | 
| 625 | 
            -
                  ic.header = Segment.parse(ic, segments.shift, ic.is_iedi? ? 'UIB':'UNB')
         | 
| 626 | 
            -
             | 
| 627 | 
            -
                  [ic, segments]
         | 
| 476 | 
            +
                  builder.go( hnd )
         | 
| 477 | 
            +
                  builder.interchange
         | 
| 628 478 | 
             
                end
         | 
| 629 479 |  | 
| 480 | 
            +
             | 
| 630 481 | 
             
                #
         | 
| 631 482 | 
             
                # Returns +true+ if this is an I-EDI interchange (Interactive EDI)
         | 
| 632 483 | 
             
                #
         | 
| @@ -1009,8 +860,8 @@ module EDI::E | |
| 1009 860 | 
             
                end
         | 
| 1010 861 |  | 
| 1011 862 |  | 
| 1012 | 
            -
                def add( msg )
         | 
| 1013 | 
            -
                  super
         | 
| 863 | 
            +
                def add( msg, auto_validate=true )
         | 
| 864 | 
            +
                  super( msg )
         | 
| 1014 865 | 
             
                  @trailer.d0060 = @trailer.d0060.to_i if @trailer.d0060.is_a? String
         | 
| 1015 866 | 
             
                  @trailer.d0060 += 1
         | 
| 1016 867 | 
             
                end
         | 
| @@ -1059,8 +910,10 @@ module EDI::E | |
| 1059 910 | 
             
                }
         | 
| 1060 911 | 
             
                @@message_default_keys = @@message_defaults.keys
         | 
| 1061 912 |  | 
| 1062 | 
            -
                # Creates an empty UN/EDIFACT message
         | 
| 1063 | 
            -
                # | 
| 913 | 
            +
                # Creates an empty UN/EDIFACT message.
         | 
| 914 | 
            +
                #
         | 
| 915 | 
            +
                # Don't use directly - call method +new_message+ of class Interchange 
         | 
| 916 | 
            +
                # or MsgGroup instead!
         | 
| 1064 917 | 
             
                #
         | 
| 1065 918 | 
             
                # == First parameter
         | 
| 1066 919 | 
             
                #
         | 
| @@ -1109,6 +962,7 @@ module EDI::E | |
| 1109 962 | 
             
                      :d0065 => @name, :d0052=> @version, :d0054=> @release, 
         | 
| 1110 963 | 
             
                      :d0051 => @resp_agency, :d0057 => @subset, :is_iedi => root.is_iedi?
         | 
| 1111 964 | 
             
                    }
         | 
| 965 | 
            +
                    par[:d0002] = self.root.header.cS001.d0002 if %w/CONTRL AUTACK KEYMAN/.include? @name # TODO: Experimental - revise!
         | 
| 1112 966 | 
             
                    @maindata = EDI::Dir::Directory.create(root.syntax, par )
         | 
| 1113 967 |  | 
| 1114 968 | 
             
                    if root.is_iedi?
         | 
| @@ -1171,7 +1025,7 @@ module EDI::E | |
| 1171 1025 |  | 
| 1172 1026 | 
             
                # Internal use only!
         | 
| 1173 1027 |  | 
| 1174 | 
            -
                def parse_segment(buf, tag) # :nodoc:
         | 
| 1028 | 
            +
                def parse_segment(buf, tag=nil) # :nodoc:
         | 
| 1175 1029 | 
             
                  Segment.parse(self, buf, tag)
         | 
| 1176 1030 | 
             
                end
         | 
| 1177 1031 |  | 
| @@ -1278,7 +1132,7 @@ module EDI::E | |
| 1278 1132 |  | 
| 1279 1133 | 
             
                  ni.seek!( @header )
         | 
| 1280 1134 | 
             
                  @header.update_with( ni )
         | 
| 1281 | 
            -
                  each do |seg| | 
| 1135 | 
            +
                  each do |seg|
         | 
| 1282 1136 | 
             
                    if ni.seek!(seg)
         | 
| 1283 1137 | 
             
                      seg.update_with( ni )
         | 
| 1284 1138 | 
             
                    else
         | 
| @@ -1375,7 +1229,7 @@ module EDI::E | |
| 1375 1229 |  | 
| 1376 1230 | 
             
                    # FIXME: Code redundancy in type detection - remove later!
         | 
| 1377 1231 | 
             
                    case id
         | 
| 1378 | 
            -
                    when /[CES]\d{3}/ | 
| 1232 | 
            +
                    when /[CES]\d{3}/	# Composite
         | 
| 1379 1233 | 
             
                      add new_CDE(id, status)
         | 
| 1380 1234 | 
             
                    when /\d{4}/		# Simple DE
         | 
| 1381 1235 | 
             
                      add new_DE(id, status, fmt_of_DE(id))
         | 
| @@ -1400,6 +1254,7 @@ module EDI::E | |
| 1400 1254 |  | 
| 1401 1255 | 
             
                def Segment.parse (p, buf, tag_expected=nil)
         | 
| 1402 1256 | 
             
                  # Buffer contains a single segment
         | 
| 1257 | 
            +
                  
         | 
| 1403 1258 | 
             
                  obj_list = EDI::E::edi_split( buf, p.root.una.de_sep, p.root.una.esc_char )
         | 
| 1404 1259 | 
             
                  tag = obj_list.shift 		  # First entry must be the segment tag
         | 
| 1405 1260 |  | 
| @@ -1624,4 +1479,452 @@ module EDI::E | |
| 1624 1479 | 
             
                end
         | 
| 1625 1480 | 
             
              end
         | 
| 1626 1481 |  | 
| 1627 | 
            -
             | 
| 1482 | 
            +
             | 
| 1483 | 
            +
              #########################################################################
         | 
| 1484 | 
            +
              #
         | 
| 1485 | 
            +
              # = Class StreamingParser
         | 
| 1486 | 
            +
              #
         | 
| 1487 | 
            +
              # == Introduction
         | 
| 1488 | 
            +
              #
         | 
| 1489 | 
            +
              # Turning a whole EDI interchange into an EDI::E::Interchange object
         | 
| 1490 | 
            +
              # with method +parse+ is both convenient and memory consuming.
         | 
| 1491 | 
            +
              # Sometimes, interchanges become just too large to keep them completely
         | 
| 1492 | 
            +
              # in memory. 
         | 
| 1493 | 
            +
              # The same reasoning holds for large XML documents, where there is a
         | 
| 1494 | 
            +
              # common solution: The SAX/SAX2 API, a streaming approach. This class
         | 
| 1495 | 
            +
              # implements the same idea for UN/EDIFACT data.
         | 
| 1496 | 
            +
              #
         | 
| 1497 | 
            +
              # Use StreamingParser instances to parse UN/EDIFACT data *sequentially*.
         | 
| 1498 | 
            +
              # Sequential parsing saves main memory and is applicable to
         | 
| 1499 | 
            +
              # arbitrarily large interchanges.
         | 
| 1500 | 
            +
              #
         | 
| 1501 | 
            +
              # At its core lies method +go+. It scans the input stream and
         | 
| 1502 | 
            +
              # employs callbacks <tt>on_*</tt> which implement most of the parser tasks.
         | 
| 1503 | 
            +
              #
         | 
| 1504 | 
            +
              # == Syntax check
         | 
| 1505 | 
            +
              #
         | 
| 1506 | 
            +
              # Without your customizing the callbacks, this parser just scans
         | 
| 1507 | 
            +
              # through the data. Only callback <tt>on_error()</tt> contains code:
         | 
| 1508 | 
            +
              # It raises an exception telling you about the location and kind
         | 
| 1509 | 
            +
              # of syntax error encountered.
         | 
| 1510 | 
            +
              #
         | 
| 1511 | 
            +
              # === Example: Syntax check
         | 
| 1512 | 
            +
              #
         | 
| 1513 | 
            +
              #   parser = EDI::E::StreamingParser.new
         | 
| 1514 | 
            +
              #   parser.go( File.open 'damaged_file.edi' )
         | 
| 1515 | 
            +
              #   --> EDI::EDISyntaxError at offset 1234, last chars = UNt+1+0
         | 
| 1516 | 
            +
              #
         | 
| 1517 | 
            +
              #
         | 
| 1518 | 
            +
              # == Callbacks
         | 
| 1519 | 
            +
              #
         | 
| 1520 | 
            +
              # Most callbacks provided here are just empty shells. They usually receive
         | 
| 1521 | 
            +
              # a string of interest (a segment content, i.e. everything from the segment
         | 
| 1522 | 
            +
              # tag to and excluding the segment terminator) and also the
         | 
| 1523 | 
            +
              # segment tag as a separate string when tags could differ.
         | 
| 1524 | 
            +
              #
         | 
| 1525 | 
            +
              # Overwrite them to adapt the parser to your needs!
         | 
| 1526 | 
            +
              #
         | 
| 1527 | 
            +
              # === Example: Counting segments
         | 
| 1528 | 
            +
              #
         | 
| 1529 | 
            +
              #   class MyParser < EDI::E::StreamingParser
         | 
| 1530 | 
            +
              #     attr_reader :counters
         | 
| 1531 | 
            +
              #
         | 
| 1532 | 
            +
              #     def initialize
         | 
| 1533 | 
            +
              #       @counters = Hash.new(0)
         | 
| 1534 | 
            +
              #       super
         | 
| 1535 | 
            +
              #     end
         | 
| 1536 | 
            +
              #
         | 
| 1537 | 
            +
              #     def on_segment( s, tag )
         | 
| 1538 | 
            +
              #       @counters[tag] += 1
         | 
| 1539 | 
            +
              #     end
         | 
| 1540 | 
            +
              #   end
         | 
| 1541 | 
            +
              #
         | 
| 1542 | 
            +
              #   parser = MyParser.new
         | 
| 1543 | 
            +
              #   parser.go( File.open 'myfile.edi' )
         | 
| 1544 | 
            +
              #   puts "Segment tag statistics:"
         | 
| 1545 | 
            +
              #   parser.counters.keys.sort.each do |tag|
         | 
| 1546 | 
            +
              #     print "%03s: %4d\n" % [ tag, parser.counters[tag] ]
         | 
| 1547 | 
            +
              #   end
         | 
| 1548 | 
            +
              #
         | 
| 1549 | 
            +
              # == Want to save time? Throw <tt>:done</tt> when already done!
         | 
| 1550 | 
            +
              #
         | 
| 1551 | 
            +
              # Most callbacks may <b>terminate further parsing</b> by throwing
         | 
| 1552 | 
            +
              # symbol <tt>:done</tt>. This saves a lot of time e.g. if you already
         | 
| 1553 | 
            +
              # found what you were looking for. Otherwise, parsing continues
         | 
| 1554 | 
            +
              # until +getc+ hits +EOF+ or an error occurs.
         | 
| 1555 | 
            +
              #
         | 
| 1556 | 
            +
              # === Example: A simple search
         | 
| 1557 | 
            +
              #
         | 
| 1558 | 
            +
              #   parser = EDI::E::StreamingParser.new
         | 
| 1559 | 
            +
              #   def parser.on_segment( s, tag ) # singleton
         | 
| 1560 | 
            +
              #     if tag == 'ADJ'
         | 
| 1561 | 
            +
              #       puts "Interchange contains at least one segment ADJ !"
         | 
| 1562 | 
            +
              #       puts "Here is its contents: #{s}"
         | 
| 1563 | 
            +
              #       throw :done   # Skip further parsing
         | 
| 1564 | 
            +
              #     end
         | 
| 1565 | 
            +
              #   end
         | 
| 1566 | 
            +
              #   parser.go( File.open 'myfile.edi' )
         | 
| 1567 | 
            +
                
         | 
| 1568 | 
            +
              class StreamingParser
         | 
| 1569 | 
            +
             | 
| 1570 | 
            +
                def initialize
         | 
| 1571 | 
            +
                  @path = 'input stream'
         | 
| 1572 | 
            +
                end
         | 
| 1573 | 
            +
             | 
| 1574 | 
            +
                # Convenience method. Returns the path of the File object
         | 
| 1575 | 
            +
                # passed to method +go+ or just string 'input stream'
         | 
| 1576 | 
            +
                def path
         | 
| 1577 | 
            +
                  @path
         | 
| 1578 | 
            +
                end
         | 
| 1579 | 
            +
             | 
| 1580 | 
            +
                # Called at start of reading - overwrite for your init purposes.
         | 
| 1581 | 
            +
                # Note: Must *not* throw <tt>:done</tt> !
         | 
| 1582 | 
            +
                #
         | 
| 1583 | 
            +
                def on_interchange_start
         | 
| 1584 | 
            +
                end
         | 
| 1585 | 
            +
             | 
| 1586 | 
            +
                # Called at EOF - overwrite for your cleanup purposes.
         | 
| 1587 | 
            +
                # Note: Must *not* throw <tt>:done</tt> !
         | 
| 1588 | 
            +
                #
         | 
| 1589 | 
            +
                def on_interchange_end
         | 
| 1590 | 
            +
                end
         | 
| 1591 | 
            +
             | 
| 1592 | 
            +
                # Called when UNA pseudo segment encountered
         | 
| 1593 | 
            +
                #
         | 
| 1594 | 
            +
                def on_una( s )
         | 
| 1595 | 
            +
                end
         | 
| 1596 | 
            +
             | 
| 1597 | 
            +
                # Called when UNB or UIB encountered
         | 
| 1598 | 
            +
                #
         | 
| 1599 | 
            +
                def on_unb_uib( s, tag )
         | 
| 1600 | 
            +
                end
         | 
| 1601 | 
            +
             | 
| 1602 | 
            +
                # Called when UNZ or UIZ encountered
         | 
| 1603 | 
            +
                #
         | 
| 1604 | 
            +
                def on_unz_uiz( s, tag )
         | 
| 1605 | 
            +
                end
         | 
| 1606 | 
            +
             | 
| 1607 | 
            +
                # Called when UNG encountered
         | 
| 1608 | 
            +
                #
         | 
| 1609 | 
            +
                def on_ung( s )
         | 
| 1610 | 
            +
                end
         | 
| 1611 | 
            +
             | 
| 1612 | 
            +
                # Called when UNE encountered
         | 
| 1613 | 
            +
                #
         | 
| 1614 | 
            +
                def on_une( s )
         | 
| 1615 | 
            +
                end
         | 
| 1616 | 
            +
             | 
| 1617 | 
            +
                # Called when UNH or UIH encountered
         | 
| 1618 | 
            +
                #
         | 
| 1619 | 
            +
                def on_unh_uih( s, tag )
         | 
| 1620 | 
            +
                end
         | 
| 1621 | 
            +
             | 
| 1622 | 
            +
                # Called when UNT or UIT encountered
         | 
| 1623 | 
            +
                #
         | 
| 1624 | 
            +
                def on_unt_uit( s, tag )
         | 
| 1625 | 
            +
                end
         | 
| 1626 | 
            +
             | 
| 1627 | 
            +
                # Called when any other segment encountered
         | 
| 1628 | 
            +
                #
         | 
| 1629 | 
            +
                def on_segment( s, tag )
         | 
| 1630 | 
            +
                end
         | 
| 1631 | 
            +
             | 
| 1632 | 
            +
                # This callback is usually kept empty. It is called when the parser
         | 
| 1633 | 
            +
                # finds strings between segments or in front of or trailing an interchange.
         | 
| 1634 | 
            +
                #
         | 
| 1635 | 
            +
                # Strictly speaking, such strings are not permitted by the UN/EDIFACT
         | 
| 1636 | 
            +
                # syntax rules (ISO 9573). However, it is quite common to put a line break
         | 
| 1637 | 
            +
                # between segments for better readability. The default settings thus 
         | 
| 1638 | 
            +
                # ignore such occurrences.
         | 
| 1639 | 
            +
                #
         | 
| 1640 | 
            +
                # If you need strict conformance checking, feel free to put some code
         | 
| 1641 | 
            +
                # into this callback method, otherwise just ignore it.
         | 
| 1642 | 
            +
                # 
         | 
| 1643 | 
            +
                #
         | 
| 1644 | 
            +
                def on_other( s )
         | 
| 1645 | 
            +
                end
         | 
| 1646 | 
            +
             | 
| 1647 | 
            +
                # Called upon syntax errors. Parsing should be aborted now.
         | 
| 1648 | 
            +
                #
         | 
| 1649 | 
            +
                def on_error(err, offset, fragment, c=nil)
         | 
| 1650 | 
            +
                  raise err, "offset = %d, last chars = %s%s" % 
         | 
| 1651 | 
            +
                    [offset, fragment, c.nil? ? '<EOF>' : c.chr]
         | 
| 1652 | 
            +
                end
         | 
| 1653 | 
            +
             | 
| 1654 | 
            +
                #
         | 
| 1655 | 
            +
                # The one-pass reader & dispatcher of segments, SAX-style.
         | 
| 1656 | 
            +
                #
         | 
| 1657 | 
            +
                # It reads sequentially through the given stream of octets and 
         | 
| 1658 | 
            +
                # generates calls to the callbacks <tt>on_...</tt>
         | 
| 1659 | 
            +
                # Parameter +hnd+ may be any object supporting method +getc+.
         | 
| 1660 | 
            +
                #
         | 
| 1661 | 
            +
                def go( hnd )
         | 
| 1662 | 
            +
                  state, offset, iedi, item, tag, una = :outside, 0, false, '', '', ''
         | 
| 1663 | 
            +
                  seg_term, esc_char = nil, ?? # @ic.una.seg_term, @ic.una.esc_char
         | 
| 1664 | 
            +
                  una_count = uib_unb_count = nil
         | 
| 1665 | 
            +
             | 
| 1666 | 
            +
                  @path = hnd.path if hnd.respond_to? :path
         | 
| 1667 | 
            +
             | 
| 1668 | 
            +
                  self.on_interchange_start
         | 
| 1669 | 
            +
             | 
| 1670 | 
            +
                  catch(:done) do
         | 
| 1671 | 
            +
                    loop do
         | 
| 1672 | 
            +
                      c = hnd.getc
         | 
| 1673 | 
            +
             | 
| 1674 | 
            +
                      case state # State machine
         | 
| 1675 | 
            +
             | 
| 1676 | 
            +
                        # Characters outside of a segment or UNA context
         | 
| 1677 | 
            +
                      when :outside
         | 
| 1678 | 
            +
                        case c
         | 
| 1679 | 
            +
             | 
| 1680 | 
            +
                        when nil
         | 
| 1681 | 
            +
                          break # Regular exit at EOF
         | 
| 1682 | 
            +
             | 
| 1683 | 
            +
                        when (?A..?Z)
         | 
| 1684 | 
            +
                          unless item.empty? # Flush
         | 
| 1685 | 
            +
                            self.on_other( item )
         | 
| 1686 | 
            +
                            item = ''
         | 
| 1687 | 
            +
                          end
         | 
| 1688 | 
            +
                          item << c; tag << c
         | 
| 1689 | 
            +
                          state = :tag1
         | 
| 1690 | 
            +
             | 
| 1691 | 
            +
                        else
         | 
| 1692 | 
            +
                          item << c
         | 
| 1693 | 
            +
                        end
         | 
| 1694 | 
            +
             | 
| 1695 | 
            +
                        # Found first tag char, now expecting second
         | 
| 1696 | 
            +
                      when :tag1
         | 
| 1697 | 
            +
                        case c
         | 
| 1698 | 
            +
             | 
| 1699 | 
            +
                        when (?A..?Z)
         | 
| 1700 | 
            +
                          item << c; tag << c
         | 
| 1701 | 
            +
                          state = :tag2
         | 
| 1702 | 
            +
             | 
| 1703 | 
            +
                        else # including 'nil'
         | 
| 1704 | 
            +
                          self.on_error(EDISyntaxError, offset, item, c)
         | 
| 1705 | 
            +
                        end
         | 
| 1706 | 
            +
             | 
| 1707 | 
            +
                        # Found second tag char, now expecting last
         | 
| 1708 | 
            +
                      when :tag2
         | 
| 1709 | 
            +
                        case c
         | 
| 1710 | 
            +
                        when (?A..?Z)
         | 
| 1711 | 
            +
                          item << c; tag << c
         | 
| 1712 | 
            +
                          if tag=='UNA'
         | 
| 1713 | 
            +
                            state = :in_una
         | 
| 1714 | 
            +
                            una_count = 0
         | 
| 1715 | 
            +
                          elsif tag=~/U[IN]B/
         | 
| 1716 | 
            +
                            state = :in_uib_unb
         | 
| 1717 | 
            +
                            uib_unb_count = 0
         | 
| 1718 | 
            +
                          else
         | 
| 1719 | 
            +
                            state = :in_segment
         | 
| 1720 | 
            +
                          end
         | 
| 1721 | 
            +
                        else # including 'nil'
         | 
| 1722 | 
            +
                          self.on_error(EDISyntaxError, offset, item, c)
         | 
| 1723 | 
            +
                        end
         | 
| 1724 | 
            +
             | 
| 1725 | 
            +
                      when :in_una
         | 
| 1726 | 
            +
                        self.on_error(EDISyntaxError, offset, item) if c.nil?
         | 
| 1727 | 
            +
                        item << c; una_count += 1
         | 
| 1728 | 
            +
                        if una_count == 6 # completed?
         | 
| 1729 | 
            +
                          esc_char, seg_term = item[6], item[8]
         | 
| 1730 | 
            +
                          self.on_una( item )
         | 
| 1731 | 
            +
                          item, tag = '', ''
         | 
| 1732 | 
            +
                          state = :outside
         | 
| 1733 | 
            +
                        end
         | 
| 1734 | 
            +
             | 
| 1735 | 
            +
                        # Set seg_term if version==2 && charset=='UNOB'
         | 
| 1736 | 
            +
                      when :in_uib_unb
         | 
| 1737 | 
            +
                        self.on_error(EDISyntaxError, offset, item) if c.nil?
         | 
| 1738 | 
            +
                        item << c; uib_unb_count += 1
         | 
| 1739 | 
            +
                        if uib_unb_count == 7 # Read up to charset?
         | 
| 1740 | 
            +
                          # Set seg_term if not previously set by UNA
         | 
| 1741 | 
            +
                          if seg_term.nil? && item[4,4]=='UNOB' && item[9]==?2
         | 
| 1742 | 
            +
                            seg_term = ?\x14  # Special case
         | 
| 1743 | 
            +
                          else
         | 
| 1744 | 
            +
                            seg_term = ?'     # Default value
         | 
| 1745 | 
            +
                          end
         | 
| 1746 | 
            +
                          state = :in_segment # Continue normally
         | 
| 1747 | 
            +
                        end
         | 
| 1748 | 
            +
             | 
| 1749 | 
            +
                      when :in_segment
         | 
| 1750 | 
            +
                        case c
         | 
| 1751 | 
            +
                        when nil
         | 
| 1752 | 
            +
                          self.on_error(EDISyntaxError, offset, item)
         | 
| 1753 | 
            +
                        when esc_char
         | 
| 1754 | 
            +
                          state = :esc_mode
         | 
| 1755 | 
            +
                        when seg_term
         | 
| 1756 | 
            +
                          dispatch_item( item , tag )
         | 
| 1757 | 
            +
                          item, tag = '', ''
         | 
| 1758 | 
            +
                          state = :outside
         | 
| 1759 | 
            +
                        else
         | 
| 1760 | 
            +
                          item << c
         | 
| 1761 | 
            +
                        end
         | 
| 1762 | 
            +
             | 
| 1763 | 
            +
                      when :esc_mode
         | 
| 1764 | 
            +
                        case c
         | 
| 1765 | 
            +
                        when nil
         | 
| 1766 | 
            +
                          self.on_error(EDISyntaxError, offset, item)
         | 
| 1767 | 
            +
                        when seg_term      # Treat seg_term as regular character
         | 
| 1768 | 
            +
                          item << seg_term
         | 
| 1769 | 
            +
                        # when esc_char      # Redundant - skip
         | 
| 1770 | 
            +
                        #   item << esc_char << esc_char
         | 
| 1771 | 
            +
                        else
         | 
| 1772 | 
            +
                          item << esc_char << c
         | 
| 1773 | 
            +
                        end
         | 
| 1774 | 
            +
                        state = :in_segment
         | 
| 1775 | 
            +
                        
         | 
| 1776 | 
            +
                      else # Should never occur...
         | 
| 1777 | 
            +
                        raise ArgumentError, "unexpected state: #{state}"
         | 
| 1778 | 
            +
                      end  
         | 
| 1779 | 
            +
                      offset += 1
         | 
| 1780 | 
            +
                    end # loop
         | 
| 1781 | 
            +
            #        self.on_error(EDISyntaxError, offset, item) unless state==:outside
         | 
| 1782 | 
            +
                  end # catch(:done)
         | 
| 1783 | 
            +
             | 
| 1784 | 
            +
                  self.on_interchange_end
         | 
| 1785 | 
            +
                  offset
         | 
| 1786 | 
            +
                end
         | 
| 1787 | 
            +
             | 
| 1788 | 
            +
                private
         | 
| 1789 | 
            +
             | 
| 1790 | 
            +
                # Private dispatch method to simplify the parser
         | 
| 1791 | 
            +
             | 
| 1792 | 
            +
                def dispatch_item( item, tag ) # :nodoc:
         | 
| 1793 | 
            +
                  case tag
         | 
| 1794 | 
            +
                  when 'UNB', 'UIB'
         | 
| 1795 | 
            +
                    on_unb_uib( item, tag )
         | 
| 1796 | 
            +
                  when 'UNZ', 'UIZ'
         | 
| 1797 | 
            +
                    on_unz_uiz( item, tag )
         | 
| 1798 | 
            +
                  when 'UNG'
         | 
| 1799 | 
            +
                    on_ung( item )
         | 
| 1800 | 
            +
                  when 'UNE'
         | 
| 1801 | 
            +
                    on_une( item )
         | 
| 1802 | 
            +
                  when 'UNH', 'UIH'
         | 
| 1803 | 
            +
                    on_unh_uih( item, tag )
         | 
| 1804 | 
            +
                  when 'UNT', 'UIT'
         | 
| 1805 | 
            +
                    on_unt_uit( item, tag )
         | 
| 1806 | 
            +
                  when /[A-Z]{3}/
         | 
| 1807 | 
            +
                    on_segment( item, tag )
         | 
| 1808 | 
            +
                  else
         | 
| 1809 | 
            +
                    self.on_error(EDISyntaxError, offset, "Illegal tag: #{tag}")
         | 
| 1810 | 
            +
                  end
         | 
| 1811 | 
            +
                end
         | 
| 1812 | 
            +
             | 
| 1813 | 
            +
              end # StreamingParser
         | 
| 1814 | 
            +
             | 
| 1815 | 
            +
              #########################################################################
         | 
| 1816 | 
            +
              #
         | 
| 1817 | 
            +
              # = Class StreamingBuilder
         | 
| 1818 | 
            +
              #
         | 
| 1819 | 
            +
              # The StreamingBuilder parses the input stream just like StreamingParser
         | 
| 1820 | 
            +
              # and in addition builds the complete interchange.
         | 
| 1821 | 
            +
              #
         | 
| 1822 | 
            +
              # This method is the new basis of Interchange.parse. You might want to
         | 
| 1823 | 
            +
              # study its callbacks to get some ideas on how to create a special-purpose
         | 
| 1824 | 
            +
              # parser/builder of your own.
         | 
| 1825 | 
            +
              #
         | 
| 1826 | 
            +
             | 
| 1827 | 
            +
              class StreamingBuilder < StreamingParser
         | 
| 1828 | 
            +
                def initialize(auto_validate=true)
         | 
| 1829 | 
            +
                  @ic = nil
         | 
| 1830 | 
            +
                  @curr_group = @curr_msg = nil
         | 
| 1831 | 
            +
                  @una = nil
         | 
| 1832 | 
            +
                  @is_iedi = false
         | 
| 1833 | 
            +
                  @auto_validate = auto_validate
         | 
| 1834 | 
            +
                end
         | 
| 1835 | 
            +
             | 
| 1836 | 
            +
             | 
| 1837 | 
            +
                def interchange
         | 
| 1838 | 
            +
                  @ic
         | 
| 1839 | 
            +
                end
         | 
| 1840 | 
            +
             | 
| 1841 | 
            +
             | 
| 1842 | 
            +
                def on_una( s )
         | 
| 1843 | 
            +
                  @una = s.dup
         | 
| 1844 | 
            +
                end
         | 
| 1845 | 
            +
             | 
| 1846 | 
            +
                def on_unb_uib( s, tag ) # Expecting: "UNB+UNOA:3+...",  "UIB+UNOC:4..."
         | 
| 1847 | 
            +
                  @ic = Interchange.new( :i_edi   => (@is_iedi = tag[1]==?I),
         | 
| 1848 | 
            +
                                         :charset => s[4,4],
         | 
| 1849 | 
            +
                                         :version => s[9].to_i-?0.to_i, # 1,2,3,4 (int)
         | 
| 1850 | 
            +
                                         :una_string => @una )
         | 
| 1851 | 
            +
                  @ic.header = Segment.parse( @ic, s )
         | 
| 1852 | 
            +
                end
         | 
| 1853 | 
            +
             | 
| 1854 | 
            +
                def on_unz_uiz( s, tag )
         | 
| 1855 | 
            +
                  # FIXME: @is_edi and tag should correspond!
         | 
| 1856 | 
            +
                  @ic.trailer = Segment.parse( @ic, s )
         | 
| 1857 | 
            +
                end
         | 
| 1858 | 
            +
             | 
| 1859 | 
            +
                def on_ung( s )
         | 
| 1860 | 
            +
                  @curr_group = @ic.new_msggroup( @ic.parse_segment(s,'UNG') )
         | 
| 1861 | 
            +
                  @curr_group.header = Segment.parse( @curr_group, s )
         | 
| 1862 | 
            +
                end
         | 
| 1863 | 
            +
             | 
| 1864 | 
            +
                def on_une( s )
         | 
| 1865 | 
            +
                  @curr_group.trailer = Segment.parse( @curr_group, s )
         | 
| 1866 | 
            +
                  @ic.add( @curr_group, @auto_validate )
         | 
| 1867 | 
            +
                end
         | 
| 1868 | 
            +
             | 
| 1869 | 
            +
                def on_unh_uih( s, tag )
         | 
| 1870 | 
            +
                  # FIXME: @is_edi and tag should correspond!
         | 
| 1871 | 
            +
                  seg = @ic.parse_segment(s,tag)
         | 
| 1872 | 
            +
                  @curr_msg = (@curr_group || @ic).new_message( seg )
         | 
| 1873 | 
            +
            #      @curr_msg = (@curr_group || @ic).new_message( @ic.parse_segment(s,tag) )
         | 
| 1874 | 
            +
                  @curr_msg.header = Segment.parse( @curr_msg, s )
         | 
| 1875 | 
            +
                end
         | 
| 1876 | 
            +
             | 
| 1877 | 
            +
                def on_unt_uit( s, tag )
         | 
| 1878 | 
            +
                  # FIXME: @is_edi and tag should correspond!
         | 
| 1879 | 
            +
                  @curr_msg.trailer = Segment.parse( @curr_msg, s )
         | 
| 1880 | 
            +
                  #      puts "on_unt_uit: #@curr_msg"
         | 
| 1881 | 
            +
                  @curr_group.nil? ? @ic.add( @curr_msg, @auto_validate ) : @curr_group.add( @curr_msg )
         | 
| 1882 | 
            +
                end
         | 
| 1883 | 
            +
             | 
| 1884 | 
            +
                # Overwrite this method to react on segments of interest
         | 
| 1885 | 
            +
                #
         | 
| 1886 | 
            +
                # Note: For a skeleton Builder (just UNB/UNG/UNT etc), overwrite with
         | 
| 1887 | 
            +
                # an empty method.
         | 
| 1888 | 
            +
                #
         | 
| 1889 | 
            +
                def on_segment( s, tag )
         | 
| 1890 | 
            +
                  @curr_msg.add @curr_msg.parse_segment( s )
         | 
| 1891 | 
            +
                  super
         | 
| 1892 | 
            +
                end
         | 
| 1893 | 
            +
             | 
| 1894 | 
            +
             | 
| 1895 | 
            +
                def on_interchange_end
         | 
| 1896 | 
            +
                  if @auto_validate
         | 
| 1897 | 
            +
                    @ic.header.validate
         | 
| 1898 | 
            +
                    @ic.trailer.validate
         | 
| 1899 | 
            +
                    # Content is already validated through @ic.add() and @curr_group.add()
         | 
| 1900 | 
            +
                  end
         | 
| 1901 | 
            +
                end
         | 
| 1902 | 
            +
             | 
| 1903 | 
            +
              end # StreamingBuilder
         | 
| 1904 | 
            +
             | 
| 1905 | 
            +
             | 
| 1906 | 
            +
              # Just an idea - not sure it's worth an implementation...
         | 
| 1907 | 
            +
              #########################################################################
         | 
| 1908 | 
            +
              #
         | 
| 1909 | 
            +
              # = Class StreamingSkimmer
         | 
| 1910 | 
            +
              #
         | 
| 1911 | 
            +
              # The StreamingSkimmer works as a simplified StreamingBuilder.
         | 
| 1912 | 
            +
              # It only skims through the service segements of an interchange and 
         | 
| 1913 | 
            +
              # builds an interchange skeleton from them containing just the interchange,
         | 
| 1914 | 
            +
              # group, and message level, but *not* the regular messages.
         | 
| 1915 | 
            +
              # Thus, all messages are *empty* and not fit for validation
         | 
| 1916 | 
            +
              # (use class StreamingBuilder to build a complete interchange).
         | 
| 1917 | 
            +
              #
         | 
| 1918 | 
            +
              # StreamingSkimmer lacks an implementation of callback
         | 
| 1919 | 
            +
              # method <tt>on_segment()</tt>. The interchange skeletons it produces are 
         | 
| 1920 | 
            +
              # thus quicky built and have a small memory footprint.
         | 
| 1921 | 
            +
              # Customize the class by overwriting <tt>on_segment()</tt>. 
         | 
| 1922 | 
            +
              #
         | 
| 1923 | 
            +
             | 
| 1924 | 
            +
              class StreamingSkimmer < StreamingBuilder
         | 
| 1925 | 
            +
                def on_segment( s, tag )
         | 
| 1926 | 
            +
                  # Deliberately left empty
         | 
| 1927 | 
            +
                end
         | 
| 1928 | 
            +
              end
         | 
| 1929 | 
            +
             | 
| 1930 | 
            +
            end # module EDI::E
         |