On Aug 9, 2008, at 5:46 PM, Patrick Li wrote:

> string = Farm.create do
>  barn do
>    animal "dog"
>    animal "cat"
>  end
>  pond do
>    animal "whale"
>    animal "shark"
>  end
> end
>
> The string should print:
> Farm contains
> [
>  Barn contains
>  [
>    dog
>    cat
>  ]
>  Pond contains
>  [
>    whale
>    shark
>  ]
> ]
>
> It would be really really nice to have this also:
> Farm.create do
>  animal "whale"
> end



cfp: ~> cat a.rb
# following is an example dsl built from my current idea of dsl best
# practices, which can be found @
# http://drawohara.com/post/39582749/ruby-the-best-way-to-build-ruby-dsls
#

   f =
     farm {
       barn {
         animal :dog
         animal :cat
       }

       pond {
         animal :whale
         animal :shark
       }
     }

   p f

   #=> #<Farm:0x250d0 @pond=#<Farm::Pond:0x24e78 @animals=[#<Whale: 
0x24d74>, #<Shark:0x24d38>]>, @barn=#<Farm::Barn:0x24fe0  
@animals=[#<Dog:0x24edc>, #<Cat:0x24ea0>]>>


BEGIN {

   # these classe are unimportant
   #
     class Animal; end
     class Dog < Animal; end
     class Cat < Animal; end
     class Whale < Animal; end
     class Shark < Animal; end

   # this is important, note how it *wraps* a class so the  
instance_eval is the
   # *dsl* instance_eval, note that of the wrapped object
   #
     module Dsl
       module ClassMethods
         def dsl &block
           unless @dsl
             name = self.name.downcase.split(%r/::/).last
             @dsl = (
               Class.new do
                 attr name
                 const_set :Name, name
                 def initialize object, &block
                   ivar = "@#{ self.class.const_get(:Name) }"
                   instance_variable_set ivar, object
                   instance_eval &block if block
                 end
               end
             )
           end
           @dsl.module_eval &block if block
           @dsl
         end
       end

       module InstanceMethods
         def dsl &block
           self.class.dsl.new(self, &block)
         end
       end

       def Dsl.included other
         other.send :extend, ClassMethods
         other.send :include, InstanceMethods
       end
     end

   # again this is mostly unimportant, just note how they make use of  
the module
   # for declaring the dsl class, and how they use it in intiialize
   #
     class Farm
       include Dsl

       attr_accessor 'barn'
       attr_accessor 'pond'

       def initialize &block
         dsl &block
       end

       class Barn
         include Dsl

         attr_accessor 'animals'

         def initialize &block
           @animals = []
           dsl &block
         end

         dsl {
           def animal name
             barn.animals << Object.const_get(name.to_s.capitalize).new
           end
         }
       end

       class Pond
         include Dsl

         attr_accessor 'animals'

         def initialize &block
           @animals = []
           dsl &block
         end

         dsl {
           def animal name
             pond.animals << Object.const_get(name.to_s.capitalize).new
           end
         }
       end

       dsl {
         def barn *a, &b
           farm.barn = Barn.new(*a, &b)
         end

         def pond *a, &b
           farm.pond = Pond.new(*a, &b)
         end
       }
     end

   # this is just the top level hook
   #
     def farm(*a, &b) Farm.new(*a, &b) end
}


a @ http://codeforpeople.com/
--
we can deny everything, except that we have the possibility of being  
better. simply reflect on that.
h.h. the 14th dalai lama