user-choices 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/History.txt +17 -0
  2. data/LICENSE.txt +34 -0
  3. data/Manifest.txt +40 -0
  4. data/README.txt +1 -0
  5. data/Rakefile +19 -0
  6. data/Rakefile.hoe +24 -0
  7. data/examples/older/README.txt +133 -0
  8. data/examples/older/command-line.rb +51 -0
  9. data/examples/older/default-values.rb +47 -0
  10. data/examples/older/multiple-sources.rb +63 -0
  11. data/examples/older/postprocess.rb +45 -0
  12. data/examples/older/switches.rb +50 -0
  13. data/examples/older/two-args.rb +37 -0
  14. data/examples/older/types.rb +67 -0
  15. data/examples/tutorial/index.html +648 -0
  16. data/examples/tutorial/tutorial1.rb +48 -0
  17. data/examples/tutorial/tutorial2.rb +52 -0
  18. data/examples/tutorial/tutorial3.rb +55 -0
  19. data/examples/tutorial/tutorial4.rb +55 -0
  20. data/examples/tutorial/tutorial5.rb +42 -0
  21. data/examples/tutorial/tutorial6.rb +42 -0
  22. data/examples/tutorial/tutorial7.rb +48 -0
  23. data/lib/user-choices/arglist-strategies.rb +178 -0
  24. data/lib/user-choices/builder.rb +89 -0
  25. data/lib/user-choices/command-line-source.rb +220 -0
  26. data/lib/user-choices/command.rb +42 -0
  27. data/lib/user-choices/conversions.rb +154 -0
  28. data/lib/user-choices/ruby-extensions.rb +20 -0
  29. data/lib/user-choices/sources.rb +269 -0
  30. data/lib/user-choices/version.rb +3 -0
  31. data/lib/user-choices.rb +131 -0
  32. data/setup.rb +1585 -0
  33. data/test/arglist-strategy-tests.rb +42 -0
  34. data/test/builder-tests.rb +569 -0
  35. data/test/command-line-source-tests.rb +443 -0
  36. data/test/conversion-tests.rb +157 -0
  37. data/test/set-standalone-test-paths.rb +5 -0
  38. data/test/source-tests.rb +442 -0
  39. data/test/user-choices-slowtests.rb +274 -0
  40. data/user-choices.tmproj +575 -0
  41. metadata +138 -0
@@ -0,0 +1,648 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+
8
+ <title>Using User-Choices</title>
9
+
10
+ </head>
11
+
12
+ <body>
13
+ <h1 style="text-align: center">Using the User-Choices gem</h1>
14
+ <p style="text-align: center"><a href="http://www.exampler.com/blog/" title="Exploration Through Example - Brian Marick's Blog">Brian Marick</a>, <a href="http://www.exampler.com" title="Exampler Consulting">Exampler Consulting</a></p>
15
+
16
+ <p>
17
+ Suppose you have a command-line application that uses some number of, oh, let's say "connections". Most of the time, a user will want the same number of connections, so you'll let them set a personal default in a <em>configuration file</em>. Sometimes they want to change the default. Maybe they'll want a one-time change; for that, a <em>command-line option</em> is best. But sometimes they'll want to make the change for an entire login session; for that, changing an <em>environment variable</em> is most convenient.
18
+ </p>
19
+ <p>
20
+ The User-Choices gem gives your code a unified interface to all those <em>sources</em>. Your code can obey the user's choices without having to bother (much) about how she made them.
21
+ </p>
22
+
23
+ <p>
24
+ This tutorial explains how to set up that interface. See also the <a href="http://user-choices.rubyforge.org/rdoc/" title="user-choices API">API documentation</a>. You can get the source from the <a href="http://rubyforge.org/frs/?group_id=4192" title="RubyForge: User Choices: Project Filelist">downloads page</a> or, less directly, through the <a href="http://rubyforge.org/projects/user-choices" title="RubyForge: User Choices: Project Info">project page</a>.
25
+ </p>
26
+ <a href="#using">1. Using the choices during execution</a><br />
27
+ <a href="#sources">2. Defining the sources</a><br />
28
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#environmentsource">Environment variables</a><br />
29
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#yamlsource">YAML files</a><br />
30
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#xmlsource">XML files</a><br />
31
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#commandlinesource">The command line</a><br />
32
+ <a href="#choices">3. Defining the choices</a><br />
33
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#keywordargs">3.1 Keyword arguments for <code>add_choice</code></a><br />
34
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#cmdline">3.2 Extra behavior for the command line</a><br />
35
+ <a href="#postprocessing">4. Optionally touching up choices before execution</a><br />
36
+ <a href="#notes">5. Notes</a><br />
37
+ <p>
38
+
39
+ </p>
40
+
41
+
42
+ <h2><a name="using">1. Using the choices during execution</a></h2>
43
+ <p>
44
+ Your program will use a variant of the <a href="http://en.wikipedia.org/wiki/Command_pattern" title="Command pattern - Wikipedia, the free encyclopedia">Command pattern</a>. Its rough structure will look like this:
45
+ </p>
46
+
47
+ <blockquote><table bgcolor="white" border="0"><tr><td>
48
+ <code>
49
+ require 'user-choices' <br />
50
+ &nbsp; <br />
51
+ class TutorialExample &lt; UserChoices:<font style="color: #800020;">:Command</font> <br />
52
+ &nbsp;&nbsp;include UserChoices <br />
53
+ &nbsp; <br />
54
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_sources(builder)</font>... <br />
55
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_choices(builder)</font>... <br />
56
+ <br />
57
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">execute</font> <br />
58
+ &nbsp;&nbsp;&nbsp;&nbsp;puts "There are <font style="color: #800020;">#{<font style="color: #806000;">@user_choices</font>[<font style="color: #800020;">:connections</font>]} connections."</font> <br />
59
+ &nbsp;&nbsp;&nbsp;&nbsp;pp <font style="color: #806000;">@user_choices</font> <br />
60
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
61
+ <font style="color: #002FBD;">end</font> <br />
62
+ &nbsp; <br />
63
+ &nbsp; <br />
64
+ if $0 == __FILE__ <br />
65
+ &nbsp;&nbsp;TutorialExample.new.execute <br />
66
+ <font style="color: #002FBD;">end</font> <br />
67
+ </code>
68
+
69
+ </td></tr></table></blockquote>
70
+
71
+ <p>
72
+ Your entire program runs within the <code>execute</code> method of your <code>Command</code> object. The instance variable <code>@user_choices</code> is a hash whose keys are symbols you've used to name the choices. Its values are the user's choices.
73
+ </p>
74
+
75
+ <p>
76
+ Within <code>add_choices</code> I've set up the choices to default to 0, so if I were to run the program without making any choices, I'd get this result:
77
+ </p>
78
+
79
+ <blockquote>
80
+ <code>
81
+ $ ruby tutorial1.rb<br/>
82
+ There are 0 connections.<br/>
83
+ {:connections=>0}<br/>
84
+ </code>
85
+ </blockquote>
86
+
87
+ <p>In the <code>add_sources</code> method, I've also told <code>TutorialExample</code> to obey a <code>.myprog-config.yml</code> file in my home directory. Suppose it looks like this:</p>
88
+
89
+ <blockquote>
90
+ <code>
91
+ connections: 19
92
+ </code>
93
+ </blockquote>
94
+
95
+ <p>In that case, the output would be:</p>
96
+
97
+ <blockquote>
98
+ <code>
99
+ $ ruby tutorial1.rb<br/>
100
+ There are 19 connections.<br/>
101
+ {:connections=>19} <br/>
102
+ </code>
103
+ </blockquote>
104
+
105
+ <p>
106
+ The configuration file value took precedence over the default. An environment variable's value can, in turn, take precedence over that:
107
+ </p>
108
+
109
+ <blockquote>
110
+ <code>
111
+ $ (export <strong>myprog_connections=3</strong>; ruby tutorial1.rb) <br/>
112
+ There are 3 connections. <br/>
113
+ {:connections=>3} <br/>
114
+ </code>
115
+ </blockquote>
116
+
117
+ <p>
118
+ And a command-line choice can take precedence over the environment:
119
+ </p>
120
+
121
+ <blockquote>
122
+ <code>
123
+ $ (export myprog_connections=3; ruby tutorial1.rb <b>--connections 999</b>) <br/>
124
+ There are 999 connections. <br/>
125
+ {:connections=>999} <br/>
126
+ </code>
127
+ </blockquote>
128
+
129
+ <p>Part of the point of User-Choices is reasonably helpful error messages. For example, here's the result of a bad value for the number of connections:
130
+ </p>
131
+ <blockquote>
132
+ <code>
133
+ > (export <strong>myprog_connections=hi</strong>; ruby tutorial1.rb)<br />
134
+ Error in the environment: myprog_connections's value must be an integer, and 'hi' doesn't look right.<br />
135
+ </code></blockquote>
136
+
137
+ <p>Notice that the error messsage is in terms of the source (the environment variable "myprog_connections", not the internal symbol <code>:connections</code>).
138
+ </p>
139
+ <p>
140
+ In the case of a command-line error, more help text is printed:</p>
141
+
142
+ <blockquote>
143
+ <code>
144
+ $ ruby tutorial1.rb <strong>--connections hi</strong> <br />
145
+ Error in the command line: --connections's value must be an integer, and 'hi' doesn't look right. <br />
146
+ Usage: ruby tutorial1.rb [options] <br />
147
+ <br />
148
+ Options: <br />
149
+ -c, --connections COUNT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Number of connections to open. <br />
150
+ -?, -h, --help&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Show this message. <br /> </code></blockquote>
151
+
152
+
153
+ <h2><a name="sources">2. Defining the sources</a></h2>
154
+ <p>
155
+ The different sources for the tutorial program are configured in <code>add_sources</code>:</p>
156
+
157
+ <blockquote><table bgcolor="white" border="0"><tr><td>
158
+ <code>
159
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_sources</font>(builder) <br />
160
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_source(CommandLineSource, <font style="color: #800020;">:usage</font>, <br />
161
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Usage ruby #{$0} [options]")<br />
162
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_source(EnvironmentSource, <font style="color: #800020;">:with_prefix</font>, "myprog_") <br />
163
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_source(YamlConfigFileSource, <font style="color: #800020;">:from_file</font>, ".myprog-config.yml") <br />
164
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
165
+ &nbsp; <br />
166
+ </code>
167
+
168
+ </td></tr></table></blockquote>
169
+
170
+ <p>Behind the scenes, User-Choices creates a <code>ChoicesBuilder</code> object it hands to the configuration method <code>add_sources</code>. To harvest choices from a new source, you identify it to the <code>builder</code> with an <code>add_source</code> call. The calls should be made in precedence order, highest to lowest. A source for default values comes automatically, so you don't need to list it.
171
+ </p>
172
+ <p>
173
+ The sources are distinguished by the names of classes. Each class takes different arguments. Notice that the arguments are in a weird pseudo-keyword style:</p>
174
+ <blockquote>
175
+ builder.add_source(<i>ClassName</i>, <font style="color: #800020;">:<i>symbol</i></font>, <i>one or more args</i>, <font style="color: #800020;">:<i>symbol</i></font>, <i>one or more args</i>, ...)
176
+ </blockquote>
177
+
178
+ <p>I make no apologies. Well, maybe one or two.</p>
179
+
180
+ <dl>
181
+ <dt><b><a name="environmentsource">EnvironmentSource</a></b></dt>
182
+ <dd>
183
+ <p>
184
+ There are two ways of specifying which environment variables should be considered choices for this program.
185
+ </p>
186
+ <ul>
187
+ <li>
188
+ <p>
189
+ <b><code>builder.add_source(EnvironmentSource, <font style="color: #800020;">:with_prefix</font>, "prefix")</code></b> says that any environment variable beginning with the prefix is a user choice that matters to this program. The symbol-name of the choice is constructed from the environment variable name, less the prefix. So if the prefix is "amazon_", the environment variable "amazon_login" produces choice <font style="color: #800020;">:login</font>.
190
+ </p>
191
+ </li>
192
+ <li>
193
+ <p>
194
+ <b><code>builder.add_source(EnvironmentSource, <font style="color: #800020;">:mapping</font>, <i>hash</i>)</code></b> gives an explicit map between choice names and environment variable names. So if the hash is <code>{<font style="color: #800020;">:home</font> => "HOME", <font style="color: #800020;">:shell_level</font> => "SHLVL"}</code>, the program would (on my machine) have <code>@user_choices[<font style="color: #800020;">:home</font>]</code> be "/Users/marick" and <code>@user_choices[<font style="color: #800020;">:shell_level</font>]</code> be 1.
195
+ </p>
196
+ </li>
197
+ </ul>
198
+ <p>
199
+ You can use both ways by chaining them together:
200
+ </p>
201
+
202
+ <blockquote><table bgcolor="white" border="0"><tr><td>
203
+ <code>
204
+ &nbsp;&nbsp;&nbsp;builder.add_source(EnvironmentSource, <font style="color: #800020;">:with_prefix</font>, "prefix_", <font style="color: #800020;">:mapping</font>, {<font style="color: #800020;">:home</font> => "HOME" }) <br />
205
+ </code>
206
+
207
+ </td></td></table></blockquote>
208
+
209
+ </dd>
210
+
211
+ <dt><b><a name="yamlsource">YamlConfigFileSource</a></b></dt>
212
+ <dd>
213
+ <p>
214
+ <b><code>builder.add_source(YamlConfigFileSource, <font style="color: #800020;">:from_file</font>, "filename")</code></b> says that the user choices are in a <a href="http://yaml.org/" title="YAML Ain't Markup Language">YAML</a> file named "filename" in the user's home directory. The home directory is found the same way RubyGems finds it.
215
+ </p>
216
+ <p>
217
+ YAML files should contain a single level of keys and values. The values can be numbers, strings, and arrays of numbers or strings. Here is an acceptable file:
218
+ </p>
219
+ <blockquote>
220
+ ordinary_choice: 2<br/>
221
+ names:<br/>
222
+ &nbsp;&nbsp; - dawn<br/>
223
+ &nbsp;&nbsp; - paul<br/>
224
+ &nbsp;&nbsp; - sophie<br/>
225
+
226
+ </blockquote>
227
+ <p>
228
+ The keys are turned into symbols and become the choice names. The above file produces <font style="color: #800020;">:ordinary_choice</font> and <font style="color: #800020;">:names</font>. If the key has a dash in it, that's converted to an underscore.</p>
229
+ <p> The values, by default, are strings (or arrays of strings), though that can be overridden when the choice is described.
230
+ </p>
231
+ <p>
232
+ The results of more complicated files are undefined.
233
+ </p>
234
+ </dd>
235
+
236
+ <dt><b><a name="xmlsource">XmlConfigFileSource</a></b></dt>
237
+ <dd>
238
+ <p> <b><code>builder.add_source(XmlConfigFileSource, <font style="color: #800020;">:from_file</font>, "filename")</code></b> says that the user choices are in a XML file named "filename" in the user's home directory. The home directory is found the same way RubyGems finds it.
239
+ </p>
240
+ <p>
241
+ Here is an acceptable XML file:
242
+ </p>
243
+ <blockquote>
244
+ &lt;config&gt; <br />
245
+ &nbsp;&nbsp;&nbsp;&lt;ordinary_choice&gt;2&lt;/ordinary_choice&gt; <br />
246
+ &nbsp;&nbsp;&nbsp;&lt;names&gt;dawn&lt;/names&gt; <br />
247
+ &nbsp;&nbsp;&nbsp;&lt;names&gt;paul&lt;/names&gt; <br />
248
+ &nbsp;&nbsp;&nbsp;&lt;names&gt;sophie&lt;/names&gt; <br />
249
+ &lt;/config&gt; <br />
250
+ </blockquote>
251
+ <p>
252
+ The root tag (<code>&lt;config&gt;</code>) is irrelevant. Name it what you like. The tag names are converted into choice symbols, so <code>&lt;ordinary_choice&gt;</code> becomes <font style="color: #800020;">:ordinary_choice</font>. If the tag name contains a dash, it's converted into an underscore.
253
+ </p>
254
+ <p>
255
+ Tag text contents have whitespace stripped off, then become string choice values. (This default typing can be changed in <code>add_choices</code>.) If you want an array of values, you repeat the tag multiple times (as in &lt;names&gt; above).
256
+ </p>
257
+ <p>
258
+ The results of more complicated files are undefined.
259
+ </p>
260
+
261
+ </dd>
262
+
263
+ <dt><b><a name="commandlinesource">CommandLineSource</a></b></dt>
264
+ <dd>
265
+ <p>
266
+ User-Choices uses <a href="http://www.ruby-doc.org/stdlib/" title="Ruby Standard Library Documentation: OptionParser">OptionParser</a> to handle command lines. Arguments to <code>builder.add_source</code> and (later) <code>builder.add_choice</code> are passed along to <code>OptionParser</code> to help it do its work. Part of its work is generating helpful output to show on request or in the case of a user error, and the <code>add_source</code> call helps with that.
267
+ </p>
268
+ <p>
269
+
270
+ <b><code>builder.add_source(CommandLineSource, <font style="color: #800020;">:usage</font>, <em>"line"...</em>)</code></b> gives <code>OptionParser</code> lines to print before it describes command-line options. Here's a typical example:
271
+ </p>
272
+
273
+ <blockquote><table bgcolor="white" border="0"><tr><td>
274
+ <code>
275
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_source(CommandLineSource, <font style="color: #800020;">:usage</font>, <br />
276
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Usage: ruby <font style="color: #800020;">#{$0} [options] input-file output-file",</font> <br />
277
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Encode the input file into the output file.") <br />
278
+ &nbsp; <br />
279
+ </code>
280
+
281
+ </td></tr></table></blockquote>
282
+ </dd>
283
+ </dl>
284
+
285
+
286
+
287
+
288
+
289
+
290
+
291
+
292
+ <h2><a name="choices">3. Defining the choices</a></h2>
293
+ <p>
294
+ Once the sources of choices have been named, each different choice needs to be described. Here's the description for the tutorial program:
295
+ </p>
296
+ <blockquote><table bgcolor="white" border="0"><tr><td>
297
+ <code>
298
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_choices</font>(builder) <br />
299
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_choice(<font style="color: #800020;">:connections</font>, <font style="color: #800020;">:type</font>=><font style="color: #800020;">:integer</font>, :<font style="color: #800020;">default</font>=>0) { | command_line | <br />
300
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command_line.uses_option("-c", "--connections COUNT", <br />
301
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Number of connections to open.") <br />
302
+ &nbsp;&nbsp;&nbsp;&nbsp;} <br />
303
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
304
+ </code>
305
+
306
+ </td></tr></table></blockquote>
307
+ <p>
308
+ In this case, there's one choice (<font style="color: #800020;">:connections</font>). The <font style="color: #800020;">:type</font> keyword arguments tells User-Choices to convert a string fetched from any defined source (in this case, the command line) into an integer. The <font style="color: #800020;">:default</font> keyword argument gives a value to use if the user doesn't give one on the command line.
309
+ </p>
310
+
311
+ <p>The block argument is used to do additional setup for the command line. That'll be explained <a href="#cmdline">shortly</a>.
312
+ </p>
313
+
314
+
315
+ <h3><a name="keywordargs"/>3.1 Keyword arguments for <code>add_choice</code></a></h3>
316
+ <ul>
317
+ <li><p>
318
+ The <b><font style="color: #800020;">:type</font></b> keyword describes type checking and conversion that should be done. In all cases, it's fine if the conversion has already been done (which is often the case when a default value is given). If no <font style="color: #800020;">:type</font> argument is given, the choice stays a string.
319
+ </p>
320
+
321
+ <p>
322
+ A <code>StandardError</code> is raised if the conversion can't be done.
323
+ </p>
324
+ <p>
325
+ Normal YAML parsing converts text into native types. (A YAML line like "count: 1" is automatically parsed into <code>{"count" => 1}</code>.) These conversions <em>do not happen</em> in User-Choices because there's no equivalent for other sources like XML.)
326
+ </p>
327
+ <dl>
328
+ <dt><font style="color: #800020;">:type</font> => <b><font style="color: #800020;">:integer</font></b></dt>
329
+ <dd><p>
330
+ Convert the string into an integer. Accepts whatever <code>String#to_i</code> does.
331
+ </p></dd>
332
+
333
+ <dt><font style="color: #800020;">:type</font> => <b><font style="color: #800020;">:boolean</font></b></dt>
334
+ <dd><p>
335
+ Convert the string into either <code>true</code> or <code>false</code>. The string must be one of "true" or "false" (case-insensitive).
336
+ </p></dd>
337
+
338
+ <dt><font style="color: #800020;">:type</font> => <b><font style="color: #800020;">[:string]</font></b></dt>
339
+
340
+ <dd><p>
341
+ The string is split at comma boundaries to become an array of strings. Whitespace is <em>not</em> stripped. This type need only be declared when the data could come from a command-line argument of this form: <code>--names a,b,c</code> or an environment variable like <code>NAMES="a,b,c"</code>. YAML and XML files describe the data so that User-Choices doesn't need help to know you want an array.
342
+ </p></dd>
343
+
344
+ <dt><font style="color: #800020;">:type</font> => <i>["one", "two", ...]</i></dt>
345
+ <dd><p>
346
+ The value chosen must be one of the given strings. There's no conversion.
347
+ </p></dd>
348
+
349
+ </dl>
350
+ </li>
351
+
352
+ <li><p>
353
+ The <b><font style="color: #800020;">:length</font></b> keyword applies only when the given value is converted to an array. It takes either an integer or range. A <code>StandardError</code> with a helpful message is raised if the actual length doesn't match.
354
+ </p></li>
355
+
356
+ <li><p>
357
+ The <b><font style="color: #800020;">:default</font></b> keyword gives a default value for a choice. That value is type-checked if a type is given. (But note that type-checking succeeds if the default value is already of the correct type. There's no need to use <code>"1"</code> for a choice of type :integer. You can use the more natural <code>1</code>. )
358
+ </p>
359
+ <p>
360
+ If no default is given, and no value is specified in any source, the choice-symbol (e.g., <font style="color: #800020;">:connections</font>) is not a key in the <code>@user_choices</code> hash (and so <code>@user_choices[<font style="color: #800020;">:connections</font>]</code> would be <code>nil</code> and <code>@user_choices.has_key?(<font style="color: #800020;">:connections</font>)</code> would be <code>false</code>).
361
+
362
+ </p></li>
363
+ </ul>
364
+ <h3><a name="cmdline">3.2 Extra behavior for the command line</a></h3>
365
+ <p>
366
+ The block given to <code>:add_choice</code> serves as a front end to <a href="http://www.ruby-doc.org/stdlib/" title="Ruby Standard Library Documentation: OptionParser">OptionParser</a>. It also lets you treat command-line arguments as just another kind of user choice (rather than having to mess around with <code>ARGV</code>).
367
+ </p>
368
+ <p>
369
+ Within the block, you can send these messages to the block's argument:
370
+ </p>
371
+
372
+ <dl>
373
+ <dt><b>uses_option(<i>string</i>...)</b></dt>
374
+ <dd>
375
+ <p>
376
+ The strings are passed on to <code>OptionParser#on</code>. There are quite possibly variations that don't work well. (If you find any, <a href="mailto:marick@exampler.com">send me mail</a>.) The common variation that definitely works looks like this:
377
+ </p>
378
+
379
+ <blockquote><table bgcolor="white" border="0"><tr><td>
380
+ <code>
381
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command_line.uses_option("-c", "--connections COUNT", <br />
382
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Number of connections to open.") <br />
383
+ </code>
384
+
385
+ </td></tr></table></blockquote>
386
+ <p>The first argument is the short form of the option, the second the long form, and any remaining lines are documentation to print in the help text. Given the above, any of these are acceptable:
387
+ </p>
388
+
389
+ <blockquote><code>
390
+ $ ruby tutorial1.rb -c 2 <br />
391
+ $ ruby tutorial1.rb -c2 <br />
392
+ $ ruby tutorial1.rb --connections 2 <br />
393
+ $ ruby tutorial1.rb --connections=2 <br />
394
+ $ ruby tutorial1.rb --conn 2 <br />
395
+ </code></blockquote>
396
+
397
+ </dd>
398
+
399
+ <dt><b>uses_switch("-<i>s</i>", "--<i>switch</i>", <i>string</i>...)</b></dt>
400
+ <dd>
401
+ <p>
402
+ Switches are almost like options, but they don't take arguments. If the switch is given, the user choice is "true". If its inverse (see below) is given, the choice is "false". Otherwise, the default is used. Here's a typical example:
403
+ </p>
404
+
405
+ <blockquote><table bgcolor="white" border="0"><tr><td>
406
+ <code>
407
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_choice(<font style="color: #800020;">:ssh</font>, <font style="color: #800020;">:type</font>=><font style="color: #800020;">:boolean</font>, :<font style="color: #800020;">default</font>=>false) { | command_line | <br />
408
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command_line.uses_switch("-s", "--ssh", <br />
409
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Use ssh to open connection.") <br />
410
+ &nbsp;&nbsp;&nbsp;&nbsp;} <br />
411
+ </code>
412
+
413
+ </td></tr></table></blockquote>
414
+ <p>
415
+ A user tells the program to use SSH like this:
416
+ </p>
417
+
418
+
419
+ <blockquote><code>
420
+ $ ruby tutorial2.rb --ssh<br />
421
+ SSH should be used. <br />
422
+ $ ruby tutorial2.rb --s file <br />
423
+ SSH should be used. <br />
424
+ </code></blockquote>
425
+
426
+ <p>SSH is turned off like this:</p>
427
+
428
+ <blockquote><code>
429
+ $ ruby tutorial2.rb --no-ssh <br />
430
+ SSH should not be used. <br />
431
+ </code></blockquote>
432
+
433
+ <p>
434
+ (Turning the switch off is pointless in this case, since the default is <code>false</code>.)
435
+ </p>
436
+ <p>
437
+ The documentation explains both options this way:
438
+
439
+ </p>
440
+ <blockquote><code>
441
+ $ ruby tutorial2.rb --help <br />
442
+ Usage: ruby tutorial2.rb [options]<br />
443
+ <br />
444
+ Options: <br />
445
+ -c, --connections COUNT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Number of connections to open. <br />
446
+ <b>-s, --[no-]ssh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Use ssh to open connection.</b> <br />
447
+ -?, -h, --help&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Show this message. <br />
448
+ </code></blockquote>
449
+
450
+ </dd>
451
+
452
+ <dt><b>uses_arglist</b></dt>
453
+ <dd>
454
+ <p>
455
+ The command line argument list can be treated as another choice:
456
+ </p>
457
+
458
+ <blockquote><table bgcolor="white" border="0"><tr><td>
459
+ <code>
460
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_choice(<font style="color: #800020;">:files</font>) { | command_line | <br />
461
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command_line.uses_arglist <br />
462
+ &nbsp;&nbsp;&nbsp;&nbsp;} <br />
463
+ </code>
464
+
465
+ </td></tr></table></blockquote>
466
+
467
+ <blockquote><code>
468
+ $ ruby tutorial3.rb arg1 arg2 <br />
469
+ SSH should not be used. <br />
470
+ There are 19 connections. <br />
471
+ {<b>:files=>["arg1", "arg2"]</b>, :ssh=>false, :connections=>19} <br />
472
+ </code></blockquote>
473
+
474
+ <p>
475
+ What should happen if no command-line arguments are given? If that were to be treated as choosing the empty array, lesser priority sources could never affect the outcome. Since everything is typically of lower priority than the command line, that would make it pointless for a configuration file, say, ever to specify any values for the choice. So, instead, an empty argument list is treated just like a command-line option that's not mentioned: nothing is put into the <code>@user-choices</code> array.
476
+ </p>
477
+ <p>
478
+ As an example of how this works, consider this YAML file:
479
+ </p>
480
+
481
+ <blockquote><code>
482
+ connections: 19 <br />
483
+ files: <br />
484
+ &nbsp;&nbsp; - one <br />
485
+ &nbsp;&nbsp; - two <br />
486
+ </code></blockquote>
487
+
488
+ <p>
489
+ Any command-line arguments will take precedence:
490
+ </p>
491
+
492
+ <blockquote><code>
493
+ $ ruby tutorial3.rb <strong>cmd</strong> <br />
494
+ SSH should not be used. <br />
495
+ There are 19 connections. <br />
496
+ {<b>:files=>["cmd"]</b>, :ssh=>false, :connections=>19} <br />
497
+ </code></blockquote>
498
+
499
+ <p>
500
+ But an empty command line will yield to the YAML file:
501
+ </p>
502
+ <blockquote><code>
503
+ $ ruby tutorial3.rb <br />
504
+ SSH should not be used. <br />
505
+ There are 19 connections. <br />
506
+ {<b>:files=>["one", "two"]</b>, :ssh=>false, :connections=>19} <br />
507
+ </code></blockquote>
508
+
509
+ <p>
510
+ There is no need to <font style="color: #800020;">:type</font> the choice as a [<font style="color: #800020;">:string</font>], since that's obvious from context. If you want to limit the length of the argument list, you can add a <font style="color: #800020;">:length</font>:
511
+ </p>
512
+
513
+ <blockquote><table bgcolor="white" border="0"><tr><td>
514
+ <code>
515
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_choice(<font style="color: #800020;">:files</font>, <b><font style="color: #800020;">:length</font> => 1..2</b>) { | command_line | <br />
516
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command_line.uses_arglist <br />
517
+ &nbsp;&nbsp;&nbsp;&nbsp;} <br />
518
+ </code>
519
+
520
+ </td></tr></table></blockquote>
521
+
522
+ <blockquote><code>
523
+ $ ruby tutorial4.rb 1 2 3 <br />
524
+ <b>Error in the command line: 3 arguments given, 1 or 2 expected.</b> <br />
525
+ Usage: ruby tutorial4.rb [options] file1 [file2] <br />
526
+ <br />
527
+ Options: <br />
528
+ -c, --connections COUNT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Number of connections to open. <br />
529
+ -s, --[no-]ssh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Use ssh to open connection. <br />
530
+ -?, -h, --help&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Show this message. <br />
531
+ </code></blockquote>
532
+ <p>
533
+ What happens in this case if the user gives no arguments on the command line, considering that the <font style="color: #800020;">:length</font> asks for one or two? It is <em>not</em> an error because the YAML file supplies two arguments:
534
+ </p>
535
+ <blockquote><code>
536
+ $ ruby tutorial4.rb <br />
537
+ SSH should not be used. <br />
538
+ There are 19 connections. <br />
539
+ {:files=>["one", "two"], :ssh=>false, :connections=>19} <br />
540
+ </code></blockquote>
541
+ </dd>
542
+ <dt><b>uses_arg</b></dt>
543
+ <dd>
544
+ <p>
545
+ Sometimes the argument list must have exactly one member. In that case, it's annoying to have to take that argument out of the array. <code>users_arg</code> does that for you, returning the single argument directly. Giving two or more arguments produces an error. If the user supplies no arguments on the command line, some other source must provide it. If no other source does, User-Choices raises a <code>StandardError</code> with a nice error message.
546
+ </p>
547
+
548
+ <blockquote><table bgcolor="white" border="0"><tr><td>
549
+ <code>
550
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_choices</font>(builder) <br />
551
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_choice(<font style="color: #800020;">:infile</font>) { | command_line | <br />
552
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>command_line.uses_arg</b> <br />
553
+ &nbsp;&nbsp;&nbsp;&nbsp;} <br />
554
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
555
+ </code>
556
+
557
+ </td></td></table></blockquote>
558
+
559
+ <blockquote><code>
560
+ $ ruby tutorial5.rb 1 <br />
561
+ {:infile=>"1"} <br />
562
+ $ ruby tutorial5.rb <br />
563
+ Error in the command line: 0 arguments given, 1 expected. <br />
564
+ Usage: ruby tutorial5.rb infile <br />
565
+ <br />
566
+ Options: <br />
567
+ -?, -h, --help&nbsp;&nbsp;&nbsp;Show this message. <br />
568
+ </code></blockquote>
569
+ </dd>
570
+
571
+ <dt><b>uses_optional_arg</b></dt>
572
+ <dd>
573
+ <p>
574
+ This is like <code>uses_arg</code> except that it's OK for the user to provide no argument (either on the command line or some other source). In that case, the choice symbol doesn't appear as a key in the result.
575
+ </p>
576
+
577
+ <blockquote><table bgcolor="white" border="0"><tr><td>
578
+ <code>
579
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_choices</font>(builder) <br />
580
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_choice(<font style="color: #800020;">:infile</font>) { | command_line | <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>command_line.uses_optional_arg</b> <br />
581
+ &nbsp;&nbsp;&nbsp;&nbsp;} <br />
582
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
583
+ </code>
584
+
585
+ </td></td></table></blockquote>
586
+
587
+ <blockquote><code>
588
+ $ ruby tutorial6.rb <br />
589
+ {} <br />
590
+ </code></blockquote>
591
+ </dd>
592
+
593
+ </dl>
594
+
595
+
596
+ <h2><a name="postprocessing">4. Optionally touching up choices before execution</a></h2>
597
+ <p>
598
+ Consider this declaration:
599
+ </p>
600
+
601
+ <blockquote><table bgcolor="white" border="0"><tr><td>
602
+ <code>
603
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_sources</font>(builder) <br />
604
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_source(CommandLineSource, <font style="color: #800020;">:usage</font>, <br />
605
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Usage: ruby <font style="color: #800020;">#{$0} <strong>infile outfile</strong>")</font> <br />
606
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
607
+ &nbsp; <br />
608
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">add_choices</font>(builder) <br />
609
+ &nbsp;&nbsp;&nbsp;&nbsp;builder.add_choice(<font style="color: #800020;">:files</font>, <font style="color: #800020;">:length</font> => 2) { | command_line | <br />
610
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command_line.<strong>uses_arglist</strong> <br />
611
+ &nbsp;&nbsp;&nbsp;&nbsp;} <br />
612
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
613
+ </code>
614
+
615
+ </td></tr></table></blockquote>
616
+
617
+ <p>
618
+ What you want is two <code>@user_choices</code> elements, something like <code>@user_choices[<font style="color: #800020;">:infile</font>]</code> and <code>@user_choices[<font style="color: #800020;">:outfile</font>]</code>. But there's no way to associate names with individual arglist elements, just with the whole thing.
619
+ </p>
620
+
621
+ <p>The solution to the problem is a postprocessing step that allows you to modify <code>@user_choices</code> before your program starts its work. That's done like this:
622
+ </p>
623
+ <blockquote><table bgcolor="white" border="0"><tr><td>
624
+ <code>
625
+ &nbsp;&nbsp;<font style="color: #002FBD;">def</font> <font style="color: #600080;">postprocess_user_choices</font> <br />
626
+ &nbsp;&nbsp;&nbsp;&nbsp;<font style="color: #806000;">@user_choices</font>[<font style="color: #800020;">:infile</font>] = <font style="color: #806000;">@user_choices</font>[<font style="color: #800020;">:files</font>][0] <br />
627
+ &nbsp;&nbsp;&nbsp;&nbsp;<font style="color: #806000;">@user_choices</font>[<font style="color: #800020;">:outfile</font>] = <font style="color: #806000;">@user_choices</font>[<font style="color: #800020;">:files</font>][1] <br />
628
+ &nbsp;&nbsp;<font style="color: #002FBD;">end</font> <br />
629
+ </code>
630
+
631
+ </td></tr></table></blockquote>
632
+
633
+ <blockquote><code>
634
+ $ ruby tutorial7.rb one two <br />
635
+ {:outfile=>"two", :files=>["one", "two"], :infile=>"one"} <br /> </code></blockquote>
636
+
637
+ <p>There's no need to call <code>postprocess_user_choices</code> yourself. It's done automatically.</p>
638
+
639
+ <h2><a name="notes">5. Notes</a></h2>
640
+ <ul>
641
+ <li>
642
+ <p>
643
+ Right now, a source can provide an element to <code>@user_choices</code> even if that element is never named as a choice in <code>add_choices</code>. I'm not sure if that's good, bad, or indifferent.
644
+ </p>
645
+ </li>
646
+ </ul>
647
+ </body>
648
+ </html>