I am playing around with writing some DSLs to help me get more familier
with how they work and I have a couple of questions. First here's an
example of one I am playing with (based on the classic dungeon adventure
problem)
class Adventure
def initialize
@rooms = Hash.new
@current_room = nil
end
def room(reference, &block)
if @rooms.has_key?(reference)
puts "Error: Room #{reference} has already been defined"
else
@rooms[reference] = {:short => '', :long => '', :exits =>
Hash.new}
end
@current_room = reference
yield block
end
def short(text)
@rooms[@current_room][:short] = text
end
def long(text)
@rooms[@current_room][:long] = text
end
def door(direction, destination, &block)
if @rooms[@current_room][:exits].has_key?(direction)
puts "Error: Room #{@current_room} already has an exit defined in
#{direction}"
else
@rooms[@current_room][:exits][direction] = {:where => destination,
:condition => block_given? ? block : true}
end
end
end
class Object
def adventure(&block)
a = Adventure.new
a.instance_eval(&block)
end
end
adventure do
room :cave do
short "You are in a large cave"
long "You are in a very large cave"
door :west, :another_cave
door :east, :exit do
if player.has_item(:key)
true
else
false
end
end
end
room :another_cave do
short "A cave"
long "Oh great ... another cave"
door :east, :cave
end
room :exit do
short "Freedom"
long "Freeeeeeeeeeedom!!!!!!"
door :west, :cave
end
require 'pp'
pp @rooms
end
Now this works just fine but a couple of things just don't seem right.
Notice the @current_room variable that is set in the room method and
referenced in short, long and door. This seems clunky - it is basically
a
global variable to help tie things together. Is there some better way of
doing this? Perhaps by passing it as a parameter somehow.
Also is the yield at the bottom of the room method the right way to go
or
should I be calling instance_eval or similar?
Also how do I stop, for example, the door method being called outside of
the room method? I have a solution where I set @current_room to nil
after
the yield and then each method checks to see if it is set and if not it
errors. But again this seems to be quite a pain.
on 2013-02-11 16:19
on 2013-02-11 18:21
Hey Peter, How does this look? https://gist.github.com/cookrn/4755773 By making the Room a separate object, the scoping might be more as you had hoped e.g. the door method Regarding the yield vs. instance_eval -- this is a matter of opinion. If you wanted to be very explicit, you could yield the adventure and room objects to the blocks given to those methods. This makes your DSL seem a bit less magic, but maybe that's ok. I gave a talk on DSLs at Boulder Ruby a while back that you might find interesting: https://speakerdeck.com/cookrn/dsls-as-teaching-tools Ryan On Mon, Feb 11, 2013 at 8:18 AM, Peter Hickman <
on 2013-02-11 21:42
Thanks Ryan. That was just the sort of thing I was looking for. Thanks for that.
on 2013-02-12 11:53
Hi, Dwemthy's Array by Why can also be helpful http://mislav.uniqpath.com/poignant-guide/dwemthy/ ati
on 2013-02-12 12:08
Gulys Thanks for that. It woud seem that picking an adventure game was the best choice for learning DSLs. At least I understand the domain :) Peter
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.