optix 1.1.1 → 1.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.
- data/.gitignore +1 -0
- data/.travis.yml +10 -0
- data/README.md +265 -127
- data/examples/{filetool.rb → singleton_style/filetool.rb} +3 -8
- data/examples/{printer.rb → singleton_style/printer.rb} +0 -0
- data/examples/thor_style/bare.rb +37 -0
- data/examples/thor_style/hello_world.rb +25 -0
- data/examples/thor_style/kitchen_sink.rb +103 -0
- data/examples/thor_style/nested.rb +22 -0
- data/examples/thor_style/printer.rb +40 -0
- data/lib/optix/version.rb +1 -1
- data/lib/optix.rb +68 -0
- data/spec/optix_spec.rb +4 -3
- metadata +20 -14
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
+
[](https://travis-ci.org/busyloop/optix)
|
|
2
|
+
|
|
1
3
|
# Optix
|
|
2
4
|
|
|
3
5
|
Optix is an unobtrusive, composable command line parser based on Trollop.
|
|
6
|
+
It is intended to be a lighter weight alternative to [Thor](https://github.com/wycats/thor).
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
## Features
|
|
7
10
|
|
|
8
|
-
*
|
|
9
|
-
No subclassing or introduction of dependencies.
|
|
11
|
+
* Convenient, declarative syntax (similar to [Thor](https://github.com/wycats/thor))
|
|
10
12
|
|
|
11
13
|
* Nested subcommands such as `git remote show origin` may be composed at runtime in arbitrary order.
|
|
12
14
|
|
|
13
15
|
* Subcommands inherit from their parent. Common options (such as '--debug' or '--loglevel')
|
|
14
16
|
need to be declared only once to make them available to an entire branch.
|
|
15
17
|
|
|
16
|
-
* Stands on the shoulders of [Trollop](http://trollop.rubyforge.org) (by William Morgan), one of the
|
|
17
|
-
option-parser implementations
|
|
18
|
+
* Stands on the shoulders of [Trollop](http://trollop.rubyforge.org) (by William Morgan), one of the
|
|
19
|
+
most advanced option-parser implementations available.
|
|
18
20
|
|
|
19
21
|
* Automatic validation and help-screens.
|
|
20
22
|
|
|
21
23
|
* Should work on all major Ruby versions (tested on 1.9.3, 1.9.2 and 1.8.7).
|
|
22
24
|
|
|
23
|
-
* Strong test-suite.
|
|
24
25
|
|
|
25
26
|
## Installation
|
|
26
27
|
|
|
@@ -30,37 +31,35 @@ Optix is an unobtrusive, composable command line parser based on Trollop.
|
|
|
30
31
|
|
|
31
32
|
```ruby
|
|
32
33
|
#!/usr/bin/env ruby
|
|
34
|
+
|
|
33
35
|
require 'optix'
|
|
34
36
|
|
|
35
37
|
module Example
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
class Printer < Optix::CLI
|
|
39
|
+
|
|
40
|
+
# Declare global cli-options
|
|
41
|
+
cli_root do
|
|
42
|
+
# A label to be printed on the root help-screen
|
|
39
43
|
text "I am printer. I print strings to the screen."
|
|
40
44
|
text "Please invoke one of my not so many sub-commands."
|
|
41
|
-
|
|
42
|
-
#
|
|
45
|
+
|
|
46
|
+
# An option that is inherited by all commands
|
|
43
47
|
opt :debug, "Enable debugging", :default => false
|
|
44
48
|
end
|
|
45
|
-
|
|
46
|
-
# Declare
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
puts "DEBUGGING IS ENABLED!" if opts[:debug]
|
|
61
|
-
(1..opts[:count]).each do
|
|
62
|
-
puts argv.join(' ')
|
|
63
|
-
end
|
|
49
|
+
|
|
50
|
+
# Declare a command called "print"
|
|
51
|
+
desc "Print a string"
|
|
52
|
+
text "Print a string to the screen"
|
|
53
|
+
opt :count, "Print how many times?", :default => 1
|
|
54
|
+
params "<string>"
|
|
55
|
+
def print(cmd, opts, argv)
|
|
56
|
+
if argv.length < 1
|
|
57
|
+
raise Optix::HelpNeeded
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
puts "DEBUGGING IS ENABLED!" if opts[:debug]
|
|
61
|
+
(1..opts[:count]).each do
|
|
62
|
+
puts argv.join(' ')
|
|
64
63
|
end
|
|
65
64
|
end
|
|
66
65
|
end
|
|
@@ -95,56 +94,106 @@ See the `examples/`-folder for more elaborate examples.
|
|
|
95
94
|
|
|
96
95
|
## Documentation
|
|
97
96
|
|
|
97
|
+
A cli is composed by sub-classing `Optix::CLI` and using the DSL
|
|
98
|
+
to describe the desired commands, labels and options.
|
|
99
|
+
|
|
100
|
+
At runtime you call `Optix.invoke!(ARGV)` to invoke the parser. It will parse
|
|
101
|
+
and validate the input (in this case: ARGV), create an instance of the class
|
|
102
|
+
containing the requested method (if any) and call the latter.
|
|
103
|
+
|
|
104
|
+
In case of a validation error Optix displays an adequate
|
|
105
|
+
error-message and auto-generated help-screen.
|
|
106
|
+
|
|
107
|
+
If your program contains multiple classes inheriting from `Optix::CLI`
|
|
108
|
+
then the resulting cli will be the sum of their parts. You don't have
|
|
109
|
+
to pay attention to load-order, an Optix CLI assembles correctly in
|
|
110
|
+
any order.
|
|
111
|
+
|
|
98
112
|
### Commands and Sub-Commands
|
|
99
113
|
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
In Optix all commands are created equal. In order to nest
|
|
115
|
+
commands you simply declare them with a `parent`. The minimum
|
|
116
|
+
code to create a program that can be invoked as `program.rb hello world`
|
|
117
|
+
would look like this:
|
|
102
118
|
|
|
103
119
|
```ruby
|
|
120
|
+
#!/usr/bin/env ruby
|
|
121
|
+
|
|
104
122
|
require 'optix'
|
|
105
123
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
end
|
|
109
|
-
```
|
|
124
|
+
module Example
|
|
125
|
+
class HelloWorld < Optix::CLI
|
|
110
126
|
|
|
111
|
-
|
|
127
|
+
# Declare a command called "world" as child of "hello"
|
|
128
|
+
parent 'hello'
|
|
129
|
+
def world(cmd, opts, argv)
|
|
130
|
+
puts "Hello world!"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
112
134
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
135
|
+
if __FILE__ == $0
|
|
136
|
+
# Perform the actual parsing and execution.
|
|
137
|
+
Optix.invoke!(ARGV)
|
|
116
138
|
end
|
|
117
139
|
```
|
|
118
140
|
|
|
119
|
-
|
|
141
|
+
You'll notice that we didn't actually declare the parent-command itself (`hello`).
|
|
142
|
+
In optix this isn't necessary, any gaps in the hierarchy are automatically filled in.
|
|
143
|
+
|
|
144
|
+
Commands may be nested to any depth, e.g. try this parent in the above code: `parent 'shout at the'`.
|
|
145
|
+
|
|
146
|
+
## Commands that have sub-commands can not be invoked
|
|
147
|
+
|
|
148
|
+
When you have a command `foo bar` then the parent command `foo` can not be invoked.
|
|
149
|
+
It will still appear in help-screens and behave as expected, though.
|
|
150
|
+
This is a deliberate constraint enforced by Optix in order to protect the sanity of your users.
|
|
151
|
+
|
|
152
|
+
## Helpers
|
|
153
|
+
|
|
154
|
+
Optix ignores all methods that aren't preceded by a DSL-directive
|
|
155
|
+
(e.g. `parent`, `desc` or `opt`). Thus you don't need to do anything
|
|
156
|
+
special to mix command-methods with helper-methods.
|
|
157
|
+
|
|
158
|
+
Example:
|
|
120
159
|
|
|
121
160
|
```ruby
|
|
122
|
-
|
|
123
|
-
|
|
161
|
+
module Example
|
|
162
|
+
class HelperExample < Optix::CLI
|
|
163
|
+
|
|
164
|
+
# This method becomes a command because it is
|
|
165
|
+
# preceded by an optix-directive ('parent')
|
|
166
|
+
parent 'hello'
|
|
167
|
+
def world(cmd, opts, argv)
|
|
168
|
+
puts "Hello world!"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# This method is ignored by Optix
|
|
172
|
+
def iam_a_helper(yay)
|
|
173
|
+
# ...
|
|
174
|
+
end
|
|
175
|
+
end
|
|
124
176
|
end
|
|
125
177
|
```
|
|
126
178
|
|
|
127
|
-
Remember: Optix doesn't care about the order of declarations.
|
|
128
|
-
The `sub sub` command may be declared prior to the `sub` command.
|
|
129
|
-
|
|
130
|
-
A common pattern is to insert your `Optix::command` blocks directly
|
|
131
|
-
at the module level so they get invoked during class-loading.
|
|
132
|
-
This way your CLI assembles itself automatically and the
|
|
133
|
-
command-hierarchy mirrors the modules/classes that
|
|
134
|
-
are actually loaded.
|
|
135
|
-
|
|
136
179
|
|
|
137
|
-
### Optix
|
|
180
|
+
### Optix DSL
|
|
138
181
|
|
|
139
|
-
|
|
182
|
+
The following directives are available:
|
|
140
183
|
|
|
141
184
|
### desc
|
|
142
185
|
|
|
143
186
|
Short description, displayed in the subcommand-list on the help-screen of the *parent* command.
|
|
144
187
|
|
|
145
188
|
```ruby
|
|
146
|
-
|
|
147
|
-
|
|
189
|
+
module Example
|
|
190
|
+
class Frobnitz < Optix::CLI
|
|
191
|
+
|
|
192
|
+
desc 'Frobnicate a gizmo'
|
|
193
|
+
def frob(cmd, opts, argv)
|
|
194
|
+
...
|
|
195
|
+
end
|
|
196
|
+
end
|
|
148
197
|
end
|
|
149
198
|
```
|
|
150
199
|
|
|
@@ -153,22 +202,113 @@ end
|
|
|
153
202
|
Long description, displayed on the help-screen for this command.
|
|
154
203
|
|
|
155
204
|
```ruby
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
205
|
+
module Example
|
|
206
|
+
class Frobnitz < Optix::CLI
|
|
207
|
+
|
|
208
|
+
text "Frobnicate the gizmo by subtle twiddling."
|
|
209
|
+
text "Please only apply this to 2-state devices or you might bork it."
|
|
210
|
+
def frob(cmd, opts, argv)
|
|
211
|
+
...
|
|
212
|
+
end
|
|
213
|
+
end
|
|
159
214
|
end
|
|
160
215
|
```
|
|
161
216
|
|
|
162
217
|
* May be called multiple times for a multi-line description.
|
|
163
218
|
|
|
219
|
+
### parent
|
|
220
|
+
|
|
221
|
+
Specifies the parent for this command.
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
module Example
|
|
225
|
+
class Frobnitz < Optix::CLI
|
|
226
|
+
|
|
227
|
+
parent 'foo bar', ['desc for foo', 'desc for bar']
|
|
228
|
+
def batz(cmd, opts, argv)
|
|
229
|
+
puts "foo bar batz was called!"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
* Commands may be nested to any depth (delimited by whitespace), missing
|
|
236
|
+
parts of the hierarchy are filled in automatically.
|
|
237
|
+
|
|
238
|
+
* The second argument to `parent` is an optional array of descriptions (`desc`) for
|
|
239
|
+
the auto-generated parents.
|
|
240
|
+
|
|
241
|
+
* Use the special `parent :none` to describe the root-command in programs that
|
|
242
|
+
should accept arguments directly (`foo.rb --debug=true`) without any
|
|
243
|
+
commands (`foo.rb command --debug=true`). See `examples/thor_style/bare.rb`
|
|
244
|
+
for an example of this.
|
|
245
|
+
|
|
246
|
+
### cli_root
|
|
247
|
+
|
|
248
|
+
Takes a block to specify the root-command.
|
|
249
|
+
|
|
250
|
+
This is a useful shorthand in programs that have
|
|
251
|
+
(sub-)commands where it hence doesn't make sense to use
|
|
252
|
+
the `parent :none`-syntax.
|
|
253
|
+
|
|
254
|
+
```ruby
|
|
255
|
+
#!/usr/bin/env ruby
|
|
256
|
+
|
|
257
|
+
require 'optix'
|
|
258
|
+
|
|
259
|
+
module Example
|
|
260
|
+
class Frobnitz < Optix::CLI
|
|
261
|
+
|
|
262
|
+
# Declare global cli-options (aka "the root-command")
|
|
263
|
+
cli_root do
|
|
264
|
+
# A label to be printed on the root help-screen
|
|
265
|
+
text "I am printer. I print strings to the screen."
|
|
266
|
+
text "Please invoke one of my not so many sub-commands."
|
|
267
|
+
|
|
268
|
+
# An option that is inherited by all commands
|
|
269
|
+
opt :debug, "Enable debugging", :default => false
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Declare a command called "print"
|
|
273
|
+
desc "Print a string"
|
|
274
|
+
text "Print a string to the screen"
|
|
275
|
+
opt :count, "Print how many times?", :default => 1
|
|
276
|
+
params "<string>"
|
|
277
|
+
def print(cmd, opts, argv)
|
|
278
|
+
if argv.length < 1
|
|
279
|
+
raise Optix::HelpNeeded
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
if opts[:debug]
|
|
283
|
+
# ...
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
if __FILE__ == $0
|
|
290
|
+
# Perform the actual parsing and execution.
|
|
291
|
+
Optix.invoke!(ARGV)
|
|
292
|
+
end
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
* All sub-commands inherit the `opt`s from their parents.
|
|
296
|
+
A `text` declared inside `cli_root` will display on the root help-screen.
|
|
297
|
+
|
|
164
298
|
|
|
165
299
|
### opt
|
|
166
300
|
|
|
167
301
|
Declares an option.
|
|
168
302
|
|
|
169
303
|
```ruby
|
|
170
|
-
|
|
171
|
-
|
|
304
|
+
module Example
|
|
305
|
+
class Frobnitz < Optix::CLI
|
|
306
|
+
|
|
307
|
+
opt :some_name, "some description", :default => 'some_default'
|
|
308
|
+
def frob(cmd, opts, argv)
|
|
309
|
+
...
|
|
310
|
+
end
|
|
311
|
+
end
|
|
172
312
|
end
|
|
173
313
|
```
|
|
174
314
|
|
|
@@ -231,13 +371,18 @@ Takes the following optional arguments:
|
|
|
231
371
|
Describes positional parameters that this command accepts.
|
|
232
372
|
|
|
233
373
|
```ruby
|
|
234
|
-
|
|
235
|
-
|
|
374
|
+
module Example
|
|
375
|
+
class Frobnitz < Optix::CLI
|
|
376
|
+
params "<foo> [bar]"
|
|
377
|
+
def frob(cmd, opts, argv)
|
|
378
|
+
...
|
|
379
|
+
end
|
|
380
|
+
end
|
|
236
381
|
end
|
|
237
382
|
```
|
|
238
383
|
|
|
239
|
-
* Note: Optix does **not** validate or inspect positional parameters
|
|
240
|
-
The value of this command is only used to display a proper synopsis in the help-screen.
|
|
384
|
+
* Note: Optix does **not** validate or inspect positional parameters. This is up to you inside your method.
|
|
385
|
+
The value of this command is only used by Optix to display a proper synopsis in the help-screen.
|
|
241
386
|
|
|
242
387
|
### depends
|
|
243
388
|
|
|
@@ -245,11 +390,17 @@ Marks two (or more!) options as requiring each other. Only handles
|
|
|
245
390
|
undirected (i.e., mutual) dependencies.
|
|
246
391
|
|
|
247
392
|
```ruby
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
393
|
+
module Example
|
|
394
|
+
class Frobnitz < Optix::CLI
|
|
395
|
+
|
|
396
|
+
opt :we, ""
|
|
397
|
+
opt :are, ""
|
|
398
|
+
opt :family, ""
|
|
399
|
+
depends :we, :are, :family
|
|
400
|
+
def frob(cmd, opts, argv)
|
|
401
|
+
...
|
|
402
|
+
end
|
|
403
|
+
end
|
|
253
404
|
end
|
|
254
405
|
```
|
|
255
406
|
|
|
@@ -258,23 +409,35 @@ end
|
|
|
258
409
|
Marks two (or more!) options as conflicting.
|
|
259
410
|
|
|
260
411
|
```ruby
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
412
|
+
module Example
|
|
413
|
+
class Frobnitz < Optix::CLI
|
|
414
|
+
|
|
415
|
+
opt :force, "Force this operation"
|
|
416
|
+
opt :no_op, "Dry run, don't actually do anything"
|
|
417
|
+
conflict :force, :no_op
|
|
418
|
+
def frob(cmd, opts, argv)
|
|
419
|
+
...
|
|
420
|
+
end
|
|
421
|
+
end
|
|
265
422
|
end
|
|
266
423
|
```
|
|
267
424
|
|
|
268
425
|
### trigger
|
|
269
426
|
|
|
270
|
-
Triggers
|
|
427
|
+
Triggers short-circuit argument parsing for "action-options"
|
|
271
428
|
(options that directly trigger an action) such as `--version`.
|
|
272
429
|
|
|
273
430
|
```ruby
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
431
|
+
module Example
|
|
432
|
+
class Frobnitz < Optix::CLI
|
|
433
|
+
|
|
434
|
+
opt :version, "Print version and exit"
|
|
435
|
+
trigger :version do
|
|
436
|
+
puts "Version 1.0"
|
|
437
|
+
end
|
|
438
|
+
def frob(cmd, opts, argv)
|
|
439
|
+
...
|
|
440
|
+
end
|
|
278
441
|
end
|
|
279
442
|
end
|
|
280
443
|
```
|
|
@@ -296,14 +459,20 @@ end
|
|
|
296
459
|
|
|
297
460
|
### filter
|
|
298
461
|
|
|
299
|
-
Filters
|
|
462
|
+
Filters group functionality that is common to a branch of sub-commands.
|
|
300
463
|
|
|
301
464
|
```ruby
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
465
|
+
module Example
|
|
466
|
+
class Frobnitz < Optix::CLI
|
|
467
|
+
|
|
468
|
+
opt :debug, "Enable debugging"
|
|
469
|
+
filter do |cmd, opts, argv|
|
|
470
|
+
if opts[:debug]
|
|
471
|
+
# .. enable debugging ..
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
def frob(cmd, opts, argv)
|
|
475
|
+
...
|
|
307
476
|
end
|
|
308
477
|
end
|
|
309
478
|
end
|
|
@@ -322,28 +491,25 @@ end
|
|
|
322
491
|
parsing and display the help-screen.
|
|
323
492
|
|
|
324
493
|
|
|
325
|
-
###
|
|
326
|
-
|
|
327
|
-
The exec-block is called when your command is invoked, after validation
|
|
328
|
-
passed. It should contain (or invoke) your actual business logic.
|
|
494
|
+
### Method signature
|
|
329
495
|
|
|
330
496
|
```ruby
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
497
|
+
module Example
|
|
498
|
+
class Frobnitz < Optix::CLI
|
|
499
|
+
|
|
500
|
+
def frob(cmd, opts, argv)
|
|
501
|
+
...
|
|
335
502
|
end
|
|
336
503
|
end
|
|
337
504
|
end
|
|
338
505
|
```
|
|
339
506
|
|
|
340
|
-
* Your
|
|
507
|
+
* Your command-method receives three arguments:
|
|
341
508
|
* `cmd` (Array) The full command that was executed, e.g.: ['foo', 'bar', 'baz']
|
|
342
509
|
* `opts` (Hash) The options-hash, e.g.: { :debug => true }
|
|
343
510
|
* `argv` (Array) Positional parameters that your command may have received, e.g.: ['a','b','c']
|
|
344
511
|
|
|
345
|
-
* You may raise `Optix::HelpNeeded`
|
|
346
|
-
parsing and display the help-screen.
|
|
512
|
+
* You may raise `Optix::HelpNeeded` to abort parsing and display the help-screen.
|
|
347
513
|
|
|
348
514
|
|
|
349
515
|
## Chain of execution
|
|
@@ -359,40 +525,12 @@ This is the chain of execution when you pass ['foo', 'bar', 'batz'] to `Optix.in
|
|
|
359
525
|
1. Filters for `batz` (if any)
|
|
360
526
|
1. Exec{}-block for `batz`
|
|
361
527
|
|
|
528
|
+
## Advanced usage
|
|
362
529
|
|
|
530
|
+
Optix is very flexible and can be easily shaped into many forms.
|
|
363
531
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
In rare cases you may want to have multiple independent Optix command-trees in a single app.
|
|
367
|
-
This can be achieved by passing a scope-name to your command-declarations, like so:
|
|
368
|
-
|
|
369
|
-
```ruby
|
|
370
|
-
# Declare root-command in the :default scope
|
|
371
|
-
Optix::command '', :default do
|
|
372
|
-
# opts, triggers, etc
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
# Declare root-command in another, independent scope
|
|
376
|
-
Optix::command '', :other_scope do
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
# Declare a sub-command in the other scope
|
|
380
|
-
Optix::command 'sub', :other_scope do
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
# Then either invoke the :default scope
|
|
384
|
-
Optix.invoke!(ARGV)
|
|
385
|
-
# ...or...
|
|
386
|
-
Optix.invoke!(ARGV, :other_scope)
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
## Re-initialization
|
|
390
|
-
|
|
391
|
-
In even rarer cases you may need to reset Optix at runtime.
|
|
392
|
-
To make Optix forget all scopes, configuration and commands, invoke:
|
|
393
|
-
|
|
394
|
-
`Optix.reset!`
|
|
395
|
-
|
|
532
|
+
Please see the specs, source-code and the examples in `examples/singleton_style`
|
|
533
|
+
for advanced usage patterns (no sub-classing, lower level access).
|
|
396
534
|
|
|
397
535
|
## Contributing
|
|
398
536
|
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
1
2
|
require 'optix'
|
|
2
3
|
|
|
3
4
|
#
|
|
4
|
-
# Example application to demonstrate Optix.
|
|
5
|
-
#
|
|
6
|
-
# The easiest way to get started is to download this file
|
|
7
|
-
# and actually run it. Play around, try invoking '--help',
|
|
8
|
-
# '-v', '--version' and the subcommands.
|
|
9
|
-
#
|
|
10
|
-
# Then come back here to learn how it's done. :)
|
|
5
|
+
# Example application to demonstrate Optix advanced usage.
|
|
11
6
|
#
|
|
12
7
|
module Example
|
|
13
8
|
class FileTool
|
|
@@ -15,7 +10,7 @@ module Example
|
|
|
15
10
|
# Also declaring the first-level sub-commands right here.
|
|
16
11
|
# Just like the root-command these commands can not be invoked
|
|
17
12
|
# directly (because they have sub-commands). Their declaration
|
|
18
|
-
# is not mandatory
|
|
13
|
+
# is not mandatory but by declaring them explicitly we can add
|
|
19
14
|
# some useful help-texts to aid the user.
|
|
20
15
|
Optix::command do
|
|
21
16
|
# Let's have a nice description
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'optix'
|
|
4
|
+
|
|
5
|
+
module Example
|
|
6
|
+
class Bare < Optix::CLI
|
|
7
|
+
|
|
8
|
+
# Normally Optix would create a sub-command
|
|
9
|
+
# called "main" for the method below.
|
|
10
|
+
#
|
|
11
|
+
# But in this example we don't want any sub-commands,
|
|
12
|
+
# thus we specify 'parent :none' and attach our options
|
|
13
|
+
# directly to the root.
|
|
14
|
+
parent :none
|
|
15
|
+
desc "Print a string"
|
|
16
|
+
text "Printer v1.0"
|
|
17
|
+
text "I print a string to the screen, possibly many times."
|
|
18
|
+
opt :count, "Print how many times?", :default => 1
|
|
19
|
+
params "<string>"
|
|
20
|
+
def main(cmd, opts, argv)
|
|
21
|
+
if argv.length < 1
|
|
22
|
+
raise Optix::HelpNeeded
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
puts "DEBUGGING IS ENABLED!" if opts[:debug]
|
|
26
|
+
(1..opts[:count]).each do
|
|
27
|
+
puts argv.join(' ')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if __FILE__ == $0
|
|
34
|
+
# Perform the actual parsing and execution.
|
|
35
|
+
Optix.invoke!(ARGV)
|
|
36
|
+
end
|
|
37
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Minimum hello-world example.
|
|
4
|
+
#
|
|
5
|
+
# Note: In a real program you'd want to at least
|
|
6
|
+
# provide some label and help-text using
|
|
7
|
+
# "desc" and "text".
|
|
8
|
+
|
|
9
|
+
require 'optix'
|
|
10
|
+
|
|
11
|
+
module Example
|
|
12
|
+
class HelloWorld < Optix::CLI
|
|
13
|
+
|
|
14
|
+
# Declare a command called "world" as child of "hello"
|
|
15
|
+
parent 'hello', "Try me!"
|
|
16
|
+
def world(cmd, opts, argv)
|
|
17
|
+
puts "Hello world!"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if __FILE__ == $0
|
|
23
|
+
# Perform the actual parsing and execution.
|
|
24
|
+
Optix.invoke!(ARGV)
|
|
25
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'optix'
|
|
4
|
+
|
|
5
|
+
# A kitchen sink example, demonstrating most Optix functionality
|
|
6
|
+
|
|
7
|
+
module KitchenSink
|
|
8
|
+
# We declare the root-command ("global options") right here
|
|
9
|
+
# NOTE: This is an alternative (equivalent) syntax to using 'cli_root'
|
|
10
|
+
# inside a sub-class of Optix::CLI
|
|
11
|
+
Optix::command do
|
|
12
|
+
# Help-screen text
|
|
13
|
+
text "Kitchen-sink-multi-tool. I can print text, calculate, sing and dance!"
|
|
14
|
+
text "Please invoke one of my sub-commands."
|
|
15
|
+
|
|
16
|
+
# A global option that is inherited by all sub-commands
|
|
17
|
+
opt :debug, "Enable debugging", :default => false
|
|
18
|
+
|
|
19
|
+
# Declare a filter on the :debug-option. All sub-commands
|
|
20
|
+
# inherit this filter as well so you will see the text printed
|
|
21
|
+
# when you use the '-d' option on any command.
|
|
22
|
+
filter do |cmd, opts, argv|
|
|
23
|
+
puts "DEBUG: '#{cmd.join(' ')}' was called with opts=#{opts}, argv=#{argv}" if opts[:debug]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# This is our Printer again.
|
|
29
|
+
# You probably remember him from the first example. ;)
|
|
30
|
+
class Printer < Optix::CLI
|
|
31
|
+
desc "Print a string"
|
|
32
|
+
text "Print a string to the screen"
|
|
33
|
+
params "<string>"
|
|
34
|
+
opt :count, "Print how many times?", :default => 1
|
|
35
|
+
def print(cmd, opts, argv)
|
|
36
|
+
if argv.length < 1
|
|
37
|
+
raise Optix::HelpNeeded
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
(1..opts[:count]).each do
|
|
41
|
+
puts argv.join(' ')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# A simple Calculator
|
|
47
|
+
class Calculator < Optix::CLI
|
|
48
|
+
# We want all commands in here to be subcommands of
|
|
49
|
+
# 'calc'. Since 'calc' itself is not declared anywhere
|
|
50
|
+
# it is implicitly created, and we also pass a description.
|
|
51
|
+
parent 'calc', 'Calculator'
|
|
52
|
+
desc "Multiply some numbers"
|
|
53
|
+
text "Multiplication is ohsom!"
|
|
54
|
+
params "<int> <int> [int] ..."
|
|
55
|
+
def multi(cmd, opts, argv)
|
|
56
|
+
if argv.length < 2
|
|
57
|
+
puts "Error: Need at least two parameters!"
|
|
58
|
+
raise Optix::HelpNeeded
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
o = 1
|
|
62
|
+
argv.each do |i|
|
|
63
|
+
o *= i.to_i
|
|
64
|
+
end
|
|
65
|
+
puts o
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Hint: Don't forget to specify the parent again,
|
|
69
|
+
# otherwise the method would become a child
|
|
70
|
+
# of root. We can leave out the description
|
|
71
|
+
# here because we already provided one above
|
|
72
|
+
# the 'multi'-method.
|
|
73
|
+
parent 'calc' #, 'Calculator'
|
|
74
|
+
desc "Add some numbers"
|
|
75
|
+
text "Addition is ohsom!"
|
|
76
|
+
|
|
77
|
+
# Demonstrate a trigger here (makes no sense, but heck)
|
|
78
|
+
opt :argh, "Just say 'ARGH!' and exit"
|
|
79
|
+
trigger :argh do |cmd, opts, argv|
|
|
80
|
+
puts "ARGH!"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
params "<int> <int> [int] ..."
|
|
84
|
+
def add(cmd, opts, argv)
|
|
85
|
+
if argv.length < 2
|
|
86
|
+
puts "Error: Need at least two parameters!"
|
|
87
|
+
raise Optix::HelpNeeded
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
o = 0
|
|
91
|
+
argv.each do |i|
|
|
92
|
+
o += i.to_i
|
|
93
|
+
end
|
|
94
|
+
puts o
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if __FILE__ == $0
|
|
100
|
+
# Perform the actual parsing and execution.
|
|
101
|
+
Optix.invoke!(ARGV)
|
|
102
|
+
end
|
|
103
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'optix'
|
|
4
|
+
|
|
5
|
+
module Example
|
|
6
|
+
class Frobnitz < Optix::CLI
|
|
7
|
+
|
|
8
|
+
parent 'foo bar', ['desc for foo', 'desc for bar']
|
|
9
|
+
|
|
10
|
+
desc 'i am foobarbaz' # label for 'foo bar baz'
|
|
11
|
+
text 'hell yea, i am foobarbaz' # help-screen for 'foo bar baz'
|
|
12
|
+
def baz(cmd, opts, argv)
|
|
13
|
+
puts "foo bar baz was called!"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if __FILE__ == $0
|
|
19
|
+
# Perform the actual parsing and execution.
|
|
20
|
+
Optix.invoke!(ARGV)
|
|
21
|
+
end
|
|
22
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'optix'
|
|
4
|
+
|
|
5
|
+
module Example
|
|
6
|
+
class Printer < Optix::CLI
|
|
7
|
+
|
|
8
|
+
# Declare global options; the text-label that is printed
|
|
9
|
+
# for the root-command and one option that is inherited by
|
|
10
|
+
# all sub-commands.
|
|
11
|
+
cli_root do
|
|
12
|
+
text "I am printer. I print strings to the screen."
|
|
13
|
+
text "Please invoke one of my not so many sub-commands."
|
|
14
|
+
# Opts are inherited by all children
|
|
15
|
+
opt :debug, "Enable debugging", :default => false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Declare a sub-command called "print"
|
|
19
|
+
desc "Print a string"
|
|
20
|
+
text "Print a string to the screen"
|
|
21
|
+
opt :count, "Print how many times?", :default => 1
|
|
22
|
+
params "<string>"
|
|
23
|
+
# Your CLI-methods always
|
|
24
|
+
def print(cmd, opts, argv)
|
|
25
|
+
if argv.length < 1
|
|
26
|
+
raise Optix::HelpNeeded
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
puts "DEBUGGING IS ENABLED!" if opts[:debug]
|
|
30
|
+
(1..opts[:count]).each do
|
|
31
|
+
puts argv.join(' ')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if __FILE__ == $0
|
|
38
|
+
# Perform the actual parsing and execution.
|
|
39
|
+
Optix.invoke!(ARGV)
|
|
40
|
+
end
|
data/lib/optix/version.rb
CHANGED
data/lib/optix.rb
CHANGED
|
@@ -271,3 +271,71 @@ class Optix
|
|
|
271
271
|
end
|
|
272
272
|
end
|
|
273
273
|
|
|
274
|
+
class Optix
|
|
275
|
+
class CLI
|
|
276
|
+
class << self
|
|
277
|
+
def add_context(key, value, &block)
|
|
278
|
+
@optix_context ||= []
|
|
279
|
+
@optix_context << [key, value, block]
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
[:desc, :text, :opt, :params, :filter,
|
|
283
|
+
:trigger, :depends, :conflicts, :parent].each do |meth|
|
|
284
|
+
define_method(meth) do |*value, &block|
|
|
285
|
+
add_context(meth, value, &block)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def parent(path, label=nil)
|
|
290
|
+
@optix_context ||= []
|
|
291
|
+
if label
|
|
292
|
+
label = [label] if label.is_a? String
|
|
293
|
+
p = path.split
|
|
294
|
+
(0..p.length-1).each do |i|
|
|
295
|
+
_path = p[0..i].join(' ')
|
|
296
|
+
Optix::command(_path) do
|
|
297
|
+
desc label[i]
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
@optix_parent = path
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def cli_root(&block)
|
|
306
|
+
if block
|
|
307
|
+
add_context(:cli_root, '', &block)
|
|
308
|
+
else
|
|
309
|
+
@optix_parent = :none
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def method_added(meth)
|
|
314
|
+
return if @optix_context.nil?
|
|
315
|
+
if @optix_parent == :none
|
|
316
|
+
cmd_path = nil
|
|
317
|
+
elsif @optix_parent
|
|
318
|
+
cmd_path = "#{@optix_parent} #{meth.to_s}"
|
|
319
|
+
else
|
|
320
|
+
cmd_path = meth.to_s
|
|
321
|
+
end
|
|
322
|
+
cmd = Optix::command(cmd_path) {}
|
|
323
|
+
@optix_context.each do |e|
|
|
324
|
+
if :cli_root == e[0]
|
|
325
|
+
Optix::command *e[1], &e[2]
|
|
326
|
+
next
|
|
327
|
+
end
|
|
328
|
+
cmd.send(e[0], *e[1], &e[2])
|
|
329
|
+
end
|
|
330
|
+
me = self
|
|
331
|
+
|
|
332
|
+
cmd.send(:exec) do |cmd, opts, argv|
|
|
333
|
+
self.new.send(meth, cmd, opts, argv)
|
|
334
|
+
end
|
|
335
|
+
@optix_parent = ''
|
|
336
|
+
@optix_context = nil
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
data/spec/optix_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
+
require 'tempfile'
|
|
2
3
|
require 'optix'
|
|
3
4
|
|
|
4
5
|
describe Optix do
|
|
@@ -124,7 +125,7 @@ describe Optix do
|
|
|
124
125
|
opt :a, '', :default => true
|
|
125
126
|
opt :b, '', :default => 1
|
|
126
127
|
opt :c, '', :default => 1.0
|
|
127
|
-
opt :d, '', :default => File.new('
|
|
128
|
+
opt :d, '', :default => File.new(Tempfile.new('optix_test').path)
|
|
128
129
|
opt :e, '', :default => Date.new
|
|
129
130
|
opt :f, '', :default => "foo"
|
|
130
131
|
end
|
|
@@ -150,7 +151,7 @@ describe Optix do
|
|
|
150
151
|
Optix::command('', @context) do
|
|
151
152
|
opt :b, '', :default => [1]
|
|
152
153
|
opt :c, '', :default => [1.0]
|
|
153
|
-
opt :d, '', :default => [File.new('
|
|
154
|
+
opt :d, '', :default => [File.new(Tempfile.new('optix_test').path)]
|
|
154
155
|
opt :e, '', :default => [Date.new]
|
|
155
156
|
opt :f, '', :default => ["foo"]
|
|
156
157
|
end
|
|
@@ -940,7 +941,7 @@ describe Optix do
|
|
|
940
941
|
|
|
941
942
|
describe "FileTool" do
|
|
942
943
|
before :each do
|
|
943
|
-
load 'examples/filetool.rb'
|
|
944
|
+
load 'examples/singleton_style/filetool.rb'
|
|
944
945
|
end
|
|
945
946
|
|
|
946
947
|
it "prints version when invoked with -v" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: optix
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,11 +9,11 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-12-18 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: chronic
|
|
16
|
-
requirement: &
|
|
16
|
+
requirement: &7466120 !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ! '>='
|
|
@@ -21,10 +21,10 @@ dependencies:
|
|
|
21
21
|
version: '0'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *7466120
|
|
25
25
|
- !ruby/object:Gem::Dependency
|
|
26
26
|
name: rake
|
|
27
|
-
requirement: &
|
|
27
|
+
requirement: &7465200 !ruby/object:Gem::Requirement
|
|
28
28
|
none: false
|
|
29
29
|
requirements:
|
|
30
30
|
- - ! '>='
|
|
@@ -32,10 +32,10 @@ dependencies:
|
|
|
32
32
|
version: '0'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
|
-
version_requirements: *
|
|
35
|
+
version_requirements: *7465200
|
|
36
36
|
- !ruby/object:Gem::Dependency
|
|
37
37
|
name: rspec
|
|
38
|
-
requirement: &
|
|
38
|
+
requirement: &7464000 !ruby/object:Gem::Requirement
|
|
39
39
|
none: false
|
|
40
40
|
requirements:
|
|
41
41
|
- - ! '>='
|
|
@@ -43,10 +43,10 @@ dependencies:
|
|
|
43
43
|
version: '0'
|
|
44
44
|
type: :development
|
|
45
45
|
prerelease: false
|
|
46
|
-
version_requirements: *
|
|
46
|
+
version_requirements: *7464000
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
48
|
name: simplecov
|
|
49
|
-
requirement: &
|
|
49
|
+
requirement: &7463020 !ruby/object:Gem::Requirement
|
|
50
50
|
none: false
|
|
51
51
|
requirements:
|
|
52
52
|
- - ! '>='
|
|
@@ -54,7 +54,7 @@ dependencies:
|
|
|
54
54
|
version: '0'
|
|
55
55
|
type: :development
|
|
56
56
|
prerelease: false
|
|
57
|
-
version_requirements: *
|
|
57
|
+
version_requirements: *7463020
|
|
58
58
|
description: Optix is an unobtrusive, composable command line parser.
|
|
59
59
|
email:
|
|
60
60
|
- moe@busyloop.net
|
|
@@ -63,12 +63,18 @@ extensions: []
|
|
|
63
63
|
extra_rdoc_files: []
|
|
64
64
|
files:
|
|
65
65
|
- .gitignore
|
|
66
|
+
- .travis.yml
|
|
66
67
|
- Gemfile
|
|
67
68
|
- LICENSE
|
|
68
69
|
- README.md
|
|
69
70
|
- Rakefile
|
|
70
|
-
- examples/filetool.rb
|
|
71
|
-
- examples/printer.rb
|
|
71
|
+
- examples/singleton_style/filetool.rb
|
|
72
|
+
- examples/singleton_style/printer.rb
|
|
73
|
+
- examples/thor_style/bare.rb
|
|
74
|
+
- examples/thor_style/hello_world.rb
|
|
75
|
+
- examples/thor_style/kitchen_sink.rb
|
|
76
|
+
- examples/thor_style/nested.rb
|
|
77
|
+
- examples/thor_style/printer.rb
|
|
72
78
|
- lib/optix.rb
|
|
73
79
|
- lib/optix/trollop.rb
|
|
74
80
|
- lib/optix/version.rb
|
|
@@ -89,7 +95,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
89
95
|
version: '0'
|
|
90
96
|
segments:
|
|
91
97
|
- 0
|
|
92
|
-
hash:
|
|
98
|
+
hash: 540681097072146683
|
|
93
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
100
|
none: false
|
|
95
101
|
requirements:
|
|
@@ -98,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
98
104
|
version: '0'
|
|
99
105
|
segments:
|
|
100
106
|
- 0
|
|
101
|
-
hash:
|
|
107
|
+
hash: 540681097072146683
|
|
102
108
|
requirements: []
|
|
103
109
|
rubyforge_project:
|
|
104
110
|
rubygems_version: 1.8.10
|