ahoward-main 2.9.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 +1044 -0
- data/README.erb +784 -0
- data/Rakefile +241 -0
- data/TODO +20 -0
- data/lib/main.rb +60 -0
- data/lib/main/base.rb +515 -0
- data/lib/main/cast.rb +100 -0
- data/lib/main/factories.rb +20 -0
- data/lib/main/getoptlong.rb +470 -0
- data/lib/main/logger.rb +51 -0
- data/lib/main/mode.rb +42 -0
- data/lib/main/parameter.rb +699 -0
- data/lib/main/softspoken.rb +12 -0
- data/lib/main/stdext.rb +38 -0
- data/lib/main/usage.rb +208 -0
- data/lib/main/util.rb +91 -0
- data/main.gemspec +28 -0
- data/samples/a.rb +17 -0
- data/samples/b.rb +17 -0
- data/samples/c.rb +21 -0
- data/samples/d.rb +26 -0
- data/samples/e.rb +18 -0
- data/samples/f.rb +27 -0
- data/samples/g.rb +10 -0
- data/samples/h.rb +36 -0
- data/test/main.rb +861 -0
- metadata +83 -0
data/lib/main/logger.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Main
|
|
2
|
+
### because active_record fubars the hell out of Logger ;-(
|
|
3
|
+
class Logger < ::Logger
|
|
4
|
+
def self.new *a, &b
|
|
5
|
+
super(*a, &b).instance_eval{ @default_formatter = @formatter = Formatter.new; self }
|
|
6
|
+
end
|
|
7
|
+
def format_message(severity, datetime, progname, msg)
|
|
8
|
+
(@formatter || @default_formatter).call(severity, datetime, progname, msg)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def device
|
|
12
|
+
@logdev.instance_eval{ @dev }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def tty?
|
|
16
|
+
device.respond_to?('tty?') and device.tty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def turn which
|
|
20
|
+
@logdev.extend OnOff unless OnOff === @logdev
|
|
21
|
+
@logdev.turn which
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def on
|
|
25
|
+
turn :on
|
|
26
|
+
end
|
|
27
|
+
alias_method "on!", "on"
|
|
28
|
+
def self.on *a, &b
|
|
29
|
+
new(*a, &b).instance_eval{ turn :on; self }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def off
|
|
33
|
+
turn :off
|
|
34
|
+
end
|
|
35
|
+
alias_method "off!", "off"
|
|
36
|
+
def self.off *a, &b
|
|
37
|
+
new(*a, &b).instance_eval{ turn :off; self }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module OnOff
|
|
41
|
+
def turn which
|
|
42
|
+
@turned = which.to_s =~ %r/on/i ? :on : :off
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def write message
|
|
46
|
+
return message.to_s.size if @turned == :off
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/main/mode.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class Mode < ::String
|
|
2
|
+
class Error < ::StandardError; end
|
|
3
|
+
class Duplicate < Error; end
|
|
4
|
+
class Ambiguous < Error
|
|
5
|
+
include Main::Softspoken
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class List < ::Array
|
|
9
|
+
def initialize *a, &b
|
|
10
|
+
super
|
|
11
|
+
ensure
|
|
12
|
+
self.fields = []
|
|
13
|
+
end
|
|
14
|
+
def add klass
|
|
15
|
+
mode_name = Mode.new klass.mode_name
|
|
16
|
+
raise Duplicate, mode_name if has_key? mode_name
|
|
17
|
+
self[mode_name] = klass
|
|
18
|
+
end
|
|
19
|
+
def find_by_mode m, options = {}
|
|
20
|
+
quiet = options['quiet'] || options[:quiet]
|
|
21
|
+
each_pair do |mode, klass|
|
|
22
|
+
return mode if mode == m
|
|
23
|
+
end
|
|
24
|
+
candidates = []
|
|
25
|
+
each_pair do |mode, klass|
|
|
26
|
+
candidates << mode if mode.index(m) == 0
|
|
27
|
+
end
|
|
28
|
+
case candidates.size
|
|
29
|
+
when 0
|
|
30
|
+
nil
|
|
31
|
+
when 1
|
|
32
|
+
candidates.first
|
|
33
|
+
else
|
|
34
|
+
raise Ambiguous, "ambiguous mode: #{ m } = (#{ candidates.sort.join ' or ' })?"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.list *a, &b
|
|
40
|
+
List.new *a, &b
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
module Main
|
|
2
|
+
class Parameter
|
|
3
|
+
class Error < StandardError
|
|
4
|
+
include Softspoken
|
|
5
|
+
fattr 'wrapped'
|
|
6
|
+
end
|
|
7
|
+
class Arity < Error; end
|
|
8
|
+
class NotGiven < Arity; end
|
|
9
|
+
class InValid < Error; end
|
|
10
|
+
class NoneSuch < Error; end
|
|
11
|
+
class AmbigousOption < Error; end
|
|
12
|
+
class NeedlessArgument < Error; end
|
|
13
|
+
class MissingArgument < Error; end
|
|
14
|
+
class InvalidOption < Error; end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def wrapped_error w
|
|
18
|
+
e = Error.new "(#{ w.message } (#{ w.class }))"
|
|
19
|
+
e.wrapped = w
|
|
20
|
+
e.set_backtrace(w.backtrace || [])
|
|
21
|
+
e
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def wrap_errors
|
|
25
|
+
begin
|
|
26
|
+
yield
|
|
27
|
+
rescue => e
|
|
28
|
+
raise wrapped_error(e)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Types = [ Parameter ]
|
|
33
|
+
def inherited other
|
|
34
|
+
Types << other
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def sym
|
|
38
|
+
@sym ||= name.split(%r/::/).last.downcase.to_sym
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def class_for type
|
|
42
|
+
sym = type.to_s.downcase.to_sym
|
|
43
|
+
c = Types.detect{|t| t.sym == sym}
|
|
44
|
+
raise ArgumentError, type.inspect unless c
|
|
45
|
+
c
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create type, *a, &b
|
|
49
|
+
c = class_for type
|
|
50
|
+
obj = c.allocate
|
|
51
|
+
obj.type = c.sym
|
|
52
|
+
obj.instance_eval{ initialize *a, &b }
|
|
53
|
+
obj
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
fattr 'type'
|
|
58
|
+
fattr 'names'
|
|
59
|
+
fattr 'abbreviations'
|
|
60
|
+
fattr 'argument'
|
|
61
|
+
fattr 'given'
|
|
62
|
+
fattr 'cast'
|
|
63
|
+
fattr 'validate'
|
|
64
|
+
fattr 'description'
|
|
65
|
+
fattr 'synopsis'
|
|
66
|
+
fattr('values'){ [] }
|
|
67
|
+
fattr('defaults'){ [] }
|
|
68
|
+
fattr('examples'){ [] }
|
|
69
|
+
|
|
70
|
+
fattr 'arity' => 1
|
|
71
|
+
fattr 'required' => false
|
|
72
|
+
|
|
73
|
+
fattr 'error_handler_before'
|
|
74
|
+
fattr 'error_handler_instead'
|
|
75
|
+
fattr 'error_handler_after'
|
|
76
|
+
|
|
77
|
+
def initialize name, *names, &block
|
|
78
|
+
@names = Cast.list_of_string name, *names
|
|
79
|
+
@names.map! do |name|
|
|
80
|
+
if name =~ %r/^-+/
|
|
81
|
+
name.gsub! %r/^-+/, ''
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if name =~ %r/=.*$/
|
|
85
|
+
argument( name =~ %r/=\s*\[.*$/ ? :optional : :required )
|
|
86
|
+
name.gsub! %r/=.*$/, ''
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
name
|
|
90
|
+
end
|
|
91
|
+
@names = @names.sort_by{|name| name.size}.reverse
|
|
92
|
+
@names[1..-1].each do |name|
|
|
93
|
+
raise ArgumentError, "only one long name allowed (#{ @names.inspect })" if
|
|
94
|
+
name.size > 1
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
DSL.evaluate(self, &block) if block
|
|
98
|
+
sanity_check!
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def sanity_check!
|
|
102
|
+
raise Arity, "#{ name } with arity -1 (zero or more args) cannot be required" if(arity == -1 and required?)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def name
|
|
106
|
+
names.first
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def default
|
|
110
|
+
defaults.first
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def typename
|
|
114
|
+
prefix = '--' if type.to_s =~ %r/option/
|
|
115
|
+
"#{ type }(#{ prefix }#{ name })"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def add_value value
|
|
119
|
+
given true
|
|
120
|
+
values << value
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def value
|
|
124
|
+
values.first
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def argument_none?
|
|
128
|
+
argument.nil?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def argument_required?
|
|
132
|
+
argument and
|
|
133
|
+
argument.to_s.downcase.to_sym == :required
|
|
134
|
+
end
|
|
135
|
+
def argument_optional?
|
|
136
|
+
argument and
|
|
137
|
+
argument.to_s.downcase.to_sym == :optional
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def optional?
|
|
141
|
+
not required?
|
|
142
|
+
end
|
|
143
|
+
def optional= bool
|
|
144
|
+
self.required !bool
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
=begin
|
|
148
|
+
def setup!
|
|
149
|
+
return false unless given?
|
|
150
|
+
adding_handlers do
|
|
151
|
+
check_arity
|
|
152
|
+
apply_casting
|
|
153
|
+
check_validation
|
|
154
|
+
end
|
|
155
|
+
true
|
|
156
|
+
end
|
|
157
|
+
=end
|
|
158
|
+
|
|
159
|
+
def setup!
|
|
160
|
+
adding_handlers do
|
|
161
|
+
check_arity
|
|
162
|
+
apply_casting
|
|
163
|
+
check_validation
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def check_arity
|
|
168
|
+
return true if not given? and optional?
|
|
169
|
+
|
|
170
|
+
ex = values.size == 0 ? NotGiven : Arity
|
|
171
|
+
|
|
172
|
+
(raise ex, "#{ typename })" if values.size.zero? and argument_required?) unless arity == -1
|
|
173
|
+
|
|
174
|
+
if arity >= 0
|
|
175
|
+
min = arity
|
|
176
|
+
sign = ''
|
|
177
|
+
else
|
|
178
|
+
min = arity.abs - 1
|
|
179
|
+
sign = '-'
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
arity = min
|
|
183
|
+
|
|
184
|
+
=begin
|
|
185
|
+
puts
|
|
186
|
+
p :values => values
|
|
187
|
+
p :arity => arity
|
|
188
|
+
p :argument_required => argument_required?
|
|
189
|
+
p :argument_none => argument_none?
|
|
190
|
+
puts
|
|
191
|
+
=end
|
|
192
|
+
if values.size < arity
|
|
193
|
+
if argument_optional?
|
|
194
|
+
raise ex, "#{ typename }) #{ values.size }/#{ sign }#{ arity }" if(values.size < arity and values.size > 0)
|
|
195
|
+
elsif argument_required? or argument_none?
|
|
196
|
+
raise ex, "#{ typename }) #{ values.size }/#{ sign }#{ arity }" if(values.size < arity)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def apply_casting
|
|
202
|
+
if cast?
|
|
203
|
+
op = cast.respond_to?('call') ? cast : Cast[cast]
|
|
204
|
+
values.map! do |val|
|
|
205
|
+
Parameter.wrap_errors{ op.call val }
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def check_validation
|
|
211
|
+
if validate?
|
|
212
|
+
values.each do |value|
|
|
213
|
+
validate[value] or
|
|
214
|
+
raise InValid, "invalid: #{ typename }=#{ value.inspect }"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def add_handlers e
|
|
220
|
+
esc =
|
|
221
|
+
class << e
|
|
222
|
+
self
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
this = self
|
|
226
|
+
|
|
227
|
+
%w[ before instead after ].each do |which|
|
|
228
|
+
getter = "error_handler_#{ which }"
|
|
229
|
+
query = "error_handler_#{ which }?"
|
|
230
|
+
if send(query)
|
|
231
|
+
handler = send getter
|
|
232
|
+
esc.module_eval do
|
|
233
|
+
define_method(getter) do |main|
|
|
234
|
+
main.instance_eval_block self, &handler
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def adding_handlers
|
|
242
|
+
begin
|
|
243
|
+
yield
|
|
244
|
+
rescue Exception => e
|
|
245
|
+
add_handlers e
|
|
246
|
+
raise
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
class Argument < Parameter
|
|
251
|
+
fattr 'required' => true
|
|
252
|
+
|
|
253
|
+
fattr 'synopsis' do
|
|
254
|
+
label = name
|
|
255
|
+
op = required ? "->" : "~>"
|
|
256
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
|
257
|
+
value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
|
|
258
|
+
"#{ label } (#{ arity } #{ op } #{ value })"
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
class Option < Parameter
|
|
263
|
+
fattr 'required' => false
|
|
264
|
+
fattr 'arity' => 0
|
|
265
|
+
|
|
266
|
+
fattr 'synopsis' do
|
|
267
|
+
long, *short = names
|
|
268
|
+
value = cast || name
|
|
269
|
+
rhs = argument ? (argument == :required ? "=#{ name }" : "=[#{ name }]") : nil
|
|
270
|
+
label = ["--#{ long }#{ rhs }", short.map{|s| "-#{ s }"}].flatten.join(", ")
|
|
271
|
+
unless argument_none?
|
|
272
|
+
op = required ? "->" : "~>"
|
|
273
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
|
274
|
+
value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
|
|
275
|
+
"#{ label } (#{ arity } #{ op } #{ value })"
|
|
276
|
+
else
|
|
277
|
+
label
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
class Keyword < Parameter
|
|
283
|
+
fattr 'required' => false
|
|
284
|
+
fattr 'argument' => :required
|
|
285
|
+
|
|
286
|
+
fattr 'synopsis' do
|
|
287
|
+
label = "#{ name }=#{ name }"
|
|
288
|
+
op = required ? "->" : "~>"
|
|
289
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
|
290
|
+
value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
|
|
291
|
+
"#{ label } (#{ arity } #{ op } #{ value })"
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
class Environment < Parameter
|
|
296
|
+
fattr 'argument' => :required
|
|
297
|
+
|
|
298
|
+
fattr 'synopsis' do
|
|
299
|
+
label = "env[#{ name }]=#{ name }"
|
|
300
|
+
op = required ? "->" : "~>"
|
|
301
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
|
302
|
+
value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
|
|
303
|
+
"#{ label } (#{ arity } #{ op } #{ value })"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
class List < ::Array
|
|
308
|
+
fattr :main
|
|
309
|
+
fattr :argv
|
|
310
|
+
fattr :env
|
|
311
|
+
|
|
312
|
+
def parse main
|
|
313
|
+
@main, @argv, @env = main, main.argv, main.env
|
|
314
|
+
|
|
315
|
+
ignore, stop = [], argv.index('--')
|
|
316
|
+
|
|
317
|
+
if stop
|
|
318
|
+
ignore = argv[stop .. -1]
|
|
319
|
+
(argv.size - stop).times{ argv.pop }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
parse_options argv
|
|
323
|
+
|
|
324
|
+
return 'help' if detect{|p| p.name.to_s == 'help' and p.given?}
|
|
325
|
+
|
|
326
|
+
parse_keywords argv
|
|
327
|
+
parse_arguments argv
|
|
328
|
+
parse_environment env
|
|
329
|
+
|
|
330
|
+
defaults!
|
|
331
|
+
validate!
|
|
332
|
+
|
|
333
|
+
argv.push *ignore[1 .. -1] unless ignore.empty?
|
|
334
|
+
|
|
335
|
+
return self
|
|
336
|
+
ensure
|
|
337
|
+
@main, @argv, @env = nil
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def parse_options argv, params = nil
|
|
341
|
+
params ||= options
|
|
342
|
+
|
|
343
|
+
spec, h, s = [], {}, {}
|
|
344
|
+
|
|
345
|
+
params.each do |p|
|
|
346
|
+
head, *tail = p.names
|
|
347
|
+
long = "--#{ head }"
|
|
348
|
+
shorts = tail.map{|t| "-#{ t }"}
|
|
349
|
+
type =
|
|
350
|
+
if p.argument_required? then GetoptLong::REQUIRED_ARGUMENT
|
|
351
|
+
elsif p.argument_optional? then GetoptLong::OPTIONAL_ARGUMENT
|
|
352
|
+
else GetoptLong::NO_ARGUMENT
|
|
353
|
+
end
|
|
354
|
+
a = [ long, shorts, type ].flatten
|
|
355
|
+
spec << a
|
|
356
|
+
h[long] = p
|
|
357
|
+
s[long] = a
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
begin
|
|
361
|
+
GetoptLong.new(argv, *spec).each do |long, value|
|
|
362
|
+
value =
|
|
363
|
+
case s[long].last
|
|
364
|
+
when GetoptLong::NO_ARGUMENT
|
|
365
|
+
value.empty? ? true : value
|
|
366
|
+
when GetoptLong::OPTIONAL_ARGUMENT
|
|
367
|
+
value.empty? ? true : value
|
|
368
|
+
when GetoptLong::REQUIRED_ARGUMENT
|
|
369
|
+
value
|
|
370
|
+
end
|
|
371
|
+
p = h[long]
|
|
372
|
+
p.add_value value
|
|
373
|
+
end
|
|
374
|
+
rescue GetoptLong::AmbigousOption, GetoptLong::NeedlessArgument,
|
|
375
|
+
GetoptLong::MissingArgument, GetoptLong::InvalidOption => e
|
|
376
|
+
c = Parameter.const_get e.class.name.split(/::/).last
|
|
377
|
+
ex = c.new e.message
|
|
378
|
+
ex.set_backtrace e.message
|
|
379
|
+
ex.extend Softspoken
|
|
380
|
+
raise ex
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
=begin
|
|
384
|
+
params.each do |p|
|
|
385
|
+
p.setup!
|
|
386
|
+
end
|
|
387
|
+
=end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def parse_arguments argv, params=nil
|
|
391
|
+
params ||= select{|p| p.type == :argument}
|
|
392
|
+
|
|
393
|
+
params.each do |p|
|
|
394
|
+
if p.arity >= 0
|
|
395
|
+
p.arity.times do
|
|
396
|
+
break if argv.empty?
|
|
397
|
+
value = argv.shift
|
|
398
|
+
p.add_value value
|
|
399
|
+
end
|
|
400
|
+
else
|
|
401
|
+
arity = p.arity.abs - 1
|
|
402
|
+
arity.times do
|
|
403
|
+
break if argv.empty?
|
|
404
|
+
value = argv.shift
|
|
405
|
+
p.add_value value
|
|
406
|
+
end
|
|
407
|
+
argv.size.times do
|
|
408
|
+
value = argv.shift
|
|
409
|
+
p.add_value value
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
=begin
|
|
415
|
+
params.each do |p|
|
|
416
|
+
p.setup!
|
|
417
|
+
end
|
|
418
|
+
=end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def parse_keywords argv, params=nil
|
|
422
|
+
params ||= select{|p| p.type == :keyword}
|
|
423
|
+
|
|
424
|
+
replacements = {}
|
|
425
|
+
|
|
426
|
+
params.each do |p|
|
|
427
|
+
names = p.names
|
|
428
|
+
name = names.sort_by{|n| [n.size,n]}.last
|
|
429
|
+
|
|
430
|
+
kre = %r/^\s*(#{ names.join '|' })\s*=/
|
|
431
|
+
opt = "--#{ name }"
|
|
432
|
+
i = -1
|
|
433
|
+
|
|
434
|
+
argv.each_with_index do |a, idx|
|
|
435
|
+
i += 1
|
|
436
|
+
b = argv[idx + 1]
|
|
437
|
+
s = "#{ a }#{ b }"
|
|
438
|
+
m, key, *ignored = kre.match(s).to_a
|
|
439
|
+
if m
|
|
440
|
+
replacements[i] ||= a.gsub %r/^\s*#{ key }/, opt
|
|
441
|
+
next
|
|
442
|
+
end
|
|
443
|
+
=begin
|
|
444
|
+
abbrev = name.index(key) == 0
|
|
445
|
+
if abbrev
|
|
446
|
+
replacements[i] ||= a.gsub %r/^\s*#{ key }/, opt
|
|
447
|
+
end
|
|
448
|
+
=end
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
replacements.each do |i, r|
|
|
453
|
+
argv[i] = r
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
parse_options argv, params
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def parse_environment env, params=nil
|
|
460
|
+
params ||= select{|p| p.type == :environment}
|
|
461
|
+
|
|
462
|
+
params.each do |p|
|
|
463
|
+
names = p.names
|
|
464
|
+
name = names.first
|
|
465
|
+
value = env[name]
|
|
466
|
+
next unless value
|
|
467
|
+
p.add_value value
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
=begin
|
|
471
|
+
params.each do |p|
|
|
472
|
+
p.setup!
|
|
473
|
+
end
|
|
474
|
+
=end
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def defaults!
|
|
478
|
+
each do |p|
|
|
479
|
+
if(p.defaults? and (not p.given?))
|
|
480
|
+
p.defaults.each do |default|
|
|
481
|
+
p.values << (default.respond_to?('to_proc') ? main.instance_eval(&default) : default) # so as NOT to set 'given?'
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def validate!
|
|
488
|
+
each do |p|
|
|
489
|
+
#p.adding_handlers do
|
|
490
|
+
#next if p.arity == -1
|
|
491
|
+
#raise NotGiven, "#{ p.typename } not given" if(p.required? and (not p.given?))
|
|
492
|
+
#end
|
|
493
|
+
p.setup!
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
[:parameter, :option, :argument, :keyword, :environment].each do |m|
|
|
498
|
+
define_method("#{ m }s"){ select{|p| p.type == m or m == :parameter} }
|
|
499
|
+
|
|
500
|
+
define_method("has_#{ m }?") do |name, *names|
|
|
501
|
+
catch :has do
|
|
502
|
+
names = Cast.list_of_string name, *names
|
|
503
|
+
send("#{ m }s").each do |param|
|
|
504
|
+
common = Cast.list_of_string(param.names) & names
|
|
505
|
+
throw :has, true unless common.empty?
|
|
506
|
+
end
|
|
507
|
+
false
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def delete name, *names
|
|
513
|
+
name, *names = name.names if Parameter === name
|
|
514
|
+
names = Cast.list_of_string name, *names
|
|
515
|
+
keep = []
|
|
516
|
+
each do |param|
|
|
517
|
+
common = Cast.list_of_string(param.names) & names
|
|
518
|
+
keep << param if common.empty?
|
|
519
|
+
end
|
|
520
|
+
replace keep
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def << *a
|
|
524
|
+
delete *a
|
|
525
|
+
super
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
class DSL
|
|
530
|
+
def self.evaluate param, &block
|
|
531
|
+
new(param).instance_eval(&block)
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
attr 'param'
|
|
535
|
+
|
|
536
|
+
def initialize param
|
|
537
|
+
@param = param
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def fattr a = nil, &block
|
|
541
|
+
name = param.name
|
|
542
|
+
a ||= name
|
|
543
|
+
b = fattr_block_for name, &block
|
|
544
|
+
Main.current.module_eval{ fattr *a, &b }
|
|
545
|
+
end
|
|
546
|
+
alias_method 'attribute', 'fattr'
|
|
547
|
+
|
|
548
|
+
def fattr_block_for name, &block
|
|
549
|
+
block ||= lambda{|param| [0,1].include?(param.arity) ? param.value : param.values }
|
|
550
|
+
lambda{ block.call self.param[name] }
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
def attr *a, &b
|
|
554
|
+
fattr *a, &b
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
def example *list
|
|
558
|
+
list.flatten.compact.each do |elem|
|
|
559
|
+
param.examples << elem.to_s
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
alias_method "examples", "example"
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def type *sym
|
|
566
|
+
sym.size == 0 ? param.type : (param.type = sym.first)
|
|
567
|
+
end
|
|
568
|
+
def type?
|
|
569
|
+
param.type?
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def synopsis *arg
|
|
573
|
+
arg.size == 0 ? param.synopsis : (param.synopsis arg.first)
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def argument arg
|
|
577
|
+
param.argument arg
|
|
578
|
+
end
|
|
579
|
+
def argument_required bool = true
|
|
580
|
+
if bool
|
|
581
|
+
param.argument :required
|
|
582
|
+
else
|
|
583
|
+
param.argument false
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
def argument_required?
|
|
587
|
+
param.argument_required?
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def argument_optional bool = true
|
|
591
|
+
if bool
|
|
592
|
+
param.argument :optional
|
|
593
|
+
else
|
|
594
|
+
param.argument false
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
def argument_optional?
|
|
598
|
+
param.argument_optional?
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def required bool = true
|
|
602
|
+
param.required = bool
|
|
603
|
+
end
|
|
604
|
+
def required?
|
|
605
|
+
param.required?
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def optional bool = true
|
|
609
|
+
if bool
|
|
610
|
+
param.required !bool
|
|
611
|
+
else
|
|
612
|
+
param.required bool
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
def optional?
|
|
616
|
+
param.optional?
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def cast sym=nil, &b
|
|
620
|
+
param.cast = sym || b
|
|
621
|
+
end
|
|
622
|
+
def cast?
|
|
623
|
+
param.cast?
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def validate sym=nil, &b
|
|
627
|
+
param.validate = sym || b
|
|
628
|
+
end
|
|
629
|
+
def validate?
|
|
630
|
+
param.validate?
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def description s
|
|
634
|
+
param.description = s.to_s
|
|
635
|
+
end
|
|
636
|
+
def description?
|
|
637
|
+
param.description?
|
|
638
|
+
end
|
|
639
|
+
alias_method 'desc', 'description'
|
|
640
|
+
|
|
641
|
+
def default *values, &block
|
|
642
|
+
if block.nil? and values.empty?
|
|
643
|
+
raise ArgumentError, 'no default'
|
|
644
|
+
end
|
|
645
|
+
unless values.empty?
|
|
646
|
+
param.defaults.push *values
|
|
647
|
+
end
|
|
648
|
+
unless block.nil?
|
|
649
|
+
param.defaults.push block
|
|
650
|
+
end
|
|
651
|
+
param.defaults
|
|
652
|
+
end
|
|
653
|
+
alias_method 'defaults', 'default'
|
|
654
|
+
def defaults?
|
|
655
|
+
param.defaults?
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
def arity value
|
|
659
|
+
raise Arity if value.nil?
|
|
660
|
+
value = -1 if value.to_s == '*'
|
|
661
|
+
value = Integer value
|
|
662
|
+
raise Arity if value.zero?
|
|
663
|
+
param.arity = value
|
|
664
|
+
if param.arity == -1
|
|
665
|
+
optional true
|
|
666
|
+
end
|
|
667
|
+
value
|
|
668
|
+
end
|
|
669
|
+
def arity?
|
|
670
|
+
param.arity?
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
def error which = :instead, &block
|
|
674
|
+
param.send "error_handler_#{ which }=", block
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
class Table < ::Array
|
|
679
|
+
def initialize
|
|
680
|
+
super()
|
|
681
|
+
self.fields = []
|
|
682
|
+
extend BoundsCheck
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
def to_options
|
|
686
|
+
(hash = self.to_hash ).keys.each { |key| hash[key] = hash[key].value }
|
|
687
|
+
return hash
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
module BoundsCheck
|
|
691
|
+
def [] *a, &b
|
|
692
|
+
p = super
|
|
693
|
+
ensure
|
|
694
|
+
raise NoneSuch, a.join(',') unless p
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
end
|