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,442 @@
1
+ load "set-standalone-test-paths.rb" unless $started_from_rakefile
2
+ require 'test/unit'
3
+ require 's4t-utils'
4
+ require 'builder'
5
+ require 'user-choices'
6
+ include S4tUtils
7
+ require 'extensions/string'
8
+
9
+
10
+ # The general contract of these objects.
11
+ class TestAbstractSource < Test::Unit::TestCase
12
+ include UserChoices
13
+
14
+ class SubHash < UserChoices::AbstractSource
15
+
16
+ # New never takes arguments. Class-specific initialization is done
17
+ # with an appropriately-named method.
18
+ #
19
+ # That method must return self and set up the external_name hash
20
+ # so that all symbols handled by this object can be given an external
21
+ # name.
22
+ def only_symbol(symbol, external_name, value)
23
+ @external_names[symbol] = external_name
24
+ @symbol = symbol
25
+ @value = value
26
+ self
27
+ end
28
+
29
+ # After fill(), values have been read, but not checked.
30
+ def fill; self[@symbol] = @value; end
31
+
32
+ def source; "the test hash"; end
33
+
34
+ end
35
+
36
+ def test_specific_initializer_notes_external_names
37
+ sh = SubHash.new.only_symbol(:sym, "name", "val")
38
+ assert_equal('name', sh.external_names[:sym])
39
+ end
40
+
41
+ def test_filling_sets_values
42
+ sh = SubHash.new.only_symbol(:sym, "name", "val")
43
+ sh.fill
44
+ assert_equal('val', sh[:sym])
45
+ end
46
+
47
+ def test_will_do_conversion_when_told
48
+ sh = SubHash.new.only_symbol(:sym, "name", "1")
49
+ sh.fill
50
+
51
+ conversions = { :sym => [Conversion.for(:integer)] }
52
+ sh.apply(conversions)
53
+ assert_equal(1, sh[:sym])
54
+ end
55
+
56
+ # Checking - really just want to know that the error message comes out right.
57
+
58
+ def test_will_do_integer_error_checking_when_told
59
+ sh = SubHash.new.only_symbol(:sym, "name", "val")
60
+ sh.fill
61
+
62
+ conversions = { :sym => [Conversion.for(:integer)] }
63
+ assert_raises_with_matching_message(StandardError,
64
+ /^Error in the test hash: name's value must be an integer, and 'val' doesn't look right/) {
65
+ sh.apply(conversions)
66
+ }
67
+ end
68
+
69
+
70
+ def test_will_do_boolean_error_checking_when_told
71
+ sh = SubHash.new.only_symbol(:sym, "name", "val")
72
+ sh.fill
73
+
74
+ conversions = { :sym => [Conversion.for(:boolean)] }
75
+ assert_raises_with_matching_message(StandardError,
76
+ /^Error in the test hash: name's value must be a boolean, and 'val' doesn't look right/) {
77
+ sh.apply(conversions)
78
+ }
79
+ end
80
+
81
+
82
+ def test_will_do_alternative_error_checking_when_told
83
+ sh = SubHash.new.only_symbol(:sym, "name", "val")
84
+ sh.fill
85
+
86
+ conversions = { :sym => [Conversion.for(["foo", "bar"])] }
87
+ assert_raises_with_matching_message(StandardError,
88
+ /^Error in the test hash: name's value must be one of 'foo' or 'bar', and 'val' doesn't look right/) {
89
+ sh.apply(conversions)
90
+ }
91
+ end
92
+
93
+
94
+ def test_will_do_exact_length_checking_when_told
95
+ sh = SubHash.new.only_symbol(:sym, "name", ["one", "two"])
96
+ sh.fill
97
+
98
+ conversions = { :sym => [Conversion.for([:string]), # actually not needed.
99
+ Conversion.for(:length => 5)] }
100
+ assert_raises_with_matching_message(StandardError,
101
+ /^Error in the test hash: name's value must be of length 5, and \["one", "two"\] doesn't look right/) {
102
+ sh.apply(conversions)
103
+ }
104
+ end
105
+
106
+
107
+ def test_will_do_range_length_checking_when_told
108
+ sh = SubHash.new.only_symbol(:sym, "name", ["one", "two"])
109
+ sh.fill
110
+
111
+ conversions = { :sym => [Conversion.for([:string]), # actually not needed.
112
+ Conversion.for(:length => 3..5)] }
113
+ assert_raises_with_matching_message(StandardError,
114
+ /^Error in the test hash: name's value must be a list whose length is in this range: 3..5, and \["one", "two"\] doesn't look right/) {
115
+ sh.apply(conversions)
116
+ }
117
+ end
118
+
119
+
120
+
121
+ def test_it_is_ok_for_key_not_to_appear
122
+ sh = SubHash.new.only_symbol(:sym, "name", "val")
123
+ sh.fill
124
+
125
+ conversions = { :another => [Conversion.for(:integer)] }
126
+ sh.apply(conversions)
127
+ assert_equal('val', sh[:sym])
128
+ assert_equal(nil, sh[:another])
129
+ end
130
+
131
+ end
132
+
133
+ class DefaultSourceTest < Test::Unit::TestCase
134
+ include UserChoices
135
+
136
+ def setup
137
+ @choices = DefaultSource.new.use_hash(:a => 'a')
138
+ @choices.fill
139
+ end
140
+
141
+ def test_default_values_are_created_with_key_not_string
142
+ assert_equal(1, @choices.size)
143
+ assert_equal('a', @choices[:a])
144
+ assert_equal(':a', @choices.external_names[:a])
145
+ end
146
+
147
+ def test_nil_is_default_default
148
+ assert_nil(@choices[:foo])
149
+ end
150
+
151
+
152
+ def test_error_message_will_look_good
153
+ assert_raises_with_matching_message(StandardError,
154
+ /^Error in the default values: :a's value/) {
155
+ @choices.apply( :a => [Conversion.for(:integer)])
156
+ }
157
+ end
158
+
159
+ def test_value_conversions_are_from_strings
160
+ c = DefaultSource.new.use_hash(:a => '5')
161
+ c.fill
162
+
163
+ c.apply(:a => [Conversion.for(:integer)])
164
+ assert_equal(5, c[:a])
165
+ end
166
+
167
+ end
168
+
169
+ class EnvironmentSourceTest < Test::Unit::TestCase
170
+ include UserChoices
171
+
172
+ def test_the_environment_args_of_interest_can_be_described_by_prefix
173
+ with_environment_vars('amazon_option' => "1") do
174
+ choices = EnvironmentSource.new.with_prefix('amazon_')
175
+ choices.fill
176
+ assert_true(choices.has_key?(:option))
177
+ assert_equal('1', choices[:option])
178
+ end
179
+ end
180
+
181
+ def test_the_environment_args_can_use_empty_string_as_the_prefix
182
+ # Though it's a silly thing to do.
183
+ with_environment_vars('amazon_option' => "1") do
184
+ choices = EnvironmentSource.new.with_prefix('')
185
+ choices.fill
186
+ assert_true(choices.has_key?(:amazon_option))
187
+ assert_equal('1', choices[:amazon_option])
188
+ end
189
+ end
190
+
191
+ def test_the_environment_args_of_interest_can_be_listed_explicitly
192
+ with_environment_vars('amazon_option' => "1",
193
+ 'root' => 'ok',
194
+ '~' => 'ok, too') do
195
+ choices = EnvironmentSource.new.mapping(:option => 'amazon_option',
196
+ :root => 'root',
197
+ :home => '~')
198
+ choices.fill
199
+ assert_equal(3, choices.size)
200
+ assert_equal('1', choices[:option])
201
+ assert_equal('ok', choices[:root])
202
+ assert_equal('ok, too', choices[:home])
203
+ end
204
+ end
205
+
206
+ def test_can_also_combine_both_forms
207
+ with_environment_vars('amazon_o' => "1",
208
+ 'other_option' => 'still found') do
209
+ choices = EnvironmentSource.new.with_prefix('amazon_').mapping(:other => 'other_option')
210
+ choices.fill
211
+
212
+ assert_equal(2, choices.size)
213
+ assert_equal('1', choices[:o])
214
+ assert_equal('still found', choices[:other])
215
+ end
216
+ end
217
+
218
+ def test_the_order_of_combination_does_not_matter
219
+ with_environment_vars('amazon_o' => "1",
220
+ 'other_option' => 'still found') do
221
+ choices = EnvironmentSource.new.mapping(:other => 'other_option').with_prefix('amazon_')
222
+ choices.fill
223
+
224
+ assert_equal(2, choices.size)
225
+ assert_equal('1', choices[:o])
226
+ assert_equal('still found', choices[:other])
227
+ end
228
+ end
229
+
230
+ def test_unmentioned_environment_vars_are_ignored
231
+ with_environment_vars('unfound' => "1") do
232
+ choices = EnvironmentSource.new.with_prefix("my_")
233
+ choices.fill
234
+ assert_true(choices.empty?)
235
+ end
236
+ end
237
+
238
+ def test_nil_is_default
239
+ with_environment_vars('found' => "1") do
240
+ choices = EnvironmentSource.new.mapping(:option => 'f')
241
+ choices.fill
242
+ assert_nil(choices[:foo])
243
+ assert_nil(choices[:option]) # for fun
244
+ end
245
+ end
246
+
247
+ def test_value_checking_is_set_up_properly
248
+ with_environment_vars('amazon_option' => "1") do
249
+ assert_raises_with_matching_message(StandardError,
250
+ /^Error in the environment: amazon_option's value/) {
251
+ choices = EnvironmentSource.new.with_prefix('amazon_')
252
+ choices.fill
253
+ choices.apply(:option => [Conversion.for(:boolean)])
254
+ }
255
+ end
256
+ end
257
+
258
+ def test_value_conversion_is_set_up_properly
259
+ with_environment_vars('a' => "1", 'names' => 'foo,bar') do
260
+ choices = EnvironmentSource.new.mapping(:a => 'a', :names => 'names')
261
+ choices.fill
262
+ choices.apply(:a => [Conversion.for(:integer)],
263
+ :names => [Conversion.for([:string])])
264
+ assert_equal(1, choices[:a])
265
+ assert_equal(['foo', 'bar'], choices[:names])
266
+ end
267
+ end
268
+
269
+
270
+ end
271
+
272
+ # Common behavior for all config files. Using XML as an example.
273
+ class FileSourceTestCase < Test::Unit::TestCase
274
+ include UserChoices
275
+
276
+ def setup
277
+ builder = Builder::XmlMarkup.new(:indent => 2)
278
+ @some_xml = builder.config {
279
+ builder.reverse("true")
280
+ builder.maximum("53")
281
+ builder.host('a.com')
282
+ builder.host('b.com')
283
+ }
284
+ end
285
+
286
+
287
+ def test_config_file_need_not_exist
288
+ assert_false(File.exist?(".amazonrc"))
289
+ choices = XmlConfigFileSource.new.from_file(".amazonrc")
290
+
291
+ assert_true(choices.empty?)
292
+ end
293
+
294
+
295
+ def test_config_file_value_checking_is_set_up_properly
296
+ with_local_config_file(".amazonrc", @some_xml) do
297
+ assert_raises_with_matching_message(StandardError,
298
+ %r{Error in configuration file ./.amazonrc: maximum's value.*'low'.*'high'}) {
299
+ choices = XmlConfigFileSource.new.from_file(".amazonrc")
300
+ choices.fill
301
+ choices.apply(:maximum => [Conversion.for(['low', 'high'])])
302
+ }
303
+ end
304
+ end
305
+
306
+
307
+ def test_value_conversions_are_set_up_properly
308
+ with_local_config_file('.amazonrc', @some_xml) do
309
+ choices = XmlConfigFileSource.new.from_file('.amazonrc')
310
+ choices.fill
311
+ choices.apply(:maximum => [Conversion.for(:integer)])
312
+ assert_equal(53, choices[:maximum])
313
+ end
314
+ end
315
+
316
+
317
+
318
+ def test_unmentioned_values_are_nil
319
+ with_local_config_file('.amazonrc', @some_xml) do
320
+ choices = XmlConfigFileSource.new.from_file('.amazonrc')
321
+ choices.fill
322
+ assert_nil(choices[:unmentioned])
323
+ end
324
+ end
325
+
326
+ def test_dashed_choice_names_are_underscored
327
+ with_local_config_file('.amazonrc', "<config><the-name>5</the-name></config>") do
328
+ choices = XmlConfigFileSource.new.from_file('.amazonrc')
329
+ choices.fill
330
+ assert_equal('5', choices[:the_name])
331
+ end
332
+ end
333
+
334
+
335
+ end
336
+
337
+
338
+
339
+ class XmlConfigFileSourceTestCase < Test::Unit::TestCase
340
+ include UserChoices
341
+
342
+ def setup
343
+ builder = Builder::XmlMarkup.new(:indent => 2)
344
+ @some_xml = builder.config {
345
+ builder.reverse("true")
346
+ builder.maximum("53")
347
+ builder.host('a.com')
348
+ builder.host('b.com')
349
+ }
350
+ end
351
+
352
+ def test_xml_config_file_normal_use
353
+ with_local_config_file('.amazonrc', @some_xml) {
354
+ choices = XmlConfigFileSource.new.from_file(".amazonrc")
355
+ choices.fill
356
+ choices.apply(:reverse => [Conversion.for(:boolean)],
357
+ :maximum => [Conversion.for(:integer)])
358
+
359
+ assert_equal(3, choices.size)
360
+ assert_equal(true, choices[:reverse])
361
+ assert_equal(53, choices[:maximum])
362
+ assert_equal(['a.com', 'b.com'], choices[:host])
363
+ }
364
+ end
365
+
366
+ def test_config_file_with_bad_xml
367
+ with_local_config_file('.amazonrc',"<malformed></xml>") {
368
+ assert_raise_with_matching_message(REXML::ParseException,
369
+ %r{Badly formatted configuration file ./.amazonrc: .*Missing end tag}) do
370
+ XmlConfigFileSource.new.from_file(".amazonrc")
371
+ end
372
+ }
373
+ end
374
+
375
+
376
+ end
377
+
378
+
379
+ class YamlConfigFileSourceTestCase < Test::Unit::TestCase
380
+ include UserChoices
381
+
382
+ def setup
383
+ @some_yaml = "
384
+ | ---
385
+ | reverse: true
386
+ | maximum: 53
387
+ | host:
388
+ | - a.com
389
+ | - b.com
390
+ | listarg: 1,2, 3
391
+ ".trim('|')
392
+ end
393
+
394
+ def test_string_assurance
395
+ choices = YamlConfigFileSource.new
396
+ a = [1]
397
+ choices.ensure_element_is_string(a, 0)
398
+ assert_equal(["1"], a)
399
+
400
+ h = {'foo' => false }
401
+ choices.ensure_element_is_string(h, 'foo')
402
+ assert_equal({'foo' => 'false'}, h)
403
+
404
+ a = [1, 2.0, true, 'already']
405
+ choices.ensure_array_values_are_strings(a)
406
+ assert_equal(['1', '2.0', 'true', 'already'], a)
407
+
408
+ h = {'1' => '2', 'false' => true, 99 => 100 }
409
+ choices.ensure_hash_values_are_strings(h)
410
+ assert_equal({'1' => '2', 'false' => 'true', 99 => '100' }, h)
411
+
412
+ h = {'1' => '2', 'false' => [1, true], 99 => {100 => true}}
413
+ choices.ensure_hash_values_are_strings(h)
414
+ assert_equal({'1' => '2', 'false' => ['1', 'true'], 99 => {100 => 'true'} }, h)
415
+ end
416
+
417
+ def test_yaml_config_file_normal_use
418
+ with_local_config_file('.amazonrc', @some_yaml) {
419
+ choices = YamlConfigFileSource.new.from_file(".amazonrc")
420
+ choices.fill
421
+
422
+ assert_equal(4, choices.size)
423
+ assert_equal("true", choices[:reverse])
424
+ assert_equal("53", choices[:maximum])
425
+ assert_equal(['a.com', 'b.com'], choices[:host])
426
+ assert_equal("1,2, 3", choices[:listarg])
427
+ }
428
+ end
429
+
430
+ def test_config_file_with_bad_yaml
431
+ with_local_config_file('.amazonrc',"foo:\n\tfred") {
432
+ assert_raise_with_matching_message(ArgumentError,
433
+ %r{Badly formatted configuration file ./.amazonrc: .*syntax error}) do
434
+ pp YamlConfigFileSource.new.from_file(".amazonrc"), 'should never have been reached'
435
+ end
436
+ }
437
+ end
438
+
439
+
440
+
441
+
442
+ end