From: "Gulyás Attila" Date: 2013-02-12T19:50:23+09:00 Subject: Re: Playing with DSLs, a question or two Hi, Dwemthy's Array by Why can also be helpful http://mislav.uniqpath.com/poignant-guide/dwemthy/ ati On 2/11/13, Peter Hickman wrote: > Thanks Ryan. > > That was just the sort of thing I was looking for. > > Thanks for that. > > > On 11 February 2013 17:18, Ryan Cook 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 >> >