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 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 you a program called 'a.rb' to be invoked as
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 (1 ~> bool(bar=false))
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 lib/main.rb lib/main/*
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 'bar'
5
-
6
- examples 'a', 'b'
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
- #spec.add_dependency 'lib', '>= version'
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
@@ -2,7 +2,7 @@ module Main
2
2
  #
3
3
  # top level constants
4
4
  #
5
- Main::VERSION = '2.5.0' unless
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', '~> 4.1.0'
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 'attributes', '~> 4.3.0'
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'
@@ -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
- stderr.puts e.message unless(SystemExit === e and e.message.to_s == 'exit')
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