chunker/lib/chunker.rb
branchruby-modules
changeset 4 01a3332bfe0a
parent 2 e5c705047540
equal deleted inserted replaced
3:233041485364 4:01a3332bfe0a
     1 #!/usr/bin/ruby
     1 # vim: set nosta noet ts=4 sw=4:
       
     2 
       
     3 require 'strscan'
       
     4 require 'stringio'
       
     5 
     2 #
     6 #
     3 # Chunker: A convenience library for parsing __END__ tokens consistently.
     7 # Chunker: A convenience library for parsing __END__ tokens consistently.
     4 #
     8 #
     5 # == Version
     9 # == Version
     6 #
    10 #
    15 
    19 
    16 ### Namespace for the datablock parser.
    20 ### Namespace for the datablock parser.
    17 ###
    21 ###
    18 module Chunker
    22 module Chunker
    19 
    23 
    20 	require 'strscan'
    24 	# VCS Revision
    21 	require 'stringio'
    25 	VCSRev = %q$Rev$
    22 
    26 
    23 	# SVN Revision
    27 	# VCS Id
    24 	#
    28 	VCSId = %q$Id$
    25 	SVNRev = %q$Rev$
       
    26 
       
    27 	# SVN Id
       
    28 	#
       
    29 	SVNId = %q$Id$
       
    30 
    29 
    31 	# Package version
    30 	# Package version
    32 	#
    31 	VERSION = '1.0.0'
    33 	VERSION = '0.1'
       
    34 
    32 
    35 
    33 
    36 	### Parser class for __END__ data blocks.
    34 	### Parser class for __END__ data blocks.
    37 	### Find each __TOKEN__ within the __END__, and put each into a
    35 	### Find each __TOKEN__ within the __END__, and put each into a
    38 	### DATA_TOKEN constant within the namespace that included us.
    36 	### DATA_TOKEN constant within the namespace that included us.
    39 	###
    37 	###
    40 	class DataParser
    38 	class DataParser
    41 
    39 
    42 		# The mark for a DATA block.
    40 		# The mark for a DATA block.
    43 		#
       
    44 		END_TOKEN = /^__END__\r?\n/
    41 		END_TOKEN = /^__END__\r?\n/
    45 
    42 
    46 		# The mark for a 'sub' block.
    43 		# The mark for a 'sub' block.
    47 		#
       
    48 		CHUNK_TOKEN = /^__([A-Z\_0-9]+)__\r?\n/
    44 		CHUNK_TOKEN = /^__([A-Z\_0-9]+)__\r?\n/
    49 
    45 
    50 
    46 
    51 		### Constructor: Given a +klass+ and an +io+ to the class file,
    47 		### Constructor: Given a +klass+ and an +io+ to the class file,
    52 		### extract the data blocks and install constants.
    48 		### extract the data blocks and install constants.
    57 
    53 
    58 			@klass   = klass
    54 			@klass   = klass
    59 			@scanner = StringScanner.new( end_string )
    55 			@scanner = StringScanner.new( end_string )
    60 			io.close
    56 			io.close
    61 
    57 
       
    58 			# put each chunk into its own constant
       
    59 			#
    62 			if @scanner.check_until( CHUNK_TOKEN )
    60 			if @scanner.check_until( CHUNK_TOKEN )
    63 				# put each chunk into its own constant
       
    64 				self.extract_blocks
    61 				self.extract_blocks
       
    62 
       
    63 			# no sub blocks, put the whole mess into DATA_END
       
    64 			#
    65 			else
    65 			else
    66 				# no sub blocks, put the whole mess into DATA_END
       
    67 				@klass.const_set( :DATA_END, StringIO.new( end_string ) )
    66 				@klass.const_set( :DATA_END, StringIO.new( end_string ) )
    68 			end
    67 			end
    69 		end
    68 		end
    70 
    69 
    71 
    70 
    91 
    90 
    92 					@scanner.pos = self.next_position
    91 					@scanner.pos = self.next_position
    93 				else
    92 				else
    94 					label = @scanner[1]
    93 					label = @scanner[1]
    95 
    94 
       
    95 					# Pull the next token text out of the data, set up the next pass
       
    96 					#
    96 					if data = @scanner.scan_until( CHUNK_TOKEN )
    97 					if data = @scanner.scan_until( CHUNK_TOKEN )
    97 						# Pull the next token text out of the data, set up the next pass
    98 						data = data[ 0, data.length - @scanner[0].length ]
    98 						#
       
    99 						data         = data[ 0, data.length - @scanner[0].length ]
       
   100 						@scanner.pos = self.next_position
    99 						@scanner.pos = self.next_position
       
   100 
       
   101 					# No additional blocks
       
   102 					#
   101 					else
   103 					else
   102 						# No additional blocks
       
   103 						#
       
   104 						data = @scanner.rest
   104 						data = @scanner.rest
   105 					end
   105 					end
   106 				end
   106 				end
   107 
   107 
   108 				# Add the IO constant to the class that included me.
   108 				# Add the IO constant to the class that included me.
   109 				#
       
   110 				@klass.const_set( "DATA_#{label}".to_sym, StringIO.new( data ) )
   109 				@klass.const_set( "DATA_#{label}".to_sym, StringIO.new( data ) )
   111 			end
   110 			end
   112 		end
   111 		end
   113 
   112 
   114 
   113 
   124 	### it as an IO object.  Parse the IO for data block tokens.
   123 	### it as an IO object.  Parse the IO for data block tokens.
   125 	###
   124 	###
   126     def self.included( klass )
   125     def self.included( klass )
   127 		# klass.instance_eval{ __FILE__ }   awww, nope.
   126 		# klass.instance_eval{ __FILE__ }   awww, nope.
   128 		# __FILE__ won't work here, so we find the filename via caller().
   127 		# __FILE__ won't work here, so we find the filename via caller().
   129 		#
       
   130 		io = File.open( caller(1).last.sub(/:.*?$/, ''), 'r' )
   128 		io = File.open( caller(1).last.sub(/:.*?$/, ''), 'r' )
   131 
   129 
   132 		DataParser.new( klass, io )
   130 		DataParser.new( klass, io )
   133     end
   131     end
   134 end
   132 end