NAME
Class::Declarative - Provides a declarative framework for Perl
VERSION
Version 0.06
SYNOPSIS
Perl's dominant paradigm, like most languages, is imperative. That is, when told to run a program, the computer does first one thing, then the next, until it terminates. Where this paradigm breaks down is when we are better off conceiving of our task not as programming a single agent (the computer) but a collection of objects, say, a GUI. In a GUI program, we talk about events that fire based on user actions. The events themselves lend themselves well to modeling by imperative code, but the overall organization of the program as a whole is much easier when we look at the system as a set of data structures.
Traditionally, when writing a GUI based on, say, Wx, these data structures have been built in an imperative initialization function.
That approach sucks.
Instead, I find it a lot easier to write a full description of the GUI, then hang code on it. So at some point, I thought, well, why not just write a class to interpret that pseudocode description directly?
That is this module.
Class::Declarative
provides the framework for building a complete Perl program based on a simple, indented, rather informal language. The language has no keywords at all; you have to provide a set of objects to define the semantics of everything. (Caveat: Class::Declarative
knows how to build code, so there are some things it will interpret on its own. We'll use those to write the test suites.)
A declarative object can report its own source code, and that source code can compile into an equivalent declarative object. This means that dynamically constructed objects or applications can be written out as executable code, and code has introspective capability while in the loaded state. Class::Declarative
also has a macro system that allows the construction of code during the build phase; a macro always dumps as its source, not the result of the expansion, so you can capture dynamic behavior that runs dynamically every time.
Class::Declarative
runs as a filter by default. That means that you can do this, giving it one or more semantic classes:
use Class::Declarative qw(Wx::Declarative); # Wx::Declarative is a set of semantic classes for wxPerl.
frame (xsize=450, ysize=400, x=50, y=50) "Caret Wx::Declarative sample"
menubar:
menu "&File":
item blinktime "&Blink time...\tCtrl-B"
separator
item about "&About...\tCtrl-A"
separator
item quit "E&xit\tAlt-X"
status (segments=2) "Welcome to Wx::Declarative!"
on quit {
^Close(1);
}
on about:
message:
title "Caret Wx::Declarative Sample"
body "About Caret"
icons "OK INFORMATION"
on blinktime:
dialog (template=getnumber):
help "The caret blink time is the time between two blinks"
prompt "Time in milliseconds"
title "Caret sample"
get { Wx::Caret::GetBlinkTime }
min "0"
max "10000"
result "blinktime"
on OK {
if ($^blinktime != -1) {
Wx::Caret::SetBlinkTime( $^blinktime );
Wx::LogStatus( $self, 'Blink time set to %d milliseconds', $^blinktime );
}
}
This structure is almost obvious without any explanation at all. That's kind of the point of declarative programming. This particular example doesn't show much Perl code, but the basic rule of thumb is that anything in curly brackets {} is Perl. Class::Declarative
gives you a little syntactic sugar to make things easy ("^word" expands to "$self->word", "$^word" expands to "$self->{word}", and "my $self = shift;" is appended to all functions). I don't know about you, but without that kind of help, I'd never get the structural references right, so I've done it once, right.
In addition to being declarative, a Class::Declarative
program is event-based. That means that things happen when events fire. There are many other Perl-y event frameworks, such as POE and Wx itself. Class::Declarative
organizes its object framework to work around Wx because that's the framework I know. I don't see why it couldn't build a POE application just as well, and that would be an excellent exercise after I've done Wx. Tell me if you're interested in this.
At any rate, all code snippets can function as event handlers. Events take place in the context of some node in the tree - and that node may not be the node where the code was defined. For instance, under Wx, you might define a code handler in the button that triggers it, but when that code actually runs, "$self" won't point to the button, it'll point to the frame or dialog the button is placed on.
TUTORIAL
For more information about how to use Class::Declarative
, you'll probably want to see "Declarative/Tutorial.pod" in .. instead of this file; the rest of this presentation is devoted to the internal workings of Class::Declarative
. (Old literate programming habits, I guess.)
SETTING UP THE CLASS STRUCTURE
import
The import
function is called when the package is imported. It's used for the filter support; don't call it.
If semantic classes are supplied in the use
command, we're going to instantiate and scan them here. They'll be used to decorate the parse tree appropriately.
class_build_handler ($string, $hash), app_build_handler ($string, $hash), build_handler ($string)
Given a tag name, class_build_handler
returns a hashref of information about how the tag expects to be treated:
* The class its objects should be blessed into, as a coderef to generate the object ('Class::Declarative::Node' is the default) * Its line parser, by name ('default-line' is the default) * Its body parser, by name ('default-body' is the default) * A second-level hashref of hashrefs providing overriding semantics for descendants of this tag.
If you also provide a hashref, it is assigned to the tag name.
The app_build_handler
does the same thing, but specific to the given application - this allows dynamic tag definition.
Finally, build_handler
is a read-only lookup for a tag in the context of its ancestry that climbs the tree to find the contextual semantics for the tag.
makenode($ancestry, $code)
Finds the right build handler for the tag in question, then builds the right class of node with the code given.
known_tags()
Returns the keys of the build handler hash. This is probably a fossil.
FILTERING SOURCE CODE
By default, Class::Declarative
runs as a filter. That means it intercepts code coming in and can change it before Perl starts parsing. Needless to say, filters act very cautiously, because the only thing that can parse Perl correctly is Perl (and sometimes even Perl has doubts). So this filter basically just wraps the entire input source in a call to new
, which is then parsed and called after the filter returns.
filter
The filter
function is called by the source code filtering process. You probably don't want to call it. But if you've ever wondered how difficult it is to write a source code filter, read it. Hint: it really isn't difficult.
PARSERS
The parsing process in Class::Declarative
is recursive. The basic form is a tagged line followed by indented text, followed by another tagged line with indented text, and so on. Alternatively, the indented part can be surrounded by brackets.
tag [rest of line]
indented text
indented text
indented text
tag [rest of line] {
bracketed text
bracketed text
}
By default, each tag parses its indented text in the same way, and it's turtles all the way down. Bracketed text, however, is normally not parsed as declarative (or "nodal") structure, but is left untouched for special handling, typically being parsed by Perl and wrapped as a closure.
However, all this is merely the default. Any tag may also specify a different parser for its own indented text, or may carry out some transformation on the text before invoking the parser. It's up to the tag. The data
tag, for instance, treats each indented line as a row in a table.
Once the body is handled, the "rest of line" is also parsed into data useful for the node. Again, there is a default parser, which takes a line of the following form:
tag name (parameter, parameter=value) [option, option=value] "label or other string text" parser < { bracketed text }
Any element of that line may be omitted, except for the tag.
init_parsers(), including locally defined is_blank, is_blank_or_comment, and line_indentation
Sets up the registry and builds our default line and body parsers.
init_default_line_parser(), init_default_body_parser(), init_locator_parser()
These are called by init_parsers
to initialize our various sublanguage parsers. You don't need to call them.
parser($name)
Retrieves a parser from the registry.
parse_line ($node)
Given a node, finds the line parser for it, and runs it on the node's line.
parse($node, $body)
Given a node and body text for it, finds the body parser appropriate to the node's tag and runs it on the node and the body text specified.
parse_using($string, $parser)
Given a string and the name of a parser, calls the parser on the string and returns the result.
BUILDING AND MANAGING THE APPLICATION
You'd think this would be up at the top, but we had to do a lot of work just to be ready to instantiate a Class::Declarative
object.
new
The new
function is of course called to create a new Class::Declarative
object. If you pass it some code, it will load that code immediately.
semantic_handler ($tag)
Returns the instance of a semantic module, such as 'core' or 'wx'.
start
This is called from outside to kick off the process defined in this application. The way we handle this is just to ask the first semantic class to start itself. The idea there being that it's probably going to be Wx or something that provides the interface. (It could also be a Web server or something.)
The core semantics just execute all the top-level items that are flagged callable.
id($idstring)
Wx works with numeric IDs for events, and I presume the other event-based systems do, too. I don't like numbers; they're hard to read and tell apart. So Class::Declarative
registers event names for you, assigning application-wide unique numeric IDs you can use in your payload objects.
root()
Returns $self; for nodes, returns the parent. The upshot is that by calling root
we can get the root of the tree, fast.
describe([$use])
Returns a reconstructed set of source code used to compile this present Class::Declarative
object. If it was assembled in parts, you still get the whole thing back. Macro results are not included in this dump (they're presumed to be the result of macros in the tree itself, so they should be regenerated the next time anyway).
If you specify a true value for $use, the dump will include a "use" statement at the start in order to make the result an executable Perl script. The dump is always in filter format (if you built it with -nofilter) and contains Class::Declarative
's best guess of the semantic modules used. If you're using a "use lib" to affect your %INC, the result won't work right unless you modify it, but if it's all standard modules, the dump result, after loading, should work the same as the original entry.
find_data
The find_data
function finds a top-level data node.
AUTHOR
Michael Roberts, <michael at vivtek.com>
BUGS
Please report any bugs or feature requests to bug-class-declarative at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Class-Declarative. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Class::Declarative
You can also look for information at:
RT: CPAN's request tracker
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
ACKNOWLEDGEMENTS
LICENSE AND COPYRIGHT
Copyright 2010 Michael Roberts.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.