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