riel 1.0.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/README +0 -0
- data/lib/riel/ansicolor.rb +93 -0
- data/lib/riel/array.rb +20 -0
- data/lib/riel/command.rb +30 -0
- data/lib/riel/date.rb +16 -0
- data/lib/riel/dir.rb +90 -0
- data/lib/riel/enumerable.rb +66 -0
- data/lib/riel/env.rb +49 -0
- data/lib/riel/file.rb +212 -0
- data/lib/riel/filetype.rb +189 -0
- data/lib/riel/hash.rb +12 -0
- data/lib/riel/io.rb +20 -0
- data/lib/riel/log.rb +548 -0
- data/lib/riel/matchdata.rb +13 -0
- data/lib/riel/optproc.rb +369 -0
- data/lib/riel/pathname.rb +16 -0
- data/lib/riel/rcfile.rb +35 -0
- data/lib/riel/regexp.rb +152 -0
- data/lib/riel/setdiff.rb +53 -0
- data/lib/riel/size_converter.rb +62 -0
- data/lib/riel/string.rb +81 -0
- data/lib/riel/tempfile.rb +28 -0
- data/lib/riel/text.rb +408 -0
- data/lib/riel/timer.rb +52 -0
- data/lib/riel.rb +13 -0
- data/test/riel/array_test.rb +22 -0
- data/test/riel/command_test.rb +28 -0
- data/test/riel/date_test.rb +17 -0
- data/test/riel/dir_test.rb +98 -0
- data/test/riel/enumerable_test.rb +27 -0
- data/test/riel/env_test.rb +52 -0
- data/test/riel/file_test.rb +242 -0
- data/test/riel/filetype_test.rb +32 -0
- data/test/riel/hash_test.rb +12 -0
- data/test/riel/io_test.rb +22 -0
- data/test/riel/log_test.rb +184 -0
- data/test/riel/matchdata_test.rb +15 -0
- data/test/riel/optproc_test.rb +233 -0
- data/test/riel/pathname_test.rb +36 -0
- data/test/riel/rcfile_test.rb +44 -0
- data/test/riel/regexp_test.rb +24 -0
- data/test/riel/setdiff_test.rb +26 -0
- data/test/riel/size_converter_test.rb +64 -0
- data/test/riel/string_test.rb +58 -0
- data/test/riel/tempfile_test.rb +16 -0
- data/test/riel/text_test.rb +102 -0
- data/test/riel/timer_test.rb +43 -0
- metadata +134 -0
data/lib/riel/optproc.rb
ADDED
@@ -0,0 +1,369 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'riel/env'
|
5
|
+
require 'riel/log'
|
6
|
+
require 'riel/text'
|
7
|
+
require 'riel/enumerable'
|
8
|
+
|
9
|
+
|
10
|
+
module OptProc
|
11
|
+
|
12
|
+
class Option
|
13
|
+
include Loggable
|
14
|
+
|
15
|
+
attr_reader :md, :tags, :res
|
16
|
+
|
17
|
+
ARG_INTEGER = %r{^ ([\-\+]?\d+) $ }x
|
18
|
+
ARG_FLOAT = %r{^ ([\-\+]?\d* (?:\.\d+)?) $ }x
|
19
|
+
ARG_STRING = %r{^ [\"\']? (.*?) [\"\']? $ }x
|
20
|
+
ARG_BOOLEAN = %r{^ (yes|true|on|no|false|off) $ }ix
|
21
|
+
|
22
|
+
ARG_TYPES = Array.new
|
23
|
+
ARG_TYPES << [ :integer, ARG_INTEGER ]
|
24
|
+
ARG_TYPES << [ :float, ARG_FLOAT ]
|
25
|
+
ARG_TYPES << [ :string, ARG_STRING ]
|
26
|
+
ARG_TYPES << [ :boolean, ARG_BOOLEAN ]
|
27
|
+
|
28
|
+
def initialize(args = Hash.new, &blk)
|
29
|
+
@tags = args[:tags] || Array.new
|
30
|
+
@rc = args[:rc]
|
31
|
+
@rc = [ @rc ] if @rc.kind_of?(String)
|
32
|
+
@md = nil
|
33
|
+
@set = blk || args[:set]
|
34
|
+
|
35
|
+
@type = nil
|
36
|
+
@valuere = nil
|
37
|
+
|
38
|
+
@argtype = nil
|
39
|
+
|
40
|
+
@res = args[:res]
|
41
|
+
@res = [ @res ] if @res.kind_of?(Regexp)
|
42
|
+
|
43
|
+
if args[:arg]
|
44
|
+
# log { "args.class: #{args[:arg].class}" }
|
45
|
+
demargs = args[:arg].dup
|
46
|
+
while arg = demargs.shift
|
47
|
+
# log { "arg: #{arg}" }
|
48
|
+
case arg
|
49
|
+
when :required
|
50
|
+
@type = "required"
|
51
|
+
when :optional
|
52
|
+
@type = "optional"
|
53
|
+
when :none
|
54
|
+
@type = nil
|
55
|
+
when :regexp
|
56
|
+
@valuere = demargs.shift
|
57
|
+
else
|
58
|
+
if re = ARG_TYPES.assoc(arg)
|
59
|
+
# log { "re: #{re}" }
|
60
|
+
@valuere = re[1]
|
61
|
+
@argtype = arg
|
62
|
+
@type ||= "required"
|
63
|
+
else
|
64
|
+
# log { "no expression for arg #{arg}" }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# log { "valuere: #{@valuere}" }
|
71
|
+
# log { "type: #{@type}" }
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect
|
75
|
+
'[' + @tags.collect { |t| t.inspect }.join(" ") + ']'
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_str
|
79
|
+
to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_s
|
83
|
+
@tags.join(" ")
|
84
|
+
end
|
85
|
+
|
86
|
+
def match_rc?(field)
|
87
|
+
@rc && @rc.include?(field)
|
88
|
+
end
|
89
|
+
|
90
|
+
def match_value(val)
|
91
|
+
# log { "valuere: #{@valuere.inspect}; val: #{val}" }
|
92
|
+
@md = @valuere && @valuere.match(val)
|
93
|
+
# log { "md: #{@md.inspect}" }
|
94
|
+
@md && @md[1]
|
95
|
+
end
|
96
|
+
|
97
|
+
def match_tag(tag)
|
98
|
+
stack { "@rc: #{@rc.inspect}; @tags: #{@tags.inspect}" }
|
99
|
+
|
100
|
+
if tm = @tags.detect do |t|
|
101
|
+
log { "t: #{t}; tag: #{tag}; idx: #{t.index(tag)}" }
|
102
|
+
t.index(tag) == 0 && tag.length <= t.length
|
103
|
+
end
|
104
|
+
|
105
|
+
log { "tm: #{tm}" }
|
106
|
+
if tag.length == tm.length
|
107
|
+
1.0
|
108
|
+
else
|
109
|
+
len = tag.length.to_f * 0.01 # / tm.length
|
110
|
+
log { "len: #{len}" }
|
111
|
+
len
|
112
|
+
end
|
113
|
+
else
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def match(args, opt = args[0])
|
119
|
+
return nil unless %r{^-}.match(opt)
|
120
|
+
|
121
|
+
# log { "opt: #{opt.inspect}; args: #{args.inspect}" }
|
122
|
+
# log { "@rc: #{@rc.inspect}; @re: #{@re.inspect}; @tags: #{@tags.inspect}" }
|
123
|
+
|
124
|
+
tag, val = opt.split('=', 2)
|
125
|
+
tag ||= opt
|
126
|
+
|
127
|
+
# log { "opt: #{opt}; opt: #{opt.class}; tag: #{tag}; tags: #{@tags.inspect}" }
|
128
|
+
# log { "res: #{@res.inspect}" }
|
129
|
+
|
130
|
+
@md = nil
|
131
|
+
|
132
|
+
if @res && (@md = @res.collect { |re| re.match(opt) }.detect)
|
133
|
+
# log { "matched: #{@md}" }
|
134
|
+
1.0
|
135
|
+
else
|
136
|
+
match_tag(tag)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_value(args, opt = args[0])
|
141
|
+
tag, val = opt.split('=', 2)
|
142
|
+
args.shift
|
143
|
+
|
144
|
+
# log { "opt : #{opt}" }
|
145
|
+
# log { "tag : #{tag}" }
|
146
|
+
# log { "tags: #{@tags.inspect}" }
|
147
|
+
# log { "val : #{val.inspect}" }
|
148
|
+
# log { "md : #{@md.inspect}" }
|
149
|
+
|
150
|
+
if @md
|
151
|
+
# log { "already have match data" }
|
152
|
+
elsif @type == "required"
|
153
|
+
if val
|
154
|
+
# already have value
|
155
|
+
# log { "already have value: #{val}" }
|
156
|
+
elsif args.size > 0
|
157
|
+
val = args.shift
|
158
|
+
# log { "got next value: #{val}" }
|
159
|
+
else
|
160
|
+
$stderr.puts "value expected"
|
161
|
+
end
|
162
|
+
|
163
|
+
if val
|
164
|
+
match_value(val)
|
165
|
+
end
|
166
|
+
elsif @type == "optional"
|
167
|
+
if val
|
168
|
+
# log { "already have value: #{val}" }
|
169
|
+
match_value(val)
|
170
|
+
elsif args.size > 0
|
171
|
+
if %r{^-}.match(args[0])
|
172
|
+
# log { "skipping next value; apparently option" }
|
173
|
+
elsif match_value(args[0])
|
174
|
+
# log { "value matches: #{val}" }
|
175
|
+
args.shift
|
176
|
+
else
|
177
|
+
# log { "value does not match" }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
else
|
181
|
+
# log { "no type" }
|
182
|
+
end
|
183
|
+
|
184
|
+
value = value_from_match
|
185
|
+
|
186
|
+
set(value, opt, args)
|
187
|
+
end
|
188
|
+
|
189
|
+
def value_from_match
|
190
|
+
if @md
|
191
|
+
if @argtype.nil? || @argtype == :regexp
|
192
|
+
@md
|
193
|
+
else
|
194
|
+
convert_value(@md[1])
|
195
|
+
end
|
196
|
+
elsif @argtype == :boolean
|
197
|
+
true
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def convert_value(val)
|
202
|
+
if val
|
203
|
+
case @argtype
|
204
|
+
when :string
|
205
|
+
val
|
206
|
+
when :integer
|
207
|
+
val.to_i
|
208
|
+
when :float
|
209
|
+
val.to_f
|
210
|
+
when :boolean
|
211
|
+
to_boolean(val)
|
212
|
+
when :regexp
|
213
|
+
val
|
214
|
+
when nil
|
215
|
+
val
|
216
|
+
else
|
217
|
+
log { "unknown argument type: #{@type.inspect}" }
|
218
|
+
end
|
219
|
+
elsif @argtype == :boolean
|
220
|
+
true
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def to_boolean(val)
|
225
|
+
%w{ yes true on soitenly }.include?(val.downcase)
|
226
|
+
end
|
227
|
+
|
228
|
+
def set(val, opt = nil, args = nil)
|
229
|
+
# log { "argtype: #{@argtype}; md: #{@md.inspect}" }
|
230
|
+
|
231
|
+
setargs = [ val, opt, args ].select_with_index { |x, i| i < @set.arity }
|
232
|
+
# log "val: #{val}"
|
233
|
+
@set.call(*setargs)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
class OptionSet
|
239
|
+
include Loggable
|
240
|
+
|
241
|
+
attr_reader :options
|
242
|
+
|
243
|
+
def initialize(data)
|
244
|
+
@options = Array.new
|
245
|
+
@shortopts = Array.new
|
246
|
+
@longopts = Array.new
|
247
|
+
@regexps = Hash.new
|
248
|
+
|
249
|
+
data.each do |optdata|
|
250
|
+
opt = OptProc::Option.new(optdata)
|
251
|
+
@options << opt
|
252
|
+
|
253
|
+
opt.tags.each do |tag|
|
254
|
+
ch = tag[0]
|
255
|
+
if ch == 45 # 45 = '-'
|
256
|
+
ch = tag[1]
|
257
|
+
assocopts = nil
|
258
|
+
if ch == tag
|
259
|
+
ch = tag[2]
|
260
|
+
assocopts = @longopts
|
261
|
+
else
|
262
|
+
assocopts = @shortopts
|
263
|
+
end
|
264
|
+
|
265
|
+
(assocopts[ch] ||= Array.new) << opt
|
266
|
+
end
|
267
|
+
|
268
|
+
if res = opt.res
|
269
|
+
res.each do |re|
|
270
|
+
(@regexps[re] ||= Array.new) << opt
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
if false
|
277
|
+
[ @longopts, @shortopts ].each do |list|
|
278
|
+
list.each_with_index do |v, idx|
|
279
|
+
log { "#{idx} => #{v.inspect}" }
|
280
|
+
end
|
281
|
+
end
|
282
|
+
[ @regexps ].each do |map|
|
283
|
+
map.each do |k, v|
|
284
|
+
log { "#{k} => #{v.inspect}" }
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
COMBINED_OPTS_RES = [
|
292
|
+
# -number non-num, then anything
|
293
|
+
Regexp.new('^ ( - \d+ ) ( \D+.* ) $ ', Regexp::EXTENDED),
|
294
|
+
# -letter anything
|
295
|
+
Regexp.new('^ ( - [a-z] ) ( .+ ) $ ', Regexp::EXTENDED)
|
296
|
+
]
|
297
|
+
|
298
|
+
def process_option(args)
|
299
|
+
opt = args[0]
|
300
|
+
|
301
|
+
# log { "processing option #{opt}" }
|
302
|
+
|
303
|
+
if md = COMBINED_OPTS_RES.collect { |re| re.match(opt) }.detect
|
304
|
+
lhs = md[1]
|
305
|
+
rhs = "-" + md[2]
|
306
|
+
|
307
|
+
# log { "lhs, rhs: #{lhs.inspect}, #{rhs.inspect}" }
|
308
|
+
|
309
|
+
args[0, 1] = lhs, rhs
|
310
|
+
|
311
|
+
return process_option(args)
|
312
|
+
elsif opt[0] == 45
|
313
|
+
ch = opt[1]
|
314
|
+
assocopts = if ch == 45 # 45 = '-'
|
315
|
+
ch = opt[2]
|
316
|
+
@longopts[ch]
|
317
|
+
elsif ch.nil?
|
318
|
+
nil
|
319
|
+
else
|
320
|
+
@shortopts[ch]
|
321
|
+
end
|
322
|
+
|
323
|
+
# log { "opts: #{assocopts.inspect}" }
|
324
|
+
if assocopts && x = set_option(assocopts, args)
|
325
|
+
return x
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
if x = set_option(@options, args)
|
330
|
+
return x
|
331
|
+
elsif @bestmatch
|
332
|
+
# what's the best match here ...?
|
333
|
+
log { "bestmatch: #{@bestmatch}" }
|
334
|
+
log { "bestopts : #{@bestopts.inspect}" }
|
335
|
+
if @bestopts.size == 1
|
336
|
+
@bestopts[0].set_value(args)
|
337
|
+
return @bestopts[0]
|
338
|
+
else
|
339
|
+
optstr = @bestopts.collect { |x| '(' + x.tags.join(', ') + ')' }.join(', ')
|
340
|
+
$stderr.puts "ERROR: ambiguous match of '#{args[0]}'; matches options: #{optstr}"
|
341
|
+
exit 2
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
nil
|
346
|
+
end
|
347
|
+
|
348
|
+
def set_option(optlist, args)
|
349
|
+
@bestmatch = nil
|
350
|
+
@bestopts = Array.new
|
351
|
+
|
352
|
+
optlist.each do |option|
|
353
|
+
if mv = option.match(args)
|
354
|
+
if mv >= 1.0
|
355
|
+
# exact match:
|
356
|
+
option.set_value(args)
|
357
|
+
return option
|
358
|
+
elsif !@bestmatch || @bestmatch <= mv
|
359
|
+
@bestmatch = mv
|
360
|
+
@bestopts << option
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
nil
|
365
|
+
end
|
366
|
+
|
367
|
+
end
|
368
|
+
|
369
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
require 'riel/string'
|
6
|
+
|
7
|
+
|
8
|
+
class Pathname
|
9
|
+
|
10
|
+
# a compliment to the +dirname+, +basename+, and +extname+ family, this returns
|
11
|
+
# the basename without the extension, e.g. "foo" from "/usr/share/lib/foo.bar".
|
12
|
+
def rootname
|
13
|
+
basename.to_s - extname.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/lib/riel/rcfile.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
|
5
|
+
# Represents a resource file, where '#' is used to comment to end of lines, and
|
6
|
+
# name/value pairs are separated by '=' or ':'.
|
7
|
+
|
8
|
+
class RCFile
|
9
|
+
|
10
|
+
attr_reader :settings
|
11
|
+
|
12
|
+
# Reads the RC file, if it exists, and if a block is passed, calls the block
|
13
|
+
# with each name/value pair, which are also accessible via
|
14
|
+
# <code>settings</code>.
|
15
|
+
|
16
|
+
def initialize(fname, &blk)
|
17
|
+
@settings = Array.new
|
18
|
+
|
19
|
+
if File.exists?(fname)
|
20
|
+
IO.readlines(fname).each do |line|
|
21
|
+
line.sub!(/\s*#.*/, "")
|
22
|
+
line.chomp!
|
23
|
+
name, value = line.split(/\s*[=:]\s*/)
|
24
|
+
if name && value
|
25
|
+
name.strip!
|
26
|
+
value.strip!
|
27
|
+
@settings << [ name, value ]
|
28
|
+
if blk
|
29
|
+
blk.call(name, value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/riel/regexp.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
# Negates the given expression.
|
5
|
+
class NegatedRegexp < Regexp
|
6
|
+
|
7
|
+
def match(str)
|
8
|
+
!super
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class Regexp
|
14
|
+
|
15
|
+
# shell expressions to Ruby regular expression sequences
|
16
|
+
SH2RE = Hash[
|
17
|
+
'*' => '.*',
|
18
|
+
'?' => '.',
|
19
|
+
# '[' => '\[',
|
20
|
+
# ']' => '\]',
|
21
|
+
'.' => '\.',
|
22
|
+
'$' => '\$',
|
23
|
+
'/' => '\/',
|
24
|
+
'(' => '\(',
|
25
|
+
')' => '\)',
|
26
|
+
]
|
27
|
+
|
28
|
+
# Returns a regular expression for the given Unix file system expression.
|
29
|
+
|
30
|
+
def self.unixre_to_string(pat)
|
31
|
+
pat.gsub(%r{(\\.)|(.)}) do
|
32
|
+
$1 || SH2RE[$2] || $2
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
WORD_START_RE = Regexp.new('^ # start of word
|
37
|
+
[\[\(]* # parentheses or captures, maybe
|
38
|
+
(?: \\\w | \\w) # literal \w, or what \w matches
|
39
|
+
',
|
40
|
+
Regexp::EXTENDED)
|
41
|
+
|
42
|
+
WORD_END_RE = Regexp.new('(?: # one of the following:
|
43
|
+
\\\w # - \w for regexp
|
44
|
+
| #
|
45
|
+
\w # - a literal A-Z, a-z, 0-9, or _
|
46
|
+
| #
|
47
|
+
(?: # - one of the following:
|
48
|
+
\[[^\]]* # LB, with no RB until:
|
49
|
+
(?: # - either of:
|
50
|
+
\\w # - "\w"
|
51
|
+
| #
|
52
|
+
\w # - a literal A-Z, a-z, 0-9, or _
|
53
|
+
) #
|
54
|
+
[^\]]*\] # - anything (except RB) to the next RB
|
55
|
+
) #
|
56
|
+
) #
|
57
|
+
(?: # optionally, one of the following:
|
58
|
+
\* # - "*"
|
59
|
+
| #
|
60
|
+
\+ # - "+"
|
61
|
+
| #
|
62
|
+
\? # - "?"
|
63
|
+
| #
|
64
|
+
\{\d*,\d*\} # - "{3,4}", "{,4}, "{,123}" (also matches the invalid {,})
|
65
|
+
)? #
|
66
|
+
$ # fin
|
67
|
+
',
|
68
|
+
Regexp::EXTENDED)
|
69
|
+
|
70
|
+
# Handles negation, whole words, and ignore case (Ruby no longer supports
|
71
|
+
# Rexexp.new(/foo/i), as of 1.8).
|
72
|
+
|
73
|
+
def self.create(pat, args = Hash.new)
|
74
|
+
negated = args[:negated]
|
75
|
+
ignorecase = args[:ignorecase]
|
76
|
+
wholewords = args[:wholewords]
|
77
|
+
wholelines = args[:wholelines]
|
78
|
+
extended = args[:extended]
|
79
|
+
multiline = args[:multiline]
|
80
|
+
|
81
|
+
pattern = pat.dup
|
82
|
+
|
83
|
+
# we handle a ridiculous number of possibilities here:
|
84
|
+
# /foobar/ -- "foobar"
|
85
|
+
# /foo/bar/ -- "foo", then slash, then "bar"
|
86
|
+
# /foo\/bar/ -- same as above
|
87
|
+
# /foo/bar/i -- same as above, case insensitive
|
88
|
+
# /foo/bari -- "/foo/bari" exactly
|
89
|
+
# /foo/bar\/i -- "/foo/bar/i" exactly
|
90
|
+
# foo/bar/ -- "foo/bar/" exactly
|
91
|
+
# foo/bar/ -- "foo/bar/" exactly
|
92
|
+
|
93
|
+
if pattern.sub!(%r{ ^ !(?=/) }x, "")
|
94
|
+
negated = true
|
95
|
+
end
|
96
|
+
|
97
|
+
if pattern.sub!(%r{ ^ \/ (.*[^\\]) \/ ([mix]+) $ }x) { $1 }
|
98
|
+
modifiers = $2
|
99
|
+
|
100
|
+
multiline ||= modifiers.index('m')
|
101
|
+
ignorecase ||= modifiers.index('i')
|
102
|
+
extended ||= modifiers.index('x')
|
103
|
+
else
|
104
|
+
pattern.sub!(%r{ ^\/ (.*[^\\]) \/ $ }x) { $1 }
|
105
|
+
end
|
106
|
+
|
107
|
+
if wholewords
|
108
|
+
# sanity check:
|
109
|
+
|
110
|
+
errs = [
|
111
|
+
[ WORD_START_RE, "start" ],
|
112
|
+
[ WORD_END_RE, "end" ]
|
113
|
+
].collect do |ary|
|
114
|
+
re, err = *ary
|
115
|
+
re.match(pattern) ? nil : err
|
116
|
+
end.compact
|
117
|
+
|
118
|
+
if errs.length > 0
|
119
|
+
Log.warn "pattern '#{pattern}' does not " + errs.join(" and ") + " on a word boundary"
|
120
|
+
end
|
121
|
+
pattern = '\b' + pattern + '\b'
|
122
|
+
elsif wholelines
|
123
|
+
pattern = '^' + pattern + '$' # ' for emacs
|
124
|
+
end
|
125
|
+
|
126
|
+
reclass = negated ? NegatedRegexp : Regexp
|
127
|
+
|
128
|
+
flags = [
|
129
|
+
[ ignorecase, Regexp::IGNORECASE ],
|
130
|
+
[ extended, Regexp::EXTENDED ],
|
131
|
+
[ multiline, Regexp::MULTILINE ]
|
132
|
+
].inject(0) do |tot, ary|
|
133
|
+
val, flag = *ary
|
134
|
+
tot | (val ? flag : 0)
|
135
|
+
end
|
136
|
+
|
137
|
+
reclass.new(pattern, flags)
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.matches_word_start?(pat)
|
141
|
+
WORD_START_RE.match(pat)
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.matches_word_end?(pat)
|
145
|
+
WORD_END_RE.match(pat)
|
146
|
+
end
|
147
|
+
|
148
|
+
# applies Perl-style substitution (s/foo/bar/).
|
149
|
+
def self.perl_subst(pat)
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
data/lib/riel/setdiff.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
# Compares two enumerables, treating them as sets, showing whether they are
|
5
|
+
# identical, A contains B, B contains A, or A and B contain common elements.
|
6
|
+
|
7
|
+
class SetDiff
|
8
|
+
def SetDiff.new(a, b)
|
9
|
+
allitems = a | b
|
10
|
+
|
11
|
+
a_and_b = Array.new
|
12
|
+
a_not_in_b = Array.new
|
13
|
+
b_not_in_a = Array.new
|
14
|
+
|
15
|
+
allitems.each do |it|
|
16
|
+
if a.include?(it)
|
17
|
+
if b.include?(it)
|
18
|
+
a_and_b
|
19
|
+
else
|
20
|
+
a_not_in_b
|
21
|
+
end
|
22
|
+
else
|
23
|
+
b_not_in_a
|
24
|
+
end << it
|
25
|
+
end
|
26
|
+
|
27
|
+
super(a_and_b, a_not_in_b, b_not_in_a)
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :a_and_b, :a_not_in_b, :b_not_in_a
|
31
|
+
|
32
|
+
def initialize(a_and_b, a_not_in_b, b_not_in_a)
|
33
|
+
@a_and_b = a_and_b
|
34
|
+
@a_not_in_b = a_not_in_b
|
35
|
+
@b_not_in_a = b_not_in_a
|
36
|
+
end
|
37
|
+
|
38
|
+
def diff_type
|
39
|
+
@diff_type ||= if @a_and_b.empty?
|
40
|
+
:no_common
|
41
|
+
elsif @a_not_in_b.empty?
|
42
|
+
if @b_not_in_a.empty?
|
43
|
+
:identical
|
44
|
+
else
|
45
|
+
:b_contains_a
|
46
|
+
end
|
47
|
+
elsif @b_not_in_a.empty?
|
48
|
+
:a_contains_b
|
49
|
+
else
|
50
|
+
:common
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
class SizeConverter
|
5
|
+
# http://www.gnu.org/software/coreutils/manual/html_node/Block-size.html
|
6
|
+
|
7
|
+
# don't round to closest -- just convert
|
8
|
+
def self.convert_to_kilobytes(size, decimal_places = 1)
|
9
|
+
### SizeConverter._convert(Human::CONVERSIONS, 2, size, decimal_places)
|
10
|
+
end
|
11
|
+
|
12
|
+
class Human
|
13
|
+
CONVERSIONS = [
|
14
|
+
[ 12, "T" ],
|
15
|
+
[ 9, "G" ],
|
16
|
+
[ 6, "M" ],
|
17
|
+
[ 3, "K" ]
|
18
|
+
]
|
19
|
+
|
20
|
+
# returns a string representation of the size. Note that K, G, M are
|
21
|
+
# gibibytes, etc., that is, powers of 10.
|
22
|
+
|
23
|
+
def self.convert(size, decimal_places = 1)
|
24
|
+
SizeConverter._convert(CONVERSIONS, 10, size, decimal_places)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class SI
|
29
|
+
# http://physics.nist.gov/cuu/Units/binary.html
|
30
|
+
CONVERSIONS = [
|
31
|
+
[ 40, "TiB" ],
|
32
|
+
[ 30, "GiB" ],
|
33
|
+
[ 20, "MiB" ],
|
34
|
+
[ 10, "KiB" ]
|
35
|
+
]
|
36
|
+
|
37
|
+
# returns a string representation of the size. Note that K, G, M are
|
38
|
+
# gigabytes, etc.
|
39
|
+
|
40
|
+
def self.convert(size, decimal_places = 1)
|
41
|
+
SizeConverter._convert(CONVERSIONS, 2, size, decimal_places)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# legacy:
|
46
|
+
|
47
|
+
def self.convert(size, decimal_places = 1)
|
48
|
+
Human::convert(size, decimal_places)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self._convert(conversions, base, size, decimal_places)
|
52
|
+
sizef = size.to_f
|
53
|
+
conversions.each do |conv|
|
54
|
+
sz = sizef / (base ** conv[0])
|
55
|
+
if sz >= 1.0
|
56
|
+
return sprintf("%.*f%s", decimal_places, sz, conv[1])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
sprintf("%.*f", decimal_places, size)
|
61
|
+
end
|
62
|
+
end
|