chunker 0.1.53

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.
Files changed (6) hide show
  1. data/LICENSE +29 -0
  2. data/README +59 -0
  3. data/Rakefile +156 -0
  4. data/lib/chunker.rb +135 -0
  5. data/spec/chunker_spec.rb +117 -0
  6. metadata +57 -0
data/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ Copyright (c) 2008, Mahlon E. Smith
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are
6
+ permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above copyright notice, this
12
+ list of conditions and the following disclaimer in the documentation and/or
13
+ other materials provided with the distribution.
14
+
15
+ * Neither the name of the author, nor the names of contributors may be used to
16
+ endorse or promote products derived from this software without specific prior
17
+ written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,59 @@
1
+
2
+ Preface:
3
+
4
+ Ruby provides an automatic constant called DATA, which is an IO object
5
+ that references all text in the current file under an __END__ token.
6
+
7
+ I find it convenient to use the __END__ area to store all sorts of
8
+ stuff, rather than have to worry about distributing separate files.
9
+
10
+
11
+ The problem:
12
+
13
+ The DATA constant is determined from whatever ruby believes $0 to be.
14
+ It doesn't work inside of other required libraries, so you'll see stuff
15
+ like this all the time:
16
+
17
+ END = File.open( __FILE__ ).read.split( /^__END__/, 2 ).last
18
+
19
+ It works, but it's more work than I want to do.
20
+
21
+
22
+ A workaround:
23
+
24
+ Chunker solves this by parsing __END__ tokens for you, and making it
25
+ available in the form of a 'DATA_END' constant. It installs this
26
+ constant into the class that includes Chunker, so you can use it again
27
+ and again, assuming you use a different file for each class.
28
+
29
+ It also automatically parses out other things that look like tokens, so
30
+ you can easily have multiple, distinct documents all embedded into the
31
+ __END__ block.
32
+
33
+
34
+ Usage:
35
+
36
+ There is no direct interface to Chunker. Just include it from a
37
+ class to have that file's __END__ data blocks magically become DATA_*
38
+ IO constants within that class.
39
+
40
+
41
+ Example:
42
+
43
+ This produces the string "Yep.\n".
44
+
45
+
46
+ require 'chunker'
47
+ class Foom
48
+ include Chunker
49
+ end
50
+
51
+ puts Foom.new.class.const_get( :DATA_WICKED ).read
52
+
53
+ __END__
54
+ Stuff in the END block!
55
+ __WOW__
56
+ Ultimate success!
57
+ __WICKED__
58
+ Yep.
59
+
data/Rakefile ADDED
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env rake
2
+ #
3
+ # Chunker Rakefile
4
+ #
5
+
6
+ require 'rubygems'
7
+ require 'pathname'
8
+
9
+ require 'rake'
10
+ require 'rake/packagetask'
11
+ require 'rake/gempackagetask'
12
+ require 'spec/rake/spectask'
13
+ require 'rubygems/installer'
14
+ require 'rubygems/uninstaller'
15
+
16
+
17
+ ######################################################################
18
+ ### P A T H S A N D F I L E S
19
+ ######################################################################
20
+
21
+ BASEDIR = Pathname.new( __FILE__ ).expand_path.dirname.relative_path_from( Pathname.getwd )
22
+
23
+ TEXT_FILES = %w{ Rakefile README LICENSE }.collect {|f| BASEDIR + f }
24
+
25
+ SPECDIR = BASEDIR + 'spec'
26
+ SPEC_FILES = Pathname.glob( SPECDIR + '**/*_spec.rb' ).reject {|f| f =~ /^\.svn/ }
27
+
28
+ LIBDIR = BASEDIR + 'lib'
29
+ LIB_FILES = Pathname.glob( LIBDIR + '**/*.rb').reject {|i| i =~ /\.svn/ }
30
+
31
+ RELEASE_FILES = TEXT_FILES + LIB_FILES + SPEC_FILES
32
+
33
+
34
+ ######################################################################
35
+ ### H E L P E R S
36
+ ######################################################################
37
+
38
+ ### Given a +file+ path, find the first captured match of +pattern+,
39
+ ### or the string 'UNKNOWN' if not found. (easy to notice something is wrong.)
40
+ ###
41
+ def find_pattern( file, pattern )
42
+ ver = nil
43
+ File.open( file ) do |f|
44
+ ver = f.each do |line|
45
+ break $1 if line =~ pattern
46
+ end
47
+ end
48
+ return ver.is_a?( String ) ? ver : 'UNKNOWN'
49
+ end
50
+
51
+
52
+ ######################################################################
53
+ ### P A C K A G E C O N S T A N T S
54
+ ######################################################################
55
+
56
+ PKG_NAME = 'chunker'
57
+ PKG_VERSION = find_pattern( LIBDIR + 'chunker.rb', /VERSION = ['"](\d\.\d(?:\/\d)?)['"]/ )
58
+ PKG_REVISION = find_pattern( LIBDIR + 'chunker.rb', /SVNRev = .+Rev: (\d+)/ )
59
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}.#{PKG_REVISION}"
60
+
61
+
62
+ ######################################################################
63
+ ### T A S K S
64
+ ######################################################################
65
+
66
+ task :default => [ :test, :package ]
67
+
68
+
69
+ ### Task: run rspec tests
70
+ ###
71
+ desc "Run tests"
72
+ Spec::Rake::SpecTask.new('test') do |task|
73
+ task.spec_files = SPEC_FILES
74
+ task.spec_opts = %w{ -c -fs }
75
+ end
76
+
77
+
78
+ ### Task: generate ctags
79
+ ### This assumes exuberant ctags, since ctags 'native' doesn't support ruby anyway.
80
+ ###
81
+ desc "Generate a ctags 'tags' file from Chunker source"
82
+ task :ctags do
83
+ sh "ctags -R #{LIBDIR}"
84
+ end
85
+
86
+
87
+ ### Task: Create gem from source
88
+ ###
89
+ gem = Gem::Specification.new do |gem|
90
+ pkg_build = PKG_REVISION || 0
91
+
92
+ gem.summary = "A convenience library for parsing __END__ tokens consistently."
93
+ gem.name = PKG_NAME
94
+ gem.version = "%s.%s" % [ PKG_VERSION, pkg_build ]
95
+ gem.author = 'Mahlon E. Smith'
96
+ gem.email = 'mahlon@martini.nu'
97
+ gem.homepage = 'http://projects.martini.nu/ruby-modules/wiki/Chunker'
98
+ gem.rubyforge_project = 'mahlon'
99
+ gem.has_rdoc = true
100
+
101
+ gem.files = RELEASE_FILES.
102
+ collect {|f| f.relative_path_from(BASEDIR).to_s }
103
+ gem.test_files = SPEC_FILES.
104
+ collect {|f| f.relative_path_from(BASEDIR).to_s }
105
+
106
+ gem.description = <<-EOF
107
+ Ruby provides an automatic constant called DATA, which is an IO object
108
+ that references all text in the current file under an __END__ token.
109
+
110
+ I find it convenient to use the __END__ area to store all sorts of
111
+ stuff, rather than have to worry about distributing separate files.
112
+
113
+ The DATA constant is determined from whatever ruby believes $0 to be.
114
+ It doesn't work inside of other required libraries, so you'll see stuff
115
+ like this all the time:
116
+
117
+ END = File.open( __FILE__ ).read.split( /^__END__/, 2 ).last
118
+
119
+ It works, but it's more work than I want to do.
120
+
121
+ Chunker solves this by parsing __END__ tokens for you, and making it
122
+ available in the form of a 'DATA_END' constant. It installs this
123
+ constant into the class that includes Chunker, so you can use it again
124
+ and again, assuming you use a different file for each class.
125
+
126
+ It also automatically parses out other things that look like tokens, so
127
+ you can easily have multiple, distinct documents all embedded into the
128
+ __END__ block.
129
+ EOF
130
+ end
131
+
132
+ Rake::GemPackageTask.new( gem ) do |pkg|
133
+ pkg.need_zip = true
134
+ pkg.need_tar = true
135
+ pkg.need_tar_bz2 = true
136
+ end
137
+
138
+
139
+ ### Task: install
140
+ ###
141
+ task :install_gem => [ :package ] do
142
+ $stderr.puts
143
+ installer = Gem::Installer.new( "pkg/#{PKG_FILE_NAME}.gem" )
144
+ installer.install
145
+ end
146
+ task :install => [ :install_gem ]
147
+
148
+
149
+ ### Task: uninstall
150
+ ###
151
+ task :uninstall_gem do
152
+ uninstaller = Gem::Uninstaller.new( PKG_NAME )
153
+ uninstaller.uninstall
154
+ end
155
+ task :uninstall => [ :uninstall_gem ]
156
+
data/lib/chunker.rb ADDED
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Chunker: A convenience library for parsing __END__ tokens consistently.
4
+ #
5
+ # == Version
6
+ #
7
+ # $Id: chunker.rb 53 2008-11-09 00:27:36Z mahlon $
8
+ #
9
+ # == Author
10
+ #
11
+ # * Mahlon E. Smith <mahlon@martini.nu>
12
+ #
13
+ # :include: LICENSE
14
+ #
15
+
16
+ ### Namespace for the datablock parser.
17
+ ###
18
+ module Chunker
19
+
20
+ require 'strscan'
21
+ require 'stringio'
22
+
23
+ # SVN Revision
24
+ #
25
+ SVNRev = %q$Rev: 53 $
26
+
27
+ # SVN Id
28
+ #
29
+ SVNId = %q$Id: chunker.rb 53 2008-11-09 00:27:36Z mahlon $
30
+
31
+ # Package version
32
+ #
33
+ VERSION = '0.1'
34
+
35
+
36
+ ### Parser class for __END__ data blocks.
37
+ ### Find each __TOKEN__ within the __END__, and put each into a
38
+ ### DATA_TOKEN constant within the namespace that included us.
39
+ ###
40
+ class DataParser
41
+
42
+ # The mark for a DATA block.
43
+ #
44
+ END_TOKEN = /^__END__\r?\n/
45
+
46
+ # The mark for a 'sub' block.
47
+ #
48
+ CHUNK_TOKEN = /^__([A-Z\_0-9]+)__\r?\n/
49
+
50
+
51
+ ### Constructor: Given a +klass+ and an +io+ to the class file,
52
+ ### extract the data blocks and install constants.
53
+ ###
54
+ def initialize( klass, io )
55
+ io.open if io.closed?
56
+ end_string = io.read.split( END_TOKEN, 2 ).last
57
+
58
+ @klass = klass
59
+ @scanner = StringScanner.new( end_string )
60
+ io.close
61
+
62
+ if @scanner.check_until( CHUNK_TOKEN )
63
+ # put each chunk into its own constant
64
+ self.extract_blocks
65
+ else
66
+ # no sub blocks, put the whole mess into DATA_END
67
+ @klass.const_set( :DATA_END, StringIO.new( end_string ) )
68
+ end
69
+ end
70
+
71
+
72
+ #########
73
+ protected
74
+ #########
75
+
76
+ ### Parse the current +io+ for data blocks, set contents to
77
+ ### IO constants in the including class.
78
+ ###
79
+ def extract_blocks
80
+ label = nil
81
+
82
+ while @scanner.scan_until( CHUNK_TOKEN ) and ! @scanner.eos?
83
+ data = ''
84
+
85
+ # First pass, __END__ contents (until next token, instead
86
+ # of entire data block.)
87
+ #
88
+ if label.nil?
89
+ label = 'END'
90
+ data = @scanner.pre_match
91
+
92
+ @scanner.pos = self.next_position
93
+ else
94
+ label = @scanner[1]
95
+
96
+ if data = @scanner.scan_until( CHUNK_TOKEN )
97
+ # Pull the next token text out of the data, set up the next pass
98
+ #
99
+ data = data[ 0, data.length - @scanner[0].length ]
100
+ @scanner.pos = self.next_position
101
+ else
102
+ # No additional blocks
103
+ #
104
+ data = @scanner.rest
105
+ end
106
+ end
107
+
108
+ # Add the IO constant to the class that included me.
109
+ #
110
+ @klass.const_set( "DATA_#{label}".to_sym, StringIO.new( data ) )
111
+ end
112
+ end
113
+
114
+
115
+ ### Return the next scanner position for searching.
116
+ ###
117
+ def next_position
118
+ return @scanner.pos - @scanner[0].length
119
+ end
120
+ end
121
+
122
+
123
+ ### Hook included: Find the file path for how we arrived here, and open
124
+ ### it as an IO object. Parse the IO for data block tokens.
125
+ ###
126
+ def self.included( klass )
127
+ # klass.instance_eval{ __FILE__ } awww, nope.
128
+ # __FILE__ won't work here, so we find the filename via caller().
129
+ #
130
+ io = File.open( caller(1).last.sub(/:.*?$/, ''), 'r' )
131
+
132
+ DataParser.new( klass, io )
133
+ end
134
+ end
135
+
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent
6
+ libdir = basedir + "lib"
7
+
8
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
9
+ }
10
+
11
+ require 'chunker'
12
+ require 'rubygems'
13
+ require 'spec'
14
+
15
+ ENDSTUFF = <<ENDSTUFF
16
+ Stuff within the end block.
17
+
18
+ Content of the END block
19
+ Content of the END block
20
+ Content of the END block
21
+ Content of the END block
22
+ ENDSTUFF
23
+
24
+ HURGADURGA = <<HURGADURGA
25
+
26
+ Content of the HURGADURGA block
27
+ Content of the HURGADURGA block
28
+ Content of the HURGADURGA block
29
+ Content of the HURGADURGA block
30
+
31
+ HURGADURGA
32
+
33
+ HURRRRG = <<HURRRRG
34
+ 123123123 123123123 123123123
35
+ 123123123 123123123 123123123
36
+ 123123123 123123123 123123123
37
+ HURRRRG
38
+
39
+ POOP = <<POOP
40
+ Content of the POOP block
41
+ POOP
42
+
43
+ FILE_TEXT = <<EO_FILE_TEXT
44
+
45
+ This is stuff we shouldn't see or care about.
46
+ You know, stuff like code, presumably.
47
+
48
+ __END__
49
+ #{ENDSTUFF}
50
+ EO_FILE_TEXT
51
+
52
+ FILE_TEXT_MULTIPLE = <<EO_FILE_TEXT
53
+
54
+ This is stuff we shouldn't see or care about.
55
+ You know, stuff like code, presumably.
56
+
57
+ __END__
58
+ #{ENDSTUFF}
59
+ __POOP__
60
+ #{POOP}
61
+ __HURRRRG__
62
+ #{HURRRRG}
63
+ __HURGADURGA__
64
+ #{HURGADURGA}
65
+ EO_FILE_TEXT
66
+
67
+
68
+ describe Chunker::DataParser do
69
+
70
+ it "doesn't include content above the __END__ token" do
71
+ klass = Class.new
72
+ dp = Chunker::DataParser.new( klass, StringIO.new( FILE_TEXT_MULTIPLE ))
73
+ dp.instance_variable_get( :@scanner ).string.
74
+ should_not =~ /This is stuff we shouldn't see/
75
+ end
76
+
77
+ it "doesn't contain the __END__ token itself" do
78
+ klass = Class.new
79
+ dp = Chunker::DataParser.new( klass, StringIO.new( FILE_TEXT ))
80
+ dp.instance_variable_get( :@scanner ).string.should_not =~ /^__END__/
81
+ end
82
+ end
83
+
84
+
85
+ describe 'A class that includes Chunker' do
86
+
87
+ it "has all content in DATA_END if there are no sub blocks" do
88
+ File.stub!( :open ).and_return( StringIO.new( FILE_TEXT ))
89
+ klass = Class.new { include Chunker }
90
+
91
+ klass.constants.should_not include( 'DATA_POOP' )
92
+ klass.constants.should_not include( 'DATA_HURRRRG' )
93
+ klass.constants.should_not include( 'DATA_HURGADURGA' )
94
+ klass.constants.should include( 'DATA_END' )
95
+ end
96
+
97
+ it "separates data sub blocks into individual constants" do
98
+ File.stub!( :open ).and_return( StringIO.new( FILE_TEXT_MULTIPLE ))
99
+ klass = Class.new { include Chunker }
100
+
101
+ klass.constants.should include( 'DATA_END' )
102
+ klass.constants.should include( 'DATA_POOP' )
103
+ klass.constants.should include( 'DATA_HURRRRG' )
104
+ klass.constants.should include( 'DATA_HURGADURGA' )
105
+ end
106
+
107
+ it "has IO constants that contain the data block contents" do
108
+ File.stub!( :open ).and_return( StringIO.new( FILE_TEXT_MULTIPLE ))
109
+ klass = Class.new { include Chunker }
110
+
111
+ klass.const_get( :DATA_END ).read.chomp.should == ENDSTUFF
112
+ klass.const_get( :DATA_POOP ).read.chomp.should == POOP
113
+ klass.const_get( :DATA_HURRRRG ).read.chomp.should == HURRRRG
114
+ klass.const_get( :DATA_HURGADURGA ).read.chomp.should == HURGADURGA
115
+ end
116
+ end
117
+
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chunker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.53
5
+ platform: ruby
6
+ authors:
7
+ - Mahlon E. Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-10 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: "Ruby provides an automatic constant called DATA, which is an IO object that references all text in the current file under an __END__ token. I find it convenient to use the __END__ area to store all sorts of stuff, rather than have to worry about distributing separate files. The DATA constant is determined from whatever ruby believes $0 to be. It doesn't work inside of other required libraries, so you'll see stuff like this all the time: END = File.open( __FILE__ ).read.split( /^__END__/, 2 ).last It works, but it's more work than I want to do. Chunker solves this by parsing __END__ tokens for you, and making it available in the form of a 'DATA_END' constant. It installs this constant into the class that includes Chunker, so you can use it again and again, assuming you use a different file for each class. It also automatically parses out other things that look like tokens, so you can easily have multiple, distinct documents all embedded into the __END__ block."
17
+ email: mahlon@martini.nu
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - README
27
+ - LICENSE
28
+ - lib/chunker.rb
29
+ - spec/chunker_spec.rb
30
+ has_rdoc: true
31
+ homepage: http://projects.martini.nu/ruby-modules/wiki/Chunker
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project: mahlon
52
+ rubygems_version: 1.3.1
53
+ signing_key:
54
+ specification_version: 2
55
+ summary: A convenience library for parsing __END__ tokens consistently.
56
+ test_files:
57
+ - spec/chunker_spec.rb