clio 0.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.
Files changed (145) hide show
  1. data/COPYING +170 -0
  2. data/HISTORY +11 -0
  3. data/MANIFEST +181 -0
  4. data/METADATA +18 -0
  5. data/NEWS +10 -0
  6. data/README +52 -0
  7. data/admin/config/reap.yaml +30 -0
  8. data/admin/depot/commandline.rb +219 -0
  9. data/admin/depot/multicommand.rb +403 -0
  10. data/admin/depot/test_multicommand.rb +40 -0
  11. data/admin/log/notes.xml +28 -0
  12. data/admin/log/stats.html +25 -0
  13. data/admin/log/syntax.log +0 -0
  14. data/admin/log/testunit.log +16 -0
  15. data/admin/pack/clio-0.0.1.gem +0 -0
  16. data/admin/share/reap/example.rb +7 -0
  17. data/admin/temps/lib/clio/about.rb.erb +4 -0
  18. data/lib/clio/ansicode.rb +319 -0
  19. data/lib/clio/command.rb +296 -0
  20. data/lib/clio/commandable.rb +195 -0
  21. data/lib/clio/commandline.rb +275 -0
  22. data/lib/clio/consoleutils.rb +117 -0
  23. data/lib/clio/errors.rb +16 -0
  24. data/lib/clio/option.rb +36 -0
  25. data/lib/clio/progressbar.rb +253 -0
  26. data/lib/clio/runmode.rb +126 -0
  27. data/lib/clio/string.rb +147 -0
  28. data/test/test_command.rb +42 -0
  29. data/test/test_commandline.rb +83 -0
  30. data/vendor/Console/Console.cpp +1203 -0
  31. data/vendor/Console/Console.rdoc +690 -0
  32. data/vendor/Console/Console_ANSI.rdoc +302 -0
  33. data/vendor/Console/HISTORY.txt +7 -0
  34. data/vendor/Console/INSTALL.txt +18 -0
  35. data/vendor/Console/Makefile +162 -0
  36. data/vendor/Console/README.txt +26 -0
  37. data/vendor/Console/doc/classes/Win32.html +115 -0
  38. data/vendor/Console/doc/classes/Win32/Console.html +650 -0
  39. data/vendor/Console/doc/classes/Win32/Console.src/M000001.html +31 -0
  40. data/vendor/Console/doc/classes/Win32/Console.src/M000002.html +23 -0
  41. data/vendor/Console/doc/classes/Win32/Console.src/M000003.html +23 -0
  42. data/vendor/Console/doc/classes/Win32/Console.src/M000004.html +27 -0
  43. data/vendor/Console/doc/classes/Win32/Console.src/M000005.html +23 -0
  44. data/vendor/Console/doc/classes/Win32/Console.src/M000006.html +28 -0
  45. data/vendor/Console/doc/classes/Win32/Console.src/M000007.html +23 -0
  46. data/vendor/Console/doc/classes/Win32/Console.src/M000008.html +24 -0
  47. data/vendor/Console/doc/classes/Win32/Console.src/M000009.html +44 -0
  48. data/vendor/Console/doc/classes/Win32/Console.src/M000010.html +23 -0
  49. data/vendor/Console/doc/classes/Win32/Console.src/M000011.html +33 -0
  50. data/vendor/Console/doc/classes/Win32/Console.src/M000012.html +26 -0
  51. data/vendor/Console/doc/classes/Win32/Console.src/M000013.html +27 -0
  52. data/vendor/Console/doc/classes/Win32/Console.src/M000014.html +28 -0
  53. data/vendor/Console/doc/classes/Win32/Console.src/M000015.html +23 -0
  54. data/vendor/Console/doc/classes/Win32/Console.src/M000016.html +23 -0
  55. data/vendor/Console/doc/classes/Win32/Console.src/M000017.html +23 -0
  56. data/vendor/Console/doc/classes/Win32/Console.src/M000018.html +29 -0
  57. data/vendor/Console/doc/classes/Win32/Console.src/M000019.html +23 -0
  58. data/vendor/Console/doc/classes/Win32/Console.src/M000020.html +23 -0
  59. data/vendor/Console/doc/classes/Win32/Console.src/M000021.html +28 -0
  60. data/vendor/Console/doc/classes/Win32/Console.src/M000022.html +23 -0
  61. data/vendor/Console/doc/classes/Win32/Console.src/M000023.html +28 -0
  62. data/vendor/Console/doc/classes/Win32/Console.src/M000024.html +35 -0
  63. data/vendor/Console/doc/classes/Win32/Console.src/M000025.html +28 -0
  64. data/vendor/Console/doc/classes/Win32/Console.src/M000026.html +28 -0
  65. data/vendor/Console/doc/classes/Win32/Console.src/M000027.html +28 -0
  66. data/vendor/Console/doc/classes/Win32/Console.src/M000028.html +31 -0
  67. data/vendor/Console/doc/classes/Win32/Console.src/M000029.html +23 -0
  68. data/vendor/Console/doc/classes/Win32/Console.src/M000030.html +23 -0
  69. data/vendor/Console/doc/classes/Win32/Console.src/M000031.html +23 -0
  70. data/vendor/Console/doc/classes/Win32/Console.src/M000032.html +27 -0
  71. data/vendor/Console/doc/classes/Win32/Console.src/M000033.html +27 -0
  72. data/vendor/Console/doc/classes/Win32/Console.src/M000034.html +25 -0
  73. data/vendor/Console/doc/classes/Win32/Console/ANSI.html +103 -0
  74. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.html +220 -0
  75. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000035.html +32 -0
  76. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000036.html +205 -0
  77. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000037.html +40 -0
  78. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000038.html +25 -0
  79. data/vendor/Console/doc/classes/Win32/Console/API.html +758 -0
  80. data/vendor/Console/doc/classes/Win32/Console/API.src/M000039.html +27 -0
  81. data/vendor/Console/doc/classes/Win32/Console/API.src/M000040.html +27 -0
  82. data/vendor/Console/doc/classes/Win32/Console/API.src/M000041.html +27 -0
  83. data/vendor/Console/doc/classes/Win32/Console/API.src/M000042.html +32 -0
  84. data/vendor/Console/doc/classes/Win32/Console/API.src/M000043.html +32 -0
  85. data/vendor/Console/doc/classes/Win32/Console/API.src/M000044.html +28 -0
  86. data/vendor/Console/doc/classes/Win32/Console/API.src/M000045.html +26 -0
  87. data/vendor/Console/doc/classes/Win32/Console/API.src/M000046.html +26 -0
  88. data/vendor/Console/doc/classes/Win32/Console/API.src/M000047.html +27 -0
  89. data/vendor/Console/doc/classes/Win32/Console/API.src/M000048.html +30 -0
  90. data/vendor/Console/doc/classes/Win32/Console/API.src/M000049.html +29 -0
  91. data/vendor/Console/doc/classes/Win32/Console/API.src/M000050.html +27 -0
  92. data/vendor/Console/doc/classes/Win32/Console/API.src/M000051.html +28 -0
  93. data/vendor/Console/doc/classes/Win32/Console/API.src/M000052.html +30 -0
  94. data/vendor/Console/doc/classes/Win32/Console/API.src/M000053.html +27 -0
  95. data/vendor/Console/doc/classes/Win32/Console/API.src/M000054.html +29 -0
  96. data/vendor/Console/doc/classes/Win32/Console/API.src/M000055.html +29 -0
  97. data/vendor/Console/doc/classes/Win32/Console/API.src/M000056.html +28 -0
  98. data/vendor/Console/doc/classes/Win32/Console/API.src/M000057.html +27 -0
  99. data/vendor/Console/doc/classes/Win32/Console/API.src/M000058.html +47 -0
  100. data/vendor/Console/doc/classes/Win32/Console/API.src/M000059.html +32 -0
  101. data/vendor/Console/doc/classes/Win32/Console/API.src/M000060.html +47 -0
  102. data/vendor/Console/doc/classes/Win32/Console/API.src/M000061.html +34 -0
  103. data/vendor/Console/doc/classes/Win32/Console/API.src/M000062.html +32 -0
  104. data/vendor/Console/doc/classes/Win32/Console/API.src/M000063.html +32 -0
  105. data/vendor/Console/doc/classes/Win32/Console/API.src/M000064.html +35 -0
  106. data/vendor/Console/doc/classes/Win32/Console/API.src/M000065.html +26 -0
  107. data/vendor/Console/doc/classes/Win32/Console/API.src/M000066.html +27 -0
  108. data/vendor/Console/doc/classes/Win32/Console/API.src/M000067.html +29 -0
  109. data/vendor/Console/doc/classes/Win32/Console/API.src/M000068.html +27 -0
  110. data/vendor/Console/doc/classes/Win32/Console/API.src/M000069.html +27 -0
  111. data/vendor/Console/doc/classes/Win32/Console/API.src/M000070.html +28 -0
  112. data/vendor/Console/doc/classes/Win32/Console/API.src/M000071.html +27 -0
  113. data/vendor/Console/doc/classes/Win32/Console/API.src/M000072.html +26 -0
  114. data/vendor/Console/doc/classes/Win32/Console/API.src/M000073.html +27 -0
  115. data/vendor/Console/doc/classes/Win32/Console/API.src/M000074.html +31 -0
  116. data/vendor/Console/doc/classes/Win32/Console/API.src/M000075.html +27 -0
  117. data/vendor/Console/doc/classes/Win32/Console/API.src/M000076.html +32 -0
  118. data/vendor/Console/doc/classes/Win32/Console/API.src/M000077.html +27 -0
  119. data/vendor/Console/doc/classes/Win32/Console/API.src/M000078.html +32 -0
  120. data/vendor/Console/doc/classes/Win32/Console/API.src/M000079.html +32 -0
  121. data/vendor/Console/doc/classes/Win32/Console/API.src/M000080.html +32 -0
  122. data/vendor/Console/doc/classes/Win32/Console/Constants.html +360 -0
  123. data/vendor/Console/doc/created.rid +1 -0
  124. data/vendor/Console/doc/files/Console_ANSI_rdoc.html +407 -0
  125. data/vendor/Console/doc/files/Console_cpp.html +104 -0
  126. data/vendor/Console/doc/files/Console_rdoc.html +964 -0
  127. data/vendor/Console/doc/files/lib/Win32/Console/ANSI_rb.html +123 -0
  128. data/vendor/Console/doc/files/lib/Win32/Console_rb.html +297 -0
  129. data/vendor/Console/doc/fr_class_index.html +32 -0
  130. data/vendor/Console/doc/fr_file_index.html +31 -0
  131. data/vendor/Console/doc/fr_method_index.html +106 -0
  132. data/vendor/Console/doc/index.html +24 -0
  133. data/vendor/Console/doc/rdoc-style.css +172 -0
  134. data/vendor/Console/extconf.rb +18 -0
  135. data/vendor/Console/lib/Term/ansicolor.rb +76 -0
  136. data/vendor/Console/lib/Win32/Console.rb +970 -0
  137. data/vendor/Console/lib/Win32/Console/ANSI.rb +305 -0
  138. data/vendor/Console/test/test_cursor.rb +9 -0
  139. data/vendor/Console/test/test_mouse.rb +6 -0
  140. data/vendor/Console/test/test_readinput.rb +62 -0
  141. data/vendor/Console/test/test_readoutput.rb +52 -0
  142. data/vendor/Console/test/test_sendevent.rb +17 -0
  143. data/vendor/Console/test/test_title.rb +14 -0
  144. data/vendor/Console/test/test_write.rb +36 -0
  145. metadata +253 -0
@@ -0,0 +1,30 @@
1
+ ---
2
+
3
+ syntax: {}
4
+
5
+ autotools: ~
6
+
7
+ testunit: {}
8
+
9
+ basicstats: {}
10
+
11
+ notes: {}
12
+
13
+ rdoc:
14
+ template: jamis
15
+ inline: true
16
+
17
+ ridoc: {}
18
+
19
+ gem:
20
+ exclude:
21
+ - doc/rdoc
22
+ - doc/ri
23
+
24
+ rubyforge:
25
+ unixname: clio
26
+ groupid: 6913
27
+ # publishmap:
28
+ # admin/web: .
29
+ # doc/rdoc: rdoc
30
+
@@ -0,0 +1,219 @@
1
+ require 'shellwords'
2
+ require 'facets/kernel/object_class'
3
+
4
+ module Clio
5
+ ### = Commandline
6
+ ###
7
+ ### What a strange thing is the Clio Commandline.
8
+ ### An entity unknown until put upon.
9
+ class Commandline
10
+ instance_methods.each{ |m| private m if m !~ /^(__|instance_|object_|send$|inspect$)/ }
11
+
12
+ ### Splits a raw command line into two smaller
13
+ ### ones. The first including only options
14
+ ### upto the first non-option argument. This
15
+ ### makes quick work of separating subcommands
16
+ ### from a main command.
17
+ # TODO: Rename this method.
18
+ def self.gerrymander(argv=ARGV)
19
+ if String===argv
20
+ argv = Shellwords.shellwords(argv)
21
+ end
22
+ sub = argv.find{ |x| x !~ /^[-]/ }
23
+ idx = argv.index(sub)
24
+ opts = argv[0...idx]
25
+ scmd = argv[idx..-1]
26
+ return new(opts), new(scmd)
27
+ end
28
+
29
+ ### Define an option attibute.
30
+ ### While commandline can be used without
31
+ ### pre-declartion of support options
32
+ ### doding so allows for creating option
33
+ ### aliases. Eg. --quiet and -q.
34
+ def self.attr(name, *aliases)
35
+ name = name.to_s
36
+ if name =~ /\?$/
37
+ name = name.chomp('?')
38
+ #attr_writer name
39
+ module_eval "def #{name}?; self[:#{name}] ; end"
40
+ aliases.each do |alt|
41
+ alt = alt.to_s.chomp('?')
42
+ alias_method("#{alt}?", "#{name}?")
43
+ #alias_method("#{alt}=", "#{name}=")
44
+ end
45
+ else
46
+ attr_reader(name)
47
+ aliases.each do |alt|
48
+ #alt = alt.to_s.chomp('?') # TODO: raise error ?
49
+ alias_method("#{alt}" , "#{name}")
50
+ #alias_method("#{alt}=", "#{name}=")
51
+ end
52
+ end
53
+ end
54
+
55
+ ### Returns a list of all pre-defined options.
56
+ ### It does this by seaching class ancestry
57
+ ### for instance_methods until it reaches the
58
+ ### Commandline base class.
59
+ ### TODO: Rename #runmodes method.
60
+ ### TODO: Robust enough? Use an Inheritor instead?
61
+ def self.runmodes
62
+ index = ancestors.index(::Clio::Commandline)
63
+ options = ancestors[0...index].inject([]) do |opts, ancestor|
64
+ opts | ancestor.instance_methods(false)
65
+ end
66
+ options.reject{ |o| o =~ /\=$/ }
67
+ options.collect{ |o| o.to_sym }
68
+ end
69
+
70
+ public
71
+
72
+ ### This method provides the centralized means of accessing
73
+ ### the options and arguments of the commandline.
74
+ def [](index)
75
+ case index
76
+ when Integer
77
+ @arguments[index] ||= (
78
+ args = @argv.select{ |e| e !~ /^-/ }
79
+ val = args[index]
80
+ @argv.delete(args[index])
81
+ val
82
+ )
83
+ else
84
+ index = index.to_s
85
+ name = index.chomp('?')
86
+ key = name.to_sym
87
+ return @options[key] if @options.key?(key)
88
+
89
+ @options[key] = if index =~ /\?$/
90
+ name.size == 1 ? option_letter_flag(name) : option_flag(name)
91
+ else
92
+ name.size == 1 ? option_letter_value(name) : option_value(name)
93
+ end
94
+ end
95
+ end
96
+
97
+ ### Access to the underlying commandline "ARGV".
98
+ ### This will show what is yet to be processed.
99
+ def instance_delegate ; @argv ; end
100
+
101
+ private
102
+
103
+ ### New Commandline. Takse a single argument
104
+ ### which can be a "shell" string, or an array
105
+ ### of shell arguments, like ARGV. If none
106
+ ### is given it defaults to ARGV.
107
+ def initialize(argv=ARGV)
108
+ case argv
109
+ when String
110
+ @argv = Shellwords.shellwords(argv)
111
+ else
112
+ @argv = argv.dup
113
+ end
114
+ @arguments = []
115
+ @options = {}
116
+
117
+ object_class.runmodes.each do |name|
118
+ self[name]
119
+ end
120
+ end
121
+
122
+ ###
123
+ #def initialize(argv=ARGV)
124
+ # if Hash===argv
125
+ # argv.each{ |k,v| send("#{k}=", v) }
126
+ # else
127
+ # self.class.runmodes.each do |name|
128
+ # option(name)
129
+ # end
130
+ # end
131
+ #end
132
+
133
+ ### Routes to #[].
134
+ def method_missing(name, *args)
135
+ super unless args.empty?
136
+ case name.to_s
137
+ when /\=$/
138
+ super
139
+ else
140
+ self[name]
141
+ end
142
+ end
143
+
144
+ ### Helper method. This can be replaceb by
145
+ ### String#index when it supports blocks.
146
+ ### Err.. when will that be Matz?
147
+ def index(array, &block)
148
+ find = array.find(&block)
149
+ array.index(find)
150
+ end
151
+
152
+ ### Parse a flag option.
153
+ def option_flag(name)
154
+ p name
155
+ o = "--#{name}"
156
+ i = index(@argv){ |e| e =~ /^#{o}[=]?/ }
157
+ return false unless i
158
+ raise ArgumentError if @argv[i] =~ /=/
159
+ @argv[i] = nil
160
+ return true
161
+ end
162
+
163
+ ### Parse a value option.
164
+ def option_value(name)
165
+ o = "--#{name}"
166
+ i = index(@argv){ |e| e =~ /^#{o}[=]?/ }
167
+ return false unless i
168
+
169
+ if @argv[i] =~ /=/
170
+ key, val = *@argv[i].split('=')
171
+ argv[i] = nil
172
+ else
173
+ case @argv[i+1]
174
+ when nil, /^-/
175
+ raise ArgumentError
176
+ else
177
+ key = @argv[i]
178
+ val = @argv[i+1]
179
+ @argv[i,2] = nil
180
+ end
181
+ end
182
+ return val
183
+ end
184
+
185
+ ### Parse a single letter flag option.
186
+ def option_letter_flag(letter)
187
+ o = letter
188
+ i = index(@argv){ |e| e =~ /[-]\w+(#{o})\w*$/ }
189
+ if i
190
+ @argv[i] = @argv[i].gsub(o,'')
191
+ true
192
+ end
193
+ false
194
+ end
195
+
196
+ ### Parse a single letter value option.
197
+ def option_letter_value(letter)
198
+ o = letter
199
+ i = index(@argv){ |e| e =~ /#{o}(\=|$)/ }
200
+ return nil unless i
201
+ if @argv[i] =~ /=/
202
+ rest, val = argv[i].split('=')
203
+ @argv[i] = rest
204
+ else
205
+ case @argv[i+1]
206
+ when nil, /^-/
207
+ raise ArgumentError
208
+ else
209
+ val = argv[i+1]
210
+ @argv[i] = argv[i].gsub(o,'')
211
+ @argv[i+1] = nil
212
+ end
213
+ end
214
+ return val
215
+ end
216
+
217
+ end
218
+ end
219
+
@@ -0,0 +1,403 @@
1
+ warn "MultiCommand is under construction!"
2
+ exit 0
3
+
4
+ # = Clio::MultiCommand
5
+ #
6
+ # Base class with metaclass DSL for creating
7
+ # well defined, static command line interfaces.
8
+ #
9
+ # == Authors
10
+ #
11
+ # * Trans
12
+ #
13
+ # == Copying
14
+ #
15
+ # Copyright (c) 2005,2008 Thomas Sawyer
16
+ # Lesser GNU Public License
17
+ #
18
+ # This module is free software. You may use, modify, and/or
19
+ # redistribute this software under the same terms as Ruby.
20
+ #
21
+ # This program is distributed in the hope that it will be
22
+ # useful, but WITHOUT ANY WARRANTY; without even the implied
23
+ # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
24
+ # PURPOSE.
25
+
26
+ begin
27
+ require 'annotatable'
28
+ rescue LoadError => e
29
+ $stderr << "ERROR: Clio Multicommand depends on 'annotatable'."
30
+ exit 0
31
+ end
32
+
33
+ #require 'facets/argvector'
34
+ require 'clio/errors'
35
+
36
+ module Clio
37
+
38
+ # Here is an example of usage:
39
+ #
40
+ # # General Options
41
+ #
42
+ # # Build Subcommand
43
+ #
44
+ # class MyMultiCommand < Clio::MultiCommand
45
+ #
46
+ # cmd "save to file"
47
+ #
48
+ # opt :file do
49
+ # desc "very useful option"
50
+ # type :path,
51
+ # flag :f,
52
+ # end
53
+ #
54
+ # def save(args, opts)
55
+ #
56
+ # end
57
+ #
58
+ # def fallback(opts)
59
+ # # if no subcommand
60
+ # end
61
+ # end
62
+ #
63
+ # MyMultiCommand.start
64
+ #
65
+ class MultiCommand
66
+
67
+ include Annotatable
68
+
69
+ annotation :cmd
70
+
71
+ annotation :opt
72
+
73
+ def self.method_added(name)
74
+ anns = self.class.annotations(name)
75
+ desc = anns[:cmd]
76
+ @options << Option.new(name, @_desc, @_opts)
77
+ @_opt = nil
78
+ end
79
+ end
80
+
81
+
82
+
83
+
84
+
85
+ # Include this in your dispatch command if you want
86
+ # all options to be traeted the same.
87
+
88
+ module UniversalOptions
89
+ end
90
+
91
+ #
92
+
93
+ def self.option_arity(arity_hash=nil)
94
+ if arity_hash
95
+ (@option_arity ||= {}).merge!(arity_hash)
96
+ end
97
+ @option_arity
98
+ end
99
+
100
+ #
101
+
102
+ def self.start(line=nil)
103
+ cargs = Argvector.new(line || ARGV, option_arity)
104
+ pre = cargs.preoptions
105
+
106
+ if instance_method(:call).arity == 0 #is_a?(SingleCommand)
107
+ args, opts = *cargs.parameters
108
+ new(args, opts).call
109
+ else
110
+ subc, args, opts = *cargs.subcommand
111
+ if self < UniversalOptions
112
+ new(pre, opts).call(subc, args, opts)
113
+ else
114
+ new(pre).call(subc, args, opts)
115
+ end
116
+ end
117
+ end
118
+
119
+ # Command Arguments (for single commands).
120
+
121
+ attr :arguments
122
+
123
+ # Command options. For dispatch commands these are the pre-options.
124
+
125
+ attr :options
126
+
127
+ # For dispatchers, this is a convenience method for creating subcommands.
128
+
129
+ def self.subcommand(name, command_class, options=nil)
130
+ options ||= {}
131
+ if options[:no_merge]
132
+ file, line = __FILE__, __LINE__+1
133
+ code = %{
134
+ def #{name}(args, opts)
135
+ #{command_class}.new(args, opts).call
136
+ end
137
+ }
138
+ else
139
+ file, line = __FILE__, __LINE__+1
140
+ code = %{
141
+ def #{name}(args, opts)
142
+ opts.merge(options)
143
+ #{command_class}.new(args, opts).call
144
+ end
145
+ }
146
+ end
147
+ class_eval(code, file, line)
148
+ end
149
+
150
+ private
151
+
152
+ #
153
+
154
+ def initialize(*args)
155
+ @arguments = []
156
+ @options = {}
157
+
158
+ opts, args = *args.partition{ |e| Hash===e }
159
+ #TEST("options should all be hashes"){ ! opts.all?{ |e| Hash===e }
160
+ initialize_arguments(*args)
161
+ initialize_options(*opts)
162
+ end
163
+
164
+ #
165
+
166
+ def initialize_arguments(*arguments)
167
+ @arguments.concat(arguments)
168
+ end
169
+
170
+ #
171
+
172
+ def initialize_options(*options)
173
+ options = options.inject{ |h,o| h.merge(o) }
174
+ begin
175
+ opt, val = nil, nil
176
+ options.each do |opt, val|
177
+ opt = opt.gsub('-','_')
178
+ send("#{opt}=", val)
179
+ end
180
+ rescue NoMethodError
181
+ option_missing(opt, val)
182
+ end
183
+ @options.update(options)
184
+ end
185
+
186
+ public
187
+
188
+ # For a single command (ie. a subcommand) override #call with arity=0.
189
+
190
+ def call(cmd=nil, *args)
191
+ opts = Hash==args.last ? args.pop : {}
192
+ #TEST("options should all be hashes"){ ! opts.all?{ |e| Hash===e }
193
+ #cmd = :default if cmd.nil?
194
+ if cmd.nil?
195
+ default
196
+ else
197
+ begin
198
+ # FIXME: rename call to [] ?
199
+ raise NameError if cmd == 'call'
200
+ raise NameError unless commands.include?(cmd.to_sym)
201
+ subcommand = method(cmd)
202
+ parameters = [args, opts]
203
+ rescue NameError
204
+ subcommand = method(:command_missing)
205
+ parameters = [cmd, args, opts]
206
+ end
207
+ if subcommand.arity < 0
208
+ subcommand.call(*parameters[0..subcommand.arity])
209
+ else
210
+ subcommand.call(*parameters[0,subcommand.arity])
211
+ end
212
+ end
213
+ end
214
+
215
+ # Display help message.
216
+ # The one provided is just a very limited dummy routine.
217
+
218
+ # def help
219
+ # puts "USAGE #{File.basename($0)} [options]"
220
+ # puts "\nOptions:"
221
+ # options = self.class.instance_methods(false)
222
+ # options = options - Command.instance_methods(true)
223
+ # options = options.select{ |m| m.to_s =~ /=$/ }
224
+ # options.each do |opt|
225
+ # puts " --#{opt.to_s.chomp('=')}"
226
+ # end
227
+ # end
228
+
229
+ private
230
+
231
+ # Override default to provide non-subcommand functionality.
232
+
233
+ def default; end
234
+
235
+ # TODO: Add "if no setter method".
236
+ def commands
237
+ @_commands ||= (
238
+ cmds = self.class.instance_methods(true) - Command.instance_methods(true)
239
+ cmds.select{ |c| c !~ /\W/ }
240
+ cmds.collect{ |c| c.to_sym }
241
+ )
242
+ end
243
+
244
+ #
245
+
246
+ def command_missing(cmd, args, opt)
247
+ raise NoCommandError.new(cmd, args << opt)
248
+ end
249
+
250
+ #
251
+
252
+ def option_missing(opt, arg=nil)
253
+ raise NoOptionError.new(opt)
254
+ end
255
+
256
+ end
257
+
258
+ # Temporary backward compatability.
259
+ MasterCommand = Command
260
+
261
+ end
262
+
263
+
264
+ module Console #:nodoc:
265
+ # For backward compatibility.
266
+ Command = CLI::Command
267
+ end
268
+
269
+
270
+ # SCRAP CODE FOR REFERENCE TO POSSIBLE ADD FUTURE FEATURES
271
+
272
+ =begin
273
+
274
+ # We include a module here so you can define your own help
275
+ # command and call #super to utilize this one.
276
+
277
+ module Help
278
+
279
+ def help
280
+ opts = help_options
281
+ s = ""
282
+ s << "#{File.basename($0)}\n\n"
283
+ unless opts.empty?
284
+ s << "OPTIONS\n"
285
+ s << help_options
286
+ s << "\n"
287
+ end
288
+ s << "COMMANDS\n"
289
+ s << help_commands
290
+ puts s
291
+ end
292
+
293
+ private
294
+
295
+ def help_commands
296
+ help = self.class.help
297
+ bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
298
+ lines = []
299
+ help.each { |cmd, str|
300
+ cmd = cmd.to_s
301
+ if cmd !~ /^_/
302
+ lines << " " + cmd + (" " * (bufs - cmd.size)) + str
303
+ end
304
+ }
305
+ lines.join("\n")
306
+ end
307
+
308
+ def help_options
309
+ help = self.class.help
310
+ bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
311
+ lines = []
312
+ help.each { |cmd, str|
313
+ cmd = cmd.to_s
314
+ if cmd =~ /^_/
315
+ lines << " " + cmd.gsub(/_/,'-') + (" " * (bufs - cmd.size)) + str
316
+ end
317
+ }
318
+ lines.join("\n")
319
+ end
320
+
321
+ module ClassMethods
322
+
323
+ def help( str=nil )
324
+ return (@help ||= {}) unless str
325
+ @current_help = str
326
+ end
327
+
328
+ def method_added( meth )
329
+ if @current_help
330
+ @help ||= {}
331
+ @help[meth] = @current_help
332
+ @current_help = nil
333
+ end
334
+ end
335
+
336
+ end
337
+
338
+ end
339
+
340
+ include Help
341
+ extend Help::ClassMethods
342
+
343
+ =end
344
+
345
+ =begin
346
+
347
+ # Provides a very basic usage help string.
348
+ #
349
+ # TODO Add support for __options.
350
+ def usage
351
+ str = []
352
+ public_methods(false).sort.each do |meth|
353
+ meth = meth.to_s
354
+ case meth
355
+ when /^_/
356
+ opt = meth.sub(/^_+/, '')
357
+ meth = method(meth)
358
+ if meth.arity == 0
359
+ str << (opt.size > 1 ? "[--#{opt}]" : "[-#{opt}]")
360
+ elsif meth.arity == 1
361
+ str << (opt.size > 1 ? "[--#{opt} value]" : "[-#{opt} value]")
362
+ elsif meth.arity > 0
363
+ v = []; meth.arity.times{ |i| v << 'value' + (i + 1).to_s }
364
+ str << (opt.size > 1 ? "[--#{opt} #{v.join(' ')}]" : "[-#{opt} #{v.join(' ')}]")
365
+ else
366
+ str << (opt.size > 1 ? "[--#{opt} *values]" : "[-#{opt} *values]")
367
+ end
368
+ when /=$/
369
+ opt = meth.chomp('=')
370
+ str << (opt.size > 1 ? "[--#{opt} value]" : "[-#{opt} value]")
371
+ when /!$/
372
+ opt = meth.chomp('!')
373
+ str << (opt.size > 1 ? "[--#{opt}]" : "[-#{opt}]")
374
+ end
375
+ end
376
+ return str.join(" ")
377
+ end
378
+
379
+ #
380
+
381
+ def self.usage_class(usage)
382
+ c = Class.new(self)
383
+ argv = Shellwords.shellwords(usage)
384
+ argv.each_with_index do |name, i|
385
+ if name =~ /^-/
386
+ if argv[i+1] =~ /^[(.*?)]/
387
+ c.class_eval %{
388
+ attr_accessor :#{name}
389
+ }
390
+ else
391
+ c.class_eval %{
392
+ attr_reader :#{name}
393
+ def #{name}! ; @#{name} = true ; end
394
+ }
395
+ end
396
+ end
397
+ end
398
+ return c
399
+ end
400
+
401
+ end
402
+
403
+ =end