escort 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +8 -0
  3. data/README.md +365 -14
  4. data/TODO.md +157 -44
  5. data/escort.gemspec +1 -0
  6. data/examples/{1_1_basic.rb → attic/1_1_basic.rb} +0 -0
  7. data/examples/{1_2_basic_requires_arguments.rb → attic/1_2_basic_requires_arguments.rb} +0 -0
  8. data/examples/{2_2_command.rb → attic/2_2_command.rb} +0 -0
  9. data/examples/{2_2_command_requires_arguments.rb → attic/2_2_command_requires_arguments.rb} +0 -0
  10. data/examples/{2_3_nested_commands.rb → attic/2_3_nested_commands.rb} +0 -0
  11. data/examples/{3_validations.rb → attic/3_validations.rb} +0 -0
  12. data/examples/{4_1_config_file.rb → attic/4_1_config_file.rb} +0 -0
  13. data/examples/{argument_handling → attic/argument_handling}/basic.rb +0 -0
  14. data/examples/{argument_handling → attic/argument_handling}/basic_command.rb +0 -0
  15. data/examples/{argument_handling → attic/argument_handling}/no_arguments.rb +0 -0
  16. data/examples/{argument_handling → attic/argument_handling}/no_arguments_command.rb +0 -0
  17. data/examples/{command_aliases → attic/command_aliases}/app.rb +0 -0
  18. data/examples/{config_file → attic/config_file}/.apprc2 +0 -0
  19. data/examples/{config_file → attic/config_file}/app.rb +0 -0
  20. data/examples/{config_file → attic/config_file}/sub_commands.rb +0 -0
  21. data/examples/{default_command → attic/default_command}/app.rb +0 -0
  22. data/examples/{sub_commands → attic/sub_commands}/app.rb +0 -0
  23. data/examples/{validation_basic → attic/validation_basic}/app.rb +0 -0
  24. data/examples/basic +10 -0
  25. data/examples/basic_conflicts +17 -0
  26. data/examples/basic_depends_on +25 -0
  27. data/examples/basic_flags +15 -0
  28. data/examples/basic_options +14 -0
  29. data/examples/basic_options_multi +15 -0
  30. data/examples/basic_require_arguments +17 -0
  31. data/examples/basic_texts +21 -0
  32. data/examples/basic_validations +21 -0
  33. data/examples/command +19 -0
  34. data/examples/commands/example_command.rb +13 -0
  35. data/lib/escort/action_command/base.rb +4 -0
  36. data/lib/escort/app.rb +33 -11
  37. data/lib/escort/formatter/borderless_table.rb +4 -0
  38. data/lib/escort/formatter/command.rb +87 -0
  39. data/lib/escort/formatter/commands.rb +36 -0
  40. data/lib/escort/formatter/default_help_formatter.rb +68 -73
  41. data/lib/escort/formatter/global_command.rb +17 -0
  42. data/lib/escort/formatter/option.rb +138 -0
  43. data/lib/escort/formatter/options.rb +17 -3
  44. data/lib/escort/formatter/shell_command_executor.rb +49 -0
  45. data/lib/escort/formatter/terminal.rb +17 -9
  46. data/lib/escort/formatter/terminal_formatter.rb +6 -0
  47. data/lib/escort/logger.rb +4 -4
  48. data/lib/escort/option_dependency_validator.rb +83 -0
  49. data/lib/escort/option_parser.rb +11 -1
  50. data/lib/escort/setup/configuration/reader.rb +0 -2
  51. data/lib/escort/setup/configuration/writer.rb +0 -2
  52. data/lib/escort/setup/dsl/command.rb +2 -7
  53. data/lib/escort/setup/dsl/global.rb +2 -9
  54. data/lib/escort/setup/dsl/options.rb +56 -0
  55. data/lib/escort/setup_accessor.rb +23 -6
  56. data/lib/escort/trollop.rb +4 -3
  57. data/lib/escort/validator.rb +4 -1
  58. data/lib/escort/version.rb +1 -1
  59. data/lib/escort.rb +8 -1
  60. data/spec/integration/basic_conflicts_spec.rb +47 -0
  61. data/spec/integration/basic_depends_on_spec.rb +275 -0
  62. data/spec/integration/basic_options_spec.rb +9 -21
  63. data/spec/integration/basic_options_with_multi_spec.rb +30 -0
  64. data/spec/integration/basic_spec.rb +5 -6
  65. data/spec/integration/basic_validations_spec.rb +77 -0
  66. data/spec/integration/basic_with_arguments_spec.rb +33 -0
  67. data/spec/integration/basic_with_text_fields_spec.rb +21 -0
  68. data/spec/lib/escort/formatter/command_spec.rb +238 -0
  69. data/spec/lib/escort/formatter/global_command_spec.rb +50 -0
  70. data/spec/lib/escort/formatter/option_spec.rb +300 -0
  71. data/spec/lib/escort/formatter/shell_command_executor_spec.rb +59 -0
  72. data/spec/lib/escort/formatter/string_splitter_spec.rb +12 -0
  73. data/spec/lib/escort/formatter/terminal_spec.rb +19 -0
  74. data/spec/lib/escort/setup_accessor_spec.rb +1 -0
  75. data/spec/spec_helper.rb +9 -3
  76. data/spec/support/integration_helpers.rb +2 -0
  77. data/spec/{helpers/execute_action_matcher.rb → support/matchers/execute_action_for_command_matcher.rb} +3 -3
  78. data/spec/support/matchers/execute_action_with_arguments_matcher.rb +25 -0
  79. data/spec/support/matchers/execute_action_with_options_matcher.rb +29 -0
  80. data/spec/support/matchers/exit_with_code_matcher.rb +29 -0
  81. data/spec/support/shared_contexts/integration_setup.rb +34 -0
  82. metadata +86 -28
  83. data/examples/basic/app.rb +0 -16
  84. data/lib/escort/formatter/common.rb +0 -58
  85. data/spec/helpers/exit_with_code_matcher.rb +0 -21
  86. data/spec/helpers/give_option_to_action_with_value_matcher.rb +0 -22
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NWYwMTA5MzIxMTE3MjU2YzJhNmE0MTA5NzViNTQwZWMyNmU0YzM5Nw==
4
+ NTg0OWNmNDI2ZDU1MDc4NTE4NDgyMGMzMDY3NDBlNTZlOGJiNDM0Nw==
5
5
  data.tar.gz: !binary |-
6
- ZTgyNDA0NDU4OTllYTk2NGNkYTJmNGIzODk2NDQ4ZjVhOGVmZjllMQ==
6
+ MjkxNTExZTY2ZDJhNTlmNmY0MTBiOGNiNGQyZDg4ZTAxYzQ1NTMwNA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZTU3Y2FkZWE2YjVjMTVjMDE4ZDI0OTA2YTRhZmFhMzdhN2Q4YzhhNTRkNDlh
10
- ZDdlODcyZjUzOTEzNzliM2QzMmEyOGE2OTEzYjJjZmViMGNjNjA0MzBjMDBh
11
- ODQ0MzA0NTU2ZGViMWY2ZWNkZWIyMWZiN2U0Y2ViY2IwNjc1MDg=
9
+ ZWU2OGE4M2NkZGRhZjJlNDZjZTE3ZDk0OWUwMGZhOWY3YWY1NDMxMmI1MmJm
10
+ MmYzYjY3YjllY2Q1OGE3YzVjNGIxZTI3ZTY4OWVlNTI1ODM0MTRkNzU3MzBm
11
+ ODlmODhhMGY2ODMwYzFlNTM3NGRhNzlkOGM1NzRhYmIxNzYzZDQ=
12
12
  data.tar.gz: !binary |-
13
- NDIxMjk4N2M4ZTZjMzBmNjFmOWVmYzQxMzI4Zjk5OTRlNjU1NTA0OTc2MzFj
14
- MjMxOWZhNTliYmJjNmMzYzI2NDQ0MDExMDc5ZDdkNDA4NGU1YmUwOWQxNzI0
15
- Y2NmMGEwYmJjNjAwYmU2MDFlNmY5OTU2ZTJkYTIxZGFlMWFiYTE=
13
+ YzM5YWI1NTZhYWJmZWE2ODBjZGM1YTZkYTY5ODQ3NWIwMjQ0YWYxY2NhOGMz
14
+ ODA5ODk5ZmE5N2RmZjc3ZTllNDQ1NWNmYjU1MDRkNDdlNGEzNWY2YzhhZGEz
15
+ ZGM3OTk4ZmFmMjI4NmY4ZGVhNzM2ZmE2NzJhOTZlNGI4YWUxYmE=
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - jruby-19mode # JRuby in 1.9 mode
7
+ - rbx-19mode
8
+ script: bundle exec rspec spec
data/README.md CHANGED
@@ -1,20 +1,49 @@
1
+ [![Build Status](https://travis-ci.org/skorks/escort.png?branch=master)](https://travis-ci.org/skorks/escort)
2
+
1
3
  # Escort
2
4
 
3
5
  Writing even complex command-line apps should be quick, easy and fun. Escort takes the excellent [Trollop](http://trollop.rubyforge.org/) option parser and adds a whole bunch of awesome features to produce a library you will always want to turn to when a 'quick script' is in order.
4
6
 
5
7
  ## Why Write Another CLI Tool
6
8
 
7
- A lot of the existing CLI making libraries delegate to OptionParser for actually parsing the option string, while OptionParser is nice it doesn't allow things like specifying the same option multiple times (e.g. like CURL -H parameter) which I like and use quite often. Trollop handles this case nicely, so a Trollop-based CLI tool is slightly superior.
9
+ A lot of the existing CLI making libraries delegate to OptionParser for actually parsing the option string, while OptionParser is nice it doesn't allow things like specifying the same option multiple times (e.g. like CURL -H parameter) which I like and use quite often. Trollop handles this case nicely, so a Trollop-based CLI tool is superior.
8
10
 
9
- Also a lot of the other CLI libraries in an attempt to be extra terse and DRY make their syntax a little obtuse. Escort tries to create a DSL that strikes a balance between being terse and being easy to understand.
11
+ Also a lot of the other CLI libraries in an attempt to be extra terse and DRY make their syntax a little obtuse. Escort tries to create a DSL that strikes a balance between being terse and being easy to understand, remember and read.
10
12
 
11
- I find that I end up with a similar structure for the CLI apps that I write and I want to capture that as a bit of a convention. An app doesn't stop at the option parsing, how do you actually structure the code that executes the work?
13
+ I find that I end up with a similar structure for the CLI apps that I write and I want to capture that as a bit of a convention/pattern. An app doesn't stop at the option parsing, how do you actually structure the code that executes the work?
12
14
 
13
- In general, some liraries give you great option parsing, but no infinitely nesting sub command, others have sub commands, but the option parsing is inferior. I want everything out of my CLI library, without having to dig through feature lists trying to figure out what will give me the biggest bang for my buck.
15
+ In general, some libraries give you great option parsing, but no infinitely nesting sub-command, others have sub-commands, but the option parsing is inferior. I want everything from my CLI library, without having to dig through feature lists, trying to figure out what will give me the biggest bang for my buck.
14
16
 
15
17
  ## Features
16
18
 
17
- TODO
19
+ * Long and short form options (e.g. `-g` and `--global`)
20
+ * Command support (e.g. `my_app do` - where `do` is a command)
21
+ * Infinitely nesting sub-command support (e.g. `my_app do something fun` - where `do`, `something` and `fun` are commands)
22
+ * Options on a per command and sub-command level (e.g. `my_app -g do -g something -g fun` - where `-g` can mean different things for each command)
23
+ * Nicely formatted help text (your app get `-h` and `--help` options automatically)
24
+ * Version support (your app can get the `-v` and `--version` based on specification)
25
+ * Multi options (e.g. `my_app -x foo -x bar -x baz`)
26
+ * You can mark options as conflicting with each other
27
+ * You can mark options as depending on each other
28
+ * Declarative validation support for each option, with multiple validation rules per option
29
+ * Specify script arguments as required or optional (when arguments are required `my_app -g foo` will execute, but `my_app -g` will prompt the user to enter arguments)
30
+ * Specify summary and description for your script as well as per command or sub-command
31
+ * A pattern for executing command actions (e.g. a base class to inherit from, with many helper methods)
32
+ * Automatic option to up the verbosity level of output, for debugging purposes
33
+ * Sensible and robust error handling, that distinguishes between library issues and implementation errors
34
+ * Config file support (e.g. `.my_apprc`), JSON format, created in user home directory by default
35
+ * Set up config files to be auto-creatable
36
+ * Built in config file discovery by walking up the directory tree
37
+ * Automatic option to supply a specific config file for current run
38
+ * Config file values can override default values
39
+ * Command line options can override config file values
40
+ * A space for user specific config values in the config file, which is available at runtime
41
+ * Automatic command with options to update and create config files in a default location or anywhere else (e.g. `my_app escort --update-config`)
42
+ * Tool to bootstrap an Escort script (basic, with commands and with sub-commands) (NOT YET IMPLEMENTED)
43
+ * Envrionment aware configuration (NOT YET IMPLEMENTED)
44
+ * Project specific scripts support (NOT YET IMPLEMENTED)
45
+ * Lots of usage examples (COMING SOON)
46
+ * Lots of documentation (COMING SOON)
18
47
 
19
48
  ## Installation
20
49
 
@@ -38,32 +67,354 @@ Let's say you want to do a basic app. As long as `escort` is installed as a gem,
38
67
  #!/usr/bin/env ruby
39
68
 
40
69
  require 'escort'
70
+ require 'my_app'
71
+
72
+ Escort::App.create do |app|
73
+ app.action do |options, arguments|
74
+ MyApp::ExampleCommand.new(options, arguments).execute
75
+ end
76
+ end
77
+ ```
78
+
79
+ If your script is called `app.rb` you is executable, you can then call it like this:
80
+
81
+ ```
82
+ ./app.rb
83
+ ./app.rb --help
84
+ ```
85
+ And it will run whatever code you have in the execute method of your `ExampleCommand`. The command should inherit from `::Escort::ActionCommand::Base` which will give it access to things like `options`, `arguments`, `command_options`, `config` etc.
86
+
87
+ Of course the above configuration didn't let us specify any options (except for `--help`), so lets add some.
88
+
89
+ ```ruby
90
+ #!/usr/bin/env ruby
91
+
92
+ require 'escort'
93
+ require 'my_app'
41
94
 
42
95
  Escort::App.create do |app|
43
- app.version "0.2.5"
96
+ app.options do |opts|
97
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
98
+ end
99
+
100
+ app.action do |options, arguments|
101
+ MyApp::ExampleCommand.new(options, arguments).execute
102
+ end
103
+ end
104
+ ```
105
+ We can now do:
44
106
 
107
+ ```
108
+ ./app.rb -o blah
109
+ ./app.rb --option1=blah
110
+ ```
111
+
112
+ Now when your command is being executed and passed the option through on the command-line, the `command_options` hash will contain the value keyed on the canonical name of your option `:option1`.
113
+
114
+ ### Multi Options
115
+
116
+ Creating an option that can be specified multiple times is dead simple:
117
+
118
+ ```ruby
119
+ #!/usr/bin/env ruby
120
+
121
+ require 'escort'
122
+ require 'my_app'
123
+
124
+ Escort::App.create do |app|
45
125
  app.options do |opts|
46
- opts.opt :global_option, "Global option", :short => '-g', :long => '--global', :type => :string, :default => "global"
47
- opts.opt :multi_option, "Option that can be specified multiple times alksjdfh lakjdfh adf alksdfh alkdfjh alsdfjhaskdjfh alsdkfjh alksfdjh akdfjh alkdsjf alksdjfh alksdfjh asdfjklh aslkdfhj aslkdfjh adfjkhl", :short => '-m', :long => '--multi', :type => :string, :multi => true
126
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
127
+ opts.opt :option2, "Option2", :short => :none, :long => '--option2', :type => :string, :multi => true
48
128
  end
49
129
 
50
130
  app.action do |options, arguments|
51
- puts "Action \nglobal options: #{options} \narguments: #{arguments}"
131
+ MyApp::ExampleCommand.new(options, arguments).execute
52
132
  end
53
133
  end
54
134
  ```
55
135
 
56
- If your file is called `app.rb` you is executable, you can then call it like this:
136
+ We can now do:
137
+
138
+ ```
139
+ ./app.rb --option2=hello --option2=world
140
+ ```
141
+
142
+ Notice that the short form for the multi option was disabled so we had to use the long form, we could, of course create a short form for this option as well.
143
+
144
+ The `command_options` hash in our `ExampleCommand` will have an array of values for multi options.
145
+
146
+ ### Flags
147
+
148
+ Flags are easy, just decalre an option as a `:boolean` and you can use it as a flag and will automatically gain a negation for it as well. For example:
149
+
150
+ ```ruby
151
+ #!/usr/bin/env ruby
152
+
153
+ require 'escort'
154
+ require 'my_app'
155
+
156
+ Escort::App.create do |app|
157
+ app.options do |opts|
158
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :boolean, :default => true
159
+ end
160
+
161
+ app.action do |options, arguments|
162
+ MyApp::ExampleCommand.new(options, arguments).execute
163
+ end
164
+ end
165
+ ```
166
+
167
+ We can now do:
57
168
 
58
169
  ```
59
- ./app.rb
60
170
  ./app.rb -h
61
- ./app.rb -g "hello" foobar
62
- ./app.rb -m "foo" -m "bar" --global="yadda" foobar
171
+ ./app.rb -o
172
+ ./app.rb --option1
173
+ ./app.rb --no-option1
174
+ ```
175
+
176
+ If you use the negation, the value of `:option1` in the command hash will be `false`, otherwise it will be `true`. Since we made `true` the default value, even if you don't specify the option at all, `:option1` will be `true`. If you don't set the default for a flag to `true`, then it will have no negation, since not using the flag will be equivalent to setting to `false`.
177
+
178
+ ### Required Arguments
179
+
180
+ When you define an Escort app, by default it will not require you to supply any arguments to it. However you can specify that arguments are required for this app.
181
+
182
+ ```ruby
183
+ #!/usr/bin/env ruby
184
+
185
+ require 'escort'
186
+ require 'my_app'
187
+
188
+ Escort::App.create do |app|
189
+ app.requires_arguments
190
+
191
+ app.options do |opts|
192
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
193
+ opts.opt :option2, "Option2", :short => :none, :long => '--option2', :type => :string, :multi => true
194
+ end
195
+
196
+ app.action do |options, arguments|
197
+ MyApp::ExampleCommand.new(options, arguments).execute
198
+ end
199
+ end
200
+ ```
201
+
202
+ We can now do:
203
+
63
204
  ```
205
+ ./app.rb -o foo --option2=bar argument1
206
+ ```
207
+
208
+ This will execute fine, but if we do:
209
+
210
+ ```
211
+ ./app.rb -o foo --option2=bar
212
+ ```
213
+
214
+ The app will prompt the user to enter an argument. Pressing `Enter` will allow to enter more arguments. Like with many command-line apps you will need to press `Ctrl-D` to jump out of the prompt and allow the app to execute.
215
+
216
+
217
+ ### Better Help Text
218
+
219
+ You automatically get some nicely formatted help text for your app. But to make the help a bit nicer, make sure you specify a summary and description.
64
220
 
65
- ### Sub Commands
221
+ ```ruby
222
+ #!/usr/bin/env ruby
223
+
224
+ require 'escort'
225
+ require 'my_app'
226
+
227
+ Escort::App.create do |app|
228
+ app.version "0.1.1"
229
+ app.summary "Summary 1"
230
+ app.description "Description 1"
231
+
232
+ app.requires_arguments
233
+
234
+ app.options do |opts|
235
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
236
+ opts.opt :option2, "Option2", :short => :none, :long => '--option2', :type => :string, :multi => true
237
+ end
238
+
239
+ app.action do |options, arguments|
240
+ MyApp::ExampleCommand.new(options, arguments).execute
241
+ end
242
+ end
243
+ ```
244
+
245
+ Your help text will then look like this:
246
+
247
+ ```
248
+ NAME
249
+ app.rb - Summary 1
250
+
251
+ Description 1
252
+
253
+ USAGE
254
+ app.rb [options] [arguments...]
255
+
256
+ VERSION
257
+ 0.1.1
258
+
259
+ OPTIONS
260
+ --option1, -o <s> - Option1 (default: option 1)
261
+ --option2 <s> - Option2
262
+ --verbosity <s> - Verbosity level of output for current execution (e.g. INFO, DEBUG) (default: WARN)
263
+ --error-output-format <s> - The format to use when outputting errors (e.g. basic, advanced) (default: basic)
264
+ --version, -v - Print version and exit
265
+ --help, -h - Show this message
266
+ ```
267
+
268
+ As you can see we've also specified the version, which gives us the `--version` flag.
269
+
270
+ ### Option Dependencies
271
+
272
+ You can set up some options to be dependent on the presence or absence of other options. The app will not execute successfully unless all the dependencies are met. You can make your dependencies quite complex, but it is best to keep it pretty simple.
273
+
274
+ ```ruby
275
+ #!/usr/bin/env ruby
276
+
277
+ require 'escort'
278
+ require 'my_app'
279
+
280
+ Escort::App.create do |app|
281
+ app.options do |opts|
282
+ opts.opt :flag1, "Flag 1", :short => '-f', :long => '--flag1', :type => :boolean
283
+ opts.opt :flag2, "Flag 2", :short => :none, :long => '--flag2', :type => :boolean, :default => true
284
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string
285
+ opts.opt :option2, "Option2", :short => :none, :long => '--option2', :type => :string, :multi => true
286
+ opts.opt :option3, "Option3", :short => :none, :long => '--option3', :type => :string
287
+ opts.opt :option4, "Option4", :short => :none, :long => '--option4', :type => :string
288
+
289
+ opts.dependency :option1, :on => :flag1
290
+ opts.dependency :option2, :on => [:flag1, :option1]
291
+ opts.dependency :option3, :on => {:option1 => 'foo'}
292
+ #opts.dependency :option4, :on => [{:flag1 => false}, :option1] #This will get you into big trouble as it can never be fulfilled
293
+ opts.dependency :option4, :on => [{:flag2 => false}, :option1]
294
+ end
295
+
296
+ app.action do |options, arguments|
297
+ MyApp::ExampleCommand.new(options, arguments).execute
298
+ end
299
+ end
300
+ ```
301
+
302
+ In this case if you're using `:option1` you need to have `:flag1` as well e.g.:
303
+
304
+ ```
305
+ ./app.rb -o foo -f
306
+ ```
307
+
308
+ `:option2` requires both `:option1` and `:flag1` (of course since `:option1` already has a dependency on `:flag1` it is bit redundant, but demostrates the syntax).
309
+
310
+ `:option3` is dependant on `:option1` having a particular value, so we need to have:
311
+
312
+ ```
313
+ ./app.rb -o foo -f --option3=bar
314
+ ```
315
+
316
+ in order for the app to execute successfully, when we've specified `:option3` on the command-line.
317
+
318
+ Note how with `:option4` we can mix the syntax. However also note the commented out dependency, this is how you can get into trouble if you make your dependencies too complex. `:option4` in the comment requires `:option1` and the absence of `:flag1`, but `:option1` requires `:flag1` to be present. Needless to say with that specification, using `:option4` on the command-line will never allow the app to execute successfully.
319
+
320
+
321
+ ### Option Conflicts
322
+
323
+ You can specify 2 or more option as conflicting, which means the app will not execute successfully if more than one of those options are provided on the command-line together.
324
+
325
+ ```ruby
326
+ #!/usr/bin/env ruby
327
+
328
+ require 'escort'
329
+ require 'my_app'
330
+
331
+ Escort::App.create do |app|
332
+ app.options do |opts|
333
+ opts.opt :flag1, "Flag 1", :short => '-f', :long => '--flag1', :type => :boolean
334
+ opts.opt :flag2, "Flag 2", :short => :none, :long => '--flag2', :type => :boolean
335
+
336
+ opts.conflict :flag1, :flag2, :flag3
337
+ end
338
+
339
+ app.action do |options, arguments|
340
+ MyApp::ExampleCommand.new(options, arguments).execute
341
+ end
342
+ end
343
+ ```
344
+
345
+ This will succeed:
346
+
347
+ ```
348
+ ./app.rb --flag1
349
+ ```
350
+
351
+ This will fail:
352
+
353
+ ```
354
+ ./app.rb --flag1 --flag2
355
+ ```
356
+
357
+ Simple!
358
+
359
+
360
+ ### Validations
361
+
362
+ Validations are pretty easy, they can be defined inside the options block. You must provide an option symbol and an error message for when validation fails. The actual validation is a block, when the block evaluates to true, it means validation is successful, othewise validation fails. That's all there is to it.
363
+
364
+ ```ruby
365
+ #!/usr/bin/env ruby
366
+
367
+ require 'escort'
368
+ require 'my_app'
369
+
370
+ Escort::App.create do |app|
371
+ app.options do |opts|
372
+ opts.opt :option1, "Option 1", :short => '-o', :long => '--option1', :type => :string
373
+ opts.opt :int1, "Int 1", :short => '-i', :long => '--int1', :type => :int
374
+ opts.opt :option2, "Option 2", :short => :none, :long => '--option2', :type => :string
375
+
376
+ opts.validate(:option1, "must be either 'foo' or 'bar'") { |option| ["foo", "bar"].include?(option) }
377
+ opts.validate(:int1, "must be between 10 and 20 exclusive") { |option| option > 10 && option < 20 }
378
+ opts.validate(:option2, "must be two words") {|option| option =~ /\w\s\w/}
379
+ opts.validate(:option2, "must be at least 20 characters long") {|option| option.length >= 20}
380
+ end
381
+
382
+ app.action do |options, arguments|
383
+ MyApp::ExampleCommand.new(options, arguments).execute
384
+ end
385
+ end
386
+ ```
387
+
388
+ In this case running:
389
+
390
+ ```
391
+ ./app.rb -o baz
392
+ ```
393
+
394
+ will produce:
395
+
396
+ ```
397
+ option1 must be either 'foo' or 'bar'
398
+ ```
399
+
400
+ But if you run:
401
+
402
+ ```
403
+ ./app.rb -o bar
404
+ ```
405
+
406
+ Everything will work fine.
407
+
408
+ As you can see you may define multiple validation rules for the same option without any issues, all will need to pass for the app to execute successfully.
409
+
410
+
411
+ ### Configuration File
412
+ TODO
413
+
414
+ ### Commands
415
+ TODO
66
416
 
417
+ ### Sub-Commands
67
418
  TODO
68
419
 
69
420
  ## Alternatives