Metaprogramming and DSL Resources?
As a newcomer to Ruby and metaprogramming, I'm still looking for good resources on this and the art of creating DSLs.
I'm picking up a little here and there by reading blog posts and articles like this one on InfoQ, as well as pouring over code from Rails and other Ruby projects, but it seems like there would be a market for a book written by someone who has done a lot in this area.
p.s. Here's a modification of an implementation from the aforementioned article that supports passing in a lambda expression, a block, or both. I suspect there may be better ways to do some of this and would love to hear it. Regardless, I'm continually astounded by the power of mixins and the reflection capabilities built into Ruby.
module PropertiesOutput:
def self.extended(base)
base.class_eval do
def fire_event_for(sym, arg)
@listener[sym].each {|l| l.call(arg) }
end
end
end
def property(sym, predicate=nil, &block)
define_method(sym) do
instance_variable_get("@#{sym}")
end
define_method("#{sym}=") do |arg|
if block_given? and predicate
return if !predicate.call(arg) and !block.call(arg)
elsif predicate
return if !predicate.call(arg)
elsif block_given?
return if !block.call(arg)
end
instance_variable_set("@#{sym}", arg)
fire_event_for(sym, arg)
end
define_method("add_#{sym}_listener") do |x|
@listener ||= {}
@listener[sym] ||= []
@listener[sym] << x
end
define_method("remove_#{sym}_listener") do |x|
@listener[sym].delete_at(x)
end
end
def is?(test)
lambda {|val| test === val }
end
def includes?(*test)
lambda {|val| test.include? val }
end
end
class CruiseShip
extend Properties
# property :direction
# property :direction, includes?('north', 'south', 'east', 'west')
property :speed, is?(0..300)
property(:direction, includes?('north', 'south', 'east', 'west')) {|v| v == 'thataway' }
# property(:speed) {|v| v >= 0 && v <= 300 }
end
h = CruiseShip.new
h.add_direction_listener(lambda {|x| puts "Oy... someone changed the direction to #{x}"})
h.direction = "north"
h.add_speed_listener(lambda {|x| puts "Oy... someone changed the speed to #{x}"})
h.add_speed_listener(lambda {|x| puts "Yo, dude... someone changed the speed to #{x}"})
h.speed = 200
h.speed = 300
h.speed = 301
h.speed = -1
h.direction = "south"
puts h.direction
puts h.speed
h.remove_speed_listener(1)
h.speed = 200
h.speed = 350
h.direction = "thataway"
h.direction = "whatever"
puts h.direction
puts h.speed
Oy... someone changed the direction to north
Oy... someone changed the speed to 200
Yo, dude... someone changed the speed to 200
Oy... someone changed the speed to 300
Yo, dude... someone changed the speed to 300
Oy... someone changed the direction to south
south
300
Oy... someone changed the speed to 200
Oy... someone changed the direction to thataway
thataway
200
Labels: ruby dsl metaprogramming
0 Comments:
Post a Comment
<< Home