The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

OpenInteract2::Context - Provides the environment for a server

SYNOPSIS

 use OpenInteract2::Context qw( DEBUG LOG CTX );
 
 # You can create a variable for the context as well, but normal way
 # is to import it
 my $ctx = OpenInteract2::Context->instance;
 
 # If debugging for server is turned on, log info the long way (Note:
 # 'LDEBUG' defined by inporting ':log' from OpenInteract2::Constants)
 CTX->DEBUG && CTX->log( LDEBUG, "Things are looking great!" );
 
 # ...or the short way (with the import)
 DEBUG && LOG( LDEBUG, "Things are looking great!" );
 
 # Get the information for the 'TT" content generator
 my $generator_info = CTX->content_generator( 'TT' );
 
 # Grab the server configuration
 my $conf = CTX->server_config;
 
 # Grab the 'main' datasource -- this could be DBI/LDAP/...
 my $db = CTX->datasource( 'main' );
 
 # Get the 'accounting' datasource
 my $db = CTX->datasource( 'accounting' );
 
 # Get the default system datasource
 my $db = CTX->datasource;
 
 # Get the template object (XXX: Future -- may be named like datasource...)
 my $template = CTX->template;
 
 # Find an object class
 my $news_class = CTX->lookup_object( 'news' );
 my $news = $news_class->fetch( 42 );
 
 # All in one step
 my $news = CTX->lookup_object( 'news' )->fetch( 42 );
 
 # Lookup an action
 my $action = CTX->lookup_action( 'news' );
 $action->params({ security_level => 8, news => $news });
 $action->task( 'show' );
 return $action->execute;
 
 # XXX: Add a cleanup handler (NOT DONE)
 #CTX->add_handler( 'cleanup', \&my_cleanup );

DESCRIPTION

This class supports a singleton object that contains your server configuration plus pointers to other OpenInteract services. Much of the information it holds is similar to what was in the OpenInteract::Request ($R) object in OpenInteract 1.x. However, the OpenInteract2::Context object does not include any information about the current request.

The information is holds and services it provides access to include:

debugging and logging

The DEBUG and LOG subroutines are exported to provide simple access to logging from any class.

configuration

The data in the server configuration is always available. (See server_config property.)

datasource

All datasources are retrieved through the context, including DBI, LDAP and any others. (See datasource())

object aliases

SPOPS object classes are stored based on the name so you do not need to know the class of the object you are working with, just the name. (See lookup_object())

actions

The context contains the action table and can lookup action information as well as create a OpenInteract2::Action object from it. (See lookup_action(), lookup_action_info(), lookup_action_none(), lookup_action_not_found())

other aliases

Other classes are stored on an alias basis as well; currently this is limited to the items under the configuration key system_alias. (See alias())

security checking

You can check the security for any object or class from one place. (See check_security()

caching

If it is configured, you can get the cache object for storing or looking up data. (See cache property)

packages

The package repository and packages in your site are available from the context. (See properties repository and packages)

METHODS

Class Methods

instance()

This is the method you will see many times when the object is not being imported, since it returns the current context. There is only one context object available at any one time. If the context has not yet been created (with create()), throws an exception.

Returns: OpenInteract2::Context object

create( $base_config|\%config_params, [ \%setup_params ] )

Creates a new context. If you pass in a OpenInteract2::Config::Base object or specify 'website_dir' in \%params, it will run the server initialization routines in setup(). (If you pass in an invalid 'website_dir' an exception is thrown.)

If you do not know these items when the context is created, you can do something like:

 my $context = OpenInteract2::Context->create();
 
 ... some time later ...
 
 my $base_config = OpenInteract2::Config::Base->new({ website_dir => $dir } );
 ... or ...
 my $base_config = OpenInteract2::Config::Base->new({ filename => $file } );
 $context->base_config( $base_config );
 $context->setup();

Finally, create() stores the context for later retrieval by instance().

If the context has already been created then it is returned just as if you had called instance().

See setup() for the parameters possible in \%setup_params.

Returns: the new OpenInteract2::Context object.

setup( \%params )

Runs a series of routines, mostly from OpenInteract2::Setup, to initialize the singleton context object. If the base_config property has not been set with a valid OpenInteract2::Config::Base object, an exception is thrown.

If you pass to create() a base_config object or a valid website directory, setup() will be called automatically.

You can skip steps of the process by passing the step name in an arrayref 'skip' in \%params. (You normally pass these to create().) This is most useful when you're creating a website for the first time.

For instance, if you do not wish to activate the SPOPS objects:

 OpenInteract2::Context->create({ skip => 'activate spops' });

If you do not wish to read in the action table or SPOPS configuration:

 OpenInteract2::Context->create({ skip => [ 'initialize action',
                                            'initialize spops' ] });

The steps we take to setup the site are listed below. Steps performed by OpenInteract2::Setup are marked with the method called.

  • Read in the server configuration and assign the debugging level from it. (Setup: read_server_config()) (Skip: n/a)

  • Read in the package repository (Setup: read_repository()) and all packages in the site (Setup: read_packages()). (Skip: 'initialize repository')

  • Create a temporary library directory so all classes are found in one location. (Setup: create_temp_lib) (Skip: 'initialize temp lib')

  • Create aliases from the server configuration. These are stored under the configuration key system_alias. (Setup: read_aliases()) We also ensure that the classes aliased are brought into the system via require. (Skip: 'initialize alias')

  • Require modules specified in the session_info server configuration key under 'class' and 'impl_class'. (Skip: 'initialize session')

  • Read in the action table from the available packages. (Setup: read_action_table()) We also ensure that all classes referenced in the action table are brought into the system via require. (Skip: 'initialize action')

  • Read in the SPOPS object configurations from the available packages. (Setup: read_spops_config()) Activate all SPOPS objects at once. (Setup: activate_spops_classes()) (Skip: 'initialize spops'; you can also skip just the activation step with 'activate spops')

  • Create the cache. If it is not configured this is a no-op. (Setup: create_cache()) (Skip: 'initialize cache')

  • Initialize all content generators. (Setup: initialize_content_generator()) (Skip: 'initialize generator')

  • Initialize the main controller with default actions. (Skip: 'initialize controller'; also skipped with 'initialize action')

Returns: the context object

Object Methods: Debugging and Logging

assign_debug_level( [ $level ] )

Assign the debugging level for the context. If $level is not passed in we read the global 'DEBUG' item in the server configuration. The level should a descriptive token in the set ( 'ALL', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'OFF' ) corresponding to the relevant logging level.

Returns: new debugging level

DEBUG()

Shortcut to the current debugging level. This value can be imported by your code. By default this is set in the server config, but you can set it on-the-fly in code as well using the method assign_debug_level().

Example:

 CTX->DEBUG && CTX->log( LERROR, "Cannot find authenticated user in cookie: $@" );

If you find yourself using the above idiom many times within a package, you can also import the DEBUG symbol:

 use OpenInteract2::Context qw( DEBUG LOG );

And call it in a slightly different manner:

 DEBUG && LOG( LERROR, "Cannot find authenticated user in cookie: $@" );

Returns: current debugging level

(See also: assign_debug_level, LOG, log.)

log( $level, @message )

If $level is constant LALL or it is less than or equal to the current DEBUG level as set in the server configuration, @message is sent along with calling information to STDERR using warn.

The message is formatted:

 level [line-num] Package::subroutine: error-message

For example:

 INFO [97] OpenInteract2::Context::lookup_action_info: Action [news] not found in table

The error-message is created by joining the contents of @message together.

Example:

 # Always log this

 CTX->log( LALL, "We should never reach this area. Something is wrong" );

 # This is a lot of information, so we shouldn't display this
 # except at permissive debugging levels

 CTX->DEBUG && CTX->log( LDEBUG, "Context is: ", Dumper( CTX ) );

The logging level symbols are also listed in OpenInteract2::Constants. Here they are in briefer form, ordered:

  • LALL: Highest rank, intended to turn on all logging. Messages sent with this rank will always get logged.

  • LDEBUG: Fine-grained informational events that are most useful to debug an application.

  • LINFO: Informational messages that highlight the progress of the application at coarse-grained level.

  • LWARN: Potentially harmful situations.

  • LERROR: Error events that might still allow the application to continue running.

  • LFATAL: Very severe error events that will presumably lead the application to abort.

  • LOFF: Lowest rank, intended to turn off logging for all messages except those sent with LALL.

So setting DEBUG in your server configuration to LFATAL would only log messages sent with LFATAL and LALL rankings. Setting it to LWARN would log messages sent with LWARN, LERROR, LFATAL and LALL rankings.

Returns: nothing

(See also: LOG and DEBUG shortcuts.)

LOG( $level, @message)

Shortcut for logging call. This routine can be imported by your code. The effects are exactly the same as specified in log, but the call can be a little shorter. The following are common debugging calls:

 my CTX = OpenInteract2::Context->instance;
 CTX->log( LALL, "We should never reach this area. Something is wrong" );
 CTX->DEBUG && CTX->log( LDEBUG, "Context is: ", Dumper( CTX ) );

These can be written using both common symbols as:

 use OpenInteract2::Context qw( DEBUG LOG );
 ...
 my CTX = OpenInteract2::Context->instance;
 LOG( LALL, "We should never reach this area. Something is wrong" );
 DEBUG && LOG( LDEBUG, "Context is: ", Dumper( CTX ) );

Returns: see entry for log.

Object Methods: Actions

lookup_action( $action_name [, \%values )

Looks up the information for $action_name in the action table and returns a OpenInteract2::Action object created from it. We also pass along \%values as the second argument to new() -- any properties found there will override what's in the action table configuration, and any properties there will be set into the resulting object.

If $action_name is not found, an exception is thrown.

Returns: OpenInteract2::Action object

lookup_action_name( $url_chunk )

Given the URL piece $url_chunk, find the associated action name. Whenever we set the action table (using action_table()), we scan the actions to see if they have an associated URL, peeking into the 'url' key in the action configuration.

If so, we only create one entry in the URL-to-name mapping.

If not, we create three entries in the URL-to-name mapping: the lowercased name, the uppercased name, and the name with the first character uppercased.

Additionally, we check the action configuration key 'url_alt' to see if it may have one or more URLs that it responds to. Each of these go into the URL-to-name mapping as well.

For example, say we had the following action configuration:

 [news]
 class = OpenInteract2::Action::News
 task_default = list

This would give the action key 'news' to three separate URLs: 'news', 'NEWS', and 'News'.

Given:

 [news]
 class = OpenInteract2::Action::News
 task_default = list
 url_alt = NeWs
 url_alt = Newsy

It would respond to the three URLs listed above, plus 'NeWs' and 'Newsy'.

Given:

 [news]
 class = OpenInteract2::Action::News
 task_default = list
 url = WhatReallyMatters

It would only respond to a single URL: 'WhatReallyMatters'.

lookup_action_none()

Finds the action configured for no name -- this is used when the user does not specify an action to take, such as when the root of a deployed URL is queried. (e.g., 'http://www.mysite.com/')

If the configured item is not found or the action it refers to is not found, an exception is thrown.

Returns: OpenInteract2::Action object

lookup_action_not_found()

Finds the action configured for when an action is not found. This can be used when an action is requested but not found in the action table. Think of it as a 'catch-all' for requests you cannot foresee in advance, such as mapping requests to the filesystem to an OpenInteract action.

Currently, this is not called by default when you try to lookup an action that is not found. This is a change from 1.x behavior. Instead, you would probably do something like:

 my $action = eval { CTX->lookup_action( 'my_action' ) };
 if ( $@ ) {
     $action = eval { CTX->lookup_action_not_found() };
 }

This requires more on your part, but there is no peek-a-boo logic going on, which to us is a good trade-off.

If the configured item is not found or the action it refers to is not found, an exception is thrown.

Returns: OpenInteract2::Action object

lookup_action_info( $action_name )

Find the raw action information mapped to $action_name. This is used mostly for internal purposes.

This method follows 'redir' paths to their end. See OpenInteract2::Action for more information about these. If an action redirects to an action which is not found, we still return undef.

This method will never throw any exceptions or errors.

Returns: hashref of action information, or undef if the action is not defined.

action_table( [ \%action_table ] )

Retrieves the action table, and sets it if passed in. The action table is a hashref of hashrefs -- the keys are the names of the actions, the values the information for the actions themselves.

When it gets passed in we do some work to find all the URLs each action will respond to and save them elsewhere in the server configuration.

Application developers will probably never use this.

Returns: hashref of action information

Object Methods: SPOPS

lookup_object( $object_name )

Finds the SPOPS object class mapped to $object_name. An exception is thrown if $object_name is not specified or not defined as an SPOPS object.

Here are two different examples. The first uses a temporary variable to hold the class name, the second does not.

 my $news_class = CTX->lookup_object( 'news' );
 my $newest_items = $news_class->fetch_group({ where => 'posted_on = ?',
                                               value => [ $now ] });
 
 my $older_items = CTX->lookup_object( 'news' )
                      ->fetch_group({ where => 'posted_on = ?',
                                      value => [ $then ] });

Returns: SPOPS class name; throws an exception if $object_name is not found.

Object Methods: Attributes and Aliases

global_attribute( $name, [ $value ] )

This is preliminary and may go away.

Get/set method for global attributes. Global attributes are a simple way of passing temporary information to various routines or actions without setting them in the server configuration.

These may get removed at the end of a request cycle or folded into something else.

Returns: Current value for attribute $name

alias( $type, $name, [ $value ] )

Get/set method for aliases. Setting an alias also dynamically creates a subroutine that returns $value. Note that resetting the alias after it has already been created will cause Perl to warn you about redefining a subroutine.

Example:

Create an alias for 'argle_bargle', which returns a class name for the 'nonsense' object class:

 my $nonsense = fetch_nonsense({ where => 'speaker = "native"' });
 print "Nonsense: ", $nonsense->spew, "\n";
 my $nonsense_class = ref $nonsense;
 CTX->alias( 'object', 'argle_bargle', $nonsense_class );

 ... later ...

 print CTX->argle_bargle->fetch_nonsense()->spew;

The best strategy for setting aliases would probably be to create them at server startup so they will always be available.

Returns: the currently set value for the alias $name.

Object Methods: Deployment Context

There are three separate deployment contexts used in OpenInteract2: the application context, image context and static context. These control how OI2 parses incoming requests and the URLs it generates in OpenInteract2::URL.

All deployment contexts are set from the server configuration file at startup. You'll find the relevant configuration keys under context_info.

assign_deploy_url( $path )

This is the primary application context, and the one you should be most interested in. OI2 uses this value to define a URL-space which it controls. Since OI2 controls the space it's free to parse incoming URLs and assign resources to them, and to generate URLs and have them map to known resources.

The default deployment context is '', or the root context. So the following request:

 http://foo.com/User/show/

OI2 will try to find an action mapping to 'User' and assign the 'show' task to it. Similarly when OI2 generates a URL it will not prepend any URL-space to it.

However, if we set the context to /OI2, like:

 CTX->assign_deploy_url( '/OI2' )

then the following request:

 http://foo.com/User/show/

will not be properly parsed by OI2. In fact OI2 won't be able to find an action for the request and will map it to the 'none' action, which is not what you want. Instead it will look for the following:

 http://foo.com/OI2/User/show/

And when it generates a URL, such as with:

 my $url = OpenInteract2::URL->create( '/User/show/', { user_id => 55 } );

It will create:

 /OI2/User/show/?user_id=55

Use the server configuration key context_info.deployed_under to set this.

Returns: new deployment URL.

assign_deploy_image_url( $path|$url )

This serves the same purpose as the application deployment context in generating URLs but has no effect on URL/request parsing. It's useful if you have your images on a separate host, so you can do:

 CTX->assign_image_url( 'http://images.foo.com' );
 ...
 my $url = OpenInteract2::URL->create_image( '/images/photos/happy_baby.jpg' );

and generate the URL:

 http://images.foo.com/images/photos/happy_baby.jpg

Unlike assign_deploy_url you can use a fully-qualified URL here.

Returns: new deployment URL for images.

assign_deploy_static_url( $path|$url )

Exactly like assign_deploy_image_url, except it's used for static resources other than images.

Returns: new deployment URL for static resources.

Object Methods: Other Resources

datasource( [ $name ] )

Returns the datasource mapped to $name. If $name is not provided, the method looks up the default datasource in the server configuration (under datasource_info.default_connection) and uses that.

Returns: the result of looking up the datasource using OpenInteract2::DatasourceManager

content_generator( $name )

Returns information necessary to call the content generator named by $name. A 'content generator' is simply a class which can marry some sort of template with some sort of data to produce content. The generator that comes with OpenInteract is the Template Toolkit, but there is no reason you cannot use another templating system or an entirely different technology, like SOAP.

Returns: a list with three-members - the class of the generator, the method of the generator and a subroutine reference to the method.

template( [ $template ] )

Get/set method for the global template object.

XXX: we might modify this to keep multiple template objects and have them be available by name. Then you could mix-and-match templates as you wish, using Template for most of your site but HTML::Template for a self-contained piece of it.

Returns: Currently available template object

PROPERTIES

The following are simple get/set properties of the context object.

base_config: Holds the OpenInteract2::Config::Base object. This must be defined for the context to be initialized.

server_config: Holds the OpenInteract2::Config::IniFile object with the server configuration. This will be defined after the context is initialized via setup().

repository: Holds the OpenInteract2::Repository object with methods for retrieving packages. This will be defined after the context is initialized via setup().

packages: Holds an arrayref of OpenInteract2::Package objects. These will be defined after the context is initialized via setup().

cache: Holds an object whose parent is OpenInteract2::Cache. This allows you to store and retrieve data rapidly. This will be defined (if configured) after the context is initialized via setup().

BUGS

None known.

TO DO

SEE ALSO

OpenInteract2::Action

OpenInteract2::Config::Base

OpenInteract2::Setup

OpenInteract2::URL

COPYRIGHT

Copyright (c) 2002-2003 Chris Winters. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHORS

Chris Winters <chris@cwinters.com>