module OptionBlock

An option block is a block with '<key> <value>' pairs in addition to ordinary Ruby code which is passed to new to help configure an object. This allows syntax similar to that used to configure Tk widgets in Ruby/Tk.

The block is evaluated in the context of the newly created object with the help of method_missing to respond to the keys. The values are stored and can be accessed later by using the keys as methods or by using a hash. Instance methods of the newly created object can also be called in the option block. (Attributes can be referred to directly in the block. This practice may seem useful, but it tends to undermine encapsulation.)

Using OptionBlock

To add an option block to the new method of your class, do include OptionBlock in your class. If you define initialize, be sure to call super. After the super call returns, the keys will be associated with the values provided in the option block.

Use the following methods to set defaults and to access options.

Module#option_block_defaults(hash)

Called in the context of a class that includes OptionBlock, specifies recognized options and their defaults. Defaults are not inherited by subclasses (to do). The keys specified in the defaults are the only keys that will be recognized in the option block.

OptionBlock#option(key)

Called in instance context, returns the value of the option specified by key.

OptionBlock#options

Called in instance context, returns the hash of all option keys and values.

In the context of the instance, options may also be read from and written to with the option's key name as if it were a method, along with 0 or 1 arguments, respectively. This form of access does not work if a method by that name already exists. The existing method is unaffected. The one argument form is typeically used in a block accompanying new.

As a special case, if a block is provided in place of a value with an option key, it is automatically converted into a Proc and stored as the value of the key.

Note that, in the scope of the block, self refers to the OptionBlock instance, which, during a call to new, is the same as the object being created. The self and attributes of the enclosing scope are not directly accessible, but can be passed into the block using local variables.

namespace usage

The OptionBlock module uses the instance variable @option_block_options in the client class and uses the class variable @@option_block_defaults_map in Module.

The module uses method_missing to filter out reference to the options. This will not override existing methods, even in superclasses. Also, if method_missing sees a method call that doesn't match one of the specified defaults, it lets superclasses take care of the method call.

to do

Inheritance. This is needed for OptionBlock to be useful with modules.

examples

require 'option-block'

class Sample
  include OptionBlock

  # Note backslash in line below.
  # Can also use form: option_block_defaults ({:string => ... })
  option_block_defaults \
    :string       => "Sample default",  # note comma
    :number       => 123.456,
    :action       => proc {}

  @@cur_id = 0

  def initialize
    @id = @@cur_id    # just to make things interesting later...
    @@cur_id += 1

    super

    # After super, we can do some error checking or other processing.
    unless number.kind_of? Numeric
      raise ArgumentError,
            "number must be a number, which '#{number.inspect}' isn't."
    end
  end

end

sample = Sample.new {
  string  "override!"
  action { |name| puts "Do something, #{name}!" }
}

p sample.options

  # prints:
  # {:string=>"override!", :action=>#<Proc:0x401ca0a0>, :number=>123.456}

sample.action["you lazy computer"]

  # prints:
  # Do something, you lazy computer!

puts '-----------------'

$samples = []

def sample(&block)
  $samples << Sample.new(&block)
end

outside_value = "Whatever"  # can also pass in self this way
sample {
  string outside_value

  number 0/0.0
}

sample {
  num = @id + 0.654         # arbitrary ruby code, in context of new Sample
  number num
}

p $samples.collect { |s| s.string }

  # prints:
  # ["Whatever", "Sample default"]

p $samples.collect { |s| s.number }

  # prints:
  # [NaN, 2.654]

puts '-----------------'

begin
  sample {
    number :a_symbol
  }
rescue ArgumentError => e
  print "Testing our error checker:\n", e
end

  # prints:
  # Testing our error checker:
  # number must be a number, which ':a_symbol' isn't.

version

OptionBlock 1.0

The current version of this software can be found at http://redshift.sourceforge.net/option-block .

license

This software is distributed under the Ruby license. See http://www.ruby-lang.org.

author

Joel VanderWerf, vjoel@sourceforge.net