optix 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/busyloop/optix.png?branch=master)](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
|