Thanks Ryan.

That was just the sort of thing I was looking for.

Thanks for that.


On 11 February 2013 17:18, Ryan Cook <cookrn / gmail.com> wrote:

> 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 <
> peterhickman386 / googlemail.com> wrote:
>
>> 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.
>>
>>
>
>
> --
> Ryan Cook
> 720.319.7660
>