All things to All people!
   
 

8 Basic State Management

State management in moto allows for persistence of information in the Context, Sessions and States.

The Context and the Session store <Object>s. The Context stores objects that are global, may be set, updated and retrieved from any page, and never expire unless explicitly removed from the Context and deleted. The Session stores objects that are needed only for one user's Session and expire when that user's Session expires. The State stores variables passed in the URL or submitted via HTML form submissions (HTTP Post operations).

Before you can make use of moto's state management facilities however, you must $use("codex.http")

8.1 The State

State variables, also called CGI variables, are created by following a URL, submitting a HTML form, or HTTP redirection. Following the URL

http://www.webcodex.com/index.moto?foo=bar&maka=shlaka  

Will add two variables to the state when the index.moto page is executed. The state variable 'foo' will have the value 'bar' and the state variable 'maka' will have the value 'shlaka'.

<form action="index.moto">
  <input type="hidden" name="foo" value="bar">
  <input type="hidden" name="maka" value="shlaka">
  <input type="submit" value="click me">
</form>
 

Clicking the 'click me' button on this form will make the same two state variables with the same two values available to index.moto when it is executing.

$do(sendRedirect("index.moto?foo=bar&maka=shlaka"))  

Executing a moto page that redirects to another moto page via the above redirect will also result in our two state variable being added.

The values of state variables can be retrieved within the code for index.moto in one of three ways. First, the current state can be retrieved and a method can be called on it to get the value of the state variable

$(getState().getValue("foo"))  

Second, if you want the value of a state variable for the current state (as you usually do) than you can call the convenience function getValue() directly

$(getValue("foo"))  

Third, you can use the two argument version of getValue() to provide a default value if the state variable is not defined in the current state.

$(getValue("foo","bar"))  

8.1.1 State Variables As Arguments

There are usually multiple means of getting to a moto page some of which pass a certain state variable and some of which don't. If someone got to index.moto by typing

http://www.webcodex.com/index.moto  

directly into their browser for instance the variable 'foo' would not be defined. In this case the getValue function (or method) would return the value null. There are two ways in which to handle this case

In web application programming, state variables (URL or form variables) are often looked at as arguments to the dynamic web page. If the argument is a required argument then you would want it's non-existence to be treated as an error

$if(getValue("foo") == null) $(sendRedirect(some error page) $endif  

But if it was an optional argument than you will likely have some default value in mind. In this case the third method of retrieving state variables is useful

$(getValue("foo","bar"))  

In this case if the state variable foo was not passed than the default value 'bar' will be used in it's place.

8.1.2 Special characters in State Variables

The characters =, &, and ? separate variable names from variable values, variable name value pairs from each other, and the query string from the hostname and path. In the case a variable name or value contains these special characters (or spaces, or another special character '%') that name or value must first be URL encoded before it may used in a URL.

<a href="index.moto?foo=$(urlEncode(foovalue))">  

The function urlEncode() will replace the special characters with their hexadecimal counterparts %hex value which will be understood by the web server and decoded on the next page view. Thus there is no need to URL decode explicitly in your source code.

8.1.3 HTML Forms

State variable values may also be set through user input in HTML forms

<form name="login form" method="post" action="do_login.moto">
  User: <input type="input" name="user">
  Password: <input type="password" name="password">
  <input type="submit" name="_action" value="login">
</form>

 

Clicking the login button on this form will actually submit three variables. The state variable 'user' will contain whatever the user entered for the user prompt. The state variable 'password' will contain whatever the user entered for his or her password. There will also be a state variable '_action' which contains the value 'login'. Now let's suppose that the page do_login is coded as follows

$switch(getValue('_action',"login"))
  $case(login)
     $if(getValue("user","") eq "bob" && getValue("password","") eq "bobpw")
        $do(redirect("index.moto?sid="+getState().getSID()))
     $else
        $do(redirect("login.moto?sid="+getState().getSID()+"&_err=Password+rejected"))
     $endif
$endswitch
 

Here we see that if the user attempted to log in with user name 'bob' and password 'bobpw' then he will be forwarded on to index.moto, otherwise he will be redirected back to the login page.

8.2 The Session

For applications that make use of Sessions, the current Session is retrieved from the state by calling the method getSession()

$declare(State s = getState())
$declare(Session session = s.getSession())
 

Once you have the Session, you may retrieve named Session variables from it by calling the get() method

$declare(Integer idUser = <Integer>session.get("idUser"))
$declare(String username = <String>session.get("uname"))
 

Before you may get any objects out of the Session however, you must put objects into it. This is done by calling two methods on the Session object: put() and promote().

$do(session.put("idUser",session.promote(new Integer(rset.getInt(0)))))
$do(session.put("uname",session.promote(rset.getString(1))))
 

The put method does nothing more than associate a String valued key with the object you want to store in the Session. The action of the promote method is less obvious.

8.2.1 Object Lifetimes

All objects allocated in moto have an implicitly specified lifetime after which the storage for the object will be recovered by the memory manager. By default the lifetime of an object ends when the page that it was constructed on is done executing. These objects are said to have a page lifetime. Objects that we want to make available on subsequent page views (without reconstructing them) need to stick around longer than that. This is what the promote method does

When you call Session.promote() on an object you change its lifetime to the lifetime of the Session you are storing it in. In the following section you will find that there is a Context.promote() function as well. If promote() were not called on an object before it was put in the Session or Context then the reference stored in the Session or Context would be invalid since the object may have already been deleted.

Methods such as Session.put() do not implicitly call promote because there would be many cases it could not catch. For instance suppose a SymbolTable was allocated and stored in the Session. Then objects were put into the SymbolTable. In this case the reference for the SymbolTable may be valid on subsequent page views, but the references for objects in the SymbolTable would not be. The bottom line is anything that is put into anything that is put into anything that is put into the Session or Context must be promoted.

8.3 The Context

The Context is the appropriate place to store singletons, that is Objects that you only need one of for your application. This is because there is only one Context and every page can get objects out of or store objects in the Context. Perhaps the most common type of object to store in the Context are caches.

To store an object in the Context, the Context must first be retrieved by calling the function getContext(), the object to be stored should then be promoted by calling Context.promote(), and finally the object must be associated with a name in the Context by calling Context.put().

$do(getContext().promote(cardData))
$do(getContext().put("CardList",cardData))
 

To retrieve an object from the Context use the method Context.get()

cardData =<TabularData>getContext().get("CardList")