grizzled-ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|