chunker/lib/chunker.rb
branchruby-modules
changeset 1 9e127bf6e84f
child 2 e5c705047540
equal deleted inserted replaced
0:83c0eed6db19 1:9e127bf6e84f
       
     1 #
       
     2 # Chunker!
       
     3 #
       
     4 #	Mahlon E. Smith <mahlon@martini.nu>
       
     5 #
       
     6 
       
     7 
       
     8 ### Namespace for the datablock parser.
       
     9 ###
       
    10 module Chunker
       
    11 
       
    12 	require 'strscan'
       
    13 	require 'stringio'
       
    14 
       
    15 	# SVN Revision
       
    16 	#
       
    17 	SVNRev = %q$Rev$
       
    18 
       
    19 	# SVN Id
       
    20 	#
       
    21 	SVNId = %q$Id$
       
    22 
       
    23 	# Package version
       
    24 	#
       
    25 	VERSION = '0.1'
       
    26 
       
    27 
       
    28 	### Parser class for __END__ data blocks.
       
    29 	### Find each __MARKER__ within the __END__, and put each into a
       
    30 	### DATA_MARKER constant within the namespace that included us.
       
    31 	###
       
    32 	class DataParser
       
    33 
       
    34 		# The mark for a DATA block.
       
    35 		#
       
    36 		END_MARKER = /^__END__\r?\n/
       
    37 
       
    38 		# The mark for a 'sub' block.
       
    39 		#
       
    40 		CHUNK_MARKER = /^__([A-Z\_0-9]+)__\r?\n/
       
    41 
       
    42 
       
    43 		### Constructor: Given a +klass+ and an +io+ to the class file,
       
    44 		### extract the data blocks and install constants.
       
    45 		###
       
    46 		def initialize( klass, io )
       
    47 			io.open if io.closed?
       
    48 			end_string = io.read.split( END_MARKER, 2 ).last
       
    49 
       
    50 			@klass   = klass
       
    51 			@scanner = StringScanner.new( end_string )
       
    52 			io.close
       
    53 
       
    54 			if @scanner.check_until( CHUNK_MARKER )
       
    55 				# put each chunk into its own constant
       
    56 				self.extract_blocks
       
    57 			else
       
    58 				# no sub blocks, put the whole mess into DATA_END
       
    59 				@klass.const_set( :DATA_END, StringIO.new( end_string ) )
       
    60 			end
       
    61 		end
       
    62 
       
    63 
       
    64 		#########
       
    65 		protected
       
    66 		#########
       
    67 
       
    68 		### Parse the current +io+ for data blocks, set contents to
       
    69 		### IO constants in the including class.
       
    70 		###
       
    71 		def extract_blocks
       
    72 			label = nil
       
    73 
       
    74 			while @scanner.scan_until( CHUNK_MARKER ) and ! @scanner.eos?
       
    75 				data = ''
       
    76 
       
    77 				# First pass, __END__ contents (until next marker, instead
       
    78 				# of entire data block.)
       
    79 				#
       
    80 				if label.nil?
       
    81 					label = 'END'
       
    82 					data  = @scanner.pre_match
       
    83 
       
    84 					@scanner.pos = self.next_position
       
    85 				else
       
    86 					label = @scanner[1]
       
    87 
       
    88 					if data = @scanner.scan_until( CHUNK_MARKER )
       
    89 						# Pull the next marker text out of the data, set up the next pass
       
    90 						#
       
    91 						data         = data[ 0, data.length - @scanner[0].length ]
       
    92 						@scanner.pos = self.next_position
       
    93 					else
       
    94 						# No additional blocks
       
    95 						#
       
    96 						data = @scanner.rest
       
    97 					end
       
    98 				end
       
    99 
       
   100 				# Add the IO constant to the class that included me.
       
   101 				#
       
   102 				@klass.const_set( "DATA_#{label}".to_sym, StringIO.new( data ) )
       
   103 			end
       
   104 		end
       
   105 
       
   106 
       
   107 		### Return the next scanner position for searching.
       
   108 		###
       
   109 		def next_position
       
   110 			return @scanner.pos - @scanner[0].length
       
   111 		end
       
   112 	end
       
   113 
       
   114 
       
   115 	### Included hook: Find the file path for how we arrived here, and open
       
   116 	### it as an IO object. __FILE__ won't work, so we find it via caller().
       
   117 	### Start parsing this file for data blocks.
       
   118 	###
       
   119     def self.included( klass )
       
   120 		# klass.instance_eval{ __FILE__ }   awww, nope.
       
   121 		io = File.open( caller(1).last.sub(/:.*?$/, ''), 'r' )
       
   122 		DataParser.new( klass, io )
       
   123     end
       
   124 end
       
   125