main 2.5.0 → 2.6.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 +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
|