main 0.0.2 → 2.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 +136 -25
- data/README.tmpl +126 -9
- data/TODO +2 -0
- data/a.rb +26 -0
- data/gemspec.rb +1 -1
- data/gen_readme.rb +4 -0
- data/lib/arrayfields.rb +434 -0
- data/lib/main.rb +46 -17
- data/lib/main/arrayfields.rb +190 -103
- data/lib/main/attributes.rb +84 -25
- data/lib/main/base.rb +146 -23
- data/lib/main/factories.rb +12 -8
- data/lib/main/mode.rb +42 -0
- data/lib/main/parameter.rb +77 -38
- data/lib/main/pervasives.rb +52 -0
- data/lib/main/softspoken.rb +12 -0
- data/lib/main/stdext.rb +18 -0
- data/lib/main/usage.rb +21 -3
- data/samples/e.rb +18 -0
- data/samples/f.rb +27 -0
- data/test/main.rb +156 -30
- metadata +25 -17
- data/lib/main/proxy.rb +0 -54
data/gen_readme.rb
CHANGED
@@ -12,7 +12,11 @@ template = IO::read 'README.tmpl'
|
|
12
12
|
samples = ''
|
13
13
|
prompt = '~ > '
|
14
14
|
|
15
|
+
skip = %w[ e.rb f.rb ]
|
16
|
+
|
15
17
|
Dir['sample*/*'].sort.each do |sample|
|
18
|
+
next if skip.include? File.basename(sample)
|
19
|
+
|
16
20
|
samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
|
17
21
|
|
18
22
|
cmd = "cat #{ sample }"
|
data/lib/arrayfields.rb
ADDED
@@ -0,0 +1,434 @@
|
|
1
|
+
#
|
2
|
+
# The ArrayFields module implements methods which allow an Array to be indexed
|
3
|
+
# by String or Symbol. It is not required to manually use this module to
|
4
|
+
# extend Arrays - they are auto-extended on a per-object basis when
|
5
|
+
# Array#fields= is called
|
6
|
+
#
|
7
|
+
module ArrayFields
|
8
|
+
self::VERSION = '4.5.0' unless defined? self::VERSION
|
9
|
+
def self.version() VERSION end
|
10
|
+
#
|
11
|
+
# multiton cache of fields - wraps fields and fieldpos map to save memory
|
12
|
+
#
|
13
|
+
class FieldSet
|
14
|
+
class << self
|
15
|
+
def new fields
|
16
|
+
@sets[fields] ||= super
|
17
|
+
end
|
18
|
+
def init_sets
|
19
|
+
@sets = {}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
init_sets
|
24
|
+
|
25
|
+
attr :fields
|
26
|
+
attr :fieldpos
|
27
|
+
def initialize fields
|
28
|
+
raise ArgumentError, "<#{ fields.inspect }> not inject-able" unless
|
29
|
+
fields.respond_to? :inject
|
30
|
+
|
31
|
+
@fieldpos =
|
32
|
+
fields.inject({}) do |h, f|
|
33
|
+
unless String === f or Symbol === f
|
34
|
+
raise ArgumentError, "<#{ f.inspect }> neither String nor Symbol"
|
35
|
+
end
|
36
|
+
h[f] = h.size
|
37
|
+
h
|
38
|
+
end
|
39
|
+
|
40
|
+
@fields = fields
|
41
|
+
end
|
42
|
+
def pos f
|
43
|
+
return @fieldpos[f] if @fieldpos.has_key? f
|
44
|
+
f = f.to_s
|
45
|
+
return @fieldpos[f] if @fieldpos.has_key? f
|
46
|
+
f = f.intern
|
47
|
+
return @fieldpos[f] if @fieldpos.has_key? f
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
#
|
52
|
+
# methods redefined to work with fields as well as numeric indexes
|
53
|
+
#
|
54
|
+
def [] idx, *args
|
55
|
+
if @fieldset and (String === idx or Symbol === idx)
|
56
|
+
pos = @fieldset.pos idx
|
57
|
+
return nil unless pos
|
58
|
+
super(pos, *args)
|
59
|
+
else
|
60
|
+
super
|
61
|
+
end
|
62
|
+
end
|
63
|
+
def slice idx, *args
|
64
|
+
if @fieldset and (String === idx or Symbol === idx)
|
65
|
+
pos = @fieldset.pos idx
|
66
|
+
return nil unless pos
|
67
|
+
super(pos, *args)
|
68
|
+
else
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def []=(idx, *args)
|
74
|
+
if @fieldset and (String === idx or Symbol === idx)
|
75
|
+
pos = @fieldset.pos idx
|
76
|
+
unless pos
|
77
|
+
@fieldset.fields << idx
|
78
|
+
@fieldset.fieldpos[idx] = pos = size
|
79
|
+
end
|
80
|
+
super(pos, *args)
|
81
|
+
else
|
82
|
+
super
|
83
|
+
end
|
84
|
+
end
|
85
|
+
def at idx
|
86
|
+
if @fieldset and (String === idx or Symbol === idx)
|
87
|
+
pos = @fieldset.pos idx
|
88
|
+
return nil unless pos
|
89
|
+
super pos
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
def delete_at idx
|
95
|
+
if @fieldset and (String === idx or Symbol === idx)
|
96
|
+
pos = @fieldset.pos idx
|
97
|
+
return nil unless pos
|
98
|
+
super pos
|
99
|
+
else
|
100
|
+
super
|
101
|
+
end
|
102
|
+
end
|
103
|
+
def fill(obj, *args)
|
104
|
+
idx = args.first
|
105
|
+
if idx and @fieldset and (String === idx or Symbol === idx)
|
106
|
+
idx = args.shift
|
107
|
+
pos = @fieldset.pos idx
|
108
|
+
super(obj, pos, *args)
|
109
|
+
else
|
110
|
+
super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def values_at(*idxs)
|
115
|
+
idxs.flatten!
|
116
|
+
if @fieldset
|
117
|
+
idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i}
|
118
|
+
end
|
119
|
+
super(*idxs)
|
120
|
+
end
|
121
|
+
def indices(*idxs)
|
122
|
+
idxs.flatten!
|
123
|
+
if @fieldset
|
124
|
+
idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i}
|
125
|
+
end
|
126
|
+
super(*idxs)
|
127
|
+
end
|
128
|
+
def indexes(*idxs)
|
129
|
+
idxs.flatten!
|
130
|
+
if @fieldset
|
131
|
+
idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i}
|
132
|
+
end
|
133
|
+
super(*idxs)
|
134
|
+
end
|
135
|
+
|
136
|
+
def slice!(*args)
|
137
|
+
ret = self[*args]
|
138
|
+
self[*args] = nil
|
139
|
+
ret
|
140
|
+
end
|
141
|
+
def each_with_field
|
142
|
+
each_with_index do |elem, i|
|
143
|
+
yield elem, @fieldset.fields[i]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
#
|
147
|
+
# methods which give a hash-like interface
|
148
|
+
#
|
149
|
+
def each_pair
|
150
|
+
each_with_index do |elem, i|
|
151
|
+
yield @fieldset.fields[i], elem
|
152
|
+
end
|
153
|
+
end
|
154
|
+
def each_key
|
155
|
+
@fieldset.each{|field| yield field}
|
156
|
+
end
|
157
|
+
def each_value *args, &block
|
158
|
+
each *args, &block
|
159
|
+
end
|
160
|
+
def fetch key
|
161
|
+
self[key] or raise IndexError, 'key not found'
|
162
|
+
end
|
163
|
+
|
164
|
+
def has_key? key
|
165
|
+
@fieldset.fields.include? key
|
166
|
+
end
|
167
|
+
def member? key
|
168
|
+
@fieldset.fields.include? key
|
169
|
+
end
|
170
|
+
def key? key
|
171
|
+
@fieldset.fields.include? key
|
172
|
+
end
|
173
|
+
|
174
|
+
def has_value? value
|
175
|
+
if respond_to? 'include?'
|
176
|
+
self.include? value
|
177
|
+
else
|
178
|
+
a = []
|
179
|
+
each{|val| a << val}
|
180
|
+
a.include? value
|
181
|
+
end
|
182
|
+
end
|
183
|
+
def value? value
|
184
|
+
if respond_to? 'include?'
|
185
|
+
self.include? value
|
186
|
+
else
|
187
|
+
a = []
|
188
|
+
each{|val| a << val}
|
189
|
+
a.include? value
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def keys
|
194
|
+
fields
|
195
|
+
end
|
196
|
+
def store key, value
|
197
|
+
self[key] = value
|
198
|
+
end
|
199
|
+
def values
|
200
|
+
if respond_to? 'to_ary'
|
201
|
+
self.to_ary
|
202
|
+
else
|
203
|
+
a = []
|
204
|
+
each{|val| a << val}
|
205
|
+
a
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def to_hash
|
210
|
+
if respond_to? 'to_ary'
|
211
|
+
h = {}
|
212
|
+
@fieldset.fields.zip(to_ary){|f,e| h[f] = e}
|
213
|
+
h
|
214
|
+
else
|
215
|
+
a = []
|
216
|
+
each{|val| a << val}
|
217
|
+
h = {}
|
218
|
+
@fieldset.fields.zip(a){|f,e| h[f] = e}
|
219
|
+
h
|
220
|
+
end
|
221
|
+
end
|
222
|
+
def to_h
|
223
|
+
if respond_to? 'to_ary'
|
224
|
+
h = {}
|
225
|
+
@fieldset.fields.zip(to_ary){|f,e| h[f] = e}
|
226
|
+
h
|
227
|
+
else
|
228
|
+
a = []
|
229
|
+
each{|val| a << val}
|
230
|
+
h = {}
|
231
|
+
@fieldset.fields.zip(a){|f,e| h[f] = e}
|
232
|
+
h
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def update other
|
237
|
+
other.each{|k,v| self[k] = v}
|
238
|
+
to_hash
|
239
|
+
end
|
240
|
+
def replace other
|
241
|
+
Hash === other ? update(other) : super
|
242
|
+
end
|
243
|
+
def invert
|
244
|
+
to_hash.invert
|
245
|
+
end
|
246
|
+
|
247
|
+
def to_pairs
|
248
|
+
fields.zip values
|
249
|
+
end
|
250
|
+
alias_method 'pairs', 'to_pairs'
|
251
|
+
|
252
|
+
def copy
|
253
|
+
cp = clone
|
254
|
+
cp.fields = fields.clone
|
255
|
+
cp
|
256
|
+
end
|
257
|
+
|
258
|
+
alias_method 'dup', 'copy'
|
259
|
+
alias_method 'clone', 'copy'
|
260
|
+
|
261
|
+
def deepcopy
|
262
|
+
cp = Marshal.load(Marshal.dump(self))
|
263
|
+
cp.fields = Marshal.load(Marshal.dump(self.fields))
|
264
|
+
cp
|
265
|
+
end
|
266
|
+
end
|
267
|
+
Arrayfields = ArrayFields
|
268
|
+
|
269
|
+
module Arrayfields
|
270
|
+
def self.new *pairs
|
271
|
+
pairs = pairs.map{|pair| Enumerable === pair ? pair.to_a : pair}.flatten
|
272
|
+
raise ArgumentError, "pairs must be evenly sized" unless(pairs.size % 2 == 0)
|
273
|
+
(( array = [] )).fields = []
|
274
|
+
0.step(pairs.size - 2, 2) do |a|
|
275
|
+
b = a + 1
|
276
|
+
array[ pairs[a] ] = pairs[b]
|
277
|
+
end
|
278
|
+
array
|
279
|
+
end
|
280
|
+
def self.[] *pairs
|
281
|
+
new *pairs
|
282
|
+
end
|
283
|
+
end
|
284
|
+
def Arrayfields(*a, &b) Arrayfields.new(*a, &b) end
|
285
|
+
#
|
286
|
+
# Fieldable encapsulates methods in common for classes which may have their
|
287
|
+
# fields set and subsequently be auto-extended by ArrayFields
|
288
|
+
#
|
289
|
+
module Fieldable
|
290
|
+
#
|
291
|
+
# sets fields an dynamically extends this Array instance with methods for
|
292
|
+
# keyword access
|
293
|
+
#
|
294
|
+
def fields= fields
|
295
|
+
extend ArrayFields unless ArrayFields === self
|
296
|
+
|
297
|
+
@fieldset =
|
298
|
+
if ArrayFields::FieldSet === fields
|
299
|
+
fields
|
300
|
+
else
|
301
|
+
ArrayFields::FieldSet.new fields
|
302
|
+
end
|
303
|
+
end
|
304
|
+
#
|
305
|
+
# access to fieldset
|
306
|
+
#
|
307
|
+
attr_reader :fieldset
|
308
|
+
#
|
309
|
+
# access to field list
|
310
|
+
#
|
311
|
+
def fields
|
312
|
+
@fieldset and @fieldset.fields
|
313
|
+
end
|
314
|
+
end
|
315
|
+
#
|
316
|
+
# Array instances are extened with two methods only: Fieldable#fields= and
|
317
|
+
# Fieldable#fields. only when Fieldable#fields= is called will the full set
|
318
|
+
# of ArrayFields methods auto-extend the Array instance. the Array class also
|
319
|
+
# has added a class generator when the fields are known apriori.
|
320
|
+
#
|
321
|
+
class Array
|
322
|
+
include Fieldable
|
323
|
+
|
324
|
+
class << self
|
325
|
+
def struct *fields
|
326
|
+
fields = fields.flatten
|
327
|
+
Class.new(self) do
|
328
|
+
include ArrayFields
|
329
|
+
const_set :FIELDS, ArrayFields::FieldSet.new(fields)
|
330
|
+
fields.each do |field|
|
331
|
+
field = field.to_s
|
332
|
+
if field =~ %r/^[a-zA-Z_][a-zA-Z0-9_]*$/
|
333
|
+
begin
|
334
|
+
module_eval <<-code
|
335
|
+
def #{ field } *a
|
336
|
+
a.size == 0 ? self['#{ field }'] : (self.#{ field } = a.shift)
|
337
|
+
end
|
338
|
+
def #{ field }= value
|
339
|
+
self['#{ field }'] = value
|
340
|
+
end
|
341
|
+
code
|
342
|
+
rescue SyntaxError
|
343
|
+
:by_ignoring_it
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
def initialize *a, &b
|
348
|
+
super
|
349
|
+
ensure
|
350
|
+
@fieldset = self.class.const_get :FIELDS
|
351
|
+
end
|
352
|
+
def self.[] *elements
|
353
|
+
array = new
|
354
|
+
array.replace elements
|
355
|
+
array
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
def fields *fields, &block
|
360
|
+
(( array = new(&block) )).fields = fields.map{|x| Enumerable === x ? x.to_a : x}.flatten
|
361
|
+
array
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
#
|
366
|
+
# proxy class that allows an array to be wrapped in a way that still allows #
|
367
|
+
# keyword access. also facilitate usage of ArrayFields with arraylike objects.
|
368
|
+
# thnx to Sean O'Dell for the suggestion.
|
369
|
+
#
|
370
|
+
# sample usage
|
371
|
+
#
|
372
|
+
# fa = FieldedArray.new %w(zero one two), [0,1,2]
|
373
|
+
# p fa['zero'] #=> 0
|
374
|
+
#
|
375
|
+
#
|
376
|
+
class FieldedArray
|
377
|
+
include Fieldable
|
378
|
+
class << self
|
379
|
+
def [](*pairs)
|
380
|
+
pairs.flatten!
|
381
|
+
raise ArgumentError, "argument must be key/val pairs" unless
|
382
|
+
(pairs.size % 2 == 0)
|
383
|
+
fields, elements = [], []
|
384
|
+
while((f = pairs.shift) and (e = pairs.shift))
|
385
|
+
fields << f and elements << e
|
386
|
+
end
|
387
|
+
new fields, elements
|
388
|
+
end
|
389
|
+
end
|
390
|
+
def initialize fields = [], array = []
|
391
|
+
@a = array
|
392
|
+
self.fields = fields
|
393
|
+
end
|
394
|
+
def method_missing(meth, *args, &block)
|
395
|
+
@a.send(meth, *args, &block)
|
396
|
+
end
|
397
|
+
delegates =
|
398
|
+
%w(
|
399
|
+
to_s
|
400
|
+
to_str
|
401
|
+
inspect
|
402
|
+
)
|
403
|
+
delegates.each do |meth|
|
404
|
+
class_eval "def #{ meth }(*a,&b); @a.#{ meth }(*a,&b);end"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
Fieldedarray = FieldedArray
|
408
|
+
|
409
|
+
class PseudoHash < ::Array
|
410
|
+
class << self
|
411
|
+
def [](*pairs)
|
412
|
+
pairs.flatten!
|
413
|
+
raise ArgumentError, "argument must be key/val pairs" unless
|
414
|
+
(pairs.size % 2 == 0 and pairs.size >= 2)
|
415
|
+
keys, values = [], []
|
416
|
+
while((k = pairs.shift) and (v = pairs.shift))
|
417
|
+
keys << k and values << v
|
418
|
+
end
|
419
|
+
new keys, values
|
420
|
+
end
|
421
|
+
end
|
422
|
+
def initialize keys = [], values = []
|
423
|
+
self.fields = keys
|
424
|
+
self.replace values
|
425
|
+
end
|
426
|
+
def to_yaml opts = {}
|
427
|
+
YAML::quick_emit object_id, opts do |out|
|
428
|
+
out.map taguri, to_yaml_style do |map|
|
429
|
+
each_pair{|f,v| map.add f,v}
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
Pseudohash = PseudoHash
|
data/lib/main.rb
CHANGED
@@ -1,32 +1,61 @@
|
|
1
1
|
module Main
|
2
|
-
|
3
|
-
|
2
|
+
#
|
3
|
+
# top level constants
|
4
|
+
#
|
5
|
+
Main::VERSION = '2.0.0' unless
|
6
|
+
defined? Main::VERSION
|
7
|
+
def self.version() Main::VERSION end
|
4
8
|
|
5
|
-
LIBDIR = File.join(File.dirname(File.expand_path(__FILE__)), self.name.downcase, '')
|
9
|
+
Main::LIBDIR = File.join(File.dirname(File.expand_path(__FILE__)), self.name.downcase, '') unless
|
10
|
+
defined? Main::LIBDIR
|
11
|
+
def self.libdir() Main::LIBDIR end
|
6
12
|
|
7
|
-
EXIT_SUCCESS = 0
|
8
|
-
EXIT_FAILURE = 1
|
9
|
-
EXIT_WARN = 42
|
13
|
+
Main::EXIT_SUCCESS = 0 unless defined? Main::EXIT_SUCCESS
|
14
|
+
Main::EXIT_FAILURE = 1 unless defined? Main::EXIT_FAILURE
|
15
|
+
Main::EXIT_WARN = 42 unless defined? Main::EXIT_WARN
|
10
16
|
#
|
11
17
|
# built-in
|
12
18
|
#
|
13
19
|
require 'logger'
|
14
20
|
require 'enumerator'
|
21
|
+
require 'set'
|
22
|
+
|
15
23
|
#
|
16
|
-
#
|
24
|
+
# we try to use gems to pick up dependancies
|
17
25
|
#
|
18
|
-
|
19
|
-
|
26
|
+
|
27
|
+
=begin
|
28
|
+
begin
|
29
|
+
require 'rubygems'
|
30
|
+
rescue LoadError
|
31
|
+
42
|
32
|
+
end
|
33
|
+
begin
|
34
|
+
require 'attributes'
|
35
|
+
rescue LoadError
|
36
|
+
require libdir + 'attributes'
|
37
|
+
end
|
38
|
+
begin
|
39
|
+
require 'arrayfields'
|
40
|
+
rescue LoadError
|
41
|
+
require libdir + 'arrayfields'
|
42
|
+
end
|
43
|
+
=end
|
44
|
+
require libdir + 'attributes'
|
45
|
+
require libdir + 'arrayfields'
|
46
|
+
|
20
47
|
#
|
21
48
|
# main libs
|
22
49
|
#
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
26
|
-
require
|
27
|
-
require
|
28
|
-
require
|
29
|
-
require
|
30
|
-
require
|
50
|
+
require libdir + 'stdext'
|
51
|
+
require libdir + 'softspoken'
|
52
|
+
require libdir + 'util'
|
53
|
+
require libdir + 'usage'
|
54
|
+
require libdir + 'cast'
|
55
|
+
require libdir + 'parameter'
|
56
|
+
require libdir + 'getoptlong'
|
57
|
+
require libdir + 'mode'
|
58
|
+
require libdir + 'base'
|
59
|
+
require libdir + 'factories'
|
31
60
|
end
|
32
61
|
|