jeckyl 0.2.7 → 0.3.7

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/lib/jeckyl.rb CHANGED
@@ -11,8 +11,10 @@
11
11
  #
12
12
  # == JECKYL
13
13
  #
14
+ require 'optparse'
14
15
  require 'jeckyl/version'
15
16
  require 'jeckyl/errors'
17
+ require 'jeckyl/helpers'
16
18
 
17
19
  #
18
20
  # The Jeckyl configurator module, which is just a wrapper. See {file:README.md Readme} for details.
@@ -37,6 +39,7 @@ module Jeckyl
37
39
  #
38
40
  # More details are available in the {file:README.md Readme} file
39
41
  class Config < Hash
42
+
40
43
 
41
44
  # create a configuration hash by evaluating the parameters defined in the given config file.
42
45
  #
@@ -66,12 +69,18 @@ module Jeckyl
66
69
  # hash for comments accessed with the same symbol
67
70
  @_comments = {}
68
71
  # hash for input defaults
69
- @_defaults={}
72
+ @_defaults = {}
73
+ # hash for optparse options
74
+ @_options = {}
75
+ # hash for short descriptions
76
+ @_descriptions = {}
70
77
  # save order in which methods are defined for generating config files
71
78
  @_order = Array.new
72
79
 
73
80
  # get the defaults defined in the config parser
74
81
  get_defaults(:local=> local, :flag_errors => flag_errors_on_defaults)
82
+
83
+ self[:config_files] = Array.new
75
84
 
76
85
  return self if config_file.nil?
77
86
 
@@ -90,20 +99,30 @@ module Jeckyl
90
99
 
91
100
  # gives access to a hash containing an entry for each parameter and the comments
92
101
  # defined by the class definitions - used internally by class methods
93
- def comments
102
+ def _comments
94
103
  @_comments
95
104
  end
96
105
 
97
106
  # This contains an array of the parameter names - used internally by class methods
98
- def order
107
+ def _order
99
108
  @_order
100
109
  end
101
110
 
102
111
  # this contains a hash of the defaults for each parameter - used internally by class methods
103
- def defaults
112
+ def _defaults
104
113
  @_defaults
105
114
  end
106
115
 
116
+ # return hash of options - used internally to generate files etc
117
+ def _options
118
+ @_options
119
+ end
120
+
121
+ # return has of descriptions
122
+ def _descriptions
123
+ @_descriptions
124
+ end
125
+
107
126
  # a class method to check a given config file one item at a time
108
127
  #
109
128
  # This evaluates the given config file and reports if there are any errors to the
@@ -165,14 +184,26 @@ module Jeckyl
165
184
  def self.generate_config(local=false)
166
185
  me = self.new(nil, :local => local)
167
186
  # everything should now exist
168
- me.order.each do |key|
187
+ me._order.each do |key|
188
+
189
+ if me._descriptions.has_key?(key) then
190
+ puts "# #{me._descriptions[key]}"
191
+ puts "#"
192
+ end
169
193
 
170
- if me.comments.has_key?(key) then
171
- me.comments[key].each do |comment|
194
+ if me._comments.has_key?(key) then
195
+ me._comments[key].each do |comment|
172
196
  puts "# #{comment}"
173
197
  end
174
198
  end
175
- def_value = me.defaults[key]
199
+ # output an option description if needed
200
+ if me._options.has_key?(key) then
201
+ puts "#"
202
+ puts "# Optparse options for this parameter:"
203
+ puts "# #{me._options[key].join(", ")}"
204
+ puts "#"
205
+ end
206
+ def_value = me._defaults[key]
176
207
  default = def_value.nil? ? '' : def_value.inspect
177
208
 
178
209
  puts "##{key.to_s} #{default}"
@@ -191,7 +222,7 @@ module Jeckyl
191
222
  def self.intersection(full_config)
192
223
  me = self.new # create the defaults for this class
193
224
  my_hash = {}
194
- me.order.each do |my_key|
225
+ me._order.each do |my_key|
195
226
  if full_config.has_key?(my_key) then
196
227
  my_hash[my_key] = full_config[my_key]
197
228
  end
@@ -212,6 +243,35 @@ module Jeckyl
212
243
  return descs
213
244
  end
214
245
 
246
+ # get a config file option from the given command line args
247
+ #
248
+ # This is needed with the optparse methods for obvious reasons - the options
249
+ # can only be parsed once and you may want to parse them with a config file specified
250
+ # on the command line. This does it the old-fashioned way and strips the option
251
+ # from the command line arguments.
252
+ #
253
+ # Note that the optparse method also includes this option but just for the benefit of --help
254
+ #
255
+ # @param [Array] args which should usually be set to ARGV
256
+ # @param [String] c_file being the path to the config file, which will be
257
+ # updated with the command line option if specified.
258
+ #
259
+ def self.get_config_opt(args, c_file)
260
+ #c_file = nil
261
+ if arg_index = args.index('-c') then
262
+ # got a -c option so expect a file next
263
+ c_file = args[arg_index + 1]
264
+
265
+ # check the file exists
266
+ if c_file && FileTest.readable?(c_file) then
267
+ # it does so strip the args out
268
+ args.slice!(arg_index, 2)
269
+
270
+ end
271
+ end
272
+ return [args, c_file]
273
+ end
274
+
215
275
 
216
276
  # set the prefix to the parameter names that should be used for corresponding
217
277
  # parameter methods defined for a subclass. Parameter names in config files
@@ -238,15 +298,23 @@ module Jeckyl
238
298
 
239
299
  # Read, check and merge another parameter file into this one, being of the same config class.
240
300
  #
301
+ # If the file does not exist then silently ignore the merge
302
+ #
241
303
  # @param [String] conf_file - path to file to parse
242
304
  #
243
305
  def merge(conf_file)
244
306
 
245
- self[:config_files] << conf_file
246
-
247
- # get the values from the config file itself
248
- self.instance_eval(File.read(conf_file), conf_file)
249
-
307
+ if conf_file.kind_of?(Hash) then
308
+ self.merge!(conf_file)
309
+ else
310
+
311
+ return unless FileTest.exists?(conf_file)
312
+
313
+ self[:config_files] << conf_file
314
+
315
+ # get the values from the config file itself
316
+ self.instance_eval(File.read(conf_file), conf_file)
317
+ end
250
318
  rescue SyntaxError => err
251
319
  raise ConfigSyntaxError, err.message
252
320
  rescue Errno::ENOENT
@@ -254,12 +322,88 @@ module Jeckyl
254
322
  raise ConfigFileMissing, "#{conf_file}"
255
323
  end
256
324
 
325
+ # parse the given command line using the defined options
326
+ #
327
+ # @param [Array] args which should usually be ARGV
328
+ # @yield self and optparse object to allow incidental options to be added
329
+ # @return false if --help so that the caller can decide what to do (e.g. exit)
330
+ def optparse(args)
331
+
332
+ # ensure calls to parameter methods do not trample on things
333
+ @_last_symbol = nil
334
+
335
+ opts = OptionParser.new
336
+ # get the prefix for parameter methods (once)
337
+ prefix = self.prefix
338
+
339
+ opts.on('-c', '--config-file [FILENAME]', String, 'specify an alternative config file')
340
+
341
+ # need to define usage etc
342
+
343
+ # loop through each of the options saved
344
+ @_options.each_pair do |param, options|
345
+
346
+ options << @_descriptions[param] if @_descriptions.has_key?(param)
347
+
348
+ # opt_str = ''
349
+ # options.each do |os|
350
+ # opt_str << os.inspect
351
+ # end
352
+
353
+ # puts "#{param}: #{opt_str}"
354
+
355
+ # get the method itself to call with the given arg
356
+ pref_method = self.method("#{prefix}_#{param}".to_sym)
357
+
358
+ # now process the option
359
+ opts.on(*options) do |val|
360
+ # and save the results having passed it through the parameter method
361
+ self[param] = pref_method.call(val)
362
+
363
+ end
364
+ end
365
+
366
+ # allow non-jeckyl options to be added (without the checks!)
367
+ if block_given? then
368
+ # pass out self to allow parameters to be saved and the opts object
369
+ yield(self, opts)
370
+ end
371
+
372
+ # add in a little bit of help
373
+ opts.on_tail('-h', '--help', 'you are looking at it') do
374
+ puts opts
375
+ return false
376
+ end
377
+
378
+ opts.parse!(args)
379
+
380
+ return true
381
+
382
+ end
383
+
384
+ # output the hash as a formatted set
385
+ def to_s(opts={})
386
+ keys = self.keys.collect {|k| k.to_s}
387
+ cols = 0
388
+ keys.each {|k| cols = k.length if k.length > cols}
389
+ keys.sort.each do |key_s|
390
+ print ' '
391
+ print key_s.ljust(cols)
392
+ key = key_s.to_sym
393
+ desc = @_descriptions[key]
394
+ value = self[key].inspect
395
+ print ": #{value}"
396
+ print " (#{desc})" unless desc.nil?
397
+ puts
398
+ end
399
+ end
400
+
257
401
 
258
402
  protected
259
403
 
260
404
  # create a description for the current parameter, to be used when generating a config template
261
405
  #
262
- # @param [*String] being one or more string arguments that are used to generate config file templates
406
+ # @param [*String] strings being one or more string arguments that are used to generate config file templates
263
407
  # and documents
264
408
  def comment(*strings)
265
409
  @_comments[@_last_symbol] = strings unless @_last_symbol.nil?
@@ -273,244 +417,22 @@ module Jeckyl
273
417
  @_defaults[@_last_symbol] = val
274
418
  end
275
419
 
276
- # the following are all helper methods to parse values and raise exceptions if the values are not correct
277
-
278
- # file helpers - meanings should be apparent
279
-
280
- # check that the parameter is a directory and that the directory is writable
281
- #
282
- # Jeckyl checking method to be used in parameter methods to check the validity of
283
- # given parameters, returning the parameter if valid or else raising an exception
284
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
285
- # the parameter is not validly formed
286
- #
287
- # @param [String] - path
288
- #
289
- def a_writable_dir(path)
290
- if FileTest.directory?(path) && FileTest.writable?(path) then
291
- path
292
- else
293
- raise_config_error(path, "directory is not writable or does not exist")
294
- end
295
- end
296
-
297
- # check parameter is a readable file
420
+ # set optparse options for the parameter
298
421
  #
299
- # Jeckyl checking method to be used in parameter methods to check the validity of
300
- # given parameters, returning the parameter if valid or else raising an exception
301
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
302
- # the parameter is not validly formed
303
- #
304
- # @param [String] - path to file
305
- #
306
- def a_readable_file(path)
307
- if FileTest.readable?(path) then
308
- path
309
- else
310
- raise_config_error(path, "file does not exist")
311
- end
312
- end
313
-
314
- # simple type helpers
315
-
316
- # check the parameter is of the required type
317
- #
318
- # Jeckyl checking method to be used in parameter methods to check the validity of
319
- # given parameters, returning the parameter if valid or else raising an exception
320
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
321
- # the parameter is not validly formed
322
- #
323
- # @param [Object] obj to check type of
324
- # @param [Class] type, being a class constant such as Numeric, String
325
- #
326
- def a_type_of(obj, type)
327
- if obj.kind_of?(type) then
328
- obj
329
- else
330
- raise_config_error(obj, "value is not of required type: #{type}")
331
- end
422
+ # @param [Array] opts - options using the same format as optparse
423
+ def option(*opts)
424
+ @_options[@_last_symbol] = opts unless @_last_symbol.nil?
332
425
  end
333
426
 
334
- # check that the parameter is within the required range
427
+ # set optparse description for the parameter
335
428
  #
336
- # Jeckyl checking method to be used in parameter methods to check the validity of
337
- # given parameters, returning the parameter if valid or else raising an exception
338
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
339
- # the parameter is not validly formed
340
- #
341
- # @param [Numeric] val to check
342
- # @param [Numeric] lower bound of range
343
- # @param [Numeric] upper bound of range
344
- #
345
- def in_range(val, lower, upper)
346
- raise_syntax_error("#{lower.to_s}..#{upper.to_s} is not a range") unless (lower .. upper).kind_of?(Range)
347
- if (lower .. upper) === val then
348
- val
349
- else
350
- raise_config_error(val, "value is not within required range: #{lower.to_s}..#{upper.to_s}")
351
- end
352
- end
353
-
354
-
355
- # boolean helpers
356
-
357
- # check parameter is a boolean, true or false but not strings "true" or "false"
358
- #
359
- # Jeckyl checking method to be used in parameter methods to check the validity of
360
- # given parameters, returning the parameter if valid or else raising an exception
361
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
362
- # the parameter is not validly formed
363
- #
364
- # @param [Boolean] val to check
365
- #
366
- def a_boolean(val)
367
- if val.kind_of?(TrueClass) || val.kind_of?(FalseClass) then
368
- val
369
- else
370
- raise_config_error(val, "Value is not a Boolean")
371
- end
372
- end
373
-
374
- # check the parameter is a flag, being "true", "false", "yes", "no", "on", "off", or 1 , 0
375
- # and return a proper boolean
376
- #
377
- # Jeckyl checking method to be used in parameter methods to check the validity of
378
- # given parameters, returning the parameter if valid or else raising an exception
379
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
380
- # the parameter is not validly formed
381
- #
382
- # @param [String] val to check
383
- #
384
- def a_flag(val)
385
- val = val.downcase if val.kind_of?(String)
386
- case val
387
- when "true", "yes", "on", 1
388
- true
389
- when "false", "no", "off", 0
390
- false
391
- else
392
- raise_config_error(val, "Cannot convert to Boolean")
393
- end
394
- end
395
-
396
-
397
- # compound objects
398
-
399
- # check the parameter is an array
400
- #
401
- # Jeckyl checking method to be used in parameter methods to check the validity of
402
- # given parameters, returning the parameter if valid or else raising an exception
403
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
404
- # the parameter is not validly formed
405
- #
406
- # @param [Array] ary to check
407
- #
408
- def an_array(ary)
409
- if ary.kind_of?(Array) then
410
- ary
411
- else
412
- raise_config_error(ary, "value is not an Array")
413
- end
414
- end
415
-
416
- # check the parameter is an array and the array is of the required type
417
- #
418
- # Jeckyl checking method to be used in parameter methods to check the validity of
419
- # given parameters, returning the parameter if valid or else raising an exception
420
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
421
- # the parameter is not validly formed
422
- #
423
- # @param [Array] ary of values to check
424
- # @param [Class] type being the class that the values must belong to
425
- #
426
- def an_array_of(ary, type)
427
- raise_syntax_error("Provided a value that is a type: #{type.to_s}") unless type.class == Class
428
- if ary.kind_of?(Array) then
429
- ary.each do |element|
430
- unless element.kind_of?(type) then
431
- raise_config_error(element, "element of array is not of type: #{type}")
432
- end
433
- end
434
- return ary
435
- else
436
- raise_config_error(ary, "value is not an Array")
437
- end
438
- end
439
-
440
- # check the parameter is a hash
441
- #
442
- # Jeckyl checking method to be used in parameter methods to check the validity of
443
- # given parameters, returning the parameter if valid or else raising an exception
444
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
445
- # the parameter is not validly formed
446
- #
447
- # @param [Hash] hsh to check
448
- #
449
- def a_hash(hsh)
450
- if hsh.kind_of?(Hash) then
451
- true
452
- else
453
- raise_config_error(hsh, "value is not a Hash")
454
- end
455
- end
456
-
457
- # strings and text and stuff
458
-
459
- # check the parameter is a string
460
- #
461
- # Jeckyl checking method to be used in parameter methods to check the validity of
462
- # given parameters, returning the parameter if valid or else raising an exception
463
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
464
- # the parameter is not validly formed
465
- #
466
- # @param [String] str to check
467
- #
468
- def a_string(str)
469
- if str.kind_of?(String) then
470
- str
471
- else
472
- raise_config_error(str.to_s, "is not a String")
473
- end
474
- end
475
-
476
- # check the parameter is a string and matches the required pattern
477
- #
478
- # Jeckyl checking method to be used in parameter methods to check the validity of
479
- # given parameters, returning the parameter if valid or else raising an exception
480
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
481
- # the parameter is not validly formed
482
- #
483
- # @param [String] str to match against the pattern
484
- # @param [Regexp] pattern to match with
485
- #
486
- def a_matching_string(str, pattern)
487
- raise_syntax_error("Attempt to pattern match without a Regexp") unless pattern.kind_of?(Regexp)
488
- if pattern =~ a_string(str) then
489
- str
490
- else
491
- raise_config_error(str, "does not match required pattern: #{pattern.source}")
492
- end
493
- end
494
-
495
- # set membership - set is an array of members, usually symbols
496
- #
497
- # Jeckyl checking method to be used in parameter methods to check the validity of
498
- # given parameters, returning the parameter if valid or else raising an exception
499
- # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
500
- # the parameter is not validly formed
501
- #
502
- # @param [Symbol] symb being the symbol to check
503
- # @param [Array] set containing the valid symbols that symb should belong to
504
- #
505
- def a_member_of(symb, set)
506
- raise_syntax_error("Sets to test membership must be arrays") unless set.kind_of?(Array)
507
- if set.include?(symb) then
508
- symb
509
- else
510
- raise_config_error(symb, "is not a member of: #{set.join(', ')}")
511
- end
429
+ # @param [Array] str - options using the same format as optparse
430
+ def describe(str)
431
+ @_descriptions[@_last_symbol] = str unless @_last_symbol.nil?
512
432
  end
513
433
 
434
+ # add in all the parameter checking helper methods
435
+ include Jeckyl::Helpers
514
436
 
515
437
  private
516
438
 
@@ -556,15 +478,20 @@ module Jeckyl
556
478
  self.class.instance_methods(!local).each do |method_name|
557
479
  if md = /^#{self.prefix}_/.match(method_name) then
558
480
 
559
- # its a prefixed method so call it
481
+ # its a prefixed method so get it
560
482
 
561
483
  pref_method = self.method(method_name.to_sym)
562
484
  # get the corresponding symbol for the hash
563
485
  @_last_symbol = md.post_match.to_sym
564
486
  @_order << @_last_symbol
565
- # and call the method with no parameters, which will
566
- # call the comment method and the default method where defined
567
- # and thereby capture their values
487
+ # and call the method with any parameter, which will
488
+ # call the default method where defined and capture its value
489
+ # Note that a default is defined in the same terms as the input
490
+ # to its parameter method, and may therefore need to be processed
491
+ # by the method to get the desired value. For example, if a parameter
492
+ # method expects data expressed in MBytes, but passes on bytes then
493
+ # the method has to be called with the default to get the real or final
494
+ # default. This is done in the block after this one:
568
495
  begin
569
496
  a_value = pref_method.call(1)
570
497
  rescue Exception
data/spec/jeckyl_spec.rb CHANGED
@@ -7,6 +7,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test/test_subclass')
7
7
 
8
8
  conf_path = File.expand_path(File.dirname(__FILE__) + '/../test/conf.d')
9
9
 
10
+
10
11
  describe "Jeckyl" do
11
12
 
12
13
  # general tests
@@ -30,6 +31,9 @@ describe "Jeckyl" do
30
31
  conf[:email].should == "robert@osburn-sharp.ath.cx"
31
32
  conf.has_key?(:sieve).should be_true
32
33
  conf[:config_files].length.should == 1
34
+ conf[:option_set][:peter].should == 37
35
+ conf[:offset].should == 134
36
+ conf[:start_day].should == 6
33
37
  end
34
38
 
35
39
 
@@ -89,6 +93,11 @@ describe "Jeckyl" do
89
93
  lambda{conf = TestJeckyl.new(conf_file)}.should raise_error(Jeckyl::ConfigError, /^\[pi\]:.*value is not of required type: Float$/)
90
94
  end
91
95
 
96
+ it "should raise an exception if it gets a negative instead of a positive" do
97
+ conf_file = conf_path + '/not_positive.rb'
98
+ lambda{conf = TestJeckyl.new(conf_file)}.should raise_error(Jeckyl::ConfigError, /^\[offset\]:.*value is not a positive number: \-\d+$/)
99
+ end
100
+
92
101
  end
93
102
 
94
103
  describe "Boolean Parameters" do
@@ -114,7 +123,7 @@ describe "Jeckyl" do
114
123
 
115
124
  it "should raise an exception if it gets a string instead of a hash" do
116
125
  conf_file = conf_path + '/not_a_hash'
117
- lambda{conf = TestJeckyl.new(conf_file)}.should raise_error(Jeckyl::ConfigError, /^\[options\]:.*value is not a Hash$/)
126
+ lambda{conf = TestJeckyl.new(conf_file)}.should raise_error(Jeckyl::ConfigError, /^\[option_set\]:.*value is not a Hash$/)
118
127
  end
119
128
 
120
129
  it "should raise an exception if it gets an array of strings instead of a integers" do
@@ -197,6 +206,21 @@ describe "Jeckyl" do
197
206
  conf[:config_files].length.should == 2
198
207
  conf[:pi].should == 3.14592
199
208
  end
209
+
210
+ it "should merge another hash" do
211
+ conf_file = conf_path + '/jeckyl'
212
+ conf = TestJeckyl.new(conf_file)
213
+ merge_file = File.join(conf_path, 'merger.rb')
214
+ merge = TestJeckyl.new(merge_file)
215
+ conf.merge(merge)
216
+ conf[:log_dir].should match(/reports$/)
217
+ conf[:log_level].should == :debug
218
+ conf[:log_rotation].should == 6
219
+ conf[:email].should == "robert@osburn-associates.ath.cx"
220
+ conf[:config_files].length.should == 1
221
+ conf[:pi].should == 3.14592
222
+
223
+ end
200
224
  end
201
225
 
202
226
  end
data/test/conf.d/jeckyl CHANGED
@@ -2,8 +2,10 @@
2
2
  # Jeckyl test file
3
3
  #
4
4
 
5
+ root = File.expand_path('../..', File.dirname(__FILE__))
6
+
5
7
  # should be a writable directory
6
- log_dir "./test"
8
+ log_dir File.join(root, "test")
7
9
 
8
10
  #should be a valid symbol
9
11
  log_level :verbose
@@ -25,7 +27,7 @@ sieve [2, 5, 7, 10, 15]
25
27
 
26
28
  # hash of anything
27
29
  my_opts = {:peter=>37, :paul=>40, :birds=>true}
28
- options my_opts
30
+ option_set my_opts
29
31
 
30
32
  # string formatted as email address
31
33
  email "robert@osburn-sharp.ath.cx"
@@ -44,3 +46,10 @@ flag "no"
44
46
  flag 1
45
47
  flag 0
46
48
 
49
+
50
+ # new things
51
+ offset 134
52
+
53
+ start_day 6
54
+
55
+
@@ -0,0 +1,55 @@
1
+ #
2
+ # Jeckyl test file
3
+ #
4
+
5
+ root = File.expand_path('../..', File.dirname(__FILE__))
6
+
7
+ # should be a writable directory
8
+ log_dir File.join(root, "test")
9
+
10
+ #should be a valid symbol
11
+ log_level :debug
12
+
13
+ # should be an integer
14
+ log_rotation 10
15
+
16
+ # can be a float or any numeric
17
+ threshold 2.3
18
+ threshold 9.6
19
+
20
+ # must be a float
21
+ pi 3.1459276
22
+
23
+ # array of anything
24
+ collection ["names", 1, true ]
25
+ # array of integers
26
+ sieve [2, 5, 7, 10, 15]
27
+
28
+ # hash of anything
29
+ my_opts = {:peter=>37, :paul=>40, :birds=>true}
30
+ option_set my_opts
31
+
32
+ # string formatted as email address
33
+ email "robert@osburn-sharp.ath.cx"
34
+
35
+ # real booleans
36
+ debug false
37
+ debug true
38
+
39
+ # generous booleans
40
+ flag "true"
41
+ flag "false"
42
+ flag "off"
43
+ flag "on"
44
+ flag "yes"
45
+ flag "no"
46
+ flag 1
47
+ flag 0
48
+
49
+
50
+ # new things
51
+ offset 134
52
+
53
+ start_day 6
54
+
55
+ log_level :debug
@@ -25,7 +25,7 @@ sieve [2, 5, 7, 10, 15]
25
25
 
26
26
  # hash of anything
27
27
  my_opts = {:peter=>37, :paul=>40, :birds=>true}
28
- options my_opts
28
+ option_set my_opts
29
29
 
30
30
  # string formatted as email address
31
31
  email "robert@osburn-associates.ath.cx"