# # Chunker! # # Mahlon E. Smith # ### Namespace for the datablock parser. ### module Chunker require 'strscan' require 'stringio' # SVN Revision # SVNRev = %q$Rev$ # SVN Id # SVNId = %q$Id$ # Package version # VERSION = '0.1' ### Parser class for __END__ data blocks. ### Find each __MARKER__ within the __END__, and put each into a ### DATA_MARKER constant within the namespace that included us. ### class DataParser # The mark for a DATA block. # END_MARKER = /^__END__\r?\n/ # The mark for a 'sub' block. # CHUNK_MARKER = /^__([A-Z\_0-9]+)__\r?\n/ ### Constructor: Given a +klass+ and an +io+ to the class file, ### extract the data blocks and install constants. ### def initialize( klass, io ) io.open if io.closed? end_string = io.read.split( END_MARKER, 2 ).last @klass = klass @scanner = StringScanner.new( end_string ) io.close if @scanner.check_until( CHUNK_MARKER ) # put each chunk into its own constant self.extract_blocks else # no sub blocks, put the whole mess into DATA_END @klass.const_set( :DATA_END, StringIO.new( end_string ) ) end end ######### protected ######### ### Parse the current +io+ for data blocks, set contents to ### IO constants in the including class. ### def extract_blocks label = nil while @scanner.scan_until( CHUNK_MARKER ) and ! @scanner.eos? data = '' # First pass, __END__ contents (until next marker, instead # of entire data block.) # if label.nil? label = 'END' data = @scanner.pre_match @scanner.pos = self.next_position else label = @scanner[1] if data = @scanner.scan_until( CHUNK_MARKER ) # Pull the next marker text out of the data, set up the next pass # data = data[ 0, data.length - @scanner[0].length ] @scanner.pos = self.next_position else # No additional blocks # data = @scanner.rest end end # Add the IO constant to the class that included me. # @klass.const_set( "DATA_#{label}".to_sym, StringIO.new( data ) ) end end ### Return the next scanner position for searching. ### def next_position return @scanner.pos - @scanner[0].length end end ### Included hook: Find the file path for how we arrived here, and open ### it as an IO object. __FILE__ won't work, so we find it via caller(). ### Start parsing this file for data blocks. ### def self.included( klass ) # klass.instance_eval{ __FILE__ } awww, nope. io = File.open( caller(1).last.sub(/:.*?$/, ''), 'r' ) DataParser.new( klass, io ) end end