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
.
-
Unit::TestApp
defines attributes requestParams
and engineParams
. Both are hashes passed over to request and engine constructors correspondingly.
-
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_
$app->env
variables.
-
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.
Abandoned
--
MichaelDaum - 14 Oct 2024