On Aug 9, 2008, at 5:46 PM, Patrick Li wrote:
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/