main 2.9.3 → 3.0.1
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 +27 -11
- data/README.erb +5 -3
- data/Rakefile +3 -2
- data/TODO +1 -1
- data/a.rb +5 -0
- data/lib/main.rb +2 -15
- data/lib/main/dsl.rb +77 -0
- data/lib/main/factories.rb +17 -11
- data/lib/main/mode.rb +4 -2
- data/lib/main/parameter.rb +16 -13
- data/lib/main/program.rb +6 -0
- data/lib/main/program/class_methods.rb +260 -0
- data/lib/main/program/instance_methods.rb +253 -0
- data/lib/main/stdext.rb +18 -20
- data/main.gemspec +5 -4
- data/samples/e.rb +15 -6
- data/samples/f.rb +2 -0
- data/test/main.rb +78 -54
- metadata +29 -6
- data/lib/main/base.rb +0 -517
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: main
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ara T. Howard
|
@@ -9,11 +9,30 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-11 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: fattr
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: arrayfields
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.5.0
|
34
|
+
version:
|
35
|
+
description: a class factory and dsl for generating command line programs real quick
|
17
36
|
email: ara.t.howard@gmail.com
|
18
37
|
executables: []
|
19
38
|
|
@@ -22,13 +41,17 @@ extensions: []
|
|
22
41
|
extra_rdoc_files: []
|
23
42
|
|
24
43
|
files:
|
25
|
-
-
|
44
|
+
- a.rb
|
26
45
|
- lib/main/cast.rb
|
46
|
+
- lib/main/dsl.rb
|
27
47
|
- lib/main/factories.rb
|
28
48
|
- lib/main/getoptlong.rb
|
29
49
|
- lib/main/logger.rb
|
30
50
|
- lib/main/mode.rb
|
31
51
|
- lib/main/parameter.rb
|
52
|
+
- lib/main/program/class_methods.rb
|
53
|
+
- lib/main/program/instance_methods.rb
|
54
|
+
- lib/main/program.rb
|
32
55
|
- lib/main/softspoken.rb
|
33
56
|
- lib/main/stdext.rb
|
34
57
|
- lib/main/usage.rb
|
data/lib/main/base.rb
DELETED
@@ -1,517 +0,0 @@
|
|
1
|
-
module Main
|
2
|
-
class Base
|
3
|
-
class << self
|
4
|
-
def wrap_run!
|
5
|
-
const_set :RUN, instance_method(:run)
|
6
|
-
|
7
|
-
class_eval do
|
8
|
-
def run *a, &b
|
9
|
-
argv.push "--#{ argv.shift }" if argv.first == 'help'
|
10
|
-
return mode_run! if mode_given?
|
11
|
-
|
12
|
-
status =
|
13
|
-
catch :exit do
|
14
|
-
begin
|
15
|
-
|
16
|
-
parse_parameters
|
17
|
-
|
18
|
-
if params['help'] and params['help'].given?
|
19
|
-
print usage.to_s
|
20
|
-
exit
|
21
|
-
end
|
22
|
-
|
23
|
-
pre_run
|
24
|
-
before_run
|
25
|
-
self.class.const_get(:RUN).bind(self).call(*a, &b)
|
26
|
-
after_run
|
27
|
-
post_run
|
28
|
-
|
29
|
-
finalize
|
30
|
-
rescue Exception => e
|
31
|
-
handle_exception e
|
32
|
-
end
|
33
|
-
nil
|
34
|
-
end
|
35
|
-
|
36
|
-
handle_throw status
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def method_added m
|
42
|
-
return if @in_method_added
|
43
|
-
super if defined? super
|
44
|
-
@in_method_added = true
|
45
|
-
begin
|
46
|
-
wrap_run! if m.to_s == 'run'
|
47
|
-
ensure
|
48
|
-
@in_method_added = false
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.inheritable_fattr name, &block
|
53
|
-
block ||= lambda{}
|
54
|
-
fattr( name ){
|
55
|
-
catch :value do
|
56
|
-
if parent?
|
57
|
-
value = parent.send name
|
58
|
-
value =
|
59
|
-
begin
|
60
|
-
Util.mcp value
|
61
|
-
rescue
|
62
|
-
value.clone rescue value.dup
|
63
|
-
end
|
64
|
-
throw :value, value
|
65
|
-
end
|
66
|
-
instance_eval &block
|
67
|
-
end
|
68
|
-
}
|
69
|
-
end
|
70
|
-
|
71
|
-
# fattrs
|
72
|
-
fattr( 'name' ){ File.basename $0 }
|
73
|
-
fattr( 'synopsis' ){ Main::Usage.default_synopsis(self) }
|
74
|
-
fattr( 'description' )
|
75
|
-
fattr( 'usage' ){ Main::Usage.default_usage self }
|
76
|
-
fattr( 'modes' ){ Main::Mode.list }
|
77
|
-
fattr( 'mode_definitions' ){ Array.new }
|
78
|
-
fattr( 'mode_name' ){ 'main' }
|
79
|
-
fattr( 'parent' ){ nil }
|
80
|
-
fattr( 'children' ){ Set.new }
|
81
|
-
|
82
|
-
fattr( 'program' ){ File.basename $0 }
|
83
|
-
fattr( 'author' )
|
84
|
-
fattr( 'version' )
|
85
|
-
fattr( 'stdin' ){ $stdin }
|
86
|
-
fattr( 'stdout' ){ $stdout }
|
87
|
-
fattr( 'stderr' ){ $stderr }
|
88
|
-
fattr( 'logger' ){ stderr }
|
89
|
-
fattr( 'logger_level' ){ Logger::INFO }
|
90
|
-
fattr( 'exit_status' ){ Main::EXIT_SUCCESS }
|
91
|
-
fattr( 'exit_success' ){ Main::EXIT_SUCCESS }
|
92
|
-
fattr( 'exit_failure' ){ Main::EXIT_FAILURE }
|
93
|
-
fattr( 'exit_warn' ){ Main::EXIT_WARN }
|
94
|
-
inheritable_fattr( 'parameters' ){ Main::Parameter::List[] }
|
95
|
-
inheritable_fattr( 'can_has_hash' ){ Hash.new }
|
96
|
-
inheritable_fattr( 'mixin_table' ){ Hash.new }
|
97
|
-
|
98
|
-
# override a few fattrs
|
99
|
-
def mode_name=(value)
|
100
|
-
@mode_name = Mode.new value
|
101
|
-
end
|
102
|
-
|
103
|
-
def usage *argv, &block
|
104
|
-
usage! unless defined? @usage
|
105
|
-
return @usage if argv.empty? and block.nil?
|
106
|
-
key, value, *ignored = argv
|
107
|
-
value = block.call if block
|
108
|
-
@usage[key.to_s] = value.to_s
|
109
|
-
end
|
110
|
-
|
111
|
-
def create parent = Base, *a, &b
|
112
|
-
Class.new parent do |child|
|
113
|
-
child.parent = parent unless parent == Base
|
114
|
-
parent.children.add child
|
115
|
-
child.context do
|
116
|
-
child.class_eval &b if b
|
117
|
-
child.default_options!
|
118
|
-
#child.wrap_run! unless child.const_defined?(:RUN)
|
119
|
-
mode_definitions.each do |name, block|
|
120
|
-
klass =
|
121
|
-
create context do
|
122
|
-
mode_name name.to_s
|
123
|
-
module_eval &block if block
|
124
|
-
end
|
125
|
-
modes.add klass
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def context &block
|
132
|
-
@@context ||= []
|
133
|
-
unless block
|
134
|
-
@@context.last
|
135
|
-
else
|
136
|
-
begin
|
137
|
-
@@context.push self
|
138
|
-
block.call @@context.last
|
139
|
-
ensure
|
140
|
-
@@context.pop
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
module ::Main
|
146
|
-
singleton_class{
|
147
|
-
def current
|
148
|
-
::Main::Base.context
|
149
|
-
end
|
150
|
-
}
|
151
|
-
end
|
152
|
-
|
153
|
-
def fully_qualified_mode
|
154
|
-
list = []
|
155
|
-
ancestors.each do |ancestor|
|
156
|
-
break unless ancestor < Base
|
157
|
-
list << ancestor.mode_name
|
158
|
-
end
|
159
|
-
list.reverse[1..-1]
|
160
|
-
end
|
161
|
-
|
162
|
-
def run(&b) define_method(:run, &b) end
|
163
|
-
|
164
|
-
def new(*a, &b)
|
165
|
-
allocate.instance_eval do
|
166
|
-
pre_initialize
|
167
|
-
before_initialize
|
168
|
-
main_initialize *a, &b
|
169
|
-
initialize
|
170
|
-
after_initialize
|
171
|
-
post_initialize
|
172
|
-
self
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
module DSL
|
178
|
-
def parameter *a, &b
|
179
|
-
(parameters << Parameter.create(:parameter, *a, &b)).last
|
180
|
-
end
|
181
|
-
|
182
|
-
def option *a, &b
|
183
|
-
(parameters << Parameter.create(:option, *a, &b)).last
|
184
|
-
end
|
185
|
-
alias_method 'opt', 'option'
|
186
|
-
alias_method 'switch', 'option'
|
187
|
-
|
188
|
-
def default_options!
|
189
|
-
option 'help', 'h' unless parameters.has_option?('help', 'h')
|
190
|
-
end
|
191
|
-
|
192
|
-
def argument *a, &b
|
193
|
-
(parameters << Parameter.create(:argument, *a, &b)).last
|
194
|
-
end
|
195
|
-
alias_method 'arg', 'argument'
|
196
|
-
|
197
|
-
def keyword *a, &b
|
198
|
-
(parameters << Parameter.create(:keyword, *a, &b)).last
|
199
|
-
end
|
200
|
-
alias_method 'kw', 'keyword'
|
201
|
-
|
202
|
-
def environment *a, &b
|
203
|
-
(parameters << Parameter.create(:environment, *a, &b)).last
|
204
|
-
end
|
205
|
-
alias_method 'env', 'environment'
|
206
|
-
|
207
|
-
=begin
|
208
|
-
def mode name, &b
|
209
|
-
klass =
|
210
|
-
create context do
|
211
|
-
mode_name name.to_s
|
212
|
-
module_eval &b if b
|
213
|
-
end
|
214
|
-
modes.add klass
|
215
|
-
end
|
216
|
-
=end
|
217
|
-
|
218
|
-
def mode name, &b
|
219
|
-
mode_definitions << [name, b]
|
220
|
-
end
|
221
|
-
|
222
|
-
def can_has ptype, *a, &b
|
223
|
-
key = a.map{|s| s.to_s}.sort_by{|s| -s.size }.first
|
224
|
-
can_has_hash.update key => [ptype, a, b]
|
225
|
-
key
|
226
|
-
end
|
227
|
-
|
228
|
-
def has key, *keys
|
229
|
-
keys = [key, *keys].flatten.compact.map{|k| k.to_s}
|
230
|
-
keys.map do |key|
|
231
|
-
ptype, a, b = can_has_hash[key]
|
232
|
-
abort "yo - can *not* has #{ key.inspect }!?" unless(ptype and a and b)
|
233
|
-
send ptype, *a, &b
|
234
|
-
key
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
def mixin name, *names, &block
|
239
|
-
names = [name, *names].flatten.compact.map{|name| name.to_s}
|
240
|
-
if block
|
241
|
-
names.each do |name|
|
242
|
-
mixin_table[name] = block
|
243
|
-
end
|
244
|
-
else
|
245
|
-
names.each do |name|
|
246
|
-
module_eval &mixin_table[name]
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
## TODO - for some reason these hork the usage!
|
252
|
-
|
253
|
-
%w[ examples samples api ].each do |chunkname|
|
254
|
-
module_eval <<-code
|
255
|
-
def #{ chunkname } *a, &b
|
256
|
-
txt = b ? b.call : a.join("\\n")
|
257
|
-
usage['#{ chunkname }'] = txt
|
258
|
-
end
|
259
|
-
code
|
260
|
-
end
|
261
|
-
alias_method 'example', 'examples'
|
262
|
-
alias_method 'sample', 'samples'
|
263
|
-
end
|
264
|
-
extend DSL
|
265
|
-
|
266
|
-
fattr 'argv'
|
267
|
-
fattr 'env'
|
268
|
-
fattr 'params'
|
269
|
-
fattr 'logger'
|
270
|
-
fattr 'stdin'
|
271
|
-
fattr 'stdout'
|
272
|
-
fattr 'stderr'
|
273
|
-
|
274
|
-
%w(
|
275
|
-
program name synopsis description author version
|
276
|
-
exit_status exit_success exit_failure exit_warn
|
277
|
-
logger_level
|
278
|
-
usage
|
279
|
-
).each{|a| fattr(a){ self.class.send a}}
|
280
|
-
|
281
|
-
%w( parameters param ).each do |dst|
|
282
|
-
alias_method "#{ dst }", "params"
|
283
|
-
alias_method "#{ dst }=", "params="
|
284
|
-
alias_method "#{ dst }?", "params?"
|
285
|
-
end
|
286
|
-
|
287
|
-
%w( debug info warn fatal error ).each do |m|
|
288
|
-
module_eval <<-code
|
289
|
-
def #{ m } *a, &b
|
290
|
-
logger.#{ m } *a, &b
|
291
|
-
end
|
292
|
-
code
|
293
|
-
end
|
294
|
-
|
295
|
-
=begin
|
296
|
-
=end
|
297
|
-
def pre_initialize() :hook end
|
298
|
-
def before_initialize() :hook end
|
299
|
-
def main_initialize argv = ARGV, env = ENV, opts = {}
|
300
|
-
@argv, @env, @opts = argv, env, opts
|
301
|
-
setup_finalizers
|
302
|
-
setup_io_restoration
|
303
|
-
setup_io_redirection
|
304
|
-
setup_logging
|
305
|
-
end
|
306
|
-
def initialize() :hook end
|
307
|
-
def after_initialize() :hook end
|
308
|
-
def post_initialize() :hook end
|
309
|
-
|
310
|
-
def setup_finalizers
|
311
|
-
@finalizers = finalizers = []
|
312
|
-
ObjectSpace.define_finalizer(self) do
|
313
|
-
while((f = finalizers.pop)); f.call; end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
def finalize
|
318
|
-
while((f = @finalizers.pop)); f.call; end
|
319
|
-
end
|
320
|
-
|
321
|
-
def setup_io_redirection
|
322
|
-
self.stdin = @opts['stdin'] || @opts[:stdin] || self.class.stdin
|
323
|
-
self.stdout = @opts['stdout'] || @opts[:stdout] || self.class.stdout
|
324
|
-
self.stderr = @opts['stderr'] || @opts[:stderr] || self.class.stderr
|
325
|
-
end
|
326
|
-
|
327
|
-
def setup_logging
|
328
|
-
log = self.class.logger || stderr
|
329
|
-
self.logger = log
|
330
|
-
end
|
331
|
-
def logger= log
|
332
|
-
unless(defined?(@logger) and @logger == log)
|
333
|
-
case log
|
334
|
-
when ::Logger, Logger
|
335
|
-
@logger = log
|
336
|
-
when IO, StringIO
|
337
|
-
@logger = Logger.new log
|
338
|
-
@logger.level = logger_level
|
339
|
-
else
|
340
|
-
@logger = Logger.new *log
|
341
|
-
@logger.level = logger_level
|
342
|
-
end
|
343
|
-
end
|
344
|
-
@logger
|
345
|
-
end
|
346
|
-
|
347
|
-
def setup_io_restoration
|
348
|
-
[STDIN, STDOUT, STDERR].each do |io|
|
349
|
-
dup = io.dup and @finalizers.push lambda{ io.reopen dup rescue nil }
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
def stdin= io
|
354
|
-
unless(defined?(@stdin) and (@stdin == io))
|
355
|
-
@stdin =
|
356
|
-
if io.respond_to? 'read'
|
357
|
-
io
|
358
|
-
else
|
359
|
-
fd = open io.to_s, 'r+'
|
360
|
-
@finalizers.push lambda{ fd.close }
|
361
|
-
fd
|
362
|
-
end
|
363
|
-
begin
|
364
|
-
STDIN.reopen @stdin
|
365
|
-
rescue
|
366
|
-
$stdin = @stdin
|
367
|
-
::Object.const_set 'STDIN', @stdin
|
368
|
-
end
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
def stdout= io
|
373
|
-
unless(defined?(@stdout) and (@stdout == io))
|
374
|
-
@stdout =
|
375
|
-
if io.respond_to? 'write'
|
376
|
-
io
|
377
|
-
else
|
378
|
-
fd = open io.to_s, 'w+'
|
379
|
-
@finalizers.push lambda{ fd.close }
|
380
|
-
fd
|
381
|
-
end
|
382
|
-
STDOUT.reopen @stdout rescue($stdout = @stdout)
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
def stderr= io
|
387
|
-
unless(defined?(@stderr) and (@stderr == io))
|
388
|
-
@stderr =
|
389
|
-
if io.respond_to? 'write'
|
390
|
-
io
|
391
|
-
else
|
392
|
-
fd = open io.to_s, 'w+'
|
393
|
-
@finalizers.push lambda{ fd.close }
|
394
|
-
fd
|
395
|
-
end
|
396
|
-
STDERR.reopen @stderr rescue($stderr = @stderr)
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
def pre_parse_parameters() :hook end
|
401
|
-
def before_parse_parameters() :hook end
|
402
|
-
def parse_parameters
|
403
|
-
pre_parse_parameters
|
404
|
-
before_parse_parameters
|
405
|
-
|
406
|
-
self.class.parameters.parse self
|
407
|
-
@params = Parameter::Table.new
|
408
|
-
self.class.parameters.each{|p| @params[p.name.to_s] = p}
|
409
|
-
|
410
|
-
after_parse_parameters
|
411
|
-
post_parse_parameters
|
412
|
-
end
|
413
|
-
def after_parse_parameters() :hook end
|
414
|
-
def post_parse_parameters() :hook end
|
415
|
-
|
416
|
-
def pre_run() :hook end
|
417
|
-
def before_run() :hook end
|
418
|
-
def run
|
419
|
-
raise NotImplementedError, 'run not defined'
|
420
|
-
end
|
421
|
-
def after_run() :hook end
|
422
|
-
def post_run() :hook end
|
423
|
-
|
424
|
-
def mode_given?
|
425
|
-
begin
|
426
|
-
modes.size > 0 and
|
427
|
-
argv.size > 0 and
|
428
|
-
modes.find_by_mode(argv.first)
|
429
|
-
rescue Mode::Ambiguous
|
430
|
-
true
|
431
|
-
end
|
432
|
-
end
|
433
|
-
def mode_run!
|
434
|
-
mode = modes.find_by_mode argv.shift
|
435
|
-
klass = modes[mode] or abort "bad mode <#{ mode }>"
|
436
|
-
main = klass.new @argv, @env, @opts
|
437
|
-
main.mode = mode
|
438
|
-
main.run
|
439
|
-
end
|
440
|
-
def modes
|
441
|
-
self.class.modes
|
442
|
-
end
|
443
|
-
fattr 'mode'
|
444
|
-
|
445
|
-
def help! status = 0
|
446
|
-
print usage.to_s
|
447
|
-
exit(status)
|
448
|
-
end
|
449
|
-
|
450
|
-
def abort message = 'exit'
|
451
|
-
raise SystemExit.new(message)
|
452
|
-
end
|
453
|
-
|
454
|
-
def handle_exception e
|
455
|
-
if e.respond_to?(:error_handler_before)
|
456
|
-
fcall(e, :error_handler_before, self)
|
457
|
-
end
|
458
|
-
|
459
|
-
if e.respond_to?(:error_handler_instead)
|
460
|
-
fcall(e, :error_handler_instead, self)
|
461
|
-
else
|
462
|
-
if e.respond_to? :status
|
463
|
-
exit_status(( e.status ))
|
464
|
-
end
|
465
|
-
|
466
|
-
if Softspoken === e or SystemExit === e
|
467
|
-
quiet = ((SystemExit === e and e.message.respond_to?('abort')) or # see main/stdext.rb
|
468
|
-
(SystemExit === e and e.message == 'exit'))
|
469
|
-
stderr.puts e.message unless quiet
|
470
|
-
else
|
471
|
-
fatal{ e }
|
472
|
-
end
|
473
|
-
end
|
474
|
-
|
475
|
-
if e.respond_to?(:error_handler_after)
|
476
|
-
fcall(e, :error_handler_after, self)
|
477
|
-
end
|
478
|
-
|
479
|
-
exit_status(( exit_failure )) if exit_status == exit_success
|
480
|
-
exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
|
481
|
-
exit exit_status
|
482
|
-
end
|
483
|
-
|
484
|
-
def fcall obj, m, *argv, &block
|
485
|
-
m = obj.method m
|
486
|
-
arity = m.arity
|
487
|
-
if arity >= 0
|
488
|
-
argv = argv[0, arity]
|
489
|
-
else
|
490
|
-
arity = arity.abs - 1
|
491
|
-
argv = argv[0, arity] + argv[arity .. -1]
|
492
|
-
end
|
493
|
-
m.call *argv, &block
|
494
|
-
end
|
495
|
-
|
496
|
-
def handle_throw status
|
497
|
-
exit(( Integer(status) rescue 0 ))
|
498
|
-
end
|
499
|
-
|
500
|
-
%w[ before instead after ].each do |which|
|
501
|
-
module_eval <<-code
|
502
|
-
def error_handler_#{ which } *argv, &block
|
503
|
-
block.call *argv
|
504
|
-
end
|
505
|
-
code
|
506
|
-
end
|
507
|
-
|
508
|
-
def instance_eval_block *argv, &block
|
509
|
-
sc =
|
510
|
-
class << self
|
511
|
-
self
|
512
|
-
end
|
513
|
-
sc.module_eval{ define_method '__instance_eval_block', &block }
|
514
|
-
fcall self, '__instance_eval_block', *argv, &block
|
515
|
-
end
|
516
|
-
end
|
517
|
-
end
|