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 CHANGED
@@ -1,3 +1,4 @@
1
+ *.swp
1
2
  *.gem
2
3
  *.rbc
3
4
  .bundle
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.2"
5
+ - "1.9.3"
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+
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
- * Lightweight, unobtrusive syntax.
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 most complete and robust
17
- option-parser implementations ever created.
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
- module Printer
37
- # Declare the "root"-command
38
- Optix::command do
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
- # Declare a global option (all subcommands inherit this)
45
+
46
+ # An option that is inherited by all commands
43
47
  opt :debug, "Enable debugging", :default => false
44
48
  end
45
-
46
- # Declare sub-command
47
- Optix::command 'print' do
48
- desc "Print a string"
49
- text "Print a string to the screen"
50
- params "<string>"
51
-
52
- opt :count, "Print how many times?", :default => 1
53
-
54
- # This block is invoked when validations pass.
55
- exec do |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(' ')
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
- Commands may be declared anywhere, at any time, in any order.
101
- Declaring the "root"-command looks like this:
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
- Optix::command do
107
- # opts, triggers, etc
108
- end
109
- ```
124
+ module Example
125
+ class HelloWorld < Optix::CLI
110
126
 
111
- Now, let's add a sub-command:
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
- ```ruby
114
- Optix::command 'sub' do
115
- # opts, triggers, etc
135
+ if __FILE__ == $0
136
+ # Perform the actual parsing and execution.
137
+ Optix.invoke!(ARGV)
116
138
  end
117
139
  ```
118
140
 
119
- And finally, a sub-sub command:
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
- Optix::command 'sub sub' do
123
- # opts, triggers, etc
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::command DSL
180
+ ### Optix DSL
138
181
 
139
- Within `Optix::command` the following directives are available:
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
- Optix::command "frobnitz" do
147
- desc "Frobnicates the gizmo"
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
- Optix::command "frobnitz" do
157
- text "Frobnicate the gizmo by subtle twiddling."
158
- text "Please only apply this to 2-state devices or you might bork it."
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
- Optix::command do
171
- opt :some_name, "some description", :default => 'some_default'
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
- Optix::command do
235
- params "<foo> [bar]"
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 at all (this is your job, inside exec{}).
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
- Optix::command do
249
- opt :we, ""
250
- opt :are, ""
251
- opt :family, ""
252
- depends :we, :are, :family
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
- Optix::command do
262
- opt :force, "Force this operation"
263
- opt :no_op, "Dry run, don't actually do anything"
264
- conflict :force, :no_op
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 allow to short-circuit argument parsing for "action-options"
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
- Optix::command do
275
- opt :version, "Print version and exit"
276
- trigger :version do
277
- puts "Version 1.0"
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 allow to group functionality that is common to a branch of subcommands.
462
+ Filters group functionality that is common to a branch of sub-commands.
300
463
 
301
464
  ```ruby
302
- Optix::command do
303
- opt :debug, "Enable debugging"
304
- filter do |cmd, opts, argv|
305
- if opts[:debug]
306
- # .. enable debugging ..
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
- ### exec
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
- Optix::command do
332
- exec do |cmd, opts, argv|
333
- if opts[:debug]
334
- # .. enable debugging ..
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 block receives three arguments:
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` inside your exec-block to abort
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
- ## Scopes
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, but by declaring them explicitly we can add
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
@@ -1,3 +1,3 @@
1
1
  class Optix
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
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('/tmp')
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('/tmp')]
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.1.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-07-13 00:00:00.000000000 Z
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: &13737160 !ruby/object:Gem::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: *13737160
24
+ version_requirements: *7466120
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &13735760 !ruby/object:Gem::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: *13735760
35
+ version_requirements: *7465200
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &13734640 !ruby/object:Gem::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: *13734640
46
+ version_requirements: *7464000
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: simplecov
49
- requirement: &13733600 !ruby/object:Gem::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: *13733600
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: 3252014974996613428
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: 3252014974996613428
107
+ hash: 540681097072146683
102
108
  requirements: []
103
109
  rubyforge_project:
104
110
  rubygems_version: 1.8.10