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 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