[albatross-users] Session management how-to?

Dave Kuhlman dkuhlman at cutter.rexx.com
Fri Jun 28 06:36:48 EST 2002


On Sun, Jun 23, 2002 at 07:47:43PM +1000, Dave Cole wrote:
> 
> > I'm also writing up a few notes, as I learn things, on how-to do
> > things in Albatross.  I've attached what I've done so far below. 
> > This is preliminary, needless to say; I'm still learning Albatross. 
> > But, if you you have any comments, I'm interested.  And, if you
> > should feel that this would be of use to other Albatross users
> > (after a bit more work, of course), let me know.
> 
> This is enormously valuable.  We are not able to do this as we are all
> too close to the code now.

Thanks for comments and encouragement.

I've worked over this how-to document a bit more and made some
additions. I also tried to incorporate your comments. The new
version is appended below.  And, I've attached a file containing
the original source (HTML).  You can also find it at my Web site
(http://www.rexx.com/~dkuhlman/albatross_index.html).

You are welcome to use it any way you think would be helpful.  If
you do decide to use it, let me know and I'll try to help with
converting it to your source format.  You use LaTeX2HTML, and
I've used LaTeX/TeX only briefly, so this would be a good learning
experience for me.

[snip]

> If you use the session server then idle sessions are deleted
> automatically.  The session server will handle multiple web servers.
> Session management is a non-issue for browser side sessions.

Ah ... I think I've got it.  I made a correction to the doc.

[snip]

> 
> Small correction; SimpleSessionFileApp and SessionFileAppContext saves
> sessions in the file system while SimpleSessionApp and
> SessionAppContext save sessions in the session server via TCP socket.
> 

Not sure that I understand this, but I added it to the doc.

Thanks again for Albatross.

  - Dave K

================================================================


                       Albatross How-to for Beginners

   Contents:
     * Introduction
     * How-to Structure an Object-based Application
     * How-to Pass a Value to the Presentation
     * How-to Pass a Value to the Controller
     * How-to Create a Session-based Application
     * How-to Manage Sessions
     * How-to Convert an Object App to a Modular App
     * Transitions -- How-to Move From Page to Page

   Back to top
     _________________________________________________________________

Introduction

   This document explains how to do a number of simple tasks with
   Albatross. It is hoped that it will get a beginning Albatross user
   past a number of early questions and problems so that s/he can move on
   to more complex tasks.
   Back to top
     _________________________________________________________________

How-to Structure an Object-based Application

   This section describes the structure of an object-based application.
   It can be used as a "template" for creating an application.

   An object-based application is one
    1. Create the presentation -- Create an HTML file for each page. This
       is the Albatross template file.
    2. Create the controller -- Create a Python script. This script will
       contain the following items:
    3. Import the needed Albatross classes. For an object-based
       application, the following are appropriate:
        from albatross import SimpleApp, SimpleAppContext
    4. Define a class for each page in the application. Here is a sample:
        class Page2:
            name = 'page2'
            def page_process(self, ctx):
                if ctx.req_equals('button_1'):
                    if ctx.get_value('next_page') == 'page3':
                        ctx.set_page('page3')
                o
                o
                o
            def page_enter(self, ctx):
                o
                o
                o
            def page_leave(self, ctx):
                o
                o
                o
            def page_display(self, ctx):
                o
                o
                o
                ctx.run_template('page2.html')
    5. Define a context class as a subclass of SessionAppContext. Here is
       a sample:
        class AppContext(SimpleAppContext):
            def __init__(self, app):
                SimpleAppContext.__init__(self, app)
    6. Define an application class as a subclass of SimpleSessionApp. The
       constructor should call the constructor of the superclass and
       register each page (class) in the application. Also provide a
       create_context method that creates and returns an instance of your
       context class. Here is an example:
        class App(SimpleApp):
            def __init__(self):
                SimpleApp.__init__(self,
                    base_url = 'dispatch.py',
                    template_path = '.',
                    start_page = 'page1',
                    secret = 'tomato')
                for page_class in (Page1, Page2, Page3):
                    self.register_page(page_class.name, page_class())
            def create_context(self):
                return AppContext(self)
    7. Create the "main" script. Here is a sample:
        if __name__ == '__main__':
            app = App()
            app.run(Request())

   Back to top
     _________________________________________________________________

How-to Pass a Value to the Presentation

   This technique enables you to pass a value from the controller (the
   Python script) to the presentation (the HTML file/template).
    1. In the controller (i.e. in the Python script), add the value to
       the the locals in the context. Here is a sample of the
       page_display method in the class defined for the page:
        class Page1:
            name = 'page1'
            def page_display(self, ctx):
                ctx.locals.variable_1 = 'a sample value'
                ctx.locals.variable_2 = 'another value'
                ctx.run_template('page1.html')
    2. In the presentation (i.e. the HTML page), use an Albatross tag to
       "capture" the value from the context. Here is a sample tag:
        <p>Value #1: <al-input name="variable_1"></p>
        <p>Value #2: <al-value expr="variable_2"></p>

   Back to top
     _________________________________________________________________

How-to Pass a Value to the Controller

   This technique enables you to capture input from the presentation
   (i.e. from the end-user at the Web browser) and pass those values back
   to the controller (the Python script).
    1. Add "input" items to the presentation (the HTML template):
        <p>Enter your name: <al-input type="text" name="user_name"></p>
        <p>Enter your name: <input type="text" name="user_phone"></p>
       Note that the first item (user_name) also allows you to pass a
       value from the controller to the presentation. The second item
       (user_phone) does not.
    2. Capture the entered values in the controller. Here is a sample
       page_display method:
        class Page2:
            name = 'page1'
            def page_process(self, ctx):
                user_name = ctx.locals.user_name
                user_phone = ctx.locals.user_phone
                # Do something with user_name and user_phone.
                o
                o
                o

   Back to top
     _________________________________________________________________

How-to Create a Session-based Application

   This section describes how-to create an application that manages
   sessions and saves state across pages (responses) within each session.

   Let's suppose that you start with a simple object-based application as
   described above.

   In order to convert this simple application into a session-based one,
   you will need to do the following:
    1. Import and inherit from SimpleSessionApp and SessionAppContext
       (instead of from SimpleApp and SimpleAppContext) as follows:
        from albatross import SimpleSessionApp, SessionAppContext
    2. Define your context class as a subclass of SessionAppContext
       (instead of AppContext). For example:
        class AppContext(SessionAppContext):
            def __init__(self, app):
                SessionAppContext.__init__(self, app)
    3. Define your application class as a subclass of SimpleSessionApp
       (instead of SimpleApp). Note that in addition to switching to the
       use of SimpleSessionApp, you will also need to add the keyword
       argument session_appid. Here is an example:
        class App(SimpleSessionApp):
            def __init__(self):
                SimpleSessionApp.__init__(self,
                    base_url = 'dispatch.py',
                    template_path = '.',
                    start_page = 'page1',
                    secret = 'tomato',
                    session_appid = 'sampleapp')
                for page_class in (Page1, Page2, Page3):
                    self.register_page(page_class.name, page_class())
            def create_context(self):
                return AppContext(self)
    4. Start the session server -- In order to run your session
       application, you will need to start a session server. Albatross
       provides a session server that runs as a daemon. al-session-daemon
       is in the subdirectory session-server of the Albatross
       distribution. Get help by typing:
        ./al-session-daemon -h
       And, here is an example of how to start it up:
        ./al-session-daemon -k mysessions.pid start

   Back to top
     _________________________________________________________________

How-to Manage Sessions

   In this section, we explain how to control sessions from within your
   application. In particular, we'll describe how to create a new session
   and how to remove a session (e.g. when one of your end-users finishes
   with your application, and how to save state (values) across pages
   within a session.
    1. How-to create a session -- Albatross does this for you
       automatically. If the request from the browser contains
       information about an existing session, that session is used,
       otherwise Albatross creates a new session.
    2. How-to remove a session -- Albatross will delete idle sessions for
       you automatically. However, if you want your end users to be able
       to logout or exit from your application, for example, so that they
       can start a new session, then you will need to explicitly remove
       the session. For example, suppose you have a page from which the
       application can go no further. You could remove the session on
       entering the page. The user will never leave the page, but
       reloading the page will take them back to the beginning. Here is
       an example:
        class FinalPage:
            def page_enter(self, ctx):
                ctx.remove_session()
       Or, if the final page of your application contains a "Logout" or
       "Exit" button, you can remove the session in the page_process
       method of the class for that page like this:
        class FinalPage:
            def page_process(self, ctx):
                # Check for the logout button.
                if ctx.req_equals('logout'):
                    ctx.remove_session()
                    # Send the user back to the login page.
                    ctx.set_page('login')
    3. How-to save session state across pages within the application --
       In order to direct Albatross to do this, you will "register" the
       variables that you want saved. Do this by calling
       add_session_vars, passing the names of the variables to be saved.
       Here is an example:
        class Page1:
            def page_enter(self, ctx):
                ctx.add_session_vars('user_name', 'user_phone')
       This tells Albatross to preserve these variables across pages. In
       Addition, you must also create these variables in the context. In
       Python code, the following will work:
        ctx.locals.user_name = name
        ctx.locals.user_phone = phone
       And, in HTML, an input item in a form will create the values.
       After doing this, you will be able to access these variables
       within the locals of the context object. For example in Python:
        name = ctx.locals.user_name
        phone = ctx.locals.user_phone
       And, since the values are in the local context, you will be able
       to reference these variables from your HTML code. For example:
        <al-value expr="user_name">
        <al-input name="user_phone">

   Additional notes on session management:
     * In order to run a session based application, you must start-up an
       Albatross session server. The session-server sub-directory in the
       Albatross distribution, contains an implementation of a session
       server (al-session-daemon).
     * If your application inherits its context and application classes
       from SimpleSessionApp and SessionAppContext then Albatross stores
       and saves session information in the session server via TCP
       socket.
     * In contrast, if your application inherits its context and
       application classes from SimpleSessionFileApp and
       SessionFileAppContext then Albatross stores and saves session
       information in files in the local file system. Note that if you
       change from memory-based session storage to file-based storage,
       you will need to modify the call to the application superclass
       (SimpleSessionFileApp). Add the session_dir keyword argument, with
       the directory (a string) where you want Albatross to store session
       state.

   Back to top
     _________________________________________________________________

How-to Convert an Object App to a Modular App

   First, let's make clear what we are trying to do here. We are going to
   convert an application whose controller is implemented with one class
   (object) definition for each page (i.e. an object-based application)
   into an application whose controller is implemented with one module
   (.py file) for each page (a modular application).

   Why would we do this? If the number of pages in your application has
   grown large, you may feel that you have too many classes.

   We are going to assume that you already have an object-based
   application
    1. Create a "main" Python file in your application directory. Let's
       call it main.py.
    2. In main.py, import ModularSessionApp and SessionAppContext:
        from albatross import ModularSessionApp, SessionAppContext
       If you want your session state stored in the local file system,
       rather than in the session server's memory, then use
       ModularSessionFileApp and SessionFileAppContext instead. For
       example:
        from albatross import ModularSessionFileApp, SessionFileAppContext
       And, if you want a non-session application, use the ModularApp
       class instead.
    3. In main.py define a context class as a subclass of
       SessionAppContext (or SessionFileAppContext). Here is an example:
        class AppContext(SessionAppContext):
            def __init__(self, app):
                SessionAppContext.__init__(self, app)
    4. In main.py define an application class as a subclass of
       ModularSessionApp (or ModularSessionFileApp). Here is an example:
        class App(ModularSessionApp):
            def __init__(self):
                ModularSessionApp.__init__(self,
                    base_url = 'dispatch.py',
                    module_path = '.',
                    template_path = '.',
                    start_page = 'page1',
                    secret = 'apricot',
                    session_appid = 'sampleapp')
            def create_context(self):
                return AppContext(self)
    5. In main.py, define the "main" code, which should create and run
       the application instance. This code could look like the following:
        if __name__ == '__main__':
            app = App()
            app.run(Request())
    6. Now create a Python module for each page in your application. We
       will go through this process for a single page. Suppose you have a
       page page1.html (the presentation or view) and a class Page1 (the
       controller). Then, create a module called page1.py.
    7. Copy the class that defines the controller for Page1 into
       page1.py. It might look something like the following:
        class Page1
            name = 'page1'
            def page_process(self, ctx):
                if ctx.req_equals('button_1'):
                    ctx.set_page('page2')
                elif ctx.req_equals('button_2'):
                    ctx.set_page('page3')
            def page_display(self, ctx):
                ctx.run_template('page1.html')
    8. Now, again in page1.py, (1) remove the class line and (2) out-dent
       the methods so that they are top-level functions in the module.
       Also, (3) remove the self argument from each function. It might
       now look something like this:
        name = 'page1'

        def page_process(ctx):
            if ctx.req_equals('button_1'):
                ctx.set_page('page2')
            elif ctx.req_equals('button_2'):
                ctx.set_page('page3')

        def page_display(ctx):
            ctx.run_template('page1.html')
    9. Test your new modular application. Your conversion process is
       complete.

   Back to top
     _________________________________________________________________

Transitions -- How-to Move From Page to Page

   There are two parts to this question:
    1. How do I test the conditions that determine which page is next?
    2. How do I transition to the new page?

   We'll look at an example that tests several conditions. Here is an
   example from an object-based application:
        class Page1State:
            name = 'Page1'
            def page_process(self, ctx):
                if ctx.req_equals('Button_1') and \
                        ctx.get_value('next_page') == 'Page2' and \
                        ctx.get_value('user_name') != '' and \
                        ctx.get_value('user_code') > '0' and \
                        ctx.locals.user_type == 'important':
                    ctx.set_page('Page2')
                elif ctx.req_equals('Button_2') and \
                        ctx.get_value('next_page') > 'Page3':
                    ctx.set_page('Page3')

   The above example checks to determine whether Button_1 was pressed
   and, if so, checks the values of input items next_page, user_name, and
   user_code and session variable user_type before transitioning to
   Page2. Let's assume that user_type is a variable set in a previous
   page and that it was registered with add_session_vars. Notice that
   set_page in the context object is called to tell Albatross which page
   is next. This is the Albatross mechanism for transitioning from one
   page to the next. The argument to set_page is the name passed to
   register_page (for object-based applications) or the name of the
   Python module for the page (for modular applications).

   Now, here is an example from a modular application:
        def page_process(ctx):
            if ctx.req_equals('Button_0'):
                ctx.set_page('Page3')
            elif ctx.req_equals('Button_1') and \
                    ctx.get_value('next_page') == 'Page3':
                ctx.set_page('Page3')
            elif ctx.req_equals('Button_2') and \
                    ctx.get_value('next_page') == 'Page4':
                ctx.set_page('Page4')
            elif ctx.req_equals('Button_3'):
                ctx.set_page('Page3')

   Notice that this second example (from the modular application) is
   similar to the first example (from the object-based application),
   except that there are top-level functions in a module instead of
   methods in a class.
   Back to top
     _________________________________________________________________

   Last update: 6/27/02
   Dave Kuhlman
   dkuhlman at rexx.com
   http://www.rexx.com/~dkuhlman





-- 
Dave Kuhlman
dkuhlman at rexx.com
http://www.rexx.com/~dkuhlman
-------------- next part --------------
A non-text attachment was scrubbed...
Name: alba.zip
Type: application/x-zip-compressed
Size: 2814 bytes
Desc: not available
URL: <http://www.object-craft.com.au/pipermail/albatross-users/attachments/20020627/76244b6f/attachment.bin>


More information about the Albatross-users mailing list