From: Peter Hickman Date: 2013-02-12T20:07:59+09:00 Subject: Re: Playing with DSLs, a question or two --047d7b6222d0a6096804d585099b Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Guly=E1s 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 On 12 February 2013 10:50, Guly=E1s Attila wrote: > 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 famili= er > >>> 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 =3D Hash.new > >>> @current_room =3D nil > >>> end > >>> > >>> def room(reference, &block) > >>> if @rooms.has_key?(reference) > >>> puts "Error: Room #{reference} has already been defined" > >>> else > >>> @rooms[reference] =3D {:short =3D> '', :long =3D> '', :exits = =3D> > >>> Hash.new} > >>> end > >>> > >>> @current_room =3D reference > >>> > >>> yield block > >>> end > >>> > >>> def short(text) > >>> @rooms[@current_room][:short] =3D text > >>> end > >>> > >>> def long(text) > >>> @rooms[@current_room][:long] =3D 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] =3D {:where =3D> > destination, > >>> :condition =3D> block_given? ? block : true} > >>> end > >>> end > >>> end > >>> > >>> class Object > >>> def adventure(&block) > >>> a =3D 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 basical= ly > >>> 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 g= o > >>> 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 > >> > > > > --047d7b6222d0a6096804d585099b Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable
Guly=E1s

Thanks for that. It = woud seem that picking an adventure game was the best choice for learning D= SLs. At least I understand the domain :)

Peter



On 12 February 2013 10:50, Guly=E1s Attila <tor= aritte@gmail.com> wrote:
Hi,

Dwemthy's Array by Why can also be helpful
http://mislav.uniqpath.com/poignant-guide/dwemthy/

ati

On 2/11/13, Peter Hickman <peterhickman386@googlemail.com> 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 <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 opini= on. 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-teach= ing-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 her= e'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 be= en defined"
>>> =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 def short(text)
>>> =A0 =A0 @rooms[@current_room][:short] =3D text
>>> =A0 end
>>>
>>> =A0 def long(text)
>>> =A0 =A0 @rooms[@current_room][:long] =3D text
>>> =A0 end
>>>
>>> =A0 def door(direction, destination, &block)
>>> =A0 =A0 if @rooms[@current_room][:exits].has_key?(direction) >>> =A0 =A0 =A0 puts "Error: Room #{@current_room} already ha= s an exit defined in
>>> #{direction}"
>>> =A0 =A0 else
>>> =A0 =A0 =A0 @rooms[@current_room][:exits][direction] =3D {:whe= re =3D> destination,
>>> :condition =3D> block_given? ? block : true}
>>> =A0 =A0 end
>>> =A0 end
>>> end
>>>
>>> class 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
>>> =A0 =A0 short "You are in a large cave"
>>> =A0 =A0 long "You are in a very large cave"
>>> =A0 =A0 door :west, :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 end
>>>
>>> =A0 room :exit do
>>> =A0 =A0 short "Freedom"
>>> =A0 =A0 long "Freeeeeeeeeeedom!!!!!!"
>>> =A0 =A0 door :west, :cave
>>> =A0 end
>>>
>>> =A0 require 'pp'
>>> =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 meth= od and
>>> referenced in short, long and door. This seems clunky - it is = basically
>>> a
>>> global variable to help tie things together. Is there some bet= ter 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 w= ay 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 t= o 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
>>
>


--047d7b6222d0a6096804d585099b--