main 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +126 -5
- data/TODO +10 -0
- data/a.rb +3 -20
- data/gemspec.rb +2 -1
- data/install.rb +2 -0
- data/lib/main.rb +4 -3
- data/lib/main/base.rb +88 -4
- data/lib/main/cast.rb +29 -3
- data/lib/main/logger.rb +51 -0
- data/lib/main/parameter.rb +133 -53
- data/lib/main/stdext.rb +29 -9
- data/lib/main/usage.rb +65 -23
- data/test/main.rb.bak +767 -0
- metadata +25 -5
data/README
CHANGED
@@ -5,8 +5,9 @@ SYNOPSIS
|
|
5
5
|
a class factory and dsl for generating command line programs real quick
|
6
6
|
|
7
7
|
URI
|
8
|
-
http://rubyforge.org/projects/codeforpeople/
|
9
8
|
http://codeforpeople.com/lib/ruby/
|
9
|
+
http://rubyforge.org/projects/codeforpeople/
|
10
|
+
http://codeforpeople.rubyforge.org/svn/
|
10
11
|
|
11
12
|
INSTALL
|
12
13
|
gem install main
|
@@ -25,6 +26,7 @@ DESCRIPTION
|
|
25
26
|
- parsing user defined ARGV and ENV
|
26
27
|
- zero requirements for understanding the obtuse apis of *any* command
|
27
28
|
line option parsers
|
29
|
+
- leather pants
|
28
30
|
|
29
31
|
in short main.rb aims to drastically lower the barrier to writing uniform
|
30
32
|
command line applications.
|
@@ -64,7 +66,7 @@ DESCRIPTION
|
|
64
66
|
end
|
65
67
|
}
|
66
68
|
|
67
|
-
which allows
|
69
|
+
which allows a program, called 'a.rb', to be invoked as
|
68
70
|
|
69
71
|
ruby a.rb install
|
70
72
|
|
@@ -252,7 +254,7 @@ SAMPLES
|
|
252
254
|
b.rb
|
253
255
|
|
254
256
|
SYNOPSIS
|
255
|
-
b.rb foo [options]+
|
257
|
+
b.rb foo foo foo [options]+
|
256
258
|
|
257
259
|
PARAMETERS
|
258
260
|
foo (3 -> int(foo))
|
@@ -357,7 +359,7 @@ SAMPLES
|
|
357
359
|
|
358
360
|
PARAMETERS
|
359
361
|
--foo=foo, -f (2 -> float(foo))
|
360
|
-
--bar=[bar], -b (
|
362
|
+
--bar=[bar], -b (0 ~> bool(bar=false))
|
361
363
|
--help, -h
|
362
364
|
|
363
365
|
|
@@ -365,11 +367,130 @@ SAMPLES
|
|
365
367
|
DOCS
|
366
368
|
test/main.rb
|
367
369
|
|
368
|
-
vim -o
|
370
|
+
find ./lib |xargs vim -o
|
369
371
|
|
370
372
|
API section below
|
371
373
|
|
372
374
|
HISTORY
|
375
|
+
2.6.0
|
376
|
+
- added 'mixin' feaature for storing, and later evaluating a block of
|
377
|
+
code. the purpose of this is for use with modes where you want to keep
|
378
|
+
your code dry, but may not want to define something in the base class
|
379
|
+
for all to inherit. 'mixin' allows you to define the code to inherit
|
380
|
+
once and the selectively drop it in child classes (modes) on demand.
|
381
|
+
for example
|
382
|
+
|
383
|
+
Main {
|
384
|
+
mixin :foobar do
|
385
|
+
option 'foo'
|
386
|
+
option 'bar'
|
387
|
+
end
|
388
|
+
|
389
|
+
mode :install do
|
390
|
+
mixin :foobar
|
391
|
+
end
|
392
|
+
|
393
|
+
mode :uninstall do
|
394
|
+
mixin :foobar
|
395
|
+
end
|
396
|
+
|
397
|
+
mode :clean do
|
398
|
+
end
|
399
|
+
}
|
400
|
+
|
401
|
+
- mode definitions are now deferred to the end of the Main block, so you
|
402
|
+
can do this
|
403
|
+
|
404
|
+
Main {
|
405
|
+
mode 'a' do
|
406
|
+
mixin :foo
|
407
|
+
end
|
408
|
+
|
409
|
+
mode 'b' do
|
410
|
+
mixin :foo
|
411
|
+
end
|
412
|
+
|
413
|
+
def inherited_method
|
414
|
+
42
|
415
|
+
end
|
416
|
+
|
417
|
+
mixin 'foo' do
|
418
|
+
def another_inherited_method
|
419
|
+
'forty-two'
|
420
|
+
end
|
421
|
+
end
|
422
|
+
}
|
423
|
+
|
424
|
+
- added sanity check at end of paramter contruction
|
425
|
+
|
426
|
+
- improved auto usage generation when arity is used with arguments
|
427
|
+
|
428
|
+
- removed 'p' shortcut in paramerter dsl because it collided with
|
429
|
+
Kernel.p. it's now called 'param'. this method is availble *inside* a
|
430
|
+
parameter definition
|
431
|
+
|
432
|
+
option('foo', 'f'){
|
433
|
+
synopsis "arity = #{ param.arity }"
|
434
|
+
}
|
435
|
+
|
436
|
+
- fixed bug where '--' did not signal the end of parameter parsing in a
|
437
|
+
getoptlong compliant way
|
438
|
+
|
439
|
+
- added (before/after)_parse_parameters, (before/after)_initialize, and
|
440
|
+
(before/after)_run hooks
|
441
|
+
|
442
|
+
- fixed bug where adding to usage via
|
443
|
+
|
444
|
+
usage['my_section'] = 'custom message'
|
445
|
+
|
446
|
+
totally horked the default auto generated usage message
|
447
|
+
|
448
|
+
- updated dependancies in gemspec.rb for attributes (~> 5.0.0) and
|
449
|
+
arrayfields (~> 4.3.0)
|
450
|
+
|
451
|
+
- check that client code defined run, iff not wrap_run! is called. this is
|
452
|
+
so mains with a mode, but no run defined, still function correctly when
|
453
|
+
passed a mode
|
454
|
+
|
455
|
+
- added new shortcut for creating accessors for parameters. for example
|
456
|
+
|
457
|
+
option('foo'){
|
458
|
+
argument :required
|
459
|
+
cast :int
|
460
|
+
attr
|
461
|
+
}
|
462
|
+
|
463
|
+
def run
|
464
|
+
p foo ### this attr will return the parameter's *value*
|
465
|
+
end
|
466
|
+
|
467
|
+
a block can be passed to specify how to extract the value from the
|
468
|
+
parameter
|
469
|
+
|
470
|
+
argument('foo'){
|
471
|
+
optional
|
472
|
+
default 21
|
473
|
+
cast :int
|
474
|
+
attr{|param| param.value * 2}
|
475
|
+
}
|
476
|
+
|
477
|
+
def run
|
478
|
+
p foo #=> 42
|
479
|
+
end
|
480
|
+
|
481
|
+
- fixed bug where 'abort("message")' would print "message" twice on exit
|
482
|
+
if running under a nested mode (yes again - the fix in 2.4.0 wasn't
|
483
|
+
complete)
|
484
|
+
|
485
|
+
- added a time cast, which uses Time.parse
|
486
|
+
|
487
|
+
argument('login_time'){ cast :time }
|
488
|
+
|
489
|
+
- added a date cast, which uses Date.parse
|
490
|
+
|
491
|
+
argument('login_date'){ cast :date }
|
492
|
+
|
493
|
+
|
373
494
|
2.5.0
|
374
495
|
- added 'examples', 'samples', and 'api' kewords to main dsl. each
|
375
496
|
keyword takes a list of strings which will be included in the help
|
data/TODO
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
* clean up warnings under 'ruby -d'
|
3
|
+
* error reporting weird for some errors
|
4
|
+
|
5
|
+
===============================================================================
|
6
|
+
X calls to abort sometimes lead to STDERR prining twice ;-(
|
7
|
+
X main's' with modes, but no run do not operate properly - add default run w/wrap_run!
|
8
|
+
X usage fubar when extra chunks are set
|
9
|
+
X usage of arguments is fubar when negative arities are used
|
10
|
+
X add sanity checks at parameter contruction completion
|
data/a.rb
CHANGED
@@ -1,24 +1,7 @@
|
|
1
1
|
require 'main'
|
2
2
|
|
3
3
|
Main {
|
4
|
-
option
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
api <<-txt
|
9
|
-
foobar
|
10
|
-
barfoo
|
11
|
-
txt
|
12
|
-
|
13
|
-
mode 'foo' do
|
14
|
-
option('bar'){ required }
|
15
|
-
|
16
|
-
def run
|
17
|
-
p params
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def run
|
22
|
-
p params
|
23
|
-
end
|
4
|
+
option('foo'){
|
5
|
+
examples
|
6
|
+
}
|
24
7
|
}
|
data/gemspec.rb
CHANGED
@@ -25,7 +25,8 @@ Gem::Specification::new do |spec|
|
|
25
25
|
|
26
26
|
spec.has_rdoc = File::exist? "doc"
|
27
27
|
spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
|
28
|
-
|
28
|
+
spec.add_dependency 'attributes', '>= 5.0.0'
|
29
|
+
spec.add_dependency 'arrayfields', '>= 4.3.0'
|
29
30
|
|
30
31
|
spec.extensions << "extconf.rb" if File::exists? "extconf.rb"
|
31
32
|
|
data/install.rb
CHANGED
@@ -36,9 +36,11 @@ def install_rb(srcdir=nil, destdir=nil, mode=nil, bin=nil)
|
|
36
36
|
next unless FileTest.file?(f)
|
37
37
|
next if (f = f[srcdir.length+1..-1]) == nil
|
38
38
|
next if (/CVS$/ =~ File.dirname(f))
|
39
|
+
next if (/\.svn/ =~ File.dirname(f))
|
39
40
|
next if f =~ %r/\.lnk/
|
40
41
|
next if f =~ %r/\.svn/
|
41
42
|
next if f =~ %r/\.swp/
|
43
|
+
next if f =~ %r/\.svn/
|
42
44
|
path.push f
|
43
45
|
dir |= [File.dirname(f)]
|
44
46
|
end
|
data/lib/main.rb
CHANGED
@@ -2,7 +2,7 @@ module Main
|
|
2
2
|
#
|
3
3
|
# top level constants
|
4
4
|
#
|
5
|
-
Main::VERSION = '2.
|
5
|
+
Main::VERSION = '2.6.0' unless
|
6
6
|
defined? Main::VERSION
|
7
7
|
def self.version() Main::VERSION end
|
8
8
|
|
@@ -28,13 +28,13 @@ module Main
|
|
28
28
|
42
|
29
29
|
end
|
30
30
|
begin
|
31
|
-
gem 'attributes', '~>
|
31
|
+
gem 'attributes', '~> 5.0.0'
|
32
32
|
require 'attributes'
|
33
33
|
rescue Exception
|
34
34
|
require libdir + 'attributes'
|
35
35
|
end
|
36
36
|
begin
|
37
|
-
gem '
|
37
|
+
gem 'arrayfields', '~> 4.3.0'
|
38
38
|
require 'arrayfields'
|
39
39
|
rescue Exception
|
40
40
|
require libdir + 'arrayfields'
|
@@ -45,6 +45,7 @@ module Main
|
|
45
45
|
require libdir + 'stdext'
|
46
46
|
require libdir + 'softspoken'
|
47
47
|
require libdir + 'util'
|
48
|
+
require libdir + 'logger'
|
48
49
|
require libdir + 'usage'
|
49
50
|
require libdir + 'cast'
|
50
51
|
require libdir + 'parameter'
|
data/lib/main/base.rb
CHANGED
@@ -21,7 +21,9 @@ module Main
|
|
21
21
|
end
|
22
22
|
|
23
23
|
pre_run
|
24
|
+
before_run
|
24
25
|
self.class.const_get(:RUN).bind(self).call(*a, &b)
|
26
|
+
after_run
|
25
27
|
post_run
|
26
28
|
|
27
29
|
finalize
|
@@ -66,13 +68,14 @@ module Main
|
|
66
68
|
}
|
67
69
|
end
|
68
70
|
|
71
|
+
# attributes
|
69
72
|
attribute( 'name' ){ File.basename $0 }
|
70
73
|
attribute( 'synopsis' ){ Usage.default_synopsis(self) }
|
71
74
|
attribute( 'description' )
|
72
75
|
attribute( 'usage' ){ Usage.default_usage self }
|
73
76
|
attribute( 'modes' ){ Mode.list }
|
77
|
+
attribute( 'mode_definitions' ){ Array.new }
|
74
78
|
attribute( 'mode_name' ){ 'main' }
|
75
|
-
def mode_name=(value) @mode_name = Mode.new value end
|
76
79
|
attribute( 'parent' ){ nil }
|
77
80
|
attribute( 'children' ){ Set.new }
|
78
81
|
|
@@ -89,6 +92,21 @@ module Main
|
|
89
92
|
attribute( 'exit_failure' ){ EXIT_FAILURE }
|
90
93
|
attribute( 'exit_warn' ){ EXIT_WARN }
|
91
94
|
inheritable_attribute( 'parameters' ){ Parameter::List[] }
|
95
|
+
inheritable_attribute( 'can_has_hash' ){ Hash.new }
|
96
|
+
inheritable_attribute( 'mixin_table' ){ Hash.new }
|
97
|
+
|
98
|
+
# override a few attributes
|
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
|
92
110
|
|
93
111
|
def create parent = Base, *a, &b
|
94
112
|
Class.new parent do |child|
|
@@ -97,6 +115,15 @@ module Main
|
|
97
115
|
child.context do
|
98
116
|
child.class_eval &b if b
|
99
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
|
100
127
|
end
|
101
128
|
end
|
102
129
|
end
|
@@ -115,6 +142,14 @@ module Main
|
|
115
142
|
end
|
116
143
|
end
|
117
144
|
|
145
|
+
module ::Main
|
146
|
+
singleton_class{
|
147
|
+
def current
|
148
|
+
::Main::Base.context
|
149
|
+
end
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
118
153
|
def fully_qualified_mode
|
119
154
|
list = []
|
120
155
|
ancestors.each do |ancestor|
|
@@ -129,8 +164,10 @@ module Main
|
|
129
164
|
def new(*a, &b)
|
130
165
|
allocate.instance_eval do
|
131
166
|
pre_initialize
|
167
|
+
before_initialize
|
132
168
|
main_initialize *a, &b
|
133
169
|
initialize
|
170
|
+
after_initialize
|
134
171
|
post_initialize
|
135
172
|
self
|
136
173
|
end
|
@@ -167,14 +204,51 @@ module Main
|
|
167
204
|
end
|
168
205
|
alias_method 'env', 'environment'
|
169
206
|
|
207
|
+
=begin
|
170
208
|
def mode name, &b
|
171
209
|
klass =
|
172
210
|
create context do
|
173
|
-
mode_name name
|
211
|
+
mode_name name.to_s
|
174
212
|
module_eval &b if b
|
175
213
|
end
|
176
214
|
modes.add klass
|
177
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!
|
178
252
|
|
179
253
|
%w[ examples samples api ].each do |chunkname|
|
180
254
|
module_eval <<-code
|
@@ -184,6 +258,8 @@ module Main
|
|
184
258
|
end
|
185
259
|
code
|
186
260
|
end
|
261
|
+
alias_method 'example', 'examples'
|
262
|
+
alias_method 'sample', 'samples'
|
187
263
|
end
|
188
264
|
extend DSL
|
189
265
|
|
@@ -219,6 +295,7 @@ module Main
|
|
219
295
|
=begin
|
220
296
|
=end
|
221
297
|
def pre_initialize() :hook end
|
298
|
+
def before_initialize() :hook end
|
222
299
|
def main_initialize argv = ARGV, env = ENV, opts = {}
|
223
300
|
@argv, @env, @opts = argv, env, opts
|
224
301
|
setup_finalizers
|
@@ -227,6 +304,7 @@ module Main
|
|
227
304
|
setup_logging
|
228
305
|
end
|
229
306
|
def initialize() :hook end
|
307
|
+
def after_initialize() :hook end
|
230
308
|
def post_initialize() :hook end
|
231
309
|
|
232
310
|
def setup_finalizers
|
@@ -253,7 +331,7 @@ module Main
|
|
253
331
|
def logger= log
|
254
332
|
unless(defined?(@logger) and @logger == log)
|
255
333
|
case log
|
256
|
-
when Logger
|
334
|
+
when ::Logger, Logger
|
257
335
|
@logger = log
|
258
336
|
when IO, StringIO
|
259
337
|
@logger = Logger.new log
|
@@ -320,6 +398,7 @@ module Main
|
|
320
398
|
end
|
321
399
|
|
322
400
|
def pre_parse_parameters() :hook end
|
401
|
+
def before_parse_parameters() :hook end
|
323
402
|
def parse_parameters
|
324
403
|
pre_parse_parameters
|
325
404
|
|
@@ -329,12 +408,15 @@ module Main
|
|
329
408
|
|
330
409
|
post_parse_parameters
|
331
410
|
end
|
411
|
+
def after_parse_parameters() :hook end
|
332
412
|
def post_parse_parameters() :hook end
|
333
413
|
|
334
414
|
def pre_run() :hook end
|
415
|
+
def before_run() :hook end
|
335
416
|
def run
|
336
417
|
raise NotImplementedError, 'run not defined'
|
337
418
|
end
|
419
|
+
def after_run() :hook end
|
338
420
|
def post_run() :hook end
|
339
421
|
|
340
422
|
def mode_given?
|
@@ -380,7 +462,9 @@ module Main
|
|
380
462
|
end
|
381
463
|
|
382
464
|
if Softspoken === e or SystemExit === e
|
383
|
-
|
465
|
+
quiet = ((SystemExit === e and e.message.respond_to?('abort')) or # see main/stdext.rb
|
466
|
+
(SystemExit === e and e.message == 'exit'))
|
467
|
+
stderr.puts e.message unless quiet
|
384
468
|
else
|
385
469
|
fatal{ e }
|
386
470
|
end
|