grizzled-ruby 0.1.0
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.
- data/lib/grizzled/dir.rb +81 -0
- data/lib/grizzled/fileutil/includer.rb +241 -0
- data/lib/grizzled/forwarder.rb +91 -0
- data/lib/grizzled/stack.rb +159 -0
- data/lib/grizzled/string/template.rb +416 -0
- data/lib/grizzled/unix.rb +89 -0
- data/lib/grizzled.rb +42 -0
- metadata +75 -0
data/lib/grizzled/dir.rb
ADDED
@@ -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
|
+
|