grizzled-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,81 @@
1
+ # This software is released under a BSD license, adapted from
2
+ # http://opensource.org/licenses/bsd-license.php
3
+ #
4
+ # Copyright (c) 2011, Brian M. Clapper
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # * Redistributions of source code must retain the above copyright notice,
12
+ # this list of conditions and the following disclaimer.
13
+ #
14
+ # * Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in the
16
+ # documentation and/or other materials provided with the distribution.
17
+ #
18
+ # * Neither the names "clapper.org", "Grizzled Ruby Library", nor the
19
+ # names of its contributors may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+ # ---------------------------------------------------------------------------
34
+
35
+ # Grizzled Ruby: A library of miscellaneous, general-purpose Ruby modules.
36
+ #
37
+ # Author:: Brian M. Clapper (mailto:bmc@clapper.org)
38
+ # Copyright:: Copyright (c) 2011 Brian M. Clapper
39
+ # License:: BSD License
40
+ class Dir
41
+
42
+ # Adds a +walk+ method to the standard Ruby +Dir+ class. +walk+ walks a
43
+ # directory tree, starting at _dirname_, invoking the supplied block on
44
+ # each directory. The block is passed a +Dir+ object. The directory is
45
+ # walked top-down, not depth-first. To terminate the traversal, the block
46
+ # should return +false+. Anything else (including +nil+) continues the
47
+ # traversal.
48
+ def self.walk(dirname, &block)
49
+ Grizzled::Directory.walk(dirname, &block)
50
+ end
51
+
52
+ # Adds an +expand_path+ convenience method to the standard Ruby +Dir+
53
+ # class.
54
+ def expand_path
55
+ File.expand_path(self.path)
56
+ end
57
+ end
58
+
59
+ module Grizzled
60
+
61
+ # Useful directory-related methods.
62
+ class Directory
63
+
64
+ # Walk a directory tree, starting at _dirname_, invoking the supplied
65
+ # block on each directory. The block is passed a +Dir+ object. The
66
+ # directory is walked top-down, not depth-first. To terminate the
67
+ # traversal, the block should return +false+. Anything else (including
68
+ # +nil+) continues the traversal.
69
+ def self.walk(dirname, &block)
70
+ if block.call(Dir.new(dirname)) != false
71
+ Dir.entries(dirname).each do |entry|
72
+ path = File.join(dirname, entry)
73
+ if File.directory?(path) && (entry != '..') && (entry != '.')
74
+ Grizzled::Directory.walk(path, &block)
75
+ end
76
+ end
77
+ end
78
+ nil
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,241 @@
1
+ # Provides a file inclusion preprocessor. See the documentation for
2
+ # the Grizzled::FileUtil::Includer class for complete details.
3
+ #
4
+ # ---
5
+ #
6
+ # This software is released under a BSD license, adapted from
7
+ # http://opensource.org/licenses/bsd-license.php
8
+ #
9
+ # Copyright (c) 2011, Brian M. Clapper
10
+ # All rights reserved.
11
+ #
12
+ # Redistribution and use in source and binary forms, with or without
13
+ # modification, are permitted provided that the following conditions are
14
+ # met:
15
+ #
16
+ # * Redistributions of source code must retain the above copyright notice,
17
+ # this list of conditions and the following disclaimer.
18
+ #
19
+ # * Redistributions in binary form must reproduce the above copyright
20
+ # notice, this list of conditions and the following disclaimer in the
21
+ # documentation and/or other materials provided with the distribution.
22
+ #
23
+ # * Neither the names "clapper.org", "Grizzled Ruby Library", nor the
24
+ # names of its contributors may be used to endorse or promote products
25
+ # derived from this software without specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
28
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
29
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
31
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
+ # ---------------------------------------------------------------------------
39
+
40
+ require 'open-uri'
41
+ require 'uri'
42
+ require 'pathname'
43
+ require 'grizzled/forwarder'
44
+ require 'tempfile'
45
+
46
+ module Grizzled
47
+
48
+ module FileUtil
49
+
50
+ # Thrown when include file processing encounters errors.
51
+ class IncludeException < StandardError; end
52
+
53
+ # Internal container for source information.
54
+ class IncludeSource
55
+ URL_PATTERN = %r{^(http|https|ftp)://}
56
+
57
+ attr_reader :reader, :uri
58
+
59
+ def initialize(reader, uri)
60
+ @reader = reader
61
+ @uri = uri
62
+ end
63
+ end
64
+
65
+ # == Introduction
66
+ #
67
+ # An +Includer+ object preprocesses a text file, resolve _include_
68
+ # references. The +Includer+ is an +Enumerable+, allowing iteration over
69
+ # the lines of the resulting expanded file.
70
+ #
71
+ # == Include Syntax
72
+ #
73
+ # The _include_ syntax is defined by a regular expression; any line
74
+ # that matches the regular expression is treated as an _include_
75
+ # directive. The default regular expression matches include directives
76
+ # like this::
77
+ #
78
+ # %include "/absolute/path/to/file"
79
+ # %include "../relative/path/to/file"
80
+ # %include "local_reference"
81
+ # %include "http://localhost/path/to/my.config"
82
+ #
83
+ # Relative and local file references are relative to the including file
84
+ # or URL. That, if an +Includer+ is processing file "/home/bmc/foo.txt"
85
+ # and encounters an attempt to include file "bar.txt", it will assume
86
+ # "bar.txt" is to be found in "/home/bmc".
87
+ #
88
+ # Similarly, if an +Includer+ is processing URL
89
+ # "http://localhost/bmc/foo.txt" and encounters an attempt to include
90
+ # file "bar.txt", it will assume "bar.txt" is to be found at
91
+ # "http://localhost/bmc/bar.txt".
92
+ #
93
+ # Nested includes are permitted; that is, an included file may, itself,
94
+ # include other files. The maximum recursion level is configurable and
95
+ # defaults to 100.
96
+ #
97
+ # The include syntax can be changed by passing a different regular
98
+ # expression to the +Includer+ class constructor.
99
+ #
100
+ # == Supported Methods
101
+ #
102
+ # +Includer+ supports all the methods of the +File+ class and can be
103
+ # used the same way as a +File+ object is used.
104
+ #
105
+ # == Examples
106
+ #
107
+ # Preprocess a file containing include directives, then read the result:
108
+ #
109
+ # require 'grizzled/fileutil/includer'
110
+ # include Grizzled::FileUtil
111
+ #
112
+ # inc = Includer.new(path)
113
+ # inc.each do |line|
114
+ # puts(line)
115
+ # end
116
+ class Includer
117
+
118
+ include Enumerable
119
+ include Grizzled::Forwarder
120
+
121
+ attr_reader :name, :max_nesting
122
+
123
+ # Initialize a new +Includer+.
124
+ #
125
+ # Parameters:
126
+ #
127
+ # [+source+] A string, representing a file name or URL (http, https or
128
+ # ftp), a +File+ object, or an object with an +each_line+
129
+ # method that returns individual lines of input.
130
+ # [+options+] Various processing options. See below.
131
+ #
132
+ # Options:
133
+ #
134
+ # [+:max_nesting+] Maximum include nesting level. Default: 100
135
+ # [+:include_pattern+] String regex pattern to match include directives.
136
+ # Must have a single regex group for the file name
137
+ # or URL. Default: ^%include\s"([^"]+)"
138
+ def initialize(source, options={})
139
+ @max_nesting = options.fetch(:max_nesting, 100)
140
+ inc_pattern = options.fetch(:include_pattern, '^%include\s"([^"]+)"')
141
+ @include_re = /#{inc_pattern}/
142
+ includer_source = source_to_includer_source source
143
+ @source_uri = includer_source.uri
144
+ @temp = preprocess includer_source
145
+ @input = File.open @temp.path
146
+ forward_to @input
147
+ end
148
+
149
+ # Return the path of the original include file, if defined. If the
150
+ # original source was a URL, the URL is returned. If the source was a
151
+ # string, nil is returned.
152
+ def path
153
+ @source_uri.path
154
+ end
155
+
156
+ # Force the underlying resource to be closed.
157
+ def close
158
+ @input.close
159
+ @temp.unlink
160
+ end
161
+
162
+ private
163
+
164
+ def source_to_includer_source(source)
165
+ if source.class == String
166
+ open_source(URI::parse(source))
167
+ elsif source.class == File
168
+ open_source(URI::parse(source.path))
169
+ elsif source.respond_to? :each_line
170
+ IncludeSource.new(source, nil)
171
+ else
172
+ raise IncludeException.new("Bad input of class #{source.class}")
173
+ end
174
+ end
175
+
176
+ def preprocess(includer_source)
177
+
178
+ def do_read(input, temp, level)
179
+ input.reader.each_line do |line|
180
+ if m = @include_re.match(line)
181
+ if level >= @max_nesting
182
+ raise IncludeException.new("Too many nested includes " +
183
+ "(#{level.to_s}), at: " +
184
+ "\"#{line.chomp}\"")
185
+ end
186
+ new_input = process_include(m[1], input)
187
+ do_read(new_input, temp, level + 1)
188
+ else
189
+ temp.write(line)
190
+ end
191
+ end
192
+ end
193
+
194
+
195
+ temp = Tempfile.new('grizzled_includer')
196
+ begin
197
+ do_read(includer_source, temp, 1)
198
+ ensure
199
+ temp.close
200
+ end
201
+
202
+ temp
203
+ end
204
+
205
+ # Handle an include reference.
206
+ def process_include(source, parent_input)
207
+ cur_uri = parent_input.uri
208
+ uri = URI::parse(source)
209
+ if (cur_uri != nil) and (uri.scheme == nil)
210
+ # Could be a relative path. Should be relative to the parent input.
211
+ pathname = Pathname.new(source)
212
+ if not pathname.absolute?
213
+ # Not an absolute path, and the including source has a path
214
+ # (i.e., wasn't a string). Make this one relative to the path.
215
+ uri = cur_uri.clone
216
+ uri.path = File.join(::File.dirname(cur_uri.path), source)
217
+ end
218
+ end
219
+
220
+ open_source(uri)
221
+ end
222
+
223
+ # Open an input source, based on a parsed URI.
224
+ def open_source(uri)
225
+ case uri.scheme
226
+ when nil then f = open(uri.path) # assume file/path
227
+ when 'file' then f = open(uri.path) # open path directly
228
+ when 'http' then f = open(uri.to_s) # open-uri will handle it
229
+ when 'https' then f = open(uri.to_s) # open-uri will handle it
230
+ when 'ftp' then f = open(uri.to_s) # open-uri will handle it
231
+
232
+ else raise IncludeException.new("Don't know how to open #{uri.to_s}")
233
+ end
234
+
235
+ IncludeSource.new(f, uri)
236
+ end
237
+ end # class Includer
238
+
239
+ end # module File
240
+ end # module Grizzled
241
+
@@ -0,0 +1,91 @@
1
+ # Provides a module which, when mixed in, can be used to forward all
2
+ # missing methods to another object.
3
+ #
4
+ # ---
5
+ #
6
+ # This software is released under a BSD license, adapted from
7
+ # http://opensource.org/licenses/bsd-license.php
8
+ #
9
+ # Copyright (c) 2011, Brian M. Clapper
10
+ # All rights reserved.
11
+ #
12
+ # Redistribution and use in source and binary forms, with or without
13
+ # modification, are permitted provided that the following conditions are
14
+ # met:
15
+ #
16
+ # * Redistributions of source code must retain the above copyright notice,
17
+ # this list of conditions and the following disclaimer.
18
+ #
19
+ # * Redistributions in binary form must reproduce the above copyright
20
+ # notice, this list of conditions and the following disclaimer in the
21
+ # documentation and/or other materials provided with the distribution.
22
+ #
23
+ # * Neither the names "clapper.org", "Grizzled Ruby Library", nor the
24
+ # names of its contributors may be used to endorse or promote products
25
+ # derived from this software without specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
28
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
29
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
31
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
+ # ---------------------------------------------------------------------------
39
+
40
+ module Grizzled
41
+
42
+ # +Forwarder+ makes it easy to forward calls to another object.
43
+ #
44
+ # Examples:
45
+ #
46
+ # Forward all unimplemented methods to a file:
47
+ #
48
+ # class Test
49
+ # include Grizzled::Forwarder
50
+ #
51
+ # def initialize(file)
52
+ # forward_to file
53
+ # end
54
+ # end
55
+ #
56
+ # Test.new(File.open('/tmp/foobar')).each_line do |line|
57
+ # puts(line)
58
+ # end
59
+ #
60
+ # Forward all unimplemented calls, _except_ +each+ to the specified
61
+ # object. Calls to +each+ will raise a +NoMethodError+:
62
+ #
63
+ # class Test
64
+ # include Grizzled::Forwarder
65
+ #
66
+ # def initialize(file)
67
+ # forward_to file, [:each]
68
+ # end
69
+ # end
70
+ module Forwarder
71
+
72
+ # Forward all unimplemented method calls to +obj+, except those
73
+ # whose symbols are listed in the +exceptions+ array.
74
+ def forward_to(obj, exceptions=[])
75
+ @forward_obj = obj
76
+
77
+ require 'set'
78
+
79
+ @forwarder_exceptions = Set.new(exceptions)
80
+ class << self
81
+ def method_missing(m, *args, &block)
82
+ if not @forwarder_exceptions.include? m
83
+ @forward_obj.send(m, *args, &block)
84
+ else
85
+ super(m, *args, &block)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,159 @@
1
+ # Provides a simple stack implementation. See the Grizzled::Stack class
2
+ # for complete details.
3
+ #
4
+ # ---
5
+ #
6
+ # This software is released under a BSD license, adapted from
7
+ # http://opensource.org/licenses/bsd-license.php
8
+ #
9
+ # Copyright (c) 2011, Brian M. Clapper
10
+ # All rights reserved.
11
+ #
12
+ # Redistribution and use in source and binary forms, with or without
13
+ # modification, are permitted provided that the following conditions are
14
+ # met:
15
+ #
16
+ # * Redistributions of source code must retain the above copyright notice,
17
+ # this list of conditions and the following disclaimer.
18
+ #
19
+ # * Redistributions in binary form must reproduce the above copyright
20
+ # notice, this list of conditions and the following disclaimer in the
21
+ # documentation and/or other materials provided with the distribution.
22
+ #
23
+ # * Neither the names "clapper.org", "Grizzled Ruby Library", nor the
24
+ # names of its contributors may be used to endorse or promote products
25
+ # derived from this software without specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
28
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
29
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
31
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
+ # ---------------------------------------------------------------------------
39
+
40
+ # Grizzled Ruby: A library of miscellaneous, general-purpose Ruby modules.
41
+ #
42
+ # Author:: Brian M. Clapper (mailto:bmc@clapper.org)
43
+ # Copyright:: Copyright (c) 2011 Brian M. Clapper
44
+ # License:: BSD License
45
+ module Grizzled
46
+
47
+ # Thrown for un-safe stacks if the stack
48
+ class StackUnderflowException < StandardError; end
49
+
50
+ # A simple stack wrapper on top of a Ruby array, providing a little more
51
+ # protection that using an array directly.
52
+ class Stack
53
+
54
+ include Enumerable
55
+
56
+ attr_reader :pop_empty_nil
57
+
58
+ # Initialize a new stack.
59
+ #
60
+ # Parameters:
61
+ #
62
+ # [+pop_empty_nil+] +true+ if popping an empty stack should just return
63
+ # +nil+, +false+ if it should thrown an exception.
64
+ def initialize(pop_empty_nil=true)
65
+ @the_stack = []
66
+ @pop_empty_nil = pop_empty_nil
67
+ end
68
+
69
+ # Pop the top element from the stack. If the stack is empty, this method
70
+ # throws a +StackUnderflowException+, if +pop_empty_nil+ is +false+; or
71
+ # returns nil, if +pop_empty_nil+ is +true+.
72
+ def pop
73
+ if (@the_stack.length == 0) && (not @pop_empty_nil)
74
+ raise StackUnderflowException.new
75
+ end
76
+ @the_stack.pop
77
+ end
78
+
79
+ # Pop every element of the stack, returning the results as an array
80
+ # and clearing the stack.
81
+ def pop_all
82
+ result = @the_stack.reverse
83
+ @the_stack.clear
84
+ result
85
+ end
86
+
87
+ # Convenience method for +length == 0+.
88
+ def is_empty?
89
+ length == 0
90
+ end
91
+
92
+ # Push an element or an array of elements onto the stack. Returns the
93
+ # stack itself, to allow chaining. Note: If you push an array of elements,
94
+ # the elements end up being reversed on the stack. That is, this:
95
+ #
96
+ # stack = Stack.new.push([1, 2, 3]) # yields Stack[3, 2, 1]
97
+ #
98
+ # is equivalent to
99
+ #
100
+ # stack = Stack.new
101
+ # [1, 2, 3].each {|i| stack.push i}
102
+ def push(element)
103
+ if element.class == Array
104
+ element.each {|e| @the_stack.push e}
105
+ else
106
+ @the_stack.push element
107
+ end
108
+ self
109
+ end
110
+
111
+ # Returns the size of the stack.
112
+ def length
113
+ @the_stack.length
114
+ end
115
+
116
+ # Yield each element of the stack, in turn. Unaffected by a change in
117
+ # the stack.
118
+ def each
119
+ self.to_a.each do |element|
120
+ yield element
121
+ end
122
+ end
123
+
124
+ # Clear the stack. Returns the stack, for chaining.
125
+ def clear
126
+ @the_stack.clear
127
+ self
128
+ end
129
+
130
+ # Printable version.
131
+ def inspect
132
+ @the_stack.inspect
133
+ end
134
+
135
+ # Return the stack as an array.
136
+ def to_a
137
+ @the_stack.reverse
138
+ end
139
+
140
+ # Return the stack's hash.
141
+ def hash
142
+ @the_stack.hash
143
+ end
144
+
145
+ # Determine if this hash is equal to another one.
146
+ def eql?(other)
147
+ (other.class == Stack) and (other.to_a == to_a)
148
+ end
149
+
150
+ # Compare this stack to another element.
151
+ def <=>(other)
152
+ if other.class == Stack
153
+ other.to_a <=> to_a
154
+ else
155
+ other.to_s <=> this.to_s
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,416 @@
1
+ # Provides a simple variable substitution capability, similar, in concept,
2
+ # to Python's +StringTemplate+ class. See the Grizzled::String::Template
3
+ # module, the Grizzled::String::UnixShellStringTemplate class and the
4
+ # Grizzled::String::WindowsCmdStringTemplate class for complete details.
5
+ #
6
+ # ---
7
+ #
8
+ # This software is released under a BSD license, adapted from
9
+ # http://opensource.org/licenses/bsd-license.php
10
+ #
11
+ # Copyright (c) 2011, Brian M. Clapper
12
+ # All rights reserved.
13
+ #
14
+ # Redistribution and use in source and binary forms, with or without
15
+ # modification, are permitted provided that the following conditions are
16
+ # met:
17
+ #
18
+ # * Redistributions of source code must retain the above copyright notice,
19
+ # this list of conditions and the following disclaimer.
20
+ #
21
+ # * Redistributions in binary form must reproduce the above copyright
22
+ # notice, this list of conditions and the following disclaimer in the
23
+ # documentation and/or other materials provided with the distribution.
24
+ #
25
+ # * Neither the names "clapper.org", "Grizzled Ruby Library", nor the
26
+ # names of its contributors may be used to endorse or promote products
27
+ # derived from this software without specific prior written permission.
28
+ #
29
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
33
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
+ # ---------------------------------------------------------------------------
41
+
42
+ # Grizzled Ruby: A library of miscellaneous, general-purpose Ruby modules.
43
+ #
44
+ # Author:: Brian M. Clapper (mailto:bmc@clapper.org)
45
+ # Copyright:: Copyright (c) 2011 Brian M. Clapper
46
+ # License:: BSD License
47
+ module Grizzled
48
+
49
+ module String
50
+
51
+ # Grizzled::String::Template - A simple string templating solution
52
+ # modeled after Python's +StringTemplate+ library. While Ruby has less
53
+ # need of this kind of library, because of its built-in string
54
+ # interpolation, this module can be useful for interoperating with
55
+ # Python or with the Grizzled Scala library's string templating
56
+ # library.
57
+ module Template
58
+
59
+ # Exception raised for non-existent variables in non-safe templates.
60
+ class VariableNotFoundException < StandardError; end
61
+
62
+ # What a parsed variable looks like.
63
+ class Variable
64
+
65
+ attr_reader :istart, :iend, :name, :default
66
+
67
+ def initialize(istart, iend, name, default=nil)
68
+ @istart = istart
69
+ @iend = iend
70
+ @name = name
71
+ @default = default
72
+ end
73
+
74
+ alias :inspect :to_s
75
+ def to_s
76
+ "#{name}"
77
+ end
78
+ end
79
+
80
+ # Base (abstract) class for a string template. Common logic is here.
81
+ # Subclasses implement specific methods.
82
+ class TemplateBase
83
+
84
+ attr_reader :resolver, :safe
85
+
86
+ # Initializer.
87
+ #
88
+ # Parameters:
89
+ #
90
+ # [+resolver+] A hash-like object that can take a variable name (via
91
+ # the +[]+ function) and resolve its value, returning
92
+ # the value (which is converted to string) or +nil+.
93
+ # [+options+] hash of options. See below.
94
+ #
95
+ # Options:
96
+ #
97
+ # [+:safe+] +true+ for a safe template that substitutes a blank
98
+ # string for a non-existent variable, instead of
99
+ # throwing an exception. Defaults to +true+.
100
+ def initialize(resolver, options={})
101
+ @resolver = resolver
102
+ @safe = options.fetch(:safe, true)
103
+ end
104
+
105
+ # Replace all variable references in the given string. Variable
106
+ # references are recognized per the regular expression passed to
107
+ # the constructor. If a referenced variable is not found in the
108
+ # resolver, this method either:
109
+ #
110
+ # - throws a +VariableNotFoundException+ (if +safe+ is +false+).
111
+ # - substitutes an empty string (if +safe+ is +true+)
112
+ #
113
+ # Recursive references are supported (but beware of infinite recursion).
114
+ #
115
+ # Parameters:
116
+ #
117
+ # [+s+] the string in which to replace variable references
118
+ #
119
+ # Returns the substituted result.
120
+ def substitute(s)
121
+
122
+ def substitute_variable(var, s)
123
+ end_string = var.iend == s.length ? "" : s[var.iend..-1]
124
+ value = get_variable(var.name, var.default)
125
+ transformed =
126
+ (var.istart == 0 ? "" : s[0..(var.istart-1)]) + value + end_string
127
+ substitute(transformed)
128
+ end
129
+
130
+ # Locate the next variable reference.
131
+ var = find_variable_ref(s)
132
+ if var.nil?
133
+ s
134
+ else
135
+ substitute_variable(var, s)
136
+ end
137
+ end
138
+
139
+ # Parse the location of the first variable in the string. Subclasses
140
+ # should override this method.
141
+ #
142
+ # Parameters:
143
+ #
144
+ # [+s+] the string
145
+ #
146
+ # Returns a +Variable+ object, or +nil+.
147
+ def find_variable_ref(s)
148
+ nil
149
+ end
150
+
151
+ # Get a variable's value, returning the empty string or throwing an
152
+ # exception, depending on the setting of +safe+.
153
+ #
154
+ # Parameters:
155
+ #
156
+ # [+name+] Variable name
157
+ # [+default+] Default value, or +nil+
158
+ def get_variable(name, default)
159
+
160
+ def handle_no_value(default, name)
161
+ if not default.nil?
162
+ default
163
+ elsif @safe
164
+ ""
165
+ else
166
+ raise VariableNotFoundException.new(name)
167
+ end
168
+ end
169
+
170
+ resolver[name] || handle_no_value(default, name)
171
+ end
172
+ end # TemplateBase
173
+
174
+ # A string template that uses the Unix shell-like syntax +${varname}+
175
+ # (or +$varname+) for variable references. A variable's name typically
176
+ # consists of alphanumerics and underscores, but is controlled by the a
177
+ # supplied regular expression. To include a literal "$" in a string,
178
+ # escape it with a backslash.
179
+ #
180
+ # For this class, the general form of a variable reference is:
181
+ #
182
+ # ${varname?default}
183
+ #
184
+ # The +?default+ suffix is optional and specifies a default value
185
+ # to be used if the variable has no value.
186
+ #
187
+ # A shorthand form of a variable reference is:
188
+ #
189
+ # $varname
190
+ #
191
+ # The _default_ capability is not available in the shorthand form.
192
+ class UnixShellStringTemplate < TemplateBase
193
+
194
+ ESCAPED_DOLLAR_PLACEHOLDER = "\001"
195
+ ESCAPED_DOLLAR = %r{(\\*)(\\\$)}
196
+
197
+ # Initialize a new +UnixShellStringTemplate+. Supports various hash
198
+ # options.
199
+ #
200
+ # Parameters:
201
+ #
202
+ # [+resolver+] A hash-like object that can take a variable name (via
203
+ # the +[]+ function) and resolve its value, returning
204
+ # the value (which is converted to string) or +nil+.
205
+ # [+options+] hash of options. See below.
206
+ #
207
+ # Options:
208
+ #
209
+ # [+:safe+] +true+ for a safe template that substitutes a blank
210
+ # string for a non-existent variable, instead of
211
+ # throwing an exception. Defaults to +true+.
212
+ # [+:var_pattern+] Regular expression pattern (as a string, not a
213
+ # Regexp object) to match a variable name. Defaults
214
+ # to "[A-Za-z0-9_]+"
215
+ def initialize(resolver, options={})
216
+ super(resolver, options)
217
+ var_re = options.fetch(:var_pattern, "[A-Za-z0-9_]+")
218
+ @long_var_regexp = %r{\$\{(#{var_re})(\?[^\}]*)?\}}
219
+ @short_var_regexp = %r{\$(#{var_re})}
220
+ end
221
+
222
+ # Replace all variable references in the given string. Variable
223
+ # references are recognized per the regular expression passed to
224
+ # the constructor. If a referenced variable is not found in the
225
+ # resolver, this method either:
226
+ #
227
+ # - throws a +VariableNotFoundException+ (if +safe+ is +false+).
228
+ # - substitutes an empty string (if +safe+ is +true+)
229
+ #
230
+ # Recursive references are supported (but beware of infinite recursion).
231
+ #
232
+ # Parameters:
233
+ #
234
+ # [+s+] the string in which to replace variable references
235
+ #
236
+ # Returns the substituted result.
237
+ def substitute(s)
238
+ # Kludge to handle escaped "$". Temporarily replace it with
239
+ # something highly unlikely to be in the string. Then, put a single
240
+ # "$" in its place, after the substitution. Must be sure to handle
241
+ # even versus odd number of backslash characters.
242
+
243
+ def pre_sub(s)
244
+
245
+ def handle_match(m, s)
246
+ if (m[1].length % 2) == 0
247
+ # Odd number of backslashes before "$", including
248
+ # the one with the dollar token (group 2). Valid escape.
249
+
250
+ b = m.begin(0)
251
+ start = (b == 0 ? "" : s[0..(b-1)])
252
+ start + ESCAPED_DOLLAR_PLACEHOLDER + pre_sub(s[m.end(0)..-1])
253
+ else
254
+ # Even number of backslashes before "$", including the one
255
+ # with the dollar token (group 2). Not an escape.
256
+ s
257
+ end
258
+ end
259
+
260
+ # Check for an escaped "$"
261
+ m = ESCAPED_DOLLAR.match(s)
262
+ if (m)
263
+ handle_match(m, s)
264
+ else
265
+ s
266
+ end
267
+ end
268
+
269
+ s2 = super(pre_sub(s))
270
+ s2.gsub(ESCAPED_DOLLAR_PLACEHOLDER, '$')
271
+ end
272
+
273
+ # Parse the location of the first variable in the string. Subclasses
274
+ # should override this method.
275
+ #
276
+ # Parameters:
277
+ #
278
+ # [+s+] the string
279
+ #
280
+ # Returns a +Variable+ object, or +nil+.
281
+ def find_variable_ref(s)
282
+
283
+ def handle_long_match(m)
284
+ name = m[1]
285
+ if m[2].nil?
286
+ default = nil
287
+ else
288
+ # Pull off the "?"
289
+ default = m[2][1..-1]
290
+ end
291
+
292
+ Variable.new(m.begin(0), m.end(0), name, default)
293
+ end
294
+
295
+ def handle_no_long_match(s)
296
+ m = @short_var_regexp.match(s)
297
+ if m.nil?
298
+ nil
299
+ else
300
+ Variable.new(m.begin(0), m.end(0), m[1], nil)
301
+ end
302
+ end
303
+
304
+ m = @long_var_regexp.match(s)
305
+ if m.nil?
306
+ handle_no_long_match(s)
307
+ else
308
+ handle_long_match(m)
309
+ end
310
+ end
311
+
312
+ end # UnixShellStringTemplate
313
+
314
+ # A string template that uses the Windows +cmd.exe+ syntax +%varname%+
315
+ # for variable references. A variable's name may consist of alphanumerics
316
+ # and underscores. To include a literal "%" in a string, escape it with
317
+ # a backslash ("\%").
318
+ class WindowsCmdStringTemplate < TemplateBase
319
+
320
+ ESCAPED_PERCENT = %r{(\\*)(%)}
321
+ ESCAPED_PERCENT_PLACEHOLDER = '\001'
322
+
323
+ # Initialize a new +WindowsCmdStringTemplate+. Supports various hash
324
+ # options.
325
+ #
326
+ # Parameters:
327
+ #
328
+ # [+resolver+] A hash-like object that can take a variable name (via
329
+ # the +[]+ function) and resolve its value, returning
330
+ # the value (which is converted to string) or +nil+.
331
+ # [+options+] hash of options. See below.
332
+ #
333
+ # Options:
334
+ #
335
+ # [+:safe+] +true+ for a safe template that substitutes a blank
336
+ # string for a non-existent variable, instead of
337
+ # throwing an exception. Defaults to +true+.
338
+ # [+:var_pattern+] Regular expression pattern (as a string, not a
339
+ # Regexp object) to match a variable name. Defaults
340
+ # to "[A-Za-z0-9_]+"
341
+ def initialize(resolver, options={})
342
+ super(resolver, options)
343
+ var_pat = options.fetch(:var_pattern, "[A-Za-z0-9_]+")
344
+ @var_re = %r{%(#{var_pat})%}
345
+ end
346
+
347
+ # Replace all variable references in the given string. Variable
348
+ # references are recognized per the regular expression passed to
349
+ # the constructor. If a referenced variable is not found in the
350
+ # resolver, this method either:
351
+ #
352
+ # - throws a +VariableNotFoundException+ (if +safe+ is +false+).
353
+ # - substitutes an empty string (if +safe+ is +true+)
354
+ #
355
+ # Recursive references are supported (but beware of infinite recursion).
356
+ #
357
+ # Parameters:
358
+ #
359
+ # [+s+] the string in which to replace variable references
360
+ #
361
+ # Returns the substituted result.
362
+ def substitute(s)
363
+ # Kludge to handle escaped "%". Temporarily replace it with
364
+ # something highly unlikely to be in the string. Then, put a single
365
+ # "%" in its place, after the substitution. Must be sure to handle
366
+ # even versus odd number of backslash characters.
367
+
368
+ def pre_sub(s)
369
+
370
+ def handle_match(m, s)
371
+ if (m[1].length % 2) == 1
372
+ # Odd number of backslashes before "%". Valid escape.
373
+
374
+ b = m.begin(0)
375
+ start = (b == 0 ? "" : s[0..(b-1)])
376
+ start + ESCAPED_PERCENT_PLACEHOLDER + pre_sub(s[m.end(0)..-1])
377
+ else
378
+ # Even number of backslashes before "%". Not an escape.
379
+ s
380
+ end
381
+ end
382
+
383
+ # Check for an escaped "%"
384
+ m = ESCAPED_PERCENT.match(s)
385
+ if (m)
386
+ handle_match(m, s)
387
+ else
388
+ s
389
+ end
390
+ end
391
+
392
+ s2 = super(pre_sub(s))
393
+ s2.gsub(ESCAPED_PERCENT_PLACEHOLDER, '%')
394
+ end
395
+
396
+ # Parse the location of the first variable in the string. Subclasses
397
+ # should override this method.
398
+ #
399
+ # Parameters:
400
+ #
401
+ # [+s+] the string
402
+ #
403
+ # Returns a +Variable+ object, or +nil+.
404
+ def find_variable_ref(s)
405
+ m = @var_re.match(s)
406
+ if m.nil?
407
+ nil
408
+ else
409
+ Variable.new(m.begin(0), m.end(0), m[1], nil)
410
+ end
411
+ end
412
+ end # WindowsCmdStringTemplate
413
+
414
+ end # module
415
+ end # module
416
+ end
@@ -0,0 +1,89 @@
1
+ # This software is released under a BSD license, adapted from
2
+ # http://opensource.org/licenses/bsd-license.php
3
+ #
4
+ # Copyright (c) 2011, Brian M. Clapper
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # * Redistributions of source code must retain the above copyright notice,
12
+ # this list of conditions and the following disclaimer.
13
+ #
14
+ # * Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in the
16
+ # documentation and/or other materials provided with the distribution.
17
+ #
18
+ # * Neither the names "clapper.org", "Grizzled Ruby Library", nor the
19
+ # names of its contributors may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+ # ---------------------------------------------------------------------------
34
+
35
+ # Grizzled Ruby: A library of miscellaneous, general-purpose Ruby modules.
36
+ #
37
+ # Author:: Brian M. Clapper (mailto:bmc@clapper.org)
38
+ # Copyright:: Copyright (c) 2011 Brian M. Clapper
39
+ # License:: BSD License
40
+ module Grizzled
41
+
42
+ # Unix-related OS things.
43
+ module Unix
44
+
45
+ # A +User+ object allows you to do things with Unix users, such as
46
+ # (for instance) run code as that user.
47
+ class User
48
+
49
+ require 'etc'
50
+
51
+ # Initialize a new user. The +id+ parameter is either a user name
52
+ # (string) or a UID (integer).
53
+ def initialize(id)
54
+ # Find the user in the password database.
55
+ @pwent = (id.is_a? Integer) ? Etc.getpwuid(id) : Etc.getpwnam(id)
56
+ end
57
+
58
+ # Run a block of code as this user.
59
+ #
60
+ # [+block+] the block to execute as that user. It will receive this
61
+ # +User+ object as a parameter.
62
+ #
63
+ # This function will only run as 'root'.
64
+ #
65
+ # === Example
66
+ #
67
+ # require 'grizzled/unix'
68
+ # require 'fileutils'
69
+ #
70
+ # Grizzled::Unix::User.new('root').run_as |u|
71
+ # rm_r(File.join('/tmp', '*')) # Yeah, this is dangerous
72
+ # end
73
+ def run_as(&block)
74
+
75
+ # Fork the child process. Process.fork will run a given block of
76
+ # code in the child process.
77
+ child = Process.fork do
78
+ # We're in the child. Set the process's user ID.
79
+ Process.uid = @pwent.uid
80
+
81
+ # Invoke the caller's block of code.
82
+ block.call(self)
83
+ end
84
+
85
+ Process.waitpid(child)
86
+ end
87
+ end
88
+ end
89
+ end
data/lib/grizzled.rb ADDED
@@ -0,0 +1,42 @@
1
+ # Grizzled Ruby is a library of miscellaneous Ruby classes and modules.
2
+ # See http://software.clapper.org/grizzled-ruby/ for more details.
3
+ #
4
+ # Author:: Brian M. Clapper (mailto:bmc@clapper.org)
5
+ # Copyright:: Copyright (c) 2011 Brian M. Clapper
6
+ # License:: BSD License
7
+ #
8
+ # ---
9
+ #
10
+ # This software is released under a BSD license, adapted from
11
+ # http://opensource.org/licenses/bsd-license.php
12
+ #
13
+ # Copyright (c) 2011, Brian M. Clapper
14
+ # All rights reserved.
15
+ #
16
+ # Redistribution and use in source and binary forms, with or without
17
+ # modification, are permitted provided that the following conditions are
18
+ # met:
19
+ #
20
+ # * Redistributions of source code must retain the above copyright notice,
21
+ # this list of conditions and the following disclaimer.
22
+ #
23
+ # * Redistributions in binary form must reproduce the above copyright
24
+ # notice, this list of conditions and the following disclaimer in the
25
+ # documentation and/or other materials provided with the distribution.
26
+ #
27
+ # * Neither the names "clapper.org", "Grizzled Ruby Library", nor the
28
+ # names of its contributors may be used to endorse or promote products
29
+ # derived from this software without specific prior written permission.
30
+ #
31
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
32
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
33
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
35
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
37
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
38
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
39
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42
+ # ---------------------------------------------------------------------------
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grizzled-ruby
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Brian M. Clapper
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-12 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |
23
+ Grizzled Ruby is a general purpose library of Ruby modules and classes
24
+
25
+ email: bmc@clapper.org
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - lib/grizzled/stack.rb
34
+ - lib/grizzled/fileutil/includer.rb
35
+ - lib/grizzled/dir.rb
36
+ - lib/grizzled/forwarder.rb
37
+ - lib/grizzled/string/template.rb
38
+ - lib/grizzled/unix.rb
39
+ - lib/grizzled.rb
40
+ has_rdoc: true
41
+ homepage: http://software.clapper.org/grizzled-ruby
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.6.2
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Miscellaneous, general-purpose Ruby modules and classes
74
+ test_files: []
75
+