Helma Logo
main list history

HopRepl

Introduction

HopRepl is similar to Helma Shell with the tiny difference that it does not run in a browser window but in a terminal environment (aka command line).

Note: Chris Langreiter was here years ago already when he published HopShell, a Repl written in Rebol.

The four letters Repl actually are in fact used synonymously for a shell.

Thus, you can enter simple commands to inspect and manipulate Helma space. And commands are of course JavaScript statements!

This is an alpha version, ie. you will get excepctions, stuck and not always what you expected.

Thanks to Robert and Hannes for their help; it would not be even alpha without them.

Update: I added the file patch-for-emacs-integration.diff which Massimiliano sent to me; he “added a few bits, namely set/get/push/popenv and the multiline input mode, needed for Emacs integration.” Maybe this helps other Emacs users, too.

Here is what you can do with HopRepl:

Installation And Setup

First, move the two files attached to this wiki as repl.zip and repl.jar into your application directory. I recommend the manage application because it already provides some interesting objects to explore.

nametypesize
repl.zipundefined bytes
repl.jarundefined bytes
patch-for-emacs-integration.diffundefined bytes
repl-599.jarundefined bytes
repl-599.zipundefined bytes

To be sure that the HopRepl code is really compiled into the application a Helma restart will not hurt.

Now you can start the HopRepl server by navigating to the “repl” action in your browser. In my case the location is http://localhost:8080/manage/repl

There you will see one simple line saying “HopRepl server (stopped)” followed by two links “Start” and “Restart”.

Click on the “Start” link and your HopRepl should be started. Simultaneously, the link will change to “Stop” and the port number will be displayed in brackets.

(If not you might check if anything else is running behind that port on your computer already. Use the application property replPort to change the port number.)

Using HopRepl

Let us check if you can connect from the command line shell to the HopRepl server by entering

   telnet localhost 1234

(Be sure to use the desired port number if you should have changed it in the previous step.)

Note: To add command editing, history and completion features to the Repl you could prefix the above command with rlwrap – which of course needs to be installed on your machine.

If everyhing goes well HopRepl should welcome you with a short message and the Repl prompt:

   Hello! This is HopRepl Alpha (c) 2008 Tobi Schäfer
   Helma> 

After the prompt you can enter any JavaScript statement you like. It will be (R)ead, (E)valuated and the result will be (P)rinted in your terminal. Finally, the (L)oop starts again with your next input. That is the generic principle behind Repl.

   Helma> new Date
   Thu Jan 17 2008 12:21:57 GMT+0100 (CET)
   Helma> Math
   [object Math]
   Helma> this
   [object GlobalObject]

There is one special object added to the JavaScript as defined by Rhino and Helma. It is called repl and acts as a helper object providing methods to make navigating and exploring the namespace more convenient.

   Helma> repl
   HopRepl server (port 1234)

To get a clue where in Helma space you are right now you can use the whereAmI() method:

  Helma> repl.whereAmI()
  [object GlobalObject]

The look() method prints a list of all available properties and methods in the current context:

   Helma> repl.look()
   this.Repl = [function]
   this.Server = [function]
   this.Thread = [function]
   this.Xml = {class helma.scripting.rhino.extensions.XmlObject}
   this.app = {class helma.framework.core.ApplicationBean}
   this.appStat = [function]
   this.checkAddress = [function]
   this.checkAuth = [function]
   this.createAddressFilter = [function]
   ...
   this.req = {class helma.framework.RequestBean}
   this.res = {class helma.framework.ResponseBean}
   this.root = {class helma.main.Server}
   this.scheduler = [function]
   this.session = {class helma.framework.core.SessionBean}
   this.skin_macro = [function]
   this.sortByName = [function]
   this.sortProps = [function]

For convenience you do not have to always type in “this” to access a property or a method. HopRepl provides all available properties of the current context as global variables:

   Helma> res
   [Response]
   Helma> session
   [Anonymous Session]
   Helma> skin_macro
   function skin_macro(par) {
      if (par && par.name) {
         renderSkin(par.name);
      }
   }

The enter() method changes the context to another object:

   Helma> repl.enter(root)
   helma.main.Server@d03ed2
   Helma> repl.look()
   this.application = undefined
   this.applications = {class [Ljava.lang.Object;}
   this.appsHome = {class java.io.File}
   this.appsProperties = undefined
   this.checkAppManager = [function]
   this.class = {class java.lang.Class}
   this.dbHome = {class java.io.File}
   ...
   Helma> repl.enter(checkAppManager)
   function checkAppManager() {/*
      void checkAppManager(int)
   */}

To get back to the previous context the back() method is at hand:

   Helma> repl.back()
   helma.main.Server@d03ed2

And the home() method brings you even further back, straight up to the top-level context:

   Helma> repl.home()
   [object GlobalObject]

If you should have enough of tinkering with HopRepl the quit() method closes the connection to the server:

   Helma> repl.quit()
   Connection closed by foreign host.

Getting Nifty

Of course you can also call methods, not only enter their contextes. Here is a more elaborated example of using HopRepl to stop an application:

   Helma> repl.home()
   [object GlobalObject]
   Helma> repl.enter(root)
   helma.main.Server@d03ed2
   Helma> repl.enter(getApplication("test"))
   [Application test]
   Helma> isRunning()
   true
   Helma> stop()
   Helma> isRunning()
   false

Further manipulation is possible, too. Let us change a HopObject’s property:

   Helma> repl.home()
   [object GlobalObject]
   Helma>> repl.enter(root)
   helma.main.Server@d03ed2
   Helma> repl.enter(getApplication("antville"))
   [Application antville]
   Helma> repl.enter(dataRoot)
   HopObject Site 14
   Helma> repl.enter(creator)
   HopObject p3k
   Helma> repl.look()
   this.comments = [object]
   this.created = [object]
   this.email = nobody@nowhere.com
   this.files = [object]
   ...
   Helma> email = "somebody@somewhere.com"
   somebody@somewhere.com

Please note that the e-mail address is not changed in the database, yet. To achieve this one has to add a res.commit() at this point. After invalidating the application – which of course can be done in HopRepl as well by invoking app.clearCache() – the new value is still persistent:

   Helma> res.commit()
   Helma> repl.back(); repl.back() // Back to the application’s context
   [Application antville]
   Helma> clearCache()
   Helma> dataRoot.creator.email
   somebody@somewhere.com

That is it so far. Maybe this first attempt will grow and if anyone is interested in picking up the pieces, please contact me via the helma-dev mailing list.

To Do

Inspired by Massimiliano Mirra’s MozRepl project.

// tobi

Comments

#1 by hypersmil at 2008/01/21 08:21
shouldn't it be this.email:
   ...
   REPL> email = "somebody@somewhere.com"
   somebody@somewhere.com

?

#2 by tobi at 2008/01/21 11:52

It could be but it also works without "this". Quote: For convenience you don't have to always type in "this" to access a property or a method. HopRepl provides all available properties of the current context as global variables.

#3 by tree at 2008/03/09 12:44

I got

illegal character (repl.zip/Global/repl.js#91)

when I tries to access
http://localhost:8080/manage/repl

#4 by tree at 2008/03/09 12:50

ok, there is an Gremlin character on line 91 of the zip file that is downloaded. Simply retype following line will do the trick

var target = newScope || scope;

#5 by tobi at 2008/10/15 15:29

I just uploaded fresh builds of repl.zip and repl.jar; I added the current revision ID 599 to the filename for distinction.