SourceFiles.org - Use the Source, Luke
Home | Register | News | Forums | Guide | MyLinks | Bookmark

Related Sites

Latest News
  General News
  Reviews
  Press Releases
  Software
  Hardware
  Security
  Tutorials
  Off Topic


Back to files

Tutorial: Adding verbs to objects online:

Perlmoo can be programmed online, by adding new verbs to objects.

The basics:

Suppose we have a ball object, and we want to make it be able to bounce. This is a two-step process - first we add the verb to the object, then we add the actual code to the moo to make the bounce happen.

Adding the verb to the object is just a matter of using the "teach" command:

teach ball to bounce this none none

(See 'help teach' in the moo for the full syntax to this command.)

"teach ball to bounce this none none" means that for the bounce verb to take effect, the user must enter the following command:

"bounce ball"

Now to tell the moo what to do if the ball is bounced:

verbcode ball bounce "return 'It bounces!'"

(See 'help verbcode' in the moo for the full syntax to this command.)

Now when someone types "bounce ball", they get back "It bounces!". How'd that work? Well, verbcode is a command that adds code into the moo to be run when a verb is called. The code, "return 'It bounces!'", is run inside a subroutine. Anything returned from the subroutine is displayed to the user.

More complex code:

Let's get a little bit more complicated, and make the name of the ball be output when it bounces:

verbcode ball bounce "$this=shift; return 'The '.$this->name.' bounces!'"

Remember, this code is running inside a subroutine. The subroutine is passed at least 2 parameters - the first is the object that the subroutine is a verb of. (The second is a VerbCall, which lets you see what parameters the user entered.) So, when we do:

$this=shift;
$this is now set to be the ball object. And so $this->name will call the name method of the ball, and return the ball's name.

Let's try one more thing - it'd be nice if when the ball bounced, everyone else in the same room as it saw it bounce. I've written this as a multi-line construct so you can follow it easily, but you'll have to flatten the code ont into 1 single line to enter it into the moo:

        verbcode ball bounce "
                $this=shift;
                $verbcall=shift;
                $room=$verbcall->caller->location;
                if ($room) {
                        $room->announce($verbcall->caller,
                                $verbcall->caller->name.
                                  ' bounces the '.$this->name.'.')
                };
                return 'The '.$this->name.' bounces!'
        "

Notice that I'veused the verbcall object here to find out the name of who bounced the ball (the caller), and where the caller is located.

See the contrib directory for more complex examples of objects.

Inheritance

Every object in the moo has a parent, and will by default inherit all verbs from the parent. That includes verbs that are coded online. For example:

        create ball named egg
        bounce egg

The egg inherited the ball's bounce verb. Of course, eggs are different than balls - they don't bounce very well. So we could override the egg's bounce verb to make it do something different:

verbcode egg bounce "$this=shift; return 'The '.$this->name.' cracks.'"

Now when we bounce the egg, we see it crack. But suppose we want to see it bounce first - we can pull in the code from our parent object using the super() method, as follows:

        verbcode egg bounce "
                $this=shift;
                $verbcode=shift;
                return $this->super($verbcode), 'The '.$this->name.' cracks.'
        "

Now when the egg is bounced, the ball's bounce code is run first, and returns "The egg bounces!". To that is added "The egg cracks.".

Moo infrastructure:

Here are a few things you should know about the moo infrastructure (very sketchy right now):

  • Every object in the moo is also a perl object.
  • Every verb in the moo corresponds to a method in an object named verb_<name>.
  • Any method of an object whose name ends in _safe, is a special "in-db" method, that is actually contained in the moo database, and can be modified online.
  • There is a special object called a VerbCall that contains a parsed command line. These get passed into verb methods to tell the methods what to do. A verbcall basically contains the text the user entered in, parsed some, so we can easily find the direct object, indirect object, etc.
  • Another special object is a Verb. Each object in the moo may contain several Verbs. A Verb is like a subroutine definition - it lets the moo match lines of text entered by users to verb methods in objects.
  • Finally, we have the Error object. Any function in the entire moo can legally return an erro object, so be sure to test for them with Error::iserror($ret). Error objects are basically how you raise an exception to say something's not right. (Ie, return Error->new("whoops!"))
  • Any property of an object that ends in _msg is intended to be a message parsed by Text::subst.
  • The file basedb.pl is to set up properties for base objects in the moo core (as well as add verbs, help texts, etc). If you edit it, you may need to change the moo db version number (in Db.pm), and run perlmoo-dbconvert to get the changes into db.pl.
  • If you call a method on an object and get back, say, a reference to a hash, you might expect you can add values to that hash and modify the hash in the object. Well, it ain't so. Perlmoo goes to great lengths to prevent this. So if you modify the hash, you'll have to call the method again and pass a reference to the hash in to actually modify the hash in the object.
  • Never use SUPER in perlmoo. This is because SUPER doesn't seem to work when called from inside a safe. That's probably because of namespace problems. There are currently 2 solutions:
    1. For dealing with code that runs outside of a safe, ie, the core perlmoo code, the crummy workaround I've came up with is to explicitly call your parent package's method, ie, 'Container::location($this)'. Note though that this means that if your parent package doesn't implement the method, but _it's_ parent does, then, instead of the grandparren't method being called, your parent actually inherits AUTOLOAD, which is called instead. So to work around this, I have to add stub functions here and there. It's a mess.
    2. For code that runs inside a safe, I actually have a much cleaner solution. Just use $thing->super(@params) to call the overridden method.

Programming outside the moo:

I'm always greatful to those who send me patches and new code for the moo. See the TODO and WISHLIST for ideas of stuff to work on.


Other Sites

Discussion Groups
  Beginners
  Distributions
  Networking / Security
  Software
  PDAs

About | FAQ | Privacy | Awards | Contact
Comments to the webmaster are welcome.
Copyright 2006 Sourcefiles.org All rights reserved.