clio 0.0.1 → 0.2.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.
Files changed (181) hide show
  1. data/CHANGES +66 -0
  2. data/MANIFEST +48 -169
  3. data/README +13 -17
  4. data/RELEASE +8 -0
  5. data/VERSION +1 -0
  6. data/lib/clio/buffer.rb +93 -0
  7. data/lib/clio/commandable.rb +71 -69
  8. data/lib/clio/commandline.rb +429 -230
  9. data/lib/clio/consoleutils.rb +0 -16
  10. data/lib/clio/facets/kernel.rb +13 -0
  11. data/lib/clio/facets/string.rb +25 -0
  12. data/lib/clio/layout.rb +14 -0
  13. data/lib/clio/layout/flow.rb +1 -0
  14. data/lib/clio/layout/line.rb +23 -0
  15. data/lib/clio/layout/list.rb +33 -0
  16. data/lib/clio/layout/split.rb +122 -0
  17. data/lib/clio/layout/stack.rb +17 -0
  18. data/lib/clio/layout/table.rb +46 -0
  19. data/lib/clio/progressbar.rb +189 -210
  20. data/lib/clio/string.rb +116 -109
  21. data/lib/clio/usage.rb +184 -0
  22. data/lib/clio/usage/argument.rb +84 -0
  23. data/lib/clio/usage/command.rb +440 -0
  24. data/lib/clio/usage/interface.rb +122 -0
  25. data/lib/clio/usage/main.rb +165 -0
  26. data/lib/clio/usage/option.rb +251 -0
  27. data/lib/clio/usage/parser.rb +191 -0
  28. data/lib/clio/usage/signature.rb +55 -0
  29. data/meta/abstract +3 -0
  30. data/meta/authors +1 -0
  31. data/meta/created +1 -0
  32. data/meta/homepage +1 -0
  33. data/meta/license +1 -0
  34. data/meta/repository +1 -0
  35. data/meta/summary +1 -0
  36. data/spec/commandline/autousage.rd +56 -0
  37. data/spec/commandline/bracket.rd +64 -0
  38. data/spec/commandline/completion.rd +38 -0
  39. data/spec/commandline/define.rd +44 -0
  40. data/spec/commandline/method.rd +60 -0
  41. data/spec/commandline/parse.rd +72 -0
  42. data/spec/commandline/scenario.rd +81 -0
  43. data/spec/commandline/subclass.rd +54 -0
  44. data/spec/string/unit.rd +58 -0
  45. data/spec/usage/define.rd +44 -0
  46. data/spec/usage/parse.rd +47 -0
  47. metadata +65 -196
  48. data/HISTORY +0 -11
  49. data/METADATA +0 -18
  50. data/NEWS +0 -10
  51. data/admin/config/reap.yaml +0 -30
  52. data/admin/depot/commandline.rb +0 -219
  53. data/admin/depot/multicommand.rb +0 -403
  54. data/admin/depot/test_multicommand.rb +0 -40
  55. data/admin/log/notes.xml +0 -28
  56. data/admin/log/stats.html +0 -25
  57. data/admin/log/syntax.log +0 -0
  58. data/admin/log/testunit.log +0 -16
  59. data/admin/pack/clio-0.0.1.gem +0 -0
  60. data/admin/share/reap/example.rb +0 -7
  61. data/admin/temps/lib/clio/about.rb.erb +0 -4
  62. data/lib/clio/command.rb +0 -296
  63. data/lib/clio/option.rb +0 -36
  64. data/lib/clio/runmode.rb +0 -126
  65. data/test/test_command.rb +0 -42
  66. data/test/test_commandline.rb +0 -83
  67. data/vendor/Console/Console.cpp +0 -1203
  68. data/vendor/Console/Console.rdoc +0 -690
  69. data/vendor/Console/Console_ANSI.rdoc +0 -302
  70. data/vendor/Console/HISTORY.txt +0 -7
  71. data/vendor/Console/INSTALL.txt +0 -18
  72. data/vendor/Console/Makefile +0 -162
  73. data/vendor/Console/README.txt +0 -26
  74. data/vendor/Console/doc/classes/Win32.html +0 -115
  75. data/vendor/Console/doc/classes/Win32/Console.html +0 -650
  76. data/vendor/Console/doc/classes/Win32/Console.src/M000001.html +0 -31
  77. data/vendor/Console/doc/classes/Win32/Console.src/M000002.html +0 -23
  78. data/vendor/Console/doc/classes/Win32/Console.src/M000003.html +0 -23
  79. data/vendor/Console/doc/classes/Win32/Console.src/M000004.html +0 -27
  80. data/vendor/Console/doc/classes/Win32/Console.src/M000005.html +0 -23
  81. data/vendor/Console/doc/classes/Win32/Console.src/M000006.html +0 -28
  82. data/vendor/Console/doc/classes/Win32/Console.src/M000007.html +0 -23
  83. data/vendor/Console/doc/classes/Win32/Console.src/M000008.html +0 -24
  84. data/vendor/Console/doc/classes/Win32/Console.src/M000009.html +0 -44
  85. data/vendor/Console/doc/classes/Win32/Console.src/M000010.html +0 -23
  86. data/vendor/Console/doc/classes/Win32/Console.src/M000011.html +0 -33
  87. data/vendor/Console/doc/classes/Win32/Console.src/M000012.html +0 -26
  88. data/vendor/Console/doc/classes/Win32/Console.src/M000013.html +0 -27
  89. data/vendor/Console/doc/classes/Win32/Console.src/M000014.html +0 -28
  90. data/vendor/Console/doc/classes/Win32/Console.src/M000015.html +0 -23
  91. data/vendor/Console/doc/classes/Win32/Console.src/M000016.html +0 -23
  92. data/vendor/Console/doc/classes/Win32/Console.src/M000017.html +0 -23
  93. data/vendor/Console/doc/classes/Win32/Console.src/M000018.html +0 -29
  94. data/vendor/Console/doc/classes/Win32/Console.src/M000019.html +0 -23
  95. data/vendor/Console/doc/classes/Win32/Console.src/M000020.html +0 -23
  96. data/vendor/Console/doc/classes/Win32/Console.src/M000021.html +0 -28
  97. data/vendor/Console/doc/classes/Win32/Console.src/M000022.html +0 -23
  98. data/vendor/Console/doc/classes/Win32/Console.src/M000023.html +0 -28
  99. data/vendor/Console/doc/classes/Win32/Console.src/M000024.html +0 -35
  100. data/vendor/Console/doc/classes/Win32/Console.src/M000025.html +0 -28
  101. data/vendor/Console/doc/classes/Win32/Console.src/M000026.html +0 -28
  102. data/vendor/Console/doc/classes/Win32/Console.src/M000027.html +0 -28
  103. data/vendor/Console/doc/classes/Win32/Console.src/M000028.html +0 -31
  104. data/vendor/Console/doc/classes/Win32/Console.src/M000029.html +0 -23
  105. data/vendor/Console/doc/classes/Win32/Console.src/M000030.html +0 -23
  106. data/vendor/Console/doc/classes/Win32/Console.src/M000031.html +0 -23
  107. data/vendor/Console/doc/classes/Win32/Console.src/M000032.html +0 -27
  108. data/vendor/Console/doc/classes/Win32/Console.src/M000033.html +0 -27
  109. data/vendor/Console/doc/classes/Win32/Console.src/M000034.html +0 -25
  110. data/vendor/Console/doc/classes/Win32/Console/ANSI.html +0 -103
  111. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.html +0 -220
  112. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000035.html +0 -32
  113. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000036.html +0 -205
  114. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000037.html +0 -40
  115. data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000038.html +0 -25
  116. data/vendor/Console/doc/classes/Win32/Console/API.html +0 -758
  117. data/vendor/Console/doc/classes/Win32/Console/API.src/M000039.html +0 -27
  118. data/vendor/Console/doc/classes/Win32/Console/API.src/M000040.html +0 -27
  119. data/vendor/Console/doc/classes/Win32/Console/API.src/M000041.html +0 -27
  120. data/vendor/Console/doc/classes/Win32/Console/API.src/M000042.html +0 -32
  121. data/vendor/Console/doc/classes/Win32/Console/API.src/M000043.html +0 -32
  122. data/vendor/Console/doc/classes/Win32/Console/API.src/M000044.html +0 -28
  123. data/vendor/Console/doc/classes/Win32/Console/API.src/M000045.html +0 -26
  124. data/vendor/Console/doc/classes/Win32/Console/API.src/M000046.html +0 -26
  125. data/vendor/Console/doc/classes/Win32/Console/API.src/M000047.html +0 -27
  126. data/vendor/Console/doc/classes/Win32/Console/API.src/M000048.html +0 -30
  127. data/vendor/Console/doc/classes/Win32/Console/API.src/M000049.html +0 -29
  128. data/vendor/Console/doc/classes/Win32/Console/API.src/M000050.html +0 -27
  129. data/vendor/Console/doc/classes/Win32/Console/API.src/M000051.html +0 -28
  130. data/vendor/Console/doc/classes/Win32/Console/API.src/M000052.html +0 -30
  131. data/vendor/Console/doc/classes/Win32/Console/API.src/M000053.html +0 -27
  132. data/vendor/Console/doc/classes/Win32/Console/API.src/M000054.html +0 -29
  133. data/vendor/Console/doc/classes/Win32/Console/API.src/M000055.html +0 -29
  134. data/vendor/Console/doc/classes/Win32/Console/API.src/M000056.html +0 -28
  135. data/vendor/Console/doc/classes/Win32/Console/API.src/M000057.html +0 -27
  136. data/vendor/Console/doc/classes/Win32/Console/API.src/M000058.html +0 -47
  137. data/vendor/Console/doc/classes/Win32/Console/API.src/M000059.html +0 -32
  138. data/vendor/Console/doc/classes/Win32/Console/API.src/M000060.html +0 -47
  139. data/vendor/Console/doc/classes/Win32/Console/API.src/M000061.html +0 -34
  140. data/vendor/Console/doc/classes/Win32/Console/API.src/M000062.html +0 -32
  141. data/vendor/Console/doc/classes/Win32/Console/API.src/M000063.html +0 -32
  142. data/vendor/Console/doc/classes/Win32/Console/API.src/M000064.html +0 -35
  143. data/vendor/Console/doc/classes/Win32/Console/API.src/M000065.html +0 -26
  144. data/vendor/Console/doc/classes/Win32/Console/API.src/M000066.html +0 -27
  145. data/vendor/Console/doc/classes/Win32/Console/API.src/M000067.html +0 -29
  146. data/vendor/Console/doc/classes/Win32/Console/API.src/M000068.html +0 -27
  147. data/vendor/Console/doc/classes/Win32/Console/API.src/M000069.html +0 -27
  148. data/vendor/Console/doc/classes/Win32/Console/API.src/M000070.html +0 -28
  149. data/vendor/Console/doc/classes/Win32/Console/API.src/M000071.html +0 -27
  150. data/vendor/Console/doc/classes/Win32/Console/API.src/M000072.html +0 -26
  151. data/vendor/Console/doc/classes/Win32/Console/API.src/M000073.html +0 -27
  152. data/vendor/Console/doc/classes/Win32/Console/API.src/M000074.html +0 -31
  153. data/vendor/Console/doc/classes/Win32/Console/API.src/M000075.html +0 -27
  154. data/vendor/Console/doc/classes/Win32/Console/API.src/M000076.html +0 -32
  155. data/vendor/Console/doc/classes/Win32/Console/API.src/M000077.html +0 -27
  156. data/vendor/Console/doc/classes/Win32/Console/API.src/M000078.html +0 -32
  157. data/vendor/Console/doc/classes/Win32/Console/API.src/M000079.html +0 -32
  158. data/vendor/Console/doc/classes/Win32/Console/API.src/M000080.html +0 -32
  159. data/vendor/Console/doc/classes/Win32/Console/Constants.html +0 -360
  160. data/vendor/Console/doc/created.rid +0 -1
  161. data/vendor/Console/doc/files/Console_ANSI_rdoc.html +0 -407
  162. data/vendor/Console/doc/files/Console_cpp.html +0 -104
  163. data/vendor/Console/doc/files/Console_rdoc.html +0 -964
  164. data/vendor/Console/doc/files/lib/Win32/Console/ANSI_rb.html +0 -123
  165. data/vendor/Console/doc/files/lib/Win32/Console_rb.html +0 -297
  166. data/vendor/Console/doc/fr_class_index.html +0 -32
  167. data/vendor/Console/doc/fr_file_index.html +0 -31
  168. data/vendor/Console/doc/fr_method_index.html +0 -106
  169. data/vendor/Console/doc/index.html +0 -24
  170. data/vendor/Console/doc/rdoc-style.css +0 -172
  171. data/vendor/Console/extconf.rb +0 -18
  172. data/vendor/Console/lib/Term/ansicolor.rb +0 -76
  173. data/vendor/Console/lib/Win32/Console.rb +0 -970
  174. data/vendor/Console/lib/Win32/Console/ANSI.rb +0 -305
  175. data/vendor/Console/test/test_cursor.rb +0 -9
  176. data/vendor/Console/test/test_mouse.rb +0 -6
  177. data/vendor/Console/test/test_readinput.rb +0 -62
  178. data/vendor/Console/test/test_readoutput.rb +0 -52
  179. data/vendor/Console/test/test_sendevent.rb +0 -17
  180. data/vendor/Console/test/test_title.rb +0 -14
  181. data/vendor/Console/test/test_write.rb +0 -36
@@ -2,7 +2,7 @@ require 'clio/errors'
2
2
 
3
3
  module Clio
4
4
 
5
- # = Commandable mixin
5
+ # = Commandable Mixin
6
6
  #
7
7
  # The Commandable mixin is a very quick and and easy
8
8
  # way to make almost any class usable via a command
@@ -40,6 +40,8 @@ module Clio
40
40
  # Commandable also defines #command_missing and #option_missing,
41
41
  # which you can override to provide suitable results.
42
42
  #
43
+ # TODO: Maybe command_missing is redundant, and method_missing would suffice?
44
+ #
43
45
  module Commandable
44
46
 
45
47
  # Used to invoke the command.
@@ -50,7 +52,7 @@ module Clio
50
52
  # This is the fallback subcommand. Override this to provide
51
53
  # a fallback when no command is given on the commandline.
52
54
  def command_missing
53
- raise NoMethodError
55
+ raise NoCommandError
54
56
  end
55
57
 
56
58
  # Override option_missing if needed.
@@ -62,88 +64,88 @@ module Clio
62
64
  raise NoOptionError, opt
63
65
  end
64
66
 
65
- class << self
67
+ class << self
66
68
 
67
- def run(obj, argv=ARGV)
68
- args = parse(obj, argv)
69
- subcmd = args.shift
70
- if subcmd && !obj.respond_to?("#{subcmd}=")
71
- obj.send(subcmd, *args)
72
- else
73
- obj.command_missing
69
+ def run(obj, argv=ARGV)
70
+ args = parse(obj, argv)
71
+ subcmd = args.shift
72
+ if subcmd && !obj.respond_to?("#{subcmd}=")
73
+ obj.send(subcmd, *args)
74
+ else
75
+ obj.command_missing
76
+ end
74
77
  end
75
- end
76
78
 
77
- #def run(obj)
78
- # methname, args = *parse(obj)
79
- # meth = obj.method(methname)
80
- # meth.call(*args)
81
- #end
79
+ #def run(obj)
80
+ # methname, args = *parse(obj)
81
+ # meth = obj.method(methname)
82
+ # meth.call(*args)
83
+ #end
84
+
85
+ #
86
+ def parse(obj, argv)
87
+ case argv
88
+ when String
89
+ require 'shellwords'
90
+ argv = Shellwords.shellwords(argv)
91
+ else
92
+ argv = argv.dup
93
+ end
82
94
 
83
- #
84
- def parse(obj, argv)
85
- case argv
86
- when String
87
- require 'shellwords'
88
- argv = Shellwords.shellwords(argv)
89
- else
90
95
  argv = argv.dup
96
+ args, opts, i = [], {}, 0
97
+ while argv.size > 0
98
+ case opt = argv.shift
99
+ when /=/
100
+ parse_equal(obj, opt, argv)
101
+ when /^--/
102
+ parse_option(obj, opt, argv)
103
+ when /^-/
104
+ parse_flags(obj, opt, argv)
105
+ else
106
+ args << opt
107
+ end
108
+ end
109
+ return args
91
110
  end
92
111
 
93
- argv = argv.dup
94
- args, opts, i = [], {}, 0
95
- while argv.size > 0
96
- case opt = argv.shift
97
- when /=/
98
- parse_equal(obj, opt, argv)
99
- when /^--/
100
- parse_option(obj, opt, argv)
101
- when /^-/
102
- parse_flags(obj, opt, argv)
112
+ #
113
+ def parse_equal(obj, opt, argv)
114
+ if md = /^[-]*(.*?)=(.*?)$/.match(opt)
115
+ x, v = md[1], md[2]
103
116
  else
104
- args << opt
117
+ raise ArgumentError, "#{x}"
118
+ end
119
+ if obj.respond_to?("#{x}=")
120
+ # TODO: to_b if 'true' or 'false' ?
121
+ obj.send("#{x}=",v)
122
+ else
123
+ obj.option_missing(x, v) # argv?
105
124
  end
106
125
  end
107
- return args
108
- end
109
-
110
- #
111
- def parse_equal(obj, opt, argv)
112
- if md = /^[-]*(.*?)=(.*?)$/.match(opt)
113
- x, v = md[1], md[2]
114
- else
115
- raise ArgumentError, "#{x}"
116
- end
117
- if obj.respond_to?("#{x}=")
118
- # TODO: to_b if 'true' or 'false' ?
119
- obj.send("#{x}=",v)
120
- else
121
- obj.option_missing(x, v) # argv?
122
- end
123
- end
124
-
125
- #
126
- def parse_option(obj, opt, argv)
127
- x = opt[2..-1]
128
- if obj.respond_to?("#{x}=")
129
- obj.send("#{x}=",true)
130
- else
131
- obj.option_missing(x, argv)
132
- end
133
- end
134
126
 
135
- #
136
- def parse_flags(obj, opt, args)
137
- x = opt[1..-1]
138
- c = 0
139
- x.split(//).each do |k|
140
- if obj.respond_to?("#{k}=")
141
- obj.send("#{k}=",true)
127
+ #
128
+ def parse_option(obj, opt, argv)
129
+ x = opt[2..-1]
130
+ if obj.respond_to?("#{x}=")
131
+ obj.send("#{x}=",true)
142
132
  else
143
133
  obj.option_missing(x, argv)
144
134
  end
145
135
  end
146
- end
136
+
137
+ #
138
+ def parse_flags(obj, opt, args)
139
+ x = opt[1..-1]
140
+ c = 0
141
+ x.split(//).each do |k|
142
+ if obj.respond_to?("#{k}=")
143
+ obj.send("#{k}=",true)
144
+ else
145
+ obj.option_missing(x, argv)
146
+ end
147
+ end
148
+ end
147
149
 
148
150
  end #class << self
149
151
 
@@ -1,275 +1,474 @@
1
- require 'shellwords'
2
- require 'facets/kernel/object_class'
3
- require 'facets/array/indexable'
1
+ require 'clio/facets/kernel' # for deep_copy
2
+ require 'clio/usage'
3
+ #require 'shellwords'
4
4
 
5
5
  module Clio
6
- ### = Commandline
7
- ###
8
- ### What a strange thing is the Clio Commandline.
9
- ### An entity unknown until put upon.
10
- ###
11
- ### cmd = Clio::Commandline.new("--force copy --file try.rb")
12
- ### cmd.option_alias(:f?, :force?)
13
- ### cmd.option_alias(:o, :file)
14
- ###
15
- ### cmd.file #=> 'try.rb'
16
- ### cmd.force? #=> true
17
- ### cmd.o #=> 'try.rb'
18
- ### cmd.f? #=> true
19
- ###
20
- ### TODO: Allow option setter methods (?)
21
- ### TODO: Allow a hash as argument to initialize (?)
6
+
7
+ # = Commandline
8
+ #
9
+ # Clio's Commandline class is a very versitile command line parser.
10
+ # A Command can be used either declaritively, defining usage
11
+ # and help information upfront; or lazily, whereby information
12
+ # about usage is built-up as the commandline actually gets use in
13
+ # one's program; or you can use a mixture of the two.
14
+ #
15
+ # = Underlying Notation
16
+ #
17
+ # As you might expect the fluent notation can be broken down into
18
+ # block notation.
19
+ #
20
+ # cli = Clio::Command.new
21
+ # cli.usage do
22
+ # option(:verbose, :v) do
23
+ # help('verbose output')
24
+ # end
25
+ # option(:quiet, :q) do
26
+ # help('run silently')
27
+ # xor(:V)
28
+ # end
29
+ # command(:document) do
30
+ # help('generate documentation')
31
+ # option(:output, :o) do
32
+ # type('FILE')
33
+ # help('output directory')
34
+ # end
35
+ # argument('files') do
36
+ # multiple
37
+ # end
38
+ # end
39
+ # end
40
+ #
41
+ # Clearly block notation is DRY and easier to read, but fluent
42
+ # notation is important to have because it allows the Commandline
43
+ # object to be passed around as an argument and modified easily.
44
+ #
45
+ # == Method Notation
46
+ #
47
+ # This notation is very elegant, but slightly more limited in scope.
48
+ # For instance, subcommands that use non-letter characters, such as ':',
49
+ # can not be described with this notation.
50
+ #
51
+ # cli.usage.document('*files', '--output=FILE -o')
52
+ # cli.usage('--verbose -V','--quiet -q')
53
+ #
54
+ # cli.usage.help(
55
+ # 'document' , 'generate documentation',
56
+ # 'validate' , 'run tests or specifications',
57
+ # '--verbose' , 'verbose output',
58
+ # '--quiet' , 'run siltently'
59
+ # )
60
+ #
61
+ # cli.usage.document.help(
62
+ # '--output', 'output directory'
63
+ # 'file*', 'files to document'
64
+ # )
65
+ #
66
+ # This notation is slightly more limited in scope... so...
67
+ #
68
+ # cli.usage.command(:document, '--output=FILE -o', 'files*')
69
+ #
70
+ # == Bracket Shorthand Notation
71
+ #
72
+ # The core notation can be somewhat verbose. As a further convenience
73
+ # commandline usage can be defined with a brief <i>bracket shorthand</i>.
74
+ # This is especailly useful when the usage is simple and statically defined.
75
+ #
76
+ # cli.usage['document']['--output=FILE -o']['FILE*']
77
+ #
78
+ # Using a little creativity to improve readabilty we can convert the
79
+ # whole example from above using this notation.
80
+ #
81
+ # cli.usage['--verbose -V', 'verbose output' ] \
82
+ # ['--quiet -q', 'run silently' ] \
83
+ # ['document', 'generate documention' ] \
84
+ # [ '--output=FILE -o', 'output directory' ] \
85
+ # [ 'FILE*', 'files to document' ]
86
+ #
87
+ # Alternately the help information can be left out and defined in
88
+ # a seprate set of usage calls.
89
+ #
90
+ # cli.usage['--verbose -V']['--quiet -q'] \
91
+ # ['document']['--output=FILE -o']['FILE*']
92
+ #
93
+ # cli.usage.help(
94
+ # 'document' , 'generate documentation',
95
+ # 'validate' , 'run tests or specifications',
96
+ # '--verbose' , 'verbose output',
97
+ # '--quiet' , 'run siltently'
98
+ # )
99
+ #
100
+ # cli.usage['document'].help(
101
+ # '--output', 'output directory'
102
+ # 'FILE', 'files to docment'
103
+ # )
104
+ #
105
+ # A little more verbose, but a bit more intutive.
106
+ #
107
+ # == Combining Notations
108
+ #
109
+ # Since the various notations all translate to same underlying
110
+ # structures, they can be mixed and matched as suites ones taste.
111
+ # For example we could mix Method Notation and Bracket Notation.
112
+ #
113
+ # cli.usage.document['--output=FILE -o']['file*']
114
+ # cli.usage['--verbose -V']['--quiet -q']
115
+ #
116
+ # The important thing to keep in mind when doing this is what is
117
+ # returned by each type of usage call.
118
+ #
119
+ # == Commandline Parsing
120
+ #
121
+ # With usage in place, call the +parse+ method to process the
122
+ # actual commandline.
123
+ #
124
+ # cli.parse
125
+ #
126
+ # If no command arguments are passed to +parse+, ARGV is used.
127
+ #
128
+ #--
129
+ # == Passive Parsing
130
+ #
131
+ # The Command class allows you to declare as little or as
132
+ # much of the commandline interface upfront as is suitable to
133
+ # your application. When using the commandline object, if not
134
+ # already defined, options will be lazily created. For example:
135
+ #
136
+ # cli = Clio::Commandline.new('--force')
137
+ # cli.force? #=> true
138
+ #
139
+ # Commandline sees that you expect a '--force' flag to be an
140
+ # acceptable option. So it will call cli.usage.option('force')
141
+ # behind the scenes before trying to determine the actual value
142
+ # per the content of the command line. You can add aliases as
143
+ # parameters to this call as well.
144
+ #
145
+ # cli = Clio::Commandline.new('-f')
146
+ # cli.force?(:f) #=> true
147
+ #
148
+ # Once set, you do not need to specify the alias again:
149
+ #
150
+ # cli.force? #=> true
151
+ #
152
+ # With the exception of help information, this means you can
153
+ # generally just use a commandline as needed without having
154
+ # to declare anything upfront.
155
+ #++
156
+ #
157
+ # == Usage Cache
158
+ #
159
+ # Lastly, Commandline provides a simple means to cache usage
160
+ # information to a configuration file, which then can be used
161
+ # again the next time the same command is used. This allows
162
+ # Commandline to provide high-performane tab completion.
163
+ #
164
+ #--
165
+ # == Coming Soon
166
+ #
167
+ # In the future Commandline will be able to generate Manpage
168
+ # templates.
169
+ #
170
+ # TODO: Allow option setter methods (?)
171
+ # TODO: Allow a hash as argument to initialize (?)
172
+ #++
173
+
22
174
  class Commandline
23
- instance_methods.each{ |m| private m if m !~ /^(__|instance_|object_|send$|inspect$)/ }
24
-
25
- ### Splits a raw command line into two smaller
26
- ### ones. The first including only options
27
- ### upto the first non-option argument. This
28
- ### makes quick work of separating a subcommand
29
- ### from the options for a main command.
30
- # TODO: Rename this method.
31
- def self.gerrymander(argv=ARGV)
32
- if String===argv
33
- argv = Shellwords.shellwords(argv)
34
- end
35
- sub = argv.find{ |x| x !~ /^[-]/ }
36
- idx = argv.index(sub)
37
- opts = argv[0...idx]
38
- scmd = argv[idx..-1]
39
- return opts, scmd
175
+
176
+ #
177
+ instance_methods.each do |m|
178
+ private m if m !~ /^(__|instance_|object_|send$|class$|inspect$|respond_to\?$)/
40
179
  end
41
180
 
42
- ### Define an option attibute.
43
- ### While commandline can be used without
44
- ### pre-declartion of support options
45
- ### doding so allows for creating option
46
- ### aliases. Eg. --quiet and -q.
47
- def self.attr(name, *aliases)
48
- (@predefined_options ||= []) << [name, *aliases]
49
-
50
- name = name.to_s
51
- if name =~ /\?$/
52
- key = name.chomp('?')
53
- #attr_writer name
54
- module_eval "def #{key}?; @#{key} ; end"
55
- aliases.each do |alt|
56
- alt = alt.to_s.chomp('?')
57
- alias_method("#{alt}?", "#{key}?")
58
- #alias_method("#{alt}=", "#{name}=")
59
- end
60
- else
61
- attr_reader name
62
- #module_eval "def #{name}; self[:#{name}] ; end"
63
- aliases.each do |alt|
64
- #alt = alt.to_s.chomp('?') # TODO: raise error ?
65
- alias_method("#{alt}" , "#{name}")
66
- #alias_method("#{alt}=", "#{name}=")
67
- end
181
+ class << self
182
+
183
+ #def inherited(subclass)
184
+ #p usage.to_s
185
+ #p subclass.usage.to_s
186
+ # subclass.usage = self.usage.clone #deep_copy
187
+ #p subclass.usage.to_s
188
+ # end
189
+
190
+ # Command usage.
191
+ def usage
192
+ @usage ||= (
193
+ if ancestors[1] < Commandline
194
+ ancestors[1].usage.dup
195
+ else
196
+ Usage.new
197
+ end
198
+ )
199
+ end
200
+
201
+ def usage=(u)
202
+ raise ArgumentError unless u <= Usage
203
+ @usage = u
204
+ end
205
+
206
+ # if ancestors[1] < Command
207
+ # @usage = ancestors[0].usage.deep_copy
208
+ # else
209
+ # @usage = Usage.new
210
+ # end
211
+
212
+ #
213
+ def subcommand(name, help=nil, &block)
214
+ usage.subcommand(name, help, &block)
215
+ end
216
+ alias_method :command, :subcommand
217
+ alias_method :cmd, :subcommand
218
+
219
+ #
220
+ def option(name, *aliases, &block)
221
+ usage.option(name, *aliases, &block)
222
+ end
223
+ alias_method :switch, :option
224
+
225
+ #
226
+ def opt(label, help, &block)
227
+ usage.opt(label, help, &block)
228
+ end
229
+ alias_method :swt, :opt
230
+
231
+ #
232
+ def argument(*n_type, &block)
233
+ usage.argument(*n_type, &block)
68
234
  end
235
+
236
+ #
237
+ def help(string=nil)
238
+ usage.help(string)
239
+ end
240
+
241
+ #
242
+ #def arg(label, help, &block)
243
+ # usage.arg(label, help, &block)
244
+ #end
245
+
69
246
  end
70
247
 
71
- ### Returns a list of all pre-defined options.
72
- ### It does this by seaching class ancestry
73
- ### for instance_methods until it reaches the
74
- ### Commandline base class.
75
- ### TODO: Rename #runmodes method.
76
- ### TODO: Robust enough? Use an Inheritor instead?
77
- def self.predefined_options
78
- @predefined_options ||= []
79
- ancestor = ancestors[1]
80
- if ancestor > ::Clio::Commandline
81
- @predefined_options
248
+ # New Command.
249
+ def initialize(argv=nil, opts={}, &block)
250
+ argv_set(argv || ARGV)
251
+ #if opts[:usage]
252
+ # @usage = opts[:usage]
253
+ #else
254
+ # #@usage = load_cache
255
+ #end
256
+ if self.class == Commandline
257
+ @usage = Usage.new
82
258
  else
83
- @predefined_options | ancestor.predefined_options
259
+ @usage = self.class.usage #|| Usage.new #.deep_copy
84
260
  end
261
+ @usage.instance_eval(&block) if block
85
262
  end
86
263
 
87
- public
88
-
89
- ### This method provides the centralized means
90
- ### of accessing the options and arguments on
91
- ### the commandline.
92
- def [](index)
93
- case index
94
- when Integer
95
- @arguments[index] ||= (
96
- args = @argv.select{ |e| e !~ /^-/ }
97
- val = args[index]
98
- @argv.delete(args[index])
99
- val
100
- )
101
- else
102
- return send(index) if respond_to?(index)
103
- key = index.to_s.chomp('?')
104
- val = option_parse(index)
105
- instance_variable_set("@#{key}", val)
106
- (class << self; self; end).class_eval %{
107
- def #{index}; @#{key}; end
108
- }
109
- return val
264
+ #
265
+ def argv_set(argv)
266
+ # reset parser
267
+ @parser = nil
268
+ # convert to array if string
269
+ if String===argv
270
+ argv = Shellwords.shellwords(argv)
110
271
  end
272
+ # remove anything subsequent to '--'
273
+ if index = argv.index('--')
274
+ argv = argv[0...index]
275
+ end
276
+ @argv = argv
111
277
  end
112
278
 
113
- def shift!
114
- args = @argv.select{ |e| e !~ /^-/ }
115
- val = args.first
116
- @argv.delete(val)
117
- val
279
+ #
280
+ def cli
281
+ #parse unless @cli
282
+ @cli
118
283
  end
119
284
 
120
- ### Define an option alias. This adds en entry to
121
- ### the aliases hash, pointing new to a list of
122
- ### all aliases and the first entry on th list
123
- ### being the master key.
124
- def option_alias(new, old)
125
- self[old]
126
- key = old.to_s.chomp('?')
127
- val = option_parse(new)
128
- instance_variable_set("@#{key}", val) if val
129
- (class << self; self; end).class_eval do
130
- alias_method new, old
131
- end
285
+ #
286
+ #def usage(name=nil, &block)
287
+ # @usage ||= Usage.new(name)
288
+ # @usage.instance_eval(&block) if block
289
+ # @usage
290
+ #end
291
+
292
+ def usage
293
+ @usage
132
294
  end
133
295
 
134
- ### Access to the underlying commandline "ARGV".
135
- ### This will show what is yet to be processed.
136
- def instance_delegate ; @argv ; end
137
-
138
- ### Returns a hash of all options parsed.
139
- def instance_options
140
- h = {}
141
- ivs = instance_variables - ['@arguments','@argv']
142
- ivs.each do |iv|
143
- val = instance_variable_get(iv)
144
- h[iv.sub('@','').to_sym] = val if val
145
- end
146
- h
296
+ #
297
+ def to_s
298
+ usage.to_s
147
299
  end
148
300
 
149
- ### Returns a list of all arguments parsed.
150
- def instance_arguments
151
- @arguments
301
+ #
302
+ def to_s_help
303
+ usage.to_s_help
152
304
  end
153
305
 
154
- private
155
-
156
- ### New Commandline. Takse a single argument
157
- ### which can be a "shell" string, or an array
158
- ### of shell arguments, like ARGV. If none
159
- ### is given it defaults to ARGV.
160
- def initialize(argv=ARGV)
161
- case argv
162
- when String
163
- @argv = Shellwords.shellwords(argv)
164
- #when Hash
165
- # argv.each{ |k,v| send("#{k}=", v) }
166
- else
167
- @argv = argv.dup
168
- end
169
- @arguments = []
170
-
171
- # parse predefined options attributes.
172
- object_class.predefined_options.each do |modes|
173
- key = modes.first.to_s.chomp('?')
174
- modes.reverse.each do |i|
175
- val = option_parse(i)
176
- instance_variable_set("@#{key}", val) if val
177
- end
178
- end
306
+ #
307
+ def parse(argv=nil)
308
+ argv_set(argv) if argv
309
+ @cli = parser.parse
179
310
  end
180
311
 
181
- ### Routes to #[].
182
- def method_missing(name, *args)
183
- super unless args.empty?
184
- case name.to_s
185
- when /\=$/
186
- super
187
- else
188
- self[name]
189
- end
312
+ #
313
+ def parser
314
+ @parser ||= Usage::Parser.new(usage, @argv)
315
+ end
316
+
317
+ #
318
+ def [](i)
319
+ @cli[i]
190
320
  end
191
321
 
192
- def option_parse(index)
193
- index = index.to_s
194
- name = index.chomp('?')
195
- key = name.to_sym
322
+ #
323
+ def command ; cli.command ; end
324
+
325
+ #
326
+ def commands ; cli.commands ; end
327
+
328
+ #
329
+ def arguments ; cli.arguments ; end
330
+
331
+ #
332
+ def switches ; cli.options ; end
333
+
334
+ #
335
+ alias_method :options, :switches
336
+
337
+ # Parameters
338
+ #
339
+ def parameters ; cli.parameters ; end
196
340
 
197
- kind = name.size == 1 ? 'letter' : 'word'
198
- flag = index =~ /\?$/ ? 'flag' : 'value'
341
+ #
342
+ def to_a
343
+ cli.to_a
344
+ end
199
345
 
200
- send("option_#{kind}_#{flag}", key)
346
+ # Commandline fully valid?
347
+ #
348
+ def valid?
349
+ @cli.valid?
201
350
  end
202
351
 
203
- ### Parse a flag option.
204
- def option_word_flag(name)
205
- o = "--#{name}"
206
- i = @argv.index_of{ |e| e =~ /^#{o}[=]?/ }
207
- return false unless i
208
- raise ArgumentError if @argv[i] =~ /=/
209
- @argv.delete_at(i)
210
- return true
352
+ # TODO: adding '-' is best idea?
353
+ #
354
+ def completion(argv=nil)
355
+ argv_set(argv) if argv
356
+ @argv << "\t"
357
+ parse
358
+ @argv.pop
359
+ parser.errors[0][1].completion.collect{ |s| s.to_s }
360
+ #@argv.pop if @argv.last == '?'
361
+ #load_cache
362
+ #parse
211
363
  end
212
364
 
213
- ### Parse a value option.
214
- def option_word_value(name)
215
- o = "--#{name}"
216
- i = @argv.index_of{ |e| e =~ /^#{o}[=]?/ }
217
- return false unless i
365
+ #
366
+ #def load_cache
367
+ # if usage = Usage.load_cache
368
+ # @usage = usage
369
+ # end
370
+ #end
218
371
 
219
- if @argv[i] =~ /=/
220
- key, val = *@argv[i].split('=')
221
- argv[i] = nil
222
- else
223
- case @argv[i+1]
224
- when nil, /^-/
225
- raise ArgumentError
372
+ # Method missing provide passive usage and parsing.
373
+ #
374
+ # TODO: This reparses the commandline after every query.
375
+ # Need only parse if usage has change.
376
+ def method_missing(s, *a)
377
+ begin
378
+ s = s.to_s
379
+ case s
380
+ when /[=]$/
381
+ n = s.chomp('=')
382
+ usage.option(n).type(*a)
383
+ parse
384
+ res = @cli.options[n.to_sym]
385
+ when /[!]$/
386
+ n = s.chomp('!')
387
+ cmd = usage.commands[n.to_sym] || usage.command(n, *a)
388
+ res = parse
389
+ when /[?]$/
390
+ n = s.chomp('?')
391
+ u = usage.option(n, *a)
392
+ parse
393
+ res = @cli.options[u.key]
226
394
  else
227
- key = @argv[i]
228
- val = @argv[i+1]
229
- @argv.delete_at(i) # do it twice
230
- @argv.delete_at(i)
395
+ usage.option(s, *a)
396
+ parse
397
+ res = @cli.options[s.to_sym]
231
398
  end
399
+ rescue Usage::ParseError => e
400
+ res = nil
232
401
  end
233
- return val
402
+ return res
234
403
  end
235
404
 
236
- ### Parse a single letter flag option.
237
- def option_letter_flag(letter)
238
- o = letter
239
- i = @argv.index_of{ |e| e =~ /[-][^-]\w*(#{o})\w*$/ }
240
- if i
241
- @argv[i] = @argv[i].gsub(o.to_s,'')
242
- true
243
- end
244
- false
245
- end
405
+ end # class Commandline
246
406
 
247
- ### Parse a single letter value option.
248
- def option_letter_value(letter)
249
- o = letter
250
- i = @argv.index_of{ |e| e =~ /[-]\w*#{o}(\=|$)/ }
251
- return nil unless i
252
- if @argv[i] =~ /=/
253
- rest, val = argv[i].split('=')
254
- @argv[i] = rest
255
- else
256
- case @argv[i+1]
257
- when nil, /^-/
258
- raise ArgumentError
259
- else
260
- val = @argv[i+1]
261
- new = @argv[i].gsub(o.to_s,'')
262
- if new == '-'
263
- @argv.delete_at(i)
264
- else
265
- @argv[i] = new
266
- end
267
- @argv.delete_at(i+1)
268
- end
407
+ end # module Clio
408
+
409
+
410
+
411
+ =begin demo 1
412
+
413
+ cli = Clio::Commandline.new
414
+
415
+ cli.usage do
416
+ command(:document) do
417
+ help('generate documentation')
418
+ option(:output, :o) do
419
+ type('FILE')
420
+ help('output directory')
269
421
  end
270
- return val
271
422
  end
423
+ option(:verbose, :V) do
424
+ help('verbose output')
425
+ end
426
+ option(:quiet, :q) do
427
+ help('run silently')
428
+ xor(:verbose)
429
+ end
430
+ end
431
+
432
+ #p cli
433
+ puts
434
+ puts cli.to_s_help
435
+
436
+ =end
437
+
438
+ =begin demo 2
272
439
 
440
+ cli = Clio::Commandline.new('--verbose')
441
+
442
+ cli.usage do
443
+ cmd(:document, 'generate documentation') do
444
+ opt('--output=FILE -o', 'output directory')
445
+ end
446
+ opt('--verbose -V', 'verbose output')
447
+ opt('--quiet -q', 'run silently')
273
448
  end
274
- end
449
+
450
+ =end
451
+
452
+ =begin demo 3
453
+
454
+ # cli.usage %{
455
+ # document generate documentation
456
+ # -o --output=FILE output directory
457
+ # -V --verbose verbose output
458
+ # -q --quiet run silently
459
+ # }
460
+
461
+ #p cline.verbose?(:V)
462
+ #p cline.force?(:f)
463
+ #p cline.document.output='FILE'
464
+
465
+ p cli
466
+ puts
467
+ puts cli.to_s_help
468
+
469
+ =end
470
+
471
+ #cli[['--verbose', '-V'],['--quiet', '-q']] \
472
+ # ['--force'] \
473
+ # ['document']['--output=FILE', '-o']
275
474