# on ruby's ml, ruby-talk, it's often been lamented that there no way to
# un-extend an object or otherwise have methods available in a dsl which
# evaporate afterwards.  at least when the desired scope of the dsl is the
# object itself.  for an example lamentation see
#
#   http://groups.google.com/group/ruby-talk-google/browse_thread/thread/338fd9e68c28e889?hl=en
#
# i've encounted this issue a few times myself and, for the first time, came
# up with a simple way to temporarily add methods to an object in the scope of
# a dsl call, which is presented here as a design pattern.
#

# as an example of what we'd like to take the following
#
  def foo() 40 end
  def bar() 2 end

  puts( foo + bar ) #=> 42

# obviously this prints 42.  we'd *like* a dsl which operates in the scope of
# the calling object (instance_eval) but which re-defines the 'foo' method to
# return not the integer 40, but 40.0 (floating point) but *only* the extent
# of the dsl call - restoring the 'foo' method on return.  here you can see
# the desired result and, below in the 'BEGIN' block the annotated
# implementation.
#
  puts dsl{ foo + bar } #=> 42.0

# of course we expect our 'self' to be unaltered, that is to say that 'foo'
# continues to return 42 outside the dsl
#
  puts( foo + bar ) #=> 42




# finally, the implementation, with explanation
#

BEGIN {

  # of course we bundle as much as possible in our dsl's namespace
  #
    module Dsl
    # we define all of our dsl methods inside a closure.  this will act as a
    # 'base-class' for the module we intend to extend objects with
    #
      Methods = lambda do
        def foo() 40.0 end
      end

    # because modules do not support inheritence we build a new one that has
    # the Methods injected into it, and which has support to remove said
    # methods.  note that we cannot use a static module or we'd remove methods
    # from it!
    #
      def Methods.new
        Module.new do
          module_eval &Methods
          def self.remove!
            instance_methods.each{|m| remove_method m}
          end
        end
      end

    # this method applies methods to an object using a new module, returning
    # that module to be used later to remove them
    #
      def Methods.for object
        singleton_class_for(object) do
          @dsl_methods = Dsl::Methods.new
          include @dsl_methods = Dsl::Methods.new
          @dsl_methods
        end
      end

    # just a utility method for accessing the singleton class and eval'ing
    # code in it
    #
      def Methods.singleton_class_for object, &block
        singleton_class =
          class << object; self; end
        block ? singleton_class.module_eval(&block) : singleton_class
      end
    end

  # lastly we provide a top level hook to use the dsl
  #
    def dsl &block
      methods = Dsl::Methods.for self
      instance_eval(&block)
    ensure
      methods.remove!
    end
}