File Coverage

blib/lib/GCT/XSP/ActionTaglib.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             GCT::XSP::ActionTaglib - Helps Compose AxKit XSP taglibs in a simple and extensible manor.
5              
6             =head1 SYNOPSIS
7              
8             package MyTaglib;
9             use GCT::XSP::ActionTaglib;
10             @ISA = qw(use GCT::XSP::ActionTaglib;);
11             our $NS = 'MyTaglib-URI';
12             our $tagactions;
13            
14             $tagactions->{tag1}=[ ...actionlist for tag1... ];
15            
16             =head1 DESCRIPTION
17              
18             ActionTaglib helps write a talgib module for AxKit. One or more 'actions' are assigned to each XML element processed by the taglib. When the XSP page is 'run' ActionTaglib performs these actions as it parses the XML elements they are assigned to. An action is implemented as two Perl subroutines one for when the opening XML element is parsed, the 'start' subroutine, and one for when the closing XML element is parsed, the 'end' subroutine. For example:
19              
20             by adding a tag1 entry to the tagactions hashref as follows:
21              
22             $tagactions->{tag1}=[{-action => 'myaction'}];
23              
24             And adding the following tags to an XSP page (with the relevant namespace settings)
25              
26             ...
27              
28             'myaction_start' will be called for the opening and 'myaction_end' will be called for the closing element .
29              
30              
31             This behavior extends Apache::AxKit::Language::XSP which does essentially the same thing but with one 'action' for all XML elements (parse_start / parse_end). The hope is to make taglibs easier to layout and read but more importantly it is possible to share actions between taglibs and thus save writing the same code twice for different taglibs. Libraries of actions can be written and shared by importing their actions into a taglib module that uses ActionTaglib.
32              
33             ActionTaglib has two further features to enhance the idea and useage of 'actions'. The first is that a tag can be assigned multiple actions and the second is actions can be given arbitrary options. Multiple actions are assigned in the following manor:
34              
35             $tagactions->{tag1}=[{-action => 'library_action'},
36             {-action => 'myaction'} ];
37              
38             With our ... example 'library_action_start' followed by 'myaction_start' would be called for the opening element and 'myaction_end' followed by 'library_action_end' would be called for the closing element . Note the change in order, actions are proccess in the forward order for an opening element and in the reverse order for a closnig element.
39              
40             Options can be parsed in the following manor:
41              
42             $tagactions->{tag1}= [{-action => 'myaction',
43             -options => {-anyoption => 'anyvalue'} }];
44              
45             =head1 THE tagactions HASHREF
46              
47             The tagactions hashref, as introduced above, accosiates XML elements with the actions that should be performed to proccess them. The format is to specify an array, called an 'actionlist' for each tag as follows:
48              
49             $tagactions->{tag1} = [..actionlist for tag1..]
50              
51             Actions are the spcifed, in order, as elements of the array as follows:
52              
53             [action,action,action...]
54              
55             Each action is defined by a hash containing th following keys:
56              
57             -action. Required string. the name of the action to call.
58              
59             -when. Optional string, Values can be 'start', 'end' or the default 'both'. If the value is 'start' is the action will only be called for the opening tag, if the value is 'end' the action will only be called for the closing tag and if the value is 'both' (or the key -when is ommited) the action will be called for both the opeing and closing tag.
60              
61             -pkg. Optional string, The pkg that contanis the action to be called. If ommited the default package is the package of the taglib being written, i.e. the current package..
62              
63             -options. Optional hash, Any options to be passed to the action.
64              
65             The options are passed as a hash of option value pairs. A complete example of a tagactions hashref is given bellow.
66              
67             =head1 WRITEING ACTIONS
68              
69             Actions are written in a very similar way to you would write the parse_start and parse_end subs when using AxKit::Lanaguage::XSP directly. Just like the parse* subs in AxKit::Langauge::XSP, action subs output some perl code, as a string, which will be added to the script that is being built for the current XSP page. The proccess, for taglibs in general, is that an XSP page is parsed (with a SAX parser), taglibs transfor it into a perl script, the perl script is executed and ouputs an XML document. The genrated script uses a DOM to build and output it's XML document but this is all handeled by AxKit::Langauge::XSP and a taglib author often need not know.
70              
71             If you have never written a parse_start or parse_end using AxKit::Language::XSP you can still write actions. They are defined by writing two subs: actionname_start{} and actionname_end{}. which are called or an element that is specified to use the action named 'actionname', that _start sub when the an opening tag is being parsed and the _end sub when a closing tag is being parsed. They both take the following arguments:
72              
73             ($parseargs,$options,$action,$i,$actionlist,$tagactions)
74              
75             Which are as follows:
76              
77             $parseargs, array, contains ($e, $tag, %attribs) for the _start sub and ($e, $tag) for the end sub, which is the same idea as used with parse_start and parse_end in AxKit::Lanagauge::XSP.
78              
79             $options. hashref, the options to use for this action as specified in the $tagactionshash.
80              
81             Note the passed values of $action, $i, $actionlist and $tagactions are used for wrtiting more advanced actions. They enable an action to modify the $tagactions hahref it is in and thus change the way actions are performed for tags while the source ducument is being parsed, in otherwords on-the-fly. This feature must be used with care becuase the $tagactions hash could easily be changed in a way that makes no sence. However it is enables us to write actions that and, remove or change other action specifications in the taglib.
82              
83             $action, hashref, the action currently being run.
84              
85             $i, integer, the index of the current action in it's action list.
86              
87             $actionlist, arrayref, the actionlist in which the current action is specified.
88              
89             $tagactions, hasref, all the tagactions.
90              
91             =head2 $parseargs
92              
93             The variables in passed in parse args are as follows:
94              
95             $e, the script being build
96              
97             $tag, the name of the tag being parsed
98              
99             $attribs, a hash of the attributes bellonging to the current tag.
100              
101              
102             MORE TO COME...
103              
104              
105             An example
106              
107             $tagactions->{helloworld} = [{-action => 'format', #the name of the action
108             -pkg => 'htmlformating', #the package it is defined in
109             -when => 'both', #when to call the action
110             -options => {-type => 'bold'}
111             },{
112             -action => 'text',
113             -when => 'start',
114             -options => { text => 'Hello World'}
115             }];
116              
117              
118              
119             =head2 EXPORT
120              
121             None by default.
122              
123             =cut
124              
125 1     1   40523 use 5.006;
  1         4  
  1         51  
126 1     1   7 use strict;
  1         2  
  1         46  
127 1     1   11 use warnings;
  1         9  
  1         62  
128              
129             package GCT::XSP::ActionTaglib;
130              
131 1     1   1743 use Apache::AxKit::Language::XSP;
  0            
  0            
132             our @ISA = ('Apache::AxKit::Language::XSP', 'Exporter');
133             # our $NS = ...
134             # No namespace here, ActionTaglib is designed to be inherited from
135             # Implementations should declare 'our $NS' with a unique URI.
136              
137             our $VERSION = '0.02';
138             our $REVISION; $REVISION = '$Revision: 1.4 $';
139             our $dbug=0;
140             our $dbug_tagactions=0;
141              
142             ########################################
143             # package GCT::XSP::ActionTaglib;
144             # the 'action processor', reads the action list
145             # and calls the relevant subroutines.
146              
147             sub Debug{
148             AxKit::Debug(1,"[ActionTaglib]", @_);
149             }
150             ############################################################
151             #parses all elements and calls the relevant subroutine
152             #to deal with them.
153             sub parse_element{
154             my ($parseargs,$when) = @_;
155             #$parseargs, arrayref, the standard AxKit arguments as given to
156             #parse_start / parse_end.($e,$tag,%attribs).
157             #$when, string, 'start' => opening element, 'end' => closing element.
158              
159             my $tag = $parseargs->[1];
160             #get the tagactions hashref that specifies how to deal with this element
161             my $tagactions;
162             my $pkg = $AxKit::XSP::TaglibPkg; #not very OOP! would be better if we could call $self->tagactions
163             {no strict;
164             $tagactions = ${"$pkg\::tagactions"};};
165             Debug("parse_start -$pkg-$tag-") if $dbug;
166             #get the actionlist for this element
167             if(my $actionlist = ($tagactions->{$tag})){
168             Debug("ActionTaglib Actions:") if $dbug;
169             #TODO use a reference to the actionlist and avoid creating a ne array
170             #that way the actionlist array will refer to the actual array rather
171             #than a copy of it.
172             Debug("parsing $tag $when ") if $dbug;
173             #Do all the actions for this element,
174             #pass $actionslist and $tagactions so the action can modify them is necessary.
175             return processactionlist($parseargs,$actionlist,$tagactions,$when,$pkg)
176             }else{
177             return '';
178             }
179             }
180              
181             ############################################################
182             #processes an array of actions, in forward order if they are
183             #being started ($when = 'start') and in reverse order if
184             #they are being ended ($when = 'end').
185             sub processactionlist{
186             my ($parseargs,$actionlist,$tagactions,$when,$default_pkg) = @_;
187             #$parseargs, as above.
188             #$actionlist, arrayref, a list of actions to do for this element.
189             #$tagactions, hashref, all the tagactions in this taglib
190             #$when, as above.
191             #$default_pkg, string, the default package to contain the action sub's.
192              
193             my $last = scalar @{$actionlist};
194             my $ret;
195             Debug("COUNT: $last ACTIONS TODO ") if $dbug;
196             if ($when eq 'start'){ #forward
197             for (my $i=0;$i<$last;$i++){
198             $ret .= processactioni($parseargs,$i,$actionlist,$tagactions,$when,$default_pkg);
199             }
200             }elsif($when eq 'end'){ #reverse
201             for (my $i=$last;$i>-1;$i--){
202             $ret .= processactioni($parseargs,$i,$actionlist,$tagactions,$when,$default_pkg);
203             }
204             }else{
205             die "'$when' is not a valid when, value must be start or end";
206             }
207             return $ret;
208             }
209              
210             ############################################################
211             #Process a numbered action from an actionlist but only if
212             #the time ($when) is right.
213             sub processactioni{
214             my ($parseargs,$i,$actionlist,$tagactions,$when,$default_pkg) = @_;
215             #all arguments as above, except:
216             #$i, integer, numbered action from $actionlist array to process.
217             my $action = $actionlist->[$i];
218             #the -when option specifies when the action should be done.
219             #'start' => only when an element is opening.
220             #'end' => only when an element is closing.
221             #'both' => both when an element is opening and when is is closing [default].
222             my $awhen = $action->{-when} || "both";
223             Debug("DOING ACTION $i ") if $dbug;
224             if ( $awhen eq "both" || $awhen eq $when) {
225             $action->{-pkg} ||= $default_pkg;
226             return processaction($parseargs,$action,$i,$actionlist,$tagactions,$when);
227             }else{
228             return '';
229             }
230             }
231              
232             ############################################################
233             #processes a single action
234             sub processaction{
235             my ($parseargs,$action,$i,$actionlist,$tagactions,$when) = @_;
236             #all arguments as above.
237             my $methodpkg = $action->{-pkg}; #required. the package that contains the action
238             my $method = $action->{-action} . "_$when"; #required. the name of the action to perform, appended
239             #with the time (start or end) it is being performed
240             my $options = $action->{-options}; #optional. any options the action might use/need.
241             Debug("LOOKING TO SEE IF $methodpkg -> can ($method)") if $dbug;
242             if (my $sub = $methodpkg->can($method)){
243             Debug("A SUB CALLED: $method for $parseargs->[1] ") if $dbug;
244             #finally we do the action!
245             #see the documentation on how to write an action
246             return $sub->($parseargs,$options,$action,$i,$actionlist,$tagactions);
247             }else{
248             return '';
249             }
250             }
251             ########################################
252             # Overriding Apache::AxKit::Language::XSP subs;
253              
254             sub start_document{
255             Debug("[ActionTaglib] START DOCUMENT");
256              
257             if($debug_tagations){
258             my $tagactions;
259             my $pkg = $AxKit::XSP::TaglibPkg; #holds the package name of the current taglib
260             {no strict;
261             $tagactions = \${"$pkg\::tagactions"};};
262             use Data::Dumper;
263             Debug("TAGATIONS AT START OF DOUCUMENT: " . Dumper($tagactions)) if $dbug;
264             }
265             return '';
266             }
267              
268             sub end_document{
269             Debug(1,warn "END DOCUMENT");
270              
271             if($debug_tagations){
272             my $tagactions;
273             my $pkg = $AxKit::XSP::TaglibPkg; #not very OOP!
274             {no strict;
275             $tagactions = \${"$pkg\::tagactions"};};
276             use Data::Dumper;
277             Debug("TAGATIONS END: " . Dumper($tagactions)) if $dbug;
278             }
279             return '';
280             }
281              
282             # get the ActionTaglib parse_element to deal with all
283             # elements opening (start) and closing (end).
284             sub parse_start{
285             return parse_element(\@_,'start');
286             }
287              
288             sub parse_end{
289             return parse_element(\@_,'end');
290             }
291              
292             #default to add characters as text nodes
293             sub parse_char{
294             my ($e, $text) = @_;
295             $text =~ s/^\s*//; #remove leading
296             $text =~ s/\s*$//; #and trailing spaces
297             return '' unless $text;
298             $text = Apache::AxKit::Language::XSP::makeSingleQuoted($text);
299             return ". $text";
300             }
301              
302             #drop comments
303             sub parse_comment{
304             my ($e, $comment);
305             return '';
306             }
307              
308             1;
309              
310             __END__