Foswiki 3.0 Coding Guide

This guideline is intended for developers wanting to help with the Foswiki 3.0 redesign. It lists the Foswiki 1/2 calling convention and the Foswiki 3.0 implementation.

Common notes.

Class hierarchy is based on CPAN:Moo OO framework as a lightweight subset of CPAN:Moose which reasonable script startup time. CPAN:Moose may become the base Foswiki framework but not before CGI standard could be completely dropped in favor of permanent or semi-permanent environments like FCGI, mod_perl, and eventually PSGI. Anyway, CPAN:Moo will be referred throughout this paper as the base framework though one shall keep in mind that it's not permanent situation.

Foswiki::Object is the single base class for all other Foswiki classes.

Foswiki::AppObject is the base for all classes which cannot be instantiated without properly initialized $Foswiki::app application object. Foswiki::AppObject predeclares app attribute which stores a weak reference to $Foswiki::app, and method create() which simplifies instantiation of a new Foswiki::AppObject descendant class by implicitly passing app parameter to new() call. I.e.

$this->create('Foswiki::Request')

is equivalent to:

Foswiki::Request->new( app => $this->app )

Exceptions

Any generated exception must be a Foswiki::Exception descendant. die to be avoided as much as possible. Foswiki::Exception::Fatal is the most common substitute for die though well-thought exception hierarchy is highly welcome.

Foswiki::Exception provide several useful methods: static errorStr() for handling $@; object methods rethrow(), rethrowAs(), and transmute().

ASSERT() raises Foswiki::Exception::ASSERT.

Exceptions are handled using CPAN:Try::Tiny. CPAN:Error isn't used anymore.

Attributes.

CPAN:Moo has keyword is used to generate object attributes and their accessors. It means that references like $this->{attribute} are possible but not valid. $this->attribute must be used for accessing the value and $this->attribute($value) for setting it. CPAN:Moo has more on setters/getters and naming conventions.

For attributes which are dynamically generated or by any other reason must be stored using a hash a dedicated hash-containing attribute must be introduced. It's not only architectural limitation of CPAN:Moo but a security/reliability measure as it avoids clashes between predefined and dynamic object attributes.

Examples of such container-attributes are (double-dot notation is used to attach attribute name to it's class):

Foswiki::App::heap
Foswiki::Macros::_macros
Foswiki::Configure::Item::attrs

The Foswiki::Plugins::SESSION has been eliminated. Foswiki::App is the root of the Foswiki application, and information previously accessed from the $session-> hash field are now requested from the Foswiki App

Element Foswiki 1/2 Foswiki 3 Comments
Session object $Foswiki::Plugins::SESSION n/a  
Application object n/a $Foswiki->app  
Current user $session->{user} $Foswiki->app->user  

Function Foswiki 1/2 Example Foswiki 3 Example Comments
webExists $session->webExists $Foswiki::app->store->webExists() Functions that test contents of store have been moved into store.
topicExists $Foswiki::Plugins::SESSION->topicExists $Foswiki::app->store->topicExists() Plugins::SESSION has been eliminated.

Function Foswiki 1/2 Example Foswiki 3 Example Comments
normalizeWebTopicName $Foswiki::Plugins::SESSION->normalizeWebTopicName( $web, $topic ) $Foswiki::app->request->normalizeWebTopicName( $web, $topic )  

User session

Function Foswiki 1/2 Example Foswiki 3 Example Comments
getCGISession $session->getCGISession $app->users->getCGISession  

The $Foswiki::cfg hash is moved into $Foswiki->app->cfg->data, ad other methods that return configuration data have been moved to $Foswiki->app->cfg
Element Foswiki 1/2 Foswiki 3 Comments
Script URL $Foswiki::Plugins::SESSION->getScriptUrl() $Foswiki::app->cfg->getScriptUrl() Func version unchanged
Pub URL $Foswiki::Plugins::SESSION->getPubURL() $Foswiki::app->cfg->getPubURL  
Any config element $Foswiki::cfg{key} $cfg = $Foswiki->app->cfg;
$cfg->data->{key}
 

Unit tests

FoswikiTestCase simulates Foswiki::AppObject role by providing app object attribute and create() method.

FoswikiTestCase is a localizeable class. Methods pushApp() and popApp() are there to localize/restore current test case state without use of extra curly braces. See set_up() method for a example.

Introduced two new support classes: Unit::TestApp (inherits from Foswiki::App) and Foswiki::Engine::Test.

createNewFoswikiSession() method has been replaced with createNewFoswikiApp(). The following are the differences from the old method:

  • Unit::TestApp is used instead of Foswiki::App (and, for sure, the old Foswiki approach).
  • createNewFoswikiApp() doesn't use any arguments by simply bypassing what it receives in @_ to the Unit::TestApp constructor.
  • New Foswiki::Request object isn't created by the test case but is taken from the newly created app object. Generally, use of FoswikiTestCase request attribute shall be avoided.
  • All attributes of current test case object which holds objects with Foswiki::AppObject role are fixed to use the new app object.

Because the Foswiki::Request class has been split into few subclasses it's not possible anymore for Unit::Request to be used. Besides, unlike to old approach, Foswiki::Request is not initialized by the session/app object but initializes itself using engine-provided data. These changes have their implication on how a new temporary app object gets created. Two major players in this process are Unit::TestApp and Foswiki::Egine::Test.

  1. Unit::TestApp defines attributes requestParams and engineParams. Both are hashes passed over to request and engine constructors correspondingly.
  2. Foswiki::Engine::Test defines initialAttributes attribute. This is a hash of initial values for keys of Foswiki::Engine data attributes pathData, connectionData, and queryParameters. Values could also be taken from corresponding FOSWIKI_TEST_<KEYNAME> $app->env variables.
  3. Foswiki::Engine::Test can simulate both CGI and PSGI style returns. This behavior is determined by simulate attribute. CGI style is needed to comply with the capture() method requirements.

Additionally, as Foswiki::UI is now a base class to handle user actions and all Foswiki::UI::Action are subclassing it the old $UI_FN style of simulating a request processing doesn't work anymore. Only $this->app->handleRequest is supported.

Here is an example of new style code to capture a response taken from AccessControlTests case:

    my $app = $this->app;
    my $cfg = $app->cfg;

    ...

    # Request the page with the full UI
    my $viewUrl = $cfg->getScriptUrl( 0, 'view', $this->test_web, $test_topic );

    #$this->finishFoswikiSession();
    my ($text) = $this->capture(
        sub {
            $this->createNewFoswikiApp(
                requestParams => {
                    initializer => {
                        webName   => [ $this->test_web ],
                        topicName => ["$test_topic"],
                    },
                },
                engineParams => {
                    simulate          => 'cgi',
                    initialAttributes => {
                        path_info => "/" . $this->test_web . "/$test_topic",
                        method    => 'GET',
                        action    => 'view',
                        uri       => $viewUrl,
                    },
                },
                user => undef,
            );
            return $this->app->handleRequest;
        }
    );

NOTE: The CGI simulation will not be needed when an alternative capture method with PSGI-style return support will be created.

The user parameter is not from the original test case code and added as an example of use of other Foswiki::App supported attributes to initialize a new app object.
Topic revision: r8 - 30 Jun 2016, VadimBelman
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy