From: Peter Hickman Date: 2013-02-12T00:18:57+09:00 Subject: Playing with DSLs, a question or two --047d7b66fa2f40fc2804d5746d19 Content-Type: text/plain; charset=ISO-8859-1 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. --047d7b66fa2f40fc2804d5746d19 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable
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 he= re's an example of one I am playing with (based on the classic dungeon = adventure problem)

class Adventure
=A0 def initialize
= =A0 =A0 @rooms =3D Hash.new
=A0 =A0 @current_room =3D nil
=A0 end

=A0 def room(reference, &block)
=A0 =A0 if @rooms.has_key?(reference)
=A0 =A0 =A0 puts "Error: Room #{reference} has already been defin= ed"
=A0 =A0 else
=A0 =A0 =A0 @rooms[reference] =3D= {:short =3D> '', :long =3D> '', :exits =3D> Hash.= new}
=A0 =A0 end

=A0 =A0 @current_room =3D reference

=A0 =A0 yield block
=A0 end

=A0 de= f short(text)
=A0 =A0 @rooms[@current_room][:short] =3D text
=A0 end

=A0 def long(text)
=A0 =A0 @rooms[@current_ro= om][:long] =3D text
=A0 end

=A0 def door= (direction, destination, &block)
=A0 =A0 if @rooms[@current_r= oom][:exits].has_key?(direction)
=A0 =A0 =A0 puts "Error: Room #{@current_room} already has an exi= t defined in #{direction}"
=A0 =A0 else
=A0 =A0 = =A0 @rooms[@current_room][:exits][direction] =3D {:where =3D> destinatio= n, :condition =3D> block_given? ? block : true}
=A0 =A0 end
=A0 end
end

c= lass Object
=A0 def adventure(&block)
=A0 =A0 a =3D= Adventure.new

=A0 =A0 a.instance_eval(&block)=
=A0 end
end

adventure do
=A0 room :cave do<= /div>
=A0 =A0 short "You are in a large cave"
=A0 = =A0 long "You are in a very large cave"
=A0 =A0 door :w= est, :another_cave
=A0 =A0 door :east, :exit do
=A0 =A0 =A0 if player.has_item(= :key)
=A0 =A0 =A0 =A0 true
=A0 =A0 =A0 else
= =A0 =A0 =A0 =A0 false
=A0 =A0 =A0 end
=A0 =A0 end
=
=A0 end

=A0 room :another_cave do
=A0 =A0 short "A cave"
=A0 =A0 long "Oh great= ... another cave"
=A0 =A0 door :east, :cave
=A0 e= nd

=A0 room :exit do
=A0 =A0 short "= ;Freedom"
=A0 =A0 long "Freeeeeeeeeeedom!!!!!!"
=A0 =A0 door= :west, :cave
=A0 end

=A0 require 'p= p'
=A0 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=A0variable=A0to help tie things together. Is there some better way o= f 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 whe= re I set @current_room to nil after the yield and then each method checks t= o see if it is set and if not it errors. But again this seems to be quite a= pain.

--047d7b66fa2f40fc2804d5746d19--